검색결과 리스트
PIC에 해당되는 글 2건
- 2017.03.14 mpg123의 x86상에서 text relocation 문제해결
- 2017.03.10 x86 공유 라이브러리의 TEXTRELS 오류 찾기
최근의 일련의 x86 어셈블리어 관련 글은 클론 리플레이어상에서 x86을 지원하기 위해 할 수 있는게 무엇일까 하고 살펴보던 차에 썼던 것이다. 이런 저런 관련 정보를 찾아보고 테스트를 해보니, x86을 위해 빌드한 공유 라이브러리의 TEXTRELs 문제를 고치기 위해서는 MMX/SSE 등등의 지원을 위해 들어가있는 꽤 오래된 어셈블리어 소스를 고쳐야만 하는 문제였다.
https://sourceforge.net/p/mpg123/bugs/168/
소스를 살펴보다가 알게된 사실중 하나는, x264/ffmpeg/mplayer/libvpx 등등의 프로젝트에서 꽤 오래전에 누군가가 만들었던 소스를 서로 의도하지 않게(?) 공유하고 있었다는 사실이었다. 최초 누가 만들었는지 조차도 명확하지 않지만 그에 대한 언급이 소스에 있으며, 또 다른 것으로는, 어셈블리어로 있던 소스를 인라인 어셈블리로 바꾸면서 c 소스로 바뀐 경우, 혹은 그 반대로 c 소스를 어셈블러 소스로 변환시킨 후에 손으로 최적화 한 흔적들도 보였다.
아무튼 TEXTREL 문제를 고치기 위해 PIC 코드로 고쳐야 하는 부분은 많지 않았으나, 암호같던 어셈블리어가 조금씩 눈에 들어오니 고치는 것이 가능하겠다 싶어서 어셈블리어 공부(?)/테스트 및 삽질을 같이하여 TEXTREL 문제를 제거한 패치를 만들게 되었다.
패치는 https://github.com/wkpark/mpg123/tree/textrels 링크를 통해 받고 테스트해볼 수 있다.
일단 간단히 테스트를 해볼 수 있는 것이 mpg123 -t 명령을 통해서 입력 mp3를 받아 디코딩하여 -w foo.wav 옵션으로 WAV 파일로 디코딩된 파일을 들어보는 것이었다. 패치를 거듭 살펴보고 최종적으로 빌드후 실행해서 core 덤프가 나면 어디가 문제인지 gdb로 몇차례 살펴보고, 더이상 core dump 안되고 실행이 된 후 디코딩된 wav파일이 정상적으로 소리가 들렸으며, readelf -d 명령을 통해서 TEXTREL이 더이상 보이지 않는 것을 확인하였다.
$ readelf -d src/libmpg123/.libs/libmpg123.so Dynamic section at offset 0x56e20 contains 27 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libm.so.6] 0x00000001 (NEEDED) Shared library: [libdl.so.2] 0x00000001 (NEEDED) Shared library: [libc.so.6] 0x0000000e (SONAME) Library soname: [libmpg123.so.0] 0x0000000c (INIT) 0x2f08 0x0000000d (FINI) 0x441b4 0x00000019 (INIT_ARRAY) 0x57a74 0x0000001b (INIT_ARRAYSZ) 4 (bytes) 0x0000001a (FINI_ARRAY) 0x57a78 0x0000001c (FINI_ARRAYSZ) 4 (bytes) 0x6ffffef5 (GNU_HASH) 0x138 0x00000005 (STRTAB) 0x1160 0x00000006 (SYMTAB) 0x5f0 0x0000000a (STRSZ) 2954 (bytes) 0x0000000b (SYMENT) 16 (bytes) 0x00000003 (PLTGOT) 0x58000 0x00000002 (PLTRELSZ) 848 (bytes) 0x00000014 (PLTREL) REL 0x00000017 (JMPREL) 0x2bb8 0x00000011 (REL) 0x1f48 0x00000012 (RELSZ) 3184 (bytes) 0x00000013 (RELENT) 8 (bytes) 0x6ffffffe (VERNEED) 0x1e58 0x6fffffff (VERNEEDNUM) 3 0x6ffffff0 (VERSYM) 0x1cea 0x6ffffffa (RELCOUNT) 392 0x00000000 (NULL) 0x0 $ readelf -d src/libmpg123/.libs/libmpg123.so |grep TEXT $
이렇게 빌드된 mpg123 라이브러리를 클론 리플레이어에 x86 jni 라이브러리로 넣고 x86/x86_64 AOSP+vmware에서 테스트해보니, 전에는 TEXTRELs 문제로 정지되던 것이 이제는 실행이 잘 되었다.
이 패치는 mpg123 sourceforge 사이트에 보고하였으며, 몇번의 피드백과 테스트를 통해 MMX/3DNow/3dnowext/SSE 디코더를 모두 지원하게 되었으며, 고친 소스는 위에서 언급한 github 사이트를 통해 확인할 수 있다.
x86 어셈블리어 배우기 - Text relocation 문제 재현 및 수정. (0) | 2017.03.11 |
---|---|
x86 공유 라이브러리의 TEXTRELS 오류 찾기 (0) | 2017.03.10 |
지금에야 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