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 {...@@ -152,8 +152,8 @@ pub const PieFixup = struct {
152};152};
153153
154/// `alloc_num / alloc_den` is the factor of padding when allocating.154/// `alloc_num / alloc_den` is the factor of padding when allocating.
155pub const alloc_num = 4;155const alloc_num = 4;
156pub const alloc_den = 3;156const alloc_den = 3;
157157
158/// Default path to dyld158/// Default path to dyld
159/// TODO instead of hardcoding it, we should probably look through some env vars and search paths159/// 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 {...@@ -1305,21 +1305,21 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13051305
1306 log.debug("found __TEXT segment free space 0x{x} to 0x{x}\n", .{ 0, needed_size });1306 log.debug("found __TEXT segment free space 0x{x} to 0x{x}\n", .{ 0, needed_size });
13071307
1308 var segment = SegmentCommand.empty(.{1308 try self.load_commands.append(self.base.allocator, .{
1309 .cmd = macho.LC_SEGMENT_64,1309 .Segment = SegmentCommand.empty(.{
1310 .cmdsize = @sizeOf(macho.segment_command_64),1310 .cmd = macho.LC_SEGMENT_64,
1311 .segname = makeStaticString("__TEXT"),1311 .cmdsize = @sizeOf(macho.segment_command_64),
1312 .vmaddr = 0x100000000, // always starts at 4GB1312 .segname = makeStaticString("__TEXT"),
1313 .vmsize = needed_size,1313 .vmaddr = 0x100000000, // always starts at 4GB
1314 .fileoff = 0,1314 .vmsize = needed_size,
1315 .filesize = needed_size,1315 .fileoff = 0,
1316 .maxprot = maxprot,1316 .filesize = needed_size,
1317 .initprot = initprot,1317 .maxprot = maxprot,
1318 .nsects = 0,1318 .initprot = initprot,
1319 .flags = 0,1319 .nsects = 0,
1320 .flags = 0,
1321 }),
1320 });1322 });
1321 segment.header_pad = self.header_pad;
1322 try self.load_commands.append(self.base.allocator, .{ .Segment = segment });
1323 self.cmd_table_dirty = true;1323 self.cmd_table_dirty = true;
1324 }1324 }
1325 if (self.text_section_index == null) {1325 if (self.text_section_index == null) {
...@@ -1333,7 +1333,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1333,7 +1333,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1333 };1333 };
1334 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1334 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1335 const needed_size = self.base.options.program_code_size_hint;1335 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
1338 log.debug("found __text section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });1338 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 {...@@ -1342,7 +1342,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1342 .segname = makeStaticString("__TEXT"),1342 .segname = makeStaticString("__TEXT"),
1343 .addr = text_segment.inner.vmaddr + off,1343 .addr = text_segment.inner.vmaddr + off,
1344 .size = @intCast(u32, needed_size),1344 .size = @intCast(u32, needed_size),
1345 .offset = off,1345 .offset = @intCast(u32, off),
1346 .@"align" = alignment,1346 .@"align" = alignment,
1347 .reloff = 0,1347 .reloff = 0,
1348 .nreloc = 0,1348 .nreloc = 0,
...@@ -1360,10 +1360,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -1360,10 +1360,8 @@ pub fn populateMissingMetadata(self: *MachO) !void {
13601360
1361 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;1361 const flags = macho.S_REGULAR | macho.S_ATTR_PURE_INSTRUCTIONS | macho.S_ATTR_SOME_INSTRUCTIONS;
1362 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;1362 const needed_size = @sizeOf(u64) * self.base.options.symbol_count_hint;
1363 const off = text_segment.findFreeSpace(needed_size, @sizeOf(u64));1363 const off = self.findFreeSpace(text_segment, needed_size, @sizeOf(u64));
13641364 assert(off + needed_size <= text_segment.inner.fileoff + text_segment.inner.filesize); // TODO Must expand __TEXT segment.
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);
13671365
1368 log.debug("found __ziggot section free space 0x{x} to 0x{x}\n", .{ off, off + needed_size });1366 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 {...@@ -1372,7 +1370,7 @@ pub fn populateMissingMetadata(self: *MachO) !void {
1372 .segname = makeStaticString("__TEXT"),1370 .segname = makeStaticString("__TEXT"),
1373 .addr = text_segment.inner.vmaddr + off,1371 .addr = text_segment.inner.vmaddr + off,
1374 .size = needed_size,1372 .size = needed_size,
1375 .offset = off,1373 .offset = @intCast(u32, off),
1376 .@"align" = @sizeOf(u64),1374 .@"align" = @sizeOf(u64),
1377 .reloff = 0,1375 .reloff = 0,
1378 .nreloc = 0,1376 .nreloc = 0,
...@@ -1725,6 +1723,32 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {...@@ -1725,6 +1723,32 @@ fn nextSegmentAddressAndOffset(self: *MachO) NextSegmentAddressAndOffset {
1725 };1723 };
1726}1724}
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
1728fn writeOffsetTableEntry(self: *MachO, index: usize) !void {1752fn writeOffsetTableEntry(self: *MachO, index: usize) !void {
1729 const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;1753 const text_semgent = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
1730 const sect = &text_semgent.sections.items[self.got_section_index.?];1754 const sect = &text_semgent.sections.items[self.got_section_index.?];
...@@ -2140,9 +2164,3 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {...@@ -2140,9 +2164,3 @@ fn parseLazyBindingInfoTable(self: *MachO) !void {
2140 var stream = std.io.fixedBufferStream(buffer);2164 var stream = std.io.fixedBufferStream(buffer);
2141 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);2165 try self.lazy_binding_info_table.read(stream.reader(), self.base.allocator);
2142}2166}
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;...@@ -7,11 +7,7 @@ const macho = std.macho;
7const testing = std.testing;7const testing = std.testing;
88
9const Allocator = std.mem.Allocator;9const Allocator = std.mem.Allocator;
10const MachO = @import("../MachO.zig");10const makeStaticString = @import("../MachO.zig").makeStaticString;
11const makeName = MachO.makeStaticString;
12const alloc_num = MachO.alloc_num;
13const alloc_den = MachO.alloc_den;
14const satMul = MachO.satMul;
1511
16pub const LoadCommand = union(enum) {12pub const LoadCommand = union(enum) {
17 Segment: SegmentCommand,13 Segment: SegmentCommand,
...@@ -153,7 +149,6 @@ pub const LoadCommand = union(enum) {...@@ -153,7 +149,6 @@ pub const LoadCommand = union(enum) {
153149
154pub const SegmentCommand = struct {150pub const SegmentCommand = struct {
155 inner: macho.segment_command_64,151 inner: macho.segment_command_64,
156 header_pad: ?u64 = null,
157 sections: std.ArrayListUnmanaged(macho.section_64) = .{},152 sections: std.ArrayListUnmanaged(macho.section_64) = .{},
158153
159 pub fn empty(inner: macho.segment_command_64) SegmentCommand {154 pub fn empty(inner: macho.segment_command_64) SegmentCommand {
...@@ -166,26 +161,6 @@ pub const SegmentCommand = struct {...@@ -166,26 +161,6 @@ pub const SegmentCommand = struct {
166 self.inner.nsects += 1;161 self.inner.nsects += 1;
167 }162 }
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
189 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {164 pub fn read(alloc: *Allocator, reader: anytype) !SegmentCommand {
190 const inner = try reader.readStruct(macho.segment_command_64);165 const inner = try reader.readStruct(macho.segment_command_64);
191 var segment = SegmentCommand{166 var segment = SegmentCommand{
...@@ -308,7 +283,7 @@ test "read-write segment command" {...@@ -308,7 +283,7 @@ test "read-write segment command" {
308 .inner = .{283 .inner = .{
309 .cmd = macho.LC_SEGMENT_64,284 .cmd = macho.LC_SEGMENT_64,
310 .cmdsize = 152,285 .cmdsize = 152,
311 .segname = makeName("__TEXT"),286 .segname = makeStaticString("__TEXT"),
312 .vmaddr = 4294967296,287 .vmaddr = 4294967296,
313 .vmsize = 294912,288 .vmsize = 294912,
314 .fileoff = 0,289 .fileoff = 0,
...@@ -320,8 +295,8 @@ test "read-write segment command" {...@@ -320,8 +295,8 @@ test "read-write segment command" {
320 },295 },
321 };296 };
322 try cmd.sections.append(gpa, .{297 try cmd.sections.append(gpa, .{
323 .sectname = makeName("__text"),298 .sectname = makeStaticString("__text"),
324 .segname = makeName("__TEXT"),299 .segname = makeStaticString("__TEXT"),
325 .addr = 4294983680,300 .addr = 4294983680,
326 .size = 448,301 .size = 448,
327 .offset = 16384,302 .offset = 16384,