Lenguaje Android C _init función y atributo constructor y exploración de sección .init / .init_array

Los programadores que entienden el lenguaje C saben que hay dos formas de permitir que se ejecute algún código antes que cualquier otra función cuando se carga el archivo ejecutable so. Una es definir una función void _init (void), y la otra es La propiedad del constructor se declara después de la función. Entonces, ¿hay alguna diferencia en la ejecución de estos dos métodos? Cual es el orden Las personas que entienden el formato de archivo ELF preguntarán cuál es la diferencia en su posición en el archivo. Este artículo responderá estas preguntas.

En primer lugar, debe saber sobre el formato de archivo ELF, aquí no tendrá muchas palabras, las personas que no entienden pueden buscarlo.

Aquí hay un ejemplo, agregue las siguientes líneas al código C / C ++ en su proyecto de Android:

........

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

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

........

Compilé el archivo libcheckcert.so aquí y lo puse en el teléfono para ejecutar el resultado:

........

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

........
La función _init se ejecuta primero, ¿por qué sucede esto? Cualquiera que entienda los archivos ELF sabe que hay dos secciones, .init y .init_array, que se utilizan para la inicialización cuando se carga el archivo ELF, entonces, ¿cuál es la relación entre ellos y la función _init y la propiedad del constructor? A continuación, necesitamos usar readelf e IDA pro para ver, primero readelf -d libcheckcert.so para ver la sección dinámica de ELF:

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
Puede ver que las direcciones de las secciones INIT e INIT_ARRAY son 0x4f9c y 0x1a660, abra IDA pro para ver el código en la ubicación correspondiente:

.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

Se puede ver que el código anterior ejecuta la función que definimos. La sección .init es el código de la función _init, y la sección .init_array es una matriz de punteros. Cada elemento corresponde a un bloque de código, que puede realizar una serie de operaciones de inicialización . Entonces, ¿por qué el código de la sección .init se ejecuta antes que el código de la sección .init_array? Esto depende del código de enlace, que se encuentra en el directorio bionic / linker de AOSP. Aquí hay un breve extracto del código:

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);
}
Puede ver que el código en la sección .init se ejecuta primero, y luego cada bloque de código en el .init_array se ejecuta en secuencia.

En este punto, todos deberían entender la función _init, la propiedad del constructor y la situación correspondiente de la sección .init y la sección .init_array con claridad.

Aquí hay un lugar que no entiendo. Utilice readelf para ver toda la información de símbolos en el ELF. Puede ver que .rel.dyn y .rel.plt tienen el símbolo myConstructor, un tipo es R_ARM_ABS32 y el otro es R_ARM_JUMP_SLOT. Además, si mira myConstructor en IDA pro, puede encontrar que su cuerpo de código está en la sección .text, pero también puede encontrar que también hay definiciones myConstructor en las secciones .plt y .got. En este caso, cada vez que llama explícitamente a myConstructor, debe saltar a través de PLT y luego encontrar la dirección real de myConstructor en la sección TEXTO de la tabla GOT para ejecutar. Pero la dirección en .init_array es su dirección real en la sección TEXTO. Llamar a myConstructor durante la inicialización no necesita pasar las tablas PLT y GOT. ¿No entiendo por qué es esto? Déjalo para más tarde.

Actualización: El problema anterior se debe al problema del compilador. Los archivos ELF compilados por diferentes compiladores no son los mismos. La situación que mencioné anteriormente es compilada por el compilador LLVM. Si usa arm-linux-androideabi - * Si el símbolo myConstructor solo está presente en la sección .text, no aparecerá en .rel.dyn y .rel.plt.





Publicado 60 artículos originales · Me gusta 44 · Visitas 340,000+

Supongo que te gusta

Origin blog.csdn.net/beyond702/article/details/53607212
Recomendado
Clasificación