Linux에서 정적 라이브러리 조작하기
Linux 환경에서 정적(static) 라이브리러를 조작하는 방법을 간단히 공유한다.
동기
외부 업체로부터 소스 코드는 받지 않고, C/C++로 빌드된 정적(static) 라이브러러만 받을 수 있는데, 이 라이브러리에서 특정 함수만 내가 작성한 함수로 대체하고 싶은 상황이 발생하였다.
이 경우 어차피 static 라이브러리는 object 파일의 집합일 뿐이므로, 라이브러리를 오브젝트 파일들로 해체하여 오브젝트 파일을 수정한 후에 다시 라이브러리로 만들면 원하는 바를 달성할 수 있다.
그런데 많은 S/W 엔지니어가 라이브러리에서 함수 교체는 불가능할 것으로 생각하고 시도조차 하지 않는 경우가 많은데, 사실 간단한 방법으로 가능하고, 이 글에서 간단히 그 방법을 공유한다.
툴체인
아래 예에서는 호스트 툴체인을 이용하였다. 만약에 다른 툴체인을 사용하는 경우라면 ar
, objcopy
툴을 해당 툴체인에 맞게 변경하면 된다. (예: llvm-ar
, llvm-objcopy
등)
함수 교체하기
- 먼저
ar
툴로 라이브러리 파일(.a)로부터 오브젝트 파일(.o)들을 추출한다.
아래 예와 같이 ar 툴을 실행하면 해당 라이브러리를 구성하였던 오브젝트 파일들을 얻을 수 있다.$ ar x {라이브러리_파일_이름}
- 대체하려는 함수를 포함한 오브젝트 파일을 찾는다.
이를 위해서는 아래 예와 같이nm
툴로 찾을 수 있다.$ nm -o {라이브러리_파일_이름} | grep {함수_이름}
해당 함수를 실제로 구현한 오브젝트 파일에서는 함수 이름 앞에 “T” 마크가 있고, 이 함수를 사용하는 오브젝트 파일에서는 함수 이름 앞에 “U” 마크가 있으므로 “T” 마크가 있는 오브젝트 파일을 찾으면 된다.
참고로 이와 같이 nm 툴의 결과에서 함수 이름 앞에 붙은 정보는 바인딩 타입을 나타내는데, 각 의미는 다음과 같다.- T / t: Text 섹션에 정의된 전역/로컬 심볼
- U: Undefined, 이 심볼은 현재 오브젝트 파일에서 참조되지만 그 구현은 이 파일 안에 존재하지 않음
- B / b: 초기화되지 않은 BSS 섹션(.bss)의 전역/로컬 심볼
- D / d: 초기화된 데이터 섹션(.data)의 전역/로컬 심볼
- R / r: 읽기 전용 데이터 섹션(.rodata)의 전역/로컬 심볼
- W: 약한 심볼(weak symbol), 다른 심볼로 대체 가능
- 대체하려는 함수를 포함한 오브젝트 파일에서 해당 함수를 제거한다.
아래 예와 같이objcopy
툴로 --strip-symbol 옵션을 사용하여 오브젝트 파일에서 원하는 함수를 제거할 수 있다.$ objcopy --strip-symbol={제거할_함수_이름} {오브젝트_파일_이름}
참고로 한 번에 여러 개의 함수를 제거하려면 --strip-symbol 옵션을 여러 번 사용하면 된다.
- 제거한 함수 이름으로 새로운 함수를 구현하여 컴파일하고, 빌드된 오브젝트 파일을 라이브러리에서 추출한 오브젝트가 있는 디렉토리에 복사한다.
이 과정이 귀찮으면 생략하고, 빌드되는 아무 파일에 제거했던 함수를 새로운 함수로 다시 구현해도 어차피 빌드에 포함되므로 문제없다.
- 이후
ar
툴로 아래 예와 같이 오브젝트 파일(.o) 들로 다시 라이브러리 파일(.a)을 생성하면 된다.$ ar rcs {신규_라이브러리_파일_이름} *.o
함수 이름 변경하기
참고로 오브젝트 파일에서 원하는 함수의 이름만 변경할 수도 있다.
아래 예와 같이 objcopy
툴로 --redefine-sym 옵션을 사용하여 오브젝트 파일에서 타겟 함수 이름을 변경할 수 있다.
$ objcopy --redefine-sym {기존_함수_이름}={신규_함수_이름} {오브젝트_파일_이름}
참고로 한 번에 여러 개의 함수 이름을 변경하고 싶으면 --redefine-sym 옵션을 여러 번 사용하면 된다.