authorgravatar for corentin.kerisit@gmail.comCorentin Kerisit <corentin.kerisit@gmail.com> 2026-04-11 18:05:24+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-12 01:23:17+02:00
log07f05426fc0c28ad19f87002d5d4d0366e54b7c9
tree9cc7598f550f222ff1894bbd7303a2dbc00caf75
parentbc08199ef1f9419b5b8ed4165458783314934266

Support ld64.ldd STABS layout in MachOFile.load

Apple's ld emit N_BNSYM and N_ENSYM to mark the start and end of functions, while ld64.lld doesn't. This resulted in MachOFile.load bailing out on unsupported STABS layout when the linker used is ld64.lld. This commit supports both layouts.

1 files changed, 46 insertions(+), 8 deletions(-)

lib/std/debug/MachOFile.zig+46-8
...@@ -158,6 +158,10 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)...@@ -158,6 +158,10 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)
158 }158 }
159159
160 // TODO handle globals N_GSYM, and statics N_STSYM160 // TODO handle globals N_GSYM, and statics N_STSYM
161 //
162 // NOTE: ld64.lld and Apple's ld differ in STABS layout.
163 // Apple's ld emit N_BNSYM and N_ENSYM to mark the start and end of
164 // functions, while ld64.lld doesn't.
161 switch (sym.n_type.stab) {165 switch (sym.n_type.stab) {
162 .oso => switch (state) {166 .oso => switch (state) {
163 .init, .oso_close => {167 .init, .oso_close => {
...@@ -178,6 +182,14 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)...@@ -178,6 +182,14 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)
178 else => return error.InvalidMachO,182 else => return error.InvalidMachO,
179 },183 },
180 .fun => switch (state) {184 .fun => switch (state) {
185 .oso_open => {
186 state = .fun_strx;
187 last_sym = .{
188 .strx = sym.n_strx,
189 .addr = sym.n_value,
190 .ofile = ofile,
191 };
192 },
181 .bnsym => {193 .bnsym => {
182 state = .fun_strx;194 state = .fun_strx;
183 last_sym.strx = sym.n_strx;195 last_sym.strx = sym.n_strx;
...@@ -185,20 +197,24 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)...@@ -185,20 +197,24 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)
185 .fun_strx => {197 .fun_strx => {
186 state = .fun_size;198 state = .fun_size;
187 },199 },
200 .fun_size => {
201 if (last_sym.strx != 0) {
202 appendStabSymbol(&symbols, &symbol_names, strings, last_sym);
203 }
204 last_sym = .{
205 .strx = sym.n_strx,
206 .addr = sym.n_value,
207 .ofile = ofile,
208 };
209 state = .fun_strx;
210 },
188 else => return error.InvalidMachO,211 else => return error.InvalidMachO,
189 },212 },
190 .ensym => switch (state) {213 .ensym => switch (state) {
191 .fun_size => {214 .fun_size => {
192 state = .ensym;215 state = .ensym;
193 if (last_sym.strx != 0) {216 if (last_sym.strx != 0) {
194 const name = std.mem.sliceTo(strings[last_sym.strx..], 0);217 appendStabSymbol(&symbols, &symbol_names, strings, last_sym);
195 const gop = symbol_names.getOrPutAssumeCapacity(name);
196 if (!gop.found_existing) {
197 assert(gop.index == symbols.items.len);
198 symbols.appendAssumeCapacity(last_sym);
199 } else {
200 symbols.items[gop.index] = last_sym;
201 }
202 }218 }
203 },219 },
204 else => return error.InvalidMachO,220 else => return error.InvalidMachO,
...@@ -208,6 +224,12 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)...@@ -208,6 +224,12 @@ pub fn load(gpa: Allocator, io: Io, path: []const u8, arch: std.Target.Cpu.Arch)
208 .oso_open, .ensym => {224 .oso_open, .ensym => {
209 state = .oso_close;225 state = .oso_close;
210 },226 },
227 .fun_size => {
228 state = .oso_close;
229 if (last_sym.strx != 0) {
230 appendStabSymbol(&symbols, &symbol_names, strings, last_sym);
231 }
232 },
211 else => return error.InvalidMachO,233 else => return error.InvalidMachO,
212 },234 },
213 else => {},235 else => {},
...@@ -356,6 +378,22 @@ test {...@@ -356,6 +378,22 @@ test {
356 _ = Symbol;378 _ = Symbol;
357}379}
358380
381fn appendStabSymbol(
382 symbols: *std.ArrayList(Symbol),
383 symbol_names: *std.StringArrayHashMapUnmanaged(void),
384 strings: []const u8,
385 last_sym: Symbol,
386) void {
387 const name = std.mem.sliceTo(strings[last_sym.strx..], 0);
388 const gop = symbol_names.getOrPutAssumeCapacity(name);
389 if (!gop.found_existing) {
390 assert(gop.index == symbols.items.len);
391 symbols.appendAssumeCapacity(last_sym);
392 } else {
393 symbols.items[gop.index] = last_sym;
394 }
395}
396
359fn loadOFile(gpa: Allocator, io: Io, o_file_name: []const u8) !OFile {397fn loadOFile(gpa: Allocator, io: Io, o_file_name: []const u8) !OFile {
360 const all_mapped_memory, const mapped_ofile = map: {398 const all_mapped_memory, const mapped_ofile = map: {
361 const open_paren = paren: {399 const open_paren = paren: {