| ... | ... | @@ -9,20 +9,18 @@ const macho = std.macho; |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const mem = std.mem; |
| 11 | 11 | const fat = @import("fat.zig"); |
| 12 | const commands = @import("commands.zig"); |
| 12 | 13 | |
| 13 | 14 | const Allocator = mem.Allocator; |
| 14 | 15 | const Arch = std.Target.Cpu.Arch; |
| 15 | 16 | const LibStub = @import("../tapi.zig").LibStub; |
| 17 | const LoadCommand = commands.LoadCommand; |
| 16 | 18 | const MachO = @import("../MachO.zig"); |
| 17 | 19 | |
| 18 | | usingnamespace @import("commands.zig"); |
| 20 | file: fs.File, |
| 21 | name: []const u8, |
| 19 | 22 | |
| 20 | | allocator: *Allocator, |
| 21 | | arch: ?Arch = null, |
| 22 | 23 | header: ?macho.mach_header_64 = null, |
| 23 | | file: ?fs.File = null, |
| 24 | | name: ?[]const u8 = null, |
| 25 | | syslibroot: ?[]const u8 = null, |
| 26 | 24 | |
| 27 | 25 | ordinal: ?u16 = null, |
| 28 | 26 | |
| ... | ... | @@ -61,7 +59,7 @@ pub const Id = struct { |
| 61 | 59 | }; |
| 62 | 60 | } |
| 63 | 61 | |
| 64 | | pub fn fromLoadCommand(allocator: *Allocator, lc: GenericCommandWithData(macho.dylib_command)) !Id { |
| 62 | pub fn fromLoadCommand(allocator: *Allocator, lc: commands.GenericCommandWithData(macho.dylib_command)) !Id { |
| 65 | 63 | const dylib = lc.inner.dylib; |
| 66 | 64 | const dylib_name = @ptrCast([*:0]const u8, lc.data[dylib.name - @sizeOf(macho.dylib_command) ..]); |
| 67 | 65 | const name = try allocator.dupe(u8, mem.spanZ(dylib_name)); |
| ... | ... | @@ -164,25 +162,22 @@ pub fn createAndParseFromPath( |
| 164 | 162 | errdefer allocator.free(name); |
| 165 | 163 | |
| 166 | 164 | dylib.* = .{ |
| 167 | | .allocator = allocator, |
| 168 | | .arch = arch, |
| 169 | 165 | .name = name, |
| 170 | 166 | .file = file, |
| 171 | | .syslibroot = opts.syslibroot, |
| 172 | 167 | }; |
| 173 | 168 | |
| 174 | | dylib.parse() catch |err| switch (err) { |
| 169 | dylib.parse(allocator, arch) catch |err| switch (err) { |
| 175 | 170 | error.EndOfStream, error.NotDylib => { |
| 176 | 171 | try file.seekTo(0); |
| 177 | 172 | |
| 178 | 173 | var lib_stub = LibStub.loadFromFile(allocator, file) catch { |
| 179 | | dylib.deinit(); |
| 174 | dylib.deinit(allocator); |
| 180 | 175 | allocator.destroy(dylib); |
| 181 | 176 | return null; |
| 182 | 177 | }; |
| 183 | 178 | defer lib_stub.deinit(); |
| 184 | 179 | |
| 185 | | try dylib.parseFromStub(lib_stub); |
| 180 | try dylib.parseFromStub(allocator, arch, lib_stub); |
| 186 | 181 | }, |
| 187 | 182 | else => |e| return e, |
| 188 | 183 | }; |
| ... | ... | @@ -195,7 +190,7 @@ pub fn createAndParseFromPath( |
| 195 | 190 | log.warn(" | dylib version: {}", .{dylib.id.?.current_version}); |
| 196 | 191 | |
| 197 | 192 | // TODO maybe this should be an error and facilitate auto-cleanup? |
| 198 | | dylib.deinit(); |
| 193 | dylib.deinit(allocator); |
| 199 | 194 | allocator.destroy(dylib); |
| 200 | 195 | return null; |
| 201 | 196 | } |
| ... | ... | @@ -205,50 +200,41 @@ pub fn createAndParseFromPath( |
| 205 | 200 | defer dylibs.deinit(); |
| 206 | 201 | |
| 207 | 202 | try dylibs.append(dylib); |
| 208 | | try dylib.parseDependentLibs(&dylibs); |
| 203 | try dylib.parseDependentLibs(allocator, arch, &dylibs, opts.syslibroot); |
| 209 | 204 | |
| 210 | 205 | return dylibs.toOwnedSlice(); |
| 211 | 206 | } |
| 212 | 207 | |
| 213 | | pub fn deinit(self: *Dylib) void { |
| 208 | pub fn deinit(self: *Dylib, allocator: *Allocator) void { |
| 214 | 209 | for (self.load_commands.items) |*lc| { |
| 215 | | lc.deinit(self.allocator); |
| 210 | lc.deinit(allocator); |
| 216 | 211 | } |
| 217 | | self.load_commands.deinit(self.allocator); |
| 212 | self.load_commands.deinit(allocator); |
| 218 | 213 | |
| 219 | 214 | for (self.symbols.keys()) |key| { |
| 220 | | self.allocator.free(key); |
| 215 | allocator.free(key); |
| 221 | 216 | } |
| 222 | | self.symbols.deinit(self.allocator); |
| 217 | self.symbols.deinit(allocator); |
| 223 | 218 | |
| 224 | 219 | for (self.dependent_libs.items) |*id| { |
| 225 | | id.deinit(self.allocator); |
| 226 | | } |
| 227 | | self.dependent_libs.deinit(self.allocator); |
| 228 | | |
| 229 | | if (self.name) |name| { |
| 230 | | self.allocator.free(name); |
| 220 | id.deinit(allocator); |
| 231 | 221 | } |
| 222 | self.dependent_libs.deinit(allocator); |
| 223 | allocator.free(self.name); |
| 232 | 224 | |
| 233 | 225 | if (self.id) |*id| { |
| 234 | | id.deinit(self.allocator); |
| 226 | id.deinit(allocator); |
| 235 | 227 | } |
| 236 | 228 | } |
| 237 | 229 | |
| 238 | | pub fn closeFile(self: Dylib) void { |
| 239 | | if (self.file) |file| { |
| 240 | | file.close(); |
| 241 | | } |
| 242 | | } |
| 230 | pub fn parse(self: *Dylib, allocator: *Allocator, arch: Arch) !void { |
| 231 | log.debug("parsing shared library '{s}'", .{self.name}); |
| 243 | 232 | |
| 244 | | pub fn parse(self: *Dylib) !void { |
| 245 | | log.debug("parsing shared library '{s}'", .{self.name.?}); |
| 233 | self.library_offset = try fat.getLibraryOffset(self.file.reader(), arch); |
| 246 | 234 | |
| 247 | | self.library_offset = try fat.getLibraryOffset(self.file.?.reader(), self.arch.?); |
| 235 | try self.file.seekTo(self.library_offset); |
| 248 | 236 | |
| 249 | | try self.file.?.seekTo(self.library_offset); |
| 250 | | |
| 251 | | var reader = self.file.?.reader(); |
| 237 | var reader = self.file.reader(); |
| 252 | 238 | self.header = try reader.readStruct(macho.mach_header_64); |
| 253 | 239 | |
| 254 | 240 | if (self.header.?.filetype != macho.MH_DYLIB) { |
| ... | ... | @@ -258,24 +244,24 @@ pub fn parse(self: *Dylib) !void { |
| 258 | 244 | |
| 259 | 245 | const this_arch: Arch = try fat.decodeArch(self.header.?.cputype, true); |
| 260 | 246 | |
| 261 | | if (this_arch != self.arch.?) { |
| 262 | | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ self.arch.?, this_arch }); |
| 247 | if (this_arch != arch) { |
| 248 | log.err("mismatched cpu architecture: expected {s}, found {s}", .{ arch, this_arch }); |
| 263 | 249 | return error.MismatchedCpuArchitecture; |
| 264 | 250 | } |
| 265 | 251 | |
| 266 | | try self.readLoadCommands(reader); |
| 267 | | try self.parseId(); |
| 268 | | try self.parseSymbols(); |
| 252 | try self.readLoadCommands(allocator, reader); |
| 253 | try self.parseId(allocator); |
| 254 | try self.parseSymbols(allocator); |
| 269 | 255 | } |
| 270 | 256 | |
| 271 | | fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 257 | fn readLoadCommands(self: *Dylib, allocator: *Allocator, reader: anytype) !void { |
| 272 | 258 | const should_lookup_reexports = self.header.?.flags & macho.MH_NO_REEXPORTED_DYLIBS == 0; |
| 273 | 259 | |
| 274 | | try self.load_commands.ensureCapacity(self.allocator, self.header.?.ncmds); |
| 260 | try self.load_commands.ensureCapacity(allocator, self.header.?.ncmds); |
| 275 | 261 | |
| 276 | 262 | var i: u16 = 0; |
| 277 | 263 | while (i < self.header.?.ncmds) : (i += 1) { |
| 278 | | var cmd = try LoadCommand.read(self.allocator, reader); |
| 264 | var cmd = try LoadCommand.read(allocator, reader); |
| 279 | 265 | switch (cmd.cmd()) { |
| 280 | 266 | macho.LC_SYMTAB => { |
| 281 | 267 | self.symtab_cmd_index = i; |
| ... | ... | @@ -289,8 +275,8 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 289 | 275 | macho.LC_REEXPORT_DYLIB => { |
| 290 | 276 | if (should_lookup_reexports) { |
| 291 | 277 | // Parse install_name to dependent dylib. |
| 292 | | const id = try Id.fromLoadCommand(self.allocator, cmd.Dylib); |
| 293 | | try self.dependent_libs.append(self.allocator, id); |
| 278 | const id = try Id.fromLoadCommand(allocator, cmd.Dylib); |
| 279 | try self.dependent_libs.append(allocator, id); |
| 294 | 280 | } |
| 295 | 281 | }, |
| 296 | 282 | else => { |
| ... | ... | @@ -301,27 +287,27 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 301 | 287 | } |
| 302 | 288 | } |
| 303 | 289 | |
| 304 | | fn parseId(self: *Dylib) !void { |
| 290 | fn parseId(self: *Dylib, allocator: *Allocator) !void { |
| 305 | 291 | const index = self.id_cmd_index orelse { |
| 306 | 292 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); |
| 307 | | self.id = try Id.default(self.allocator, self.name.?); |
| 293 | self.id = try Id.default(allocator, self.name); |
| 308 | 294 | return; |
| 309 | 295 | }; |
| 310 | | self.id = try Id.fromLoadCommand(self.allocator, self.load_commands.items[index].Dylib); |
| 296 | self.id = try Id.fromLoadCommand(allocator, self.load_commands.items[index].Dylib); |
| 311 | 297 | } |
| 312 | 298 | |
| 313 | | fn parseSymbols(self: *Dylib) !void { |
| 299 | fn parseSymbols(self: *Dylib, allocator: *Allocator) !void { |
| 314 | 300 | const index = self.symtab_cmd_index orelse return; |
| 315 | 301 | const symtab_cmd = self.load_commands.items[index].Symtab; |
| 316 | 302 | |
| 317 | | var symtab = try self.allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); |
| 318 | | defer self.allocator.free(symtab); |
| 319 | | _ = try self.file.?.preadAll(symtab, symtab_cmd.symoff + self.library_offset); |
| 303 | var symtab = try allocator.alloc(u8, @sizeOf(macho.nlist_64) * symtab_cmd.nsyms); |
| 304 | defer allocator.free(symtab); |
| 305 | _ = try self.file.preadAll(symtab, symtab_cmd.symoff + self.library_offset); |
| 320 | 306 | const slice = @alignCast(@alignOf(macho.nlist_64), mem.bytesAsSlice(macho.nlist_64, symtab)); |
| 321 | 307 | |
| 322 | | var strtab = try self.allocator.alloc(u8, symtab_cmd.strsize); |
| 323 | | defer self.allocator.free(strtab); |
| 324 | | _ = try self.file.?.preadAll(strtab, symtab_cmd.stroff + self.library_offset); |
| 308 | var strtab = try allocator.alloc(u8, symtab_cmd.strsize); |
| 309 | defer allocator.free(strtab); |
| 310 | _ = try self.file.preadAll(strtab, symtab_cmd.stroff + self.library_offset); |
| 325 | 311 | |
| 326 | 312 | for (slice) |sym| { |
| 327 | 313 | const add_to_symtab = MachO.symbolIsExt(sym) and (MachO.symbolIsSect(sym) or MachO.symbolIsIndr(sym)); |
| ... | ... | @@ -329,8 +315,8 @@ fn parseSymbols(self: *Dylib) !void { |
| 329 | 315 | if (!add_to_symtab) continue; |
| 330 | 316 | |
| 331 | 317 | const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx)); |
| 332 | | const name = try self.allocator.dupe(u8, sym_name); |
| 333 | | try self.symbols.putNoClobber(self.allocator, name, {}); |
| 318 | const name = try allocator.dupe(u8, sym_name); |
| 319 | try self.symbols.putNoClobber(allocator, name, {}); |
| 334 | 320 | } |
| 335 | 321 | } |
| 336 | 322 | |
| ... | ... | @@ -341,26 +327,26 @@ fn hasTarget(targets: []const []const u8, target: []const u8) bool { |
| 341 | 327 | return false; |
| 342 | 328 | } |
| 343 | 329 | |
| 344 | | fn addObjCClassSymbols(self: *Dylib, sym_name: []const u8) !void { |
| 330 | fn addObjCClassSymbols(self: *Dylib, allocator: *Allocator, sym_name: []const u8) !void { |
| 345 | 331 | const expanded = &[_][]const u8{ |
| 346 | | try std.fmt.allocPrint(self.allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), |
| 347 | | try std.fmt.allocPrint(self.allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), |
| 332 | try std.fmt.allocPrint(allocator, "_OBJC_CLASS_$_{s}", .{sym_name}), |
| 333 | try std.fmt.allocPrint(allocator, "_OBJC_METACLASS_$_{s}", .{sym_name}), |
| 348 | 334 | }; |
| 349 | 335 | |
| 350 | 336 | for (expanded) |sym| { |
| 351 | 337 | if (self.symbols.contains(sym)) continue; |
| 352 | | try self.symbols.putNoClobber(self.allocator, sym, .{}); |
| 338 | try self.symbols.putNoClobber(allocator, sym, .{}); |
| 353 | 339 | } |
| 354 | 340 | } |
| 355 | 341 | |
| 356 | | pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 342 | pub fn parseFromStub(self: *Dylib, allocator: *Allocator, arch: Arch, lib_stub: LibStub) !void { |
| 357 | 343 | if (lib_stub.inner.len == 0) return error.EmptyStubFile; |
| 358 | 344 | |
| 359 | | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); |
| 345 | log.debug("parsing shared library from stub '{s}'", .{self.name}); |
| 360 | 346 | |
| 361 | 347 | const umbrella_lib = lib_stub.inner[0]; |
| 362 | 348 | |
| 363 | | var id = try Id.default(self.allocator, umbrella_lib.install_name); |
| 349 | var id = try Id.default(allocator, umbrella_lib.install_name); |
| 364 | 350 | if (umbrella_lib.current_version) |version| { |
| 365 | 351 | try id.parseCurrentVersion(version); |
| 366 | 352 | } |
| ... | ... | @@ -369,13 +355,13 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 369 | 355 | } |
| 370 | 356 | self.id = id; |
| 371 | 357 | |
| 372 | | const target_string: []const u8 = switch (self.arch.?) { |
| 358 | const target_string: []const u8 = switch (arch) { |
| 373 | 359 | .aarch64 => "arm64-macos", |
| 374 | 360 | .x86_64 => "x86_64-macos", |
| 375 | 361 | else => unreachable, |
| 376 | 362 | }; |
| 377 | 363 | |
| 378 | | var umbrella_libs = std.StringHashMap(void).init(self.allocator); |
| 364 | var umbrella_libs = std.StringHashMap(void).init(allocator); |
| 379 | 365 | defer umbrella_libs.deinit(); |
| 380 | 366 | |
| 381 | 367 | for (lib_stub.inner) |stub, stub_index| { |
| ... | ... | @@ -395,13 +381,13 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 395 | 381 | if (exp.symbols) |symbols| { |
| 396 | 382 | for (symbols) |sym_name| { |
| 397 | 383 | if (self.symbols.contains(sym_name)) continue; |
| 398 | | try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); |
| 384 | try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {}); |
| 399 | 385 | } |
| 400 | 386 | } |
| 401 | 387 | |
| 402 | 388 | if (exp.objc_classes) |classes| { |
| 403 | 389 | for (classes) |sym_name| { |
| 404 | | try self.addObjCClassSymbols(sym_name); |
| 390 | try self.addObjCClassSymbols(allocator, sym_name); |
| 405 | 391 | } |
| 406 | 392 | } |
| 407 | 393 | } |
| ... | ... | @@ -414,13 +400,13 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 414 | 400 | if (reexp.symbols) |symbols| { |
| 415 | 401 | for (symbols) |sym_name| { |
| 416 | 402 | if (self.symbols.contains(sym_name)) continue; |
| 417 | | try self.symbols.putNoClobber(self.allocator, try self.allocator.dupe(u8, sym_name), {}); |
| 403 | try self.symbols.putNoClobber(allocator, try allocator.dupe(u8, sym_name), {}); |
| 418 | 404 | } |
| 419 | 405 | } |
| 420 | 406 | |
| 421 | 407 | if (reexp.objc_classes) |classes| { |
| 422 | 408 | for (classes) |sym_name| { |
| 423 | | try self.addObjCClassSymbols(sym_name); |
| 409 | try self.addObjCClassSymbols(allocator, sym_name); |
| 424 | 410 | } |
| 425 | 411 | } |
| 426 | 412 | } |
| ... | ... | @@ -428,7 +414,7 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 428 | 414 | |
| 429 | 415 | if (stub.objc_classes) |classes| { |
| 430 | 416 | for (classes) |sym_name| { |
| 431 | | try self.addObjCClassSymbols(sym_name); |
| 417 | try self.addObjCClassSymbols(allocator, sym_name); |
| 432 | 418 | } |
| 433 | 419 | } |
| 434 | 420 | } |
| ... | ... | @@ -451,15 +437,21 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 451 | 437 | |
| 452 | 438 | log.debug(" | {s}", .{lib}); |
| 453 | 439 | |
| 454 | | const dep_id = try Id.default(self.allocator, lib); |
| 455 | | try self.dependent_libs.append(self.allocator, dep_id); |
| 440 | const dep_id = try Id.default(allocator, lib); |
| 441 | try self.dependent_libs.append(allocator, dep_id); |
| 456 | 442 | } |
| 457 | 443 | } |
| 458 | 444 | } |
| 459 | 445 | } |
| 460 | 446 | } |
| 461 | 447 | |
| 462 | | pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { |
| 448 | pub fn parseDependentLibs( |
| 449 | self: *Dylib, |
| 450 | allocator: *Allocator, |
| 451 | arch: Arch, |
| 452 | out: *std.ArrayList(*Dylib), |
| 453 | syslibroot: ?[]const u8, |
| 454 | ) !void { |
| 463 | 455 | outer: for (self.dependent_libs.items) |id| { |
| 464 | 456 | const has_ext = blk: { |
| 465 | 457 | const basename = fs.path.basename(id.name); |
| ... | ... | @@ -472,27 +464,27 @@ pub fn parseDependentLibs(self: *Dylib, out: *std.ArrayList(*Dylib)) !void { |
| 472 | 464 | } else id.name; |
| 473 | 465 | |
| 474 | 466 | for (&[_][]const u8{ extension, ".tbd" }) |ext| { |
| 475 | | const with_ext = try std.fmt.allocPrint(self.allocator, "{s}{s}", .{ |
| 467 | const with_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ |
| 476 | 468 | without_ext, |
| 477 | 469 | ext, |
| 478 | 470 | }); |
| 479 | | defer self.allocator.free(with_ext); |
| 471 | defer allocator.free(with_ext); |
| 480 | 472 | |
| 481 | | const full_path = if (self.syslibroot) |syslibroot| |
| 482 | | try fs.path.join(self.allocator, &.{ syslibroot, with_ext }) |
| 473 | const full_path = if (syslibroot) |root| |
| 474 | try fs.path.join(allocator, &.{ root, with_ext }) |
| 483 | 475 | else |
| 484 | 476 | with_ext; |
| 485 | | defer if (self.syslibroot) |_| self.allocator.free(full_path); |
| 477 | defer if (syslibroot) |_| allocator.free(full_path); |
| 486 | 478 | |
| 487 | 479 | log.debug("trying dependency at fully resolved path {s}", .{full_path}); |
| 488 | 480 | |
| 489 | 481 | const dylibs = (try createAndParseFromPath( |
| 490 | | self.allocator, |
| 491 | | self.arch.?, |
| 482 | allocator, |
| 483 | arch, |
| 492 | 484 | full_path, |
| 493 | 485 | .{ |
| 494 | 486 | .id = id, |
| 495 | | .syslibroot = self.syslibroot, |
| 487 | .syslibroot = syslibroot, |
| 496 | 488 | }, |
| 497 | 489 | )) orelse { |
| 498 | 490 | continue; |