Android C言語の_init関数とコンストラクター属性、および.init / .init_arrayセクションの探索

C言語を理解しているプログラマーは、soまたは実行可能ファイルがロードされたときに、他の関数の前に一部のコードを実行できるようにする方法が2つあることを知っています。コンストラクタープロパティは関数の後に宣言されます。これらの2つのメソッドの実行に違いはありますか?注文は何ですか?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\n");}

........

ここで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ファイルを理解している人なら誰でも、ELFファイルが読み込まれたときに初期化に使用される.initと.init_arrayの2つのセクションがあることを知っているので、それらと_init関数とコンストラクタープロパティの関係は何ですか?以下では、readelfとIDA proを使用して表示する必要があります。最初にreadelf -d libcheckcert.soを使用して、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
INITおよびINIT_ARRAYセクションのアドレスが0x4f9cおよび0x1a660であることを確認できます。IDAproを開いて、対応する場所でコードを表示します。

.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 /リンカーディレクトリにあるリンカーコードに依存します。コードの短い抜粋のみを次に示します。

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関数、コンストラクタプロパティ、および.initセクションと.init_arrayセクションの対応する状況を明確に理解する必要があります。

これは私がよく理解していない場所です。readelfを使用してELFのすべてのシンボル情報を表示します。.rel.dynと.rel.pltの両方にmyConstructorシンボルがあり、1つはR_ARM_ABS32、もう1つはR_ARM_JUMP_SLOTです。さらに、IDA proでmyConstructorを見ると、コード本文が.textセクションにあることがわかりますが、.pltセクションと.gotセクションにもmyConstructor定義があることもわかります。この場合、myConstructorを明示的に呼び出すたびに、PLTをジャンプして、実行するGOTテーブルのTEXTセクションでmyConstructorの実際のアドレスを見つける必要があります。ただし、.init_arrayのアドレスはTEXTセクションの実際のアドレスであり、初期化中にmyConstructorを呼び出すことは、PLTテーブルとGOTテーブルを渡す必要はありません。これがなぜなのか理解できませんか?後で使用します。

更新:上記の問題はコンパイラの問題によるものです。異なるコンパイラでコンパイルされたELFファイルは同じではありません。上記の状況はLLVMコンパイラでコンパイルされています。arm-linux-androideabiを使用する場合-* myConstructorシンボルが.textセクションにのみ存在する場合、.rel.dynおよび.rel.pltには表示されません。





元の記事を60件公開 44のよう 訪問数340,000以上

おすすめ

転載: blog.csdn.net/beyond702/article/details/53607212