Pin 架构

1. 使用Pin进行instrumentation

Pin提供的API可以让我们观察一个进程的状态,比如:内存、寄存器和控制流。Pin还提供了一些更改程序行为的机制,比如:允许重写程序的寄存器和内存。(DynamoRIO的理念是尽量不影响原程序的执行) 
Pin通过一个just-in-time (JIT) compiler来实现instrumentation。这个compiler的输入不是bytecode,而是一个本地的可执行文件。可执行文件执行时,Pin会拦截它的第一条指令,然后产生(“compiles”)一个新的指令,放到一个code sequance中。然后控制就转移到了这个产生的code sequence开始执行。这个产生的code sequence和原始代码执行流程几乎一样,但是Pin会在code sequence的指令遇到分支时,重新获取控制权。在获取控制权之后,Pin会寻找到条件转移指令,并把它放到code sequence中,继续执行。 
每当JIT获取指令时,Pintool就有机会在指令被翻译并执行前,去做instrumentation。被翻译的指令和它们的instrumentation被存放在一个code cache中,将来执行同样的指令时,就不需要翻译,直接在code cache中执行,以提高性能。 
值得一提的是,IntelPin提供的API,让我们可以忽略底层架构,同一套代码在ARM和x86上都能运行。Intel Pin提供的API和DynamoRIO类似,都是事件导向的API(call-based model)。都很易用。

2. 系统架构

Intel Pin的架构如下图所示: 
Pin 架构_第1张图片 
从较高层面来说,Pin包含了一个VM,一个code cache和一个instrumentation API. 
其中VM包含了一个just-intime compiler (JIT),一个emulator和一个dispatcher. 在Pin获取了目标程序的控制后,VM通过协调它的组件来执行目标程序。JIT编译并instrument应用程序的指令,dispatcher把这些编译后的指令加载到code cache中。进入/离开code cache时,需要保存/恢复应用程序的寄存器状态。emulator会解释那些无法被直接执行的指令,这是为system call准备的,因为system call需要VM的特殊处理。 
Pin工作在操作系统之上,因此它只能捕获用户级别的指令。 
当一个被instrument之后的程序运行时,有3个程序在运行:应用程序本身、Pin、 Pintool. Pin是对应用程序进行instrument的引擎。Pintool包含了instrumentation的指令,它作为pin的一个library存在。 
应用程序本身、Pin和Pintool共享同一个地址空间,但是他们不共享libraries,所以他们其实是glibc的3个拷贝。通过让三者的库独立私有,避免了Pin、Pintool和应用程序本身的交互冲突。比如:如果应用程序开始执行开始执行一个库函数,然后触发了JIT执行的指令,控制就会转向JIT。如果JIT也执行了相同的库函数,就会再次进入这个库函数,导致错误。因为不同组件拥有不同的库拷贝,所以Pin/pintool和应用程序本身不共享任何数据,也就不会有这种问题。(这一点和DynamoRIO不同,DynamoRIO为了解决这个问题,不和应用程序本身冲突,只能选择直接调用system call,导致DynamoRIO的instrument指令无法正常使用一些第三方库)。

3. Pin的injection机制

是injector负责把Pin载入到应用程序的地址空间中。 
它利用了Unix的Ptrace API获得应用程序的控制和处理器的上下文,然后将Pin的库也在载入到应用程序的地址空间开始执行。Pin开始执行之后,会将Pin tool也载入到地址空间开始执行。 
Pin初始化执行环境,然后开始对应用进程开始jit(可以从第一条指令,也可以从当前正在运行的指令开始)。Pin也可以从一个已经instrument过得进程中分离,回归到正常的原始应用程序运行。(DynamoRIO的dynamic loader实现机制则不同,它强制应用程序一开始,就要和dynamoRIO在一起,不可分离。相比之下Pin更加灵活)

4. The JIT Compiler

Pin将一个架构的指令直接编译成同样的指令,没有经过中间指令,编译后的指令存储在一个code cache中。只有code cache中的指令被执行了,原始的指令不被执行。 
应用程序的指令是以trace为单位编译的,一个trace,就是一串指令,直到遇到以下条件终止:

(i) an unconditional control transfer (branch, call, or return),
(ii) a pre-defined number of conditional control transfers, 
(iii) a pre-defined number of instructions have been fetched in the trace. 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

每个trace可能有多个出口,比如条件指令,当碰到条件指令时,控制就会转移到VM,由VM去寻找目标trace.

4.1 Trace Linking

为了提升性能,Pin会将一些只有一个目标的trace,和它的目标trace连接起来。这样就不用去重新转移到VM,重新寻找目标了。然后,有些条件转移指令可能有不同的目标,这就需要indirect linking技术。这一点和DynamoRIO采用的技术是类似的。

4.2 Register Re-allocation

在jit运行时,我们经常需要额外的寄存器,比如:在解决indirect分支时,我们就需要3个空闲的寄存器。 
当instrumentation对一个应用程序插入一段代码,JIT必须保证这段代码没有重写任何应用程序正在使用的寄存器。 
Pin的解决方案是构建一些虚拟的寄存器,Pin和Pin tool在使用时可以将它们看做正常的寄存器使用。

你可能感兴趣的:(杂类)