aboutsummaryrefslogtreecommitdiff
path: root/src/link/MachO
diff options
context:
space:
mode:
authorJakub Konka <kubkon@jakubkonka.com>2022-07-31 18:19:17 +0200
committerJakub Konka <kubkon@jakubkonka.com>2022-08-03 21:19:41 +0200
commitf26d5ee7ea97c8fd6e5b2655f845be7e4293930e (patch)
treefab17016b079fcd7aaef84672feb469136dcc646 /src/link/MachO
parent4c750016eb9b1c0831cbb0398a4d6ee9dbdc932e (diff)
downloadzig-f26d5ee7ea97c8fd6e5b2655f845be7e4293930e.tar.gz
zig-f26d5ee7ea97c8fd6e5b2655f845be7e4293930e.zip
macho: sync with zld
gitrev a2c32e972f8c5adfcda8ed2d99379ae868f59c24 https://github.com/kubkon/zld/commit/a2c32e972f8c5adfcda8ed2d99379ae868f59c24
Diffstat (limited to 'src/link/MachO')
-rw-r--r--src/link/MachO/Archive.zig58
-rw-r--r--src/link/MachO/Atom.zig35
-rw-r--r--src/link/MachO/CodeSignature.zig12
-rw-r--r--src/link/MachO/DebugSymbols.zig510
-rw-r--r--src/link/MachO/Dylib.zig159
-rw-r--r--src/link/MachO/Object.zig306
-rw-r--r--src/link/MachO/dead_strip.zig48
-rw-r--r--src/link/MachO/fat.zig4
8 files changed, 447 insertions, 685 deletions
diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig
index ee43e5b2a2..054f75fff3 100644
--- a/src/link/MachO/Archive.zig
+++ b/src/link/MachO/Archive.zig
@@ -6,19 +6,14 @@ const fs = std.fs;
const log = std.log.scoped(.link);
const macho = std.macho;
const mem = std.mem;
-const fat = @import("fat.zig");
const Allocator = mem.Allocator;
const Object = @import("Object.zig");
file: fs.File,
+fat_offset: u64,
name: []const u8,
-
-header: ?ar_hdr = null,
-
-// The actual contents we care about linking with will be embedded at
-// an offset within a file if we are linking against a fat lib
-library_offset: u64 = 0,
+header: ar_hdr = undefined,
/// Parsed table of contents.
/// Each symbol name points to a list of all definition
@@ -103,11 +98,7 @@ pub fn deinit(self: *Archive, allocator: Allocator) void {
allocator.free(self.name);
}
-pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
- const reader = self.file.reader();
- self.library_offset = try fat.getLibraryOffset(reader, cpu_arch);
- try self.file.seekTo(self.library_offset);
-
+pub fn parse(self: *Archive, allocator: Allocator, reader: anytype) !void {
const magic = try reader.readBytesNoEof(SARMAG);
if (!mem.eql(u8, &magic, ARMAG)) {
log.debug("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic });
@@ -115,21 +106,23 @@ pub fn parse(self: *Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch
}
self.header = try reader.readStruct(ar_hdr);
- if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) {
- log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag });
+ if (!mem.eql(u8, &self.header.ar_fmag, ARFMAG)) {
+ log.debug("invalid header delimiter: expected '{s}', found '{s}'", .{
+ ARFMAG,
+ self.header.ar_fmag,
+ });
return error.NotArchive;
}
- var embedded_name = try parseName(allocator, self.header.?, reader);
+ const name_or_length = try self.header.nameOrLength();
+ var embedded_name = try parseName(allocator, name_or_length, reader);
log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name });
defer allocator.free(embedded_name);
try self.parseTableOfContents(allocator, reader);
- try reader.context.seekTo(0);
}
-fn parseName(allocator: Allocator, header: ar_hdr, reader: anytype) ![]u8 {
- const name_or_length = try header.nameOrLength();
+fn parseName(allocator: Allocator, name_or_length: ar_hdr.NameOrLength, reader: anytype) ![]u8 {
var name: []u8 = undefined;
switch (name_or_length) {
.Name => |n| {
@@ -187,9 +180,14 @@ fn parseTableOfContents(self: *Archive, allocator: Allocator, reader: anytype) !
}
}
-pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch, offset: u32) !Object {
+pub fn parseObject(
+ self: Archive,
+ allocator: Allocator,
+ cpu_arch: std.Target.Cpu.Arch,
+ offset: u32,
+) !Object {
const reader = self.file.reader();
- try reader.context.seekTo(offset + self.library_offset);
+ try reader.context.seekTo(self.fat_offset + offset);
const object_header = try reader.readStruct(ar_hdr);
@@ -198,7 +196,8 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
return error.MalformedArchive;
}
- const object_name = try parseName(allocator, object_header, reader);
+ const name_or_length = try object_header.nameOrLength();
+ const object_name = try parseName(allocator, name_or_length, reader);
defer allocator.free(object_name);
log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name });
@@ -209,15 +208,24 @@ pub fn parseObject(self: Archive, allocator: Allocator, cpu_arch: std.Target.Cpu
break :name try std.fmt.allocPrint(allocator, "{s}({s})", .{ path, object_name });
};
+ const object_name_len = switch (name_or_length) {
+ .Name => 0,
+ .Length => |len| len,
+ };
+ const object_size = (try object_header.size()) - object_name_len;
+ const contents = try allocator.allocWithOptions(u8, object_size, @alignOf(u64), null);
+ const amt = try reader.readAll(contents);
+ if (amt != object_size) {
+ return error.InputOutput;
+ }
+
var object = Object{
- .file = try fs.cwd().openFile(self.name, .{}),
.name = name,
- .file_offset = @intCast(u32, try reader.context.getPos()),
- .mtime = try self.header.?.date(),
+ .mtime = try self.header.date(),
+ .contents = contents,
};
try object.parse(allocator, cpu_arch);
- try reader.context.seekTo(0);
return object;
}
diff --git a/src/link/MachO/Atom.zig b/src/link/MachO/Atom.zig
index ba00764127..90c86e24ed 100644
--- a/src/link/MachO/Atom.zig
+++ b/src/link/MachO/Atom.zig
@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
else => {
log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
log.err(" expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
- log.err(" found {}", .{next});
+ log.err(" found {s}", .{@tagName(next)});
return error.UnexpectedRelocationType;
},
}
@@ -285,7 +285,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
else => {
log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
log.err(" expected ARM64_RELOC_UNSIGNED", .{});
- log.err(" found {}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
+ log.err(" found {s}", .{
+ @tagName(@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)),
+ });
return error.UnexpectedRelocationType;
},
},
@@ -294,7 +296,9 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
else => {
log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
log.err(" expected X86_64_RELOC_UNSIGNED", .{});
- log.err(" found {}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
+ log.err(" found {s}", .{
+ @tagName(@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)),
+ });
return error.UnexpectedRelocationType;
},
},
@@ -309,13 +313,13 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
const sect_id = @intCast(u16, rel.r_symbolnum - 1);
const sym_index = object.sections_as_symbols.get(sect_id) orelse blk: {
const sect = object.getSourceSection(sect_id);
- const match = (try context.macho_file.getMatchingSection(sect)) orelse
+ const match = (try context.macho_file.getOutputSection(sect)) orelse
unreachable;
const sym_index = @intCast(u32, object.symtab.items.len);
try object.symtab.append(gpa, .{
.n_strx = 0,
.n_type = macho.N_SECT,
- .n_sect = context.macho_file.getSectionOrdinal(match),
+ .n_sect = match + 1,
.n_desc = 0,
.n_value = sect.addr,
});
@@ -459,9 +463,10 @@ fn addPtrBindingOrRebase(
});
} else {
const source_sym = self.getSymbol(context.macho_file);
- const match = context.macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
- const sect = context.macho_file.getSection(match);
- const sect_type = sect.type_();
+ const section = context.macho_file.sections.get(source_sym.n_sect - 1);
+ const header = section.header;
+ const segment_index = section.segment_index;
+ const sect_type = header.type_();
const should_rebase = rebase: {
if (rel.r_length != 3) break :rebase false;
@@ -470,12 +475,12 @@ fn addPtrBindingOrRebase(
// that the segment is writable should be enough here.
const is_right_segment = blk: {
if (context.macho_file.data_segment_cmd_index) |idx| {
- if (match.seg == idx) {
+ if (segment_index == idx) {
break :blk true;
}
}
if (context.macho_file.data_const_segment_cmd_index) |idx| {
- if (match.seg == idx) {
+ if (segment_index == idx) {
break :blk true;
}
}
@@ -565,9 +570,8 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
};
const is_tlv = is_tlv: {
const source_sym = self.getSymbol(macho_file);
- const match = macho_file.getMatchingSectionFromOrdinal(source_sym.n_sect);
- const sect = macho_file.getSection(match);
- break :is_tlv sect.type_() == macho.S_THREAD_LOCAL_VARIABLES;
+ const header = macho_file.sections.items(.header)[source_sym.n_sect - 1];
+ break :is_tlv header.type_() == macho.S_THREAD_LOCAL_VARIABLES;
};
const target_addr = blk: {
const target_atom = rel.getTargetAtom(macho_file) orelse {
@@ -608,10 +612,7 @@ pub fn resolveRelocs(self: *Atom, macho_file: *MachO) !void {
return error.FailedToResolveRelocationTarget;
}
};
- break :base_address macho_file.getSection(.{
- .seg = macho_file.data_segment_cmd_index.?,
- .sect = sect_id,
- }).addr;
+ break :base_address macho_file.sections.items(.header)[sect_id].addr;
} else 0;
break :blk target_sym.n_value - base_address;
};
diff --git a/src/link/MachO/CodeSignature.zig b/src/link/MachO/CodeSignature.zig
index fbfd487ce2..530a13dc51 100644
--- a/src/link/MachO/CodeSignature.zig
+++ b/src/link/MachO/CodeSignature.zig
@@ -252,7 +252,7 @@ pub const WriteOpts = struct {
file: fs.File,
exec_seg_base: u64,
exec_seg_limit: u64,
- code_sig_cmd: macho.linkedit_data_command,
+ file_size: u32,
output_mode: std.builtin.OutputMode,
};
@@ -274,10 +274,9 @@ pub fn writeAdhocSignature(
self.code_directory.inner.execSegBase = opts.exec_seg_base;
self.code_directory.inner.execSegLimit = opts.exec_seg_limit;
self.code_directory.inner.execSegFlags = if (opts.output_mode == .Exe) macho.CS_EXECSEG_MAIN_BINARY else 0;
- const file_size = opts.code_sig_cmd.dataoff;
- self.code_directory.inner.codeLimit = file_size;
+ self.code_directory.inner.codeLimit = opts.file_size;
- const total_pages = mem.alignForward(file_size, self.page_size) / self.page_size;
+ const total_pages = mem.alignForward(opts.file_size, self.page_size) / self.page_size;
var buffer = try allocator.alloc(u8, self.page_size);
defer allocator.free(buffer);
@@ -289,7 +288,10 @@ pub fn writeAdhocSignature(
var i: usize = 0;
while (i < total_pages) : (i += 1) {
const fstart = i * self.page_size;
- const fsize = if (fstart + self.page_size > file_size) file_size - fstart else self.page_size;
+ const fsize = if (fstart + self.page_size > opts.file_size)
+ opts.file_size - fstart
+ else
+ self.page_size;
const len = try opts.file.preadAll(buffer, fstart);
assert(fsize <= len);
diff --git a/src/link/MachO/DebugSymbols.zig b/src/link/MachO/DebugSymbols.zig
index 4da106eca1..f191d43f98 100644
--- a/src/link/MachO/DebugSymbols.zig
+++ b/src/link/MachO/DebugSymbols.zig
@@ -25,35 +25,18 @@ base: *MachO,
dwarf: Dwarf,
file: fs.File,
-/// Table of all load commands
-load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
-/// __PAGEZERO segment
-pagezero_segment_cmd_index: ?u16 = null,
-/// __TEXT segment
-text_segment_cmd_index: ?u16 = null,
-/// __DATA_CONST segment
-data_const_segment_cmd_index: ?u16 = null,
-/// __DATA segment
-data_segment_cmd_index: ?u16 = null,
-/// __LINKEDIT segment
-linkedit_segment_cmd_index: ?u16 = null,
-/// __DWARF segment
-dwarf_segment_cmd_index: ?u16 = null,
-/// Symbol table
-symtab_cmd_index: ?u16 = null,
-/// UUID load command
-uuid_cmd_index: ?u16 = null,
-
-/// Index into __TEXT,__text section.
-text_section_index: ?u16 = null,
-
-debug_info_section_index: ?u16 = null,
-debug_abbrev_section_index: ?u16 = null,
-debug_str_section_index: ?u16 = null,
-debug_aranges_section_index: ?u16 = null,
-debug_line_section_index: ?u16 = null,
-
-load_commands_dirty: bool = false,
+segments: std.ArrayListUnmanaged(macho.segment_command_64) = .{},
+sections: std.ArrayListUnmanaged(macho.section_64) = .{},
+
+linkedit_segment_cmd_index: ?u8 = null,
+dwarf_segment_cmd_index: ?u8 = null,
+
+debug_info_section_index: ?u8 = null,
+debug_abbrev_section_index: ?u8 = null,
+debug_str_section_index: ?u8 = null,
+debug_aranges_section_index: ?u8 = null,
+debug_line_section_index: ?u8 = null,
+
debug_string_table_dirty: bool = false,
debug_abbrev_section_dirty: bool = false,
debug_aranges_section_dirty: bool = false,
@@ -78,98 +61,40 @@ pub const Reloc = struct {
/// You must call this function *after* `MachO.populateMissingMetadata()`
/// has been called to get a viable debug symbols output.
pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void {
- if (self.uuid_cmd_index == null) {
- const base_cmd = self.base.load_commands.items[self.base.uuid_cmd_index.?];
- self.uuid_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(allocator, base_cmd);
- self.load_commands_dirty = true;
- }
-
- if (self.symtab_cmd_index == null) {
- self.symtab_cmd_index = @intCast(u16, self.load_commands.items.len);
- try self.load_commands.append(self.base.base.allocator, .{
- .symtab = .{
- .cmdsize = @sizeOf(macho.symtab_command),
- .symoff = 0,
- .nsyms = 0,
- .stroff = 0,
- .strsize = 0,
- },
- });
- try self.strtab.buffer.append(allocator, 0);
- self.load_commands_dirty = true;
- }
-
- if (self.pagezero_segment_cmd_index == null) {
- self.pagezero_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- const base_cmd = self.base.load_commands.items[self.base.pagezero_segment_cmd_index.?].segment;
- const cmd = try self.copySegmentCommand(allocator, base_cmd);
- try self.load_commands.append(allocator, .{ .segment = cmd });
- self.load_commands_dirty = true;
- }
-
- if (self.text_segment_cmd_index == null) {
- self.text_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- const base_cmd = self.base.load_commands.items[self.base.text_segment_cmd_index.?].segment;
- const cmd = try self.copySegmentCommand(allocator, base_cmd);
- try self.load_commands.append(allocator, .{ .segment = cmd });
- self.load_commands_dirty = true;
- }
-
- if (self.data_const_segment_cmd_index == null) outer: {
- if (self.base.data_const_segment_cmd_index == null) break :outer; // __DATA_CONST is optional
- self.data_const_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- const base_cmd = self.base.load_commands.items[self.base.data_const_segment_cmd_index.?].segment;
- const cmd = try self.copySegmentCommand(allocator, base_cmd);
- try self.load_commands.append(allocator, .{ .segment = cmd });
- self.load_commands_dirty = true;
- }
-
- if (self.data_segment_cmd_index == null) outer: {
- if (self.base.data_segment_cmd_index == null) break :outer; // __DATA is optional
- self.data_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- const base_cmd = self.base.load_commands.items[self.base.data_segment_cmd_index.?].segment;
- const cmd = try self.copySegmentCommand(allocator, base_cmd);
- try self.load_commands.append(allocator, .{ .segment = cmd });
- self.load_commands_dirty = true;
- }
-
if (self.linkedit_segment_cmd_index == null) {
- self.linkedit_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
- const base_cmd = self.base.load_commands.items[self.base.linkedit_segment_cmd_index.?].segment;
- var cmd = try self.copySegmentCommand(allocator, base_cmd);
+ self.linkedit_segment_cmd_index = @intCast(u8, self.segments.items.len);
// TODO this needs reworking
- cmd.inner.vmsize = self.base.page_size;
- cmd.inner.fileoff = self.base.page_size;
- cmd.inner.filesize = self.base.page_size;
- try self.load_commands.append(allocator, .{ .segment = cmd });
- self.load_commands_dirty = true;
+ try self.segments.append(allocator, .{
+ .segname = makeStaticString("__LINKEDIT"),
+ .vmaddr = self.base.page_size,
+ .vmsize = self.base.page_size,
+ .fileoff = self.base.page_size,
+ .filesize = self.base.page_size,
+ .maxprot = macho.PROT.READ,
+ .initprot = macho.PROT.READ,
+ .cmdsize = @sizeOf(macho.segment_command_64),
+ });
}
if (self.dwarf_segment_cmd_index == null) {
- self.dwarf_segment_cmd_index = @intCast(u16, self.load_commands.items.len);
+ self.dwarf_segment_cmd_index = @intCast(u8, self.segments.items.len);
- const linkedit = self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
+ const linkedit = self.segments.items[self.base.linkedit_segment_cmd_index.?];
const ideal_size: u16 = 200 + 128 + 160 + 250;
const needed_size = mem.alignForwardGeneric(u64, padToIdeal(ideal_size), self.base.page_size);
- const fileoff = linkedit.inner.fileoff + linkedit.inner.filesize;
- const vmaddr = linkedit.inner.vmaddr + linkedit.inner.vmsize;
+ const fileoff = linkedit.fileoff + linkedit.filesize;
+ const vmaddr = linkedit.vmaddr + linkedit.vmsize;
log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size });
- try self.load_commands.append(allocator, .{
- .segment = .{
- .inner = .{
- .segname = makeStaticString("__DWARF"),
- .vmaddr = vmaddr,
- .vmsize = needed_size,
- .fileoff = fileoff,
- .filesize = needed_size,
- .cmdsize = @sizeOf(macho.segment_command_64),
- },
- },
+ try self.segments.append(allocator, .{
+ .segname = makeStaticString("__DWARF"),
+ .vmaddr = vmaddr,
+ .vmsize = needed_size,
+ .fileoff = fileoff,
+ .filesize = needed_size,
+ .cmdsize = @sizeOf(macho.segment_command_64),
});
- self.load_commands_dirty = true;
}
if (self.debug_str_section_index == null) {
@@ -203,18 +128,18 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: Allocator) !void
}
}
-fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
- const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
+fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u8 {
+ const segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
var sect = macho.section_64{
.sectname = makeStaticString(sectname),
- .segname = seg.inner.segname,
+ .segname = segment.segname,
.size = @intCast(u32, size),
.@"align" = alignment,
};
const alignment_pow_2 = try math.powi(u32, 2, alignment);
const off = self.findFreeSpace(size, alignment_pow_2);
- assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
+ assert(off + size <= segment.fileoff + segment.filesize); // TODO expand
log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
sect.segName(),
@@ -223,31 +148,20 @@ fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignme
off + size,
});
- sect.addr = seg.inner.vmaddr + off - seg.inner.fileoff;
+ sect.addr = segment.vmaddr + off - segment.fileoff;
sect.offset = @intCast(u32, off);
- const index = @intCast(u16, seg.sections.items.len);
- try seg.sections.append(self.base.base.allocator, sect);
- seg.inner.cmdsize += @sizeOf(macho.section_64);
- seg.inner.nsects += 1;
-
- // TODO
- // const match = MatchingSection{
- // .seg = segment_id,
- // .sect = index,
- // };
- // _ = try self.section_ordinals.getOrPut(self.base.allocator, match);
- // try self.block_free_lists.putNoClobber(self.base.allocator, match, .{});
-
- self.load_commands_dirty = true;
+ const index = @intCast(u8, self.sections.items.len);
+ try self.sections.append(self.base.base.allocator, sect);
+ segment.cmdsize += @sizeOf(macho.section_64);
+ segment.nsects += 1;
return index;
}
fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
- const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
const end = start + padToIdeal(size);
- for (seg.sections.items) |section| {
+ for (self.sections.items) |section| {
const increased_size = padToIdeal(section.size);
const test_end = section.offset + increased_size;
if (end > section.offset and start < test_end) {
@@ -258,8 +172,8 @@ fn detectAllocCollision(self: *DebugSymbols, start: u64, size: u64) ?u64 {
}
pub fn findFreeSpace(self: *DebugSymbols, object_size: u64, min_alignment: u64) u64 {
- const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- var offset: u64 = seg.inner.fileoff;
+ const segment = self.segments.items[self.dwarf_segment_cmd_index.?];
+ var offset: u64 = segment.fileoff;
while (self.detectAllocCollision(offset, object_size)) |item_end| {
offset = mem.alignForwardGeneric(u64, item_end, min_alignment);
}
@@ -296,8 +210,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
break :blk got_entry.getName(self.base);
},
};
- const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- const sect = &seg.sections.items[self.debug_info_section_index.?];
+ const sect = &self.sections.items[self.debug_info_section_index.?];
const file_offset = sect.offset + reloc.offset;
log.debug("resolving relocation: {d}@{x} ('{s}') at offset {x}", .{
reloc.target,
@@ -311,15 +224,13 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
if (self.debug_abbrev_section_dirty) {
try self.dwarf.writeDbgAbbrev(&self.base.base);
- self.load_commands_dirty = true;
self.debug_abbrev_section_dirty = false;
}
if (self.debug_info_header_dirty) {
// Currently only one compilation unit is supported, so the address range is simply
// identical to the main program header virtual address and memory size.
- const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
- const text_section = text_segment.sections.items[self.text_section_index.?];
+ const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
const low_pc = text_section.addr;
const high_pc = text_section.addr + text_section.size;
try self.dwarf.writeDbgInfoHeader(&self.base.base, module, low_pc, high_pc);
@@ -329,10 +240,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
if (self.debug_aranges_section_dirty) {
// Currently only one compilation unit is supported, so the address range is simply
// identical to the main program header virtual address and memory size.
- const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].segment;
- const text_section = text_segment.sections.items[self.text_section_index.?];
+ const text_section = self.base.sections.items(.header)[self.base.text_section_index.?];
try self.dwarf.writeDbgAranges(&self.base.base, text_section.addr, text_section.size);
- self.load_commands_dirty = true;
self.debug_aranges_section_dirty = false;
}
@@ -342,8 +251,8 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
}
{
- const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- const debug_strtab_sect = &dwarf_segment.sections.items[self.debug_str_section_index.?];
+ const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
+ const debug_strtab_sect = &self.sections.items[self.debug_str_section_index.?];
if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != debug_strtab_sect.size) {
const allocated_size = self.allocatedSize(debug_strtab_sect.offset);
const needed_size = self.dwarf.strtab.items.len;
@@ -351,7 +260,7 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
if (needed_size > allocated_size) {
debug_strtab_sect.size = 0; // free the space
const new_offset = self.findFreeSpace(needed_size, 1);
- debug_strtab_sect.addr = dwarf_segment.inner.vmaddr + new_offset - dwarf_segment.inner.fileoff;
+ debug_strtab_sect.addr = dwarf_segment.vmaddr + new_offset - dwarf_segment.fileoff;
debug_strtab_sect.offset = @intCast(u32, new_offset);
}
debug_strtab_sect.size = @intCast(u32, needed_size);
@@ -362,28 +271,53 @@ pub fn flushModule(self: *DebugSymbols, allocator: Allocator, options: link.Opti
});
try self.file.pwriteAll(self.dwarf.strtab.items, debug_strtab_sect.offset);
- self.load_commands_dirty = true;
self.debug_string_table_dirty = false;
}
}
+ var lc_buffer = std.ArrayList(u8).init(allocator);
+ defer lc_buffer.deinit();
+ const lc_writer = lc_buffer.writer();
+ var ncmds: u32 = 0;
+
+ try self.writeLinkeditSegmentData(&ncmds, lc_writer);
self.updateDwarfSegment();
- try self.writeLinkeditSegment();
- try self.updateVirtualMemoryMapping();
- try self.writeLoadCommands(allocator);
- try self.writeHeader();
- assert(!self.load_commands_dirty);
+ {
+ try lc_writer.writeStruct(self.base.uuid);
+ ncmds += 1;
+ }
+
+ var headers_buf = std.ArrayList(u8).init(allocator);
+ defer headers_buf.deinit();
+ try self.base.writeSegmentHeaders(
+ 0,
+ self.base.linkedit_segment_cmd_index.?,
+ &ncmds,
+ headers_buf.writer(),
+ );
+
+ for (self.segments.items) |seg| {
+ try headers_buf.writer().writeStruct(seg);
+ ncmds += 2;
+ }
+ for (self.sections.items) |header| {
+ try headers_buf.writer().writeStruct(header);
+ }
+
+ try self.file.pwriteAll(headers_buf.items, @sizeOf(macho.mach_header_64));
+ try self.file.pwriteAll(lc_buffer.items, @sizeOf(macho.mach_header_64) + headers_buf.items.len);
+
+ try self.writeHeader(ncmds, @intCast(u32, lc_buffer.items.len + headers_buf.items.len));
+
assert(!self.debug_abbrev_section_dirty);
assert(!self.debug_aranges_section_dirty);
assert(!self.debug_string_table_dirty);
}
pub fn deinit(self: *DebugSymbols, allocator: Allocator) void {
- for (self.load_commands.items) |*lc| {
- lc.deinit(allocator);
- }
- self.load_commands.deinit(allocator);
+ self.segments.deinit(allocator);
+ self.sections.deinit(allocator);
self.dwarf.deinit();
self.strtab.deinit(allocator);
self.relocs.deinit(allocator);
@@ -402,59 +336,19 @@ pub fn swapRemoveRelocs(self: *DebugSymbols, target: u32) void {
}
}
-fn copySegmentCommand(
- self: *DebugSymbols,
- allocator: Allocator,
- base_cmd: macho.SegmentCommand,
-) !macho.SegmentCommand {
- var cmd = macho.SegmentCommand{
- .inner = .{
- .segname = undefined,
- .cmdsize = base_cmd.inner.cmdsize,
- .vmaddr = base_cmd.inner.vmaddr,
- .vmsize = base_cmd.inner.vmsize,
- .maxprot = base_cmd.inner.maxprot,
- .initprot = base_cmd.inner.initprot,
- .nsects = base_cmd.inner.nsects,
- .flags = base_cmd.inner.flags,
- },
- };
- mem.copy(u8, &cmd.inner.segname, &base_cmd.inner.segname);
-
- try cmd.sections.ensureTotalCapacity(allocator, cmd.inner.nsects);
- for (base_cmd.sections.items) |base_sect, i| {
- var sect = macho.section_64{
- .sectname = undefined,
- .segname = undefined,
- .addr = base_sect.addr,
- .size = base_sect.size,
- .offset = 0,
- .@"align" = base_sect.@"align",
- .reloff = 0,
- .nreloc = 0,
- .flags = base_sect.flags,
- .reserved1 = base_sect.reserved1,
- .reserved2 = base_sect.reserved2,
- .reserved3 = base_sect.reserved3,
- };
- mem.copy(u8, &sect.sectname, &base_sect.sectname);
- mem.copy(u8, &sect.segname, &base_sect.segname);
-
- if (self.base.text_section_index.? == i) {
- self.text_section_index = @intCast(u16, i);
- }
+fn updateDwarfSegment(self: *DebugSymbols) void {
+ const linkedit = self.segments.items[self.linkedit_segment_cmd_index.?];
+ const dwarf_segment = &self.segments.items[self.dwarf_segment_cmd_index.?];
- cmd.sections.appendAssumeCapacity(sect);
+ const new_start_aligned = linkedit.vmaddr + linkedit.vmsize;
+ const old_start_aligned = dwarf_segment.vmaddr;
+ const diff = new_start_aligned - old_start_aligned;
+ if (diff > 0) {
+ dwarf_segment.vmaddr = new_start_aligned;
}
- return cmd;
-}
-
-fn updateDwarfSegment(self: *DebugSymbols) void {
- const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
-
var max_offset: u64 = 0;
- for (dwarf_segment.sections.items) |sect| {
+ for (self.sections.items) |*sect| {
log.debug(" {s},{s} - 0x{x}-0x{x} - 0x{x}-0x{x}", .{
sect.segName(),
sect.sectName(),
@@ -466,44 +360,19 @@ fn updateDwarfSegment(self: *DebugSymbols) void {
if (sect.offset + sect.size > max_offset) {
max_offset = sect.offset + sect.size;
}
+ sect.addr += diff;
}
- const file_size = max_offset - dwarf_segment.inner.fileoff;
+ const file_size = max_offset - dwarf_segment.fileoff;
log.debug("__DWARF size 0x{x}", .{file_size});
- if (file_size != dwarf_segment.inner.filesize) {
- dwarf_segment.inner.filesize = file_size;
- if (dwarf_segment.inner.vmsize < dwarf_segment.inner.filesize) {
- dwarf_segment.inner.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.inner.filesize, self.base.page_size);
- }
- self.load_commands_dirty = true;
- }
-}
-
-/// Writes all load commands and section headers.
-fn writeLoadCommands(self: *DebugSymbols, allocator: Allocator) !void {
- if (!self.load_commands_dirty) return;
-
- var sizeofcmds: u32 = 0;
- for (self.load_commands.items) |lc| {
- sizeofcmds += lc.cmdsize();
+ if (file_size != dwarf_segment.filesize) {
+ dwarf_segment.filesize = file_size;
+ dwarf_segment.vmsize = mem.alignForwardGeneric(u64, dwarf_segment.filesize, self.base.page_size);
}
-
- var buffer = try allocator.alloc(u8, sizeofcmds);
- defer allocator.free(buffer);
- var fib = std.io.fixedBufferStream(buffer);
- const writer = fib.writer();
- for (self.load_commands.items) |lc| {
- try lc.write(writer);
- }
-
- const off = @sizeOf(macho.mach_header_64);
- log.debug("writing {} load commands from 0x{x} to 0x{x}", .{ self.load_commands.items.len, off, off + sizeofcmds });
- try self.file.pwriteAll(buffer, off);
- self.load_commands_dirty = false;
}
-fn writeHeader(self: *DebugSymbols) !void {
+fn writeHeader(self: *DebugSymbols, ncmds: u32, sizeofcmds: u32) !void {
var header: macho.mach_header_64 = .{};
header.filetype = macho.MH_DSYM;
@@ -519,12 +388,8 @@ fn writeHeader(self: *DebugSymbols) !void {
else => return error.UnsupportedCpuArchitecture,
}
- header.ncmds = @intCast(u32, self.load_commands.items.len);
- header.sizeofcmds = 0;
-
- for (self.load_commands.items) |cmd| {
- header.sizeofcmds += cmd.cmdsize();
- }
+ header.ncmds = ncmds;
+ header.sizeofcmds = sizeofcmds;
log.debug("writing Mach-O header {}", .{header});
@@ -532,79 +397,46 @@ fn writeHeader(self: *DebugSymbols) !void {
}
pub fn allocatedSize(self: *DebugSymbols, start: u64) u64 {
- const seg = self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- assert(start >= seg.inner.fileoff);
+ const seg = self.segments.items[self.dwarf_segment_cmd_index.?];
+ assert(start >= seg.fileoff);
var min_pos: u64 = std.math.maxInt(u64);
- for (seg.sections.items) |section| {
+ for (self.sections.items) |section| {
if (section.offset <= start) continue;
if (section.offset < min_pos) min_pos = section.offset;
}
return min_pos - start;
}
-fn updateVirtualMemoryMapping(self: *DebugSymbols) !void {
- const macho_file = self.base;
- const allocator = macho_file.base.allocator;
-
- const IndexTuple = std.meta.Tuple(&[_]type{ *?u16, *?u16 });
- const indices = &[_]IndexTuple{
- .{ &macho_file.text_segment_cmd_index, &self.text_segment_cmd_index },
- .{ &macho_file.data_const_segment_cmd_index, &self.data_const_segment_cmd_index },
- .{ &macho_file.data_segment_cmd_index, &self.data_segment_cmd_index },
- };
-
- for (indices) |tuple| {
- const orig_cmd = macho_file.load_commands.items[tuple[0].*.?].segment;
- const cmd = try self.copySegmentCommand(allocator, orig_cmd);
- const comp_cmd = &self.load_commands.items[tuple[1].*.?];
- comp_cmd.deinit(allocator);
- self.load_commands.items[tuple[1].*.?] = .{ .segment = cmd };
- }
-
- // TODO should we set the linkedit vmsize to that of the binary?
- const orig_cmd = macho_file.load_commands.items[macho_file.linkedit_segment_cmd_index.?].segment;
- const orig_vmaddr = orig_cmd.inner.vmaddr;
- const linkedit_cmd = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
- linkedit_cmd.inner.vmaddr = orig_vmaddr;
-
- // Update VM address for the DWARF segment and sections including re-running relocations.
- // TODO re-run relocations
- const dwarf_cmd = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- const new_start_aligned = orig_vmaddr + linkedit_cmd.inner.vmsize;
- const old_start_aligned = dwarf_cmd.inner.vmaddr;
- const diff = new_start_aligned - old_start_aligned;
- if (diff > 0) {
- dwarf_cmd.inner.vmaddr = new_start_aligned;
-
- for (dwarf_cmd.sections.items) |*sect| {
- sect.addr += (new_start_aligned - old_start_aligned);
- }
- }
-
- self.load_commands_dirty = true;
-}
-
-fn writeLinkeditSegment(self: *DebugSymbols) !void {
+fn writeLinkeditSegmentData(self: *DebugSymbols, ncmds: *u32, lc_writer: anytype) !void {
const tracy = trace(@src());
defer tracy.end();
- try self.writeSymbolTable();
- try self.writeStringTable();
+ const source_vmaddr = self.base.segments.items[self.base.linkedit_segment_cmd_index.?].vmaddr;
+ const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
+ seg.vmaddr = source_vmaddr;
- const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
- const aligned_size = mem.alignForwardGeneric(u64, seg.inner.filesize, self.base.page_size);
- seg.inner.filesize = aligned_size;
- seg.inner.vmsize = aligned_size;
+ var symtab_cmd = macho.symtab_command{
+ .cmdsize = @sizeOf(macho.symtab_command),
+ .symoff = 0,
+ .nsyms = 0,
+ .stroff = 0,
+ .strsize = 0,
+ };
+ try self.writeSymtab(&symtab_cmd);
+ try self.writeStrtab(&symtab_cmd);
+ try lc_writer.writeStruct(symtab_cmd);
+ ncmds.* += 1;
+
+ const aligned_size = mem.alignForwardGeneric(u64, seg.filesize, self.base.page_size);
+ seg.filesize = aligned_size;
+ seg.vmsize = aligned_size;
}
-fn writeSymbolTable(self: *DebugSymbols) !void {
+fn writeSymtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
const tracy = trace(@src());
defer tracy.end();
const gpa = self.base.base.allocator;
- const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
- symtab.symoff = @intCast(u32, seg.inner.fileoff);
var locals = std.ArrayList(macho.nlist_64).init(gpa);
defer locals.deinit();
@@ -634,34 +466,36 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
const nlocals = locals.items.len;
const nexports = exports.items.len;
- const locals_off = symtab.symoff;
- const locals_size = nlocals * @sizeOf(macho.nlist_64);
- const exports_off = locals_off + locals_size;
- const exports_size = nexports * @sizeOf(macho.nlist_64);
+ const nsyms = nlocals + nexports;
- symtab.nsyms = @intCast(u32, nlocals + nexports);
- const needed_size = (nlocals + nexports) * @sizeOf(macho.nlist_64);
+ const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
+ const offset = mem.alignForwardGeneric(
+ u64,
+ seg.fileoff + seg.filesize,
+ @alignOf(macho.nlist_64),
+ );
+ const needed_size = nsyms * @sizeOf(macho.nlist_64);
- if (needed_size > seg.inner.filesize) {
+ if (needed_size > seg.filesize) {
const aligned_size = mem.alignForwardGeneric(u64, needed_size, self.base.page_size);
- const diff = @intCast(u32, aligned_size - seg.inner.filesize);
- const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- seg.inner.filesize = aligned_size;
+ const diff = @intCast(u32, aligned_size - seg.filesize);
+ const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
+ seg.filesize = aligned_size;
try MachO.copyRangeAllOverlappingAlloc(
self.base.base.allocator,
self.file,
- dwarf_seg.inner.fileoff,
- dwarf_seg.inner.fileoff + diff,
- math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
+ dwarf_seg.fileoff,
+ dwarf_seg.fileoff + diff,
+ math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
);
- const old_seg_fileoff = dwarf_seg.inner.fileoff;
- dwarf_seg.inner.fileoff += diff;
+ const old_seg_fileoff = dwarf_seg.fileoff;
+ dwarf_seg.fileoff += diff;
- log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
+ log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
- for (dwarf_seg.sections.items) |*sect| {
+ for (self.sections.items) |*sect| {
const old_offset = sect.offset;
sect.offset += diff;
@@ -674,47 +508,53 @@ fn writeSymbolTable(self: *DebugSymbols) !void {
}
}
+ lc.symoff = @intCast(u32, offset);
+ lc.nsyms = @intCast(u32, nsyms);
+
+ const locals_off = lc.symoff;
+ const locals_size = nlocals * @sizeOf(macho.nlist_64);
+ const exports_off = locals_off + locals_size;
+ const exports_size = nexports * @sizeOf(macho.nlist_64);
+
log.debug("writing local symbols from 0x{x} to 0x{x}", .{ locals_off, locals_size + locals_off });
try self.file.pwriteAll(mem.sliceAsBytes(locals.items), locals_off);
log.debug("writing exported symbols from 0x{x} to 0x{x}", .{ exports_off, exports_size + exports_off });
try self.file.pwriteAll(mem.sliceAsBytes(exports.items), exports_off);
-
- self.load_commands_dirty = true;
}
-fn writeStringTable(self: *DebugSymbols) !void {
+fn writeStrtab(self: *DebugSymbols, lc: *macho.symtab_command) !void {
const tracy = trace(@src());
defer tracy.end();
- const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment;
- const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab;
- const symtab_size = @intCast(u32, symtab.nsyms * @sizeOf(macho.nlist_64));
- symtab.stroff = symtab.symoff + symtab_size;
+ const seg = &self.segments.items[self.linkedit_segment_cmd_index.?];
+ const symtab_size = @intCast(u32, lc.nsyms * @sizeOf(macho.nlist_64));
+ const offset = mem.alignForwardGeneric(u64, lc.symoff + symtab_size, @alignOf(u64));
+ lc.stroff = @intCast(u32, offset);
const needed_size = mem.alignForwardGeneric(u64, self.strtab.buffer.items.len, @alignOf(u64));
- symtab.strsize = @intCast(u32, needed_size);
+ lc.strsize = @intCast(u32, needed_size);
- if (symtab_size + needed_size > seg.inner.filesize) {
- const aligned_size = mem.alignForwardGeneric(u64, symtab_size + needed_size, self.base.page_size);
- const diff = @intCast(u32, aligned_size - seg.inner.filesize);
- const dwarf_seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].segment;
- seg.inner.filesize = aligned_size;
+ if (offset + needed_size > seg.filesize) {
+ const aligned_size = mem.alignForwardGeneric(u64, offset + needed_size, self.base.page_size);
+ const diff = @intCast(u32, aligned_size - seg.filesize);
+ const dwarf_seg = &self.segments.items[self.dwarf_segment_cmd_index.?];
+ seg.filesize = aligned_size;
try MachO.copyRangeAllOverlappingAlloc(
self.base.base.allocator,
self.file,
- dwarf_seg.inner.fileoff,
- dwarf_seg.inner.fileoff + diff,
- math.cast(usize, dwarf_seg.inner.filesize) orelse return error.Overflow,
+ dwarf_seg.fileoff,
+ dwarf_seg.fileoff + diff,
+ math.cast(usize, dwarf_seg.filesize) orelse return error.Overflow,
);
- const old_seg_fileoff = dwarf_seg.inner.fileoff;
- dwarf_seg.inner.fileoff += diff;
+ const old_seg_fileoff = dwarf_seg.fileoff;
+ dwarf_seg.fileoff += diff;
- log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.inner.fileoff });
+ log.debug(" (moving __DWARF segment from 0x{x} to 0x{x})", .{ old_seg_fileoff, dwarf_seg.fileoff });
- for (dwarf_seg.sections.items) |*sect| {
+ for (self.sections.items) |*sect| {
const old_offset = sect.offset;
sect.offset += diff;
@@ -727,9 +567,7 @@ fn writeStringTable(self: *DebugSymbols) !void {
}
}
- log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize });
-
- try self.file.pwriteAll(self.strtab.buffer.items, symtab.stroff);
+ log.debug("writing string table from 0x{x} to 0x{x}", .{ lc.stroff, lc.stroff + lc.strsize });
- self.load_commands_dirty = true;
+ try self.file.pwriteAll(self.strtab.buffer.items, lc.stroff);
}
diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig
index ffc0b2cca6..0f16eada61 100644
--- a/src/link/MachO/Dylib.zig
+++ b/src/link/MachO/Dylib.zig
@@ -13,23 +13,9 @@ const fat = @import("fat.zig");
const Allocator = mem.Allocator;
const CrossTarget = std.zig.CrossTarget;
const LibStub = @import("../tapi.zig").LibStub;
+const LoadCommandIterator = macho.LoadCommandIterator;
const MachO = @import("../MachO.zig");
-file: fs.File,
-name: []const u8,
-
-header: ?macho.mach_header_64 = null,
-
-// The actual dylib contents we care about linking with will be embedded at
-// an offset within a file if we are linking against a fat lib
-library_offset: u64 = 0,
-
-load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
-
-symtab_cmd_index: ?u16 = null,
-dysymtab_cmd_index: ?u16 = null,
-id_cmd_index: ?u16 = null,
-
id: ?Id = null,
weak: bool = false,
@@ -53,16 +39,12 @@ pub const Id = struct {
};
}
- pub fn fromLoadCommand(allocator: Allocator, lc: macho.GenericCommandWithData(macho.dylib_command)) !Id {
- const dylib = lc.inner.dylib;
- const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
- const name = try allocator.dupe(u8, mem.sliceTo(dylib_name, 0));
-
+ pub fn fromLoadCommand(allocator: Allocator, lc: macho.dylib_command, name: []const u8) !Id {
return Id{
- .name = name,
- .timestamp = dylib.timestamp,
- .current_version = dylib.current_version,
- .compatibility_version = dylib.compatibility_version,
+ .name = try allocator.dupe(u8, name),
+ .timestamp = lc.dylib.timestamp,
+ .current_version = lc.dylib.current_version,
+ .compatibility_version = lc.dylib.compatibility_version,
};
}
@@ -126,125 +108,89 @@ pub const Id = struct {
};
pub fn deinit(self: *Dylib, allocator: Allocator) void {
- for (self.load_commands.items) |*lc| {
- lc.deinit(allocator);
- }
- self.load_commands.deinit(allocator);
-
for (self.symbols.keys()) |key| {
allocator.free(key);
}
self.symbols.deinit(allocator);
-
- allocator.free(self.name);
-
if (self.id) |*id| {
id.deinit(allocator);
}
}
-pub fn parse(
+pub fn parseFromBinary(
self: *Dylib,
allocator: Allocator,
cpu_arch: std.Target.Cpu.Arch,
dylib_id: u16,
dependent_libs: anytype,
+ name: []const u8,
+ data: []align(@alignOf(u64)) const u8,
) !void {
- log.debug("parsing shared library '{s}'", .{self.name});
-
- self.library_offset = try fat.getLibraryOffset(self.file.reader(), cpu_arch);
+ var stream = std.io.fixedBufferStream(data);
+ const reader = stream.reader();
- try self.file.seekTo(self.library_offset);
+ log.debug("parsing shared library '{s}'", .{name});
- var reader = self.file.reader();
- self.header = try reader.readStruct(macho.mach_header_64);
+ const header = try reader.readStruct(macho.mach_header_64);
- if (self.header.?.filetype != macho.MH_DYLIB) {
- log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, self.header.?.filetype });
+ if (header.filetype != macho.MH_DYLIB) {
+ log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_DYLIB, header.filetype });
return error.NotDylib;
}
- const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
+ const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(header.cputype, true);
if (this_arch != cpu_arch) {
- log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
+ log.err("mismatched cpu architecture: expected {s}, found {s}", .{
+ @tagName(cpu_arch),
+ @tagName(this_arch),
+ });
return error.MismatchedCpuArchitecture;
}
- try self.readLoadCommands(allocator, reader, dylib_id, dependent_libs);
- try self.parseId(allocator);
- try self.parseSymbols(allocator);
-}
-
-fn readLoadCommands(
- self: *Dylib,
- allocator: Allocator,
- reader: anytype,
- dylib_id: u16,
- dependent_libs: anytype,
-) !void {
- const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
-
- try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
-
- var i: u16 = 0;
- while (i < self.header.?.ncmds) : (i += 1) {
- var cmd = try macho.LoadCommand.read(allocator, reader);
+ const should_lookup_reexports = header.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
+ var it = LoadCommandIterator{
+ .ncmds = header.ncmds,
+ .buffer = data[@sizeOf(macho.mach_header_64)..][0..header.sizeofcmds],
+ };
+ while (it.next()) |cmd| {
switch (cmd.cmd()) {
.SYMTAB => {
- self.symtab_cmd_index = i;
- },
- .DYSYMTAB => {
- self.dysymtab_cmd_index = i;
+ const symtab_cmd = cmd.cast(macho.symtab_command).?;
+ const symtab = @ptrCast(
+ [*]const macho.nlist_64,
+ @alignCast(@alignOf(macho.nlist_64), &data[symtab_cmd.symoff]),
+ )[0..symtab_cmd.nsyms];
+ const strtab = data[symtab_cmd.stroff..][0..symtab_cmd.strsize];
+
+ for (symtab) |sym| {
+ const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
+ if (!add_to_symtab) continue;
+
+ const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
+ try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {});
+ }
},
.ID_DYLIB => {
- self.id_cmd_index = i;
+ self.id = try Id.fromLoadCommand(
+ allocator,
+ cmd.cast(macho.dylib_command).?,
+ cmd.getDylibPathName(),
+ );
},
.REEXPORT_DYLIB => {
if (should_lookup_reexports) {
// Parse install_name to dependent dylib.
- var id = try Id.fromLoadCommand(allocator, cmd.dylib);
+ var id = try Id.fromLoadCommand(
+ allocator,
+ cmd.cast(macho.dylib_command).?,
+ cmd.getDylibPathName(),
+ );
try dependent_libs.writeItem(.{ .id = id, .parent = dylib_id });
}
},
- else => {
- log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
- },
+ else => {},
}
- self.load_commands.appendAssumeCapacity(cmd);
- }
-}
-
-fn parseId(self: *Dylib, allocator: Allocator) !void {
- const index = self.id_cmd_index orelse {
- log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
- self.id = try Id.default(allocator, self.name);
- return;
- };
- self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].dylib);
-}
-
-fn parseSymbols(self: *Dylib, allocator: Allocator) !void {
- const index = self.symtab_cmd_index orelse return;
- const symtab_cmd = self.load_commands.items[index].symtab;
-
- const symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms);
- defer allocator.free(symtab);
- _ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset);
- const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab));
-
- const strtab = try allocator.alloc(u8, symtab_cmd.strsize);
- defer allocator.free(strtab);
- _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset);
-
- for (slice) |sym| {
- const add_to_symtab = sym.ext() and (sym.sect() or sym.indr());
-
- if (!add_to_symtab) continue;
-
- const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
- const name = try allocator.dupe(u8, sym_name);
- try self.symbols.putNoClobber(allocator, name, {});
}
}
@@ -356,10 +302,11 @@ pub fn parseFromStub(
lib_stub: LibStub,
dylib_id: u16,
dependent_libs: anytype,
+ name: []const u8,
) !void {
if (lib_stub.inner.len == 0) return error.EmptyStubFile;
- log.debug("parsing shared library from stub '{s}'", .{self.name});
+ log.debug("parsing shared library from stub '{s}'", .{name});
const umbrella_lib = lib_stub.inner[0];
diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig
index 0d929627cd..2e2f3dad84 100644
--- a/src/link/MachO/Object.zig
+++ b/src/link/MachO/Object.zig
@@ -3,6 +3,7 @@ const Object = @This();
const std = @import("std");
const build_options = @import("build_options");
const assert = std.debug.assert;
+const dwarf = std.dwarf;
const fs = std.fs;
const io = std.io;
const log = std.log.scoped(.link);
@@ -14,43 +15,20 @@ const trace = @import("../../tracy.zig").trace;
const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
+const LoadCommandIterator = macho.LoadCommandIterator;
const MachO = @import("../MachO.zig");
-const MatchingSection = MachO.MatchingSection;
const SymbolWithLoc = MachO.SymbolWithLoc;
-file: fs.File,
name: []const u8,
mtime: u64,
-
-/// Data contents of the file. Includes sections, and data of load commands.
-/// Excludes the backing memory for the header and load commands.
-/// Initialized in `parse`.
-contents: []const u8 = undefined,
-
-file_offset: ?u32 = null,
+contents: []align(@alignOf(u64)) const u8,
header: macho.mach_header_64 = undefined,
-
-load_commands: std.ArrayListUnmanaged(macho.LoadCommand) = .{},
-
-segment_cmd_index: ?u16 = null,
-text_section_index: ?u16 = null,
-symtab_cmd_index: ?u16 = null,
-dysymtab_cmd_index: ?u16 = null,
-build_version_cmd_index: ?u16 = null,
-data_in_code_cmd_index: ?u16 = null,
-
-// __DWARF segment sections
-dwarf_debug_info_index: ?u16 = null,
-dwarf_debug_abbrev_index: ?u16 = null,
-dwarf_debug_str_index: ?u16 = null,
-dwarf_debug_line_index: ?u16 = null,
-dwarf_debug_line_str_index: ?u16 = null,
-dwarf_debug_ranges_index: ?u16 = null,
+in_symtab: []const macho.nlist_64 = undefined,
+in_strtab: []const u8 = undefined,
symtab: std.ArrayListUnmanaged(macho.nlist_64) = .{},
-strtab: []const u8 = &.{},
-data_in_code_entries: []const macho.data_in_code_entry = &.{},
+sections: std.ArrayListUnmanaged(macho.section_64) = .{},
sections_as_symbols: std.AutoHashMapUnmanaged(u16, u32) = .{},
@@ -61,12 +39,8 @@ managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
pub fn deinit(self: *Object, gpa: Allocator) void {
- for (self.load_commands.items) |*lc| {
- lc.deinit(gpa);
- }
- self.load_commands.deinit(gpa);
- gpa.free(self.contents);
self.symtab.deinit(gpa);
+ self.sections.deinit(gpa);
self.sections_as_symbols.deinit(gpa);
self.atom_by_index_table.deinit(gpa);
@@ -77,22 +51,15 @@ pub fn deinit(self: *Object, gpa: Allocator) void {
self.managed_atoms.deinit(gpa);
gpa.free(self.name);
+ gpa.free(self.contents);
}
pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch) !void {
- const file_stat = try self.file.stat();
- const file_size = math.cast(usize, file_stat.size) orelse return error.Overflow;
- self.contents = try self.file.readToEndAlloc(allocator, file_size);
-
var stream = std.io.fixedBufferStream(self.contents);
const reader = stream.reader();
- const file_offset = self.file_offset orelse 0;
- if (file_offset > 0) {
- try reader.context.seekTo(file_offset);
- }
-
self.header = try reader.readStruct(macho.mach_header_64);
+
if (self.header.filetype != macho.MH_OBJECT) {
log.debug("invalid filetype: expected 0x{x}, found 0x{x}", .{
macho.MH_OBJECT,
@@ -110,92 +77,54 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
},
};
if (this_arch != cpu_arch) {
- log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
+ log.err("mismatched cpu architecture: expected {s}, found {s}", .{
+ @tagName(cpu_arch),
+ @tagName(this_arch),
+ });
return error.MismatchedCpuArchitecture;
}
- try self.load_commands.ensureUnusedCapacity(allocator, self.header.ncmds);
-
- var i: u16 = 0;
- while (i < self.header.ncmds) : (i += 1) {
- var cmd = try macho.LoadCommand.read(allocator, reader);
+ var it = LoadCommandIterator{
+ .ncmds = self.header.ncmds,
+ .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
+ };
+ while (it.next()) |cmd| {
switch (cmd.cmd()) {
.SEGMENT_64 => {
- self.segment_cmd_index = i;
- var seg = cmd.segment;
- for (seg.sections.items) |*sect, j| {
- const index = @intCast(u16, j);
- const segname = sect.segName();
- const sectname = sect.sectName();
- if (mem.eql(u8, segname, "__DWARF")) {
- if (mem.eql(u8, sectname, "__debug_info")) {
- self.dwarf_debug_info_index = index;
- } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
- self.dwarf_debug_abbrev_index = index;
- } else if (mem.eql(u8, sectname, "__debug_str")) {
- self.dwarf_debug_str_index = index;
- } else if (mem.eql(u8, sectname, "__debug_line")) {
- self.dwarf_debug_line_index = index;
- } else if (mem.eql(u8, sectname, "__debug_line_str")) {
- self.dwarf_debug_line_str_index = index;
- } else if (mem.eql(u8, sectname, "__debug_ranges")) {
- self.dwarf_debug_ranges_index = index;
- }
- } else if (mem.eql(u8, segname, "__TEXT")) {
- if (mem.eql(u8, sectname, "__text")) {
- self.text_section_index = index;
- }
- }
-
- sect.offset += file_offset;
- if (sect.reloff > 0) {
- sect.reloff += file_offset;
- }
+ const segment = cmd.cast(macho.segment_command_64).?;
+ try self.sections.ensureUnusedCapacity(allocator, segment.nsects);
+ for (cmd.getSections()) |sect| {
+ self.sections.appendAssumeCapacity(sect);
}
-
- seg.inner.fileoff += file_offset;
},
.SYMTAB => {
- self.symtab_cmd_index = i;
- cmd.symtab.symoff += file_offset;
- cmd.symtab.stroff += file_offset;
- },
- .DYSYMTAB => {
- self.dysymtab_cmd_index = i;
- },
- .BUILD_VERSION => {
- self.build_version_cmd_index = i;
- },
- .DATA_IN_CODE => {
- self.data_in_code_cmd_index = i;
- cmd.linkedit_data.dataoff += file_offset;
- },
- else => {
- log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
+ const symtab = cmd.cast(macho.symtab_command).?;
+ self.in_symtab = @ptrCast(
+ [*]const macho.nlist_64,
+ @alignCast(@alignOf(macho.nlist_64), &self.contents[symtab.symoff]),
+ )[0..symtab.nsyms];
+ self.in_strtab = self.contents[symtab.stroff..][0..symtab.strsize];
+ try self.symtab.appendSlice(allocator, self.in_symtab);
},
+ else => {},
}
- self.load_commands.appendAssumeCapacity(cmd);
}
-
- try self.parseSymtab(allocator);
}
const Context = struct {
- symtab: []const macho.nlist_64,
- strtab: []const u8,
+ object: *const Object,
};
const SymbolAtIndex = struct {
index: u32,
fn getSymbol(self: SymbolAtIndex, ctx: Context) macho.nlist_64 {
- return ctx.symtab[self.index];
+ return ctx.object.getSourceSymbol(self.index).?;
}
fn getSymbolName(self: SymbolAtIndex, ctx: Context) []const u8 {
const sym = self.getSymbol(ctx);
- assert(sym.n_strx < ctx.strtab.len);
- return mem.sliceTo(@ptrCast([*:0]const u8, ctx.strtab.ptr + sym.n_strx), 0);
+ return ctx.object.getString(sym.n_strx);
}
/// Returns whether lhs is less than rhs by allocated address in object file.
@@ -293,7 +222,6 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
defer tracy.end();
const gpa = macho_file.base.allocator;
- const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
log.debug("splitting object({d}, {s}) into atoms: one-shot mode", .{ object_id, self.name });
@@ -302,13 +230,12 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
// the GO compiler does not necessarily respect that therefore we sort immediately by type
// and address within.
const context = Context{
- .symtab = self.getSourceSymtab(),
- .strtab = self.strtab,
+ .object = self,
};
- var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, context.symtab.len);
+ var sorted_all_syms = try std.ArrayList(SymbolAtIndex).initCapacity(gpa, self.in_symtab.len);
defer sorted_all_syms.deinit();
- for (context.symtab) |_, index| {
+ for (self.in_symtab) |_, index| {
sorted_all_syms.appendAssumeCapacity(.{ .index = @intCast(u32, index) });
}
@@ -320,36 +247,36 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
// Well, shit, sometimes compilers skip the dysymtab load command altogether, meaning we
// have to infer the start of undef section in the symtab ourselves.
- const iundefsym = if (self.dysymtab_cmd_index) |cmd_index| blk: {
- const dysymtab = self.load_commands.items[cmd_index].dysymtab;
+ const iundefsym = blk: {
+ const dysymtab = self.parseDysymtab() orelse {
+ var iundefsym: usize = sorted_all_syms.items.len;
+ while (iundefsym > 0) : (iundefsym -= 1) {
+ const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
+ if (sym.sect()) break;
+ }
+ break :blk iundefsym;
+ };
break :blk dysymtab.iundefsym;
- } else blk: {
- var iundefsym: usize = sorted_all_syms.items.len;
- while (iundefsym > 0) : (iundefsym -= 1) {
- const sym = sorted_all_syms.items[iundefsym - 1].getSymbol(context);
- if (sym.sect()) break;
- }
- break :blk iundefsym;
};
// We only care about defined symbols, so filter every other out.
const sorted_syms = sorted_all_syms.items[0..iundefsym];
const subsections_via_symbols = self.header.flags & macho.MH_SUBSECTIONS_VIA_SYMBOLS != 0;
- for (seg.sections.items) |sect, id| {
+ for (self.sections.items) |sect, id| {
const sect_id = @intCast(u8, id);
log.debug("splitting section '{s},{s}' into atoms", .{ sect.segName(), sect.sectName() });
// Get matching segment/section in the final artifact.
- const match = (try macho_file.getMatchingSection(sect)) orelse {
+ const match = (try macho_file.getOutputSection(sect)) orelse {
log.debug(" unhandled section", .{});
continue;
};
log.debug(" output sect({d}, '{s},{s}')", .{
- macho_file.getSectionOrdinal(match),
- macho_file.getSection(match).segName(),
- macho_file.getSection(match).sectName(),
+ match + 1,
+ macho_file.sections.items(.header)[match].segName(),
+ macho_file.sections.items(.header)[match].sectName(),
});
const cpu_arch = macho_file.base.options.target.cpu.arch;
@@ -359,14 +286,13 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
};
// Read section's code
- const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect_id) else null;
+ const code: ?[]const u8 = if (!is_zerofill) try self.getSectionContents(sect) else null;
// Read section's list of relocations
- const raw_relocs = self.contents[sect.reloff..][0 .. sect.nreloc * @sizeOf(macho.relocation_info)];
- const relocs = mem.bytesAsSlice(
- macho.relocation_info,
- @alignCast(@alignOf(macho.relocation_info), raw_relocs),
- );
+ const relocs = @ptrCast(
+ [*]const macho.relocation_info,
+ @alignCast(@alignOf(macho.relocation_info), &self.contents[sect.reloff]),
+ )[0..sect.nreloc];
// Symbols within this section only.
const filtered_syms = filterSymbolsByAddress(
@@ -387,7 +313,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
try self.symtab.append(gpa, .{
.n_strx = 0,
.n_type = macho.N_SECT,
- .n_sect = macho_file.getSectionOrdinal(match),
+ .n_sect = match + 1,
.n_desc = 0,
.n_value = sect.addr,
});
@@ -476,7 +402,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
try self.symtab.append(gpa, .{
.n_strx = 0,
.n_type = macho.N_SECT,
- .n_sect = macho_file.getSectionOrdinal(match),
+ .n_sect = match + 1,
.n_desc = 0,
.n_value = addr,
});
@@ -501,7 +427,7 @@ pub fn splitIntoAtomsOneShot(self: *Object, macho_file: *MachO, object_id: u32)
try self.symtab.append(gpa, .{
.n_strx = 0,
.n_type = macho.N_SECT,
- .n_sect = macho_file.getSectionOrdinal(match),
+ .n_sect = match + 1,
.n_desc = 0,
.n_value = sect.addr,
});
@@ -535,21 +461,21 @@ fn createAtomFromSubsection(
code: ?[]const u8,
relocs: []const macho.relocation_info,
indexes: []const SymbolAtIndex,
- match: MatchingSection,
+ match: u8,
sect: macho.section_64,
) !*Atom {
const gpa = macho_file.base.allocator;
const sym = self.symtab.items[sym_index];
const atom = try MachO.createEmptyAtom(gpa, sym_index, size, alignment);
atom.file = object_id;
- self.symtab.items[sym_index].n_sect = macho_file.getSectionOrdinal(match);
+ self.symtab.items[sym_index].n_sect = match + 1;
log.debug("creating ATOM(%{d}, '{s}') in sect({d}, '{s},{s}') in object({d})", .{
sym_index,
self.getString(sym.n_strx),
- macho_file.getSectionOrdinal(match),
- macho_file.getSection(match).segName(),
- macho_file.getSection(match).sectName(),
+ match + 1,
+ macho_file.sections.items(.header)[match].segName(),
+ macho_file.sections.items(.header)[match].sectName(),
object_id,
});
@@ -577,7 +503,7 @@ fn createAtomFromSubsection(
try atom.contained.ensureTotalCapacity(gpa, indexes.len);
for (indexes) |inner_sym_index| {
const inner_sym = &self.symtab.items[inner_sym_index.index];
- inner_sym.n_sect = macho_file.getSectionOrdinal(match);
+ inner_sym.n_sect = match + 1;
atom.contained.appendAssumeCapacity(.{
.sym_index = inner_sym_index.index,
.offset = inner_sym.n_value - sym.n_value,
@@ -589,48 +515,84 @@ fn createAtomFromSubsection(
return atom;
}
-fn parseSymtab(self: *Object, allocator: Allocator) !void {
- const index = self.symtab_cmd_index orelse return;
- const symtab = self.load_commands.items[index].symtab;
- try self.symtab.appendSlice(allocator, self.getSourceSymtab());
- self.strtab = self.contents[symtab.stroff..][0..symtab.strsize];
+pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
+ if (index >= self.in_symtab.len) return null;
+ return self.in_symtab[index];
}
-pub fn getSourceSymtab(self: Object) []const macho.nlist_64 {
- const index = self.symtab_cmd_index orelse return &[0]macho.nlist_64{};
- const symtab = self.load_commands.items[index].symtab;
- const symtab_size = @sizeOf(macho.nlist_64) * symtab.nsyms;
- const raw_symtab = self.contents[symtab.symoff..][0..symtab_size];
- return mem.bytesAsSlice(
- macho.nlist_64,
- @alignCast(@alignOf(macho.nlist_64), raw_symtab),
- );
+pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
+ assert(index < self.sections.items.len);
+ return self.sections.items[index];
}
-pub fn getSourceSymbol(self: Object, index: u32) ?macho.nlist_64 {
- const symtab = self.getSourceSymtab();
- if (index >= symtab.len) return null;
- return symtab[index];
+pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
+ var it = LoadCommandIterator{
+ .ncmds = self.header.ncmds,
+ .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
+ };
+ while (it.next()) |cmd| {
+ switch (cmd.cmd()) {
+ .DATA_IN_CODE => {
+ const dice = cmd.cast(macho.linkedit_data_command).?;
+ const ndice = @divExact(dice.datasize, @sizeOf(macho.data_in_code_entry));
+ return @ptrCast(
+ [*]const macho.data_in_code_entry,
+ @alignCast(@alignOf(macho.data_in_code_entry), &self.contents[dice.dataoff]),
+ )[0..ndice];
+ },
+ else => {},
+ }
+ } else return null;
}
-pub fn getSourceSection(self: Object, index: u16) macho.section_64 {
- const seg = self.load_commands.items[self.segment_cmd_index.?].segment;
- assert(index < seg.sections.items.len);
- return seg.sections.items[index];
+fn parseDysymtab(self: Object) ?macho.dysymtab_command {
+ var it = LoadCommandIterator{
+ .ncmds = self.header.ncmds,
+ .buffer = self.contents[@sizeOf(macho.mach_header_64)..][0..self.header.sizeofcmds],
+ };
+ while (it.next()) |cmd| {
+ switch (cmd.cmd()) {
+ .DYSYMTAB => {
+ return cmd.cast(macho.dysymtab_command).?;
+ },
+ else => {},
+ }
+ } else return null;
}
-pub fn parseDataInCode(self: Object) ?[]const macho.data_in_code_entry {
- const index = self.data_in_code_cmd_index orelse return null;
- const data_in_code = self.load_commands.items[index].linkedit_data;
- const raw_dice = self.contents[data_in_code.dataoff..][0..data_in_code.datasize];
- return mem.bytesAsSlice(
- macho.data_in_code_entry,
- @alignCast(@alignOf(macho.data_in_code_entry), raw_dice),
- );
+pub fn parseDwarfInfo(self: Object) error{Overflow}!dwarf.DwarfInfo {
+ var di = dwarf.DwarfInfo{
+ .endian = .Little,
+ .debug_info = &[0]u8{},
+ .debug_abbrev = &[0]u8{},
+ .debug_str = &[0]u8{},
+ .debug_line = &[0]u8{},
+ .debug_line_str = &[0]u8{},
+ .debug_ranges = &[0]u8{},
+ };
+ for (self.sections.items) |sect| {
+ const segname = sect.segName();
+ const sectname = sect.sectName();
+ if (mem.eql(u8, segname, "__DWARF")) {
+ if (mem.eql(u8, sectname, "__debug_info")) {
+ di.debug_info = try self.getSectionContents(sect);
+ } else if (mem.eql(u8, sectname, "__debug_abbrev")) {
+ di.debug_abbrev = try self.getSectionContents(sect);
+ } else if (mem.eql(u8, sectname, "__debug_str")) {
+ di.debug_str = try self.getSectionContents(sect);
+ } else if (mem.eql(u8, sectname, "__debug_line")) {
+ di.debug_line = try self.getSectionContents(sect);
+ } else if (mem.eql(u8, sectname, "__debug_line_str")) {
+ di.debug_line_str = try self.getSectionContents(sect);
+ } else if (mem.eql(u8, sectname, "__debug_ranges")) {
+ di.debug_ranges = try self.getSectionContents(sect);
+ }
+ }
+ }
+ return di;
}
-pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
- const sect = self.getSourceSection(index);
+pub fn getSectionContents(self: Object, sect: macho.section_64) error{Overflow}![]const u8 {
const size = math.cast(usize, sect.size) orelse return error.Overflow;
log.debug("getting {s},{s} data at 0x{x} - 0x{x}", .{
sect.segName(),
@@ -642,8 +604,8 @@ pub fn getSectionContents(self: Object, index: u16) error{Overflow}![]const u8 {
}
pub fn getString(self: Object, off: u32) []const u8 {
- assert(off < self.strtab.len);
- return mem.sliceTo(@ptrCast([*:0]const u8, self.strtab.ptr + off), 0);
+ assert(off < self.in_strtab.len);
+ return mem.sliceTo(@ptrCast([*:0]const u8, self.in_strtab.ptr + off), 0);
}
pub fn getAtomForSymbol(self: Object, sym_index: u32) ?*Atom {
diff --git a/src/link/MachO/dead_strip.zig b/src/link/MachO/dead_strip.zig
index 909a0450d6..12f46c9f26 100644
--- a/src/link/MachO/dead_strip.zig
+++ b/src/link/MachO/dead_strip.zig
@@ -8,7 +8,6 @@ const mem = std.mem;
const Allocator = mem.Allocator;
const Atom = @import("Atom.zig");
const MachO = @import("../MachO.zig");
-const MatchingSection = MachO.MatchingSection;
pub fn gcAtoms(macho_file: *MachO) !void {
const gpa = macho_file.base.allocator;
@@ -25,12 +24,12 @@ pub fn gcAtoms(macho_file: *MachO) !void {
try prune(arena, alive, macho_file);
}
-fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO) void {
- const sect = macho_file.getSectionPtr(match);
+fn removeAtomFromSection(atom: *Atom, match: u8, macho_file: *MachO) void {
+ var section = macho_file.sections.get(match);
// If we want to enable GC for incremental codepath, we need to take into
// account any padding that might have been left here.
- sect.size -= atom.size;
+ section.header.size -= atom.size;
if (atom.prev) |prev| {
prev.next = atom.next;
@@ -38,15 +37,16 @@ fn removeAtomFromSection(atom: *Atom, match: MatchingSection, macho_file: *MachO
if (atom.next) |next| {
next.prev = atom.prev;
} else {
- const last = macho_file.atoms.getPtr(match).?;
if (atom.prev) |prev| {
- last.* = prev;
+ section.last_atom = prev;
} else {
// The section will be GCed in the next step.
- last.* = undefined;
- sect.size = 0;
+ section.last_atom = null;
+ section.header.size = 0;
}
}
+
+ macho_file.sections.set(match, section);
}
fn collectRoots(roots: *std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
@@ -173,19 +173,19 @@ fn mark(
fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *MachO) !void {
// Any section that ends up here will be updated, that is,
// its size and alignment recalculated.
- var gc_sections = std.AutoHashMap(MatchingSection, void).init(arena);
+ var gc_sections = std.AutoHashMap(u8, void).init(arena);
var loop: bool = true;
while (loop) {
loop = false;
for (macho_file.objects.items) |object| {
- for (object.getSourceSymtab()) |_, source_index| {
+ for (object.in_symtab) |_, source_index| {
const atom = object.getAtomForSymbol(@intCast(u32, source_index)) orelse continue;
if (alive.contains(atom)) continue;
const global = atom.getSymbolWithLoc();
const sym = atom.getSymbolPtr(macho_file);
- const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
+ const match = sym.n_sect - 1;
if (sym.n_desc == MachO.N_DESC_GCED) continue;
if (!sym.ext() and !refersDead(atom, macho_file)) continue;
@@ -232,7 +232,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
// TODO tombstone
const atom = entry.getAtom(macho_file);
- const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
+ const match = sym.n_sect - 1;
removeAtomFromSection(atom, match, macho_file);
_ = try gc_sections.put(match, {});
_ = macho_file.got_entries_table.remove(entry.target);
@@ -244,7 +244,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
// TODO tombstone
const atom = entry.getAtom(macho_file);
- const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
+ const match = sym.n_sect - 1;
removeAtomFromSection(atom, match, macho_file);
_ = try gc_sections.put(match, {});
_ = macho_file.stubs_table.remove(entry.target);
@@ -256,7 +256,7 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
// TODO tombstone
const atom = entry.getAtom(macho_file);
- const match = macho_file.getMatchingSectionFromOrdinal(sym.n_sect);
+ const match = sym.n_sect - 1;
removeAtomFromSection(atom, match, macho_file);
_ = try gc_sections.put(match, {});
_ = macho_file.tlv_ptr_entries_table.remove(entry.target);
@@ -265,13 +265,13 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
var gc_sections_it = gc_sections.iterator();
while (gc_sections_it.next()) |entry| {
const match = entry.key_ptr.*;
- const sect = macho_file.getSectionPtr(match);
- if (sect.size == 0) continue; // Pruning happens automatically in next step.
+ var section = macho_file.sections.get(match);
+ if (section.header.size == 0) continue; // Pruning happens automatically in next step.
- sect.@"align" = 0;
- sect.size = 0;
+ section.header.@"align" = 0;
+ section.header.size = 0;
- var atom = macho_file.atoms.get(match).?;
+ var atom = section.last_atom.?;
while (atom.prev) |prev| {
atom = prev;
@@ -279,14 +279,16 @@ fn prune(arena: Allocator, alive: std.AutoHashMap(*Atom, void), macho_file: *Mac
while (true) {
const atom_alignment = try math.powi(u32, 2, atom.alignment);
- const aligned_end_addr = mem.alignForwardGeneric(u64, sect.size, atom_alignment);
- const padding = aligned_end_addr - sect.size;
- sect.size += padding + atom.size;
- sect.@"align" = @maximum(sect.@"align", atom.alignment);
+ const aligned_end_addr = mem.alignForwardGeneric(u64, section.header.size, atom_alignment);
+ const padding = aligned_end_addr - section.header.size;
+ section.header.size += padding + atom.size;
+ section.header.@"align" = @maximum(section.header.@"align", atom.alignment);
if (atom.next) |next| {
atom = next;
} else break;
}
+
+ macho_file.sections.set(match, section);
}
}
diff --git a/src/link/MachO/fat.zig b/src/link/MachO/fat.zig
index 1511f274a8..7c328c1418 100644
--- a/src/link/MachO/fat.zig
+++ b/src/link/MachO/fat.zig
@@ -46,7 +46,9 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
return fat_arch.offset;
}
} else {
- log.err("Could not find matching cpu architecture in fat library: expected {}", .{cpu_arch});
+ log.err("Could not find matching cpu architecture in fat library: expected {s}", .{
+ @tagName(cpu_arch),
+ });
return error.MismatchedCpuArchitecture;
}
}