macho: fix DWARF in dSYM and sym naming more consistent

* Advance line and PC prior to ending sequence in debug line program
  for a fn_decl. This is equivalent to closing scope in the debugger
  and without it, the debugger will not map source-to-address info
  as a result will not print the source when breaking at a symbol.
* Fix debug aranges sentinels to be of the size as the actual tuple
  descriptor (assuming segment selector to be ommitted). In summary,
  the sentinels were 32bit 0s, whereas they ought to be 64bit 0s.
* Make naming of symbols in the binary more consistent by prefixing
  each symbol name with an underscore '_'.
This commit is contained in:
Jakub Konka 2021-05-14 19:20:21 +02:00
parent d98e39fa68
commit 6461b95163
3 changed files with 53 additions and 15 deletions

View File

@ -362,8 +362,8 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
self.base.file = file;
// Create dSYM bundle.
if (!options.strip and options.module != null) {
// Create dSYM bundle.
const dir = options.module.?.zig_cache_artifact_directory;
log.debug("creating {s}.dSYM bundle in {s}", .{ sub_path, dir.path });
@ -1223,7 +1223,11 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
self.shrinkTextBlock(&decl.link.macho, code.len);
}
decl.link.macho.size = code.len;
symbol.n_strx = try self.updateString(symbol.n_strx, mem.spanZ(decl.name));
const new_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
defer self.base.allocator.free(new_name);
symbol.n_strx = try self.updateString(symbol.n_strx, new_name);
symbol.n_type = macho.N_SECT;
symbol.n_sect = @intCast(u8, self.text_section_index.?) + 1;
symbol.n_desc = 0;
@ -1232,7 +1236,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl: *Module.Decl) !void {
if (self.d_sym) |*ds|
try ds.writeLocalSymbol(decl.link.macho.local_sym_index);
} else {
const decl_name = mem.spanZ(decl.name);
const decl_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{mem.spanZ(decl.name)});
defer self.base.allocator.free(decl_name);
const name_str_index = try self.makeString(decl_name);
const addr = try self.allocateTextBlock(&decl.link.macho, code.len, required_alignment);
@ -1371,6 +1377,9 @@ pub fn updateDeclExports(
const decl_sym = &self.locals.items[decl.link.macho.local_sym_index];
for (exports) |exp| {
const exp_name = try std.fmt.allocPrint(self.base.allocator, "_{s}", .{exp.options.name});
defer self.base.allocator.free(exp_name);
if (exp.options.section) |section_name| {
if (!mem.eql(u8, section_name, "__text")) {
try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1);
@ -1398,7 +1407,7 @@ pub fn updateDeclExports(
// Otherwise, don't do anything since we already have all the flags
// set that we need for global (strong) linkage.
// n_type == N_SECT | N_EXT
if (mem.eql(u8, exp.options.name, "_main")) {
if (mem.eql(u8, exp_name, "_main")) {
self.entry_addr = decl_sym.n_value;
}
},
@ -1420,14 +1429,14 @@ pub fn updateDeclExports(
if (exp.link.macho.sym_index) |i| {
const sym = &self.globals.items[i];
sym.* = .{
.n_strx = try self.updateString(sym.n_strx, exp.options.name),
.n_strx = try self.updateString(sym.n_strx, exp_name),
.n_type = n_type,
.n_sect = @intCast(u8, self.text_section_index.?) + 1,
.n_desc = n_desc,
.n_value = decl_sym.n_value,
};
} else {
const name_str_index = try self.makeString(exp.options.name);
const name_str_index = try self.makeString(exp_name);
const i = if (self.globals_free_list.popOrNull()) |i| i else blk: {
_ = self.globals.addOneAssumeCapacity();
self.export_info_dirty = true;
@ -2230,9 +2239,12 @@ fn makeString(self: *MachO, bytes: []const u8) !u32 {
try self.string_table.ensureCapacity(self.base.allocator, self.string_table.items.len + bytes.len + 1);
const offset = @intCast(u32, self.string_table.items.len);
log.debug("writing new string '{s}' into string table at offset 0x{x}", .{ bytes, offset });
self.string_table.appendSliceAssumeCapacity(bytes);
self.string_table.appendAssumeCapacity(0);
try self.string_table_directory.putNoClobber(
self.base.allocator,
try self.base.allocator.dupe(u8, bytes),

View File

@ -534,8 +534,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Opt
mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), text_section.size);
// Sentinel.
mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0);
mem.writeIntLittle(u32, di_buf.addManyAsArrayAssumeCapacity(4), 0);
mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
mem.writeIntLittle(u64, di_buf.addManyAsArrayAssumeCapacity(8), 0);
// Go back and populate the initial length.
const init_len = di_buf.items.len - after_init_len;
@ -1075,6 +1075,32 @@ pub fn commitDeclDebugInfo(
mem.writeIntLittle(u32, ptr, @intCast(u32, text_block.size));
}
{
// Advance line and PC.
// TODO encapsulate logic in a helper function.
try dbg_line_buffer.append(DW.LNS_advance_pc);
try leb.writeULEB128(dbg_line_buffer.writer(), text_block.size);
try dbg_line_buffer.append(DW.LNS_advance_line);
const line_off: u28 = blk: {
const tree = decl.container.file_scope.tree;
const node_tags = tree.nodes.items(.tag);
const node_datas = tree.nodes.items(.data);
const token_starts = tree.tokens.items(.start);
// TODO Look into improving the performance here by adding a token-index-to-line
// lookup table. Currently this involves scanning over the source code for newlines.
const fn_decl = decl.src_node;
assert(node_tags[fn_decl] == .fn_decl);
const block = node_datas[fn_decl].rhs;
const lbrace = tree.firstToken(block);
const rbrace = tree.lastToken(block);
const line_delta = std.zig.lineDelta(tree.source, token_starts[lbrace], token_starts[rbrace]);
break :blk @intCast(u28, line_delta);
};
try leb.writeULEB128(dbg_line_buffer.writer(), line_off);
}
try dbg_line_buffer.appendSlice(&[_]u8{ DW.LNS_extended_op, 1, DW.LNE_end_sequence });
// Now we have the full contents and may allocate a region to store it.

View File

@ -17,7 +17,7 @@ pub fn addCases(ctx: *TestContext) !void {
// Incorrect return type
case.addError(
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\}
, &[_][]const u8{":2:1: error: expected noreturn, found void"});
@ -26,7 +26,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\extern "c" fn write(usize, usize, usize) usize;
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ print();
\\
\\ exit(0);
@ -46,7 +46,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\extern "c" fn write(usize, usize, usize) usize;
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ print();
\\ print();
\\ print();
@ -73,7 +73,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\extern "c" fn write(usize, usize, usize) usize;
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ print();
\\
\\ exit(0);
@ -93,7 +93,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\extern "c" fn write(usize, usize, usize) usize;
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ print();
\\ print();
\\
@ -119,7 +119,7 @@ pub fn addCases(ctx: *TestContext) !void {
case.addCompareOutput(
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ exit(0);
\\}
,
@ -130,7 +130,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\extern "c" fn exit(usize) noreturn;
\\extern "c" fn write(usize, usize, usize) usize;
\\
\\export fn _main() noreturn {
\\export fn main() noreturn {
\\ _ = write(1, @ptrToInt("Hey!\n"), 5);
\\ exit(0);
\\}