Skip to content

Commit 5d7db76

Browse files
committed
Remove duplicate dylibs
This is a loader hard-error now to load the same library multiple times since macOS 15.
1 parent 58cd472 commit 5d7db76

File tree

3 files changed

+63
-0
lines changed

3 files changed

+63
-0
lines changed

src/Dylib.zig

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,12 @@ pub fn isAlive(self: Dylib, macho_file: *MachO) bool {
658658
return self.referenced or self.needed;
659659
}
660660

661+
pub fn markDead(self: *Dylib) void {
662+
self.referenced = false;
663+
self.needed = false;
664+
self.explicit = false;
665+
}
666+
661667
pub fn markReferenced(self: *Dylib, macho_file: *MachO) void {
662668
const tracy = trace(@src());
663669
defer tracy.end();
@@ -1011,6 +1017,22 @@ pub const Id = struct {
10111017

10121018
return out;
10131019
}
1020+
1021+
pub fn eql(id: Id, other: Id) bool {
1022+
return mem.eql(u8, id.name, other.name) and
1023+
id.timestamp == other.timestamp and
1024+
id.current_version == other.current_version and
1025+
id.compatibility_version == other.compatibility_version;
1026+
}
1027+
1028+
pub fn hash(id: Id) u64 {
1029+
var hasher = std.hash.Wyhash.init(0);
1030+
hasher.update(id.name);
1031+
hasher.update(mem.asBytes(&id.timestamp));
1032+
hasher.update(mem.asBytes(&id.current_version));
1033+
hasher.update(mem.asBytes(&id.compatibility_version));
1034+
return hasher.final();
1035+
}
10141036
};
10151037

10161038
const Export = struct {

src/MachO.zig

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ pub fn link(self: *MachO) !void {
249249
}
250250

251251
try self.parseInputFiles();
252+
try self.dedupDylibs(resolved_objects.items);
252253
try self.parseDependentDylibs(arena, lib_dirs.items, framework_dirs.items);
253254

254255
if (!self.options.relocatable) {
@@ -832,6 +833,41 @@ fn isHoisted(self: *MachO, install_name: []const u8) bool {
832833
return false;
833834
}
834835

836+
fn dedupDylibs(self: *MachO, resolved_objects: []const LinkObject) !void {
837+
const tracy = trace(@src());
838+
defer tracy.end();
839+
840+
var map = std.HashMap(Dylib.Id, void, struct {
841+
pub fn hash(ctx: @This(), id: Dylib.Id) u64 {
842+
_ = ctx;
843+
return id.hash();
844+
}
845+
846+
pub fn eql(ctx: @This(), id: Dylib.Id, other: Dylib.Id) bool {
847+
_ = ctx;
848+
return id.eql(other);
849+
}
850+
}, std.hash_map.default_max_load_percentage).init(self.allocator);
851+
defer map.deinit();
852+
try map.ensureTotalCapacity(@intCast(self.dylibs.items.len));
853+
854+
for (self.dylibs.items) |index| {
855+
const dylib = self.getFile(index).dylib;
856+
const cmd_object = resolved_objects[@intFromEnum(index)];
857+
858+
const gop = map.getOrPutAssumeCapacity(dylib.id.?);
859+
860+
if (!gop.found_existing) continue;
861+
if (cmd_object.tag == .lib) {
862+
self.warn("ignoring duplicate libraries: {}", .{cmd_object});
863+
}
864+
865+
dylib.markDead();
866+
}
867+
868+
try self.removeDylibs();
869+
}
870+
835871
fn parseDependentDylibs(
836872
self: *MachO,
837873
arena: Allocator,
@@ -1036,6 +1072,10 @@ fn deadStripDylibs(self: *MachO) !void {
10361072
self.getFile(index).dylib.markReferenced(self);
10371073
}
10381074

1075+
try self.removeDylibs();
1076+
}
1077+
1078+
fn removeDylibs(self: *MachO) !void {
10391079
var stripped = std.AutoHashMap(File.Index, void).init(self.allocator);
10401080
defer stripped.deinit();
10411081
try stripped.ensureTotalCapacity(@intCast(self.dylibs.items.len));

test/macho.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ fn testDedupDylibs(b: *Build, opts: Options) *Step {
407407
"-install_name",
408408
"@rpath/liba.dylib",
409409
"-lSystem",
410+
"-lSystem",
410411
"-lc",
411412
});
412413

0 commit comments

Comments
 (0)