kateinoigakukunのブログ

思考垂れ流しObserver

Auto-linkingまとめ

Auto-linkingとは

Auto-linkingとは、コンパイラが出力したオブジェクトファイルからリンク対象のライブラリを自動的に決定する仕組みです。 通常、ユーザはリンカのコマンドライン引数に -lm のようにリンクするライブラリを指定しますが、Auto-linkingをサポートするコンパイラとリンカを使う場合、以下のようなC言語#pragma コメントを記述することで、リンカオプションを指定をせずに、リンカにライブラリを伝えることができます。

#include <math.h>
#include <stdio.h>

#pragma comment(lib, "m")

int main(void) {
  printf("PI = %f\n", atan(1) * 4);
  return 0;
}

コンパイルの様子

# -lmの指定無しでリンクが成功する
$ clang main.c -fuse-ld=lld
$ ./a.out
PI = 3.141593

Auto-linking - Wikipedia

リンカオプションを指定する必要が無くなることで、ユーザの負担が減るだけでなく、ビルドシステム中のオプション伝播が単純化されます。嬉しいですね。

この記事ではオブジェクトファイル形式ごとのAuto-linkingの実装状況と、LLVMにおけるサポートについて解説します。

(完全に理解したがしばらくして忘れて調べ直す、を何回かやったのでいい加減文字に起こすことにした)

オブジェクトファイル形式ごとの仕組み

上で紹介した #pragma comment(lib, "...")IBMコンパイラ、MSVC、Clangが提供しているディレクティブです。(他にもサポートしてるコンパイラはあるかも) MSVCはCOFF、ClangはELF、Mach-O、COFF向けにこの機能を実装しています。

COFF

MSVCが出力するオブジェクトファイル形式COFFには、リンカオプションを格納する.drectve セクションがあります。このセクションはオブジェクトファイルのみでサポートされており、通常のセクションとは異なり、最終的な実行可能ファイルには含まれません。

コンパイラ#pragma comment(lib, "...") からリンカオプションを生成し、オブジェクトファイルの.drectveセクションに埋め込むことで、link.exeコマンドにリンクするライブラリを伝えています。

PE Format - Win32 apps | Microsoft Docs: The .drectve Section (Object Only)

ELF

ELFにはCOFFのような仕様として定義されたリンカオプション用セクションはありません。 その代わりに、LLVMがClangとlldの間のコンベンションとして特殊なセクションを定義しています。 そのため、Clangで#pragma comment(lib, "...")コンパイルしたとしても、goldやGNU ldでリンクする場合オプションが伝わりません。

LLVMの実際の実装については後述します。

Mach-O

Mach-Oオブジェクトファイルには、ヘッダの後ろにLoad Command呼ばれる構造体列が配置されており、セクションやセグメントなどのレイアウト、動作に必要なOSバージョンなど、リンカやプログラムローダに伝える情報が格納されています。このLoad Commandの一つに、 LC_LINKER_OPTION があり、その名の通りリンカオプションをリンカに伝えてくれます。

このあたりの情報は明確なドキュメントを見つけられておらず、実装を追って分かったことなので、Mach-Oの規約として定義されているものなのかは分かりません。(もしドキュメントの在り処をご存じの方がいれば教えて下さい)

リンカ(ld64)側の実装はこのあたりにあります。 macho_relocatable_file.cpp

LLVMにおけるAuto-linking

さて、LLVMにはAuto-linkingを実現するための機能が歴史的経緯により2つあります。どちらもLLVM IR上のモジュールメタデータとして表現されており、コンパイラLLVM IRを生成する際に指定します。

1つ目は、llvm.linker.options です。

このメタデータは、COFFでは.drectveMach-OではLC_LINKER_OPTIONに降下します。 一応ELFでも SHT_LLVM_LINKER_OPTIONS という特別なセクションにリンカオプションが埋め込まれるようにコード生成されますが、なんと肝心のlld側が対応していません。 他のオブジェクトファイルの形式と違い、任意のリンカオプションを受け付けるのではなく、オプションのセマンティクスを明示的にしたKey-Valueペアで表現するため、 コンパイラフロントエンドがELF向けに特別な処理を入れる必要があります。

SwiftのWindowsポートで有名なcompnerdさんが2018年に提案して、ClangフロントエンドとLLVMバックエンドの実装をしましたが、その後アップデートが無いようです。

2つ目は llvm.dependent-librariesです。

llvm.linker.optionsのELFサポートが難航している状態に対して、SonyのPlay Station向けツールチェイン開発者の方が新しく導入したメタデータです。 これは、ELFのみをサポートしており、 リンクするライブラリ名の配列を受け付け、オブジェクトファイルのSHT_LLVM_DEPENDENT_LIBRARIES というセクションに埋め込みます。 こちらはClang、LLVMバックエンド、リンカの実装が完了しています。現在のClangは#pragma comment(lib, "...")をELFの場合のみ、llvm.linker.optionsを使う実装になっています。

まとめ

大変

参考図書