diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-11-23 13:52:44 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-23 13:52:44 -0500 |
| commit | 8e6c038dd81fa7eddfdac882aa3b1d5edbdcf329 (patch) | |
| tree | fa3390a85e0836ad4dcc6420e92965c7317b90c7 /src | |
| parent | f5c0c0803fab4a18d612cc8b8649566463cf5d60 (diff) | |
| parent | 0c1d610015e04c96508750e95b2f88408ded8843 (diff) | |
| download | zig-8e6c038dd81fa7eddfdac882aa3b1d5edbdcf329.tar.gz zig-8e6c038dd81fa7eddfdac882aa3b1d5edbdcf329.zip | |
Merge pull request #10208 from ziglang/zld-frameworks
zld: resolve frameworks in BFS order and handle additional macOS flags
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 2 | ||||
| -rw-r--r-- | src/link.zig | 1 | ||||
| -rw-r--r-- | src/link/MachO.zig | 76 | ||||
| -rw-r--r-- | src/link/MachO/Dylib.zig | 78 | ||||
| -rw-r--r-- | src/main.zig | 24 |
5 files changed, 106 insertions, 75 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index e23b6a12e4..48899f6f6e 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -758,6 +758,7 @@ pub const InitOptions = struct { image_base_override: ?u64 = null, self_exe_path: ?[]const u8 = null, version: ?std.builtin.Version = null, + compatibility_version: ?std.builtin.Version = null, libc_installation: ?*const LibCInstallation = null, machine_code_model: std.builtin.CodeModel = .default, clang_preprocessor_mode: ClangPreprocessorMode = .no, @@ -1439,6 +1440,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .extra_lld_args = options.lld_argv, .soname = options.soname, .version = options.version, + .compatibility_version = options.compatibility_version, .libc_installation = libc_dirs.libc_installation, .pic = pic, .pie = pie, diff --git a/src/link.zig b/src/link.zig index 932c582367..4ad5952767 100644 --- a/src/link.zig +++ b/src/link.zig @@ -143,6 +143,7 @@ pub const Options = struct { rpath_list: []const []const u8, version: ?std.builtin.Version, + compatibility_version: ?std.builtin.Version, libc_installation: ?*const LibCInstallation, /// WASI-only. Type of WASI execution model ("command" or "reactor"). diff --git a/src/link/MachO.zig b/src/link/MachO.zig index fd4c1fad0f..483c5c8371 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -842,8 +842,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { Compilation.dump_argv(argv.items); } - try self.parseInputFiles(positionals.items, self.base.options.sysroot); - try self.parseLibs(libs.items, self.base.options.sysroot); + var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator); + defer dependent_libs.deinit(); + try self.parseInputFiles(positionals.items, self.base.options.sysroot, &dependent_libs); + try self.parseLibs(libs.items, self.base.options.sysroot, &dependent_libs); + try self.parseDependentLibs(self.base.options.sysroot, &dependent_libs); } if (self.bss_section_index) |idx| { @@ -1161,7 +1164,8 @@ const ParseDylibError = error{ } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError; const DylibCreateOpts = struct { - syslibroot: ?[]const u8 = null, + syslibroot: ?[]const u8, + dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), id: ?Dylib.Id = null, is_dependent: bool = false, }; @@ -1181,7 +1185,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy .file = file, }; - dylib.parse(self.base.allocator, self.base.options.target) catch |err| switch (err) { + dylib.parse(self.base.allocator, self.base.options.target, opts.dependent_libs) catch |err| switch (err) { error.EndOfStream, error.NotDylib => { try file.seekTo(0); @@ -1191,7 +1195,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy }; defer lib_stub.deinit(); - try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub); + try dylib.parseFromStub(self.base.allocator, self.base.options.target, lib_stub, opts.dependent_libs); }, else => |e| return e, }; @@ -1218,14 +1222,10 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); } - // TODO this should not be performed if the user specifies `-flat_namespace` flag. - // See ld64 manpages. - try dylib.parseDependentLibs(self, opts.syslibroot); - return true; } -fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { +fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { for (files) |file_name| { const full_path = full_path: { var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; @@ -1239,17 +1239,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const if (try self.parseArchive(full_path)) continue; if (try self.parseDylib(full_path, .{ .syslibroot = syslibroot, + .dependent_libs = dependent_libs, })) continue; log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); } } -fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { +fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { for (libs) |lib| { log.debug("parsing lib path '{s}'", .{lib}); if (try self.parseDylib(lib, .{ .syslibroot = syslibroot, + .dependent_libs = dependent_libs, })) continue; if (try self.parseArchive(lib)) continue; @@ -1257,6 +1259,50 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v } } +fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: anytype) !void { + // At this point, we can now parse dependents of dylibs preserving the inclusion order of: + // 1) anything on the linker line is parsed first + // 2) afterwards, we parse dependents of the included dylibs + // TODO this should not be performed if the user specifies `-flat_namespace` flag. + // See ld64 manpages. + var arena_alloc = std.heap.ArenaAllocator.init(self.base.allocator); + const arena = &arena_alloc.allocator; + defer arena_alloc.deinit(); + + while (dependent_libs.readItem()) |*id| { + defer id.deinit(self.base.allocator); + + if (self.dylibs_map.contains(id.name)) continue; + + const has_ext = blk: { + const basename = fs.path.basename(id.name); + break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; + }; + const extension = if (has_ext) fs.path.extension(id.name) else ""; + const without_ext = if (has_ext) blk: { + const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; + break :blk id.name[0..index]; + } else id.name; + + for (&[_][]const u8{ extension, ".tbd" }) |ext| { + const with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ without_ext, ext }); + const full_path = if (syslibroot) |root| try fs.path.join(arena, &.{ root, with_ext }) else with_ext; + + log.debug("trying dependency at fully resolved path {s}", .{full_path}); + + const did_parse_successfully = try self.parseDylib(full_path, .{ + .id = id.*, + .syslibroot = syslibroot, + .is_dependent = true, + .dependent_libs = dependent_libs, + }); + if (did_parse_successfully) break; + } else { + log.warn("unable to resolve dependency {s}", .{id.name}); + } + } +} + pub const MatchingSection = struct { seg: u16, sect: u16, @@ -3992,12 +4038,16 @@ pub fn populateMissingMetadata(self: *MachO) !void { self.base.options.emit.?.sub_path, }); defer self.base.allocator.free(install_name); + const current_version = self.base.options.version orelse + std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; + const compat_version = self.base.options.compatibility_version orelse + std.builtin.Version{ .major = 1, .minor = 0, .patch = 0 }; var dylib_cmd = try commands.createLoadDylibCommand( self.base.allocator, install_name, 2, - 0x10000, // TODO forward user-provided versions - 0x10000, + current_version.major << 16 | current_version.minor << 8 | current_version.patch, + compat_version.major << 16 | compat_version.minor << 8 | compat_version.patch, ); errdefer dylib_cmd.deinit(self.base.allocator); dylib_cmd.inner.cmd = macho.LC_ID_DYLIB; diff --git a/src/link/MachO/Dylib.zig b/src/link/MachO/Dylib.zig index 3d3089f468..b213687193 100644 --- a/src/link/MachO/Dylib.zig +++ b/src/link/MachO/Dylib.zig @@ -38,9 +38,6 @@ id: ?Id = null, /// a symbol is referenced by an object file. symbols: std.StringArrayHashMapUnmanaged(void) = .{}, -/// Array list of all dependent libs of this dylib. -dependent_libs: std.ArrayListUnmanaged(Id) = .{}, - pub const Id = struct { name: []const u8, timestamp: u32, @@ -139,10 +136,6 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { } self.symbols.deinit(allocator); - for (self.dependent_libs.items) |*id| { - id.deinit(allocator); - } - self.dependent_libs.deinit(allocator); allocator.free(self.name); if (self.id) |*id| { @@ -150,7 +143,7 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { } } -pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { +pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target, dependent_libs: anytype) !void { log.debug("parsing shared library '{s}'", .{self.name}); 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 { return error.MismatchedCpuArchitecture; } - try self.readLoadCommands(allocator, reader); + try self.readLoadCommands(allocator, reader, dependent_libs); try self.parseId(allocator); try self.parseSymbols(allocator); } -fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void { +fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype, dependent_libs: anytype) !void { const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds); @@ -198,8 +191,8 @@ fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void macho.LC_REEXPORT_DYLIB => { if (should_lookup_reexports) { // Parse install_name to dependent dylib. - const id = try Id.fromLoadCommand(allocator, cmd.Dylib); - try self.dependent_libs.append(allocator, id); + var id = try Id.fromLoadCommand(allocator, cmd.Dylib); + try dependent_libs.writeItem(id); } }, else => { @@ -341,7 +334,13 @@ const TargetMatcher = struct { } }; -pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { +pub fn parseFromStub( + self: *Dylib, + allocator: *Allocator, + target: std.Target, + lib_stub: LibStub, + dependent_libs: anytype, +) !void { if (lib_stub.inner.len == 0) return error.EmptyStubFile; 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 log.debug(" (found re-export '{s}')", .{lib}); - const dep_id = try Id.default(allocator, lib); - try self.dependent_libs.append(allocator, dep_id); + var dep_id = try Id.default(allocator, lib); + try dependent_libs.writeItem(dep_id); } } } @@ -521,55 +520,10 @@ pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, li log.debug(" (found re-export '{s}')", .{lib}); - const dep_id = try Id.default(allocator, lib); - try self.dependent_libs.append(allocator, dep_id); + var dep_id = try Id.default(allocator, lib); + try dependent_libs.writeItem(dep_id); } } } } } - -pub fn parseDependentLibs( - self: *Dylib, - macho_file: *MachO, - syslibroot: ?[]const u8, -) !void { - outer: for (self.dependent_libs.items) |id| { - if (macho_file.dylibs_map.contains(id.name)) continue :outer; - - const has_ext = blk: { - const basename = fs.path.basename(id.name); - break :blk mem.lastIndexOfScalar(u8, basename, '.') != null; - }; - const extension = if (has_ext) fs.path.extension(id.name) else ""; - const without_ext = if (has_ext) blk: { - const index = mem.lastIndexOfScalar(u8, id.name, '.') orelse unreachable; - break :blk id.name[0..index]; - } else id.name; - - for (&[_][]const u8{ extension, ".tbd" }) |ext| { - const with_ext = try std.fmt.allocPrint(macho_file.base.allocator, "{s}{s}", .{ - without_ext, - ext, - }); - defer macho_file.base.allocator.free(with_ext); - - const full_path = if (syslibroot) |root| - try fs.path.join(macho_file.base.allocator, &.{ root, with_ext }) - else - with_ext; - defer if (syslibroot) |_| macho_file.base.allocator.free(full_path); - - log.debug("trying dependency at fully resolved path {s}", .{full_path}); - - const did_parse_successfully = try macho_file.parseDylib(full_path, .{ - .id = id, - .syslibroot = syslibroot, - .is_dependent = true, - }); - if (!did_parse_successfully) continue; - } else { - log.debug("unable to resolve dependency {s}", .{id.name}); - } - } -} diff --git a/src/main.zig b/src/main.zig index 453124c55e..0b274b3aee 100644 --- a/src/main.zig +++ b/src/main.zig @@ -564,6 +564,7 @@ fn buildOutputType( var root_src_file: ?[]const u8 = null; var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; var have_version = false; + var compatibility_version: ?std.builtin.Version = null; var strip = false; var single_threaded = false; var function_sections = false; @@ -1613,6 +1614,29 @@ fn buildOutputType( ) catch |err| { fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); }; + } else if (mem.eql(u8, arg, "-framework") or mem.eql(u8, arg, "-weak_framework")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + try frameworks.append(linker_args.items[i]); + } else if (mem.eql(u8, arg, "-compatibility_version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + compatibility_version = std.builtin.Version.parse(linker_args.items[i]) catch |err| { + fatal("unable to parse -compatibility_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) }); + }; + } else if (mem.eql(u8, arg, "-current_version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{s}'", .{arg}); + } + version = std.builtin.Version.parse(linker_args.items[i]) catch |err| { + fatal("unable to parse -current_version '{s}': {s}", .{ linker_args.items[i], @errorName(err) }); + }; + have_version = true; } else { warn("unsupported linker arg: {s}", .{arg}); } |
