Pin

Pin is a dynamic binary instrumentation framework for the IA-32 and x86-64 instruction-set architectures that enables the creation of dynamic program analysis tools.

1. Pin, Pintool에 대한 소개 [1, 2, 3]
Pin은 프로그램 실행 중에 원하는 위치에 원하는 코드를 삽입하는 것을 가능하게 해 준다[1]. Pin은 실행할 코드를 가로채, 원하는 코드로 바꾸어 실행한다. 실행 파일의 명령어는 Pin이 참고만 할 뿐이며, 실제로 실행하는 코드는 Pin이 생성하는 코드이다. Pintool은 일종의 플러그인으로, Pin의 코드 생성 과정을 제어할 수 있다. Instrumentation은 1) 코드를 삽입할 지점 확인과 2) 삽입할 코드가 있어야 할 수 있는데, 이 모두를 Pintool에서 수행한다. Pintool은 callback function을 Pin에 등록해두고, instrument하고자 하는 지점에 Pin이 도달할 때마다 callback function을 호출하도록 해, binary instrumentation을 수행한다.

Binary instrumentation을 통해 할 수 있는 것
1) Compiler 최적화를 위한 프로파일러 (basic block profilers)
2) 마이크로 아키텍쳐 연구 (cache simulators)
3) 디버깅 (초기화되지 않은 값 사용, 할당되지 않은 영역 사용 등)
4) Binary translation (프로그램 행위 수정, 지원되지 않는 기능 에뮬레이션)

Dynamic binary instrumentation의 장점
1) Instrumentation pass를 따로 구성할 필요가 없음
2) 모든 user-level code를 instrumentation할 수 있음.
3) 실행 중인 프로세스에도 attach해서 instrument할 수 있음.
4) 다양한 플랫폼을 지원한다.

 
2. Instrumentation Granularity
Pin의 instrumentation granularity에 따라, 총 네 가지 방식으로 binary instrumentation이 가능하다. 각각은 서로 다른 API를 사용한다.

1) Trace Instrumentation (TRACE_AddInstrumentFunction)
Trace의 단위로 instrument하는 모드이다. Trace는 일반적으로 branch의 target 명령어에서 시작해 call, return 등의 무조건 분기에서 끝난다. Just-in-time instrumentation.
2) Instruction Instrumentation (INS_AddInstrumentFunction)
한 개의 instruction 단위로 instrument하는 모드이다. Just-in-time instrumentation.
3) Image Instrumentation (IMG_AddInstrumentFunction)
실행 바이너리 그 자체 또는 공유 라이브러리가 주소 공간에 올라올 때 instrument하는 모드이다. Ahead-of-time instrumentation
4) Routine Instrumentation (RTN_AddInstrumentFunction)
특정 함수를 실행하는 시점의 전후로 instrument하는 모드이다. Ahead-of-time instrumentation

Instrumentation granularity 부분을 이해하는데 어려움이 있었다. ASPLOS’04에 발표된 Pin tutorial과 예제 코드(./source/tools/SimpleExamples)를 참고하니 이해할 수 있었다. 용도에 맞는 granularity를 사용하는 것이 좋다. 불필요하게 fine-grained한 모드에서 instrument한다면 성능의 저하가 발생한다.

 
3. Pin Build
Pin은 다음과 같이 빌드할 수 있다.

$wget http://software.intel.com/sites/landingpage/pintool/downloads/pin-2.14-67254-gcc.4.4.7-linux.tar.gz
$tar -xzvf ./pin-2.14-67254-gcc.4.4.7-linux.tar.gz
$cd ./pin-2.14-67254-gcc.4.4.7-linux
$cd source/tools/ManualExamples
$make all

 
4. Simple Example
Instruction 개수를 세는 가장 기본적인 예제를 실행해보자.

$./pin -t ./source/tools/ManualExamples/obj-intel64/inscount0.so -- /bin/ls
doc  extras  ia32  intel64  LICENSE  pin  pin.log  pin.sh  README  redist.txt  source

위 명령어를 실행하면 생성된 파일(inscount.out)에서 instruction count를 볼 수 있다.

Count 434131

예제를 일반 사용자 권한으로 실행하는 경우, 다음과 같은 에러가 발생한다.

E:Attach to pid 25806 failed.
E:  The Operating System configuration prevents Pin from using the default (parent) injection mode.
E:  To resolve this, either execute the following (as root):
E:  $ echo 0 > /proc/sys/kernel/yama/ptrace_scope
E:  Or use the "-injection child" option.
E:  For more information, regarding child injection, see Injection section in the Pin User Manual.
E:[1]    25806 killed     ./pin -t ./source/tools/ManualExamples/obj-intel64/inscount0.so -- /bin/ls

이는 리눅스의 보안 설정으로 인한 문제이다. Pin은 target process에 attach하는데, 일반적인 경우에 이는 허용되어 있지 않다[6]. 부모 프로세스가 자식 프로세스에 attach하는 경우만 허용된다. 예외를 허용하기 위해서는 /proc/sys/kernel/yama/ptrace_scope에 0을 입력해주어야 한다. 0, 1, 2, 3의 값이 들어갈 수 있으며, 각각의 의미는 다음과 같다.

0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
    process running under the same uid, as long as it is dumpable (i.e.
    did not transition uids, start privileged, or have called
    prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is
    unchanged.

1 - restricted ptrace: a process must have a predefined relationship
    with the inferior it wants to call PTRACE_ATTACH on. By default,
    this relationship is that of only its descendants when the above
    classic criteria is also met. To change the relationship, an
    inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
    an allowed debugger PID to call PTRACE_ATTACH on the inferior.
    Using PTRACE_TRACEME is unchanged.

2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
    with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.

3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via
    PTRACE_TRACEME. Once set, this sysctl value cannot be changed.

5. Instruction Address Trace (Instruction Instrumentation)
코드 실행할 때 발생하는 모든 instruction address를 출력하는 예제이다. Pin 2.14 User Guide[2]에서 동일한 소스 코드를 볼 수 있으며, API와 매크로를 클릭하면 상세 설명을 볼 수 있다. 이를 참고하면 코드를 쉽게 이해할 수 있다.

#include <stdio.h>
#include "pin.H"

FILE * trace;

// This function is called before every instruction is executed
// and prints the IP
VOID printip(VOID *ip) { fprintf(trace, "%p\n", ip); }

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
    // Insert a call to printip before every instruction, and pass it the IP
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);
}

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
    fprintf(trace, "#eof\n");
    fclose(trace);
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    PIN_ERROR("This Pintool prints the IPs of every instruction executed\n" 
              + KNOB_BASE::StringKnobSummary() + "\n");
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    trace = fopen("itrace.out", "w");
    
    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);

    // Register Fini to be called when the application exits
    PIN_AddFiniFunction(Fini, 0);
    
    // Start the program, never returns
    PIN_StartProgram();
    
    return 0;
}

1) main()
우선 instruction address를 출력할 파일을 연다. 그 다음, PIN_INIT()으로 Pin을 초기화한다. INS_AddInstrumentFunction() 함수를 호출해 instruction 단위로 instrument할 것임을 선언한다. 이 때, instruction마다 호출하는 함수는 Instruction이다. PIN_AddFiniFunction()를 호출해 프로그램이 종료될 때 호출할 함수를 등록한다. 그리고 마지막으로 PIN_StartProgram()을 호출해 프로그램을 시작한다.
2) printip()
printip 함수는 인자로 주어지는 instruction address를 파일에 출력한다.
3) Instruction()
INS_InsertCall() 함수를 호출해, 각 instruction 앞에(IPOINT_BEFORE), printip를 호출할 것임을 선언한다. IARG_INST_PTR를 인자로 전달한다. (IARG_END 앞까지가 함수에 실제로 전달되는 인자이다.)

 
6. Detaching Pin from the Application

#include <stdio.h>
#include "pin.H"
#include <iostream>

// This tool shows how to detach Pin from an 
// application that is under Pin's control.

UINT64 icount = 0;

#define N 10000
VOID docount() 
{
    icount++;

    // Release control of application if 10000 
    // instructions have been executed
    if ((icount % N) == 0) 
    {
        PIN_Detach();
    }
}
 
VOID Instruction(INS ins, VOID *v)
{
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}

VOID ByeWorld(VOID *v)
{
    std::cerr << endl << "Detached at icount = " << N << endl;
}

/* ===================================================================== */
/* Print Help Message                                                    */
/* ===================================================================== */

INT32 Usage()
{
    cerr << "This tool demonstrates how to detach Pin from an " << endl;
    cerr << "application that is under Pin's control" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;
}

/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char * argv[])
{
    if (PIN_Init(argc, argv)) return Usage();

    // Callback function to invoke for every 
    // execution of an instruction
    INS_AddInstrumentFunction(Instruction, 0);
    
    // Callback functions to invoke before
    // Pin releases control of the application
    PIN_AddDetachFunction(ByeWorld, 0);
    
    // Never returns
    PIN_StartProgram();
    
    return 0;
}

main 함수에서 Pin이 target 프로세스에서 detach될 때 호출할 함수(ByeWorld)를 지정해준다(PIN_AddDetachFunction). Pintool이 instruction count를 세다가, 10000개를 넘으면 PIN_Detach()를 호출해 프로세스로부터 detach한다. 이 때 앞서 등록해둔 callback 함수인 ByeWorld가 호출되며 프로그램이 종료된다.

 
7. Build My Own Pintool
프로그래머가 새로운 Pintool을 개발해 빌드하는 방법은 다음과 같다[8, 9]. 예제로 주어지는 Pintool인 MyPinTool을 복사해 사용하길 추천한다. (./source/tools/MyPinTool) 소스 코드를 모두 작성했다고 가정한다.

1) Kit Directory Tree 안에서 빌드하기
해당 디렉토리 안에서 다음과 같이 빌드한다.

$make obj-intel64/YourTool.so

2) Kit Directory Tree 밖에서 빌드하기
자신이 만든 Pintool이 있는 디렉토리에서 다음과 같이 빌드한다.

$make PIN_ROOT=<path to Pin kit> obj-intel64/YourTool.so

 
8. Optimizing Pin tools [5]
고성능의 Pintool을 구현하는 것은 상당히 중요하다. 어떻게 구현하느냐에 따라 실행 시간에 큰 차이가 있기 때문이다.

Pintool Overhead는 세부적으로 다음과 같이 나뉜다.
total overhead
pin's overhead

[5]에서 고성능의 Pintool을 구현하는 팁 세 가지를 제시하고 있다.
(실제로 슬라이드를 보는 것이 이해가 더 빠르다.)

1) Reducing Work in Analysis Routines
가능하다면 연산을 analysis routine에서 하기보다는 instrumentation routine에서 하는 것이 좋다.
2) Reduce Analysis Calls Frequency
가능하다면 큰 granularity에서 instrument함으로써, analysis call의 호출 빈도를 최소화하는 것이 좋다.
3) Reducing Work for Analysis Transitions
Analysis routine에서의 연산을 최소화한다.
Analysis routine에서의 연산 최소화는 다음과 같은 방법으로 할 수 있다.
– Analysis routine으로 넘기는 인자 개수를 줄인다.
– Analysis routine을 inline으로 처리할 수 있도록 코딩한다.
– 조건부 instrumentation을 적용한다.
(if, then으로 나누어, 자주 수행되는 것을 if 부분에 삽입하고, 가끔 수행되는 연산을 then에 삽입한다.)

References:
[1] Pin, https://software.intel.com/en-us/articles/pintool
[2] Pin 2.14 User Guide, https://software.intel.com/sites/landingpage/pintool/docs/67254/Pin/html/
[3] Robert Cohn and C-K Luk, Instrumentation of Linux Programs with Pin
[4] Pin Tutorial, https://sites.google.com/site/pintutorial/
[5] Michal Nir Gross and Benjamin Kemper, Pin: Intel’s Dynamic Binary Instrumentation Engine, ISCA’13, https://sites.google.com/site/pintutorial/home/isca40
[6] Yama, The Linux Kernel Archives, https://www.kernel.org/doc/Documentation/security/Yama.txt
[7] Pin Modules, Pin, https://software.intel.com/sites/landingpage/pintool/docs/67254/Pin/html/modules.html
[8] Pin’s makefile Infrastructure, Pin 2.14 User Guide, https://software.intel.com/sites/landingpage/pintool/docs/67254/Pin/html/index.html#MAKEFILES
[9] Building Your Own Tool, Pin 2.14 User Guide, https://software.intel.com/sites/landingpage/pintool/docs/67254/Pin/html/index.html#BUILDINGTOOLS

Advertisements
Tagged with: , , , ,
Posted in 2) Computer Engineering
2 comments on “Pin
  1. […] pintool을 빌드한 다음, 다음과 같이 사용 가능하다. […]

  2. […] Pin 사용법에 대해 공부하고 정리함. […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

누적 방문자 수
  • 88,328 hits
%d bloggers like this: