diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-07 10:36:41 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-15 18:49:47 +0200 |
| commit | 555b66c25567ab23402e3792bdbe81b7a4e98803 (patch) | |
| tree | 7f4dab9ab3c66caf1c4057b46d5dc068f1104bbc /src | |
| parent | dbd2eb7c7f9267e8ae508d0995c1d4c5a3b46309 (diff) | |
| download | zig-555b66c25567ab23402e3792bdbe81b7a4e98803.tar.gz zig-555b66c25567ab23402e3792bdbe81b7a4e98803.zip | |
zld: move should_rebase logic into Symbol
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO/Object.zig | 58 | ||||
| -rw-r--r-- | src/link/MachO/Symbol.zig | 21 | ||||
| -rw-r--r-- | src/link/MachO/Zld.zig | 129 | ||||
| -rw-r--r-- | src/link/MachO/reloc.zig | 55 |
4 files changed, 111 insertions, 152 deletions
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 6e8925b648..9925611243 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -339,6 +339,7 @@ const TextBlockParser = struct { zld: *Zld, nlists: []NlistWithIndex, index: u32 = 0, + match: Zld.MatchingSection, fn peek(self: *TextBlockParser) ?NlistWithIndex { return if (self.index + 1 < self.nlists.len) self.nlists[self.index + 1] else null; @@ -405,6 +406,8 @@ const TextBlockParser = struct { const senior_nlist = aliases.pop(); const senior_sym = self.zld.locals.items[senior_nlist.index]; assert(senior_sym.payload == .regular); + senior_sym.payload.regular.segment_id = self.match.seg; + senior_sym.payload.regular.section_id = self.match.sect; const start_addr = senior_nlist.nlist.n_value - self.section.addr; const end_addr = if (next_nlist) |n| n.nlist.n_value - self.section.addr else self.section.size; @@ -417,6 +420,11 @@ const TextBlockParser = struct { try out.ensureTotalCapacity(aliases.items.len); for (aliases.items) |alias| { out.appendAssumeCapacity(alias.index); + + const sym = self.zld.locals.items[alias.index]; + const reg = &sym.payload.regular; + reg.segment_id = self.match.seg; + reg.section_id = self.match.sect; } break :blk out.toOwnedSlice(); } else null; @@ -439,6 +447,18 @@ const TextBlockParser = struct { try self.object.parseRelocs(self.zld, relocs, block, start_addr); } + const is_zerofill = blk: { + const tseg = self.zld.load_commands.items[self.match.seg].Segment; + const tsect = tseg.sections.items[self.match.sect]; + const tsect_type = sectionType(tsect); + break :blk tsect_type == macho.S_ZEROFILL or + tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or + tsect_type == macho.S_THREAD_LOCAL_VARIABLES; + }; + if (is_zerofill) { + mem.set(u8, block.code, 0); + } + self.index += 1; return block; @@ -511,28 +531,16 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { .object = self, .zld = zld, .nlists = filtered_nlists, + .match = match, }; while (try parser.next()) |block| { - { - const sym = zld.locals.items[block.local_sym_index]; - const reg = &sym.payload.regular; - if (reg.file) |file| { - if (file != self) { - log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? }); - continue; - } - } - reg.segment_id = match.seg; - reg.section_id = match.sect; - } - - if (block.aliases) |aliases| { - for (aliases) |alias| { - const sym = zld.locals.items[alias]; - const reg = &sym.payload.regular; - reg.segment_id = match.seg; - reg.section_id = match.sect; + const sym = zld.locals.items[block.local_sym_index]; + const reg = &sym.payload.regular; + if (reg.file) |file| { + if (file != self) { + log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? }); + continue; } } @@ -587,6 +595,18 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { try self.parseRelocs(zld, relocs, block, 0); } + const is_zerofill = blk: { + const tseg = zld.load_commands.items[match.seg].Segment; + const tsect = tseg.sections.items[match.sect]; + const tsect_type = sectionType(tsect); + break :blk tsect_type == macho.S_ZEROFILL or + tsect_type == macho.S_THREAD_LOCAL_ZEROFILL or + tsect_type == macho.S_THREAD_LOCAL_VARIABLES; + }; + if (is_zerofill) { + mem.set(u8, block.code, 0); + } + if (zld.last_text_block) |last| { last.next = block; block.prev = last; diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index 16cd0c9ecc..5f437dc209 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -2,6 +2,7 @@ const Symbol = @This(); const std = @import("std"); const assert = std.debug.assert; +const commands = @import("commands.zig"); const macho = std.macho; const mem = std.mem; @@ -57,6 +58,8 @@ pub const Regular = struct { local_sym_index: u32 = 0, + should_rebase: bool = false, + pub const Linkage = enum { translation_unit, linkage_unit, @@ -74,6 +77,9 @@ pub const Regular = struct { if (self.weak_ref) { try std.fmt.format(writer, ".weak_ref, ", .{}); } + if (self.should_rebase) { + try std.fmt.format(writer, ".should_rebase, ", .{}); + } if (self.file) |file| { try std.fmt.format(writer, ".file = {s}, ", .{file.name.?}); } @@ -108,8 +114,8 @@ pub const Proxy = struct { /// Dynamic binding info - spots within the final /// executable where this proxy is referenced from. bind_info: std.ArrayListUnmanaged(struct { - segment_id: u16, - address: u64, + local_sym_index: u32, + offset: u32, }) = .{}, /// Dylib where to locate this symbol. @@ -198,6 +204,17 @@ pub fn isTemp(symbol: Symbol) bool { return false; } +pub fn needsTlvOffset(self: Symbol, zld: *Zld) bool { + if (self.payload != .regular) return false; + + const reg = self.payload.regular; + const seg = zld.load_command.items[reg.segment_id].Segment; + const sect = seg.sections.items[reg.section_id]; + const sect_type = commands.sectionType(sect); + + return sect_type == macho.S_THREAD_LOCAL_VARIABLES; +} + pub fn asNlist(symbol: *Symbol, strtab: *StringTable) !macho.nlist_64 { const n_strx = try strtab.getOrPut(symbol.name); const nlist = nlist: { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 69f8821cb7..2f28f20253 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -107,8 +107,6 @@ locals: std.ArrayListUnmanaged(*Symbol) = .{}, imports: std.ArrayListUnmanaged(*Symbol) = .{}, globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, -threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction -local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, stubs: std.ArrayListUnmanaged(*Symbol) = .{}, got_entries: std.ArrayListUnmanaged(*Symbol) = .{}, @@ -197,8 +195,6 @@ pub fn init(allocator: *Allocator) !Zld { } pub fn deinit(self: *Zld) void { - self.threadlocal_offsets.deinit(self.allocator); - self.local_rebases.deinit(self.allocator); self.stubs.deinit(self.allocator); self.got_entries.deinit(self.allocator); @@ -225,8 +221,6 @@ pub fn deinit(self: *Zld) void { } self.dylibs.deinit(self.allocator); - self.globals.deinit(self.allocator); - for (self.imports.items) |sym| { sym.deinit(self.allocator); self.allocator.destroy(sym); @@ -239,6 +233,7 @@ pub fn deinit(self: *Zld) void { } self.locals.deinit(self.allocator); + self.globals.deinit(self.allocator); self.strtab.deinit(); } @@ -290,7 +285,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg // try self.allocateDataSegment(); // self.allocateLinkeditSegment(); // try self.allocateSymbols(); - // try self.allocateProxyBindAddresses(); // try self.flush(); } @@ -449,7 +443,7 @@ fn updateMetadata(self: *Zld) !void { } } -const MatchingSection = struct { +pub const MatchingSection = struct { seg: u16, sect: u16, }; @@ -1140,31 +1134,6 @@ fn allocateSymbols(self: *Zld) !void { } } -fn allocateProxyBindAddresses(self: *Zld) !void { - for (self.objects.items) |object| { - for (object.sections.items) |sect| { - const relocs = sect.relocs orelse continue; - - for (relocs) |rel| { - if (rel.@"type" != .unsigned) continue; // GOT is currently special-cased - if (rel.target != .symbol) continue; - - const sym = object.symbols.items[rel.target.symbol]; - if (sym.payload != .proxy) continue; - - const target_map = sect.target_map orelse continue; - const target_seg = self.load_commands.items[target_map.segment_id].Segment; - const target_sect = target_seg.sections.items[target_map.section_id]; - - try sym.payload.proxy.bind_info.append(self.allocator, .{ - .segment_id = target_map.segment_id, - .address = target_sect.addr + target_map.offset + rel.offset, - }); - } - } - } -} - fn writeStubHelperCommon(self: *Zld) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; @@ -1748,72 +1717,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { args.source_source_sect_addr = sect.inner.addr; args.source_target_sect_addr = source_sect.inner.addr; } - - const sect_type = sectionType(target_sect); - const should_rebase = rebase: { - if (!unsigned.is_64bit) break :rebase false; - - // TODO actually, a check similar to what dyld is doing, that is, verifying - // that the segment is writable should be enough here. - const is_right_segment = blk: { - if (self.data_segment_cmd_index) |idx| { - if (target_map.segment_id == idx) { - break :blk true; - } - } - if (self.data_const_segment_cmd_index) |idx| { - if (target_map.segment_id == idx) { - break :blk true; - } - } - break :blk false; - }; - - if (!is_right_segment) break :rebase false; - if (sect_type != macho.S_LITERAL_POINTERS and - sect_type != macho.S_REGULAR) - { - break :rebase false; - } - if (rel.target == .symbol) { - const sym = object.symbols.items[rel.target.symbol]; - if (sym.payload == .proxy) { - break :rebase false; - } - } - - break :rebase true; - }; - - if (should_rebase) { - try self.local_rebases.append(self.allocator, .{ - .offset = source_addr - target_seg.inner.vmaddr, - .segment_id = target_map.segment_id, - }); - } - - // TLV is handled via a separate offset mechanism. - // Calculate the offset to the initializer. - if (sect_type == macho.S_THREAD_LOCAL_VARIABLES) tlv: { - // TODO we don't want to save offset to tlv_bootstrap - if (mem.eql(u8, object.symbols.items[rel.target.symbol].name, "__tlv_bootstrap")) break :tlv; - - const base_addr = blk: { - if (self.tlv_data_section_index) |index| { - const tlv_data = target_seg.sections.items[index]; - break :blk tlv_data.addr; - } else { - const tlv_bss = target_seg.sections.items[self.tlv_bss_section_index.?]; - break :blk tlv_bss.addr; - } - }; - // Since we require TLV data to always preceed TLV bss section, we calculate - // offsets wrt to the former if it is defined; otherwise, wrt to the latter. - try self.threadlocal_offsets.append(self.allocator, .{ - .source_addr = args.source_addr, - .offset = args.target_addr - base_addr, - }); - } }, .got_page, .got_page_off, .got_load, .got, .pointer_to_got => { const dc_seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; @@ -1839,34 +1742,6 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { try rel.resolve(args); } } - - log.debug("writing contents of '{s},{s}' section from '{s}' from 0x{x} to 0x{x}", .{ - segname, - sectname, - object.name, - target_sect_off, - target_sect_off + sect.code.len, - }); - - if (sectionType(target_sect) == macho.S_ZEROFILL or - sectionType(target_sect) == macho.S_THREAD_LOCAL_ZEROFILL or - sectionType(target_sect) == macho.S_THREAD_LOCAL_VARIABLES) - { - log.debug("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{ - segmentName(target_sect), - sectionName(target_sect), - target_sect_off, - target_sect_off + sect.code.len, - }); - - // Zero-out the space - var zeroes = try self.allocator.alloc(u8, sect.code.len); - defer self.allocator.free(zeroes); - mem.set(u8, zeroes, 0); - try self.file.?.pwriteAll(zeroes, target_sect_off); - } else { - try self.file.?.pwriteAll(sect.code, target_sect_off); - } } } } diff --git a/src/link/MachO/reloc.zig b/src/link/MachO/reloc.zig index 4693e89787..ce95b26252 100644 --- a/src/link/MachO/reloc.zig +++ b/src/link/MachO/reloc.zig @@ -1,6 +1,7 @@ const std = @import("std"); const aarch64 = @import("../../codegen/aarch64.zig"); const assert = std.debug.assert; +const commands = @import("commands.zig"); const log = std.log.scoped(.reloc); const macho = std.macho; const math = std.math; @@ -567,14 +568,60 @@ pub const Parser = struct { const index = @intCast(u32, self.zld.got_entries.items.len); out_rel.target.got_index = index; try self.zld.got_entries.append(self.zld.allocator, out_rel.target); + log.debug("adding GOT entry for symbol {s} at index {}", .{ out_rel.target.name, index }); - } + } else if (out_rel.payload == .unsigned) { + const sym = out_rel.target; + switch (sym.payload) { + .proxy => { + try sym.payload.proxy.bind_info.append(self.zld.allocator, .{ + .local_sym_index = self.block.local_sym_index, + .offset = out_rel.offset, + }); + }, + else => { + const source_sym = self.zld.locals.items[self.block.local_sym_index]; + const source_reg = &source_sym.payload.regular; + const seg = self.zld.load_commands.items[source_reg.segment_id].Segment; + const sect = seg.sections.items[source_reg.section_id]; + const sect_type = commands.sectionType(sect); + + const should_rebase = rebase: { + if (!out_rel.payload.unsigned.is_64bit) break :rebase false; + + // TODO actually, a check similar to what dyld is doing, that is, verifying + // that the segment is writable should be enough here. + const is_right_segment = blk: { + if (self.zld.data_segment_cmd_index) |idx| { + if (source_reg.segment_id == idx) { + break :blk true; + } + } + if (self.zld.data_const_segment_cmd_index) |idx| { + if (source_reg.segment_id == idx) { + break :blk true; + } + } + break :blk false; + }; + + if (!is_right_segment) break :rebase false; + if (sect_type != macho.S_LITERAL_POINTERS and + sect_type != macho.S_REGULAR) + { + break :rebase false; + } - if (out_rel.payload == .branch) { + break :rebase true; + }; + source_reg.should_rebase = should_rebase; + }, + } + } else if (out_rel.payload == .branch) blk: { const sym = out_rel.target; - if (sym.stubs_index != null) continue; - if (sym.payload != .proxy) continue; + if (sym.stubs_index != null) break :blk; + if (sym.payload != .proxy) break :blk; const index = @intCast(u32, self.zld.stubs.items.len); sym.stubs_index = index; |
