macho: document more code + add test case

This commit is contained in:
Jakub Konka 2021-01-09 20:38:55 +01:00
parent b86d0e488b
commit 7d40aaad2b
4 changed files with 72 additions and 63 deletions

View File

@ -1865,19 +1865,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
// If it doesn't, it will get autofreed when we clean up the extern symbol table.
const decl_name = try std.fmt.allocPrint(self.bin_file.allocator, "_{s}", .{decl.name});
const already_defined = macho_file.extern_lazy_symbols.contains(decl_name);
const symbol: u32 = blk: {
if (macho_file.extern_lazy_symbols.get(decl_name)) |sym| {
self.bin_file.allocator.free(decl_name);
break :blk sym.index;
} else {
const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
.name = decl_name,
.dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
.index = index,
});
break :blk index;
}
const symbol: u32 = if (macho_file.extern_lazy_symbols.getIndex(decl_name)) |index| blk: {
self.bin_file.allocator.free(decl_name);
break :blk @intCast(u32, index);
} else blk: {
const index = @intCast(u32, macho_file.extern_lazy_symbols.items().len);
try macho_file.extern_lazy_symbols.putNoClobber(self.bin_file.allocator, decl_name, .{
.name = decl_name,
.dylib_ordinal = 1, // TODO this is now hardcoded, since we only support libSystem.
});
break :blk index;
};
try macho_file.stub_fixups.append(self.bin_file.allocator, .{
.symbol = symbol,

View File

@ -159,16 +159,13 @@ last_text_block: ?*TextBlock = null,
/// prior to calling `generateSymbol`, and then immediately deallocated
/// rather than sitting in the global scope.
pie_fixups: std.ArrayListUnmanaged(PieFixup) = .{},
/// A list of all stub (extern decls) fixups required for this run of the linker.
/// Warning, this is currently NOT thread-safe. See the TODO below.
/// TODO Move this list inside `updateDecl` where it should be allocated
/// prior to calling `generateSymbol`, and then immediately deallocated
/// rather than sitting in the global scope.
stub_fixups: std.ArrayListUnmanaged(StubFixup) = .{},
pub const StubFixup = struct {
symbol: u32,
already_defined: bool,
start: usize,
len: usize,
};
pub const PieFixup = struct {
/// Target address we wanted to address in absolute terms.
address: u64,
@ -179,6 +176,19 @@ pub const PieFixup = struct {
len: usize,
};
pub const StubFixup = struct {
/// Id of extern (lazy) symbol.
symbol: u32,
/// Signals whether the symbol has already been declared before. If so,
/// then there is no need to rewrite the stub entry and related.
already_defined: bool,
/// Where in the byte stream we should perform the fixup.
start: usize,
/// The length of the byte stream. For x86_64, this will be
/// variable. For aarch64, it will be fixed at 4 bytes.
len: usize,
};
/// `alloc_num / alloc_den` is the factor of padding when allocating.
pub const alloc_num = 4;
pub const alloc_den = 3;
@ -1030,13 +1040,20 @@ pub fn deinit(self: *MachO) void {
if (self.d_sym) |*ds| {
ds.deinit(self.base.allocator);
}
for (self.extern_lazy_symbols.items()) |*entry| {
entry.value.deinit(self.base.allocator);
}
self.extern_lazy_symbols.deinit(self.base.allocator);
for (self.extern_nonlazy_symbols.items()) |*entry| {
entry.value.deinit(self.base.allocator);
}
self.extern_nonlazy_symbols.deinit(self.base.allocator);
self.pie_fixups.deinit(self.base.allocator);
self.stub_fixups.deinit(self.base.allocator);
self.text_block_free_list.deinit(self.base.allocator);
self.offset_table.deinit(self.base.allocator);
self.offset_table_free_list.deinit(self.base.allocator);
self.string_table.deinit(self.base.allocator);
self.extern_lazy_symbols.deinit(self.base.allocator);
self.extern_nonlazy_symbols.deinit(self.base.allocator);
self.global_symbols.deinit(self.base.allocator);
self.global_symbol_free_list.deinit(self.base.allocator);
self.local_symbols.deinit(self.base.allocator);
@ -2047,7 +2064,6 @@ pub fn populateMissingMetadata(self: *MachO) !void {
try self.extern_nonlazy_symbols.putNoClobber(self.base.allocator, name, .{
.name = name,
.dylib_ordinal = 1, // TODO this is currently hardcoded.
.index = index,
.segment = self.data_const_segment_cmd_index.?,
.offset = index * @sizeOf(u64),
});
@ -2582,12 +2598,12 @@ fn writeAllGlobalAndUndefSymbols(self: *MachO) !void {
}
fn writeIndirectSymbolTable(self: *MachO) !void {
const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stubs = &text_seg.sections.items[self.stubs_section_index.?];
const dc_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const got = &dc_seg.sections.items[self.data_got_section_index.?];
const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const la = &data_seg.sections.items[self.la_symbol_ptr_section_index.?];
const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
const stubs = &text_segment.sections.items[self.stubs_section_index.?];
const data_const_seg = &self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
const got = &data_const_seg.sections.items[self.data_got_section_index.?];
const data_segment = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
const la_symbol_ptr = &data_segment.sections.items[self.la_symbol_ptr_section_index.?];
const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab;
dysymtab.nindirectsyms = 0;
@ -2595,8 +2611,8 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
var off = dysymtab.indirectsymoff;
stubs.reserved1 = 0;
for (self.extern_lazy_symbols.items()) |entry| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
for (self.extern_lazy_symbols.items()) |_, i| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
mem.writeIntLittle(u32, &buf, symtab_idx);
try self.base.file.?.pwriteAll(&buf, off);
off += @sizeOf(u32);
@ -2605,17 +2621,17 @@ fn writeIndirectSymbolTable(self: *MachO) !void {
const base_id = @intCast(u32, self.extern_lazy_symbols.items().len);
got.reserved1 = base_id;
for (self.extern_nonlazy_symbols.items()) |entry| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index + base_id);
for (self.extern_nonlazy_symbols.items()) |_, i| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + i + base_id);
mem.writeIntLittle(u32, &buf, symtab_idx);
try self.base.file.?.pwriteAll(&buf, off);
off += @sizeOf(u32);
dysymtab.nindirectsyms += 1;
}
la.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
for (self.extern_lazy_symbols.items()) |entry| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + entry.value.index);
la_symbol_ptr.reserved1 = got.reserved1 + @intCast(u32, self.extern_nonlazy_symbols.items().len);
for (self.extern_lazy_symbols.items()) |_, i| {
const symtab_idx = @intCast(u32, dysymtab.iundefsym + i);
mem.writeIntLittle(u32, &buf, symtab_idx);
try self.base.file.?.pwriteAll(&buf, off);
off += @sizeOf(u32);

View File

@ -20,13 +20,15 @@ pub const ExternSymbol = struct {
/// dylibs.
dylib_ordinal: i64 = 0,
/// Id of the segment where this symbol is defined (will have its address
/// resolved).
segment: u16 = 0,
/// Offset relative to the start address of the `segment`.
offset: u32 = 0,
addend: ?i32 = null,
index: u32,
pub fn deinit(self: *ExternSymbol, allocator: *Allocator) void {
if (self.name) |*name| {
if (self.name) |name| {
allocator.free(name);
}
}
@ -77,12 +79,6 @@ pub fn bindInfoSize(symbols: []*const ExternSymbol) !u64 {
size += 1;
try leb.writeILEB128(writer, symbol.offset);
if (symbol.addend) |addend| {
size += 1;
try leb.writeILEB128(writer, addend);
}
size += 2;
}
@ -110,12 +106,6 @@ pub fn writeBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void {
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
try leb.writeILEB128(writer, symbol.offset);
if (symbol.addend) |addend| {
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
try leb.writeILEB128(writer, addend);
}
try writer.writeByte(macho.BIND_OPCODE_DO_BIND);
try writer.writeByte(macho.BIND_OPCODE_DONE);
}
@ -129,12 +119,6 @@ pub fn lazyBindInfoSize(symbols: []*const ExternSymbol) !u64 {
for (symbols) |symbol| {
size += 1;
try leb.writeILEB128(writer, symbol.offset);
if (symbol.addend) |addend| {
size += 1;
try leb.writeILEB128(writer, addend);
}
size += 1;
if (symbol.dylib_ordinal > 15) {
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));
@ -156,11 +140,6 @@ pub fn writeLazyBindInfo(symbols: []*const ExternSymbol, writer: anytype) !void
try writer.writeByte(macho.BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB | @truncate(u4, symbol.segment));
try leb.writeILEB128(writer, symbol.offset);
if (symbol.addend) |addend| {
try writer.writeByte(macho.BIND_OPCODE_SET_ADDEND_SLEB);
try leb.writeILEB128(writer, addend);
}
if (symbol.dylib_ordinal > 15) {
try writer.writeByte(macho.BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
try leb.writeULEB128(writer, @bitCast(u64, symbol.dylib_ordinal));

View File

@ -199,4 +199,21 @@ pub fn addCases(ctx: *TestContext) !void {
"",
);
}
{
var case = ctx.exe("hello world linked to libc", macos_aarch64);
// TODO rewrite this test once we handle more int conversions and return args.
case.addCompareOutput(
\\extern "c" fn write(usize, usize, usize) void;
\\extern "c" fn exit(usize) noreturn;
\\
\\export fn _start() noreturn {
\\ write(1, @ptrToInt("Hello, World!\n"), 14);
\\ exit(0);
\\}
,
"Hello, World!\n",
);
}
}