たにしきんぐダム

プログラミングやったりゲームしてます

(メモ)複数のオブジェクトファイルが同じ compilation unit DIE を持っていてmtimeが同じだと、Darwin linkerが重複したオブジェクトファイルのデバッグ情報を無視する?

zenn.dev に移しました

github.com

Darwin linker はデバッグ情報を最終成果物のバイナリにリンクしない。その代わりに、リンクされたオブジェクトファイルへの絶対パスとタイムスタンプを含む N_OSO タイプの symbol を出力バイナリのシンボルテーブルに出力する。

このへん? github.com

こういうやつ

cat foo.c
int main() {
  int x = 1;
  return x;
}


❯ clang -g -o foo foo.c

❯ dsymutil --symtab foo | grep N_OSO
[     3] 00000061 66 (N_OSO        ) 00     0001   0000000064f56464 '/var/folders/xt/hyn73bjx4kl6hjj9sxnf1yzh0000gn/T/foo-e04e09.o'

デバッガは N_OSO シンボルを見て、object file 内のデバッグ情報を参照したり、dsymutil コマンドは N_OSO のエントリを参照して obect file から デバッグ情報をかき集めて dSYM bundle を生成する。 llvm-project/llvm/lib/Object/ArchiveWriter.cpp at e2cb07c322e85604dc48f9caec52b3570db0e1d8 · llvm/llvm-project · GitHub

(ここはちょっと怪しい)

// For an object file, the N_OSO entries contain the absolute path

// path to the file, and the file's timestamp. For an object

// included in an archive, the path is formatted like

// "/absolute/path/to/archive.a(member.o)", and the timestamp is the

// archive member's timestamp, rather than the archive's timestamp.

//

// However, this doesn't always uniquely identify an object within

// an archive -- an archive file can have multiple entries with the

// same filename. (This will happen commonly if the original object

// files started in different directories.) The only way they get

// distinguished, then, is via the timestamp. But this process is

// unable to find the correct object file in the archive when there

// are two files of the same name and timestamp.

llvm-project/llvm/lib/Object/ArchiveWriter.cpp at e2cb07c322e85604dc48f9caec52b3570db0e1d8 · llvm/llvm-project · GitHub

archive object 内の object file が同一 path かつタイムスタンプをもつ場合に Darwin linker がオブジェクトファイル(内のデバッグ情報を)区別できないという話。

archive object の話だから関係ないかなと思ったけど、同一 object file の CU DIE が同一 DW_AT_comp_dirDW_AT_name と同じ mtime を持っている場合、明らかに重複した object file に対する N_OSO シンボルが生成されてない問題を見つけた。

なので、上の LLVM 内の記述は 1 source file - 1 object file でコンパイルすることを前提として書かれているのではないか? object file の path と行っているけど実際はその object file に含まれる CU DIE の属性を参照しているのでは? (推測)


ScalaNative 0.4.14 での実装では package ごとに .ll ファイルを生成して、.ll.o にコンパイルする。その結果 1 Scala ファイル内に複数 package があると 複数の .o ファイルが作られるし、それらの CU DIE はいずれも同じ Scala ファイルを指す。

// Test.scala
package A { ... } // A.ll.o whose compilation unit DIE points to Test.scala
package B { ... } // B.ll.o whose compilation unit DIE points to Test.scala
package C { ... } // same

これで Test.scala から生成される .o ファイルの mtime が同じだと重複したうち一つの .o ファイルしか N_OSO が最終的なバイナリのシンボルテーブルに追加されず、dsymutil はオブジェクトファイルの検索に失敗する。

そして warning: (arm64) could not find object file symbol for symbol __SM33scala.scalanative.issue1359.Main$D1fL16java.lang.ObjectEO みたいな warning を吐いてくる。

llvm-project/llvm/tools/dsymutil/MachODebugMapParser.cpp at 1ef1eec8db21f35286d9c7fa0a48bb0b6446fe11 · llvm/llvm-project · GitHub

github.com

どうなるかな -> ディレクトリごとに分割するようにしました

参考

dkimitsa.github.io

github.com

github.com


archive 内の object file が同一 path かつタイムスタンプをもつ場合に Darwin linker がオブジェクトファイル(内のデバッグ情報を)区別できない問題を回避するため(OSXの?)arコマンドはアーカイブ内の.oファイルのmtimeをわざと1秒ごとずらす?みたいなことやるっぽい https://github.com/llvm/llvm-project/blob/e2cb07c322e85604dc48f9caec52b3570db0e1d8/llvm/lib/Object/ArchiveWriter.cpp#L698-L739

(けど、その結果link結果がdeterministicじゃなくなって困るみたいな話もあったっぽい (これは ZERO_AR_DATE=1 で回避))

github.com