macho: fix allocating sections within segment when parsing objects
This commit is contained in:
@@ -601,35 +601,35 @@ pub const segment_command = extern struct {
|
||||
/// command and their size is reflected in cmdsize.
|
||||
pub const segment_command_64 = extern struct {
|
||||
/// LC_SEGMENT_64
|
||||
cmd: u32,
|
||||
cmd: u32 = LC_SEGMENT_64,
|
||||
|
||||
/// includes sizeof section_64 structs
|
||||
cmdsize: u32,
|
||||
cmdsize: u32 = @sizeOf(segment_command_64),
|
||||
|
||||
/// segment name
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this segment
|
||||
vmaddr: u64,
|
||||
vmaddr: u64 = 0,
|
||||
|
||||
/// memory size of this segment
|
||||
vmsize: u64,
|
||||
vmsize: u64 = 0,
|
||||
|
||||
/// file offset of this segment
|
||||
fileoff: u64,
|
||||
fileoff: u64 = 0,
|
||||
|
||||
/// amount to map from the file
|
||||
filesize: u64,
|
||||
filesize: u64 = 0,
|
||||
|
||||
/// maximum VM protection
|
||||
maxprot: vm_prot_t,
|
||||
maxprot: vm_prot_t = VM_PROT_NONE,
|
||||
|
||||
/// initial VM protection
|
||||
initprot: vm_prot_t,
|
||||
initprot: vm_prot_t = VM_PROT_NONE,
|
||||
|
||||
/// number of sections in segment
|
||||
nsects: u32,
|
||||
flags: u32,
|
||||
nsects: u32 = 0,
|
||||
flags: u32 = 0,
|
||||
};
|
||||
|
||||
/// A segment is made up of zero or more sections. Non-MH_OBJECT files have
|
||||
@@ -700,34 +700,34 @@ pub const section_64 = extern struct {
|
||||
segname: [16]u8,
|
||||
|
||||
/// memory address of this section
|
||||
addr: u64,
|
||||
addr: u64 = 0,
|
||||
|
||||
/// size in bytes of this section
|
||||
size: u64,
|
||||
size: u64 = 0,
|
||||
|
||||
/// file offset of this section
|
||||
offset: u32,
|
||||
offset: u32 = 0,
|
||||
|
||||
/// section alignment (power of 2)
|
||||
@"align": u32,
|
||||
@"align": u32 = 0,
|
||||
|
||||
/// file offset of relocation entries
|
||||
reloff: u32,
|
||||
reloff: u32 = 0,
|
||||
|
||||
/// number of relocation entries
|
||||
nreloc: u32,
|
||||
nreloc: u32 = 0,
|
||||
|
||||
/// flags (section type and attributes
|
||||
flags: u32,
|
||||
flags: u32 = S_REGULAR,
|
||||
|
||||
/// reserved (for offset or index)
|
||||
reserved1: u32,
|
||||
reserved1: u32 = 0,
|
||||
|
||||
/// reserved (for count or sizeof)
|
||||
reserved2: u32,
|
||||
reserved2: u32 = 0,
|
||||
|
||||
/// reserved
|
||||
reserved3: u32,
|
||||
reserved3: u32 = 0,
|
||||
};
|
||||
|
||||
pub const nlist = extern struct {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,23 +5,26 @@ const assert = std.debug.assert;
|
||||
const fs = std.fs;
|
||||
const log = std.log.scoped(.dsym);
|
||||
const macho = std.macho;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const DW = std.dwarf;
|
||||
const leb = std.leb;
|
||||
const Allocator = mem.Allocator;
|
||||
|
||||
const build_options = @import("build_options");
|
||||
const commands = @import("commands.zig");
|
||||
const trace = @import("../../tracy.zig").trace;
|
||||
const LoadCommand = commands.LoadCommand;
|
||||
const Module = @import("../../Module.zig");
|
||||
const Type = @import("../../type.zig").Type;
|
||||
const link = @import("../../link.zig");
|
||||
const MachO = @import("../MachO.zig");
|
||||
const SrcFn = MachO.SrcFn;
|
||||
const TextBlock = MachO.TextBlock;
|
||||
const SegmentCommand = commands.SegmentCommand;
|
||||
const SrcFn = MachO.SrcFn;
|
||||
const makeStaticString = MachO.makeStaticString;
|
||||
const padToIdeal = MachO.padToIdeal;
|
||||
|
||||
usingnamespace @import("commands.zig");
|
||||
|
||||
const page_size: u16 = 0x1000;
|
||||
|
||||
base: *MachO,
|
||||
@@ -185,107 +188,86 @@ pub fn populateMissingMetadata(self: *DebugSymbols, allocator: *Allocator) !void
|
||||
log.debug("found __DWARF segment free space 0x{x} to 0x{x}", .{ off, off + needed_size });
|
||||
|
||||
try self.load_commands.append(allocator, .{
|
||||
.Segment = SegmentCommand.empty("__DWARF", .{
|
||||
.vmaddr = vmaddr,
|
||||
.vmsize = needed_size,
|
||||
.fileoff = off,
|
||||
.filesize = needed_size,
|
||||
}),
|
||||
.Segment = .{
|
||||
.inner = .{
|
||||
.segname = makeStaticString("__DWARF"),
|
||||
.vmaddr = vmaddr,
|
||||
.vmsize = needed_size,
|
||||
.fileoff = off,
|
||||
.filesize = needed_size,
|
||||
},
|
||||
},
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
}
|
||||
if (self.debug_str_section_index == null) {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
self.debug_str_section_index = @intCast(u16, dwarf_segment.sections.items.len);
|
||||
assert(self.debug_string_table.items.len == 0);
|
||||
|
||||
try dwarf_segment.addSection(allocator, "__debug_str", .{
|
||||
.addr = dwarf_segment.inner.vmaddr,
|
||||
.size = @intCast(u32, self.debug_string_table.items.len),
|
||||
.offset = @intCast(u32, dwarf_segment.inner.fileoff),
|
||||
.@"align" = 1,
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_str_section_index = try self.allocateSection(
|
||||
"__debug_str",
|
||||
@intCast(u32, self.debug_string_table.items.len),
|
||||
0,
|
||||
);
|
||||
self.debug_string_table_dirty = true;
|
||||
}
|
||||
if (self.debug_info_section_index == null) {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
self.debug_info_section_index = @intCast(u16, dwarf_segment.sections.items.len);
|
||||
|
||||
const file_size_hint = 200;
|
||||
const p_align = 1;
|
||||
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
|
||||
|
||||
log.debug("found __debug_info free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
|
||||
|
||||
try dwarf_segment.addSection(allocator, "__debug_info", .{
|
||||
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
|
||||
.size = file_size_hint,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = p_align,
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_info_section_index = try self.allocateSection("__debug_info", 200, 0);
|
||||
self.debug_info_header_dirty = true;
|
||||
}
|
||||
if (self.debug_abbrev_section_index == null) {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
self.debug_abbrev_section_index = @intCast(u16, dwarf_segment.sections.items.len);
|
||||
|
||||
const file_size_hint = 128;
|
||||
const p_align = 1;
|
||||
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
|
||||
|
||||
log.debug("found __debug_abbrev free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
|
||||
|
||||
try dwarf_segment.addSection(allocator, "__debug_abbrev", .{
|
||||
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
|
||||
.size = file_size_hint,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = p_align,
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_abbrev_section_index = try self.allocateSection("__debug_abbrev", 128, 0);
|
||||
self.debug_abbrev_section_dirty = true;
|
||||
}
|
||||
if (self.debug_aranges_section_index == null) {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
self.debug_aranges_section_index = @intCast(u16, dwarf_segment.sections.items.len);
|
||||
|
||||
const file_size_hint = 160;
|
||||
const p_align = 16;
|
||||
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
|
||||
|
||||
log.debug("found __debug_aranges free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
|
||||
|
||||
try dwarf_segment.addSection(allocator, "__debug_aranges", .{
|
||||
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
|
||||
.size = file_size_hint,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = p_align,
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_aranges_section_index = try self.allocateSection("__debug_aranges", 160, 4);
|
||||
self.debug_aranges_section_dirty = true;
|
||||
}
|
||||
if (self.debug_line_section_index == null) {
|
||||
const dwarf_segment = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
self.debug_line_section_index = @intCast(u16, dwarf_segment.sections.items.len);
|
||||
|
||||
const file_size_hint = 250;
|
||||
const p_align = 1;
|
||||
const off = dwarf_segment.findFreeSpace(file_size_hint, p_align, null);
|
||||
|
||||
log.debug("found __debug_line free space 0x{x} to 0x{x}", .{ off, off + file_size_hint });
|
||||
|
||||
try dwarf_segment.addSection(allocator, "__debug_line", .{
|
||||
.addr = dwarf_segment.inner.vmaddr + off - dwarf_segment.inner.fileoff,
|
||||
.size = file_size_hint,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = p_align,
|
||||
});
|
||||
self.load_commands_dirty = true;
|
||||
self.debug_line_section_index = try self.allocateSection("__debug_line", 250, 0);
|
||||
self.debug_line_header_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn allocateSection(self: *DebugSymbols, sectname: []const u8, size: u64, alignment: u16) !u16 {
|
||||
const seg = &self.load_commands.items[self.dwarf_segment_cmd_index.?].Segment;
|
||||
var sect = macho.section_64{
|
||||
.sectname = makeStaticString(sectname),
|
||||
.segname = seg.inner.segname,
|
||||
.size = @intCast(u32, size),
|
||||
.@"align" = alignment,
|
||||
};
|
||||
const alignment_pow_2 = try math.powi(u32, 2, alignment);
|
||||
const off = seg.findFreeSpace(size, alignment_pow_2, null);
|
||||
|
||||
assert(off + size <= seg.inner.fileoff + seg.inner.filesize); // TODO expand
|
||||
|
||||
log.debug("found {s},{s} section free space 0x{x} to 0x{x}", .{
|
||||
commands.segmentName(sect),
|
||||
commands.sectionName(sect),
|
||||
off,
|
||||
off + size,
|
||||
});
|
||||
|
||||
sect.addr = seg.inner.vmaddr + off - seg.inner.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;
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
pub fn flushModule(self: *DebugSymbols, allocator: *Allocator, options: link.Options) !void {
|
||||
// TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the
|
||||
// Zig source code.
|
||||
@@ -611,15 +593,18 @@ pub fn deinit(self: *DebugSymbols, allocator: *Allocator) void {
|
||||
}
|
||||
|
||||
fn copySegmentCommand(self: *DebugSymbols, allocator: *Allocator, base_cmd: SegmentCommand) !SegmentCommand {
|
||||
var cmd = SegmentCommand.empty("", .{
|
||||
.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,
|
||||
});
|
||||
var cmd = 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.ensureCapacity(allocator, cmd.inner.nsects);
|
||||
@@ -689,7 +674,7 @@ fn writeLoadCommands(self: *DebugSymbols, allocator: *Allocator) !void {
|
||||
}
|
||||
|
||||
fn writeHeader(self: *DebugSymbols) !void {
|
||||
var header = emptyHeader(.{
|
||||
var header = commands.emptyHeader(.{
|
||||
.filetype = macho.MH_DSYM,
|
||||
});
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const assert = std.debug.assert;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const makeStaticString = MachO.makeStaticString;
|
||||
const padToIdeal = MachO.padToIdeal;
|
||||
|
||||
pub const HeaderArgs = struct {
|
||||
@@ -217,75 +218,6 @@ pub const SegmentCommand = struct {
|
||||
inner: macho.segment_command_64,
|
||||
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
|
||||
|
||||
const SegmentOptions = struct {
|
||||
cmdsize: u32 = @sizeOf(macho.segment_command_64),
|
||||
vmaddr: u64 = 0,
|
||||
vmsize: u64 = 0,
|
||||
fileoff: u64 = 0,
|
||||
filesize: u64 = 0,
|
||||
maxprot: macho.vm_prot_t = macho.VM_PROT_NONE,
|
||||
initprot: macho.vm_prot_t = macho.VM_PROT_NONE,
|
||||
nsects: u32 = 0,
|
||||
flags: u32 = 0,
|
||||
};
|
||||
|
||||
pub fn empty(comptime segname: []const u8, opts: SegmentOptions) SegmentCommand {
|
||||
return .{
|
||||
.inner = .{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.cmdsize = opts.cmdsize,
|
||||
.segname = makeStaticString(segname),
|
||||
.vmaddr = opts.vmaddr,
|
||||
.vmsize = opts.vmsize,
|
||||
.fileoff = opts.fileoff,
|
||||
.filesize = opts.filesize,
|
||||
.maxprot = opts.maxprot,
|
||||
.initprot = opts.initprot,
|
||||
.nsects = opts.nsects,
|
||||
.flags = opts.flags,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const SectionOptions = struct {
|
||||
addr: u64 = 0,
|
||||
size: u64 = 0,
|
||||
offset: u32 = 0,
|
||||
@"align": u32 = 0,
|
||||
reloff: u32 = 0,
|
||||
nreloc: u32 = 0,
|
||||
flags: u32 = macho.S_REGULAR,
|
||||
reserved1: u32 = 0,
|
||||
reserved2: u32 = 0,
|
||||
reserved3: u32 = 0,
|
||||
};
|
||||
|
||||
pub fn addSection(
|
||||
self: *SegmentCommand,
|
||||
alloc: *Allocator,
|
||||
comptime sectname: []const u8,
|
||||
opts: SectionOptions,
|
||||
) !void {
|
||||
var section = macho.section_64{
|
||||
.sectname = makeStaticString(sectname),
|
||||
.segname = undefined,
|
||||
.addr = opts.addr,
|
||||
.size = opts.size,
|
||||
.offset = opts.offset,
|
||||
.@"align" = opts.@"align",
|
||||
.reloff = opts.reloff,
|
||||
.nreloc = opts.nreloc,
|
||||
.flags = opts.flags,
|
||||
.reserved1 = opts.reserved1,
|
||||
.reserved2 = opts.reserved2,
|
||||
.reserved3 = opts.reserved3,
|
||||
};
|
||||
mem.copy(u8, §ion.segname, &self.inner.segname);
|
||||
try self.sections.append(alloc, section);
|
||||
self.inner.cmdsize += @sizeOf(macho.section_64);
|
||||
self.inner.nsects += 1;
|
||||
}
|
||||
|
||||
pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
|
||||
const inner = try reader.readStruct(macho.segment_command_64);
|
||||
var segment = SegmentCommand{
|
||||
@@ -427,13 +359,6 @@ pub fn createLoadDylibCommand(
|
||||
return dylib_cmd;
|
||||
}
|
||||
|
||||
fn makeStaticString(bytes: []const u8) [16]u8 {
|
||||
var buf = [_]u8{0} ** 16;
|
||||
assert(bytes.len <= buf.len);
|
||||
mem.copy(u8, &buf, bytes);
|
||||
return buf;
|
||||
}
|
||||
|
||||
fn parseName(name: *const [16]u8) []const u8 {
|
||||
const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
|
||||
return name[0..len];
|
||||
@@ -513,34 +438,26 @@ test "read-write segment command" {
|
||||
0x00, 0x00, 0x00, 0x00, // reserved3
|
||||
};
|
||||
var cmd = SegmentCommand{
|
||||
.inner = .{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.inner = macho.segment_command_64.new(.{
|
||||
.cmdsize = 152,
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.vmaddr = 4294967296,
|
||||
.vmsize = 294912,
|
||||
.fileoff = 0,
|
||||
.filesize = 294912,
|
||||
.maxprot = macho.VM_PROT_READ | macho.VM_PROT_WRITE | macho.VM_PROT_EXECUTE,
|
||||
.initprot = macho.VM_PROT_EXECUTE | macho.VM_PROT_READ,
|
||||
.nsects = 1,
|
||||
.flags = 0,
|
||||
},
|
||||
}),
|
||||
};
|
||||
try cmd.sections.append(gpa, .{
|
||||
try cmd.sections.append(gpa, macho.section_64.new(.{
|
||||
.sectname = makeStaticString("__text"),
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.addr = 4294983680,
|
||||
.size = 448,
|
||||
.offset = 16384,
|
||||
.@"align" = 2,
|
||||
.reloff = 0,
|
||||
.nreloc = 0,
|
||||
.flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS,
|
||||
.reserved1 = 0,
|
||||
.reserved2 = 0,
|
||||
.reserved3 = 0,
|
||||
});
|
||||
}));
|
||||
defer cmd.deinit(gpa);
|
||||
try testRead(gpa, in_buffer, LoadCommand{ .Segment = cmd });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user