LDマニュアル は、KEEP
コマンドが何をするかを説明していません。以下は、KEEP
を特徴とするサードパーティのリンカースクリプトの抜粋です。 KEEP
コマンドはld
で何をしますか?
SECTIONS
{
.text :
{
. = ALIGN(4);
_text = .;
PROVIDE(stext = .);
KEEP(*(.isr_vector))
KEEP(*(.init))
*(.text .text.*)
*(.rodata .rodata.*)
*(.gnu.linkonce.t.*)
*(.glue_7)
*(.glue_7t)
*(.gcc_except_table)
*(.gnu.linkonce.r.*)
. = ALIGN(4);
_etext = .;
_sidata = _etext;
PROVIDE(etext = .);
_fini = . ;
*(.fini)
} >flash
Afaik LDは、シンボルが参照されていない場合でも、セクション内のシンボルを保持します。(-gc-sections)。
通常、バイナリスタートアッププロセスで特別な意味を持つセクションに使用され、依存ツリーのルートをマークします。
(以下のSabuncuの場合)
依存ツリー:
未使用のコードを削除する場合、コードを分析し、すべての到達可能なセクション(コード+グローバル変数+定数)をマークします。
そのため、セクションを選択して「使用済み」とマークし、そのセクションが参照する他のセクションを確認してから、それらのセクションを「使用済み」とマークし、参照先などを確認します。
「使用済み」とマークされていないセクションは冗長になり、削除できます。
セクションは複数の他のセクション(たとえば、3つの異なる他のセクションを呼び出す1つのプロシージャ)を参照できるため、結果を描画する場合はツリーを取得します。
ルート:
ただし、上記の原則には問題があります。常に使用される「最初の」セクションは何ですか?いわば、ツリーの最初のノード(ルート)です。これが "keep()"が行うことで、どのセクション(利用可能な場合)を最初に調べるかをリンカーに伝えます。結果として、これらは常にリンクされています。
通常、これらは、動的リンクに関連するタスクを実行するためにプログラムローダーから呼び出されるセクション(オプション、OS /ファイル形式に依存)、およびプログラムのエントリポイントです。
その使用法を示す最小限のLinux IA-32の例
main.S:
.section .text
.global _start
_start:
/* Dummy access so that after will be referenced and kept. */
mov after, %eax
/*mov keep, %eax*/
/* Exit system call. */
mov $1, %eax
/* Take the exit status 4 bytes after before. */
mov $4, %ebx
mov before(%ebx), %ebx
int $0x80
.section .before
before: .long 0
/* TODO why is the `"a"` required? */
.section .keep, "a"
keep: .long 1
.section .after
after: .long 2
link.ld:
ENTRY(_start)
SECTIONS
{
. = 0x400000;
.text :
{
*(.text)
*(.before)
KEEP(*(.keep));
*(.keep)
*(.after)
}
}
コンパイルして実行:
as --32 -o main.o main.S
ld --gc-sections -m elf_i386 -o main.out -T link.ld main.o
./main.out
echo $?
出力:
1
KEEP
行をコメントアウトすると、出力は次のようになります。
2
どちらか:
mov keep, %eax
を追加します--gc-sections
を削除出力は1
に戻ります。
Ubuntu 14.04、Binutils 2.25でテスト済み。
説明:
シンボルkeep
への参照はないため、セクション.keep
が含まれます。
したがって、ガベージコレクションが有効になっており、KEEP
を使用して例外を作成しない場合、そのセクションは実行可能ファイルに配置されません。
before
のアドレスに4を追加するため、keep
セクションが存在しない場合、終了ステータスは2
になり、次の.after
セクション。
TODO:"a"
を.keep
から削除しても何も起こりません。これにより、割り当て可能になります。その理由はわかりません。そのセクションは.text
セグメント内に配置されます。これは、マジック名のために割り当て可能です。
リンカに特定のセクションを保持させる
SECTIONS
{
....
....
*(.rodata .rodata.*)
KEEP(*(SORT(.scattered_array*)));
}