authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-20 08:30:54+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-08-20 08:33:40+02:00
log9164daaa39a0ebffcfff27d6d8c91f5857bdfe04
tree60193704528970ba9ed3eac3eca5c7525f523a42
parent91de5c212fe847a309f047e3097491bba6f86ae1

Write page zero as first segment for Mach-O exes

According to the Mach-O file format reference, the first load command should be a `__PAGEZERO` segment command. The segment is located at virtual memory location 0, has no protection rights, and causes acccesses to NULL to immediately crash. Signed-off-by: Jakub Konka <kubkon@jakubkonka.com>

1 files changed, 56 insertions(+), 4 deletions(-)

src-self-hosted/link/MachO.zig+56-4
......@@ -6,6 +6,8 @@ const assert = std.debug.assert;
66const fs = std.fs;
77const log = std.log.scoped(.link);
88const macho = std.macho;
9const math = std.math;
10const mem = std.mem;
911
1012const Module = @import("../Module.zig");
1113const link = @import("../link.zig");
......@@ -15,6 +17,14 @@ pub const base_tag: Tag = File.Tag.macho;
1517
1618base: File,
1719
20/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
21/// Same order as in the file.
22segment_cmds: std.ArrayListUnmanaged(macho.segment_command_64) = std.ArrayListUnmanaged(macho.segment_command_64){},
23
24/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
25/// Same order as in the file.
26sections: std.ArrayListUnmanaged(macho.@"section_64") = std.ArrayListUnmanaged(macho.@"section_64"){},
27
1828entry_addr: ?u64 = null,
1929
2030error_flags: File.ErrorFlags = File.ErrorFlags{},
......@@ -86,9 +96,34 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach
8696 };
8797 errdefer self.deinit();
8898
99 if (options.output_mode == .Exe) {
100 // The first segment command for executables is always a __PAGEZERO segment.
101 try self.segment_cmds.append(allocator, .{
102 .cmd = macho.LC_SEGMENT_64,
103 .cmdsize = @sizeOf(macho.segment_command_64),
104 .segname = self.makeString("__PAGEZERO"),
105 .vmaddr = 0,
106 .vmsize = 0,
107 .fileoff = 0,
108 .filesize = 0,
109 .maxprot = 0,
110 .initprot = 0,
111 .nsects = 0,
112 .flags = 0,
113 });
114 }
115
89116 return self;
90117}
91118
119fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 {
120 if (bytes.len > 16) @compileError("MachO segment/section name too long");
121
122 var buf: [16]u8 = undefined;
123 mem.copy(u8, buf[0..], bytes);
124 return buf;
125}
126
92127fn writeMachOHeader(self: *MachO) !void {
93128 var hdr: macho.mach_header_64 = undefined;
94129 hdr.magic = macho.MH_MAGIC_64;
......@@ -122,9 +157,12 @@ fn writeMachOHeader(self: *MachO) !void {
122157 };
123158 hdr.filetype = filetype;
124159
125 // TODO the rest of the header
126 hdr.ncmds = 0;
127 hdr.sizeofcmds = 0;
160 // TODO consider other commands
161 const ncmds = try math.cast(u32, self.segment_cmds.items.len);
162 hdr.ncmds = ncmds;
163 hdr.sizeofcmds = ncmds * @sizeOf(macho.segment_command_64);
164
165 // TODO should these be set to something else?
128166 hdr.flags = 0;
129167 hdr.reserved = 0;
130168
......@@ -133,6 +171,17 @@ fn writeMachOHeader(self: *MachO) !void {
133171
134172pub fn flush(self: *MachO, module: *Module) !void {
135173 // TODO implement flush
174 {
175 const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segment_cmds.items.len);
176 defer self.base.allocator.free(buf);
177
178 for (buf) |*seg, i| {
179 seg.* = self.segment_cmds.items[i];
180 }
181
182 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64));
183 }
184
136185 if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
137186 log.debug("flushing. no_entry_point_found = true\n", .{});
138187 self.error_flags.no_entry_point_found = true;
......@@ -143,7 +192,10 @@ pub fn flush(self: *MachO, module: *Module) !void {
143192 }
144193}
145194
146pub fn deinit(self: *MachO) void {}
195pub fn deinit(self: *MachO) void {
196 self.segment_cmds.deinit(self.base.allocator);
197 self.@"sections".deinit(self.base.allocator);
198}
147199
148200pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {}
149201