简介
pin是用来为程序插桩Instrumentation( 插桩)的工具;适用于windows\Linux\osx操作系统,可在IA32和INTEL®64和多种集成INTEL® 芯片芯片上执行。
pin允许一个工具在可执行文件的任意位置植入任意代码(C/C++)。当可执行文件执行时,代码是动态地添加的。这使得可在正运行的程序中连接pin。
Pin提供了丰富的API(应用程序编程接口),可以抽象出底层指令集的特性,并允许将诸如寄存器内容之类的上下文信息作为参数传递给注入的代码。pin自动地存储和恢复被注入的代码覆盖的存储器,从而使应用在执行完注入代码后继续工作。还可以访问有限的符号和调试信息。
pin包括大量的插桩工具例如存储器分析其、cache模拟器、指令追踪发生器等等的源码。使用这些示例可以很容易地开发出新的工具。
pin
Pin被看作即时编译器(JIT中文名) 输入不是字节码而是正则可执行文件
pin截获可执行文件的第一个指令的执行,并产生(编译出)新代码,以执行这个指令。然后将控制转移给新代码。生成的代码序列与原始代码序列几乎相同,但PIN确保当分支退出序列时它恢复控制。PIN使所有生成的代码保持在内存中,从而可以重用并直接从一个序列分支到另一个序列。
在JIT模式下,唯一执行的代码是生成的代码。原代码仅供参考。当生成代码时,PIN使用户能注入他们自己的代码。
PIN只可操作执行真正执行的指令。之指令的未知无所谓。虽然条件分支有一些例外,一般来说,如果指令从未执行,那么它将不被检测。

pintools
两部分:
1、 一个机制,它决定插入代码的位置和代码
2、一段插入代码
都是指导和分析代码,两个组件在同一个可执行文件pintool中PoToots可以被看作插件,可以修改PIN中的代码生成过程。

    PinTool注册带有PIN的回调例程,每当需要生成新代码时,PIN就从PIN调用。它检查要生成的代码,研究其静态属性,并决定是否和从哪调用分析函数。

    分析函数收集应用程序的数据。PIN确保在必要时保存和恢复整数和浮点寄存器状态,并允许参数传递给函数。

    Pintool工具还可以为诸如线程创建或分叉之类的事件注册通知回调例程。这些回调通常用于收集数据或工具初始化或清理。                   

Observations
由于Pintool像插件一样工作,它必须运行在与PIN和被操作的可执行文件相同的地址空间。因此,Pintool可以访问所有可执行文件的数据。它还与可执行文件共享文件描述符和其他进程信息。

    PIN和Pintool控制从第一个指令开始的程序。对于用共享库编译的可执行文件,这意味着动态加载程序和所有共享库的执行将对pintool可见。
     在编写工具时,调整分析代码比操作代码更重要。这是因为操作执行一次,但分析代码被多次调用。

Instrumentation Granularity(操作粒度)
JIT,已执行代码就发生操作即跟踪操作。
跟踪操作使得pintool一次观察和操作一个可执行文件。跟踪通常从一个被捕获的分支的目标开始,以一个无条件分支结束,包括调用和返回。一输入,可能多输出。
如果中途加入新的分支,就从新分支开始重复上述过程。
pin把一次追踪分为小块——BBL。BBL是单入口、单出口指令序列。分支到BBL的中间开始一个新的轨迹,一个新的BBL。
一般是为BBL植入分析调用,而不是为一个指令植入分析调用。
减少分析调用的数量使得仪器效率更高。跟踪仪器利用TraceEngEdToCuffice函数API调用。
使用 TRACE_AddInstrumentFunction来进行分析调用。

但是,请注意,由于PIN在执行程序时动态地发现程序的控制流,PIN的BBL可以不同于BBL的经典定义,这将在编译器教科书中找到。例如,考虑生成像这样的switch语句主体的代码。

    执行L7时,PIN将生成包含L7、L6、L5、L4的BBL,执行其他时同上。而不是如传统模块定义,只包含一个指令。
    PIN中断BLL的方式也不同以往。例如,当cpuid(处理器信息)、 popf(恢复保护栈)和REP(?)前缀指令终止追踪,BBL也终止。由于ReP前缀指令被视为隐式循环,如果ReP前缀指令迭代不止一次,在第一次之后的迭代将导致生成单个指令BBL,因此在这种情况下,您将看到比您预期的执行的更多基本块。

    Pin为Pintool编写工具提供了一种指令测试模式,让工具一次检查并执行一个可执行指令。这与跟踪工具在本质上是相同的,Pintool编写器已经摆脱了遍历操作中的迭代。在跟踪操作中,某些BBLs和它们内部的指令可以多次生成(并因此被操作)。指令操作利用了INS_AddInstrumentFunction工具函数API调用。 

    然而,有时候,观察不同的粒度比跟踪是有用的。为此,PIN提供了另外两种模式:图像和例程操作。这些模式是通过“缓存”操作请求来实现的,因此会产生空间开销,这些模式也被称为超前检测。 

    图像(IMG)分为SEC,常规分为RTN,一个例程有一个指令称为INS。可以插入操作,以便在例程程序或在执行指令之前或之后执行。
    调用图像:IMG_AddInstrumentFunction 
    图像检测依赖于符号信息来确定例程边界,因此必须在PIN_Init.之前调用PIN_InitSymbols。 

    图像首次被装载时,例程操作使得pintool检查和操作一个完整例程。
    没有使得指令被打碎为BBL的足够的信息。
    可以插入操作,以便在执行例行程序或在执行指令之前或之后执行。常规操作是为pintool的作者提供便利的,作为在图像测量过程中walking图像的部分和例程的替代品。 
    例程操作利用RTN_AddInstrumentFunction 函数API调用。存在尾部呼叫或当不能可靠地检测返回指令时,例程出口的操作在不能可靠地工作。 
    注意,在图像和常规仪器中,不可能知道是否实际执行一个例程(因为这些仪器是在图像加载时间完成的)。通过跟踪例程开始的指令,可以在跟踪或指令测试例程中仅执行执行的例程的指令。请参阅工具测试/ PARSEIXECULTED

Managed platforms support(平台支持)
PIN支持所有可执行文件,包括托管二进制文件。从PIN的观点来看,托管二进制是另一种自修改程序。有一种方法使PIN将即时编译代码(JIT代码)与所有其他动态生成代码进行区分,并将JIT代码与适当的托管函数相关联。为了获得这个功能,运行托管平台的准时编译器(JITER)应该支持JIT配置API。

                   可支持的有:
    · RTN_IsDynamic() API标记为仅使用JIT剖析API来动态创建。              
    · 一个Pintool工具可以使用RTN_AddInstrument函数API来检测例程,参见示例管理平台支持更多信息。 

    需要满足以下条件来获得管理平台的支持:
    · 将INTEL_JIT_PROFILER32 环境变量设置为适当的INTEL_JIT_PROFILER64动态库
     1、windows
            set INTEL_JIT_PROFILER32=<The Pin kit full path>\ia32\bin\pinjitprofiling.dll
            set INTEL_JIT_PROFILER64=<The Pin kit full path>\intel64\bi\pinjitprofiling.dll
     2、linux

            setenv INTEL_JIT_PROFILER32 <The Pin kit full path>/ia32/bin/libpinjitprofiling.so
            setenv INTEL_JIT_PROFILER64 <The Pin kit full path>/intel64/bin/libpinjitprofiling.so

    · 将旋钮SpPosijJITYAPI添加到PIN命令行作为Pintool选项: 
            <Pin executable> <Pin options> -t <Pintool> -support_jit_api <Other Pintool options> -- <Test application> <Test application options>

Floating Point Support in Analysis Routines(浮点支持)
PIN负责维护应用程序的浮点状态分析例程。
不能将浮点寄存器值作为参数传递给分析例程。

Instrumenting Multi-threaded Applications(多线程应用程序)
操作多线程程序要求访问全局存储线程安全的工具必须与其他线程协调。PIN试图为工具提供传统的C++程序环境,但不可能使用标准库接口来管理PoToo销中的线程。例如,Linux工具不能使用pToX库,Windows工具不应该使用Win32 API来管理线程。相反,PIN提供了自己的锁定和线程管理API,Pintool应该使用它。(见锁定:锁定原语和PIN线程API)。
除了PIN线程ID之外,PIN API还提供了一个有效的线程本地存储(TLS)TLS,它可以分配一个新的TLS密钥并将其与给定的数据销毁函数相关联。进程的任何线程都可以在自己的时隙中存储和检索值,由分配的密钥引用。与所有线程中的键相关联的初始值为NULL。有关更多信息,请参阅使用TLS的示例。

Avoiding Deadlocks in Multi-threaded Applications(避免多线程应用中的死锁/所层次)
由于PIN、工具和应用程序可以分别获取和释放锁,Pintool开发人员必须小心避免应用程序或PIN的死锁。死锁通常发生在两个线程以不同的顺序获取相同锁的情况下。例如,线程A获取锁L1,然后获取锁L2,而线程B获取锁L2,然后获取锁L1。如果线程A持有锁L1并等待L2,而线程B持有锁L2并等待L1,则这将导致死锁。为了避免这种死锁,PIN在必须获取锁的顺序上强加了一个层次结构。PIN通常在工具获取任何锁之前(例如通过PIN_GetLock())获取自己的内部锁。此外,我们假设应用程序可以在该层次结构的顶部获取锁(即,在PIN获取其内部锁之前)。下图说明了层次结构:
Application locks -> Pin internal locks -> Tool locks

    Pintool开发人员应该设计他们的pintool工具,这样他们就永远不会打破这个锁层次,他们可以这样做,遵循以下基本准则:
    · 如果该工具从PIN回调中获取任何锁,则必须在从该调用返回之前释放这些锁。通过锁定PIN回调持有一个锁,违反了PIN内部锁的层次结构。
    · 如果工具从分析例程中获取任何锁,则必须在从分析例程返回之前释放这些锁。持有一个锁跨引脚分析例程违反了关于PIN内部锁和其他锁使用的仪器应用程序本身的层次结构。 
    · 如果工具从PIN回调或分析例程中调用PIN API,则在调用API时不应该持有任何工具锁。一些PIN API使用内部PIN锁,因此在调用这些API之前持有工具锁违背了PIN内部锁的层次结构。 
    · 如果工具从分析例程中调用PIN API,则可能需要首先调用PIN锁Client()来获取PIN客户端锁。这取决于API,所以检查文档的具体API以获取更多信息。请注意,当调用PIN_LockClient() 时,该工具不应持有任何其他锁,如前一项所述。 

    虽然这些准则在大多数情况下都是足够的,但对于某些用例来说,它们可能过于严格。下一组指南解释了放松以上基本指南的安全条件:
    · 在JIT模式下,工具可以从分析例程中获取锁,而不释放它们,只要它在离开包含分析例程的跟踪之前释放这些锁。如果应用程序指令引发异常,则该工具必须预期跟踪可能退出“早期”。当应用程序引发异常时,工具可能持有的任何锁L必须遵守以下子规则:
        。当应用程序引发异常时,该工具必须建立一个执行回叫,并且如果在异常发生时获取了锁L,则该调用必须释放锁L。工具可以使用pInAdAdExtReXeTeo函数()来建立该回叫。
        。工具不能从任何PIN回调中获取锁L,以避免违反PIN内部锁的层次结构。
    · 如果工具从分析例程调用PIN API,则它可以在调用API时获取并保持锁L,满足如下情况:
         。锁L没有被任何PIN回调中获取。这避免了相对于PIN内部锁的层次结构违反。 
        。 调用的PIN API不会导致应用程序代码的执行(例如,0 PIN_CallApplicationFunction函数())。这避免了应用程序本身使用的锁的层次结构违背。 
Logo

更多推荐