authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-23 13:52:44-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-23 13:52:44-05:00
log8e6c038dd81fa7eddfdac882aa3b1d5edbdcf329
treefa3390a85e0836ad4dcc6420e92965c7317b90c7
parentf5c0c0803fab4a18d612cc8b8649566463cf5d60
parent0c1d610015e04c96508750e95b2f88408ded8843
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10208 from ziglang/zld-frameworks

zld: resolve frameworks in BFS order and handle additional macOS flags

5 files changed, 106 insertions(+), 75 deletions(-)

src/Compilation.zig+2
...@@ -758,6 +758,7 @@ pub const InitOptions = struct {...@@ -758,6 +758,7 @@ pub const InitOptions = struct {
758 image_base_override: ?u64 = null,758 image_base_override: ?u64 = null,
759 self_exe_path: ?[]const u8 = null,759 self_exe_path: ?[]const u8 = null,
760 version: ?std.builtin.Version = null,760 version: ?std.builtin.Version = null,
761 compatibility_version: ?std.builtin.Version = null,
761 libc_installation: ?*const LibCInstallation = null,762 libc_installation: ?*const LibCInstallation = null,
762 machine_code_model: std.builtin.CodeModel = .default,763 machine_code_model: std.builtin.CodeModel = .default,
763 clang_preprocessor_mode: ClangPreprocessorMode = .no,764 clang_preprocessor_mode: ClangPreprocessorMode = .no,
...@@ -1439,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1439,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1439 .extra_lld_args = options.lld_argv,1440 .extra_lld_args = options.lld_argv,
1440 .soname = options.soname,1441 .soname = options.soname,
1441 .version = options.version,1442 .version = options.version,
1443 .compatibility_version = options.compatibility_version,
1442 .libc_installation = libc_dirs.libc_installation,1444 .libc_installation = libc_dirs.libc_installation,
1443 .pic = pic,1445 .pic = pic,
1444 .pie = pie,1446 .pie = pie,
src/link.zig+1
...@@ -143,6 +143,7 @@ pub const Options = struct {...@@ -143,6 +143,7 @@ pub const Options = struct {
143 rpath_list: []const []const u8,143 rpath_list: []const []const u8,
144144
145 version: ?std.builtin.Version,145 version: ?std.builtin.Version,
146 compatibility_version: ?std.builtin.Version,
146 libc_installation: ?*const LibCInstallation,147 libc_installation: ?*const LibCInstallation,
147148
148 /// WASI-only. Type of WASI execution model ("command" or "reactor").149 /// WASI-only. Type of WASI execution model ("command" or "reactor").
src/link/MachO.zig+63-13
...@@ -842,8 +842,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {...@@ -842,8 +842,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void {
842 Compilation.dump_argv(argv.items);842 Compilation.dump_argv(argv.items);
843 }843 }
844844
845 try self.parseInputFiles(positionals.items, self.base.options.sysroot);845 var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator);
846 try self.parseLibs(libs.items, self.base.options.sysroot);846 defer dependent_libs.deinit();
847 try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs);
848 try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs);
849 try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs);
847 }850 }
848851
849 if (self.bss_section_index) |idx| {852 if (self.bss_section_index) |idx| {
...@@ -1161,7 +1164,8 @@ const ParseDylibError = error{...@@ -1161,7 +1164,8 @@ const ParseDylibError = error{
1161} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;1164} || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError;
11621165
1163const DylibCreateOpts = struct {1166const DylibCreateOpts = struct {
1164 syslibroot: ?[]const u8 = null,1167 syslibroot: ?[]const u8,
1168 dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic),
1165 id: ?Dylib.Id = null,1169 id: ?Dylib.Id = null,
1166 is_dependent: bool = false,1170 is_dependent: bool = false,
1167};1171};
...@@ -1181,7 +1185,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1181,7 +1185,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1181 .file = file,1185 .file = file,
1182 };1186 };
11831187
1184 dylib.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) {1188 dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) {
1185 error.EndOfStream, error.NotDylib => {1189 error.EndOfStream, error.NotDylib => {
1186 try file.seekTo(0);1190 try file.seekTo(0);
11871191
...@@ -1191,7 +1195,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1191,7 +1195,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1191 };1195 };
1192 defer lib_stub.deinit();1196 defer lib_stub.deinit();
11931197
1194 try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub);1198 try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs);
1195 },1199 },
1196 else => |e| return e,1200 else => |e| return e,
1197 };1201 };
...@@ -1218,14 +1222,10 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy...@@ -1218,14 +1222,10 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy
1218 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});1222 try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {});
1219 }1223 }
12201224
1221 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
1222 // See ld64 manpages.
1223 try dylib.parseDependentLibs(self, opts.syslibroot);
1224
1225 return true;1225 return true;
1226}1226}
12271227
1228fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void {1228fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1229 for (files) |file_name| {1229 for (files) |file_name| {
1230 const full_path = full_path: {1230 const full_path = full_path: {
1231 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;1231 var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1239,17 +1239,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const...@@ -1239,17 +1239,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const
1239 if (try self.parseArchive(full_path)) continue;1239 if (try self.parseArchive(full_path)) continue;
1240 if (try self.parseDylib(full_path, .{1240 if (try self.parseDylib(full_path, .{
1241 .syslibroot = syslibroot,1241 .syslibroot = syslibroot,
1242 .dependent_libs = dependent_libs,
1242 })) continue;1243 })) continue;
12431244
1244 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});1245 log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
1245 }1246 }
1246}1247}
12471248
1248fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void {1249fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1249 for (libs) |lib| {1250 for (libs) |lib| {
1250 log.debug("parsing lib path '{s}'", .{lib});1251 log.debug("parsing lib path '{s}'", .{lib});
1251 if (try self.parseDylib(lib, .{1252 if (try self.parseDylib(lib, .{
1252 .syslibroot = syslibroot,1253 .syslibroot = syslibroot,
1254 .dependent_libs = dependent_libs,
1253 })) continue;1255 })) continue;
1254 if (try self.parseArchive(lib)) continue;1256 if (try self.parseArchive(lib)) continue;
12551257
...@@ -1257,6 +1259,50 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v...@@ -1257,6 +1259,50 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v
1257 }1259 }
1258}1260}
12591261
1262fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void {
1263 // At this point, we can now parse dependents of dylibs preserving the inclusion order of:
1264 // 1) anything on the linker line is parsed first
1265 // 2) afterwards, we parse dependents of the included dylibs
1266 // TODO this should not be performed if the user specifies `-flat_namespace` flag.
1267 // See ld64 manpages.
1268 var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator);
1269 const arena = &arena_alloc.allocator;
1270 defer arena_alloc.deinit();
1271
1272 while (dependent_libs.readItem()) |*id| {
1273 defer id.deinit(self.base.allocator);
1274
1275 if (self.dylibs_map.contains(id.name)) continue;
1276
1277 const has_ext = blk: {
1278 const basename = fs.path.basename(id.name);
1279 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
1280 };
1281 const extension = if (has_ext) fs.path.extension(id.name) else "";
1282 const without_ext = if (has_ext) blk: {
1283 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
1284 break :blk id.name[0..index];
1285 } else id.name;
1286
1287 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
1288 const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext });
1289 const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext;
1290
1291 log.debug("trying dependency at fully resolved path {s}", .{full_path});
1292
1293 const did_parse_successfully = try self.parseDylib(full_path, .{
1294 .id = id.*,
1295 .syslibroot = syslibroot,
1296 .is_dependent = true,
1297 .dependent_libs = dependent_libs,
1298 });
1299 if (did_parse_successfully) break;
1300 } else {
1301 log.warn("unable to resolve dependency {s}", .{id.name});
1302 }
1303 }
1304}
1305
1260pub const MatchingSection = struct {1306pub const MatchingSection = struct {
1261 seg: u16,1307 seg: u16,
1262 sect: u16,1308 sect: u16,
...@@ -3992,12 +4038,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {...@@ -3992,12 +4038,16 @@ pub fn populateMissingMetadata(self: *MachO) !void {
3992 self.base.options.emit.?.sub_path,4038 self.base.options.emit.?.sub_path,
3993 });4039 });
3994 defer self.base.allocator.free(install_name);4040 defer self.base.allocator.free(install_name);
4041 const current_version = self.base.options.version orelse
4042 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
4043 const compat_version = self.base.options.compatibility_version orelse
4044 std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 };
3995 var dylib_cmd = try commands.createLoadDylibCommand(4045 var dylib_cmd = try commands.createLoadDylibCommand(
3996 self.base.allocator,4046 self.base.allocator,
3997 install_name,4047 install_name,
3998 2,4048 2,
3999 0x10000, // TODO forward user-provided versions4049 current_version.major << 16 | current_version.minor << 8 | current_version.patch,
4000 0x10000,4050 compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch,
4001 );4051 );
4002 errdefer dylib_cmd.deinit(self.base.allocator);4052 errdefer dylib_cmd.deinit(self.base.allocator);
4003 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;4053 dylib_cmd.inner.cmd = macho.LC_ID_DYLIB;
src/link/MachO/Dylib.zig+16-62
...@@ -38,9 +38,6 @@ id: ?Id = null,...@@ -38,9 +38,6 @@ id: ?Id = null,
38/// a symbol is referenced by an object file.38/// a symbol is referenced by an object file.
39symbols: std.StringArrayHashMapUnmanaged(void) = .{},39symbols: std.StringArrayHashMapUnmanaged(void) = .{},
4040
41/// Array list of all dependent libs of this dylib.
42dependent_libs: std.ArrayListUnmanaged(Id) = .{},
43
44pub const Id = struct {41pub const Id = struct {
45 name: []const u8,42 name: []const u8,
46 timestamp: u32,43 timestamp: u32,
...@@ -139,10 +136,6 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {...@@ -139,10 +136,6 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {
139 }136 }
140 self.symbols.deinit(allocator);137 self.symbols.deinit(allocator);
141138
142 for (self.dependent_libs.items) |*id| {
143 id.deinit(allocator);
144 }
145 self.dependent_libs.deinit(allocator);
146 allocator.free(self.name);139 allocator.free(self.name);
147140
148 if (self.id) |*id| {141 if (self.id) |*id| {
...@@ -150,7 +143,7 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {...@@ -150,7 +143,7 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void {
150 }143 }
151}144}
152145
153pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {146pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target, dependent_libs: anytype) !void {
154 log.debug("parsing shared library '{s}'", .{self.name});147 log.debug("parsing shared library '{s}'", .{self.name});
155148
156 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);149 self.library_offset = try fat.getLibraryOffset(self.file.reader(), target);
...@@ -172,12 +165,12 @@ pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {...@@ -172,12 +165,12 @@ pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void {
172 return error.MismatchedCpuArchitecture;165 return error.MismatchedCpuArchitecture;
173 }166 }
174167
175 try self.readLoadCommands(allocator, reader);168 try self.readLoadCommands(allocator, reader, dependent_libs);
176 try self.parseId(allocator);169 try self.parseId(allocator);
177 try self.parseSymbols(allocator);170 try self.parseSymbols(allocator);
178}171}
179172
180fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void {173fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype, dependent_libs: anytype) !void {
181 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;174 const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0;
182175
183 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);176 try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds);
...@@ -198,8 +191,8 @@ fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void...@@ -198,8 +191,8 @@ fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void
198 macho.LC_REEXPORT_DYLIB => {191 macho.LC_REEXPORT_DYLIB => {
199 if (should_lookup_reexports) {192 if (should_lookup_reexports) {
200 // Parse install_name to dependent dylib.193 // Parse install_name to dependent dylib.
201 const id = try Id.fromLoadCommand(allocator, cmd.Dylib);194 var id = try Id.fromLoadCommand(allocator, cmd.Dylib);
202 try self.dependent_libs.append(allocator, id);195 try dependent_libs.writeItem(id);
203 }196 }
204 },197 },
205 else => {198 else => {
...@@ -341,7 +334,13 @@ const TargetMatcher = struct {...@@ -341,7 +334,13 @@ const TargetMatcher = struct {
341 }334 }
342};335};
343336
344pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void {337pub fn parseFromStub(
338 self: *Dylib,
339 allocator: *Allocator,
340 target: std.Target,
341 lib_stub: LibStub,
342 dependent_libs: anytype,
343) !void {
345 if (lib_stub.inner.len == 0) return error.EmptyStubFile;344 if (lib_stub.inner.len == 0) return error.EmptyStubFile;
346345
347 log.debug("parsing shared library from stub '{s}'", .{self.name});346 log.debug("parsing shared library from stub '{s}'", .{self.name});
...@@ -416,8 +415,8 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li...@@ -416,8 +415,8 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
416415
417 log.debug(" (found re-export '{s}')", .{lib});416 log.debug(" (found re-export '{s}')", .{lib});
418417
419 const dep_id = try Id.default(allocator, lib);418 var dep_id = try Id.default(allocator, lib);
420 try self.dependent_libs.append(allocator, dep_id);419 try dependent_libs.writeItem(dep_id);
421 }420 }
422 }421 }
423 }422 }
...@@ -521,55 +520,10 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li...@@ -521,55 +520,10 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li
521520
522 log.debug(" (found re-export '{s}')", .{lib});521 log.debug(" (found re-export '{s}')", .{lib});
523522
524 const dep_id = try Id.default(allocator, lib);523 var dep_id = try Id.default(allocator, lib);
525 try self.dependent_libs.append(allocator, dep_id);524 try dependent_libs.writeItem(dep_id);
526 }525 }
527 }526 }
528 }527 }
529 }528 }
530}529}
531
532pub fn parseDependentLibs(
533 self: *Dylib,
534 macho_file: *MachO,
535 syslibroot: ?[]const u8,
536) !void {
537 outer: for (self.dependent_libs.items) |id| {
538 if (macho_file.dylibs_map.contains(id.name)) continue :outer;
539
540 const has_ext = blk: {
541 const basename = fs.path.basename(id.name);
542 break :blk mem.lastIndexOfScalar(u8, basename, '.') != null;
543 };
544 const extension = if (has_ext) fs.path.extension(id.name) else "";
545 const without_ext = if (has_ext) blk: {
546 const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable;
547 break :blk id.name[0..index];
548 } else id.name;
549
550 for (&[_][]const u8{ extension, ".tbd" }) |ext| {
551 const with_ext = try std.fmt.allocPrint(macho_file.base.allocator, "{s}{s}", .{
552 without_ext,
553 ext,
554 });
555 defer macho_file.base.allocator.free(with_ext);
556
557 const full_path = if (syslibroot) |root|
558 try fs.path.join(macho_file.base.allocator, &.{ root, with_ext })
559 else
560 with_ext;
561 defer if (syslibroot) |_| macho_file.base.allocator.free(full_path);
562
563 log.debug("trying dependency at fully resolved path {s}", .{full_path});
564
565 const did_parse_successfully = try macho_file.parseDylib(full_path, .{
566 .id = id,
567 .syslibroot = syslibroot,
568 .is_dependent = true,
569 });
570 if (!did_parse_successfully) continue;
571 } else {
572 log.debug("unable to resolve dependency {s}", .{id.name});
573 }
574 }
575}
src/main.zig+24
...@@ -564,6 +564,7 @@ fn buildOutputType(...@@ -564,6 +564,7 @@ fn buildOutputType(
564 var root_src_file: ?[]const u8 = null;564 var root_src_file: ?[]const u8 = null;
565 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };565 var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 };
566 var have_version = false;566 var have_version = false;
567 var compatibility_version: ?std.builtin.Version = null;
567 var strip = false;568 var strip = false;
568 var single_threaded = false;569 var single_threaded = false;
569 var function_sections = false;570 var function_sections = false;
...@@ -1613,6 +1614,29 @@ fn buildOutputType(...@@ -1613,6 +1614,29 @@ fn buildOutputType(
1613 ) catch |err| {1614 ) catch |err| {
1614 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });1615 fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) });
1615 };1616 };
1617 } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) {
1618 i += 1;
1619 if (i >= linker_args.items.len) {
1620 fatal("expected linker arg after '{s}'", .{arg});
1621 }
1622 try frameworks.append(linker_args.items[i]);
1623 } else if (mem.eql(u8, arg, "-compatibility_version")) {
1624 i += 1;
1625 if (i >= linker_args.items.len) {
1626 fatal("expected linker arg after '{s}'", .{arg});
1627 }
1628 compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
1629 fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
1630 };
1631 } else if (mem.eql(u8, arg, "-current_version")) {
1632 i += 1;
1633 if (i >= linker_args.items.len) {
1634 fatal("expected linker arg after '{s}'", .{arg});
1635 }
1636 version = std.builtin.Version.parse(linker_args.items[i]) catch |err| {
1637 fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) });
1638 };
1639 have_version = true;
1616 } else {1640 } else {
1617 warn("unsupported linker arg: {s}", .{arg});1641 warn("unsupported linker arg: {s}", .{arg});
1618 }1642 }