| ... | @@ -3,8 +3,10 @@ const Dylib = @This(); | ... | @@ -3,8 +3,10 @@ const Dylib = @This(); |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const fs = std.fs; | 5 | const fs = std.fs; |
| | 6 | const fmt = std.fmt; |
| 6 | const log = std.log.scoped(.dylib); | 7 | const log = std.log.scoped(.dylib); |
| 7 | const macho = std.macho; | 8 | const macho = std.macho; |
| | 9 | const math = std.math; |
| 8 | const mem = std.mem; | 10 | const mem = std.mem; |
| 9 | | 11 | |
| 10 | const Allocator = mem.Allocator; | 12 | const Allocator = mem.Allocator; |
| ... | @@ -37,6 +39,7 @@ id: ?Id = null, | ... | @@ -37,6 +39,7 @@ id: ?Id = null, |
| 37 | /// a symbol is referenced by an object file. | 39 | /// a symbol is referenced by an object file. |
| 38 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, | 40 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 39 | | 41 | |
| | 42 | // TODO add parsing re-exported libs from binary dylibs |
| 40 | dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, | 43 | dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 41 | | 44 | |
| 42 | pub const Id = struct { | 45 | pub const Id = struct { |
| ... | @@ -45,9 +48,72 @@ pub const Id = struct { | ... | @@ -45,9 +48,72 @@ pub const Id = struct { |
| 45 | current_version: u32, | 48 | current_version: u32, |
| 46 | compatibility_version: u32, | 49 | compatibility_version: u32, |
| 47 | | 50 | |
| | 51 | pub fn default(name: []const u8) Id { |
| | 52 | return .{ |
| | 53 | .name = name, |
| | 54 | .timestamp = 2, |
| | 55 | .current_version = 0x10000, |
| | 56 | .compatibility_version = 0x10000, |
| | 57 | }; |
| | 58 | } |
| | 59 | |
| 48 | pub fn deinit(id: *Id, allocator: *Allocator) void { | 60 | pub fn deinit(id: *Id, allocator: *Allocator) void { |
| 49 | allocator.free(id.name); | 61 | allocator.free(id.name); |
| 50 | } | 62 | } |
| | 63 | |
| | 64 | const ParseError = fmt.ParseIntError || fmt.BufPrintError; |
| | 65 | |
| | 66 | pub fn parseCurrentVersion(id: *Id, version: anytype) ParseError!void { |
| | 67 | id.current_version = try parseVersion(version); |
| | 68 | } |
| | 69 | |
| | 70 | pub fn parseCompatibilityVersion(id: *Id, version: anytype) ParseError!void { |
| | 71 | id.compatibility_version = try parseVersion(version); |
| | 72 | } |
| | 73 | |
| | 74 | fn parseVersion(version: anytype) ParseError!u32 { |
| | 75 | const string = blk: { |
| | 76 | switch (version) { |
| | 77 | .int => |int| { |
| | 78 | var out: u32 = 0; |
| | 79 | const major = try math.cast(u16, int); |
| | 80 | out += @intCast(u32, major) << 16; |
| | 81 | return out; |
| | 82 | }, |
| | 83 | .float => |float| { |
| | 84 | var buf: [256]u8 = undefined; |
| | 85 | break :blk try fmt.bufPrint(&buf, "{d:.2}", .{float}); |
| | 86 | }, |
| | 87 | .string => |string| { |
| | 88 | break :blk string; |
| | 89 | }, |
| | 90 | } |
| | 91 | }; |
| | 92 | |
| | 93 | var out: u32 = 0; |
| | 94 | var values: [3][]const u8 = undefined; |
| | 95 | |
| | 96 | var split = mem.split(string, "."); |
| | 97 | var i: u4 = 0; |
| | 98 | while (split.next()) |value| { |
| | 99 | if (i > 2) { |
| | 100 | log.warn("malformed version field: {s}", .{string}); |
| | 101 | return 0x10000; |
| | 102 | } |
| | 103 | values[i] = value; |
| | 104 | i += 1; |
| | 105 | } |
| | 106 | |
| | 107 | if (values.len > 2) { |
| | 108 | out += try fmt.parseInt(u8, values[2], 10); |
| | 109 | } |
| | 110 | if (values.len > 1) { |
| | 111 | out += @intCast(u32, try fmt.parseInt(u8, values[1], 10)) << 8; |
| | 112 | } |
| | 113 | out += @intCast(u32, try fmt.parseInt(u16, values[0], 10)) << 16; |
| | 114 | |
| | 115 | return out; |
| | 116 | } |
| 51 | }; | 117 | }; |
| 52 | | 118 | |
| 53 | pub const Error = error{ | 119 | pub const Error = error{ |
| ... | @@ -55,7 +121,7 @@ pub const Error = error{ | ... | @@ -55,7 +121,7 @@ pub const Error = error{ |
| 55 | EmptyStubFile, | 121 | EmptyStubFile, |
| 56 | MismatchedCpuArchitecture, | 122 | MismatchedCpuArchitecture, |
| 57 | UnsupportedCpuArchitecture, | 123 | UnsupportedCpuArchitecture, |
| 58 | } || fs.File.OpenError || std.os.PReadError; | 124 | } || fs.File.OpenError || std.os.PReadError || Id.ParseError; |
| 59 | | 125 | |
| 60 | pub fn createAndParseFromPath( | 126 | pub fn createAndParseFromPath( |
| 61 | allocator: *Allocator, | 127 | allocator: *Allocator, |
| ... | @@ -195,12 +261,7 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { | ... | @@ -195,12 +261,7 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 195 | fn parseId(self: *Dylib) !void { | 261 | fn parseId(self: *Dylib) !void { |
| 196 | const index = self.id_cmd_index orelse { | 262 | const index = self.id_cmd_index orelse { |
| 197 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); | 263 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); |
| 198 | self.id = .{ | 264 | self.id = Id.default(try self.allocator.dupe(u8, self.name.?)); |
| 199 | .name = try self.allocator.dupe(u8, self.name.?), | | |
| 200 | .timestamp = 2, | | |
| 201 | .current_version = 0, | | |
| 202 | .compatibility_version = 0, | | |
| 203 | }; | | |
| 204 | return; | 265 | return; |
| 205 | }; | 266 | }; |
| 206 | const id_cmd = self.load_commands.items[index].Dylib; | 267 | const id_cmd = self.load_commands.items[index].Dylib; |
| ... | @@ -266,13 +327,15 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { | ... | @@ -266,13 +327,15 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 266 | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); | 327 | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); |
| 267 | | 328 | |
| 268 | const umbrella_lib = lib_stub.inner[0]; | 329 | const umbrella_lib = lib_stub.inner[0]; |
| 269 | self.id = .{ | 330 | |
| 270 | .name = try self.allocator.dupe(u8, umbrella_lib.install_name), | 331 | var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name)); |
| 271 | // TODO parse from the stub | 332 | if (umbrella_lib.current_version) |version| { |
| 272 | .timestamp = 2, | 333 | try id.parseCurrentVersion(version); |
| 273 | .current_version = 0, | 334 | } |
| 274 | .compatibility_version = 0, | 335 | if (umbrella_lib.compatibility_version) |version| { |
| 275 | }; | 336 | try id.parseCompatibilityVersion(version); |
| | 337 | } |
| | 338 | self.id = id; |
| 276 | | 339 | |
| 277 | const target_string: []const u8 = switch (self.arch.?) { | 340 | const target_string: []const u8 = switch (self.arch.?) { |
| 278 | .aarch64 => "arm64-macos", | 341 | .aarch64 => "arm64-macos", |