지금에야 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


by dumpcookie 2017. 3. 10. 17:35
| 1 |