| author | |
| committer | |
| log | de8e6124557d189d8b55f0d63d0a02fb98d51799 |
| tree | afb93b70fb60da51785fa6b472f2792c023c2233 |
| parent | e08b6149ab8fb80ad8fa4983ad3aca8ef3303a9f |
Handle clang's linker flag `-weak_framework` as a standard framework to
link. This requires further investigation especially to do with weak
imports and how to tie one with the other.3 files changed, 79 insertions(+), 73 deletions(-)
src/link/MachO.zig+57-11| ... | ... | @@ -842,8 +842,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation) !void { |
| 842 | 842 | Compilation.dump_argv(argv.items); |
| 843 | 843 | } |
| 844 | 844 | |
| 845 | try self.parseInputFiles(positionals.items, self.base.options.sysroot); | |
| 846 | try self.parseLibs(libs.items, self.base.options.sysroot); | |
| 845 | var dependent_libs = std.fifo.LinearFifo(Dylib.Id, .Dynamic).init(self.base.allocator); | |
| 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 | } |
| 848 | 851 | |
| 849 | 852 | if (self.bss_section_index) |idx| { |
| ... | ... | @@ -1161,7 +1164,8 @@ const ParseDylibError = error{ |
| 1161 | 1164 | } || fs.File.OpenError || std.os.PReadError || Dylib.Id.ParseError; |
| 1162 | 1165 | |
| 1163 | 1166 | const DylibCreateOpts = struct { |
| 1164 | syslibroot: ?[]const u8 = null, | |
| 1167 | syslibroot: ?[]const u8, | |
| 1168 | dependent_libs: *std.fifo.LinearFifo(Dylib.Id, .Dynamic), | |
| 1165 | 1169 | id: ?Dylib.Id = null, |
| 1166 | 1170 | is_dependent: bool = false, |
| 1167 | 1171 | }; |
| ... | ... | @@ -1181,7 +1185,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1181 | 1185 | .file = file, |
| 1182 | 1186 | }; |
| 1183 | 1187 | |
| 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 | 1189 | error.EndOfStream, error.NotDylib => { |
| 1186 | 1190 | try file.seekTo(0); |
| 1187 | 1191 | |
| ... | ... | @@ -1191,7 +1195,7 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1191 | 1195 | }; |
| 1192 | 1196 | defer lib_stub.deinit(); |
| 1193 | 1197 | |
| 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 | 1200 | else => |e| return e, |
| 1197 | 1201 | }; |
| ... | ... | @@ -1218,14 +1222,10 @@ pub fn parseDylib(self: *MachO, path: []const u8, opts: DylibCreateOpts) ParseDy |
| 1218 | 1222 | try self.referenced_dylibs.putNoClobber(self.base.allocator, dylib_id, {}); |
| 1219 | 1223 | } |
| 1220 | 1224 | |
| 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 | 1225 | return true; |
| 1226 | 1226 | } |
| 1227 | 1227 | |
| 1228 | fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 1228 | fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { | |
| 1229 | 1229 | for (files) |file_name| { |
| 1230 | 1230 | const full_path = full_path: { |
| 1231 | 1231 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; |
| ... | ... | @@ -1239,17 +1239,19 @@ fn parseInputFiles(self: *MachO, files: []const []const u8, syslibroot: ?[]const |
| 1239 | 1239 | if (try self.parseArchive(full_path)) continue; |
| 1240 | 1240 | if (try self.parseDylib(full_path, .{ |
| 1241 | 1241 | .syslibroot = syslibroot, |
| 1242 | .dependent_libs = dependent_libs, | |
| 1242 | 1243 | })) continue; |
| 1243 | 1244 | |
| 1244 | 1245 | log.warn("unknown filetype for positional input file: '{s}'", .{file_name}); |
| 1245 | 1246 | } |
| 1246 | 1247 | } |
| 1247 | 1248 | |
| 1248 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !void { | |
| 1249 | fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8, dependent_libs: anytype) !void { | |
| 1249 | 1250 | for (libs) |lib| { |
| 1250 | 1251 | log.debug("parsing lib path '{s}'", .{lib}); |
| 1251 | 1252 | if (try self.parseDylib(lib, .{ |
| 1252 | 1253 | .syslibroot = syslibroot, |
| 1254 | .dependent_libs = dependent_libs, | |
| 1253 | 1255 | })) continue; |
| 1254 | 1256 | if (try self.parseArchive(lib)) continue; |
| 1255 | 1257 | |
| ... | ... | @@ -1257,6 +1259,50 @@ fn parseLibs(self: *MachO, libs: []const []const u8, syslibroot: ?[]const u8) !v |
| 1257 | 1259 | } |
| 1258 | 1260 | } |
| 1259 | 1261 | |
| 1262 | fn 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 | ||
| 1260 | 1306 | pub const MatchingSection = struct { |
| 1261 | 1307 | seg: u16, |
| 1262 | 1308 | sect: u16, |
src/link/MachO/Dylib.zig+16-62| ... | ... | @@ -38,9 +38,6 @@ id: ?Id = null, |
| 38 | 38 | /// a symbol is referenced by an object file. |
| 39 | 39 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 40 | 40 | |
| 41 | /// Array list of all dependent libs of this dylib. | |
| 42 | dependent_libs: std.ArrayListUnmanaged(Id) = .{}, | |
| 43 | ||
| 44 | 41 | pub const Id = struct { |
| 45 | 42 | name: []const u8, |
| 46 | 43 | timestamp: u32, |
| ... | ... | @@ -139,10 +136,6 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { |
| 139 | 136 | } |
| 140 | 137 | self.symbols.deinit(allocator); |
| 141 | 138 | |
| 142 | for (self.dependent_libs.items) |*id| { | |
| 143 | id.deinit(allocator); | |
| 144 | } | |
| 145 | self.dependent_libs.deinit(allocator); | |
| 146 | 139 | allocator.free(self.name); |
| 147 | 140 | |
| 148 | 141 | if (self.id) |*id| { |
| ... | ... | @@ -150,7 +143,7 @@ pub fn deinit(self: *Dylib, allocator: *Allocator) void { |
| 150 | 143 | } |
| 151 | 144 | } |
| 152 | 145 | |
| 153 | pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target) !void { | |
| 146 | pub fn parse(self: *Dylib, allocator: *Allocator, target: std.Target, dependent_libs: anytype) !void { | |
| 154 | 147 | log.debug("parsing shared library '{s}'", .{self.name}); |
| 155 | 148 | |
| 156 | 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 | 165 | return error.MismatchedCpuArchitecture; |
| 173 | 166 | } |
| 174 | 167 | |
| 175 | try self.readLoadCommands(allocator, reader); | |
| 168 | try self.readLoadCommands(allocator, reader, dependent_libs); | |
| 176 | 169 | try self.parseId(allocator); |
| 177 | 170 | try self.parseSymbols(allocator); |
| 178 | 171 | } |
| 179 | 172 | |
| 180 | fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void { | |
| 173 | fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype, dependent_libs: anytype) !void { | |
| 181 | 174 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; |
| 182 | 175 | |
| 183 | 176 | try self.load_commands.ensureUnusedCapacity(allocator, self.header.?.ncmds); |
| ... | ... | @@ -198,8 +191,8 @@ fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void |
| 198 | 191 | macho.LC_REEXPORT_DYLIB => { |
| 199 | 192 | if (should_lookup_reexports) { |
| 200 | 193 | // Parse install_name to dependent dylib. |
| 201 | const id = try Id.fromLoadCommand(allocator, cmd.Dylib); | |
| 202 | try self.dependent_libs.append(allocator, id); | |
| 194 | var id = try Id.fromLoadCommand(allocator, cmd.Dylib); | |
| 195 | try dependent_libs.writeItem(id); | |
| 203 | 196 | } |
| 204 | 197 | }, |
| 205 | 198 | else => { |
| ... | ... | @@ -341,7 +334,13 @@ const TargetMatcher = struct { |
| 341 | 334 | } |
| 342 | 335 | }; |
| 343 | 336 | |
| 344 | pub fn parseFromStub(self: *Dylib, allocator: *Allocator, target: std.Target, lib_stub: LibStub) !void { | |
| 337 | pub fn parseFromStub( | |
| 338 | self: *Dylib, | |
| 339 | allocator: *Allocator, | |
| 340 | target: std.Target, | |
| 341 | lib_stub: LibStub, | |
| 342 | dependent_libs: anytype, | |
| 343 | ) !void { | |
| 345 | 344 | if (lib_stub.inner.len == 0) return error.EmptyStubFile; |
| 346 | 345 | |
| 347 | 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 | 415 | |
| 417 | 416 | log.debug(" (found re-export '{s}')", .{lib}); |
| 418 | 417 | |
| 419 | const dep_id = try Id.default(allocator, lib); | |
| 420 | try self.dependent_libs.append(allocator, dep_id); | |
| 418 | var dep_id = try Id.default(allocator, lib); | |
| 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 | 520 | |
| 522 | 521 | log.debug(" (found re-export '{s}')", .{lib}); |
| 523 | 522 | |
| 524 | const dep_id = try Id.default(allocator, lib); | |
| 525 | try self.dependent_libs.append(allocator, dep_id); | |
| 523 | var dep_id = try Id.default(allocator, lib); | |
| 524 | try dependent_libs.writeItem(dep_id); | |
| 526 | 525 | } |
| 527 | 526 | } |
| 528 | 527 | } |
| 529 | 528 | } |
| 530 | 529 | } |
| 531 | ||
| 532 | pub 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+6| ... | ... | @@ -1613,6 +1613,12 @@ fn buildOutputType( |
| 1613 | 1613 | ) catch |err| { |
| 1614 | 1614 | fatal("unable to parse '{s}': {s}", .{ arg, @errorName(err) }); |
| 1615 | 1615 | }; |
| 1616 | } else if (mem.eql(u8, arg, "-weak_framework")) { | |
| 1617 | i += 1; | |
| 1618 | if (i >= linker_args.items.len) { | |
| 1619 | fatal("expected linker arg after '{s}'", .{arg}); | |
| 1620 | } | |
| 1621 | try frameworks.append(linker_args.items[i]); | |
| 1616 | 1622 | } else { |
| 1617 | 1623 | warn("unsupported linker arg: {s}", .{arg}); |
| 1618 | 1624 | } |