diff options
| author | Jakub Konka <kubkon@jakubkonka.com> | 2021-03-01 00:16:01 +0100 |
|---|---|---|
| committer | Jakub Konka <kubkon@jakubkonka.com> | 2021-03-17 19:59:13 +0100 |
| commit | b0ee480177c5f146cc2a5540745572436e8ca510 (patch) | |
| tree | f429bd9deee46b5dfe0e1a5d803f61480cca06d3 | |
| parent | 44ebf4863131fbf822caf6548a347abc52e5ce3b (diff) | |
| download | zig-b0ee480177c5f146cc2a5540745572436e8ca510.tar.gz zig-b0ee480177c5f146cc2a5540745572436e8ca510.zip | |
zld: merge and sort sections
| -rw-r--r-- | lib/std/macho.zig | 18 | ||||
| -rw-r--r-- | src/link/MachO/Object.zig | 15 | ||||
| -rw-r--r-- | src/link/MachO/Zld.zig | 825 |
3 files changed, 593 insertions, 265 deletions
diff --git a/lib/std/macho.zig b/lib/std/macho.zig index bca222b5b7..4cdb9dc40e 100644 --- a/lib/std/macho.zig +++ b/lib/std/macho.zig @@ -1227,6 +1227,24 @@ pub const S_ATTR_EXT_RELOC = 0x200; /// section has local relocation entries pub const S_ATTR_LOC_RELOC = 0x100; +/// template of initial values for TLVs +pub const S_THREAD_LOCAL_REGULAR = 0x11; + +/// template of initial values for TLVs +pub const S_THREAD_LOCAL_ZEROFILL = 0x12; + +/// TLV descriptors +pub const S_THREAD_LOCAL_VARIABLES = 0x13; + +/// pointers to TLV descriptors +pub const S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14; + +/// functions to call to initialize TLV values +pub const S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15; + +/// 32-bit offsets to initializers +pub const S_INIT_FUNC_OFFSETS = 0x16; + pub const cpu_type_t = integer_t; pub const cpu_subtype_t = integer_t; pub const integer_t = c_int; diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index c79869a5a7..6337f85a80 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -24,9 +24,9 @@ segment_cmd_index: ?u16 = null, symtab_cmd_index: ?u16 = null, dysymtab_cmd_index: ?u16 = null, build_version_cmd_index: ?u16 = null, - text_section_index: ?u16 = null, +// __DWARF segment sections dwarf_debug_info_index: ?u16 = null, dwarf_debug_abbrev_index: ?u16 = null, dwarf_debug_str_index: ?u16 = null, @@ -36,13 +36,6 @@ dwarf_debug_ranges_index: ?u16 = null, symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, -directory: std.AutoHashMapUnmanaged(DirectoryKey, u16) = .{}, - -pub const DirectoryKey = struct { - segname: [16]u8, - sectname: [16]u8, -}; - pub fn deinit(self: *Object) void { for (self.load_commands.items) |*lc| { lc.deinit(self.allocator); @@ -50,7 +43,6 @@ pub fn deinit(self: *Object) void { self.load_commands.deinit(self.allocator); self.symtab.deinit(self.allocator); self.strtab.deinit(self.allocator); - self.directory.deinit(self.allocator); self.allocator.free(self.name); self.file.close(); } @@ -138,11 +130,6 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi } } - try self.directory.putNoClobber(self.allocator, .{ - .segname = sect.segname, - .sectname = sect.sectname, - }, index); - sect.offset += offset_mod; if (sect.reloff > 0) sect.reloff += offset_mod; diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index 7389558573..f8cda5e9ad 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -52,13 +52,22 @@ source_version_cmd_index: ?u16 = null, uuid_cmd_index: ?u16 = null, code_signature_cmd_index: ?u16 = null, +// __TEXT segment sections text_section_index: ?u16 = null, stubs_section_index: ?u16 = null, stub_helper_section_index: ?u16 = null, +text_const_section_index: ?u16 = null, +cstring_section_index: ?u16 = null, + +// __DATA segment sections got_section_index: ?u16 = null, tlv_section_index: ?u16 = null, +tlv_data_section_index: ?u16 = null, +tlv_bss_section_index: ?u16 = null, la_symbol_ptr_section_index: ?u16 = null, +data_const_section_index: ?u16 = null, data_section_index: ?u16 = null, +bss_section_index: ?u16 = null, locals: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(Symbol)) = .{}, exports: std.StringArrayHashMapUnmanaged(macho.nlist_64) = .{}, @@ -71,13 +80,25 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, stub_helper_stubs_start_off: ?u64 = null, -segments_directory: std.AutoHashMapUnmanaged([16]u8, u16) = .{}, -directory: std.AutoHashMapUnmanaged(DirectoryKey, DirectoryEntry) = .{}, +mappings: std.AutoHashMapUnmanaged(MappingKey, SectionMapping) = .{}, +unhandled_sections: std.AutoHashMapUnmanaged(MappingKey, u0) = .{}, + +const MappingKey = struct { + object_id: u16, + source_sect_id: u16, +}; + +const SectionMapping = struct { + source_sect_id: u16, + target_seg_id: u16, + target_sect_id: u16, + offset: u32, +}; const Symbol = struct { inner: macho.nlist_64, tt: Type, - object: *Object, + object_id: u16, const Type = enum { Local, @@ -86,16 +107,6 @@ const Symbol = struct { }; }; -const DirectoryKey = struct { - segname: [16]u8, - sectname: [16]u8, -}; - -const DirectoryEntry = struct { - seg_index: u16, - sect_index: u16, -}; - const DebugInfo = struct { inner: dwarf.DwarfInfo, debug_info: []u8, @@ -221,8 +232,8 @@ pub fn deinit(self: *Zld) void { lc.deinit(self.allocator); } self.load_commands.deinit(self.allocator); - self.segments_directory.deinit(self.allocator); - self.directory.deinit(self.allocator); + self.mappings.deinit(self.allocator); + self.unhandled_sections.deinit(self.allocator); if (self.file) |*f| f.close(); } @@ -263,6 +274,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { try self.populateMetadata(); try self.parseInputFiles(files); + try self.sortSections(); try self.resolveImports(); try self.allocateTextSegment(); try self.allocateDataSegment(); @@ -282,10 +294,9 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { error.NotObject => break :try_object, else => |e| return e, }; - const index = self.objects.items.len; + const index = @intCast(u16, self.objects.items.len); try self.objects.append(self.allocator, object); - const p_object = &self.objects.items[index]; - try self.parseObjectFile(p_object); + try self.updateMetadata(index); continue; } @@ -296,10 +307,9 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { }; defer archive.deinit(); while (archive.objects.popOrNull()) |object| { - const index = self.objects.items.len; + const index = @intCast(u16, self.objects.items.len); try self.objects.append(self.allocator, object); - const p_object = &self.objects.items[index]; - try self.parseObjectFile(p_object); + try self.updateMetadata(index); } continue; } @@ -309,49 +319,425 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void { } } -fn parseObjectFile(self: *Zld, object: *const Object) !void { - const seg_cmd = object.load_commands.items[object.segment_cmd_index.?].Segment; - for (seg_cmd.sections.items) |sect| { - const segname = parseName(§.segname); - const sectname = parseName(§.sectname); +fn mapAndUpdateSections( + self: *Zld, + object_id: u16, + source_sect_id: u16, + target_seg_id: u16, + target_sect_id: u16, +) !void { + const object = self.objects.items[object_id]; + const source_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; + const source_sect = source_seg.sections.items[source_sect_id]; + const target_seg = &self.load_commands.items[target_seg_id].Segment; + const target_sect = &target_seg.sections.items[target_sect_id]; + log.warn("{}", .{target_sect}); + + const alignment = try math.powi(u32, 2, source_sect.@"align"); + const offset = mem.alignForwardGeneric(u64, target_sect.size, alignment); + const size = mem.alignForwardGeneric(u64, source_sect.size, alignment); + const key = MappingKey{ + .object_id = object_id, + .source_sect_id = source_sect_id, + }; + try self.mappings.putNoClobber(self.allocator, key, .{ + .source_sect_id = source_sect_id, + .target_seg_id = target_seg_id, + .target_sect_id = target_sect_id, + .offset = @intCast(u32, offset), + }); + log.warn("{s}: {s},{s} mapped to {s},{s} from 0x{x} to 0x{x}", .{ + object.name, + parseName(&source_sect.segname), + parseName(&source_sect.sectname), + parseName(&target_sect.segname), + parseName(&target_sect.sectname), + offset, + offset + size, + }); - const seg_index = self.segments_directory.get(sect.segname) orelse { - log.info("segname {s} not found in the output artifact", .{sect.segname}); - continue; - }; - const seg = &self.load_commands.items[seg_index].Segment; - const res = try self.directory.getOrPut(self.allocator, .{ - .segname = sect.segname, - .sectname = sect.sectname, - }); - if (!res.found_existing) { - const sect_index = @intCast(u16, seg.sections.items.len); - if (mem.eql(u8, sectname, "__thread_vars")) { - self.tlv_section_index = sect_index; - } - try seg.append(self.allocator, .{ - .sectname = makeStaticString(§.sectname), - .segname = makeStaticString(§.segname), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = sect.@"align", - .reloff = 0, - .nreloc = 0, - .flags = sect.flags, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); - res.entry.value = .{ - .seg_index = seg_index, - .sect_index = sect_index, - }; + target_sect.@"align" = math.max(target_sect.@"align", source_sect.@"align"); + target_sect.size = offset + size; +} + +fn updateMetadata(self: *Zld, object_id: u16) !void { + const object = self.objects.items[object_id]; + const object_seg = object.load_commands.items[object.segment_cmd_index.?].Segment; + const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + + // Create missing metadata + for (object_seg.sections.items) |source_sect, id| { + if (id == object.text_section_index.?) continue; + const segname = parseName(&source_sect.segname); + const sectname = parseName(&source_sect.sectname); + const flags = source_sect.flags; + + switch (flags) { + macho.S_REGULAR, macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { + if (mem.eql(u8, segname, "__TEXT")) { + if (self.text_const_section_index != null) continue; + + self.text_const_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.append(self.allocator, .{ + .sectname = makeStaticString("__const"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else if (mem.eql(u8, segname, "__DATA")) { + if (!mem.eql(u8, sectname, "__const")) continue; + if (self.data_const_section_index != null) continue; + + self.data_const_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.append(self.allocator, .{ + .sectname = makeStaticString("__const"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } + }, + macho.S_CSTRING_LITERALS => { + if (!mem.eql(u8, segname, "__TEXT")) continue; + if (self.cstring_section_index != null) continue; + + self.cstring_section_index = @intCast(u16, text_seg.sections.items.len); + try text_seg.append(self.allocator, .{ + .sectname = makeStaticString("__cstring"), + .segname = makeStaticString("__TEXT"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_CSTRING_LITERALS, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + }, + macho.S_ZEROFILL => { + if (!mem.eql(u8, segname, "__DATA")) continue; + if (self.bss_section_index != null) continue; + + self.bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.append(self.allocator, .{ + .sectname = makeStaticString("__bss"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + }, + macho.S_THREAD_LOCAL_VARIABLES => { + if (!mem.eql(u8, segname, "__DATA")) continue; + if (self.tlv_section_index != null) continue; + + self.tlv_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.append(self.allocator, .{ + .sectname = makeStaticString("__thread_vars"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_THREAD_LOCAL_VARIABLES, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + }, + macho.S_THREAD_LOCAL_REGULAR => { + if (!mem.eql(u8, segname, "__DATA")) continue; + if (self.tlv_data_section_index != null) continue; + + self.tlv_data_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.append(self.allocator, .{ + .sectname = makeStaticString("__thread_data"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_THREAD_LOCAL_REGULAR, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + }, + macho.S_THREAD_LOCAL_ZEROFILL => { + if (!mem.eql(u8, segname, "__DATA")) continue; + if (self.tlv_bss_section_index != null) continue; + + self.tlv_bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.append(self.allocator, .{ + .sectname = makeStaticString("__thread_bss"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_THREAD_LOCAL_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + }, + else => { + log.warn("unhandled section type 0x{x} for '{s}/{s}'", .{ flags, segname, sectname }); + }, } - const dest_sect = &seg.sections.items[res.entry.value.sect_index]; - dest_sect.@"align" = math.max(dest_sect.@"align", sect.@"align"); - dest_sect.size += sect.size; - seg.inner.filesize += sect.size; + } + + // Update section mappings + // __TEXT,__text has to be always defined! + try self.mapAndUpdateSections( + object_id, + object.text_section_index.?, + self.text_segment_cmd_index.?, + self.text_section_index.?, + ); + + for (object_seg.sections.items) |source_sect, id| { + const source_sect_id = @intCast(u16, id); + if (id == object.text_section_index.?) continue; + + const segname = parseName(&source_sect.segname); + const sectname = parseName(&source_sect.sectname); + const flags = source_sect.flags; + + switch (flags) { + macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.text_segment_cmd_index.?, + self.text_const_section_index.?, + ); + }, + macho.S_CSTRING_LITERALS => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.text_segment_cmd_index.?, + self.cstring_section_index.?, + ); + }, + macho.S_ZEROFILL => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.bss_section_index.?, + ); + }, + macho.S_THREAD_LOCAL_VARIABLES => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.tlv_section_index.?, + ); + }, + macho.S_THREAD_LOCAL_REGULAR => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.tlv_data_section_index.?, + ); + }, + macho.S_THREAD_LOCAL_ZEROFILL => { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.tlv_bss_section_index.?, + ); + }, + macho.S_REGULAR => { + if (mem.eql(u8, segname, "__TEXT")) { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.text_segment_cmd_index.?, + self.text_const_section_index.?, + ); + continue; + } else if (mem.eql(u8, segname, "__DATA")) { + if (mem.eql(u8, sectname, "__data")) { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.data_section_index.?, + ); + continue; + } else if (mem.eql(u8, sectname, "__const")) { + try self.mapAndUpdateSections( + object_id, + source_sect_id, + self.data_segment_cmd_index.?, + self.data_const_section_index.?, + ); + continue; + } + } + log.warn("section '{s}/{s}' will be unmapped", .{ segname, sectname }); + try self.unhandled_sections.putNoClobber(self.allocator, .{ + .object_id = object_id, + .source_sect_id = source_sect_id, + }, 0); + }, + else => { + log.warn("section '{s}/{s}' will be unmapped", .{ segname, sectname }); + try self.unhandled_sections.putNoClobber(self.allocator, .{ + .object_id = object_id, + .source_sect_id = source_sect_id, + }, 0); + }, + } + } +} + +fn sortSections(self: *Zld) !void { + const text_seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; + var text_sections = text_seg.sections.toOwnedSlice(self.allocator); + defer self.allocator.free(text_sections); + try text_seg.sections.ensureCapacity(self.allocator, text_sections.len); + + const data_seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + var data_sections = data_seg.sections.toOwnedSlice(self.allocator); + defer self.allocator.free(data_sections); + try data_seg.sections.ensureCapacity(self.allocator, data_sections.len); + + var text_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); + defer text_index_mapping.deinit(); + + var data_index_mapping = std.AutoHashMap(u16, u16).init(self.allocator); + defer data_index_mapping.deinit(); + + if (self.text_section_index) |index| { + const new_index = @intCast(u16, text_seg.sections.items.len); + self.text_section_index = new_index; + text_seg.sections.appendAssumeCapacity(text_sections[index]); + try text_index_mapping.putNoClobber(index, new_index); + } + if (self.stubs_section_index) |index| { + const new_index = @intCast(u16, text_seg.sections.items.len); + self.stubs_section_index = new_index; + text_seg.sections.appendAssumeCapacity(text_sections[index]); + try text_index_mapping.putNoClobber(index, new_index); + } + if (self.stub_helper_section_index) |index| { + const new_index = @intCast(u16, text_seg.sections.items.len); + self.stub_helper_section_index = new_index; + text_seg.sections.appendAssumeCapacity(text_sections[index]); + try text_index_mapping.putNoClobber(index, new_index); + } + if (self.text_const_section_index) |index| { + const new_index = @intCast(u16, text_seg.sections.items.len); + self.text_const_section_index = new_index; + text_seg.sections.appendAssumeCapacity(text_sections[index]); + try text_index_mapping.putNoClobber(index, new_index); + } + if (self.cstring_section_index) |index| { + const new_index = @intCast(u16, text_seg.sections.items.len); + self.cstring_section_index = new_index; + text_seg.sections.appendAssumeCapacity(text_sections[index]); + try text_index_mapping.putNoClobber(index, new_index); + } + + if (self.got_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.got_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.data_const_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.data_const_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.la_symbol_ptr_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.la_symbol_ptr_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.tlv_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.tlv_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.data_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.data_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.tlv_data_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.tlv_data_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.tlv_bss_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.tlv_bss_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + if (self.bss_section_index) |index| { + const new_index = @intCast(u16, data_seg.sections.items.len); + self.bss_section_index = new_index; + data_seg.sections.appendAssumeCapacity(data_sections[index]); + try data_index_mapping.putNoClobber(index, new_index); + } + + var it = self.mappings.iterator(); + while (it.next()) |entry| { + const mapping = &entry.value; + if (self.text_segment_cmd_index.? == mapping.target_seg_id) { + const new_index = text_index_mapping.get(mapping.target_sect_id) orelse unreachable; + mapping.target_sect_id = new_index; + } else if (self.data_segment_cmd_index.? == mapping.target_seg_id) { + const new_index = data_index_mapping.get(mapping.target_sect_id) orelse unreachable; + mapping.target_sect_id = new_index; + } else unreachable; } } @@ -790,35 +1176,9 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { } fn resolveSymbols(self: *Zld) !void { - const Address = struct { - addr: u64, - size: u64, - }; - var next_address = std.AutoHashMap(DirectoryKey, Address).init(self.allocator); - defer next_address.deinit(); - - for (self.objects.items) |*object| { + for (self.objects.items) |object, object_id| { const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - for (seg.sections.items) |sect| { - const key: DirectoryKey = .{ - .segname = sect.segname, - .sectname = sect.sectname, - }; - const indices = self.directory.get(key) orelse continue; - const out_seg = self.load_commands.items[indices.seg_index].Segment; - const out_sect = out_seg.sections.items[indices.sect_index]; - - const res = try next_address.getOrPut(key); - const next = &res.entry.value; - if (res.found_existing) { - next.addr += next.size; - } else { - next.addr = out_sect.addr; - } - next.size = sect.size; - } - for (object.symtab.items) |sym| { if (isImport(&sym)) continue; @@ -851,24 +1211,36 @@ fn resolveSymbols(self: *Zld) !void { } } - const sect = seg.sections.items[sym.n_sect - 1]; - const key: DirectoryKey = .{ - .segname = sect.segname, - .sectname = sect.sectname, + const source_sect_id = sym.n_sect - 1; + const target_mapping = self.mappings.get(.{ + .object_id = @intCast(u16, object_id), + .source_sect_id = source_sect_id, + }) orelse { + if (self.unhandled_sections.get(.{ + .object_id = @intCast(u16, object_id), + .source_sect_id = source_sect_id, + }) != null) continue; + + log.err("section not mapped for symbol '{s}': {}", .{ sym_name, sym }); + return error.SectionNotMappedForSymbol; }; - const res = self.directory.get(key) orelse continue; - - const n_value = sym.n_value - sect.addr + next_address.get(key).?.addr; + const source_sect = seg.sections.items[source_sect_id]; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + const target_addr = target_sect.addr + target_mapping.offset; + const n_value = sym.n_value - source_sect.addr + target_addr; log.warn("resolving '{s}':{} as {s} symbol at 0x{x}", .{ sym_name, sym, tt, n_value }); - var n_sect = res.sect_index + 1; - for (self.load_commands.items) |sseg, i| { - if (i == res.seg_index) { - break; + // TODO this assumes only two symbol-filled segments. Also, there might be a more + // generic way of doing this. + const n_sect = blk: { + if (self.text_segment_cmd_index.? == target_mapping.target_seg_id) { + break :blk target_mapping.target_sect_id + 1; } - n_sect += @intCast(u16, sseg.Segment.sections.items.len); - } + const prev_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + break :blk @intCast(u16, prev_seg.sections.items.len + target_mapping.target_sect_id + 1); + }; const n_strx = try self.makeString(sym_name); try locs.entry.value.append(self.allocator, .{ @@ -880,64 +1252,26 @@ fn resolveSymbols(self: *Zld) !void { .n_sect = @intCast(u8, n_sect), }, .tt = tt, - .object = object, + .object_id = @intCast(u16, object_id), }); } } } fn doRelocs(self: *Zld) !void { - const Space = struct { - address: u64, - offset: u64, - size: u64, - }; - var next_space = std.AutoHashMap(DirectoryKey, Space).init(self.allocator); - defer next_space.deinit(); - - for (self.objects.items) |object| { + for (self.objects.items) |object, object_id| { log.warn("\n\n", .{}); log.warn("relocating object {s}", .{object.name}); const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; - for (seg.sections.items) |sect| { - const key: DirectoryKey = .{ - .segname = sect.segname, - .sectname = sect.sectname, - }; - const indices = self.directory.get(key) orelse continue; - const out_seg = self.load_commands.items[indices.seg_index].Segment; - const out_sect = out_seg.sections.items[indices.sect_index]; - - const res = try next_space.getOrPut(key); - const next = &res.entry.value; - if (res.found_existing) { - next.offset += next.size; - next.address += next.size; - } else { - next.offset = out_sect.offset; - next.address = out_sect.addr; - } - next.size = sect.size; - } - - for (seg.sections.items) |sect| { + for (seg.sections.items) |sect, source_sect_id| { const segname = parseName(§.segname); const sectname = parseName(§.sectname); - const key: DirectoryKey = .{ - .segname = sect.segname, - .sectname = sect.sectname, - }; - const next = next_space.get(key) orelse continue; - - var code = blk: { - var buf = try self.allocator.alloc(u8, sect.size); - _ = try object.file.preadAll(buf, sect.offset); - break :blk std.ArrayList(u8).fromOwnedSlice(self.allocator, buf); - }; - defer code.deinit(); + var code = try self.allocator.alloc(u8, sect.size); + _ = try object.file.preadAll(code, sect.offset); + defer self.allocator.free(code); // Parse relocs (if any) var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); @@ -945,12 +1279,25 @@ fn doRelocs(self: *Zld) !void { _ = try object.file.preadAll(raw_relocs, sect.reloff); const relocs = mem.bytesAsSlice(macho.relocation_info, raw_relocs); + // Get mapping + const target_mapping = self.mappings.get(.{ + .object_id = @intCast(u16, object_id), + .source_sect_id = @intCast(u16, source_sect_id), + }) orelse { + log.warn("no mapping for {s},{s}; skipping", .{ segname, sectname }); + continue; + }; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + const target_sect_addr = target_sect.addr + target_mapping.offset; + const target_sect_off = target_sect.offset + target_mapping.offset; + var addend: ?u64 = null; var sub: ?i64 = null; for (relocs) |rel| { const off = @intCast(u32, rel.r_address); - const this_addr = next.address + off; + const this_addr = target_sect_addr + off; switch (self.arch.?) { .aarch64 => { @@ -975,7 +1322,7 @@ fn doRelocs(self: *Zld) !void { else => {}, } - const target_addr = try self.relocTargetAddr(object, rel, next_space); + const target_addr = try self.relocTargetAddr(@intCast(u16, object_id), rel); log.warn(" | target address 0x{x}", .{target_addr}); if (rel.r_extern == 1) { const target_symname = object.getString(object.symtab.items[rel.r_symbolnum].n_strx); @@ -995,16 +1342,16 @@ fn doRelocs(self: *Zld) !void { .X86_64_RELOC_GOT, => { assert(rel.r_length == 2); - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); mem.writeIntLittle(u32, inst, displacement); }, .X86_64_RELOC_TLV => { assert(rel.r_length == 2); // We need to rewrite the opcode from movq to leaq. - code.items[off - 2] = 0x8d; + code[off - 2] = 0x8d; // Add displacement. - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const displacement = @bitCast(u32, @intCast(i32, @intCast(i64, target_addr) - @intCast(i64, this_addr) - 4)); mem.writeIntLittle(u32, inst, displacement); }, @@ -1014,7 +1361,7 @@ fn doRelocs(self: *Zld) !void { .X86_64_RELOC_SIGNED_4, => { assert(rel.r_length == 2); - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const offset: i32 = blk: { if (rel.r_extern == 1) { break :blk mem.readIntLittle(i32, inst); @@ -1043,7 +1390,7 @@ fn doRelocs(self: *Zld) !void { .X86_64_RELOC_UNSIGNED => { switch (rel.r_length) { 3 => { - const inst = code.items[off..][0..8]; + const inst = code[off..][0..8]; const offset = mem.readIntLittle(i64, inst); log.warn(" | calculated addend 0x{x}", .{offset}); const result = if (sub) |s| @@ -1054,12 +1401,19 @@ fn doRelocs(self: *Zld) !void { sub = null; // TODO should handle this better. - if (mem.eql(u8, segname, "__DATA")) outer: { - if (!mem.eql(u8, sectname, "__data") and - !mem.eql(u8, sectname, "__const") and - !mem.eql(u8, sectname, "__mod_init_func")) break :outer; + outer: { + var hit: bool = false; + if (self.data_section_index) |index| inner: { + if (index != target_mapping.target_sect_id) break :inner; + hit = true; + } + if (self.data_const_section_index) |index| inner: { + if (index != target_mapping.target_sect_id) break :inner; + hit = true; + } + if (!hit) break :outer; const this_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const this_offset = next.address + off - this_seg.inner.vmaddr; + const this_offset = target_sect_addr + off - this_seg.inner.vmaddr; try self.local_rebases.append(self.allocator, .{ .offset = this_offset, .segment_id = @intCast(u16, self.data_segment_cmd_index.?), @@ -1067,7 +1421,7 @@ fn doRelocs(self: *Zld) !void { } }, 2 => { - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const offset = mem.readIntLittle(i32, inst); log.warn(" | calculated addend 0x{x}", .{offset}); const result = if (sub) |s| @@ -1091,7 +1445,7 @@ fn doRelocs(self: *Zld) !void { switch (rel_type) { .ARM64_RELOC_BRANCH26 => { assert(rel.r_length == 2); - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const displacement = @intCast(i28, @intCast(i64, target_addr) - @intCast(i64, this_addr)); var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Branch), inst); parsed.disp = @truncate(u26, @bitCast(u28, displacement) >> 2); @@ -1101,7 +1455,7 @@ fn doRelocs(self: *Zld) !void { .ARM64_RELOC_TLVP_LOAD_PAGE21, => { assert(rel.r_length == 2); - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const ta = if (addend) |a| target_addr + a else target_addr; const this_page = @intCast(i32, this_addr >> 12); const target_page = @intCast(i32, ta >> 12); @@ -1115,7 +1469,7 @@ fn doRelocs(self: *Zld) !void { .ARM64_RELOC_PAGEOFF12, .ARM64_RELOC_GOT_LOAD_PAGEOFF12, => { - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; if (Arm64.isArithmetic(inst)) { log.warn(" | detected ADD opcode", .{}); // add @@ -1153,7 +1507,7 @@ fn doRelocs(self: *Zld) !void { rn: u5, size: u1, }; - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const parsed: RegInfo = blk: { if (Arm64.isArithmetic(inst)) { const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst); @@ -1175,7 +1529,7 @@ fn doRelocs(self: *Zld) !void { .ARM64_RELOC_UNSIGNED => { switch (rel.r_length) { 3 => { - const inst = code.items[off..][0..8]; + const inst = code[off..][0..8]; const offset = mem.readIntLittle(i64, inst); log.warn(" | calculated addend 0x{x}", .{offset}); const result = if (sub) |s| @@ -1186,12 +1540,19 @@ fn doRelocs(self: *Zld) !void { sub = null; // TODO should handle this better. - if (mem.eql(u8, segname, "__DATA")) outer: { - if (!mem.eql(u8, sectname, "__data") and - !mem.eql(u8, sectname, "__const") and - !mem.eql(u8, sectname, "__mod_init_func")) break :outer; + outer: { + var hit: bool = false; + if (self.data_section_index) |index| inner: { + if (index != target_mapping.target_sect_id) break :inner; + hit = true; + } + if (self.data_const_section_index) |index| inner: { + if (index != target_mapping.target_sect_id) break :inner; + hit = true; + } + if (!hit) break :outer; const this_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const this_offset = next.address + off - this_seg.inner.vmaddr; + const this_offset = target_sect_addr + off - this_seg.inner.vmaddr; try self.local_rebases.append(self.allocator, .{ .offset = this_offset, .segment_id = @intCast(u16, self.data_segment_cmd_index.?), @@ -1199,7 +1560,7 @@ fn doRelocs(self: *Zld) !void { } }, 2 => { - const inst = code.items[off..][0..4]; + const inst = code[off..][0..4]; const offset = mem.readIntLittle(i32, inst); log.warn(" | calculated addend 0x{x}", .{offset}); const result = if (sub) |s| @@ -1227,40 +1588,50 @@ fn doRelocs(self: *Zld) !void { segname, sectname, object.name, - next.offset, - next.offset + next.size, + target_sect_off, + target_sect_off + code.len, }); - if (mem.eql(u8, sectname, "__bss") or - mem.eql(u8, sectname, "__thread_bss") or - mem.eql(u8, sectname, "__thread_vars")) + if (target_sect.flags == macho.S_ZEROFILL or + target_sect.flags == macho.S_THREAD_LOCAL_ZEROFILL or + target_sect.flags == macho.S_THREAD_LOCAL_VARIABLES) { + log.warn("zeroing out '{s},{s}' from 0x{x} to 0x{x}", .{ + parseName(&target_sect.segname), + parseName(&target_sect.sectname), + target_sect_off, + target_sect_off + code.len, + }); // Zero-out the space - var zeroes = try self.allocator.alloc(u8, next.size); + var zeroes = try self.allocator.alloc(u8, code.len); defer self.allocator.free(zeroes); mem.set(u8, zeroes, 0); - try self.file.?.pwriteAll(zeroes, next.offset); + try self.file.?.pwriteAll(zeroes, target_sect_off); } else { - try self.file.?.pwriteAll(code.items, next.offset); + try self.file.?.pwriteAll(code, target_sect_off); } } } } -fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_space: anytype) !u64 { +fn relocTargetAddr(self: *Zld, object_id: u16, rel: macho.relocation_info) !u64 { + const object = self.objects.items[object_id]; const seg = object.load_commands.items[object.segment_cmd_index.?].Segment; const target_addr = blk: { if (rel.r_extern == 1) { const sym = object.symtab.items[rel.r_symbolnum]; if (isLocal(&sym) or isExport(&sym)) { // Relocate using section offsets only. - const source_sect = seg.sections.items[sym.n_sect - 1]; - const target_space = next_space.get(.{ - .segname = source_sect.segname, - .sectname = source_sect.sectname, - }).?; + const target_mapping = self.mappings.get(.{ + .object_id = object_id, + .source_sect_id = sym.n_sect - 1, + }) orelse unreachable; + const source_sect = seg.sections.items[target_mapping.source_sect_id]; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + const target_sect_addr = target_sect.addr + target_mapping.offset; log.warn(" | symbol local to object", .{}); - break :blk target_space.address + sym.n_value - source_sect.addr; + break :blk target_sect_addr + sym.n_value - source_sect.addr; } else if (isImport(&sym)) { // Relocate to either the artifact's local symbol, or an import from // shared library. @@ -1309,12 +1680,13 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_ // here to get the actual section plus offset into that section of the relocated // symbol. Unless the fine-grained location is encoded within the cell in the code // buffer? - const source_sectname = seg.sections.items[rel.r_symbolnum - 1]; - const target_space = next_space.get(.{ - .segname = source_sectname.segname, - .sectname = source_sectname.sectname, - }).?; - break :blk target_space.address; + const target_mapping = self.mappings.get(.{ + .object_id = object_id, + .source_sect_id = @intCast(u16, rel.r_symbolnum - 1), + }) orelse unreachable; + const target_seg = self.load_commands.items[target_mapping.target_seg_id].Segment; + const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; + break :blk target_sect.addr + target_mapping.offset; } }; return target_addr; @@ -1338,7 +1710,6 @@ fn populateMetadata(self: *Zld) !void { .flags = 0, }), }); - try self.addSegmentToDir(0); } if (self.text_segment_cmd_index == null) { @@ -1358,7 +1729,6 @@ fn populateMetadata(self: *Zld) !void { .flags = 0, }), }); - try self.addSegmentToDir(self.text_segment_cmd_index.?); } if (self.text_section_index == null) { @@ -1383,10 +1753,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = 0, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.text_segment_cmd_index.?, - .sect_index = self.text_section_index.?, - }); } if (self.stubs_section_index == null) { @@ -1416,10 +1782,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = stub_size, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.text_segment_cmd_index.?, - .sect_index = self.stubs_section_index.?, - }); } if (self.stub_helper_section_index == null) { @@ -1449,10 +1811,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = 0, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.text_segment_cmd_index.?, - .sect_index = self.stub_helper_section_index.?, - }); } if (self.data_segment_cmd_index == null) { @@ -1472,7 +1830,6 @@ fn populateMetadata(self: *Zld) !void { .flags = 0, }), }); - try self.addSegmentToDir(self.data_segment_cmd_index.?); } if (self.got_section_index == null) { @@ -1492,10 +1849,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = 0, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.data_segment_cmd_index.?, - .sect_index = self.got_section_index.?, - }); } if (self.la_symbol_ptr_section_index == null) { @@ -1515,10 +1868,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = 0, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.data_segment_cmd_index.?, - .sect_index = self.la_symbol_ptr_section_index.?, - }); } if (self.data_section_index == null) { @@ -1538,10 +1887,6 @@ fn populateMetadata(self: *Zld) !void { .reserved2 = 0, .reserved3 = 0, }); - try self.addSectionToDir(.{ - .seg_index = self.data_segment_cmd_index.?, - .sect_index = self.data_section_index.?, - }); } if (self.linkedit_segment_cmd_index == null) { @@ -1561,7 +1906,6 @@ fn populateMetadata(self: *Zld) !void { .flags = 0, }), }); - try self.addSegmentToDir(self.linkedit_segment_cmd_index.?); } if (self.dyld_info_cmd_index == null) { @@ -1719,22 +2063,15 @@ fn populateMetadata(self: *Zld) !void { } fn flush(self: *Zld) !void { - { + if (self.bss_section_index) |index| { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; - for (seg.sections.items) |*sect| { - const sectname = parseName(§.sectname); - if (mem.eql(u8, sectname, "__bss") or mem.eql(u8, sectname, "__thread_bss")) { - sect.offset = 0; - } - } + const sect = &seg.sections.items[index]; + sect.offset = 0; } - { - const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; - for (seg.sections.items) |*sect| { - if (mem.eql(u8, parseName(§.sectname), "__eh_frame")) { - sect.flags = 0; - } - } + if (self.tlv_bss_section_index) |index| { + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + sect.offset = 0; } try self.setEntryPoint(); try self.writeRebaseInfoTable(); @@ -2056,9 +2393,9 @@ fn writeDebugInfo(self: *Zld) !void { var stabs = std.ArrayList(macho.nlist_64).init(self.allocator); defer stabs.deinit(); - for (self.objects.items) |*object| { + for (self.objects.items) |object, object_id| { var debug_info = blk: { - var di = try DebugInfo.parseFromObject(self.allocator, object.*); + var di = try DebugInfo.parseFromObject(self.allocator, object); break :blk di orelse continue; }; defer debug_info.deinit(self.allocator); @@ -2108,7 +2445,7 @@ fn writeDebugInfo(self: *Zld) !void { const target_syms = self.locals.get(symname) orelse continue; const target_sym: Symbol = blk: { for (target_syms.items) |ts| { - if (ts.object == object) break :blk ts; + if (ts.object_id == @intCast(u16, object_id)) break :blk ts; } else continue; }; @@ -2204,7 +2541,7 @@ fn writeSymbolTable(self: *Zld) !void { for (entries.value.items) |entry| { log.warn(" | {}", .{entry.inner}); log.warn(" | {}", .{entry.tt}); - log.warn(" | {s}", .{entry.object.name}); + log.warn(" | {s}", .{self.objects.items[entry.object_id].name}); // switch (entry.tt) { // .Global => { // symbol = entry.inner; @@ -2468,20 +2805,6 @@ pub fn parseName(name: *const [16]u8) []const u8 { return name[0..len]; } -fn addSegmentToDir(self: *Zld, idx: u16) !void { - const segment_cmd = self.load_commands.items[idx].Segment; - return self.segments_directory.putNoClobber(self.allocator, segment_cmd.inner.segname, idx); -} - -fn addSectionToDir(self: *Zld, value: DirectoryEntry) !void { - const seg = self.load_commands.items[value.seg_index].Segment; - const sect = seg.sections.items[value.sect_index]; - return self.directory.putNoClobber(self.allocator, .{ - .segname = sect.segname, - .sectname = sect.sectname, - }, value); -} - fn isLocal(sym: *const macho.nlist_64) callconv(.Inline) bool { if (isExtern(sym)) return false; const tt = macho.N_TYPE & sym.n_type; |
