I am trying to understand Mach-O symbol tables and essentially reproduce the output of dsymutil -dump-debug-map.
I have a binary with a symbol table that looks like
...
[ 157] 00000001 64 (N_SO ) 01 0000 0000000000000000
[ 158] 00003756 64 (N_SO ) 00 0000 0000000000000000 '/path/to/project/'
[ 159] 00003773 64 (N_SO ) 00 0000 0000000000000000 'test.cpp'
[ 160] 0000377c 66 (N_OSO ) 03 0001 000000006510bf32 '/path/to/project/test.cpp.o'
[ 161] 00000001 2e (N_BNSYM ) 01 0000 0000000100004e60
[ 162] 000037bd 24 (N_FUN ) 01 0000 0000000100004e60 '_Z3fooi'
[ 163] 00000001 24 (N_FUN ) 00 0000 0000000000000170
[ 164] 00000001 4e (N_ENSYM ) 01 0000 0000000100004e60
[ 165] 00000001 2e (N_BNSYM ) 01 0000 0000000100004fd0
...
[ 213] 00000001 2e (N_BNSYM ) 01 0000 0000000100005470
[ 214] 00003a90 24 (N_FUN ) 01 0000 0000000100005470 '_Z3bari'
[ 215] 00000001 24 (N_FUN ) 00 0000 0000000000000020
[ 216] 00000001 4e (N_ENSYM ) 01 0000 0000000100005470
The dumped debug map includes
---
triple: 'x86_64-apple-darwin'
binary-path: test
objects:
- filename: '/path/to/project/test.cpp.o'
timestamp: 1695596338
symbols:
- { sym: _Z3fooi, objAddr: 0x610, binAddr: 0x100005470, size: 0x20 }
An nm confirms _Z3fooi is at address 0x610 in /path/to/project/test.cpp.o.
I'm trying to figure out how the address 0x610 is computed. 0x100005470 -> 0x5470 is from the base of the __TEXT section but then the difference between 0x5470 and 0x610 is 0x4e60, which happens to be the address of the first symbol listed after the N_OSO entry. But it feels suspicious to try to rely on that for computing the corresponding offset in the .o file.
How is this computation supposed to be done?
I have started reading through the dsymutil source code in llvm but I have not found where this computation takes place.