| ... | ... | @@ -3,8 +3,10 @@ const Dylib = @This(); |
| 3 | 3 | const std = @import("std"); |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const fs = std.fs; |
| 6 | const fmt = std.fmt; |
| 6 | 7 | const log = std.log.scoped(.dylib); |
| 7 | 8 | const macho = std.macho; |
| 9 | const math = std.math; |
| 8 | 10 | const mem = std.mem; |
| 9 | 11 | |
| 10 | 12 | const Allocator = mem.Allocator; |
| ... | ... | @@ -37,6 +39,7 @@ id: ?Id = null, |
| 37 | 39 | /// a symbol is referenced by an object file. |
| 38 | 40 | symbols: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 39 | 41 | |
| 42 | // TODO add parsing re-exported libs from binary dylibs |
| 40 | 43 | dependent_libs: std.StringArrayHashMapUnmanaged(void) = .{}, |
| 41 | 44 | |
| 42 | 45 | pub const Id = struct { |
| ... | ... | @@ -45,9 +48,72 @@ pub const Id = struct { |
| 45 | 48 | current_version: u32, |
| 46 | 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 | 60 | pub fn deinit(id: *Id, allocator: *Allocator) void { |
| 49 | 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 | 119 | pub const Error = error{ |
| ... | ... | @@ -55,7 +121,7 @@ pub const Error = error{ |
| 55 | 121 | EmptyStubFile, |
| 56 | 122 | MismatchedCpuArchitecture, |
| 57 | 123 | UnsupportedCpuArchitecture, |
| 58 | | } || fs.File.OpenError || std.os.PReadError; |
| 124 | } || fs.File.OpenError || std.os.PReadError || Id.ParseError; |
| 59 | 125 | |
| 60 | 126 | pub fn createAndParseFromPath( |
| 61 | 127 | allocator: *Allocator, |
| ... | ... | @@ -195,12 +261,7 @@ fn readLoadCommands(self: *Dylib, reader: anytype) !void { |
| 195 | 261 | fn parseId(self: *Dylib) !void { |
| 196 | 262 | const index = self.id_cmd_index orelse { |
| 197 | 263 | log.debug("no LC_ID_DYLIB load command found; using hard-coded defaults...", .{}); |
| 198 | | self.id = .{ |
| 199 | | .name = try self.allocator.dupe(u8, self.name.?), |
| 200 | | .timestamp = 2, |
| 201 | | .current_version = 0, |
| 202 | | .compatibility_version = 0, |
| 203 | | }; |
| 264 | self.id = Id.default(try self.allocator.dupe(u8, self.name.?)); |
| 204 | 265 | return; |
| 205 | 266 | }; |
| 206 | 267 | const id_cmd = self.load_commands.items[index].Dylib; |
| ... | ... | @@ -266,13 +327,15 @@ pub fn parseFromStub(self: *Dylib, lib_stub: LibStub) !void { |
| 266 | 327 | log.debug("parsing shared library from stub '{s}'", .{self.name.?}); |
| 267 | 328 | |
| 268 | 329 | const umbrella_lib = lib_stub.inner[0]; |
| 269 | | self.id = .{ |
| 270 | | .name = try self.allocator.dupe(u8, umbrella_lib.install_name), |
| 271 | | // TODO parse from the stub |
| 272 | | .timestamp = 2, |
| 273 | | .current_version = 0, |
| 274 | | .compatibility_version = 0, |
| 275 | | }; |
| 330 | |
| 331 | var id = Id.default(try self.allocator.dupe(u8, umbrella_lib.install_name)); |
| 332 | if (umbrella_lib.current_version) |version| { |
| 333 | try id.parseCurrentVersion(version); |
| 334 | } |
| 335 | if (umbrella_lib.compatibility_version) |version| { |
| 336 | try id.parseCompatibilityVersion(version); |
| 337 | } |
| 338 | self.id = id; |
| 276 | 339 | |
| 277 | 340 | const target_string: []const u8 = switch (self.arch.?) { |
| 278 | 341 | .aarch64 => "arm64-macos", |