Android C 언어init 함수와 constructor 속성 및.init/.init_array절 탐색

C 언어를 아는 프로그래머들은 일부 코드가 so 또는 실행 가능한 파일을 불러올 때 다른 함수보다 먼저 실행할 수 있는 두 가지 방법을 알고 있다. 하나는void 를 정의하는 것이다.init (void) 함수, 다른 하나는 함수 뒤에 constructor 속성을 설명하는 것입니다.그렇다면 이 두 가지 방식은 집행할 때 어떤 차이가 있습니까?선착순은요?ELF 파일 형식을 아는 사람들은 파일의 위치가 어떻게 다른지 물어볼까요?이 글은 바로 이 문제들을 풀기 위한 것이다.
우선 ELF 파일의 형식을 알아야 합니다. 여기는 잔소리가 없습니다. 모르는 사람은 찾아보세요.
다음은 Android 프로젝트의 C/C++ 코드에 다음 줄을 추가하는 예입니다.
........

#ifdef __cplusplus
extern "C" {
#endif

void _init(void){mlog_info("_init enter");}

#ifdef __cplusplus
}
#endif

void __attribute__((constructor)) myConstructor(void){mlog_info("myConstructor enter
");} ........

제가 컴파일한 것은libcheckcert입니다.so 파일, 핸드폰에 올려놓고 실행한 결과:
........

12-13 11:04:46.603: I/BRIAN(12203): _init enter
12-13 11:04:46.603: I/BRIAN(12203): myConstructor enter

........
_init 함수가 가장 먼저 실행되는데 왜 그럴까요?ELF 파일을 아는 사람들은 모두 알고 있다.init 및.init_array 이 두 절은 ELF 파일이 불러올 때 초기화하는 데 사용됩니다. 그러면 그것들과init 함수와 constructor 속성은 어떤 관계가 있습니까?다음은 readelf와 IDA pro를 통해 확인할 것입니다. 먼저readelf -d libcheckcert.so ELF의 dynamic 세그먼트를 보려면 다음과 같이 하십시오.
BriansdeMacBook-Pro:armeabi-v7a brian$ arm-linux-androideabi-readelf -d libcheckcert.so 

Dynamic section at offset 0x19b80 contains 27 entries:
  Tag        Type                         Name/Value
 0x00000003 (PLTGOT)                     0x1ad84
 0x00000002 (PLTRELSZ)                   1248 (bytes)
 0x00000017 (JMPREL)                     0x4200
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0x31a8
 0x00000012 (RELSZ)                      4184 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   390
 0x00000006 (SYMTAB)                     0x148
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x1028
 0x0000000a (STRSZ)                      6825 (bytes)
 0x00000004 (HASH)                       0x2ad4
 0x00000001 (NEEDED)                     Shared library: [liblog.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]
 0x0000000e (SONAME)                     Library soname: [libcheckcert.so]
 0x0000000c (INIT)                       0x4f9c
 0x0000001a (FINI_ARRAY)                 0x1a658
 0x0000001c (FINI_ARRAYSZ)               8 (bytes)
 0x00000019 (INIT_ARRAY)                 0x1a660
 0x0000001b (INIT_ARRAYSZ)               20 (bytes)
 0x0000001e (FLAGS)                      BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x00000000 (NULL)                       0x0
INIT 및 INITARRAY 섹션의 주소는 각각 0x4f9c와 0x1a660이며, IDA pro를 열어 해당 위치의 코드를 확인합니다.
.text:00004F9C ; =============== S U B R O U T I N E =======================================
.text:00004F9C
.text:00004F9C ; Attributes: bp-based frame
.text:00004F9C
.text:00004F9C                 EXPORT _init
.text:00004F9C _init
.text:00004F9C
.text:00004F9C var_8           = -8
.text:00004F9C var_4           = -4
.text:00004F9C
.text:00004F9C                 STMFD           SP!, {R11,LR}
.text:00004FA0                 MOV             R11, SP
.text:00004FA4                 SUB             SP, SP, #8
.text:00004FA8                 LDR             R0, =(_GLOBAL_OFFSET_TABLE_ - 0x4FB4)
.text:00004FAC                 ADD             R0, PC, R0 ; _GLOBAL_OFFSET_TABLE_
.text:00004FB0                 MOV             R1, #4
.text:00004FB4                 LDR             R2, =(aBrian_1 - 0x1AD84)
.text:00004FB8                 ADD             R2, R2, R0 ; "BRIAN"
.text:00004FBC                 LDR             R3, =(a_initEnter - 0x1AD84)
.text:00004FC0                 ADD             R0, R3, R0 ; "_init enter"
.text:00004FC4                 STR             R0, [SP,#8+var_4]
.text:00004FC8                 MOV             R0, R1
.text:00004FCC                 MOV             R1, R2
.text:00004FD0                 LDR             R2, [SP,#8+var_4]
.text:00004FD4                 BL              __android_log_print
.text:00004FD8                 STR             R0, [SP,#8+var_8]
.text:00004FDC                 MOV             SP, R11
.text:00004FE0                 LDMFD           SP!, {R11,PC}
.text:00004FE0 ; End of function _init
init_array:0001A660 ; ===========================================================================
.init_array:0001A660
.init_array:0001A660 ; Segment type: Pure data
.init_array:0001A660                 AREA .init_array, DATA
.init_array:0001A660                 ; ORG 0x1A660
.init_array:0001A660                 DCD _Z13myConstructorv  ; myConstructor(void)
.init_array:0001A664                 DCD sub_4E90
.init_array:0001A668                 DCD sub_4EA8
.init_array:0001A66C                 DCD sub_4F04
.init_array:0001A670                 DCB    0
.init_array:0001A671                 DCB    0
.init_array:0001A672                 DCB    0
.init_array:0001A673                 DCB    0
.init_array:0001A673 ; .init_array   ends

위의 코드에서 우리가 정의한 함수를 실행하는 것을 볼 수 있습니다.init절은 바로...init 함수의 코드입니다.init_array절은 바늘수 그룹으로 항목마다 대응하는 코드가 하나씩 있어 일련의 초기화 작업을 할 수 있다.그럼 왜?init절의 코드가 먼저.init_array 절의 코드 실행은?이것은 링크의 코드를 보아야 합니다. 위치는 AOSP의bionic/linker 디렉터리에 있습니다. 여기는 안에 있는 작은 코드만 발췌합니다.
void soinfo::CallConstructors() {

   ........

   // DT_INIT should be called before DT_INIT_ARRAY if both are present.
   CallFunction("DT_INIT", init_func);
   CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);
}
선집행을 볼 수 있습니다.init 섹션의 코드를 순서대로 실행합니다.init_array의 각 코드 블록입니다.
여기까지는 다들 맞을 거예요init 함수, constructor 속성 및.init 섹션 및.init_array절의 대응 상황은 잘 아시죠?
다음은 제가 잘 모르는 부분을 말씀드리자면readelf로 ELF의 모든 기호 정보를 보면 알 수 있습니다.rel.dyn과.rel.plt에는 myConstructor 기호가 있는데, 한 유형은 RARM_ABS32 하나는 RARM_JUMP_SLOT.또한 IDA pro에서 myConstructor를 보면 코드 바디가 있습니다.text절, 하지만 발견할 수 있습니다.plt 및.got 섹션에는 myConstructor의 정의도 있습니다.이렇게 하면 myConstructor를 현시적으로 호출할 때마다 PLT를 통해 점프하고 GOT표에서 myConstructor가 TEXT 섹션에 있는 실제 주소를 찾아야 실행할 수 있습니다.하지만..init_array의 주소는 TEXT 섹션에 있는 실제 주소입니다. 초기화할 때 myConstructor를 호출할 때 PLT와 GOT 테이블을 통과할 필요가 없습니다.왜 그런지 모르겠어요.남겨 두었다가 나중에 해결합시다.
업데이트: 위의 문제는 컴파일러의 문제 때문입니다. 서로 다른 컴파일러가 컴파일한 ELF 파일은 그다지 같지 않습니다. 위에서 말한 이러한 상황은 LLVM 컴파일러가 컴파일한 것입니다. 만약arm-linux-androideabi-*를 사용한다면 myConstructor 기호는 오직 있습니다.text절에만 있습니다. 나타나지 않습니다.rel.dyn과.rel.plt 중.

좋은 웹페이지 즐겨찾기