검색결과 리스트
readelf에 해당되는 글 1건
- 2017.03.10 x86 공유 라이브러리의 TEXTRELS 오류 찾기
지금에야 64비트를 주로 쓰는 환경이므로 x86 32비트 환경에서 Text relocation문제가 크게 문제될 만한 사항은 아니지만, 안드로이드 6.0 이후로는 x86/arm 32비트의 경우, TEXTREL 문제가 있는 공유 라이브러리를 사용하는 경우 아예 실행조차 되지 않기때문에 (API 23 이상으로 빌드된 APK의 경우는 실행 안됨) 이 문제를 정리 차원에서 적도록 하겠다.
예를 들어 x264 / mpg123 / FFmpeg 등등의 꽤 널리 알려진 오픈소스 프로젝트의 경우, x86 32비트로 컴파일을 하면 TEXTRELs 문제가 있게 된다. 문제있는 공유라이브러리는 어떻게 찾을 수 있을까?
먼저 TEXTRELs 문제가 있는 라이브러리는 readelf, scanelf 등을 사용하여 찾을 수 있다.
예를 들어 FFmpeg의 libavcodec.so는 TEXTRELs 문제가 있으며 다음과 scanelf -T 명령을 이용하면 다음과 같은 식의 매우 긴 출력을 한다.
https://wiki.gentoo.org/wiki/Hardened/Textrels_Guide 참고
$ scanelf -T libavcodec.so TYPE TEXTRELS FILE libavcodec.so: (memory/data?) [0x845599] in (optimized out: previous put_cavs_qpel8_h_mmxext) [0x845560] libavcodec.so: (memory/data?) [0x8455EB] in (optimized out: previous put_cavs_qpel8_h_mmxext) [0x845560] libavcodec.so: (memory/data?) [0x845669] in (optimized out: previous avg_cavs_qpel8_h_mmxext) [0x845630] libavcodec.so: (memory/data?) [0x8456BB] in (optimized out: previous avg_cavs_qpel8_h_mmxext) [0x845630] libavcodec.so: (memory/data?) [0x845771] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x84577B] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x8457A8] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x8457C7] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x8457D1] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x8457FE] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] libavcodec.so: (memory/data?) [0x84581D] in (optimized out: previous avg_cavs_qpel8or16_v1_mmxext) [0x845700] ....
공유 라이브러리는 이미 리로케이션이 되어서 구체적으로 어떤 오브젝트 파일의 오류가 있는지는 알 수 없으나 TEXTRELs 문제가 있는지 없는지 정도는 간단히 파악할 수 있다.
readelf -a 혹은 readelf -d 명령을 사용하면 다음과 같은 더욱 요약된 정보를 볼 수 있다. -d 옵션은 --dynamic 옵션과 같고, dynamic 섹션 정보를 보여준다. -a 옵션은 모든 정보를 출력한다.
$ readelf -d x86/libavcodec.so Dynamic section at offset 0xc3d81c contains 36 entries: Tag Type Name/Value 0x00000003 (PLTGOT) 0xc3eb58 0x00000002 (PLTRELSZ) 2328 (bytes) 0x00000017 (JMPREL) 0x57748 0x00000014 (PLTREL) REL 0x00000011 (REL) 0x20b90 0x00000012 (RELSZ) 224184 (bytes) 0x00000013 (RELENT) 8 (bytes) 0x6ffffffa (RELCOUNT) 27939 0x00000006 (SYMTAB) 0x158 0x0000000b (SYMENT) 16 (bytes) 0x00000005 (STRTAB) 0xba18 0x0000000a (STRSZ) 60344 (bytes) 0x00000004 (HASH) 0x1a5d0 0x00000001 (NEEDED) Shared library: [libavutil.so] 0x00000001 (NEEDED) Shared library: [libswresample.so] 0x00000001 (NEEDED) Shared library: [libz.so] 0x00000001 (NEEDED) Shared library: [libmp3lame.so] 0x00000001 (NEEDED) Shared library: [libstdc++.so] 0x00000001 (NEEDED) Shared library: [libm.so] 0x00000001 (NEEDED) Shared library: [libc.so] 0x00000001 (NEEDED) Shared library: [libdl.so] 0x0000000e (SONAME) Library soname: [libavcodec.so] 0x0000001a (FINI_ARRAY) 0xc3cf94 0x0000001c (FINI_ARRAYSZ) 8 (bytes) 0x00000019 (INIT_ARRAY) 0xc3e818 0x0000001b (INIT_ARRAYSZ) 4 (bytes) 0x00000016 (TEXTREL) 0x0 0x00000010 (SYMBOLIC) 0x0 0x0000001e (FLAGS) SYMBOLIC TEXTREL BIND_NOW 0x6ffffffb (FLAGS_1) Flags: NOW 0x6ffffff0 (VERSYM) 0x1f41c 0x6ffffffc (VERDEF) 0x20b34 0x6ffffffd (VERDEFNUM) 1 0x6ffffffe (VERNEED) 0x20b50 0x6fffffff (VERNEEDNUM) 2 0x00000000 (NULL) 0x0
여기서 0x00000016 (TEXTREL) 0x0
에 해당하는 부분이 TEXTREL 문제가 있는 라이브러리라는 것을 보여준다.
scanelf 혹은 readelf를 통해서 TEXTRELs문제를 어느정도 파악했다고 하자. 그렇다면 구체적으로 어떤 소스가 TEXTRELs문제를 일으키는지는 어떻게 알 수 있을까?
우선 이 문제를 원본 소스가 있을때를 가정으로 한다면 의외로 어렵지 않게 찾아낼 수 있다.
x86 32비트 라이브러리 하나를 예로 들어보자. 여기서는 FFmpeg라이브러리를 골라보았다. FFmpeg 라이브러리를 32비트 x86용 공유 라이브러리 형태로 컴파일한 후에, 각각의 오브젝트 파일에 대해서 살펴보도록 하자.
readelf를 사용하면 공유라이브러리뿐만 아니라 오브젝트파일의 정보도 볼 수 있는데, 특히 strip되지 않는 오브젝트 파일의 경우에는 다음과 같은 정보를 볼 수 있다. FFmpeg 라이브러리의 avcodec 모듈 소스중에 하나인 gif.c를 컴파일해서 얻어진 오브젝트 파일에 대한 정보를 readelf -r 명령으로 보면 다음과 같다.
Relocation section '.rel.text.pick_palette_entry' at offset 0xe98c contains 5 entries: Offset Info Type Sym.Value Sym. Name 0000000c 00004102 R_386_PC32 00000000 __x86.get_pc_thunk.bx 00000012 0000420a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 0000001f 00004303 R_386_GOT32 00000000 __stack_chk_guard 000000cb 00004303 R_386_GOT32 00000000 __stack_chk_guard 000000f4 00004402 R_386_PC32 00000000 __stack_chk_fail_local Relocation section '.rel.text.gif_encode_close' at offset 0xe9b4 contains 6 entries: Offset Info Type Sym.Value Sym. Name 00000003 00004102 R_386_PC32 00000000 __x86.get_pc_thunk.bx 00000009 0000420a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 0000001f 00004504 R_386_PLT32 00000000 av_freep 0000002a 00004504 R_386_PLT32 00000000 av_freep 00000042 00004604 R_386_PLT32 00000000 av_frame_free 0000004a 00004504 R_386_PLT32 00000000 av_freep Relocation section '.rel.text.put_bits' at offset 0xe9e4 contains 4 entries: Offset Info Type Sym.Value Sym. Name 0000000a 00004102 R_386_PC32 00000000 __x86.get_pc_thunk.bx 00000010 0000420a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_ 0000006a 00002a09 R_386_GOTOFF 00000000 .LC2 0000007a 00004704 R_386_PLT32 00000000 av_log ...(생략)
복잡해보이지만, 이 오브젝트 파일에는 그 심볼 타입이 R_386_PC32
, R_386_GOTPC
, R_386_GOTOFF
, R_386_PLT32
등등이 있다는 것 정도를 알 수 있다. c언어로 작성된 소스코드로 얻어진 오브젝트 파일은 (이 경우 -fPIC 옵션으로 컴파일 되어) TEXRELs문제가 없는 경우이다. 반면, x86에 최적화된 x86/fft.asm 어셈블리어 소스를 컴파일해서 얻은 오브젝트 파일의 경우에는 readelf -r 명령 출력이 다음과 같으며, 이 경우는 TEXTRELs 문제가 있는 경우이다.
$ readelf -r fft.o Relocation section '.rel.text' at offset 0x2db4 contains 273 entries: Offset Info Type Sym.Value Sym. Name 00000015 0000a201 R_386_32 00000000 .rodata 0000001e 0000a201 R_386_32 00000000 .rodata 0000003a 0000a201 R_386_32 00000000 .rodata 00000052 0000a201 R_386_32 00000000 .rodata 000000a6 0000a201 R_386_32 00000000 .rodata 000000db 0000a201 R_386_32 00000000 .rodata 000000e4 0000a201 R_386_32 00000000 .rodata 00000100 0000a201 R_386_32 00000000 .rodata 00000118 0000a201 R_386_32 00000000 .rodata 00000148 0000a201 R_386_32 00000000 .rodata 00000150 0000a201 R_386_32 00000000 .rodata 000001f1 0000a201 R_386_32 00000000 .rodata 00000232 0000a201 R_386_32 00000000 .rodata 0000023a 0000a201 R_386_32 00000000 .rodata 0000028a 0000a701 R_386_32 00000000 ff_cos_32 00000292 0000a701 R_386_32 00000000 ff_cos_32 00000393 0000a201 R_386_32 00000000 .rodata 000003d3 0000a201 R_386_32 00000000 .rodata 00000411 0000a201 R_386_32 00000000 .rodata ...(중략) 0000176c 0000a701 R_386_32 00000000 ff_cos_32 0000179e 0000a801 R_386_32 00000000 ff_cos_64 000017d0 0000a901 R_386_32 00000000 ff_cos_128 00001800 0000aa01 R_386_32 00000000 ff_cos_256 00001830 0000ab01 R_386_32 00000000 ff_cos_512 00001860 0000ac01 R_386_32 00000000 ff_cos_1024 00001890 0000ad01 R_386_32 00000000 ff_cos_2048 000018c0 0000ae01 R_386_32 00000000 ff_cos_4096 ...(생략)
위에서 나오는 ff_cos_* 심볼은 fft.asm 소스상에서 .data 섹션에 들어가는 여러 상수들이다. 이 데이터의 타입이 R_386_32
라고 나오고 있다. 바로 이러한 경우에 TEXTREL문제가 나게 되는 것이다. 다른 형식의 타입(GOTOFF,GOTPC
등등)이 보이지를 않고 있다.
readelf뿐만 아니라, 오브젝트 파일에 대한 정보는 objdump를 사용해도 알 수 있는데 objdump -r 명령으로 fft.o 파일을 살펴보면 다음과 같고 readelf 명령의 결과와 거의 대동소이하다. (물론 이 경우 역시 strip되면 아무 정보도 나오지 않는다.)
$ objdump -r fft.o fft.o: file format elf32-i386 RELOCATION RECORDS FOR [.text]: OFFSET TYPE VALUE 00000015 R_386_32 .rodata 0000001e R_386_32 .rodata 0000003a R_386_32 .rodata 00000052 R_386_32 .rodata 000000a6 R_386_32 .rodata 000000db R_386_32 .rodata 000000e4 R_386_32 .rodata 00000100 R_386_32 .rodata 00000118 R_386_32 .rodata 00000148 R_386_32 .rodata 00000150 R_386_32 .rodata 000001f1 R_386_32 .rodata 00000232 R_386_32 .rodata 0000023a R_386_32 .rodata 0000028a R_386_32 ff_cos_32 00000292 R_386_32 ff_cos_32 00000393 R_386_32 .rodata 000003d3 R_386_32 .rodata 00000411 R_386_32 .rodata ...(생략)
이렇게만 봐서는 어셈블리어 소스 fft.asm으로 얻은 오브젝트 파일과, 일반 c언어 소스로 얻은 gif.o 오브젝트 파일의 차이점을 발견하기 어렵지만, fft.o는 TEXTRELs문제가 있고, gif.o는 이 문제가 없는 경우이다. 그렇다면 그것을 어떻게 판별할 수 있을까?
gcc의 다음 명령을 사용하면 x86 / 32비트 공유 라이브러리를 만들 수 있다. (내부적으로 ld 명령이 실행된다.)
gcc -m32 -shared -fPIC -Wl,-soname,libmy.so -o /tmp/libmy.so -lc foo.o bar.o
위 명령은 foo.o bar.o 오브젝트 파일을 공유라이브러리 libmy.so라는 이름으로 생성시키는 명령이다. (-m32 옵션은 32비트 옵션, -shared 옵션은 ld명령으로 공유라이브러리를 생성하라는 옵션. -o는 출력 파일 지정 옵션)
이 명령을 응용하면, gcc의 -Wl,--warn-shared-textrel
옵션을 함께 써서 TEXTRELs 문제를 가지는 것으로 추측되는 오브젝트 파일을 검사할 수 있다.
예를 들어 위에서 예를 들어 사용했던 gif.o 오브젝트 파일을 단독적으로 사용해서 공유 라이브러리를 만드는 명령은 다음과 같다.
(공유 라이브러리 이름은 임의로 my.so라고 하였고, 출력은 /tmp/mygif.so,
$ gcc -Wl,--warn-shared-textrel -m32 -shared -Wl,-soname,my.so -o /tmp/my.so gif.o
이 명령을 실행하면 아무런 오류도 없이 /tmp/my.so 공유 라이브러리가 생성되며, readelf -d 명령으로 TEXTRELS 문제를 살펴봐도 아무 문제가 없다.
반면 fft.o 오브젝트 파일에 대해 동일한 방식으로 임시 공유라이브러리를 생성하면 어떻게 될까?
$ gcc -Wl,--warn-shared-textrel -m32 -shared -Wl,-soname,my2.so -o /tmp/my2.so fft.o fft.o: warning: relocation in readonly section `.text'
이 경우 위와 같은 오류가 나며 my2.so 공유 라이브러리가 생성되었고, readelf -d 명령으로 TEXTREL 문제를 살펴보면 다음과 같이 TEXTREL 오류가 있음을 알 수 있다.
$ readelf -d /tmp/my2.so |grep TEXT 0x00000016 (TEXTREL) 0x0
종종 공유라이브러리 생성이 안될 수도 있는데, 오브젝트 파일에 심볼을 발견할 수 없는 경우, 다른 오브젝트 파일이 필요한 경우가 그러한 경우이며, 이러한 오류 등으로 링크가 안되어 공유 라이브러리가 생성이 안될 수도 있지만 여전히 리로케이션 문제가 있다는 워닝은 볼 수 있다.
$ gcc -Wl,--warn-shared-textrel -m32 -shared -Wl,-soname,my2.so -o /tmp/my2.so /tmp/foo.o /usr/bin/ld: /tmp/foo.o: relocation R_386_GOTOFF against undefined symbol `print' can not be used when making a shared object /usr/bin/ld: final link failed: Bad value collect2: error: ld returned 1 exit status
mpg123의 x86상에서 text relocation 문제해결 (0) | 2017.03.14 |
---|---|
x86 어셈블리어 배우기 - Text relocation 문제 재현 및 수정. (0) | 2017.03.11 |
RECENT COMMENT