| author | |
| committer | |
| log | b0105029caafda4668f788387270ee8fcc1ac175 |
| tree | 58b6f3c101b117533eff5b5ad1f24783a1b76fa1 |
| parent | 988b184d03fea3001b588ceb033de66a7d7ba21b |
3 files changed, 241 insertions(+), 184 deletions(-)
src/link/MachO/Archive.zig+83-114| ... | @@ -9,16 +9,14 @@ const mem = std.mem; | ... | @@ -9,16 +9,14 @@ const mem = std.mem; |
| 9 | 9 | ||
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const Object = @import("Object.zig"); | 11 | const Object = @import("Object.zig"); |
| 12 | const parseName = @import("Zld.zig").parseName; | ||
| 13 | 12 | ||
| 14 | usingnamespace @import("commands.zig"); | 13 | usingnamespace @import("commands.zig"); |
| 15 | 14 | ||
| 16 | allocator: *Allocator, | 15 | allocator: *Allocator, |
| 17 | file: fs.File, | 16 | arch: ?std.Target.Cpu.Arch = null, |
| 18 | header: ar_hdr, | 17 | file: ?fs.File = null, |
| 19 | name: []u8, | 18 | header: ?ar_hdr = null, |
| 20 | 19 | name: ?[]u8 = null, | |
| 21 | objects: std.ArrayListUnmanaged(Object) = .{}, | ||
| 22 | 20 | ||
| 23 | /// Parsed table of contents. | 21 | /// Parsed table of contents. |
| 24 | /// Each symbol name points to a list of all definition | 22 | /// Each symbol name points to a list of all definition |
| ... | @@ -29,14 +27,14 @@ toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{}, | ... | @@ -29,14 +27,14 @@ toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{}, |
| 29 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | 27 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' |
| 30 | // member indicates, for each member file. | 28 | // member indicates, for each member file. |
| 31 | /// String that begins an archive file. | 29 | /// String that begins an archive file. |
| 32 | const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | 30 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; |
| 33 | /// Size of that string. | 31 | /// Size of that string. |
| 34 | const SARMAG: u4 = 8; | 32 | pub const SARMAG: u4 = 8; |
| 35 | 33 | ||
| 36 | /// String in ar_fmag at the end of each header. | 34 | /// String in ar_fmag at the end of each header. |
| 37 | const ARFMAG: *const [2:0]u8 = "`\n"; | 35 | pub const ARFMAG: *const [2:0]u8 = "`\n"; |
| 38 | 36 | ||
| 39 | const ar_hdr = extern struct { | 37 | pub const ar_hdr = extern struct { |
| 40 | /// Member file name, sometimes / terminated. | 38 | /// Member file name, sometimes / terminated. |
| 41 | ar_name: [16]u8, | 39 | ar_name: [16]u8, |
| 42 | 40 | ||
| ... | @@ -87,64 +85,91 @@ const ar_hdr = extern struct { | ... | @@ -87,64 +85,91 @@ const ar_hdr = extern struct { |
| 87 | } | 85 | } |
| 88 | }; | 86 | }; |
| 89 | 87 | ||
| 88 | pub fn init(allocator: *Allocator) Archive { | ||
| 89 | return .{ | ||
| 90 | .allocator = allocator, | ||
| 91 | }; | ||
| 92 | } | ||
| 93 | |||
| 90 | pub fn deinit(self: *Archive) void { | 94 | pub fn deinit(self: *Archive) void { |
| 91 | self.allocator.free(self.name); | ||
| 92 | for (self.objects.items) |*object| { | ||
| 93 | object.deinit(); | ||
| 94 | } | ||
| 95 | self.objects.deinit(self.allocator); | ||
| 96 | for (self.toc.items()) |*entry| { | 95 | for (self.toc.items()) |*entry| { |
| 97 | self.allocator.free(entry.key); | 96 | self.allocator.free(entry.key); |
| 98 | entry.value.deinit(self.allocator); | 97 | entry.value.deinit(self.allocator); |
| 99 | } | 98 | } |
| 100 | self.toc.deinit(self.allocator); | 99 | self.toc.deinit(self.allocator); |
| 100 | |||
| 101 | if (self.name) |n| { | ||
| 102 | self.allocator.free(n); | ||
| 103 | } | ||
| 101 | } | 104 | } |
| 102 | 105 | ||
| 103 | /// Caller owns the returned Archive instance and is responsible for calling | 106 | pub fn closeFile(self: Archive) void { |
| 104 | /// `deinit` to free allocated memory. | 107 | if (self.file) |f| { |
| 105 | pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, ar_name: []const u8, file: fs.File) !Archive { | 108 | f.close(); |
| 106 | var reader = file.reader(); | ||
| 107 | var magic = try readMagic(allocator, reader); | ||
| 108 | defer allocator.free(magic); | ||
| 109 | |||
| 110 | if (!mem.eql(u8, magic, ARMAG)) { | ||
| 111 | // Reset file cursor. | ||
| 112 | try file.seekTo(0); | ||
| 113 | return error.NotArchive; | ||
| 114 | } | 109 | } |
| 110 | } | ||
| 115 | 111 | ||
| 116 | const header = try reader.readStruct(ar_hdr); | 112 | pub fn parse(self: *Archive) !void { |
| 113 | var reader = self.file.?.reader(); | ||
| 114 | const magic = try reader.readBytesNoEof(SARMAG); | ||
| 117 | 115 | ||
| 118 | if (!mem.eql(u8, &header.ar_fmag, ARFMAG)) | 116 | if (!mem.eql(u8, &magic, ARMAG)) { |
| 117 | log.err("invalid magic: expected '{s}', found '{s}'", .{ ARMAG, magic }); | ||
| 119 | return error.MalformedArchive; | 118 | return error.MalformedArchive; |
| 119 | } | ||
| 120 | 120 | ||
| 121 | var embedded_name = try getName(allocator, header, reader); | 121 | self.header = try reader.readStruct(ar_hdr); |
| 122 | log.debug("parsing archive '{s}' at '{s}'", .{ embedded_name, ar_name }); | ||
| 123 | defer allocator.free(embedded_name); | ||
| 124 | 122 | ||
| 125 | var name = try allocator.dupe(u8, ar_name); | 123 | if (!mem.eql(u8, &self.header.?.ar_fmag, ARFMAG)) { |
| 126 | var self = Archive{ | 124 | log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, self.header.?.ar_fmag }); |
| 127 | .allocator = allocator, | 125 | return error.MalformedArchive; |
| 128 | .file = file, | 126 | } |
| 129 | .header = header, | 127 | |
| 130 | .name = name, | 128 | var embedded_name = try parseName(self.allocator, self.header.?, reader); |
| 131 | }; | 129 | log.warn("parsing archive '{s}' at '{s}'", .{ embedded_name, self.name.? }); |
| 130 | defer self.allocator.free(embedded_name); | ||
| 132 | 131 | ||
| 133 | try self.parseTableOfContents(reader); | 132 | try self.parseTableOfContents(reader); |
| 134 | 133 | ||
| 135 | return self; | 134 | try reader.context.seekTo(0); |
| 135 | } | ||
| 136 | |||
| 137 | fn parseName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { | ||
| 138 | const name_or_length = try header.nameOrLength(); | ||
| 139 | var name: []u8 = undefined; | ||
| 140 | switch (name_or_length) { | ||
| 141 | .Name => |n| { | ||
| 142 | name = try allocator.dupe(u8, n); | ||
| 143 | }, | ||
| 144 | .Length => |len| { | ||
| 145 | var n = try allocator.alloc(u8, len); | ||
| 146 | defer allocator.free(n); | ||
| 147 | try reader.readNoEof(n); | ||
| 148 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; | ||
| 149 | name = try allocator.dupe(u8, n[0..actual_len]); | ||
| 150 | }, | ||
| 151 | } | ||
| 152 | return name; | ||
| 136 | } | 153 | } |
| 137 | 154 | ||
| 138 | fn parseTableOfContents(self: *Archive, reader: anytype) !void { | 155 | fn parseTableOfContents(self: *Archive, reader: anytype) !void { |
| 139 | const symtab_size = try reader.readIntLittle(u32); | 156 | const symtab_size = try reader.readIntLittle(u32); |
| 140 | var symtab = try self.allocator.alloc(u8, symtab_size); | 157 | var symtab = try self.allocator.alloc(u8, symtab_size); |
| 141 | defer self.allocator.free(symtab); | 158 | defer self.allocator.free(symtab); |
| 142 | try reader.readNoEof(symtab); | 159 | |
| 160 | reader.readNoEof(symtab) catch { | ||
| 161 | log.err("incomplete symbol table: expected symbol table of length 0x{x}", .{symtab_size}); | ||
| 162 | return error.MalformedArchive; | ||
| 163 | }; | ||
| 143 | 164 | ||
| 144 | const strtab_size = try reader.readIntLittle(u32); | 165 | const strtab_size = try reader.readIntLittle(u32); |
| 145 | var strtab = try self.allocator.alloc(u8, strtab_size); | 166 | var strtab = try self.allocator.alloc(u8, strtab_size); |
| 146 | defer self.allocator.free(strtab); | 167 | defer self.allocator.free(strtab); |
| 147 | try reader.readNoEof(strtab); | 168 | |
| 169 | reader.readNoEof(strtab) catch { | ||
| 170 | log.err("incomplete symbol table: expected string table of length 0x{x}", .{strtab_size}); | ||
| 171 | return error.MalformedArchive; | ||
| 172 | }; | ||
| 148 | 173 | ||
| 149 | var symtab_stream = std.io.fixedBufferStream(symtab); | 174 | var symtab_stream = std.io.fixedBufferStream(symtab); |
| 150 | var symtab_reader = symtab_stream.reader(); | 175 | var symtab_reader = symtab_stream.reader(); |
| ... | @@ -169,85 +194,29 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { | ... | @@ -169,85 +194,29 @@ fn parseTableOfContents(self: *Archive, reader: anytype) !void { |
| 169 | } | 194 | } |
| 170 | } | 195 | } |
| 171 | 196 | ||
| 172 | fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void { | 197 | /// Caller owns the Object instance. |
| 198 | pub fn parseObject(self: Archive, offset: u32) !Object { | ||
| 199 | var reader = self.file.?.reader(); | ||
| 200 | try reader.context.seekTo(offset); | ||
| 201 | |||
| 173 | const object_header = try reader.readStruct(ar_hdr); | 202 | const object_header = try reader.readStruct(ar_hdr); |
| 174 | 203 | ||
| 175 | if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG)) | 204 | if (!mem.eql(u8, &object_header.ar_fmag, ARFMAG)) { |
| 205 | log.err("invalid header delimiter: expected '{s}', found '{s}'", .{ ARFMAG, object_header.ar_fmag }); | ||
| 176 | return error.MalformedArchive; | 206 | return error.MalformedArchive; |
| 177 | |||
| 178 | var object_name = try getName(self.allocator, object_header, reader); | ||
| 179 | log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name }); | ||
| 180 | |||
| 181 | const offset = @intCast(u32, try reader.context.getPos()); | ||
| 182 | const header = try reader.readStruct(macho.mach_header_64); | ||
| 183 | |||
| 184 | const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { | ||
| 185 | macho.CPU_TYPE_ARM64 => .aarch64, | ||
| 186 | macho.CPU_TYPE_X86_64 => .x86_64, | ||
| 187 | else => |value| { | ||
| 188 | log.err("unsupported cpu architecture 0x{x}", .{value}); | ||
| 189 | return error.UnsupportedCpuArchitecture; | ||
| 190 | }, | ||
| 191 | }; | ||
| 192 | if (this_arch != arch) { | ||
| 193 | log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch }); | ||
| 194 | return error.MismatchedCpuArchitecture; | ||
| 195 | } | 207 | } |
| 196 | 208 | ||
| 197 | // TODO Implement std.fs.File.clone() or similar. | 209 | const object_name = try parseName(self.allocator, object_header, reader); |
| 198 | var new_file = try fs.cwd().openFile(ar_name, .{}); | 210 | log.warn("extracting object '{s}' from archive '{s}'", .{ object_name, self.name.? }); |
| 199 | var object = Object{ | ||
| 200 | .allocator = self.allocator, | ||
| 201 | .name = object_name, | ||
| 202 | .ar_name = try mem.dupe(self.allocator, u8, ar_name), | ||
| 203 | .file = new_file, | ||
| 204 | .header = header, | ||
| 205 | }; | ||
| 206 | |||
| 207 | try object.readLoadCommands(reader, .{ .offset = offset }); | ||
| 208 | |||
| 209 | if (object.symtab_cmd_index != null) { | ||
| 210 | try object.readSymtab(); | ||
| 211 | try object.readStrtab(); | ||
| 212 | } | ||
| 213 | 211 | ||
| 214 | if (object.data_in_code_cmd_index != null) try object.readDataInCode(); | 212 | var object = Object.init(self.allocator); |
| 215 | 213 | object.arch = self.arch.?; | |
| 216 | log.debug("\n\n", .{}); | 214 | object.file = try fs.cwd().openFile(self.name.?, .{}); |
| 217 | log.debug("{s} defines symbols", .{object.name}); | 215 | object.name = object_name; |
| 218 | for (object.symtab.items) |sym| { | 216 | object.file_offset = @intCast(u32, try reader.context.getPos()); |
| 219 | const symname = object.getString(sym.n_strx); | 217 | try object.parse(); |
| 220 | log.debug("'{s}': {}", .{ symname, sym }); | ||
| 221 | } | ||
| 222 | |||
| 223 | try self.objects.append(self.allocator, object); | ||
| 224 | } | ||
| 225 | 218 | ||
| 226 | fn readMagic(allocator: *Allocator, reader: anytype) ![]u8 { | 219 | try reader.context.seekTo(0); |
| 227 | var magic = std.ArrayList(u8).init(allocator); | ||
| 228 | try magic.ensureCapacity(SARMAG); | ||
| 229 | var i: usize = 0; | ||
| 230 | while (i < SARMAG) : (i += 1) { | ||
| 231 | const next = try reader.readByte(); | ||
| 232 | magic.appendAssumeCapacity(next); | ||
| 233 | } | ||
| 234 | return magic.toOwnedSlice(); | ||
| 235 | } | ||
| 236 | 220 | ||
| 237 | fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 { | 221 | return object; |
| 238 | const name_or_length = try header.nameOrLength(); | ||
| 239 | var name: []u8 = undefined; | ||
| 240 | switch (name_or_length) { | ||
| 241 | .Name => |n| { | ||
| 242 | name = try allocator.dupe(u8, n); | ||
| 243 | }, | ||
| 244 | .Length => |len| { | ||
| 245 | var n = try allocator.alloc(u8, len); | ||
| 246 | defer allocator.free(n); | ||
| 247 | try reader.readNoEof(n); | ||
| 248 | const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len; | ||
| 249 | name = try allocator.dupe(u8, n[0..actual_len]); | ||
| 250 | }, | ||
| 251 | } | ||
| 252 | return name; | ||
| 253 | } | 222 | } |
src/link/MachO/Object.zig+50-49| ... | @@ -15,11 +15,11 @@ const parseName = @import("Zld.zig").parseName; | ... | @@ -15,11 +15,11 @@ const parseName = @import("Zld.zig").parseName; |
| 15 | usingnamespace @import("commands.zig"); | 15 | usingnamespace @import("commands.zig"); |
| 16 | 16 | ||
| 17 | allocator: *Allocator, | 17 | allocator: *Allocator, |
| 18 | file: fs.File, | 18 | arch: ?std.Target.Cpu.Arch = null, |
| 19 | name: []u8, | 19 | header: ?macho.mach_header_64 = null, |
| 20 | ar_name: ?[]u8 = null, | 20 | file: ?fs.File = null, |
| 21 | 21 | file_offset: ?u32 = null, | |
| 22 | header: macho.mach_header_64, | 22 | name: ?[]u8 = null, |
| 23 | 23 | ||
| 24 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, | 24 | load_commands: std.ArrayListUnmanaged(LoadCommand) = .{}, |
| 25 | 25 | ||
| ... | @@ -42,6 +42,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, | ... | @@ -42,6 +42,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 42 | 42 | ||
| 43 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, | 43 | data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, |
| 44 | 44 | ||
| 45 | pub fn init(allocator: *Allocator) Object { | ||
| 46 | return .{ | ||
| 47 | .allocator = allocator, | ||
| 48 | }; | ||
| 49 | } | ||
| 50 | |||
| 45 | pub fn deinit(self: *Object) void { | 51 | pub fn deinit(self: *Object) void { |
| 46 | for (self.load_commands.items) |*lc| { | 52 | for (self.load_commands.items) |*lc| { |
| 47 | lc.deinit(self.allocator); | 53 | lc.deinit(self.allocator); |
| ... | @@ -50,25 +56,32 @@ pub fn deinit(self: *Object) void { | ... | @@ -50,25 +56,32 @@ pub fn deinit(self: *Object) void { |
| 50 | self.symtab.deinit(self.allocator); | 56 | self.symtab.deinit(self.allocator); |
| 51 | self.strtab.deinit(self.allocator); | 57 | self.strtab.deinit(self.allocator); |
| 52 | self.data_in_code_entries.deinit(self.allocator); | 58 | self.data_in_code_entries.deinit(self.allocator); |
| 53 | self.allocator.free(self.name); | 59 | |
| 54 | if (self.ar_name) |v| { | 60 | if (self.name) |n| { |
| 55 | self.allocator.free(v); | 61 | self.allocator.free(n); |
| 56 | } | 62 | } |
| 57 | } | 63 | } |
| 58 | 64 | ||
| 59 | /// Caller owns the returned Object instance and is responsible for calling | 65 | pub fn closeFile(self: Object) void { |
| 60 | /// `deinit` to free allocated memory. | 66 | if (self.file) |f| { |
| 61 | pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []const u8, file: fs.File) !Object { | 67 | f.close(); |
| 62 | var reader = file.reader(); | 68 | } |
| 63 | const header = try reader.readStruct(macho.mach_header_64); | 69 | } |
| 64 | 70 | ||
| 65 | if (header.filetype != macho.MH_OBJECT) { | 71 | pub fn parse(self: *Object) !void { |
| 66 | // Reset file cursor. | 72 | var reader = self.file.?.reader(); |
| 67 | try file.seekTo(0); | 73 | if (self.file_offset) |offset| { |
| 68 | return error.NotObject; | 74 | try reader.context.seekTo(offset); |
| 69 | } | 75 | } |
| 70 | 76 | ||
| 71 | const this_arch: std.Target.Cpu.Arch = switch (header.cputype) { | 77 | self.header = try reader.readStruct(macho.mach_header_64); |
| 78 | |||
| 79 | if (self.header.?.filetype != macho.MH_OBJECT) { | ||
| 80 | log.err("invalid filetype: expected 0x{x}, found 0x{x}", .{ macho.MH_OBJECT, self.header.?.filetype }); | ||
| 81 | return error.MalformedObject; | ||
| 82 | } | ||
| 83 | |||
| 84 | const this_arch: std.Target.Cpu.Arch = switch (self.header.?.cputype) { | ||
| 72 | macho.CPU_TYPE_ARM64 => .aarch64, | 85 | macho.CPU_TYPE_ARM64 => .aarch64, |
| 73 | macho.CPU_TYPE_X86_64 => .x86_64, | 86 | macho.CPU_TYPE_X86_64 => .x86_64, |
| 74 | else => |value| { | 87 | else => |value| { |
| ... | @@ -76,35 +89,22 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []co | ... | @@ -76,35 +89,22 @@ pub fn initFromFile(allocator: *Allocator, arch: std.Target.Cpu.Arch, name: []co |
| 76 | return error.UnsupportedCpuArchitecture; | 89 | return error.UnsupportedCpuArchitecture; |
| 77 | }, | 90 | }, |
| 78 | }; | 91 | }; |
| 79 | if (this_arch != arch) { | 92 | if (this_arch != self.arch.?) { |
| 80 | log.err("mismatched cpu architecture: found {s}, expected {s}", .{ this_arch, arch }); | 93 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); |
| 81 | return error.MismatchedCpuArchitecture; | 94 | return error.MismatchedCpuArchitecture; |
| 82 | } | 95 | } |
| 83 | 96 | ||
| 84 | var self = Object{ | 97 | try self.readLoadCommands(reader); |
| 85 | .allocator = allocator, | ||
| 86 | .name = try allocator.dupe(u8, name), | ||
| 87 | .file = file, | ||
| 88 | .header = header, | ||
| 89 | }; | ||
| 90 | |||
| 91 | try self.readLoadCommands(reader, .{}); | ||
| 92 | if (self.symtab_cmd_index != null) try self.parseSymtab(); | 98 | if (self.symtab_cmd_index != null) try self.parseSymtab(); |
| 93 | if (self.data_in_code_cmd_index != null) try self.readDataInCode(); | 99 | if (self.data_in_code_cmd_index != null) try self.readDataInCode(); |
| 94 | |||
| 95 | return self; | ||
| 96 | } | 100 | } |
| 97 | 101 | ||
| 98 | pub const ReadOffset = struct { | 102 | pub fn readLoadCommands(self: *Object, reader: anytype) !void { |
| 99 | offset: ?u32 = null, | 103 | const offset = self.file_offset orelse 0; |
| 100 | }; | 104 | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); |
| 101 | |||
| 102 | pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !void { | ||
| 103 | const offset_mod = offset.offset orelse 0; | ||
| 104 | try self.load_commands.ensureCapacity(self.allocator, self.header.ncmds); | ||
| 105 | 105 | ||
| 106 | var i: u16 = 0; | 106 | var i: u16 = 0; |
| 107 | while (i < self.header.ncmds) : (i += 1) { | 107 | while (i < self.header.?.ncmds) : (i += 1) { |
| 108 | var cmd = try LoadCommand.read(self.allocator, reader); | 108 | var cmd = try LoadCommand.read(self.allocator, reader); |
| 109 | switch (cmd.cmd()) { | 109 | switch (cmd.cmd()) { |
| 110 | macho.LC_SEGMENT_64 => { | 110 | macho.LC_SEGMENT_64 => { |
| ... | @@ -132,17 +132,18 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi | ... | @@ -132,17 +132,18 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi |
| 132 | } | 132 | } |
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | sect.offset += offset_mod; | 135 | sect.offset += offset; |
| 136 | if (sect.reloff > 0) | 136 | if (sect.reloff > 0) { |
| 137 | sect.reloff += offset_mod; | 137 | sect.reloff += offset; |
| 138 | } | ||
| 138 | } | 139 | } |
| 139 | 140 | ||
| 140 | seg.inner.fileoff += offset_mod; | 141 | seg.inner.fileoff += offset; |
| 141 | }, | 142 | }, |
| 142 | macho.LC_SYMTAB => { | 143 | macho.LC_SYMTAB => { |
| 143 | self.symtab_cmd_index = i; | 144 | self.symtab_cmd_index = i; |
| 144 | cmd.Symtab.symoff += offset_mod; | 145 | cmd.Symtab.symoff += offset; |
| 145 | cmd.Symtab.stroff += offset_mod; | 146 | cmd.Symtab.stroff += offset; |
| 146 | }, | 147 | }, |
| 147 | macho.LC_DYSYMTAB => { | 148 | macho.LC_DYSYMTAB => { |
| 148 | self.dysymtab_cmd_index = i; | 149 | self.dysymtab_cmd_index = i; |
| ... | @@ -152,7 +153,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi | ... | @@ -152,7 +153,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi |
| 152 | }, | 153 | }, |
| 153 | macho.LC_DATA_IN_CODE => { | 154 | macho.LC_DATA_IN_CODE => { |
| 154 | self.data_in_code_cmd_index = i; | 155 | self.data_in_code_cmd_index = i; |
| 155 | cmd.LinkeditData.dataoff += offset_mod; | 156 | cmd.LinkeditData.dataoff += offset; |
| 156 | }, | 157 | }, |
| 157 | else => { | 158 | else => { |
| 158 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); | 159 | log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()}); |
| ... | @@ -168,7 +169,7 @@ pub fn parseSymtab(self: *Object) !void { | ... | @@ -168,7 +169,7 @@ pub fn parseSymtab(self: *Object) !void { |
| 168 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); | 169 | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); |
| 169 | defer self.allocator.free(symtab); | 170 | defer self.allocator.free(symtab); |
| 170 | 171 | ||
| 171 | _ = try self.file.preadAll(symtab, symtab_cmd.symoff); | 172 | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff); |
| 172 | try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms); | 173 | try self.symtab.ensureCapacity(self.allocator, symtab_cmd.nsyms); |
| 173 | 174 | ||
| 174 | var stream = std.io.fixedBufferStream(symtab); | 175 | var stream = std.io.fixedBufferStream(symtab); |
| ... | @@ -187,7 +188,7 @@ pub fn parseSymtab(self: *Object) !void { | ... | @@ -187,7 +188,7 @@ pub fn parseSymtab(self: *Object) !void { |
| 187 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); | 188 | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); |
| 188 | defer self.allocator.free(strtab); | 189 | defer self.allocator.free(strtab); |
| 189 | 190 | ||
| 190 | _ = try self.file.preadAll(strtab, symtab_cmd.stroff); | 191 | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff); |
| 191 | try self.strtab.appendSlice(self.allocator, strtab); | 192 | try self.strtab.appendSlice(self.allocator, strtab); |
| 192 | } | 193 | } |
| 193 | 194 | ||
| ... | @@ -200,7 +201,7 @@ pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { | ... | @@ -200,7 +201,7 @@ pub fn readSection(self: Object, allocator: *Allocator, index: u16) ![]u8 { |
| 200 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; | 201 | const seg = self.load_commands.items[self.segment_cmd_index.?].Segment; |
| 201 | const sect = seg.sections.items[index]; | 202 | const sect = seg.sections.items[index]; |
| 202 | var buffer = try allocator.alloc(u8, sect.size); | 203 | var buffer = try allocator.alloc(u8, sect.size); |
| 203 | _ = try self.file.preadAll(buffer, sect.offset); | 204 | _ = try self.file.?.preadAll(buffer, sect.offset); |
| 204 | return buffer; | 205 | return buffer; |
| 205 | } | 206 | } |
| 206 | 207 | ||
| ... | @@ -211,7 +212,7 @@ pub fn readDataInCode(self: *Object) !void { | ... | @@ -211,7 +212,7 @@ pub fn readDataInCode(self: *Object) !void { |
| 211 | var buffer = try self.allocator.alloc(u8, data_in_code.datasize); | 212 | var buffer = try self.allocator.alloc(u8, data_in_code.datasize); |
| 212 | defer self.allocator.free(buffer); | 213 | defer self.allocator.free(buffer); |
| 213 | 214 | ||
| 214 | _ = try self.file.preadAll(buffer, data_in_code.dataoff); | 215 | _ = try self.file.?.preadAll(buffer, data_in_code.dataoff); |
| 215 | 216 | ||
| 216 | var stream = io.fixedBufferStream(buffer); | 217 | var stream = io.fixedBufferStream(buffer); |
| 217 | var reader = stream.reader(); | 218 | var reader = stream.reader(); |
src/link/MachO/Zld.zig+108-21| ... | @@ -225,14 +225,14 @@ pub fn deinit(self: *Zld) void { | ... | @@ -225,14 +225,14 @@ pub fn deinit(self: *Zld) void { |
| 225 | self.undefs.deinit(self.allocator); | 225 | self.undefs.deinit(self.allocator); |
| 226 | } | 226 | } |
| 227 | 227 | ||
| 228 | pub fn closeFiles(self: *Zld) void { | 228 | pub fn closeFiles(self: Zld) void { |
| 229 | for (self.objects.items) |*object| { | 229 | for (self.objects.items) |object| { |
| 230 | object.file.close(); | 230 | object.closeFile(); |
| 231 | } | 231 | } |
| 232 | for (self.archives.items) |*archive| { | 232 | for (self.archives.items) |archive| { |
| 233 | archive.file.close(); | 233 | archive.closeFile(); |
| 234 | } | 234 | } |
| 235 | if (self.file) |*f| f.close(); | 235 | if (self.file) |f| f.close(); |
| 236 | } | 236 | } |
| 237 | 237 | ||
| 238 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { | 238 | pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| ... | @@ -272,6 +272,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { | ... | @@ -272,6 +272,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 272 | 272 | ||
| 273 | try self.populateMetadata(); | 273 | try self.populateMetadata(); |
| 274 | try self.parseInputFiles(files); | 274 | try self.parseInputFiles(files); |
| 275 | try self.resolveSymbols(); | ||
| 275 | self.printSymtab(); | 276 | self.printSymtab(); |
| 276 | // try self.sortSections(); | 277 | // try self.sortSections(); |
| 277 | // try self.allocateTextSegment(); | 278 | // try self.allocateTextSegment(); |
| ... | @@ -284,31 +285,76 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { | ... | @@ -284,31 +285,76 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { |
| 284 | } | 285 | } |
| 285 | 286 | ||
| 286 | fn parseInputFiles(self: *Zld, files: []const []const u8) !void { | 287 | fn parseInputFiles(self: *Zld, files: []const []const u8) !void { |
| 288 | const Input = struct { | ||
| 289 | kind: enum { | ||
| 290 | object, | ||
| 291 | archive, | ||
| 292 | }, | ||
| 293 | file: fs.File, | ||
| 294 | name: []const u8, | ||
| 295 | }; | ||
| 296 | var classified = std.ArrayList(Input).init(self.allocator); | ||
| 297 | defer classified.deinit(); | ||
| 298 | |||
| 299 | // First, classify input files as either object or archive. | ||
| 287 | for (files) |file_name| { | 300 | for (files) |file_name| { |
| 288 | const file = try fs.cwd().openFile(file_name, .{}); | 301 | const file = try fs.cwd().openFile(file_name, .{}); |
| 289 | 302 | ||
| 290 | try_object: { | 303 | try_object: { |
| 291 | var object = Object.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) { | 304 | const header = try file.reader().readStruct(macho.mach_header_64); |
| 292 | error.NotObject => break :try_object, | 305 | if (header.filetype != macho.MH_OBJECT) { |
| 293 | else => |e| return e, | 306 | try file.seekTo(0); |
| 294 | }; | 307 | break :try_object; |
| 295 | const index = @intCast(u16, self.objects.items.len); | 308 | } |
| 296 | try self.objects.append(self.allocator, object); | 309 | |
| 297 | try self.resolveSymbols(index); | 310 | try file.seekTo(0); |
| 311 | try classified.append(.{ | ||
| 312 | .kind = .object, | ||
| 313 | .file = file, | ||
| 314 | .name = file_name, | ||
| 315 | }); | ||
| 298 | continue; | 316 | continue; |
| 299 | } | 317 | } |
| 300 | 318 | ||
| 301 | try_archive: { | 319 | try_archive: { |
| 302 | var archive = Archive.initFromFile(self.allocator, self.arch.?, file_name, file) catch |err| switch (err) { | 320 | const magic = try file.reader().readBytesNoEof(Archive.SARMAG); |
| 303 | error.NotArchive => break :try_archive, | 321 | if (!mem.eql(u8, &magic, Archive.ARMAG)) { |
| 304 | else => |e| return e, | 322 | try file.seekTo(0); |
| 305 | }; | 323 | break :try_archive; |
| 306 | try self.archives.append(self.allocator, archive); | 324 | } |
| 325 | |||
| 326 | try file.seekTo(0); | ||
| 327 | try classified.append(.{ | ||
| 328 | .kind = .archive, | ||
| 329 | .file = file, | ||
| 330 | .name = file_name, | ||
| 331 | }); | ||
| 307 | continue; | 332 | continue; |
| 308 | } | 333 | } |
| 309 | 334 | ||
| 310 | log.err("unexpected file type: expected object '.o' or archive '.a': {s}", .{file_name}); | 335 | log.warn("unexpected input file of unknown type '{s}'", .{file_name}); |
| 311 | return error.UnexpectedInputFileType; | 336 | } |
| 337 | |||
| 338 | // Based on our classification, proceed with parsing. | ||
| 339 | for (classified.items) |input| { | ||
| 340 | switch (input.kind) { | ||
| 341 | .object => { | ||
| 342 | var object = Object.init(self.allocator); | ||
| 343 | object.arch = self.arch.?; | ||
| 344 | object.name = try self.allocator.dupe(u8, input.name); | ||
| 345 | object.file = input.file; | ||
| 346 | try object.parse(); | ||
| 347 | try self.objects.append(self.allocator, object); | ||
| 348 | }, | ||
| 349 | .archive => { | ||
| 350 | var archive = Archive.init(self.allocator); | ||
| 351 | archive.arch = self.arch.?; | ||
| 352 | archive.name = try self.allocator.dupe(u8, input.name); | ||
| 353 | archive.file = input.file; | ||
| 354 | try archive.parse(); | ||
| 355 | try self.archives.append(self.allocator, archive); | ||
| 356 | }, | ||
| 357 | } | ||
| 312 | } | 358 | } |
| 313 | } | 359 | } |
| 314 | 360 | ||
| ... | @@ -1153,7 +1199,7 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { | ... | @@ -1153,7 +1199,7 @@ fn writeStubInStubHelper(self: *Zld, index: u32) !void { |
| 1153 | try self.file.?.pwriteAll(code, stub_off); | 1199 | try self.file.?.pwriteAll(code, stub_off); |
| 1154 | } | 1200 | } |
| 1155 | 1201 | ||
| 1156 | fn resolveSymbols(self: *Zld, object_id: u16) !void { | 1202 | fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { |
| 1157 | const object = self.objects.items[object_id]; | 1203 | const object = self.objects.items[object_id]; |
| 1158 | log.warn("resolving symbols in '{s}'", .{object.name}); | 1204 | log.warn("resolving symbols in '{s}'", .{object.name}); |
| 1159 | 1205 | ||
| ... | @@ -1200,6 +1246,47 @@ fn resolveSymbols(self: *Zld, object_id: u16) !void { | ... | @@ -1200,6 +1246,47 @@ fn resolveSymbols(self: *Zld, object_id: u16) !void { |
| 1200 | } | 1246 | } |
| 1201 | } | 1247 | } |
| 1202 | 1248 | ||
| 1249 | fn resolveSymbols(self: *Zld) !void { | ||
| 1250 | // First pass, resolve symbols in provided objects. | ||
| 1251 | for (self.objects.items) |object, object_id| { | ||
| 1252 | try self.resolveSymbolsInObject(@intCast(u16, object_id)); | ||
| 1253 | } | ||
| 1254 | |||
| 1255 | var next: usize = 0; | ||
| 1256 | while (true) { | ||
| 1257 | var archive = &self.archives.items[next]; | ||
| 1258 | var hit: bool = false; | ||
| 1259 | |||
| 1260 | for (self.undefs.items()) |entry| { | ||
| 1261 | const sym_name = entry.key; | ||
| 1262 | |||
| 1263 | // Check if the entry exists in a static archive. | ||
| 1264 | const offsets = archive.toc.get(sym_name) orelse { | ||
| 1265 | // No hit. | ||
| 1266 | continue; | ||
| 1267 | }; | ||
| 1268 | assert(offsets.items.len > 0); | ||
| 1269 | |||
| 1270 | const object = try archive.parseObject(offsets.items[0]); | ||
| 1271 | const object_id = @intCast(u16, self.objects.items.len); | ||
| 1272 | try self.objects.append(self.allocator, object); | ||
| 1273 | try self.resolveSymbolsInObject(object_id); | ||
| 1274 | |||
| 1275 | hit = true; | ||
| 1276 | break; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | if (!hit) { | ||
| 1280 | // Next archive. | ||
| 1281 | next += 1; | ||
| 1282 | if (next == self.archives.items.len) { | ||
| 1283 | break; | ||
| 1284 | } | ||
| 1285 | archive = &self.archives.items[next]; | ||
| 1286 | } | ||
| 1287 | } | ||
| 1288 | } | ||
| 1289 | |||
| 1203 | fn doRelocs(self: *Zld) !void { | 1290 | fn doRelocs(self: *Zld) !void { |
| 1204 | for (self.objects.items) |object, object_id| { | 1291 | for (self.objects.items) |object, object_id| { |
| 1205 | log.debug("\n\n", .{}); | 1292 | log.debug("\n\n", .{}); |