diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-13 13:19:08 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-09-13 13:19:08 +0200 |
| commit | 1965465ced82dc9c0fb93ea9182f196e6d6f4409 (patch) | |
| tree | e11f7269da691b2715629f86781f60b3619e2aff /src | |
| parent | 7aa6064638322286b5aed940c17da1c8f8fad81a (diff) | |
| download | zig-1965465ced82dc9c0fb93ea9182f196e6d6f4409.tar.gz zig-1965465ced82dc9c0fb93ea9182f196e6d6f4409.zip | |
macho: split resolveSymbols into standalone functions
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO.zig | 362 |
1 files changed, 185 insertions, 177 deletions
diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 3eb2ab1d7d..6362e6b9aa 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -147,13 +147,14 @@ unresolved: std.AutoArrayHashMapUnmanaged(u32, enum { stub, got, }) = .{}, +tentatives: std.AutoArrayHashMapUnmanaged(u32, void) = .{}, locals_free_list: std.ArrayListUnmanaged(u32) = .{}, globals_free_list: std.ArrayListUnmanaged(u32) = .{}, -dyld_private_sym_index: ?u32 = null, dyld_stub_binder_index: ?u32 = null, -stub_preamble_sym_index: ?u32 = null, +dyld_private_atom: ?*Atom = null, +stub_helper_preamble_atom: ?*Atom = null, strtab: std.ArrayListUnmanaged(u8) = .{}, strtab_dir: std.HashMapUnmanaged(u32, u32, StringIndexContext, std.hash_map.default_max_load_percentage) = .{}, @@ -777,10 +778,31 @@ pub fn flush(self: *MachO, comp: *Compilation) !void { sect.offset = self.tlv_bss_file_offset; } - try self.resolveSymbols(); - try self.addLoadDylibLCs(); + for (self.objects.items) |_, object_id| { + try self.resolveSymbolsInObject(@intCast(u16, object_id)); + } + + try self.resolveSymbolsInArchives(); + try self.resolveDyldStubBinder(); + try self.createDyldPrivateAtom(); + try self.createStubHelperPreambleAtom(); + try self.resolveSymbolsInDylibs(); + try self.createDsoHandleAtom(); try self.addCodeSignatureLC(); + for (self.unresolved.keys()) |index| { + const sym = self.undefs.items[index]; + const sym_name = self.getString(sym.n_strx); + const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable; + + log.err("undefined reference to symbol '{s}'", .{sym_name}); + log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name}); + } + if (self.unresolved.count() > 0) { + return error.UndefinedSymbolReference; + } + + try self.createTentativeDefAtoms(); try self.parseObjectsIntoAtoms(); try self.allocateGlobalSymbols(); try self.writeAtoms(); @@ -1055,6 +1077,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy try self.dylibs_map.putNoClobber(self.base.allocator, dylib.id.?.name, dylib_id); if (!(opts.is_dependent or self.referenced_dylibs.contains(dylib_id))) { + try self.addLoadDylibLC(dylib_id); try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); } @@ -1789,20 +1812,31 @@ pub fn createGotAtom(self: *MachO, key: GotIndirectionKey) !*Atom { return atom; } -fn createDyldPrivateAtom(self: *MachO) !*Atom { +fn createDyldPrivateAtom(self: *MachO) !void { + if (self.dyld_private_atom != null) return; const local_sym_index = @intCast(u32, self.locals.items.len); - try self.locals.append(self.base.allocator, .{ + const sym = try self.locals.addOne(self.base.allocator); + sym.* = .{ .n_strx = try self.makeString("l_zld_dyld_private"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, .n_value = 0, - }); - self.dyld_private_sym_index = local_sym_index; - return self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3); + }; + const atom = try self.createEmptyAtom(local_sym_index, @sizeOf(u64), 3); + self.dyld_private_atom = atom; + const match = MatchingSection{ + .seg = self.data_segment_cmd_index.?, + .sect = self.data_section_index.?, + }; + const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match); + sym.n_value = vaddr; + sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); + log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr }); } -fn createStubHelperPreambleAtom(self: *MachO) !*Atom { +fn createStubHelperPreambleAtom(self: *MachO) !void { + if (self.stub_helper_preamble_atom != null) return; const arch = self.base.options.target.cpu.arch; const size: u64 = switch (arch) { .x86_64 => 15, @@ -1815,14 +1849,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !*Atom { else => unreachable, }; const local_sym_index = @intCast(u32, self.locals.items.len); - try self.locals.append(self.base.allocator, .{ + const sym = try self.locals.addOne(self.base.allocator); + sym.* = .{ .n_strx = try self.makeString("l_zld_stub_preamble"), .n_type = macho.N_SECT, .n_sect = 0, .n_desc = 0, .n_value = 0, - }); + }; const atom = try self.createEmptyAtom(local_sym_index, size, alignment); + const dyld_private_sym_index = self.dyld_private_atom.?.local_sym_index; switch (arch) { .x86_64 => { try atom.relocs.ensureUnusedCapacity(self.base.allocator, 2); @@ -1833,7 +1869,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*Atom { atom.relocs.appendAssumeCapacity(.{ .offset = 3, .where = .local, - .where_index = self.dyld_private_sym_index.?, + .where_index = dyld_private_sym_index, .payload = .{ .signed = .{ .addend = 0, @@ -1866,7 +1902,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*Atom { atom.relocs.appendAssumeCapacity(.{ .offset = 0, .where = .local, - .where_index = self.dyld_private_sym_index.?, + .where_index = dyld_private_sym_index, .payload = .{ .page = .{ .kind = .page, @@ -1879,7 +1915,7 @@ fn createStubHelperPreambleAtom(self: *MachO) !*Atom { atom.relocs.appendAssumeCapacity(.{ .offset = 4, .where = .local, - .where_index = self.dyld_private_sym_index.?, + .where_index = dyld_private_sym_index, .payload = .{ .page_off = .{ .kind = .page, @@ -1931,8 +1967,16 @@ fn createStubHelperPreambleAtom(self: *MachO) !*Atom { }, else => unreachable, } - self.stub_preamble_sym_index = local_sym_index; - return atom; + self.stub_helper_preamble_atom = atom; + const match = MatchingSection{ + .seg = self.text_segment_cmd_index.?, + .sect = self.stub_helper_section_index.?, + }; + const alignment_pow_2 = try math.powi(u32, 2, atom.alignment); + const vaddr = try self.allocateAtom(atom, atom.size, alignment_pow_2, match); + sym.n_value = vaddr; + sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); + log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr }); } pub fn createStubHelperAtom(self: *MachO) !*Atom { @@ -1968,7 +2012,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { atom.relocs.appendAssumeCapacity(.{ .offset = 6, .where = .local, - .where_index = self.stub_preamble_sym_index.?, + .where_index = self.stub_helper_preamble_atom.?.local_sym_index, .payload = .{ .branch = .{ .arch = arch }, }, @@ -1988,7 +2032,7 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { atom.relocs.appendAssumeCapacity(.{ .offset = 4, .where = .local, - .where_index = self.stub_preamble_sym_index.?, + .where_index = self.stub_helper_preamble_atom.?.local_sym_index, .payload = .{ .branch = .{ .arch = arch }, }, @@ -2108,11 +2152,99 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { return atom; } -fn resolveSymbolsInObject( - self: *MachO, - object_id: u16, - tentatives: *std.AutoArrayHashMap(u32, void), -) !void { +fn createTentativeDefAtoms(self: *MachO) !void { + if (self.tentatives.count() == 0) return; + // Convert any tentative definition into a regular symbol and allocate + // text blocks for each tentative defintion. + while (self.tentatives.popOrNull()) |entry| { + const match = MatchingSection{ + .seg = self.data_segment_cmd_index.?, + .sect = self.bss_section_index.?, + }; + _ = try self.section_ordinals.getOrPut(self.base.allocator, match); + + const global_sym = &self.globals.items[entry.key]; + const size = global_sym.n_value; + const alignment = (global_sym.n_desc >> 8) & 0x0f; + + global_sym.n_value = 0; + global_sym.n_desc = 0; + global_sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); + + const local_sym_index = @intCast(u32, self.locals.items.len); + const local_sym = try self.locals.addOne(self.base.allocator); + local_sym.* = .{ + .n_strx = global_sym.n_strx, + .n_type = macho.N_SECT, + .n_sect = global_sym.n_sect, + .n_desc = 0, + .n_value = 0, + }; + + const resolv = self.symbol_resolver.getPtr(local_sym.n_strx) orelse unreachable; + resolv.local_sym_index = local_sym_index; + + const atom = try self.createEmptyAtom(local_sym_index, size, alignment); + const alignment_pow_2 = try math.powi(u32, 2, alignment); + const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match); + local_sym.n_value = vaddr; + global_sym.n_value = vaddr; + } +} + +fn createDsoHandleAtom(self: *MachO) !void { + if (self.strtab_dir.getAdapted(@as([]const u8, "___dso_handle"), StringSliceAdapter{ + .strtab = &self.strtab, + })) |n_strx| blk: { + const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk; + if (resolv.where != .undef) break :blk; + + const undef = &self.undefs.items[resolv.where_index]; + const match: MatchingSection = .{ + .seg = self.text_segment_cmd_index.?, + .sect = self.text_section_index.?, + }; + const local_sym_index = @intCast(u32, self.locals.items.len); + var nlist = macho.nlist_64{ + .n_strx = undef.n_strx, + .n_type = macho.N_SECT, + .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1), + .n_desc = 0, + .n_value = 0, + }; + try self.locals.append(self.base.allocator, nlist); + const global_sym_index = @intCast(u32, self.globals.items.len); + nlist.n_type |= macho.N_EXT; + nlist.n_desc = macho.N_WEAK_DEF; + try self.globals.append(self.base.allocator, nlist); + + _ = self.unresolved.fetchSwapRemove(resolv.where_index); + + undef.* = .{ + .n_strx = 0, + .n_type = macho.N_UNDF, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }; + resolv.* = .{ + .where = .global, + .where_index = global_sym_index, + .local_sym_index = local_sym_index, + }; + + // We create an empty atom for this symbol. + // TODO perhaps we should special-case special symbols? Create a separate + // linked list of atoms? + const atom = try self.createEmptyAtom(local_sym_index, 0, 0); + const sym = &self.locals.items[local_sym_index]; + const vaddr = try self.allocateAtom(atom, 0, 1, match); + sym.n_value = vaddr; + atom.dirty = false; // We don't really want to write it to file. + } +} + +fn resolveSymbolsInObject(self: *MachO, object_id: u16) !void { const object = &self.objects.items[object_id]; log.debug("resolving symbols in '{s}'", .{object.name}); @@ -2184,7 +2316,7 @@ fn resolveSymbolsInObject( const global = &self.globals.items[resolv.where_index]; if (symbolIsTentative(global.*)) { - _ = tentatives.fetchSwapRemove(resolv.where_index); + _ = self.tentatives.fetchSwapRemove(resolv.where_index); } else if (!(symbolIsWeakDef(sym) or symbolIsPext(sym)) and !(symbolIsWeakDef(global.*) or symbolIsPext(global.*))) { @@ -2236,7 +2368,7 @@ fn resolveSymbolsInObject( .where_index = global_sym_index, .file = object_id, }); - _ = try tentatives.getOrPut(global_sym_index); + _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index); continue; }; @@ -2260,7 +2392,7 @@ fn resolveSymbolsInObject( .n_desc = sym.n_desc, .n_value = sym.n_value, }); - _ = try tentatives.getOrPut(global_sym_index); + _ = try self.tentatives.getOrPut(self.base.allocator, global_sym_index); resolv.* = .{ .where = .global, .where_index = global_sym_index, @@ -2298,16 +2430,9 @@ fn resolveSymbolsInObject( } } -fn resolveSymbols(self: *MachO) !void { - var tentatives = std.AutoArrayHashMap(u32, void).init(self.base.allocator); - defer tentatives.deinit(); +fn resolveSymbolsInArchives(self: *MachO) !void { + if (self.archives.items.len == 0) return; - // First pass, resolve symbols in provided objects. - for (self.objects.items) |_, object_id| { - try self.resolveSymbolsInObject(@intCast(u16, object_id), &tentatives); - } - - // Second pass, resolve symbols in static libraries. var next_sym: usize = 0; loop: while (next_sym < self.unresolved.count()) { const sym = self.undefs.items[self.unresolved.keys()[next_sym]]; @@ -2324,74 +2449,19 @@ fn resolveSymbols(self: *MachO) !void { const object_id = @intCast(u16, self.objects.items.len); const object = try self.objects.addOne(self.base.allocator); object.* = try archive.parseObject(self.base.allocator, self.base.options.target, offsets.items[0]); - try self.resolveSymbolsInObject(object_id, &tentatives); + try self.resolveSymbolsInObject(object_id); continue :loop; } next_sym += 1; } +} - // Convert any tentative definition into a regular symbol and allocate - // text blocks for each tentative defintion. - while (tentatives.popOrNull()) |entry| { - const sym = &self.globals.items[entry.key]; - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.bss_section_index.?, - }; - _ = try self.section_ordinals.getOrPut(self.base.allocator, match); - - const size = sym.n_value; - const alignment = (sym.n_desc >> 8) & 0x0f; - - sym.n_value = 0; - sym.n_desc = 0; - sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); - var local_sym = sym.*; - local_sym.n_type = macho.N_SECT; - - const local_sym_index = @intCast(u32, self.locals.items.len); - try self.locals.append(self.base.allocator, local_sym); - - const resolv = self.symbol_resolver.getPtr(sym.n_strx) orelse unreachable; - resolv.local_sym_index = local_sym_index; - - const atom = try self.createEmptyAtom(local_sym_index, size, alignment); - const alignment_pow_2 = try math.powi(u32, 2, alignment); - const vaddr = try self.allocateAtom(atom, size, alignment_pow_2, match); - sym.n_value = vaddr; - } - - try self.resolveDyldStubBinder(); - { - const match = MatchingSection{ - .seg = self.data_segment_cmd_index.?, - .sect = self.data_section_index.?, - }; - const atom = try self.createDyldPrivateAtom(); - const sym = &self.locals.items[atom.local_sym_index]; - const vaddr = try self.allocateAtom(atom, @sizeOf(u64), 8, match); - sym.n_value = vaddr; - sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); - log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr }); - } - { - const match = MatchingSection{ - .seg = self.text_segment_cmd_index.?, - .sect = self.stub_helper_section_index.?, - }; - const atom = try self.createStubHelperPreambleAtom(); - const sym = &self.locals.items[atom.local_sym_index]; - const alignment = try math.powi(u32, 2, atom.alignment); - const vaddr = try self.allocateAtom(atom, atom.size, alignment, match); - sym.n_value = vaddr; - sym.n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1); - log.debug("allocated {s} atom at 0x{x}", .{ self.getString(sym.n_strx), vaddr }); - } +fn resolveSymbolsInDylibs(self: *MachO) !void { + if (self.dylibs.items.len == 0) return; - // Third pass, resolve symbols in dynamic libraries. - next_sym = 0; + var next_sym: usize = 0; loop: while (next_sym < self.unresolved.count()) { const sym = self.undefs.items[self.unresolved.keys()[next_sym]]; const sym_name = self.getString(sym.n_strx); @@ -2401,6 +2471,7 @@ fn resolveSymbols(self: *MachO) !void { const dylib_id = @intCast(u16, id); if (!self.referenced_dylibs.contains(dylib_id)) { + try self.addLoadDylibLC(dylib_id); try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); } @@ -2468,69 +2539,6 @@ fn resolveSymbols(self: *MachO) !void { next_sym += 1; } - - // Fourth pass, handle synthetic symbols and flag any undefined references. - if (self.strtab_dir.getAdapted(@as([]const u8, "___dso_handle"), StringSliceAdapter{ - .strtab = &self.strtab, - })) |n_strx| blk: { - const resolv = self.symbol_resolver.getPtr(n_strx) orelse break :blk; - if (resolv.where != .undef) break :blk; - - const undef = &self.undefs.items[resolv.where_index]; - const match: MatchingSection = .{ - .seg = self.text_segment_cmd_index.?, - .sect = self.text_section_index.?, - }; - const local_sym_index = @intCast(u32, self.locals.items.len); - var nlist = macho.nlist_64{ - .n_strx = undef.n_strx, - .n_type = macho.N_SECT, - .n_sect = @intCast(u8, self.section_ordinals.getIndex(match).? + 1), - .n_desc = 0, - .n_value = 0, - }; - try self.locals.append(self.base.allocator, nlist); - const global_sym_index = @intCast(u32, self.globals.items.len); - nlist.n_type |= macho.N_EXT; - nlist.n_desc = macho.N_WEAK_DEF; - try self.globals.append(self.base.allocator, nlist); - - _ = self.unresolved.fetchSwapRemove(resolv.where_index); - - undef.* = .{ - .n_strx = 0, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - resolv.* = .{ - .where = .global, - .where_index = global_sym_index, - .local_sym_index = local_sym_index, - }; - - // We create an empty atom for this symbol. - // TODO perhaps we should special-case special symbols? Create a separate - // linked list of atoms? - const atom = try self.createEmptyAtom(local_sym_index, 0, 0); - const sym = &self.locals.items[local_sym_index]; - const vaddr = try self.allocateAtom(atom, 0, 1, match); - sym.n_value = vaddr; - atom.dirty = false; // We don't really want to write it to file. - } - - for (self.unresolved.keys()) |index| { - const sym = self.undefs.items[index]; - const sym_name = self.getString(sym.n_strx); - const resolv = self.symbol_resolver.get(sym.n_strx) orelse unreachable; - - log.err("undefined reference to symbol '{s}'", .{sym_name}); - log.err(" first referenced in '{s}'", .{self.objects.items[resolv.file].name}); - } - - if (self.unresolved.count() > 0) - return error.UndefinedSymbolReference; } fn resolveDyldStubBinder(self: *MachO) !void { @@ -2557,6 +2565,7 @@ fn resolveDyldStubBinder(self: *MachO) !void { const dylib_id = @intCast(u16, id); if (!self.referenced_dylibs.contains(dylib_id)) { + try self.addLoadDylibLC(dylib_id); try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); } @@ -2733,6 +2742,21 @@ fn parseObjectsIntoAtoms(self: *MachO) !void { } } +fn addLoadDylibLC(self: *MachO, id: u16) !void { + const dylib = self.dylibs.items[id]; + const dylib_id = dylib.id orelse unreachable; + var dylib_cmd = try commands.createLoadDylibCommand( + self.base.allocator, + dylib_id.name, + dylib_id.timestamp, + dylib_id.current_version, + dylib_id.compatibility_version, + ); + errdefer dylib_cmd.deinit(self.base.allocator); + try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); + self.load_commands_dirty = true; +} + fn addCodeSignatureLC(self: *MachO) !void { if (self.code_signature_cmd_index != null or !self.requires_adhoc_codesig) return; self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len); @@ -2747,23 +2771,6 @@ fn addCodeSignatureLC(self: *MachO) !void { self.load_commands_dirty = true; } -fn addLoadDylibLCs(self: *MachO) !void { - for (self.referenced_dylibs.keys()) |id| { - const dylib = self.dylibs.items[id]; - const dylib_id = dylib.id orelse unreachable; - var dylib_cmd = try commands.createLoadDylibCommand( - self.base.allocator, - dylib_id.name, - dylib_id.timestamp, - dylib_id.current_version, - dylib_id.compatibility_version, - ); - errdefer dylib_cmd.deinit(self.base.allocator); - try self.load_commands.append(self.base.allocator, .{ .Dylib = dylib_cmd }); - self.load_commands_dirty = true; - } -} - fn setEntryPoint(self: *MachO) !void { if (self.base.options.output_mode != .Exe) return; @@ -2807,6 +2814,7 @@ pub fn deinit(self: *MachO) void { self.locals_free_list.deinit(self.base.allocator); self.symbol_resolver.deinit(self.base.allocator); self.unresolved.deinit(self.base.allocator); + self.tentatives.deinit(self.base.allocator); for (self.objects.items) |*object| { object.deinit(self.base.allocator); @@ -4417,7 +4425,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, buffer: []const u8) !void { .seg = self.text_segment_cmd_index.?, .sect = self.stub_helper_section_index.?, }) orelse return; - if (last_atom.local_sym_index == self.stub_preamble_sym_index.?) return; + if (last_atom == self.stub_helper_preamble_atom.?) return; // Because we insert lazy binding opcodes in reverse order (from last to the first atom), // we need reverse the order of atom traversal here as well. |
