zig/std/macho.zig
2018-02-21 02:00:33 -05:00

171 lines
4.8 KiB
Zig

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 readOneNoEof(in, MachHeader64, &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 readOneNoEof(in, LoadCommand, &lc);
if (lc.cmd == LC_SYMTAB) break;
pos += lc.cmdsize;
} else {
return error.MissingDebugInfo;
}
var cmd: SymtabCommand = undefined;
try readOneNoEof(in, SymtabCommand, &cmd);
try file.seekTo(cmd.symoff);
var syms = try allocator.alloc(Nlist64, cmd.nsyms);
defer allocator.free(syms);
try readNoEof(in, Nlist64, 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, 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]);
}
fn isSymbol(sym: &const Nlist64) bool {
return sym.n_value != 0 and sym.n_desc == 0;
}