Commit b9c02e4076

LemonBoy <thatlemon@gmail.com>
2020-02-18 12:37:25
elvis entered the building
1 parent 2a350cf
Changed files (1)
lib
lib/std/debug.zig
@@ -700,7 +700,8 @@ fn printSourceAtAddressMacOs(di1: *DebugInfo, out_stream: var, address: usize, t
     const di = try di1.lookupByAddress(address);
 
     const base_addr = di.base_address;
-    const adjusted_addr = 0x100000000 + (address - base_addr);
+    const adjusted_addr = address - base_addr;
+    assert(adjusted_addr >= 0x100000000);
 
     const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
         return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs);
@@ -734,7 +735,6 @@ pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, addres
     const module = try debug_info.lookupByAddress(address);
 
     const reloc_address = address - module.base_address;
-    warn("reloc {x} => {x}\n", .{ address, reloc_address });
 
     if (module.dwarf.findCompileUnit(reloc_address) catch null) |compile_unit| {
         const compile_unit_name = try compile_unit.die.getAttrString(&module.dwarf, DW.AT_name);
@@ -1078,29 +1078,33 @@ fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DW.DwarfInfo {
 }
 
 /// TODO resources https://github.com/ziglang/zig/issues/4353
-fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
-    const hdr = &std.c._mh_execute_header;
+fn openMachODebugInfo(allocator: *mem.Allocator, memmo: []const u8) !ObjectDebugInfo {
+    // const hdr = &std.c._mh_execute_header;
+    const hdr = @ptrCast(
+        *const macho.mach_header_64,
+        @alignCast(@alignOf(macho.mach_header_64), memmo.ptr),
+    );
     assert(hdr.magic == std.macho.MH_MAGIC_64);
 
-    const hdr_base = @ptrCast([*]u8, hdr);
+    const hdr_base = @ptrCast([*]const u8, hdr);
     var ptr = hdr_base + @sizeOf(macho.mach_header_64);
     var ncmd: u32 = hdr.ncmds;
     const symtab = while (ncmd != 0) : (ncmd -= 1) {
-        const lc = @ptrCast(*std.macho.load_command, ptr);
+        const lc = @ptrCast(*const std.macho.load_command, ptr);
         switch (lc.cmd) {
-            std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr),
+            std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
             else => {},
         }
         ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
     } else {
         return error.MissingDebugInfo;
     };
-    const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
-    const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];
+    const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
+    const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize];
 
     const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
 
-    var ofile: ?*macho.nlist_64 = null;
+    var ofile: ?*const macho.nlist_64 = null;
     var reloc: u64 = 0;
     var symbol_index: usize = 0;
     var last_len: u64 = 0;
@@ -1148,8 +1152,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
     // This sort is so that we can binary search later.
     std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
 
-    return DebugInfo{
-        .ofiles = DebugInfo.OFileTable.init(allocator),
+    return ObjectDebugInfo{
+        .base_address = undefined,
+        .mapped_memory = undefined,
+        .ofiles = ObjectDebugInfo.OFileTable.init(allocator),
         .symbols = symbols,
         .strings = strings,
     };
@@ -1188,8 +1194,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
 }
 
 const MachoSymbol = struct {
-    nlist: *macho.nlist_64,
-    ofile: ?*macho.nlist_64,
+    nlist: *const macho.nlist_64,
+    ofile: ?*const macho.nlist_64,
     reloc: u64,
 
     /// Returns the address from the macho file
@@ -1233,18 +1239,68 @@ pub const DebugInfo = struct {
 
         var i: u32 = 0;
         while (i < image_count) : (i += 1) {
+            const base_address = std.c._dyld_get_image_vmaddr_slide(i);
+
+            if (address < base_address) continue;
+
             const header = std.c._dyld_get_image_header(i) orelse continue;
             // The array of load commands is right after the header
             var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64));
-            const cmd_ptr_end = cmd_ptr + header.sizeofcmds;
 
-            var cmd = 
-            for (cmds) |*lc| {
+            var cmds = header.ncmds;
+            while (cmds != 0) : (cmds -= 1) {
+                const lc = @ptrCast(
+                    *macho.load_command,
+                    @alignCast(@alignOf(macho.load_command), cmd_ptr),
+                );
+                cmd_ptr += lc.cmdsize;
                 if (lc.cmd != macho.LC_SEGMENT_64) continue;
 
-                const segment_cmd = @ptrCast(*macho.segment_command_64, lc);
+                const segment_cmd = @ptrCast(
+                    *const std.macho.segment_command_64,
+                    @alignCast(@alignOf(std.macho.segment_command_64), lc),
+                );
+
+                const rebased_address = address - base_address;
+                const seg_start = segment_cmd.vmaddr;
+                const seg_end = seg_start + segment_cmd.vmsize;
+
+                if (rebased_address >= seg_start and rebased_address < seg_end) {
+                    if (self.address_map.getValue(base_address)) |obj_di| {
+                        return obj_di;
+                    }
+
+                    const image_name = std.c._dyld_get_image_name(i);
+                    const exe_file = try fs.openFileAbsoluteC(image_name, .{});
+                    errdefer exe_file.close();
+
+                    const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
+                        return error.DebugInfoTooLarge;
+                    const exe_mmap = try os.mmap(
+                        null,
+                        exe_len,
+                        os.PROT_READ,
+                        os.MAP_SHARED,
+                        exe_file.handle,
+                        0,
+                    );
+                    errdefer os.munmap(exe_mmap);
+
+                    const obj_di = try self.allocator.create(ObjectDebugInfo);
+                    errdefer self.allocator.destroy(obj_di);
+
+                    try self.address_map.putNoClobber(base_address, obj_di);
+
+                    obj_di.* = try openMachODebugInfo(self.allocator, exe_mmap);
+                    obj_di.base_address = base_address;
+                    obj_di.mapped_memory = exe_mmap;
+
+                    return obj_di;
+                }
             }
         }
+
+        return error.DebugInfoNotFound;
     }
 
     fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ObjectDebugInfo {
@@ -1252,7 +1308,8 @@ pub const DebugInfo = struct {
 
         var modules: [32]windows.HMODULE = undefined;
         var modules_needed: windows.DWORD = undefined;
-        // XXX Ask for the number of modules by passing size zero
+        // TODO: Ask for the number of modules by passing size zero, 32 ought to
+        // be enough for everyone in the meanwhile
         if (windows.kernel32.K32EnumProcessModules(
             process_handle,
             @ptrCast([*]windows.HMODULE, &modules),
@@ -1275,6 +1332,10 @@ pub const DebugInfo = struct {
             const seg_end = seg_start + info.SizeOfImage;
 
             if (address >= seg_start and address < seg_end) {
+                if (self.address_map.getValue(seg_start)) |obj_di| {
+                    return obj_di;
+                }
+
                 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
                 // openFileAbsoluteW requires the prefix to be present
                 mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
@@ -1286,12 +1347,8 @@ pub const DebugInfo = struct {
                 );
                 assert(len > 0);
 
-                if (self.address_map.getValue(seg_start)) |obj_di| {
-                    return obj_di;
-                }
-
-                // XXX: The compiler segfaults if the slicing is done as a
-                // parameter (#4423)
+                // The compiler segfaults if the slicing is done as a parameter
+                // (#4423)
                 const tmp = name_buffer[0..:0];
                 const file_obj = try fs.openFileAbsoluteW(tmp, .{});
                 errdefer file_obj.close();
@@ -1312,11 +1369,49 @@ pub const DebugInfo = struct {
     }
 
     fn lookupModuleDl(self: *DebugInfo, address: usize) !*ObjectDebugInfo {
-        var ctx = DIPContext{ .address = address };
+        var ctx: struct {
+            // Input
+            address: usize,
+            // Output
+            base_address: usize = undefined,
+            name: []const u8 = undefined,
+        } = .{ .address = address };
+        const CtxTy = @TypeOf(ctx);
+
+        if (os.dl_iterate_phdr(&ctx, anyerror, struct {
+            fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
+                // The base address is too high
+                if (context.address < info.dlpi_addr)
+                    return;
 
-        // XXX Locking?
-        if (os.dl_iterate_phdr(DIPContext, dl_iterate_phdr_callback, &ctx) == 0)
+                const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
+                for (phdrs) |*phdr| {
+                    if (phdr.p_type != elf.PT_LOAD) continue;
+
+                    const seg_start = info.dlpi_addr + phdr.p_vaddr;
+                    const seg_end = seg_start + phdr.p_memsz;
+
+                    if (context.address >= seg_start and context.address < seg_end) {
+                        // Android libc uses NULL instead of an empty string to mark the
+                        // main program
+                        context.name = if (info.dlpi_name) |dlpi_name|
+                            mem.toSliceConst(u8, dlpi_name)
+                        else
+                            "";
+                        context.base_address = info.dlpi_addr;
+                        // Stop the iteration
+                        return error.Found;
+                    }
+                }
+            }
+        }.callback)) {
             return error.DebugInfoNotFound;
+        } else |err| {
+            switch (err) {
+                error.Found => {},
+                else => return error.DebugInfoNotFound,
+            }
+        }
 
         if (self.address_map.getValue(ctx.base_address)) |obj_di| {
             return obj_di;
@@ -1355,58 +1450,24 @@ pub const DebugInfo = struct {
     }
 };
 
-const DIPContext = struct {
-    address: usize,
-    base_address: usize = undefined,
-    name: []const u8 = undefined,
-};
-
-fn dl_iterate_phdr_callback(info: *os.dl_phdr_info, size: usize, context: ?*DIPContext) callconv(.C) i32 {
-    const address = context.?.address;
-
-    // The base address is too high
-    if (address < info.dlpi_addr)
-        return 0;
-
-    const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
-    for (phdrs) |*phdr| {
-        if (phdr.p_type != elf.PT_LOAD) continue;
-
-        const seg_start = info.dlpi_addr + phdr.p_vaddr;
-        const seg_end = seg_start + phdr.p_memsz;
-
-        if (address >= seg_start and address < seg_end) {
-            // Android libc uses NULL instead of an empty string to mark the
-            // main program
-            context.?.name = if (info.dlpi_name) |dlpi_name|
-                mem.toSliceConst(u8, dlpi_name)
-            else
-                "";
-            context.?.base_address = info.dlpi_addr;
-            // Stop the iteration
-            return 1;
-        }
-    }
-
-    // Continue the iteration
-    return 0;
-}
+const DIPContext = struct {};
 
 pub const ObjectDebugInfo = switch (builtin.os) {
     .macosx, .ios, .watchos, .tvos => struct {
         base_address: usize,
+        mapped_memory: []u8,
         symbols: []const MachoSymbol,
         strings: []const u8,
         ofiles: OFileTable,
 
         const OFileTable = std.HashMap(
-            *macho.nlist_64,
+            *const macho.nlist_64,
             DW.DwarfInfo,
-            std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
-            std.hash_map.getTrivialEqlFn(*macho.nlist_64),
+            std.hash_map.getHashPtrAddrFn(*const macho.nlist_64),
+            std.hash_map.getTrivialEqlFn(*const macho.nlist_64),
         );
 
-        pub fn allocator(self: DebugInfo) *mem.Allocator {
+        pub fn allocator(self: @This()) *mem.Allocator {
             return self.ofiles.allocator;
         }
     },
@@ -1426,7 +1487,7 @@ pub const ObjectDebugInfo = switch (builtin.os) {
 };
 
 /// TODO resources https://github.com/ziglang/zig/issues/4353
-fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
+fn getLineNumberInfoMacOs(di: *ObjectDebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
     const ofile = symbol.ofile orelse return error.MissingDebugInfo;
     const gop = try di.ofiles.getOrPut(ofile);
     const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: {