diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-16 17:18:53 +0200 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-07-16 17:18:53 +0200 |
| commit | 54a403d4ff9e20daf1843725012ae44ec828a833 (patch) | |
| tree | 1c1679ee843c9caeb1986062a400cc42a645d560 /src | |
| parent | 5a2bea29315158bc05fb4b09842bbb9ae0ddfada (diff) | |
| download | zig-54a403d4ff9e20daf1843725012ae44ec828a833.tar.gz zig-54a403d4ff9e20daf1843725012ae44ec828a833.zip | |
zld: replace parsed reloc with a simple wrapper around macho.relocation_info
Diffstat (limited to 'src')
| -rw-r--r-- | src/link/MachO/Dylib.zig | 4 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 529 | ||||
| -rw-r--r-- | src/link/MachO/Symbol.zig | 285 | ||||
| -rw-r--r-- | src/link/MachO/TextBlock.zig | 64 | ||||
| -rw-r--r-- | src/link/MachO/Zld.zig | 74 |
5 files changed, 340 insertions, 616 deletions
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index b751249ce4..ca71b7613c 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -12,8 +12,8 @@ const fat = @import("fat.zig"); const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; -const Symbol = @import("Symbol.zig"); const LibStub = @import("../tapi.zig").LibStub; +const Zld = @import("Zld.zig"); usingnamespace @import("commands.zig"); @@ -324,7 +324,7 @@ fn parseSymbols(self: *Dylib) !void { _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); for (slice) |sym| { - const add_to_symtab = Symbol.isExt(sym) and (Symbol.isSect(sym) or Symbol.isIndr(sym)); + const add_to_symtab = Zld.symbolIsExt(sym) and (Zld.symbolIsSect(sym) or Zld.symbolIsIndr(sym)); if (!add_to_symtab) continue; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index e87a74e80c..1c074a97c7 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -9,13 +9,10 @@ const log = std.log.scoped(.object); const macho = std.macho; const math = std.math; const mem = std.mem; -const reloc = @import("reloc.zig"); const sort = std.sort; const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; -const Relocation = reloc.Relocation; -const Symbol = @import("Symbol.zig"); const TextBlock = @import("TextBlock.zig"); const Zld = @import("Zld.zig"); @@ -57,6 +54,8 @@ tu_comp_dir: ?[]const u8 = null, mtime: ?u64 = null, text_blocks: std.ArrayListUnmanaged(*TextBlock) = .{}, +sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{}, +symbol_mapping: std.AutoHashMapUnmanaged(u32, u32) = .{}, const DebugInfo = struct { inner: dwarf.DwarfInfo, @@ -163,6 +162,8 @@ pub fn deinit(self: *Object) void { self.symtab.deinit(self.allocator); self.strtab.deinit(self.allocator); self.text_blocks.deinit(self.allocator); + self.sections_as_symbols.deinit(self.allocator); + self.symbol_mapping.deinit(self.allocator); if (self.debug_info) |*db| { db.deinit(self.allocator); @@ -372,20 +373,17 @@ const TextBlockParser = struct { } const SeniorityContext = struct { - zld: *Zld, + object: *Object, }; fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { - const lsym = context.zld.locals.items[lhs.index]; - const rsym = context.zld.locals.items[rhs.index]; - const lreg = lsym.payload.regular; - const rreg = rsym.payload.regular; - - return switch (rreg.linkage) { - .global => true, - .linkage_unit => lreg.linkage == .translation_unit, - else => lsym.isTemp(context.zld), - }; + if (!Zld.symbolIsExt(rhs.nlist)) { + return Zld.symbolIsTemp(lhs.nlist, context.object.getString(lhs.nlist.n_strx)); + } else if (Zld.symbolIsPext(rhs.nlist) or Zld.symbolIsWeakDef(rhs.nlist)) { + return !Zld.symbolIsExt(lhs.nlist); + } else { + return true; + } } pub fn next(self: *TextBlockParser) !?*TextBlock { @@ -409,6 +407,7 @@ const TextBlockParser = struct { } else null; for (aliases.items) |*nlist_with_index| { + nlist_with_index.index = self.symbol_mapping.get(nlist_with_index.index); const sym = self.object.symbols.items[nlist_with_index.index]; if (sym.payload != .regular) { log.err("expected a regular symbol, found {s}", .{sym.payload}); @@ -424,7 +423,7 @@ const TextBlockParser = struct { sort.sort( NlistWithIndex, aliases.items, - SeniorityContext{ .zld = self.zld }, + SeniorityContext{ .object = self.object }, @This().lessThanBySeniority, ); } @@ -515,7 +514,7 @@ const TextBlockParser = struct { pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; - log.debug("analysing {s}", .{self.name.?}); + log.warn("analysing {s}", .{self.name.?}); const dysymtab = self.load_commands.items[self.dysymtab_cmd_index.?].Dysymtab; // We only care about defined symbols, so filter every other out. @@ -536,14 +535,14 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { for (seg.sections.items) |sect, id| { const sect_id = @intCast(u8, id); - log.debug("putting section '{s},{s}' as a TextBlock", .{ + log.warn("putting section '{s},{s}' as a TextBlock", .{ segmentName(sect), sectionName(sect), }); // Get matching segment/section in the final artifact. const match = (try zld.getMatchingSection(sect)) orelse { - log.debug("unhandled section", .{}); + log.warn("unhandled section", .{}); continue; }; @@ -577,200 +576,249 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { }; zld.has_stabs = zld.has_stabs or self.debug_info != null; - next: { - if (is_splittable) blocks: { - if (filtered_nlists.len == 0) break :blocks; - - // If the first nlist does not match the start of the section, - // then we need encapsulate the memory range [section start, first symbol) - // as a temporary symbol and insert the matching TextBlock. - const first_nlist = filtered_nlists[0].nlist; - if (first_nlist.n_value > sect.addr) { - const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: { - const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ - self.name.?, - segmentName(sect), - sectionName(sect), - }); - defer self.allocator.free(name); - const symbol = try zld.allocator.create(Symbol); - symbol.* = .{ - .strx = try zld.makeString(name), - .payload = .{ .undef = .{} }, - }; - try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol); - break :symbol symbol; - }; - - const local_sym_index = @intCast(u32, zld.locals.items.len); - symbol.payload = .{ - .regular = .{ - .linkage = .translation_unit, - .address = sect.addr, - .segment_id = match.seg, - .section_id = match.sect, - .file = self, - .local_sym_index = local_sym_index, - }, - }; - try zld.locals.append(zld.allocator, symbol); - - const block_code = code[0 .. first_nlist.n_value - sect.addr]; - const block_size = block_code.len; - - const block = try self.allocator.create(TextBlock); - errdefer self.allocator.destroy(block); - - block.* = TextBlock.init(self.allocator); - block.local_sym_index = local_sym_index; - block.code = try self.allocator.dupe(u8, block_code); - block.size = block_size; - block.alignment = sect.@"align"; - - const block_relocs = filterRelocs(relocs, 0, block_size); - if (block_relocs.len > 0) { - try self.parseRelocs(zld, block_relocs, block, 0); - } - - if (zld.has_dices) { - const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size); - try block.dices.ensureTotalCapacity(dices.len); - - for (dices) |dice| { - block.dices.appendAssumeCapacity(.{ - .offset = dice.offset - try math.cast(u32, sect.addr), - .length = dice.length, - .kind = dice.kind, - }); - } - } - - // Update target section's metadata - // TODO should we update segment's size here too? - // How does it tie with incremental space allocs? - const tseg = &zld.load_commands.items[match.seg].Segment; - const tsect = &tseg.sections.items[match.sect]; - const new_alignment = math.max(tsect.@"align", block.alignment); - const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); - const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; - tsect.size = new_size; - tsect.@"align" = new_alignment; - - if (zld.blocks.getPtr(match)) |last| { - last.*.next = block; - block.prev = last.*; - last.* = block; - } else { - try zld.blocks.putNoClobber(zld.allocator, match, block); - } - - try self.text_blocks.append(self.allocator, block); - } - - var parser = TextBlockParser{ - .allocator = self.allocator, - .section = sect, - .code = code, - .relocs = relocs, - .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.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? }); - block.deinit(); - self.allocator.destroy(block); - continue; - } - } - - if (reg.address == sect.addr) { - if (self.sections_as_symbols.get(sect_id)) |alias| { - // Add alias. - const local_sym_index = @intCast(u32, zld.locals.items.len); - const reg_alias = &alias.payload.regular; - reg_alias.segment_id = match.seg; - reg_alias.section_id = match.sect; - reg_alias.local_sym_index = local_sym_index; - try block.aliases.append(local_sym_index); - try zld.locals.append(zld.allocator, alias); - } - } - - // Update target section's metadata - // TODO should we update segment's size here too? - // How does it tie with incremental space allocs? - const tseg = &zld.load_commands.items[match.seg].Segment; - const tsect = &tseg.sections.items[match.sect]; - const new_alignment = math.max(tsect.@"align", block.alignment); - const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); - const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; - tsect.size = new_size; - tsect.@"align" = new_alignment; - - if (zld.blocks.getPtr(match)) |last| { - last.*.next = block; - block.prev = last.*; - last.* = block; - } else { - try zld.blocks.putNoClobber(zld.allocator, match, block); - } - - try self.text_blocks.append(self.allocator, block); - } - - break :next; - } + { + // next: { + // if (is_splittable) blocks: { + // if (filtered_nlists.len == 0) break :blocks; + + // // If the first nlist does not match the start of the section, + // // then we need encapsulate the memory range [section start, first symbol) + // // as a temporary symbol and insert the matching TextBlock. + // const first_nlist = filtered_nlists[0].nlist; + // if (first_nlist.n_value > sect.addr) { + // const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: { + // const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ + // self.name.?, + // segmentName(sect), + // sectionName(sect), + // }); + // defer self.allocator.free(name); + // const symbol = try zld.allocator.create(Symbol); + // symbol.* = .{ + // .strx = try zld.makeString(name), + // .payload = .{ .undef = .{} }, + // }; + // try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol); + // break :symbol symbol; + // }; + + // const local_sym_index = @intCast(u32, zld.locals.items.len); + // symbol.payload = .{ + // .regular = .{ + // .linkage = .translation_unit, + // .address = sect.addr, + // .segment_id = match.seg, + // .section_id = match.sect, + // .file = self, + // .local_sym_index = local_sym_index, + // }, + // }; + // try zld.locals.append(zld.allocator, symbol); + + // const block_code = code[0 .. first_nlist.n_value - sect.addr]; + // const block_size = block_code.len; + + // const block = try self.allocator.create(TextBlock); + // errdefer self.allocator.destroy(block); + + // block.* = TextBlock.init(self.allocator); + // block.local_sym_index = local_sym_index; + // block.code = try self.allocator.dupe(u8, block_code); + // block.size = block_size; + // block.alignment = sect.@"align"; + + // const block_relocs = filterRelocs(relocs, 0, block_size); + // if (block_relocs.len > 0) { + // try self.parseRelocs(zld, block_relocs, block, 0); + // } + + // if (zld.has_dices) { + // const dices = filterDice(self.data_in_code_entries.items, sect.addr, sect.addr + block_size); + // try block.dices.ensureTotalCapacity(dices.len); + + // for (dices) |dice| { + // block.dices.appendAssumeCapacity(.{ + // .offset = dice.offset - try math.cast(u32, sect.addr), + // .length = dice.length, + // .kind = dice.kind, + // }); + // } + // } + + // // Update target section's metadata + // // TODO should we update segment's size here too? + // // How does it tie with incremental space allocs? + // const tseg = &zld.load_commands.items[match.seg].Segment; + // const tsect = &tseg.sections.items[match.sect]; + // const new_alignment = math.max(tsect.@"align", block.alignment); + // const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); + // const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; + // tsect.size = new_size; + // tsect.@"align" = new_alignment; + + // if (zld.blocks.getPtr(match)) |last| { + // last.*.next = block; + // block.prev = last.*; + // last.* = block; + // } else { + // try zld.blocks.putNoClobber(zld.allocator, match, block); + // } + + // try self.text_blocks.append(self.allocator, block); + // } + + // var parser = TextBlockParser{ + // .allocator = self.allocator, + // .section = sect, + // .code = code, + // .relocs = relocs, + // .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.debug("deduping definition of {s} in {s}", .{ zld.getString(sym.strx), self.name.? }); + // block.deinit(); + // self.allocator.destroy(block); + // continue; + // } + // } + + // if (reg.address == sect.addr) { + // if (self.sections_as_symbols.get(sect_id)) |alias| { + // // Add alias. + // const local_sym_index = @intCast(u32, zld.locals.items.len); + // const reg_alias = &alias.payload.regular; + // reg_alias.segment_id = match.seg; + // reg_alias.section_id = match.sect; + // reg_alias.local_sym_index = local_sym_index; + // try block.aliases.append(local_sym_index); + // try zld.locals.append(zld.allocator, alias); + // } + // } + + // // Update target section's metadata + // // TODO should we update segment's size here too? + // // How does it tie with incremental space allocs? + // const tseg = &zld.load_commands.items[match.seg].Segment; + // const tsect = &tseg.sections.items[match.sect]; + // const new_alignment = math.max(tsect.@"align", block.alignment); + // const new_alignment_pow_2 = try math.powi(u32, 2, new_alignment); + // const new_size = mem.alignForwardGeneric(u64, tsect.size, new_alignment_pow_2) + block.size; + // tsect.size = new_size; + // tsect.@"align" = new_alignment; + + // if (zld.blocks.getPtr(match)) |last| { + // last.*.next = block; + // block.prev = last.*; + // last.* = block; + // } else { + // try zld.blocks.putNoClobber(zld.allocator, match, block); + // } + + // try self.text_blocks.append(self.allocator, block); + // } + + // break :next; + // } // Since there is no symbol to refer to this block, we create // a temp one, unless we already did that when working out the relocations // of other text blocks. - const symbol = self.sections_as_symbols.get(sect_id) orelse symbol: { - const name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ - self.name.?, - segmentName(sect), - sectionName(sect), - }); - defer self.allocator.free(name); - const symbol = try zld.allocator.create(Symbol); - symbol.* = .{ - .strx = try zld.makeString(name), - .payload = .{ .undef = .{} }, - }; - try self.sections_as_symbols.putNoClobber(self.allocator, sect_id, symbol); - break :symbol symbol; - }; - - const local_sym_index = @intCast(u32, zld.locals.items.len); - symbol.payload = .{ - .regular = .{ - .linkage = .translation_unit, - .address = sect.addr, - .segment_id = match.seg, - .section_id = match.sect, - .file = self, - .local_sym_index = local_sym_index, - }, - }; - try zld.locals.append(zld.allocator, symbol); + const block_local_sym_index = @intCast(u32, zld.locals.items.len); + const sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ + self.name.?, + segmentName(sect), + sectionName(sect), + }); + defer self.allocator.free(sym_name); + try zld.locals.append(zld.allocator, .{ + .n_strx = try zld.makeString(sym_name), + .n_type = macho.N_SECT, + .n_sect = zld.sectionId(match), + .n_desc = 0, + .n_value = sect.addr, + }); + const block_local = &zld.locals.items[block_local_sym_index]; + block_local.n_sect = zld.sectionId(match); const block = try self.allocator.create(TextBlock); errdefer self.allocator.destroy(block); block.* = TextBlock.init(self.allocator); - block.local_sym_index = local_sym_index; + block.local_sym_index = block_local_sym_index; block.code = try self.allocator.dupe(u8, code); block.size = sect.size; block.alignment = sect.@"align"; - if (relocs.len > 0) { - try self.parseRelocs(zld, relocs, block, 0); + try block.relocs.ensureTotalCapacity(relocs.len); + for (relocs) |rel| { + const out_rel: TextBlock.Relocation = outer: { + if (rel.r_extern == 0) { + const rel_sect_id = @intCast(u16, rel.r_symbolnum - 1); + const sect_sym_index = self.sections_as_symbols.get(rel_sect_id) orelse blk: { + const sect_sym_index = @intCast(u32, zld.locals.items.len); + const sect_sym_name = try std.fmt.allocPrint(self.allocator, "l_{s}_{s}_{s}", .{ + self.name.?, + segmentName(sect), + sectionName(sect), + }); + defer self.allocator.free(sect_sym_name); + try zld.locals.append(zld.allocator, .{ + .n_strx = try zld.makeString(sect_sym_name), + .n_type = macho.N_SECT, + .n_sect = 0, + .n_desc = 0, + .n_value = 0, + }); + try self.sections_as_symbols.putNoClobber(self.allocator, rel_sect_id, sect_sym_index); + break :blk sect_sym_index; + }; + break :outer .{ + .inner = rel, + .where = .local, + .where_index = sect_sym_index, + }; + } + + const rel_sym = self.symtab.items[rel.r_symbolnum]; + const rel_sym_name = self.getString(rel_sym.n_strx); + + if (Zld.symbolIsSect(rel_sym) and !Zld.symbolIsExt(rel_sym)) { + const where_index = self.symbol_mapping.get(rel.r_symbolnum) orelse unreachable; + break :outer .{ + .inner = rel, + .where = .local, + .where_index = where_index, + }; + } + + const resolv = zld.symbol_resolver.get(rel_sym_name) orelse unreachable; + switch (resolv.where) { + .global => { + break :outer .{ + .inner = rel, + .where = .local, + .where_index = resolv.local_sym_index, + }; + }, + .import => { + break :outer .{ + .inner = rel, + .where = .import, + .where_index = resolv.where_index, + }; + }, + else => unreachable, + } + }; + block.relocs.appendAssumeCapacity(out_rel); } if (zld.has_dices) { @@ -791,44 +839,41 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { // the filtered symbols and note which symbol is contained within so that // we can properly allocate addresses down the line. // While we're at it, we need to update segment,section mapping of each symbol too. - if (filtered_nlists.len > 0) { - var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator); - defer contained.deinit(); - try contained.ensureTotalCapacity(filtered_nlists.len); - - for (filtered_nlists) |nlist_with_index| { - const sym = self.symbols.items[nlist_with_index.index]; - assert(sym.payload == .regular); - const reg = &sym.payload.regular; - - reg.segment_id = match.seg; - reg.section_id = match.sect; - - const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: { - // TODO there has to be a better to handle this. - for (di.inner.func_list.items) |func| { - if (func.pc_range) |range| { - if (reg.address >= range.start and reg.address < range.end) { - break :blk TextBlock.Stab{ - .function = range.end - range.start, - }; - } + var contained = std.ArrayList(TextBlock.SymbolAtOffset).init(self.allocator); + defer contained.deinit(); + try contained.ensureTotalCapacity(filtered_nlists.len); + + for (filtered_nlists) |nlist_with_index| { + const nlist = nlist_with_index.nlist; + const local_sym_index = self.symbol_mapping.get(nlist_with_index.index) orelse unreachable; + const local = &zld.locals.items[local_sym_index]; + local.n_sect = zld.sectionId(match); + + const stab: ?TextBlock.Stab = if (self.debug_info) |di| blk: { + // TODO there has to be a better to handle this. + for (di.inner.func_list.items) |func| { + if (func.pc_range) |range| { + if (nlist.n_value >= range.start and nlist.n_value < range.end) { + break :blk TextBlock.Stab{ + .function = range.end - range.start, + }; } } - if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; - break :blk .static; - } else null; - - contained.appendAssumeCapacity(.{ - .local_sym_index = reg.local_sym_index, - .offset = nlist_with_index.nlist.n_value - sect.addr, - .stab = stab, - }); - } + } + // TODO + // if (zld.globals.contains(zld.getString(sym.strx))) break :blk .global; + break :blk .static; + } else null; - block.contained = contained.toOwnedSlice(); + contained.appendAssumeCapacity(.{ + .local_sym_index = local_sym_index, + .offset = nlist.n_value - sect.addr, + .stab = stab, + }); } + block.contained = contained.toOwnedSlice(); + // Update target section's metadata // TODO should we update segment's size here too? // How does it tie with incremental space allocs? @@ -853,26 +898,6 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { } } -fn parseRelocs( - self: *Object, - zld: *Zld, - relocs: []const macho.relocation_info, - block: *TextBlock, - base_addr: u64, -) !void { - var it = reloc.RelocIterator{ - .buffer = relocs, - }; - var parser = reloc.Parser{ - .object = self, - .zld = zld, - .it = &it, - .block = block, - .base_addr = base_addr, - }; - try parser.parse(); -} - pub fn symbolFromReloc(self: *Object, zld: *Zld, rel: macho.relocation_info) !*Symbol { const symbol = blk: { if (rel.r_extern == 1) { diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig deleted file mode 100644 index 37072b5618..0000000000 --- a/src/link/MachO/Symbol.zig +++ /dev/null @@ -1,285 +0,0 @@ -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; - -const Allocator = mem.Allocator; -const Dylib = @import("Dylib.zig"); -const Object = @import("Object.zig"); -const Zld = @import("Zld.zig"); - -/// Offset into the string table. -strx: u32, - -/// Index in GOT table for indirection. -got_index: ?u32 = null, - -/// Index in stubs table for late binding. -stubs_index: ?u32 = null, - -payload: union(enum) { - regular: Regular, - tentative: Tentative, - proxy: Proxy, - undef: Undefined, - - pub fn format(self: @This(), comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - return switch (self) { - .regular => |p| p.format(fmt, options, writer), - .tentative => |p| p.format(fmt, options, writer), - .proxy => |p| p.format(fmt, options, writer), - .undef => |p| p.format(fmt, options, writer), - }; - } -}, - -pub const Regular = struct { - /// Linkage type. - linkage: Linkage, - - /// Symbol address. - address: u64 = 0, - - /// Segment ID - segment_id: u16 = 0, - - /// Section ID - section_id: u16 = 0, - - /// Whether the symbol is a weak ref. - weak_ref: bool = false, - - /// Object file where to locate this symbol. - /// null means self-reference. - file: ?*Object = null, - - local_sym_index: u32 = 0, - - pub const Linkage = enum { - translation_unit, - linkage_unit, - global, - }; - - pub fn format(self: Regular, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try std.fmt.format(writer, "Regular {{ ", .{}); - try std.fmt.format(writer, ".linkage = {s}, ", .{self.linkage}); - try std.fmt.format(writer, ".address = 0x{x}, ", .{self.address}); - try std.fmt.format(writer, ".segment_id = {}, ", .{self.segment_id}); - try std.fmt.format(writer, ".section_id = {}, ", .{self.section_id}); - if (self.weak_ref) { - try std.fmt.format(writer, ".weak_ref, ", .{}); - } - if (self.file) |file| { - try std.fmt.format(writer, ".file = {s}, ", .{file.name.?}); - } - try std.fmt.format(writer, ".local_sym_index = {}, ", .{self.local_sym_index}); - try std.fmt.format(writer, "}}", .{}); - } - - pub fn sectionId(self: Regular, zld: *Zld) u8 { - // TODO there might be a more generic way of doing this. - var section: u8 = 0; - for (zld.load_commands.items) |cmd, cmd_id| { - if (cmd != .Segment) break; - if (cmd_id == self.segment_id) { - section += @intCast(u8, self.section_id) + 1; - break; - } - section += @intCast(u8, cmd.Segment.sections.items.len); - } - return section; - } -}; - -pub const Tentative = struct { - /// Symbol size. - size: u64, - - /// Symbol alignment as power of two. - alignment: u16, - - /// File where this symbol was referenced. - file: ?*Object = null, - - pub fn format(self: Tentative, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try std.fmt.format(writer, "Tentative {{ ", .{}); - try std.fmt.format(writer, ".size = 0x{x}, ", .{self.size}); - try std.fmt.format(writer, ".alignment = 0x{x}, ", .{self.alignment}); - if (self.file) |file| { - try std.fmt.format(writer, ".file = {s}, ", .{file.name.?}); - } - try std.fmt.format(writer, "}}", .{}); - } -}; - -pub const Proxy = struct { - /// Dylib where to locate this symbol. - /// null means self-reference. - file: ?*Dylib = null, - - local_sym_index: u32 = 0, - - pub fn dylibOrdinal(proxy: Proxy) u16 { - const dylib = proxy.file orelse return 0; - return dylib.ordinal.?; - } - - pub fn format(self: Proxy, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try std.fmt.format(writer, "Proxy {{ ", .{}); - if (self.file) |file| { - try std.fmt.format(writer, ".file = {s}, ", .{file.name.?}); - } - try std.fmt.format(writer, ".local_sym_index = {d}, ", .{self.local_sym_index}); - try std.fmt.format(writer, "}}", .{}); - } -}; - -pub const Undefined = struct { - /// File where this symbol was referenced. - /// null means synthetic, e.g., dyld_stub_binder. - file: ?*Object = null, - - pub fn format(self: Undefined, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try std.fmt.format(writer, "Undefined {{ ", .{}); - if (self.file) |file| { - try std.fmt.format(writer, ".file = {s}, ", .{file.name.?}); - } - try std.fmt.format(writer, "}}", .{}); - } -}; - -pub fn format(self: Symbol, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void { - _ = fmt; - _ = options; - try std.fmt.format(writer, "Symbol {{", .{}); - try std.fmt.format(writer, ".strx = {d}, ", .{self.strx}); - if (self.got_index) |got_index| { - try std.fmt.format(writer, ".got_index = {}, ", .{got_index}); - } - if (self.stubs_index) |stubs_index| { - try std.fmt.format(writer, ".stubs_index = {}, ", .{stubs_index}); - } - try std.fmt.format(writer, "{}, ", .{self.payload}); - try std.fmt.format(writer, "}}", .{}); -} - -pub fn isTemp(symbol: Symbol, zld: *Zld) bool { - const sym_name = zld.getString(symbol.strx); - switch (symbol.payload) { - .regular => |regular| { - if (regular.linkage == .translation_unit) { - return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); - } - }, - else => {}, - } - return false; -} - -pub fn asNlist(symbol: *Symbol, zld: *Zld) !macho.nlist_64 { - const nlist = nlist: { - switch (symbol.payload) { - .regular => |regular| { - var nlist = macho.nlist_64{ - .n_strx = symbol.strx, - .n_type = macho.N_SECT, - .n_sect = regular.sectionId(zld), - .n_desc = 0, - .n_value = regular.address, - }; - - if (regular.linkage != .translation_unit) { - nlist.n_type |= macho.N_EXT; - } - if (regular.linkage == .linkage_unit) { - nlist.n_type |= macho.N_PEXT; - nlist.n_desc |= macho.N_WEAK_DEF; - } - - break :nlist nlist; - }, - .tentative => { - // TODO - break :nlist macho.nlist_64{ - .n_strx = symbol.strx, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - }, - .proxy => |proxy| { - break :nlist macho.nlist_64{ - .n_strx = symbol.strx, - .n_type = macho.N_UNDF | macho.N_EXT, - .n_sect = 0, - .n_desc = (proxy.dylibOrdinal() * macho.N_SYMBOL_RESOLVER) | macho.REFERENCE_FLAG_UNDEFINED_NON_LAZY, - .n_value = 0, - }; - }, - .undef => { - // TODO - break :nlist macho.nlist_64{ - .n_strx = symbol.strx, - .n_type = macho.N_UNDF, - .n_sect = 0, - .n_desc = 0, - .n_value = 0, - }; - }, - } - }; - return nlist; -} - -pub fn isStab(sym: macho.nlist_64) bool { - return (macho.N_STAB & sym.n_type) != 0; -} - -pub fn isPext(sym: macho.nlist_64) bool { - return (macho.N_PEXT & sym.n_type) != 0; -} - -pub fn isExt(sym: macho.nlist_64) bool { - return (macho.N_EXT & sym.n_type) != 0; -} - -pub fn isSect(sym: macho.nlist_64) bool { - const type_ = macho.N_TYPE & sym.n_type; - return type_ == macho.N_SECT; -} - -pub fn isUndf(sym: macho.nlist_64) bool { - const type_ = macho.N_TYPE & sym.n_type; - return type_ == macho.N_UNDF; -} - -pub fn isIndr(sym: macho.nlist_64) bool { - const type_ = macho.N_TYPE & sym.n_type; - return type_ == macho.N_INDR; -} - -pub fn isAbs(sym: macho.nlist_64) bool { - const type_ = macho.N_TYPE & sym.n_type; - return type_ == macho.N_ABS; -} - -pub fn isWeakDef(sym: macho.nlist_64) bool { - return (sym.n_desc & macho.N_WEAK_DEF) != 0; -} - -pub fn isWeakRef(sym: macho.nlist_64) bool { - return (sym.n_desc & macho.N_WEAK_REF) != 0; -} diff --git a/src/link/MachO/TextBlock.zig b/src/link/MachO/TextBlock.zig index 93763ded18..04cb33855c 100644 --- a/src/link/MachO/TextBlock.zig +++ b/src/link/MachO/TextBlock.zig @@ -5,10 +5,9 @@ const commands = @import("commands.zig"); const log = std.log.scoped(.text_block); const macho = std.macho; const mem = std.mem; -const reloc = @import("reloc.zig"); const Allocator = mem.Allocator; -const Relocation = reloc.Relocation; +const Arch = std.Target.Cpu.Arch; const Zld = @import("Zld.zig"); allocator: *Allocator, @@ -102,6 +101,15 @@ pub const Stab = union(enum) { } }; +pub const Relocation = struct { + inner: macho.relocation_info, + where: enum { + local, + import, + }, + where_index: u32, +}; + pub fn init(allocator: *Allocator) TextBlock { return .{ .allocator = allocator, @@ -137,18 +145,10 @@ pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void { const source_addr = blk: { const sym = zld.locals.items[self.local_sym_index]; - break :blk sym.payload.regular.address + rel.offset; + break :blk sym.n_value + rel.offset; }; const target_addr = blk: { - const is_via_got = switch (rel.payload) { - .pointer_to_got => true, - .page => |page| page.kind == .got, - .page_off => |page_off| page_off.kind == .got, - .load => |load| load.kind == .got, - else => false, - }; - - if (is_via_got) { + if (isGotIndirection(rel, zld.target.?.cpu.arch)) { const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment; const got = dc_seg.sections.items[zld.got_section_index.?]; const got_index = rel.target.got_index orelse { @@ -228,31 +228,18 @@ pub fn print_this(self: *const TextBlock, zld: *Zld) void { log.warn(" stab: {}", .{stab}); } if (self.aliases.items.len > 0) { - log.warn(" aliases:", .{}); - for (self.aliases.items) |index| { - log.warn(" {}: {}", .{ index, zld.locals.items[index] }); - } + log.warn(" aliases: {any}", .{self.aliases.items}); } if (self.references.count() > 0) { - log.warn(" references:", .{}); - for (self.references.keys()) |index| { - log.warn(" {}: {}", .{ index, zld.locals.items[index] }); - } + log.warn(" references: {any}", .{self.references.keys()}); } if (self.contained) |contained| { log.warn(" contained symbols:", .{}); for (contained) |sym_at_off| { if (sym_at_off.stab) |stab| { - log.warn(" {}: {}, stab: {}\n", .{ - sym_at_off.offset, - zld.locals.items[sym_at_off.local_sym_index], - stab, - }); + log.warn(" {}: {}, stab: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index, stab }); } else { - log.warn(" {}: {}\n", .{ - sym_at_off.offset, - zld.locals.items[sym_at_off.local_sym_index], - }); + log.warn(" {}: {}", .{ sym_at_off.offset, sym_at_off.local_sym_index }); } } } @@ -282,3 +269,22 @@ pub fn print(self: *const TextBlock, zld: *Zld) void { } self.print_this(zld); } + +fn isGotIndirection(rel: macho.relocation_info, arch: Arch) bool { + return switch (arch) { + .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, rel.r_type)) { + .ARM64_RELOC_POINTER_TO_GOT, + .ARM64_RELOC_GOT_LOAD_PAGE21, + .ARM64_RELOC_GOT_LOAD_PAGEOFF12, + => true, + else => false, + }, + .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, rel.r_type)) { + .X86_64_RELOC_GOT, + .X86_64_RELOC_GOT_LOAD, + => true, + else => false, + }, + else => unreachable, + }; +} diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index d6dd9f597c..862e6b5b0c 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -105,7 +105,6 @@ imports: std.ArrayListUnmanaged(macho.nlist_64) = .{}, undefs: std.ArrayListUnmanaged(macho.nlist_64) = .{}, tentatives: std.ArrayListUnmanaged(macho.nlist_64) = .{}, symbol_resolver: std.StringArrayHashMapUnmanaged(SymbolWithLoc) = .{}, -object_mapping: std.AutoHashMapUnmanaged(u16, []u32) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -199,14 +198,6 @@ pub fn deinit(self: *Zld) void { } self.symbol_resolver.deinit(self.allocator); - { - var it = self.object_mapping.valueIterator(); - while (it.next()) |value_ptr| { - self.allocator.free(value_ptr.*); - } - } - self.object_mapping.deinit(self.allocator); - self.strtab.deinit(self.allocator); // TODO dealloc all blocks @@ -251,33 +242,33 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg try self.resolveSymbols(); log.warn("locals", .{}); - for (self.locals.items) |sym| { - log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym }); + for (self.locals.items) |sym, id| { + log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); } log.warn("globals", .{}); - for (self.globals.items) |sym| { - log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym }); + for (self.globals.items) |sym, id| { + log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); } log.warn("tentatives", .{}); - for (self.tentatives.items) |sym| { - log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym }); + for (self.tentatives.items) |sym, id| { + log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); } log.warn("undefines", .{}); - for (self.undefs.items) |sym| { - log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym }); + for (self.undefs.items) |sym, id| { + log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); } log.warn("imports", .{}); - for (self.imports.items) |sym| { - log.warn(" | {s}: {}", .{ self.getString(sym.n_strx), sym }); + for (self.imports.items) |sym, id| { + log.warn(" {d}: {s}, {}", .{ id, self.getString(sym.n_strx), sym }); } log.warn("symbol resolver", .{}); for (self.symbol_resolver.keys()) |key| { - log.warn(" | {s} => {}", .{ key, self.symbol_resolver.get(key).? }); + log.warn(" {s} => {}", .{ key, self.symbol_resolver.get(key).? }); } log.warn("mappings", .{}); @@ -285,7 +276,7 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg const object_id = @intCast(u16, id); log.warn(" in object {s}", .{object.name.?}); for (object.symtab.items) |sym, sym_id| { - if (self.localSymIndex(object_id, @intCast(u32, sym_id))) |local_id| { + if (object.symbol_mapping.get(@intCast(u32, sym_id))) |local_id| { log.warn(" | {d} => {d}", .{ sym_id, local_id }); } else { log.warn(" | {d} no local mapping for {s}", .{ sym_id, object.getString(sym.n_strx) }); @@ -293,8 +284,19 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg } } + try self.parseTextBlocks(); + + var it = self.blocks.iterator(); + while (it.next()) |entry| { + const seg = self.load_commands.items[entry.key_ptr.seg].Segment; + const sect = seg.sections.items[entry.key_ptr.sect]; + + log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) }); + log.warn(" {}", .{sect}); + entry.value_ptr.*.print(self); + } + return error.TODO; - // try self.parseTextBlocks(); // try self.sortSections(); // try self.addRpaths(args.rpaths); // try self.addDataInCodeLC(); @@ -305,16 +307,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg // self.allocateLinkeditSegment(); // try self.allocateTextBlocks(); - // // var it = self.blocks.iterator(); - // // while (it.next()) |entry| { - // // const seg = self.load_commands.items[entry.key_ptr.seg].Segment; - // // const sect = seg.sections.items[entry.key_ptr.sect]; - - // // log.warn("\n\n{s},{s} contents:", .{ segmentName(sect), sectionName(sect) }); - // // log.warn(" {}", .{sect}); - // // entry.value_ptr.*.print(self); - // // } - // try self.flush(); } @@ -1414,10 +1406,6 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { log.warn("resolving symbols in '{s}'", .{object.name}); - const mapping = try self.allocator.alloc(u32, object.symtab.items.len); - mem.set(u32, mapping, 0); - try self.object_mapping.putNoClobber(self.allocator, object_id, mapping); - for (object.symtab.items) |sym, id| { const sym_id = @intCast(u32, id); const sym_name = object.getString(sym.n_strx); @@ -1464,7 +1452,7 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { .n_desc = 0, .n_value = sym.n_value, }); - mapping[sym_id] = local_sym_index; + try object.symbol_mapping.putNoClobber(self.allocator, sym_id, local_sym_index); // If the symbol's scope is not local aka translation unit, then we need work out // if we should save the symbol as a global, or potentially flag the error. @@ -2987,15 +2975,6 @@ pub fn getString(self: *Zld, off: u32) []const u8 { return mem.spanZ(@ptrCast([*:0]const u8, self.strtab.items.ptr + off)); } -fn localSymIndex(self: Zld, object_id: u16, orig_id: u32) ?u32 { - const mapping = self.object_mapping.get(object_id) orelse return null; - const local_sym_index = mapping[orig_id]; - if (local_sym_index == 0) { - return null; - } - return local_sym_index; -} - pub fn symbolIsStab(sym: macho.nlist_64) bool { return (macho.N_STAB & sym.n_type) != 0; } @@ -3045,10 +3024,9 @@ pub fn symbolIsNull(sym: macho.nlist_64) bool { return sym.n_value == 0 and sym.n_desc == 0 and sym.n_type == 0 and sym.n_strx == 0 and sym.n_sect == 0; } -pub fn symbolIsTemp(self: Zld, sym: macho.nlist_64) bool { +pub fn symbolIsTemp(sym: macho.nlist_64, sym_name: []const u8) bool { if (!symbolIsSect(sym)) return false; if (symbolIsExt(sym)) return false; - const sym_name = self.getString(sym.n_strx); return mem.startsWith(u8, sym_name, "l") or mem.startsWith(u8, sym_name, "L"); } |
