Commit 6461b95163

Jakub Konka <kubkon@jakubkonka.com>
2021-05-14 19:20:21
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 '_'.
1 parent d98e39f
Changed files (3)
src
test
stage2
src/link/MachO/DebugSymbols.zig
@@ -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.
src/link/MachO.zig
@@ -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),
test/stage2/darwin.zig
@@ -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);
                 \\}