| ... | @@ -6,6 +6,8 @@ const assert = std.debug.assert; | ... | @@ -6,6 +6,8 @@ const assert = std.debug.assert; |
| 6 | const fs = std.fs; | 6 | const fs = std.fs; |
| 7 | const log = std.log.scoped(.link); | 7 | const log = std.log.scoped(.link); |
| 8 | const macho = std.macho; | 8 | const macho = std.macho; |
| | 9 | const math = std.math; |
| | 10 | const mem = std.mem; |
| 9 | | 11 | |
| 10 | const Module = @import("../Module.zig"); | 12 | const Module = @import("../Module.zig"); |
| 11 | const link = @import("../link.zig"); | 13 | const link = @import("../link.zig"); |
| ... | @@ -15,6 +17,14 @@ pub const base_tag: Tag = File.Tag.macho; | ... | @@ -15,6 +17,14 @@ pub const base_tag: Tag = File.Tag.macho; |
| 15 | | 17 | |
| 16 | base: File, | 18 | base: File, |
| 17 | | 19 | |
| | 20 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| | 21 | /// Same order as in the file. |
| | 22 | segment_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. |
| | 26 | sections: std.ArrayListUnmanaged(macho.section_64) = std.ArrayListUnmanaged(macho.section_64){}, |
| | 27 | |
| 18 | entry_addr: ?u64 = null, | 28 | entry_addr: ?u64 = null, |
| 19 | | 29 | |
| 20 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 30 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| ... | @@ -86,9 +96,33 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach | ... | @@ -86,9 +96,33 @@ fn createFile(allocator: *Allocator, file: fs.File, options: link.Options) !Mach |
| 86 | }; | 96 | }; |
| 87 | errdefer self.deinit(); | 97 | errdefer self.deinit(); |
| 88 | | 98 | |
| | 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 | |
| 89 | return self; | 116 | return self; |
| 90 | } | 117 | } |
| 91 | | 118 | |
| | 119 | fn makeString(self: *MachO, comptime bytes: []const u8) [16]u8 { |
| | 120 | var buf: [16]u8 = undefined; |
| | 121 | if (bytes.len > buf.len) @compileError("MachO segment/section name too long"); |
| | 122 | mem.copy(u8, buf[0..], bytes); |
| | 123 | return buf; |
| | 124 | } |
| | 125 | |
| 92 | fn writeMachOHeader(self: *MachO) !void { | 126 | fn writeMachOHeader(self: *MachO) !void { |
| 93 | var hdr: macho.mach_header_64 = undefined; | 127 | var hdr: macho.mach_header_64 = undefined; |
| 94 | hdr.magic = macho.MH_MAGIC_64; | 128 | hdr.magic = macho.MH_MAGIC_64; |
| ... | @@ -122,9 +156,12 @@ fn writeMachOHeader(self: *MachO) !void { | ... | @@ -122,9 +156,12 @@ fn writeMachOHeader(self: *MachO) !void { |
| 122 | }; | 156 | }; |
| 123 | hdr.filetype = filetype; | 157 | hdr.filetype = filetype; |
| 124 | | 158 | |
| 125 | // TODO the rest of the header | 159 | // TODO consider other commands |
| 126 | hdr.ncmds = 0; | 160 | const ncmds = try math.cast(u32, self.segment_cmds.items.len); |
| 127 | hdr.sizeofcmds = 0; | 161 | hdr.ncmds = ncmds; |
| | 162 | hdr.sizeofcmds = ncmds * @sizeOf(macho.segment_command_64); |
| | 163 | |
| | 164 | // TODO should these be set to something else? |
| 128 | hdr.flags = 0; | 165 | hdr.flags = 0; |
| 129 | hdr.reserved = 0; | 166 | hdr.reserved = 0; |
| 130 | | 167 | |
| ... | @@ -133,6 +170,17 @@ fn writeMachOHeader(self: *MachO) !void { | ... | @@ -133,6 +170,17 @@ fn writeMachOHeader(self: *MachO) !void { |
| 133 | | 170 | |
| 134 | pub fn flush(self: *MachO, module: *Module) !void { | 171 | pub fn flush(self: *MachO, module: *Module) !void { |
| 135 | // TODO implement flush | 172 | // TODO implement flush |
| | 173 | { |
| | 174 | const buf = try self.base.allocator.alloc(macho.segment_command_64, self.segment_cmds.items.len); |
| | 175 | defer self.base.allocator.free(buf); |
| | 176 | |
| | 177 | for (buf) |*seg, i| { |
| | 178 | seg.* = self.segment_cmds.items[i]; |
| | 179 | } |
| | 180 | |
| | 181 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), @sizeOf(macho.mach_header_64)); |
| | 182 | } |
| | 183 | |
| 136 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | 184 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { |
| 137 | log.debug("flushing. no_entry_point_found = true\n", .{}); | 185 | log.debug("flushing. no_entry_point_found = true\n", .{}); |
| 138 | self.error_flags.no_entry_point_found = true; | 186 | self.error_flags.no_entry_point_found = true; |
| ... | @@ -143,7 +191,10 @@ pub fn flush(self: *MachO, module: *Module) !void { | ... | @@ -143,7 +191,10 @@ pub fn flush(self: *MachO, module: *Module) !void { |
| 143 | } | 191 | } |
| 144 | } | 192 | } |
| 145 | | 193 | |
| 146 | pub fn deinit(self: *MachO) void {} | 194 | pub fn deinit(self: *MachO) void { |
| | 195 | self.segment_cmds.deinit(self.base.allocator); |
| | 196 | self.sections.deinit(self.base.allocator); |
| | 197 | } |
| 147 | | 198 | |
| 148 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {} | 199 | pub fn allocateDeclIndexes(self: *MachO, decl: *Module.Decl) !void {} |
| 149 | | 200 | |