GNUコンパイラに生成コードをユーザー独自のセクションに配置するように命令したときに、デフォルトのリンカスクリプトが使用可能か実験しました.
- 実行環境
- 独自セクションにコードを配置する
- exeをダンプ(デフォルトリンカスクリプトでビルド)
- デフォルトのリンカスクリプトを手に入れる
- 独自リンカスクリプトでビルド
- exeをダンプ(独自リンカスクリプトでビルド)
- dumpbin /all でダンプしてみる
- 結論
- 参考
実行環境
項目 | 説明 |
---|---|
OS | Windows11 |
MinGW(ビルド用) | x86_64-posix-seh-rev0 8.1.0 |
MSVCビルドツール(ダンプ用) | Visual Studio 2017相当 |
独自セクションにコードを配置する
元来コンパイラは関数や変数の性質に応じてコードを自動的に適切なセクションに配置しオブジェクトファイルを生成します. 最終的にはリンカが各オブジェクトファイルを結合し、全体としてセクションごとにコードを配置します. セクションの種類や名前は処理系に依存しますが、逆に言えばユーザーが独自に定義することもできます.
コードの一部を明示的に指定したセクションに配置するにはその旨を記述します. 今回は以下のように__attribute__を使用して明示的にセクションを指定してコードを配置しました.
// main.c #include<stdio.h> // 関数foo()は独自セクション.mysecに配置 void foo(void) __attribute__((section(".mysec"))); // 何も記述しない場合関数は各制御系で決められたセクションに配置される(.textセクションやPセクションと呼ばれることが多い) int main(){ foo(); return 0; } void foo(void){ printf("good bye\n"); }
異常のソースファイルをgccコマンドでビルドします. 何のエラーもなくビルドすることが出来ました. パスにスペースが含まれる場合ダブルクォーテーションで囲むのをお忘れなく.
somewhere> "C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\gcc.exe" main.c
exeをダンプ(デフォルトリンカスクリプトでビルド)
MinGWについてくるreadelfを使おうとしたのですが、exeには使用できませんでした. なのでダンプツールとしてはVisual Studioのビルドツールの一部であるdumpbin.exeを使用しましょう. dumpbin.exeの詳しい使い方は以下の記事を見てください. dllもexeも同じ方法でダンプすることが出来ます.
今回はセクションさえ見れれば十分なので/summaryオプションをつけてダンプします.
somewhere> dumpbin /summary a.exe Microsoft (R) COFF/PE Dumper Version 14.16.27048.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file a.exe File Type: EXECUTABLE IMAGE Summary 1000 .CRT 1000 .bss 1000 .data 1000 .debug_abbrev 1000 .debug_aranges 1000 .debug_frame 2000 .debug_info 1000 .debug_line 1000 .debug_str 1000 .idata 1000 .mysec # .mysecセクション 1000 .pdata 1000 .rdata 2000 .text 1000 .tls 1000 .xdata
ということで、デフォルトのリンカスクリプトを使用したにも関わらず、普通にユーザー独自のセクションにコードがロケートされました. めでたしめでたし.
デフォルトのリンカスクリプトを手に入れる
このままでは終われないので、ちゃんと自分でもリンカスクリプトを用意しましょう. gccコマンドに -Wl,option という形式でオプションを渡すとリンカ(ld.exe)へのオプションを渡すことが出来ます. これを利用してリンカに--verboseオプションを渡すとデフォルトで使用しているリンカスクリプトを表示させることが出来ます. 単に--verboseオプションをつけるとこれはリンカではなくコンパイラへのオプションになってしまうので注意です .
somewhere> "C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\gcc.exe" main.c -Wl,--verbose GNU ld (GNU Binutils) 2.30 Supported emulations: i386pep i386pe using internal linker script: ================================================== /* Default linker script, for normal executables */ /* Copyright (C) 2014-2018 Free Software Foundation, Inc. Copying and distribution of this script, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. */ OUTPUT_FORMAT(pei-x86-64) SEARCH_DIR("=/c/mingw810/prerequisites/x86_64-binutils-nomulti/x86_64-w64-mingw32/lib"); SEARCH_DIR("=/c/mingw810/prerequisites/x86_64-binutils-nomulti/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SECTIONS { /* Make the virtual address and file offset synced if the alignment is lower than the target page size. */ . = SIZEOF_HEADERS; . = ALIGN(__section_alignment__); .text __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) : { KEEP(*(.init)) *(.text) *(SORT(.text$*)) *(.text.*) *(.gnu.linkonce.t.*) *(.glue_7t) *(.glue_7) . = ALIGN(8); /* Note: we always define __CTOR_LIST__ and ___CTOR_LIST__ here, we do not PROVIDE them. This is because the ctors.o startup code in libgcc defines them as common symbols, with the expectation that they will be overridden by the definitions here. If we PROVIDE the symbols then they will not be overridden and global constructors will not be run. This does mean that it is not possible for a user to define their own __CTOR_LIST__ and __DTOR_LIST__ symbols. If that ability is needed a custom linker script will have to be used. (The custom script can just be a copy of this script with the PROVIDE() qualifiers added). See PR 22762 for more details. */ ___CTOR_LIST__ = .; __CTOR_LIST__ = .; LONG (-1); LONG (-1); KEEP (*(.ctors)); KEEP (*(.ctor)); KEEP (*(SORT_BY_NAME(.ctors.*))); LONG (0); LONG (0); /* See comment about __CTOR_LIST__ above. The same reasoning applies here too. */ ___DTOR_LIST__ = .; __DTOR_LIST__ = .; LONG (-1); LONG (-1); KEEP (*(.dtors)); KEEP (*(.dtor)); KEEP (*(SORT_BY_NAME(.dtors.*))); LONG (0); LONG (0); KEEP (*(.fini)) /* ??? Why is .gcc_exc here? */ *(.gcc_exc) PROVIDE (etext = .); KEEP (*(.gcc_except_table)) } /* The Cygwin32 library uses a section to avoid copying certain data on fork. This used to be named ".data". The linker used to include this between __data_start__ and __data_end__, but that breaks building the cygwin32 dll. Instead, we name the section ".data_cygwin_nocopy" and explicitly include it after __data_end__. */ .data BLOCK(__section_alignment__) : { __data_start__ = . ; *(.data) *(.data2) *(SORT(.data$*)) KEEP(*(.jcr)) __data_end__ = . ; *(.data_cygwin_nocopy) } .rdata BLOCK(__section_alignment__) : { *(.rdata) *(SORT(.rdata$*)) __rt_psrelocs_start = .; KEEP(*(.rdata_runtime_pseudo_reloc)) __rt_psrelocs_end = .; } __rt_psrelocs_size = __rt_psrelocs_end - __rt_psrelocs_start; ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .; __RUNTIME_PSEUDO_RELOC_LIST_END__ = .; ___RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; __RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size; .eh_frame BLOCK(__section_alignment__) : { KEEP (*(.eh_frame*)) } .pdata BLOCK(__section_alignment__) : { KEEP(*(.pdata*)) } .xdata BLOCK(__section_alignment__) : { KEEP(*(.xdata*)) } .bss BLOCK(__section_alignment__) : { __bss_start__ = . ; *(.bss) *(COMMON) __bss_end__ = . ; } .edata BLOCK(__section_alignment__) : { *(.edata) } /DISCARD/ : { *(.debug$S) *(.debug$T) *(.debug$F) *(.drectve) *(.note.GNU-stack) *(.gnu.lto_*) } .idata BLOCK(__section_alignment__) : { /* This cannot currently be handled with grouped sections. See pep.em:sort_sections. */ KEEP (SORT(*)(.idata$2)) KEEP (SORT(*)(.idata$3)) /* These zeroes mark the end of the import list. */ LONG (0); LONG (0); LONG (0); LONG (0); LONG (0); KEEP (SORT(*)(.idata$4)) __IAT_start__ = .; SORT(*)(.idata$5) __IAT_end__ = .; KEEP (SORT(*)(.idata$6)) KEEP (SORT(*)(.idata$7)) } .CRT BLOCK(__section_alignment__) : { ___crt_xc_start__ = . ; KEEP (*(SORT(.CRT$XC*))) /* C initialization */ ___crt_xc_end__ = . ; ___crt_xi_start__ = . ; KEEP (*(SORT(.CRT$XI*))) /* C++ initialization */ ___crt_xi_end__ = . ; ___crt_xl_start__ = . ; KEEP (*(SORT(.CRT$XL*))) /* TLS callbacks */ /* ___crt_xl_end__ is defined in the TLS Directory support code */ ___crt_xp_start__ = . ; KEEP (*(SORT(.CRT$XP*))) /* Pre-termination */ ___crt_xp_end__ = . ; ___crt_xt_start__ = . ; KEEP (*(SORT(.CRT$XT*))) /* Termination */ ___crt_xt_end__ = . ; } /* Windows TLS expects .tls$AAA to be at the start and .tls$ZZZ to be at the end of the .tls section. This is important because _tls_start MUST be at the beginning of the section to enable SECREL32 relocations with TLS data. */ .tls BLOCK(__section_alignment__) : { ___tls_start__ = . ; KEEP (*(.tls$AAA)) KEEP (*(.tls)) KEEP (*(.tls$)) KEEP (*(SORT(.tls$*))) KEEP (*(.tls$ZZZ)) ___tls_end__ = . ; } .endjunk BLOCK(__section_alignment__) : { /* end is deprecated, don't use it */ PROVIDE (end = .); PROVIDE ( _end = .); __end__ = .; } .rsrc BLOCK(__section_alignment__) : SUBALIGN(4) { KEEP (*(.rsrc)) KEEP (*(.rsrc$*)) } .reloc BLOCK(__section_alignment__) : { *(.reloc) } .stab BLOCK(__section_alignment__) (NOLOAD) : { *(.stab) } .stabstr BLOCK(__section_alignment__) (NOLOAD) : { *(.stabstr) } /* DWARF debug sections. Symbols in the DWARF debugging sections are relative to the beginning of the section. Unlike other targets that fake this by putting the section VMA at 0, the PE format will not allow it. */ /* DWARF 1.1 and DWARF 2. */ .debug_aranges BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_aranges) } .zdebug_aranges BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_aranges) } .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_pubnames) } .zdebug_pubnames BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_pubnames) } .debug_pubtypes BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_pubtypes) } .zdebug_pubtypes BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_pubtypes) } /* DWARF 2. */ .debug_info BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_info .gnu.linkonce.wi.*) } .zdebug_info BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_info .zdebug.gnu.linkonce.wi.*) } .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_abbrev) } .zdebug_abbrev BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_abbrev) } .debug_line BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_line) } .zdebug_line BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_line) } .debug_frame BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_frame) } .zdebug_frame BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_frame) } .debug_str BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_str) } .zdebug_str BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_str) } .debug_loc BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_loc) } .zdebug_loc BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_loc) } .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_macinfo) } .zdebug_macinfo BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_macinfo) } /* SGI/MIPS DWARF 2 extensions. */ .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_weaknames) } .zdebug_weaknames BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_weaknames) } .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_funcnames) } .zdebug_funcnames BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_funcnames) } .debug_typenames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_typenames) } .zdebug_typenames BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_typenames) } .debug_varnames BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_varnames) } .zdebug_varnames BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_varnames) } .debug_macro BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_macro) } .zdebug_macro BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_macro) } /* DWARF 3. */ .debug_ranges BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_ranges) } .zdebug_ranges BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_ranges) } /* DWARF 4. */ .debug_types BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_types .gnu.linkonce.wt.*) } .zdebug_types BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_types .zdebug.gnu.linkonce.wt.*) } /* For Go and Rust. */ .debug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : { *(.debug_gdb_scripts) } .zdebug_gdb_scripts BLOCK(__section_alignment__) (NOLOAD) : { *(.zdebug_gdb_scripts) } } ==================================================
この部分をコピペして新しくld.ldsというファイルにしましょう. そして次の追加のセクション情報をいい感じのところに記述します.
// ld.lds .mysec BLOCK(__section_alignment__) : { *(.mysec) }
独自リンカスクリプトでビルド
独自リンカスクリプトの準備はできたので、後はリンカにこの独自リンカスクリプトを使用してロケートするようにオプションを指定します. -cオプション、あるいは-Tオプションを使用すれば明示的に使用するリンカオプションを指定することが出来ます. いわく、リンカスクリプトのことをリンクコマンドファイルともいうので-cオプションと言いらしいです. -Tオプションは-cオプションと全く同じですが、他のツールとの互換性のために残されているとのこと.
とにもかくにも次のコマンドでビルドします.
somewhere> "C:\Program Files\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin\gcc.exe" main.c -Wl,--verbose -T ld.lds
すると先ほどは using internal linker script: と表示されていた部分が using external linker script: となっているはずです.
exeをダンプ(独自リンカスクリプトでビルド)
独自リンカスクリプトを用いてロケートした.exeのセクション情報をを先ほどと同様にダンプすると次のように表示されました.
somewhere>dumpbin /summary a.exe Microsoft (R) COFF/PE Dumper Version 14.16.27048.0 Copyright (C) Microsoft Corporation. All rights reserved. Dump of file a.exe File Type: EXECUTABLE IMAGE Summary 1000 .CRT 1000 .bss 1000 .data 1000 .debug_abbrev 1000 .debug_aranges 1000 .debug_frame 2000 .debug_info 1000 .debug_line 1000 .debug_str 1000 .idata 1000 .mysec 1000 .pdata 1000 .rdata 2000 .text 1000 .tls 1000 .xdata
全く同じですね.
dumpbin /all でダンプしてみる
なんとか差分を見つけたいので両者をdumpbin /all でダンプしてみると、タイムスタンプ以外にセクションの順番が違うということが分かりました. が、それ以外に違いらしい違いは見られませんでした.
結論
コンパイラに生成コードをユーザー独自のセクションに配置するように命令したとしても、自動的にそのセクションが作成されるので、独自リンカスクリプトを用意する必要はない.