authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-26 21:39:07+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-04-13 10:56:03+02:00
logb0105029caafda4668f788387270ee8fcc1ac175
tree58b6f3c101b117533eff5b5ad1f24783a1b76fa1
parent988b184d03fea3001b588ceb033de66a7d7ba21b

zld: refactor object and archive parsing


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;
99
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
11const Object = @import("Object.zig");11const Object = @import("Object.zig");
12const parseName = @import("Zld.zig").parseName;
1312
14usingnamespace @import("commands.zig");13usingnamespace @import("commands.zig");
1514
16allocator: *Allocator,15allocator: *Allocator,
17file: fs.File,16arch: ?std.Target.Cpu.Arch = null,
18header: ar_hdr,17file: ?fs.File = null,
19name: []u8,18header: ?ar_hdr = null,
2019name: ?[]u8 = null,
21objects: std.ArrayListUnmanaged(Object) = .{},
2220
23/// Parsed table of contents.21/// Parsed table of contents.
24/// Each symbol name points to a list of all definition22/// 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.
32const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";30pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
33/// Size of that string.31/// Size of that string.
34const SARMAG: u4 = 8;32pub const SARMAG: u4 = 8;
3533
36/// String in ar_fmag at the end of each header.34/// String in ar_fmag at the end of each header.
37const ARFMAG: *const [2:0]u8 = "`\n";35pub const ARFMAG: *const [2:0]u8 = "`\n";
3836
39const ar_hdr = extern struct {37pub 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,
4240
...@@ -87,64 +85,91 @@ const ar_hdr = extern struct {...@@ -87,64 +85,91 @@ const ar_hdr = extern struct {
87 }85 }
88};86};
8987
88pub fn init(allocator: *Allocator) Archive {
89 return .{
90 .allocator = allocator,
91 };
92}
93
90pub fn deinit(self: *Archive) void {94pub 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}
102105
103/// Caller owns the returned Archive instance and is responsible for calling106pub fn closeFile(self: Archive) void {
104/// `deinit` to free allocated memory.107 if (self.file) |f| {
105pub 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}
115111
116 const header = try reader.readStruct(ar_hdr);112pub fn parse(self: *Archive) !void {
113 var reader = self.file.?.reader();
114 const magic = try reader.readBytesNoEof(SARMAG);
117115
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 }
120120
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);
124122
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);
132131
133 try self.parseTableOfContents(reader);132 try self.parseTableOfContents(reader);
134133
135 return self;134 try reader.context.seekTo(0);
135}
136
137fn 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}
137154
138fn parseTableOfContents(self: *Archive, reader: anytype) !void {155fn 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 };
143164
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 };
148173
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}
171196
172fn readObject(self: *Archive, arch: std.Target.Cpu.Arch, ar_name: []const u8, reader: anytype) !void {197/// Caller owns the Object instance.
198pub 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);
174203
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 }
196208
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 }
213211
214 if (object.data_in_code_cmd_index != null) try object.readDataInCode();212 var object = Object.init(self.allocator);
215213 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}
225218
226fn 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}
236220
237fn 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;
15usingnamespace @import("commands.zig");15usingnamespace @import("commands.zig");
1616
17allocator: *Allocator,17allocator: *Allocator,
18file: fs.File,18arch: ?std.Target.Cpu.Arch = null,
19name: []u8,19header: ?macho.mach_header_64 = null,
20ar_name: ?[]u8 = null,20file: ?fs.File = null,
2121file_offset: ?u32 = null,
22header: macho.mach_header_64,22name: ?[]u8 = null,
2323
24load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},24load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
2525
...@@ -42,6 +42,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{},...@@ -42,6 +42,12 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
4242
43data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},43data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{},
4444
45pub fn init(allocator: *Allocator) Object {
46 return .{
47 .allocator = allocator,
48 };
49}
50
45pub fn deinit(self: *Object) void {51pub 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}
5864
59/// Caller owns the returned Object instance and is responsible for calling65pub fn closeFile(self: Object) void {
60/// `deinit` to free allocated memory.66 if (self.file) |f| {
61pub 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}
6470
65 if (header.filetype != macho.MH_OBJECT) {71pub 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 }
7076
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 }
8396
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}
97101
98pub const ReadOffset = struct {102pub 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
102pub 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);
105105
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 }
134134
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 }
139140
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);
170171
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);
173174
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);
189190
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}
193194
...@@ -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}
206207
...@@ -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);
213214
214 _ = try self.file.preadAll(buffer, data_in_code.dataoff);215 _ = try self.file.?.preadAll(buffer, data_in_code.dataoff);
215216
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}
227227
228pub fn closeFiles(self: *Zld) void {228pub 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}
237237
238pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {238pub 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 {
272272
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}
285286
286fn parseInputFiles(self: *Zld, files: []const []const u8) !void {287fn 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, .{});
289302
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 }
300318
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 }
309334
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}
314360
...@@ -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}
11551201
1156fn resolveSymbols(self: *Zld, object_id: u16) !void {1202fn 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});
11591205
...@@ -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}
12021248
1249fn 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
1203fn doRelocs(self: *Zld) !void {1290fn 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", .{});