본문 바로가기
컴퓨터 사이언스

[Linking] Library interpositioning

by 제크와 죠세핀 2019. 5. 1.
반응형

공유 라이브러리 함수를 호출하려고 할 때 중간에 가로채면서(!) 내가 만든 다른 함수를 실행하게 하는 방법이다.

Interpositioning Compile time / Link time / Load & run time에서 모두 가능하다.

Interpositioning을 하면 보안과 monitoring, profiling에 좋다고 한다. 보안은 왜 그런 지 모르겠지만, 함수 프로파일링에는 공감 : 함수가 몇 번 호출되는 지, malloc tracing하는 데 도움이 된다.

 

1)    Compile time에서의 interpositioning

전처리기를 이용한 interpositioning으로 생각하면 된다.

헤더 내에 #define malloc(size) mymalloc(size) 와 같이 선언을 해버리면 c코드에서 이 헤더를 include했을 때 전처리기를 이용해서 mallocmymalloc으로 바꿔버림. 이걸 이용해서 libc.so에 있는 malloc 대신 내가 만든 mymalloc을 실행하는 것이 compile time에서 가능하다.

예시 파일 : 3(int.c, malloc.h, mymalloc.c)가 있다.

int.c 는 평범하게 헤더를 include하고 malloc, free를 하는 main 코드이며

malloc.h#define malloc(size) mymalloc(size) 이런식으로 선언해두고 밑에 mymalloc(size)함수의 선언을 해둔 코드,

그리고 mymalloc.c은 컴파일 할 때 -DCOMPILETIME이라는 옵션이 있으면 mymalloc함수를 정의하는 코드이다.( #ifdef, #endif 전처리문(모두 컴파일 전에 처리됨)을 이용)

 

** 이때 컴파일, 링킹하는 방법은 다음과 같다.

gcc -Wall -DCOMFILETIME -c mymalloc.c

gcc -Wall -I. -o intc int.c mymalloc.o

이렇게 해라고 되어 있는데 생소한 gcc 옵션에 대해 살펴보면

(1)   -Wall 모든 모호한 코딩에 대해서 경고를 보내는 옵션이라고 한다.

(2)   -DCOMFILETIME “-D”라는 옵션으로 #define을 할 수 있는 방법이다. 이걸 이용하면 #ifdef, #endif를 사용할 때 그전에 #define 하는 걸 gcc에서 command로 지시할 수 있다.

(3)   -I.라는 건 “-I”가 내가 “”형태로 include한 헤더파일의 위치를 구체적으로 명시하는 그런 옵션인데 -I.는 아마도 현재 폴더에서 찾으라는 의미인 것 같다.

 

2)    Link-time에서의 interpositioning

리눅스 정적 링커는 --wrap 플래그를 통해 link interpositioning을 지원한다.

--wrap,malloc이라고 하면 기존의 심볼 malloc로의 참조는 __wrap_malloc으로 되고, 심볼 __real_malloc의 참조가 심볼 malloc으로 결정된다사용방법은 -Wl,option이다. option을 링커에 전달한다고 생각하면 된다.

실행 예시 :

gcc -DLINKTIME -c mymalloc.c

gcc -c int.c

gcc -Wl,--wrap,malloc -Wl,--wrap,free -o intl int.o mymalloc.o         

 

3)    Run-time에서의 interpositioning

런타임에 함수 호출을 가로채는 방법인데, 동적 링커의 LD_PRELOAD 환경변수를 활용한다. 이 환경변수에 공유라이브러리가 설정되어 있으면 동적 링커는 제일 먼저 LD_PRELOAD에 설정된 라이브러리를 먼저 검사한다고 한다. LD_PRELOAD에다가 내가 만든 다이나믹 링커를 넣어주면 libc.so에 있는 malloc 대신 내가 만든 malloc 먼저 검사하니까 내 함수를 호출 할 수 있다.

이 때, malloc 내에서 libc.somalloc을 실행하는데 그러면서 dlsym을 통해 libc.somalloc을 함수포인터로 받아야한다.

** dlsym이란? 동적 라이브러리(.dll)를 열어서 symbol의 시작 번지를 return하는 함수이다.

실행 예시 : 

gcc -DRUNTIME -shared -fpic -o mymalloc.so mymalloc.c -ldl

gcc -o intr int.c

LD_PRELOAD=./mymalloc.so ./intr

보통은 dlopen과 동시에 쓰는 것 같은데 dlsym(RTLD_NEXT, “malloc”)이라고 쓰면 현재 라이브러리 다음으로, 검색했을 때 나오는 함수를 찾으란 의미라고 한다. (, LD_PRELOAD를 쓰면 현재 library(mymalloc3.so) 다음에 나오는 libc.so에 있는 malloc을 저 함수가 찾아주는 것.)

오늘 공부하면서 낯설었던 것은 함수 포인터를 쓰는 방법이었다. void (*fp)(); 이런 식으로 써주면 맨 앞이 반환 값의 자료형, 함수포인터 이름, 매개변수 이런 식으로 되는 것. 함수포인터는 괄호를 붙여줘야하는 듯하다. 예시에서는 (void *)malloc의 반환형이니까 그렇게 한 것 같고 (*mallocp)라는 malloc pointer를 선언한듯하다.

** 함수 포인터를 쓰는 이유에 대해서는 좀 더 공부가 필요할 듯하다. 일단은 함수도 사실은 변수와 마찬가지로 메모리 상의 특정 지점이니까 자연스럽게 포인터로 쓸 수 있다고 받아들이자.

 

참고

코드 : https://koyo.kr/post/csapp-linking-2/

'Computer Systems A Programmer's Perspective (3rd Edition)'

반응형

댓글