-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add support for stack traces on macosx #780
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
const builtin = @import("builtin"); | ||
const std = @import("index.zig"); | ||
const io = std.io; | ||
const mem = std.mem; | ||
|
||
const MH_MAGIC_64 = 0xFEEDFACF; | ||
const MH_PIE = 0x200000; | ||
const LC_SYMTAB = 2; | ||
|
||
const MachHeader64 = packed struct { | ||
magic: u32, | ||
cputype: u32, | ||
cpusubtype: u32, | ||
filetype: u32, | ||
ncmds: u32, | ||
sizeofcmds: u32, | ||
flags: u32, | ||
reserved: u32, | ||
}; | ||
|
||
const LoadCommand = packed struct { | ||
cmd: u32, | ||
cmdsize: u32, | ||
}; | ||
|
||
const SymtabCommand = packed struct { | ||
symoff: u32, | ||
nsyms: u32, | ||
stroff: u32, | ||
strsize: u32, | ||
}; | ||
|
||
const Nlist64 = packed struct { | ||
n_strx: u32, | ||
n_type: u8, | ||
n_sect: u8, | ||
n_desc: u16, | ||
n_value: u64, | ||
}; | ||
|
||
pub const Symbol = struct { | ||
name: []const u8, | ||
address: u64, | ||
|
||
fn addressLessThan(lhs: &const Symbol, rhs: &const Symbol) bool { | ||
return lhs.address < rhs.address; | ||
} | ||
}; | ||
|
||
pub const SymbolTable = struct { | ||
allocator: &mem.Allocator, | ||
symbols: []const Symbol, | ||
strings: []const u8, | ||
|
||
// Doubles as an eyecatcher to calculate the PIE slide, see loadSymbols(). | ||
// Ideally we'd use _mh_execute_header because it's always at 0x100000000 | ||
// in the image but as it's located in a different section than executable | ||
// code, its displacement is different. | ||
pub fn deinit(self: &SymbolTable) void { | ||
self.allocator.free(self.symbols); | ||
self.symbols = []const Symbol {}; | ||
|
||
self.allocator.free(self.strings); | ||
self.strings = []const u8 {}; | ||
} | ||
|
||
pub fn search(self: &const SymbolTable, address: usize) ?&const Symbol { | ||
var min: usize = 0; | ||
var max: usize = self.symbols.len - 1; // Exclude sentinel. | ||
while (min < max) { | ||
const mid = min + (max - min) / 2; | ||
const curr = &self.symbols[mid]; | ||
const next = &self.symbols[mid + 1]; | ||
if (address >= next.address) { | ||
min = mid + 1; | ||
} else if (address < curr.address) { | ||
max = mid; | ||
} else { | ||
return curr; | ||
} | ||
} | ||
return null; | ||
} | ||
}; | ||
|
||
pub fn loadSymbols(allocator: &mem.Allocator, in: &io.FileInStream) !SymbolTable { | ||
var file = in.file; | ||
try file.seekTo(0); | ||
|
||
var hdr: MachHeader64 = undefined; | ||
try readNoEof(in, &hdr); | ||
if (hdr.magic != MH_MAGIC_64) return error.MissingDebugInfo; | ||
const is_pie = MH_PIE == (hdr.flags & MH_PIE); | ||
|
||
var pos: usize = @sizeOf(@typeOf(hdr)); | ||
var ncmd: u32 = hdr.ncmds; | ||
while (ncmd != 0) : (ncmd -= 1) { | ||
try file.seekTo(pos); | ||
var lc: LoadCommand = undefined; | ||
try readNoEof(in, &lc); | ||
if (lc.cmd == LC_SYMTAB) break; | ||
pos += lc.cmdsize; | ||
} else { | ||
return error.MissingDebugInfo; | ||
} | ||
|
||
var cmd: SymtabCommand = undefined; | ||
try readNoEof(in, &cmd); | ||
|
||
try file.seekTo(cmd.symoff); | ||
var syms = try allocator.alloc(Nlist64, cmd.nsyms); | ||
defer allocator.free(syms); | ||
try readNoEof(in, syms); | ||
|
||
try file.seekTo(cmd.stroff); | ||
var strings = try allocator.alloc(u8, cmd.strsize); | ||
errdefer allocator.free(strings); | ||
try in.stream.readNoEof(strings); | ||
|
||
var nsyms: usize = 0; | ||
for (syms) |sym| if (isSymbol(sym)) nsyms += 1; | ||
if (nsyms == 0) return error.MissingDebugInfo; | ||
|
||
var symbols = try allocator.alloc(Symbol, nsyms + 1); // Room for sentinel. | ||
errdefer allocator.free(symbols); | ||
|
||
var pie_slide: usize = 0; | ||
var nsym: usize = 0; | ||
for (syms) |sym| { | ||
if (!isSymbol(sym)) continue; | ||
const start = sym.n_strx; | ||
const end = ??mem.indexOfScalarPos(u8, strings, start, 0); | ||
const name = strings[start..end]; | ||
const address = sym.n_value; | ||
symbols[nsym] = Symbol { .name = name, .address = address }; | ||
nsym += 1; | ||
if (is_pie and mem.eql(u8, name, "_SymbolTable_deinit")) { | ||
pie_slide = @ptrToInt(SymbolTable.deinit) - address; | ||
} | ||
} | ||
|
||
// Effectively a no-op, lld emits symbols in ascending order. | ||
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan); | ||
|
||
// Insert the sentinel. Since we don't know where the last function ends, | ||
// we arbitrarily limit it to the start address + 4 KB. | ||
const top = symbols[nsyms - 1].address + 4096; | ||
symbols[nsyms] = Symbol { .name = "", .address = top }; | ||
|
||
if (pie_slide != 0) { | ||
for (symbols) |*symbol| symbol.address += pie_slide; | ||
} | ||
|
||
return SymbolTable { | ||
.allocator = allocator, | ||
.symbols = symbols, | ||
.strings = strings, | ||
}; | ||
} | ||
|
||
fn readNoEof(in: &io.FileInStream, sink: var) !void { | ||
if (@typeOf(sink) == []Nlist64) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I couldn't figure out a way to do this generically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a lot of responses to this, starting with minimal changes, ending with let's change the standard library.
fn readNoEof(in: &io.FileInStream, sink: var) !void {
switch (@typeId(@typeOf(sink)) {
builtin.TypeId.Slice => {
const T = @typeOf(sink).Child;
...
},
builtin.TypeId.Pointer => {
const T = @typeOf(sink).Child;
...
},
else => @compileError("expected slice or pointer"),
}
}
fn readNoEof(in: &io.FileInStream, comptime T: type, result: []T) !void {
return in.stream.readNoEof(([]u8)(result));
}
fn readOneNoEof(in: &io.FileInStream, comptime T: type, result: &T) !void {
return readNoEof(in, T, result[0..1]);
} related: #287
Hope that helps! My proposal is:
|
||
const T = @typeOf(sink[0]); | ||
const len = @sizeOf(T) * sink.len; | ||
const bytes = @ptrCast(&u8, &sink[0]); | ||
return in.stream.readNoEof(bytes[0..len]); | ||
} else { | ||
const T = @typeOf(*sink); | ||
const len = @sizeOf(T); | ||
const bytes = @ptrCast(&u8, sink); | ||
return in.stream.readNoEof(bytes[0..len]); | ||
} | ||
} | ||
|
||
fn isSymbol(sym: &const Nlist64) bool { | ||
return sym.n_value != 0 and sym.n_desc == 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch