StackOverflowで気になる質問を見つけたので調査した結果を共有します
https://stackoverflow.com/questions/43103922/remove-dead-code-before-linking
問題
int do_things(void);
int application_main(void) {
return do_things();
}
int test_main(void) {
return 42;
}
int main(void) {
return test_main();
}
元の問題は、application_main
とtest_main
を(おそらくはプリプロセッサで)切り替えたいのですが、ライブラリに関数do_things
が実装されていないためリンクが失敗します。
/tmp/demo-d19626.o: 関数 `application_main' 内:
demo.c:(.text+0x5): `do_things' に対する定義されていない参照です
clang-6.0: error: linker command failed with exit code 1 (use -v to see invocation)
このdo_things
をリンク前に除けるかという問題です。
global DCE (dead code elimination)
今回はLLVMの最適化ツールoptによって削除する作戦で行きましょう。optは入力されたIRを変換するPassに通すためのコマンドです。Passについては例えば大学院生のためのLLVMなどに詳しく解説してあります。
まずLLVM IRに変換します
clang demo.c -c -emit-llvm -o demo.bc # OK
これには当然do_things
のシンボルが定義されています
$ llvm-nm demo.bc
---------------- T application_main
U do_things
---------------- T main
---------------- T test_main
次にopt
を適用していきましょう。ここではglobaldce Passを使用しましょう。これは使っていない内部関数を削除するためのPassです。しかし、do_things
は内部関数ではないので内部関数にする必要があります。それにはinternalize
Passを使います
$ opt -o stripped.bc -internalize -globaldce demo.bc
$ llvm-nm stripped.bc # 何も出力されない
残念全部消えてしまいました(´・ω・`)
どうやらmain
まで内部関数に指定されてしまったようです。main
を内部関数にしないためにはinternalize-public-api-list
を使用するようです
$ opt -o stripped.bc -internalize -internalize-public-api-list=main -globaldce demo.bc
$ llvm-nm stripped.bc
---------------- T main
---------------- t test_main
これで無事目的が達成できました(*'▽')
$ clang stripped.bc # OK