macho: move findFreeSpace back to MachO struct
However, adding a twist where `findFreeSpace` accepts a `SegmentCommand` as argument meaning we want to look for free space specifically within that segment and nowhere else.
This commit is contained in:
@@ -152,8 +152,8 @@ pub const PieFixup = struct {
|
||||
};
|
||||
|
||||
/// `alloc_num / alloc_den` is the factor of padding when allocating.
|
||||
pub const alloc_num = 4;
|
||||
pub const alloc_den = 3;
|
||||
const alloc_num = 4;
|
||||
const alloc_den = 3;
|
||||
|
||||
/// Default path to dyld
|
||||
/// TODO instead of hardcoding it, we should probably look through some env vars and search paths
|
||||
@@ -1305,21 +1305,21 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
|
||||
log.debug("found __TEXT segment free space 0x{x} to 0x{x}\n", .{ 0, needed_size });
|
||||
|
||||
var segment = SegmentCommand.empty(.{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.cmdsize = @sizeOf(macho.segment_command_64),
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.vmaddr = 0x100000000, // always starts at 4GB
|
||||
.vmsize = needed_size,
|
||||
.fileoff = 0,
|
||||
.filesize = needed_size,
|
||||
.maxprot = maxprot,
|
||||
.initprot = initprot,
|
||||
.nsects = 0,
|
||||
.flags = 0,
|
||||
try self.load_commands.append(self.base.allocator, .{
|
||||
.Segment = SegmentCommand.empty(.{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.cmdsize = @sizeOf(macho.segment_command_64),
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.vmaddr = 0x100000000, // always starts at 4GB
|
||||
.vmsize = needed_size,
|
||||
.fileoff = 0,
|
||||
.filesize = needed_size,
|
||||
.maxprot = maxprot,
|
||||
.initprot = initprot,
|
||||
.nsects = 0,
|
||||
.flags = 0,
|
||||
}),
|
||||
});
|
||||
segment.header_pad = self.header_pad;
|
||||
try self.load_commands.append(self.base.allocator, .{ .Segment = segment });
|
||||
self.cmd_table_dirty = true;
|
||||
}
|
||||
if (self.text_section_index == null) {
|
||||
@@ -1333,7 +1333,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
};
|
||||
const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
|
||||
const needed_size = self.base.options.program_code_size_hint;
|
||||
const off = text_segment.findFreeSpace(needed_size, @as(u16, 1) << alignment);
|
||||
const off = self.findFreeSpace(text_segment, needed_size, @as(u16, 1) << alignment);
|
||||
|
||||
log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
|
||||
|
||||
@@ -1342,7 +1342,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.addr = text_segment.inner.vmaddr + off,
|
||||
.size = @intCast(u32, needed_size),
|
||||
.offset = off,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = alignment,
|
||||
.reloff = 0,
|
||||
.nreloc = 0,
|
||||
@@ -1360,10 +1360,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
|
||||
const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
|
||||
const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
|
||||
const off = text_segment.findFreeSpace(needed_size, @sizeOf(u64));
|
||||
|
||||
// TODO Audit this. Is it possible to not find free space? We need to grow __TEXT then.
|
||||
assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize);
|
||||
const off = self.findFreeSpace(text_segment, needed_size, @sizeOf(u64));
|
||||
assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
|
||||
|
||||
log.debug("found __ziggot section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
|
||||
|
||||
@@ -1372,7 +1370,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.addr = text_segment.inner.vmaddr + off,
|
||||
.size = needed_size,
|
||||
.offset = off,
|
||||
.offset = @intCast(u32, off),
|
||||
.@"align" = @sizeOf(u64),
|
||||
.reloff = 0,
|
||||
.nreloc = 0,
|
||||
@@ -1725,6 +1723,32 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
|
||||
};
|
||||
}
|
||||
|
||||
fn detectAllocCollision(self: *MachO, segment: *const SegmentCommand, start: u64, size: u64) ?u64 {
|
||||
const end = start + satMul(size, alloc_num) / alloc_den;
|
||||
for (segment.sections.items) |section| {
|
||||
const increased_size = satMul(section.size, alloc_num) / alloc_den;
|
||||
const test_end = section.offset + increased_size;
|
||||
if (end > section.offset and start < test_end) {
|
||||
return test_end;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
fn findFreeSpace(self: *MachO, segment: *const SegmentCommand, object_size: u64, min_alignment: u16) u64 {
|
||||
var start: u64 = if (parseAndCmpName(&segment.inner.segname, "__TEXT")) self.header_pad else 0;
|
||||
while (self.detectAllocCollision(segment, start, object_size)) |item_end| {
|
||||
start = mem.alignForwardGeneric(u64, item_end, min_alignment);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
/// Saturating multiplication
|
||||
fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||
const T = @TypeOf(a, b);
|
||||
return std.math.mul(T, a, b) catch std.math.maxInt(T);
|
||||
}
|
||||
|
||||
fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
|
||||
const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
|
||||
const sect = &text_semgent.sections.items[self.got_section_index.?];
|
||||
@@ -2140,9 +2164,3 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
|
||||
var stream = std.io.fixedBufferStream(buffer);
|
||||
try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
|
||||
}
|
||||
|
||||
/// Saturating multiplication
|
||||
pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
|
||||
const T = @TypeOf(a, b);
|
||||
return std.math.mul(T, a, b) catch std.math.maxInt(T);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,7 @@ const macho = std.macho;
|
||||
const testing = std.testing;
|
||||
|
||||
const Allocator = std.mem.Allocator;
|
||||
const MachO = @import("../MachO.zig");
|
||||
const makeName = MachO.makeStaticString;
|
||||
const alloc_num = MachO.alloc_num;
|
||||
const alloc_den = MachO.alloc_den;
|
||||
const satMul = MachO.satMul;
|
||||
const makeStaticString = @import("../MachO.zig").makeStaticString;
|
||||
|
||||
pub const LoadCommand = union(enum) {
|
||||
Segment: SegmentCommand,
|
||||
@@ -153,7 +149,6 @@ pub const LoadCommand = union(enum) {
|
||||
|
||||
pub const SegmentCommand = struct {
|
||||
inner: macho.segment_command_64,
|
||||
header_pad: ?u64 = null,
|
||||
sections: std.ArrayListUnmanaged(macho.section_64) = .{},
|
||||
|
||||
pub fn empty(inner: macho.segment_command_64) SegmentCommand {
|
||||
@@ -166,26 +161,6 @@ pub const SegmentCommand = struct {
|
||||
self.inner.nsects += 1;
|
||||
}
|
||||
|
||||
fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
|
||||
const end = start + satMul(size, alloc_num) / alloc_den;
|
||||
for (self.sections.items) |section| {
|
||||
const increased_size = satMul(section.size, alloc_num) / alloc_den;
|
||||
const test_end = section.offset + increased_size;
|
||||
if (end > section.offset and start < test_end) {
|
||||
return test_end;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16) u32 {
|
||||
var start: u64 = if (self.header_pad) |pad| pad else 0;
|
||||
while (self.detectAllocCollision(start, object_size)) |item_end| {
|
||||
start = mem.alignForwardGeneric(u64, item_end, min_alignment);
|
||||
}
|
||||
return @intCast(u32, start);
|
||||
}
|
||||
|
||||
pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
|
||||
const inner = try reader.readStruct(macho.segment_command_64);
|
||||
var segment = SegmentCommand{
|
||||
@@ -308,7 +283,7 @@ test "read-write segment command" {
|
||||
.inner = .{
|
||||
.cmd = macho.LC_SEGMENT_64,
|
||||
.cmdsize = 152,
|
||||
.segname = makeName("__TEXT"),
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.vmaddr = 4294967296,
|
||||
.vmsize = 294912,
|
||||
.fileoff = 0,
|
||||
@@ -320,8 +295,8 @@ test "read-write segment command" {
|
||||
},
|
||||
};
|
||||
try cmd.sections.append(gpa, .{
|
||||
.sectname = makeName("__text"),
|
||||
.segname = makeName("__TEXT"),
|
||||
.sectname = makeStaticString("__text"),
|
||||
.segname = makeStaticString("__TEXT"),
|
||||
.addr = 4294983680,
|
||||
.size = 448,
|
||||
.offset = 16384,
|
||||
|
||||
Reference in New Issue
Block a user