zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 54888c6f4699b07eadd21b744d797006fb96a284 (tree)
parent 51e334af447b126862238f0743342755d719f897
Author: Jakub Konka <kubkon@jakubkonka.com>
Date:   Tue,  6 Jul 2021 00:00:11 +0200

zld: create TextBlocks for tentative definitions

and fix the links in the `TextBlock`s linked list!

Diffstat:
Msrc/link/MachO/Object.zig | 57+++++++++++++++++++++++++++++++++------------------------
Msrc/link/MachO/Symbol.zig | 20++++++++++++++++++--
Msrc/link/MachO/Zld.zig | 144+++++++++++++++++++++++++++-----------------------------------------------------
3 files changed, 99 insertions(+), 122 deletions(-)

diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig @@ -9,6 +9,7 @@ const log = std.log.scoped(.object); const macho = std.macho; const mem = std.mem; const reloc = @import("reloc.zig"); +const sort = std.sort; const Allocator = mem.Allocator; const Arch = std.Target.Cpu.Arch; @@ -345,13 +346,15 @@ const TextBlockParser = struct { }; fn lessThanBySeniority(context: SeniorityContext, lhs: NlistWithIndex, rhs: NlistWithIndex) bool { - const lreg = context.zld.locals.items[lhs.index].payload.regular; - const rreg = context.zld.locals.items[rhs.index].payload.regular; + 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 => false, + else => lsym.isTemp(), }; } @@ -388,7 +391,7 @@ const TextBlockParser = struct { if (aliases.items.len > 1) { // Bubble-up senior symbol as the main link to the text block. - std.sort.sort( + sort.sort( NlistWithIndex, aliases.items, SeniorityContext{ .zld = self.zld }, @@ -427,13 +430,12 @@ const TextBlockParser = struct { }; self.index += 1; - block.print_this(self.zld); return block; } }; -pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock { +pub fn parseTextBlocks(self: *Object, zld: *Zld) !void { const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; log.warn("analysing {s}", .{self.name.?}); @@ -453,9 +455,7 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock { }); } - std.sort.sort(NlistWithIndex, sorted_nlists.items, {}, NlistWithIndex.lessThan); - - var last_block: ?*TextBlock = null; + sort.sort(NlistWithIndex, sorted_nlists.items, {}, NlistWithIndex.lessThan); for (seg.sections.items) |sect, sect_id| { log.warn("putting section '{s},{s}' as a TextBlock", .{ @@ -496,24 +496,35 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock { }; while (try parser.next()) |block| { - const sym = zld.locals.items[block.local_sym_index]; - if (sym.payload.regular.file) |file| { - if (file != self) { - log.warn("deduping definition of {s} in {s}", .{ sym.name, self.name.? }); - continue; + { + 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; } - block.segment_id = match.seg; - block.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; + } + } // TODO parse relocs - if (last_block) |last| { + if (zld.last_text_block) |last| { last.next = block; block.prev = last; } - last_block = block; + zld.last_text_block = block; } break :next; @@ -531,6 +542,8 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock { symbol.payload = .{ .regular = .{ .linkage = .translation_unit, + .segment_id = match.seg, + .section_id = match.sect, .file = self, }, }; @@ -545,21 +558,17 @@ pub fn parseTextBlocks(self: *Object, zld: *Zld) !?*TextBlock { .code = code, .size = sect.size, .alignment = sect.@"align", - .segment_id = match.seg, - .section_id = match.sect, }; // TODO parse relocs - if (last_block) |last| { + if (zld.last_text_block) |last| { last.next = block; block.prev = last; } - last_block = block; + zld.last_text_block = block; } } - - return last_block; } pub fn parseInitializers(self: *Object) !void { diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig @@ -46,7 +46,7 @@ pub const Regular = struct { segment_id: u16 = 0, /// Section ID - section: u16 = 0, + section_id: u16 = 0, /// Whether the symbol is a weak ref. weak_ref: bool = false, @@ -69,7 +69,8 @@ pub const Regular = struct { 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, ".section = {}, ", .{self.section}); + 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, ", .{}); } @@ -170,6 +171,21 @@ pub fn new(allocator: *Allocator, name: []const u8) !*Symbol { return new_sym; } +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, ".name = {s}, ", .{self.name}); + 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) bool { switch (symbol.payload) { .regular => |regular| { diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig @@ -107,11 +107,6 @@ locals: std.ArrayListUnmanaged(*Symbol) = .{}, imports: std.ArrayListUnmanaged(*Symbol) = .{}, globals: std.StringArrayHashMapUnmanaged(*Symbol) = .{}, -/// Offset into __DATA,__common section. -/// Set if the linker found tentative definitions in any of the objects. -tentative_defs_offset: u64 = 0, -has_tentative_defs: bool = false, - threadlocal_offsets: std.ArrayListUnmanaged(TlvOffset) = .{}, // TODO merge with Symbol abstraction local_rebases: std.ArrayListUnmanaged(Pointer) = .{}, stubs: std.ArrayListUnmanaged(*Symbol) = .{}, @@ -145,8 +140,6 @@ pub const TextBlock = struct { relocs: ?[]*Relocation = null, size: u64, alignment: u32, - segment_id: u16 = 0, - section_id: u16 = 0, next: ?*TextBlock = null, prev: ?*TextBlock = null, @@ -168,23 +161,21 @@ pub const TextBlock = struct { pub fn print_this(self: *const TextBlock, zld: *Zld) void { log.warn("TextBlock", .{}); - log.warn(" | {}: '{s}'", .{ self.local_sym_index, zld.locals.items[self.local_sym_index].name }); + log.warn(" | {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] }); if (self.aliases) |aliases| { log.warn(" | Aliases:", .{}); for (aliases) |index| { - log.warn(" | {}: '{s}'", .{ index, zld.locals.items[index].name }); + log.warn(" | {}: {}", .{ index, zld.locals.items[index] }); } } if (self.references) |references| { log.warn(" | References:", .{}); for (references) |index| { - log.warn(" | {}: '{s}'", .{ index, zld.locals.items[index].name }); + log.warn(" | {}: {}", .{ index, zld.locals.items[index] }); } } log.warn(" | size = {}", .{self.size}); log.warn(" | align = {}", .{self.alignment}); - log.warn(" | segment_id = {}", .{self.segment_id}); - log.warn(" | section_id = {}", .{self.section_id}); } pub fn print(self: *const TextBlock, zld: *Zld) void { @@ -300,7 +291,6 @@ pub fn link(self: *Zld, files: []const []const u8, output: Output, args: LinkArg // try self.allocateDataSegment(); // self.allocateLinkeditSegment(); // try self.allocateSymbols(); - // try self.allocateTentativeSymbols(); // try self.allocateProxyBindAddresses(); // try self.flush(); } @@ -415,37 +405,6 @@ fn updateMetadata(self: *Zld) !void { } } - // Ensure we have __DATA,__common section if we have tentative definitions. - // Update size and alignment of __DATA,__common section. - if (self.has_tentative_defs) { - const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const common_section_index = self.common_section_index orelse ind: { - self.common_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, "__common", .{ - .flags = macho.S_ZEROFILL, - }); - break :ind self.common_section_index.?; - }; - const common_sect = &data_seg.sections.items[common_section_index]; - - var max_align: u16 = 0; - var added_size: u64 = 0; - for (self.globals.values()) |sym| { - if (sym.payload != .tentative) continue; - max_align = math.max(max_align, sym.payload.tentative.alignment); - added_size += sym.payload.tentative.size; - } - - common_sect.@"align" = math.max(common_sect.@"align", max_align); - - const alignment = try math.powi(u32, 2, common_sect.@"align"); - const offset = mem.alignForwardGeneric(u64, common_sect.size, alignment); - const size = mem.alignForwardGeneric(u64, added_size, alignment); - - common_sect.size = offset + size; - self.tentative_defs_offset = offset; - } - tlv_align: { const has_tlv = self.tlv_section_index != null or @@ -1182,48 +1141,6 @@ fn allocateSymbols(self: *Zld) !void { } } -fn allocateTentativeSymbols(self: *Zld) !void { - if (!self.has_tentative_defs) return; - - const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const common_sect = &data_seg.sections.items[self.common_section_index.?]; - - const alignment = try math.powi(u32, 2, common_sect.@"align"); - var base_address: u64 = common_sect.addr + self.tentative_defs_offset; - - log.debug("base address for tentative definitions 0x{x}", .{base_address}); - - // TODO there might be a more generic way of doing this. - var section: u8 = 0; - for (self.load_commands.items) |cmd, cmd_id| { - if (cmd != .Segment) break; - if (cmd_id == self.data_segment_cmd_index.?) { - section += @intCast(u8, self.common_section_index.?) + 1; - break; - } - section += @intCast(u8, cmd.Segment.sections.items.len); - } - - // Convert tentative definitions into regular symbols. - for (self.globals.values()) |sym| { - if (sym.payload != .tentative) continue; - - const address = mem.alignForwardGeneric(u64, base_address + sym.payload.tentative.size, alignment); - - log.debug("tentative definition '{s}' allocated from 0x{x} to 0x{x}", .{ sym.name, base_address, address }); - - sym.payload = .{ - .regular = .{ - .linkage = .global, - .address = base_address, - .section = section, - .weak_ref = false, - }, - }; - base_address = address; - } -} - fn allocateProxyBindAddresses(self: *Zld) !void { for (self.objects.items) |object| { for (object.sections.items) |sect| { @@ -1648,15 +1565,56 @@ fn resolveSymbols(self: *Zld) !void { } // Put any globally defined regular symbol as local. - // Mark if we need to allocate zerofill section for tentative definitions + // Convert any tentative definition into a regular symbol and allocate + // text blocks for each tentative defintion. for (self.globals.values()) |symbol| { switch (symbol.payload) { .regular => |*reg| { reg.local_sym_index = @intCast(u32, self.locals.items.len); try self.locals.append(self.allocator, symbol); }, - .tentative => { - self.has_tentative_defs = true; + .tentative => |tent| { + if (self.common_section_index == null) { + const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + self.common_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, "__common", .{ + .flags = macho.S_ZEROFILL, + }); + } + + const size = tent.size; + const code = try self.allocator.alloc(u8, size); + mem.set(u8, code, 0); + const alignment = tent.alignment; + const local_sym_index = @intCast(u32, self.locals.items.len); + + symbol.payload = .{ + .regular = .{ + .linkage = .global, + .segment_id = self.data_segment_cmd_index.?, + .section_id = self.common_section_index.?, + .local_sym_index = local_sym_index, + }, + }; + try self.locals.append(self.allocator, symbol); + + const block = try self.allocator.create(TextBlock); + errdefer self.allocator.destroy(block); + + block.* = .{ + .local_sym_index = local_sym_index, + .code = code, + .size = size, + .alignment = alignment, + }; + + // TODO I'm not 100% sure about this yet, but I believe we should keep a separate list of + // TextBlocks per segment. + if (self.last_text_block) |last| { + last.next = block; + block.prev = last; + } + self.last_text_block = block; }, else => {}, } @@ -1733,13 +1691,7 @@ fn resolveSymbols(self: *Zld) !void { fn parseTextBlocks(self: *Zld) !void { for (self.objects.items) |object| { - if (try object.parseTextBlocks(self)) |block| { - if (self.last_text_block) |last| { - last.next = block; - block.prev = last; - } - self.last_text_block = block; - } + try object.parseTextBlocks(self); } if (self.last_text_block) |block| {