authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-28 12:17:55+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-06-28 15:14:50+02:00
logeca12b74b8f2ff9510de67907df2bc272614c8c8
tree9fa91b35ec449e2190dbbe9ba1168c179be84d2c
parent5aff1d5d6fde5f95576cd639e6fa80281524e2d6

zld: recurse dylibs reexports when defined and desired


4 files changed, 116 insertions(+), 139 deletions(-)

src/link/MachO.zig+14-9
......@@ -871,17 +871,19 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
871871
872872 // If we're compiling native and we can find libSystem.B.{dylib, tbd},
873873 // we link against that instead of embedded libSystem.B.tbd file.
874 const libc_stub_path = blk: {
875 if (self.base.options.is_native_os) {
876 if (try resolveLib(arena, lib_dirs.items, "System", .lib)) |full_path| {
877 break :blk full_path;
878 }
874 var link_native_libsystem = false;
875 if (self.base.options.is_native_os) {
876 if (try resolveLib(arena, lib_dirs.items, "System", .lib)) |full_path| {
877 try libs.append(full_path);
878 link_native_libsystem = true;
879879 }
880
881 break :blk try comp.zig_lib_directory.join(arena, &[_][]const u8{
880 }
881 if (!link_native_libsystem) {
882 const full_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{
882883 "libc", "darwin", "libSystem.B.tbd",
883884 });
884 };
885 try positionals.append(full_path);
886 }
885887
886888 // frameworks
887889 var framework_dirs = std.ArrayList([]const u8).init(arena);
......@@ -935,6 +937,10 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
935937 try argv.append("-o");
936938 try argv.append(full_out_path);
937939
940 if (link_native_libsystem) {
941 try argv.append("-lSystem");
942 }
943
938944 for (search_lib_names.items) |l_name| {
939945 try argv.append(try std.fmt.allocPrint(arena, "-l{s}", .{l_name}));
940946 }
......@@ -950,7 +956,6 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
950956 .syslibroot = self.base.options.sysroot,
951957 .libs = libs.items,
952958 .rpaths = rpaths.items,
953 .libc_stub_path = libc_stub_path,
954959 });
955960
956961 break :outer;
src/link/MachO/Dylib.zig+79-49
......@@ -45,8 +45,8 @@ id: ?Id = null,
4545/// a symbol is referenced by an object file.
4646symbols: std.StringArrayHashMapUnmanaged(void) = .{},
4747
48// TODO add parsing re-exported libs from binary dylibs
49dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{},
48/// Array list of all dependent libs of this dylib.
49dependent_libs: std.ArrayListUnmanaged(Id) = .{},
5050
5151pub const Id = struct {
5252 name: []const u8,
......@@ -54,15 +54,28 @@ pub const Id = struct {
5454 current_version: u32,
5555 compatibility_version: u32,
5656
57 pub fn default(name: []const u8) Id {
58 return .{
59 .name = name,
57 pub fn default(allocator: *Allocator, name: []const u8) !Id {
58 return Id{
59 .name = try allocator.dupe(u8, name),
6060 .timestamp = 2,
6161 .current_version = 0x10000,
6262 .compatibility_version = 0x10000,
6363 };
6464 }
6565
66 pub fn fromLoadCommand(allocator: *Allocator, lc: GenericCommandWithData(macho.dylib_command)) !Id {
67 const dylib = lc.inner.dylib;
68 const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
69 const name = try allocator.dupe(u8, mem.spanZ(dylib_name));
70
71 return Id{
72 .name = name,
73 .timestamp = dylib.timestamp,
74 .current_version = dylib.current_version,
75 .compatibility_version = dylib.compatibility_version,
76 };
77 }
78
6679 pub fn deinit(id: *Id, allocator: *Allocator) void {
6780 allocator.free(id.name);
6881 }
......@@ -129,12 +142,12 @@ pub const Error = error{
129142 UnsupportedCpuArchitecture,
130143} || fs.File.OpenError || std.os.PReadError || Id.ParseError;
131144
132pub fn createAndParseFromPath(
133 allocator: *Allocator,
134 arch: Arch,
135 path: []const u8,
136 syslibroot: ?[]const u8,
137) Error!?[]*Dylib {
145pub const CreateOpts = struct {
146 syslibroot: ?[]const u8 = null,
147 id: ?Id = null,
148};
149
150pub fn createAndParseFromPath(allocator: *Allocator, arch: Arch, path: []const u8, opts: CreateOpts) Error!?[]*Dylib {
138151 const file = fs.cwd().openFile(path, .{}) catch |err| switch (err) {
139152 error.FileNotFound => return null,
140153 else => |e| return e,
......@@ -152,7 +165,7 @@ pub fn createAndParseFromPath(
152165 .arch = arch,
153166 .name = name,
154167 .file = file,
155 .syslibroot = syslibroot,
168 .syslibroot = opts.syslibroot,
156169 };
157170
158171 dylib.parse() catch |err| switch (err) {
......@@ -171,6 +184,20 @@ pub fn createAndParseFromPath(
171184 else => |e| return e,
172185 };
173186
187 if (opts.id) |id| {
188 if (dylib.id.?.current_version < id.compatibility_version) {
189 log.warn("found dylib is incompatible with the required minimum version", .{});
190 log.warn(" | dylib: {s}", .{id.name});
191 log.warn(" | required minimum version: {}", .{id.compatibility_version});
192 log.warn(" | dylib version: {}", .{dylib.id.?.current_version});
193
194 // TODO maybe this should be an error and facilitate auto-cleanup?
195 dylib.deinit();
196 allocator.destroy(dylib);
197 return null;
198 }
199 }
200
174201 var dylibs = std.ArrayList(*Dylib).init(allocator);
175202 defer dylibs.deinit();
176203
......@@ -191,8 +218,8 @@ pub fn deinit(self: *Dylib) void {
191218 }
192219 self.symbols.deinit(self.allocator);
193220
194 for (self.dependent_libs.keys()) |key| {
195 self.allocator.free(key);
221 for (self.dependent_libs.items) |*id| {
222 id.deinit(self.allocator);
196223 }
197224 self.dependent_libs.deinit(self.allocator);
198225
......@@ -287,6 +314,8 @@ fn readFatStruct(reader: anytype, comptime T: type) !T {
287314}
288315
289316fn readLoadCommands(self: *Dylib, reader: anytype) !void {
317 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
318
290319 try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds);
291320
292321 var i: u16 = 0;
......@@ -302,6 +331,13 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
302331 macho.LC_ID_DYLIB => {
303332 self.id_cmd_index = i;
304333 },
334 macho.LC_REEXPORT_DYLIB => {
335 if (should_lookup_reexports) {
336 // Parse install_name to dependent dylib.
337 const id = try Id.fromLoadCommand(self.allocator, cmd.Dylib);
338 try self.dependent_libs.append(self.allocator, id);
339 }
340 },
305341 else => {
306342 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
307343 },
......@@ -313,22 +349,10 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void {
313349fn parseId(self: *Dylib) !void {
314350 const index = self.id_cmd_index orelse {
315351 log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{});
316 self.id = Id.default(try self.allocator.dupe(u8, self.name.?));
352 self.id = try Id.default(self.allocator, self.name.?);
317353 return;
318354 };
319 const id_cmd = self.load_commands.items[index].Dylib;
320 const dylib = id_cmd.inner.dylib;
321
322 // TODO should we compare the name from the dylib's id with the user-specified one?
323 const dylib_name = @ptrCast([*:0]const u8, id_cmd.data[dylib.name - @sizeOf(macho.dylib_command) ..]);
324 const name = try self.allocator.dupe(u8, mem.spanZ(dylib_name));
325
326 self.id = .{
327 .name = name,
328 .timestamp = dylib.timestamp,
329 .current_version = dylib.current_version,
330 .compatibility_version = dylib.compatibility_version,
331 };
355 self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib);
332356}
333357
334358fn parseSymbols(self: *Dylib) !void {
......@@ -380,7 +404,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
380404
381405 const umbrella_lib = lib_stub.inner[0];
382406
383 var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name));
407 var id = try Id.default(self.allocator, umbrella_lib.install_name);
384408 if (umbrella_lib.current_version) |version| {
385409 try id.parseCurrentVersion(version);
386410 }
......@@ -470,7 +494,9 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
470494 }
471495
472496 log.debug(" | {s}", .{lib});
473 try self.dependent_libs.put(self.allocator, try self.allocator.dupe(u8, lib), {});
497
498 const dep_id = try Id.default(self.allocator, lib);
499 try self.dependent_libs.append(self.allocator, dep_id);
474500 }
475501 }
476502 }
......@@ -478,36 +504,40 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void {
478504}
479505
480506pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
481 outer: for (self.dependent_libs.keys()) |lib| {
482 const dirname = fs.path.dirname(lib) orelse {
483 log.warn("unable to resolve dependency {s}", .{lib});
484 continue;
507 outer: for (self.dependent_libs.items) |id| {
508 const has_ext = blk: {
509 const basename = fs.path.basename(id.name);
510 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
485511 };
486 const filename = fs.path.basename(lib);
487 const without_ext = if (mem.lastIndexOfScalar(u8, filename, '.')) |index|
488 filename[0..index]
489 else
490 filename;
491
492 for (&[_][]const u8{ "dylib", "tbd" }) |ext| {
493 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}.{s}", .{
512 const extension = if (has_ext) fs.path.extension(id.name) else "";
513 const without_ext = if (has_ext) blk: {
514 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
515 break :blk id.name[0..index];
516 } else id.name;
517
518 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
519 const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{
494520 without_ext,
495521 ext,
496522 });
497523 defer self.allocator.free(with_ext);
498524
499 const lib_path = if (self.syslibroot) |syslibroot|
500 try fs.path.join(self.allocator, &.{ syslibroot, dirname, with_ext })
525 const full_path = if (self.syslibroot) |syslibroot|
526 try fs.path.join(self.allocator, &.{ syslibroot, with_ext })
501527 else
502 try fs.path.join(self.allocator, &.{ dirname, with_ext });
528 with_ext;
529 defer if (self.syslibroot) |_| self.allocator.free(full_path);
503530
504 log.debug("trying dependency at fully resolved path {s}", .{lib_path});
531 log.debug("trying dependency at fully resolved path {s}", .{full_path});
505532
506533 const dylibs = (try createAndParseFromPath(
507534 self.allocator,
508535 self.arch.?,
509 lib_path,
510 self.syslibroot,
536 full_path,
537 .{
538 .id = id,
539 .syslibroot = self.syslibroot,
540 },
511541 )) orelse {
512542 continue;
513543 };
......@@ -516,7 +546,7 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void {
516546
517547 continue :outer;
518548 } else {
519 log.warn("unable to resolve dependency {s}", .{lib});
549 log.warn("unable to resolve dependency {s}", .{id.name});
520550 }
521551 }
522552}
src/link/MachO/Symbol.zig+2-1
......@@ -111,7 +111,8 @@ pub const Unresolved = struct {
111111 base: Symbol,
112112
113113 /// File where this symbol was referenced.
114 file: *Object,
114 /// null means synthetic, e.g., dyld_stub_binder.
115 file: ?*Object = null,
115116
116117 pub const base_type: Symbol.Type = .unresolved;
117118};
src/link/MachO/Zld.zig+21-80
......@@ -38,7 +38,6 @@ objects: std.ArrayListUnmanaged(*Object) = .{},
3838archives: std.ArrayListUnmanaged(*Archive) = .{},
3939dylibs: std.ArrayListUnmanaged(*Dylib) = .{},
4040
41libsystem_dylib_index: ?u16 = null,
4241next_dylib_ordinal: u16 = 1,
4342
4443load_commands: std.ArrayListUnmanaged(LoadCommand) = .{},
......@@ -199,7 +198,6 @@ const LinkArgs = struct {
199198 syslibroot: ?[]const u8,
200199 libs: []const []const u8,
201200 rpaths: []const []const u8,
202 libc_stub_path: []const u8,
203201};
204202
205203pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: LinkArgs) !void {
......@@ -240,7 +238,6 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
240238 try self.populateMetadata();
241239 try self.parseInputFiles(files, args.syslibroot);
242240 try self.parseLibs(args.libs, args.syslibroot);
243 try self.parseLibSystem(args.libc_stub_path, args.syslibroot);
244241 try self.resolveSymbols();
245242 try self.resolveStubsAndGotEntries();
246243 try self.updateMetadata();
......@@ -280,7 +277,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8, syslibroot: ?[]const u
280277 self.allocator,
281278 self.arch.?,
282279 full_path,
283 syslibroot,
280 .{ .syslibroot = syslibroot },
284281 )) |dylibs| {
285282 defer self.allocator.free(dylibs);
286283 try self.dylibs.appendSlice(self.allocator, dylibs);
......@@ -297,7 +294,7 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
297294 self.allocator,
298295 self.arch.?,
299296 lib,
300 syslibroot,
297 .{ .syslibroot = syslibroot },
301298 )) |dylibs| {
302299 defer self.allocator.free(dylibs);
303300 try self.dylibs.appendSlice(self.allocator, dylibs);
......@@ -313,36 +310,6 @@ fn parseLibs(self: *Zld, libs: []const []const u8, syslibroot: ?[]const u8) !voi
313310 }
314311}
315312
316fn parseLibSystem(self: *Zld, libc_stub_path: []const u8, syslibroot: ?[]const u8) !void {
317 const dylibs = (try Dylib.createAndParseFromPath(
318 self.allocator,
319 self.arch.?,
320 libc_stub_path,
321 syslibroot,
322 )) orelse return error.FailedToParseLibSystem;
323 defer self.allocator.free(dylibs);
324
325 assert(dylibs.len == 1); // More than one dylib output from parsing libSystem!
326 const dylib = dylibs[0];
327
328 self.libsystem_dylib_index = @intCast(u16, self.dylibs.items.len);
329 try self.dylibs.append(self.allocator, dylib);
330
331 // Add LC_LOAD_DYLIB load command.
332 dylib.ordinal = self.next_dylib_ordinal;
333 const dylib_id = dylib.id orelse unreachable;
334 var dylib_cmd = try createLoadDylibCommand(
335 self.allocator,
336 dylib_id.name,
337 dylib_id.timestamp,
338 dylib_id.current_version,
339 dylib_id.compatibility_version,
340 );
341 errdefer dylib_cmd.deinit(self.allocator);
342 try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
343 self.next_dylib_ordinal += 1;
344}
345
346313fn mapAndUpdateSections(
347314 self: *Zld,
348315 object: *Object,
......@@ -1656,6 +1623,21 @@ fn resolveSymbols(self: *Zld) !void {
16561623 }
16571624 self.unresolved.clearRetainingCapacity();
16581625
1626 // Put dyld_stub_binder as an unresolved special symbol.
1627 {
1628 const name = try self.allocator.dupe(u8, "dyld_stub_binder");
1629 errdefer self.allocator.free(name);
1630 const undef = try self.allocator.create(Symbol.Unresolved);
1631 errdefer self.allocator.destroy(undef);
1632 undef.* = .{
1633 .base = .{
1634 .@"type" = .unresolved,
1635 .name = name,
1636 },
1637 };
1638 try unresolved.append(&undef.base);
1639 }
1640
16591641 var referenced = std.AutoHashMap(*Dylib, void).init(self.allocator);
16601642 defer referenced.deinit();
16611643
......@@ -1664,9 +1646,7 @@ fn resolveSymbols(self: *Zld) !void {
16641646 const proxy = inner: {
16651647 for (self.dylibs.items) |dylib, i| {
16661648 const proxy = (try dylib.createProxy(undef.name)) orelse continue;
1667 if (self.libsystem_dylib_index.? != @intCast(u16, i)) { // LibSystem gets load command seperately.
1668 try referenced.put(dylib, {});
1669 }
1649 try referenced.put(dylib, {});
16701650 break :inner proxy;
16711651 }
16721652 if (mem.eql(u8, undef.name, "___dso_handle")) {
......@@ -1681,7 +1661,6 @@ fn resolveSymbols(self: *Zld) !void {
16811661 .@"type" = .proxy,
16821662 .name = name,
16831663 },
1684 .file = null,
16851664 };
16861665 break :inner &proxy.base;
16871666 }
......@@ -1717,21 +1696,13 @@ fn resolveSymbols(self: *Zld) !void {
17171696 if (self.unresolved.count() > 0) {
17181697 for (self.unresolved.values()) |undef| {
17191698 log.err("undefined reference to symbol '{s}'", .{undef.name});
1720 log.err(" | referenced in {s}", .{
1721 undef.cast(Symbol.Unresolved).?.file.name.?,
1722 });
1699 if (undef.cast(Symbol.Unresolved).?.file) |file| {
1700 log.err(" | referenced in {s}", .{file.name.?});
1701 }
17231702 }
17241703
17251704 return error.UndefinedSymbolReference;
17261705 }
1727
1728 // Finally put dyld_stub_binder as an Import
1729 const libsystem_dylib = self.dylibs.items[self.libsystem_dylib_index.?];
1730 const proxy = (try libsystem_dylib.createProxy("dyld_stub_binder")) orelse {
1731 log.err("undefined reference to symbol 'dyld_stub_binder'", .{});
1732 return error.UndefinedSymbolReference;
1733 };
1734 try self.imports.putNoClobber(self.allocator, proxy.name, proxy);
17351706}
17361707
17371708fn resolveStubsAndGotEntries(self: *Zld) !void {
......@@ -3173,33 +3144,3 @@ pub fn parseName(name: *const [16]u8) []const u8 {
31733144 const len = mem.indexOfScalar(u8, name, @as(u8, 0)) orelse name.len;
31743145 return name[0..len];
31753146}
3176
3177fn printSymbols(self: *Zld) void {
3178 log.debug("globals", .{});
3179 for (self.globals.values()) |value| {
3180 const sym = value.cast(Symbol.Regular) orelse unreachable;
3181 log.debug(" | {s} @ {*}", .{ sym.base.name, value });
3182 log.debug(" => alias of {*}", .{sym.base.alias});
3183 log.debug(" => linkage {s}", .{sym.linkage});
3184 log.debug(" => defined in {s}", .{sym.file.name.?});
3185 }
3186 for (self.objects.items) |object| {
3187 log.debug("locals in {s}", .{object.name.?});
3188 for (object.symbols.items) |sym| {
3189 log.debug(" | {s} @ {*}", .{ sym.name, sym });
3190 log.debug(" => alias of {*}", .{sym.alias});
3191 if (sym.cast(Symbol.Regular)) |reg| {
3192 log.debug(" => linkage {s}", .{reg.linkage});
3193 } else {
3194 log.debug(" => unresolved", .{});
3195 }
3196 }
3197 }
3198 log.debug("proxies", .{});
3199 for (self.imports.values()) |value| {
3200 const sym = value.cast(Symbol.Proxy) orelse unreachable;
3201 log.debug(" | {s} @ {*}", .{ sym.base.name, value });
3202 log.debug(" => alias of {*}", .{sym.base.alias});
3203 log.debug(" => defined in libSystem.B.dylib", .{});
3204 }
3205}