authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-18 17:22:12+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-12-19 12:19:00+01:00
log99578e828b84ba7b308e7bbfac3e99650379edc0
tree0e61bcf5e69bd18ecb1bcc8e5aa4925b3ce35753
parent6a021d845a78241415c1f8537cb8ae63a8367b89

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.

2 files changed, 51 insertions(+), 58 deletions(-)

src/link/MachO.zig+47-29
......@@ -152,8 +152,8 @@ pub const PieFixup = struct {
152152};
153153
154154/// `alloc_num / alloc_den` is the factor of padding when allocating.
155pub const alloc_num = 4;
156pub const alloc_den = 3;
155const alloc_num = 4;
156const alloc_den = 3;
157157
158158/// Default path to dyld
159159/// 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 {
13051305
13061306 log.debug("found __TEXT segment free space 0x{x} to 0x{x}\n", .{ 0, needed_size });
13071307
1308 var segment = SegmentCommand.empty(.{
1309 .cmd = macho.LC_SEGMENT_64,
1310 .cmdsize = @sizeOf(macho.segment_command_64),
1311 .segname = makeStaticString("__TEXT"),
1312 .vmaddr = 0x100000000, // always starts at 4GB
1313 .vmsize = needed_size,
1314 .fileoff = 0,
1315 .filesize = needed_size,
1316 .maxprot = maxprot,
1317 .initprot = initprot,
1318 .nsects = 0,
1319 .flags = 0,
1308 try self.load_commands.append(self.base.allocator, .{
1309 .Segment = SegmentCommand.empty(.{
1310 .cmd = macho.LC_SEGMENT_64,
1311 .cmdsize = @sizeOf(macho.segment_command_64),
1312 .segname = makeStaticString("__TEXT"),
1313 .vmaddr = 0x100000000, // always starts at 4GB
1314 .vmsize = needed_size,
1315 .fileoff = 0,
1316 .filesize = needed_size,
1317 .maxprot = maxprot,
1318 .initprot = initprot,
1319 .nsects = 0,
1320 .flags = 0,
1321 }),
13201322 });
1321 segment.header_pad = self.header_pad;
1322 try self.load_commands.append(self.base.allocator, .{ .Segment = segment });
13231323 self.cmd_table_dirty = true;
13241324 }
13251325 if (self.text_section_index == null) {
......@@ -1333,7 +1333,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13331333 };
13341334 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
13351335 const needed_size = self.base.options.program_code_size_hint;
1336 const off = text_segment.findFreeSpace(needed_size, @as(u16, 1) << alignment);
1336 const off = self.findFreeSpace(text_segment, needed_size, @as(u16, 1) << alignment);
13371337
13381338 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
13391339
......@@ -1342,7 +1342,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13421342 .segname = makeStaticString("__TEXT"),
13431343 .addr = text_segment.inner.vmaddr + off,
13441344 .size = @intCast(u32, needed_size),
1345 .offset = off,
1345 .offset = @intCast(u32, off),
13461346 .@"align" = alignment,
13471347 .reloff = 0,
13481348 .nreloc = 0,
......@@ -1360,10 +1360,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13601360
13611361 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
13621362 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1363 const off = text_segment.findFreeSpace(needed_size, @sizeOf(u64));
1364
1365 // TODO Audit this. Is it possible to not find free space? We need to grow __TEXT then.
1366 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize);
1363 const off = self.findFreeSpace(text_segment, needed_size, @sizeOf(u64));
1364 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
13671365
13681366 log.debug("found __ziggot section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });
13691367
......@@ -1372,7 +1370,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13721370 .segname = makeStaticString("__TEXT"),
13731371 .addr = text_segment.inner.vmaddr + off,
13741372 .size = needed_size,
1375 .offset = off,
1373 .offset = @intCast(u32, off),
13761374 .@"align" = @sizeOf(u64),
13771375 .reloff = 0,
13781376 .nreloc = 0,
......@@ -1725,6 +1723,32 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
17251723 };
17261724}
17271725
1726fn detectAllocCollision(self: *MachO, segment: *const SegmentCommand, start: u64, size: u64) ?u64 {
1727 const end = start + satMul(size, alloc_num) / alloc_den;
1728 for (segment.sections.items) |section| {
1729 const increased_size = satMul(section.size, alloc_num) / alloc_den;
1730 const test_end = section.offset + increased_size;
1731 if (end > section.offset and start < test_end) {
1732 return test_end;
1733 }
1734 }
1735 return null;
1736}
1737
1738fn findFreeSpace(self: *MachO, segment: *const SegmentCommand, object_size: u64, min_alignment: u16) u64 {
1739 var start: u64 = if (parseAndCmpName(&segment.inner.segname, "__TEXT")) self.header_pad else 0;
1740 while (self.detectAllocCollision(segment, start, object_size)) |item_end| {
1741 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
1742 }
1743 return start;
1744}
1745
1746/// Saturating multiplication
1747fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
1748 const T = @TypeOf(a, b);
1749 return std.math.mul(T, a, b) catch std.math.maxInt(T);
1750}
1751
17281752fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
17291753 const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
17301754 const sect = &text_semgent.sections.items[self.got_section_index.?];
......@@ -2140,9 +2164,3 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
21402164 var stream = std.io.fixedBufferStream(buffer);
21412165 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
21422166}
2143
2144/// Saturating multiplication
2145pub fn satMul(a: anytype, b: anytype) @TypeOf(a, b) {
2146 const T = @TypeOf(a, b);
2147 return std.math.mul(T, a, b) catch std.math.maxInt(T);
2148}
src/link/MachO/commands.zig+4-29
......@@ -7,11 +7,7 @@ const macho = std.macho;
77const testing = std.testing;
88
99const Allocator = std.mem.Allocator;
10const MachO = @import("../MachO.zig");
11const makeName = MachO.makeStaticString;
12const alloc_num = MachO.alloc_num;
13const alloc_den = MachO.alloc_den;
14const satMul = MachO.satMul;
10const makeStaticString = @import("../MachO.zig").makeStaticString;
1511
1612pub const LoadCommand = union(enum) {
1713 Segment: SegmentCommand,
......@@ -153,7 +149,6 @@ pub const LoadCommand = union(enum) {
153149
154150pub const SegmentCommand = struct {
155151 inner: macho.segment_command_64,
156 header_pad: ?u64 = null,
157152 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
158153
159154 pub fn empty(inner: macho.segment_command_64) SegmentCommand {
......@@ -166,26 +161,6 @@ pub const SegmentCommand = struct {
166161 self.inner.nsects += 1;
167162 }
168163
169 fn detectAllocCollision(self: SegmentCommand, start: u64, size: u64) ?u64 {
170 const end = start + satMul(size, alloc_num) / alloc_den;
171 for (self.sections.items) |section| {
172 const increased_size = satMul(section.size, alloc_num) / alloc_den;
173 const test_end = section.offset + increased_size;
174 if (end > section.offset and start < test_end) {
175 return test_end;
176 }
177 }
178 return null;
179 }
180
181 pub fn findFreeSpace(self: SegmentCommand, object_size: u64, min_alignment: u16) u32 {
182 var start: u64 = if (self.header_pad) |pad| pad else 0;
183 while (self.detectAllocCollision(start, object_size)) |item_end| {
184 start = mem.alignForwardGeneric(u64, item_end, min_alignment);
185 }
186 return @intCast(u32, start);
187 }
188
189164 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
190165 const inner = try reader.readStruct(macho.segment_command_64);
191166 var segment = SegmentCommand{
......@@ -308,7 +283,7 @@ test "read-write segment command" {
308283 .inner = .{
309284 .cmd = macho.LC_SEGMENT_64,
310285 .cmdsize = 152,
311 .segname = makeName("__TEXT"),
286 .segname = makeStaticString("__TEXT"),
312287 .vmaddr = 4294967296,
313288 .vmsize = 294912,
314289 .fileoff = 0,
......@@ -320,8 +295,8 @@ test "read-write segment command" {
320295 },
321296 };
322297 try cmd.sections.append(gpa, .{
323 .sectname = makeName("__text"),
324 .segname = makeName("__TEXT"),
298 .sectname = makeStaticString("__text"),
299 .segname = makeStaticString("__TEXT"),
325300 .addr = 4294983680,
326301 .size = 448,
327302 .offset = 16384,