From 7ad63f2e5bf3d93e27a5233dcdcc341a5df1ebbe Mon Sep 17 00:00:00 2001 From: vlkrs Date: Sat, 11 Jul 2026 22:51:56 +0200 Subject: [PATCH 1/2] link: Find versioned shared libraries on OpenBSD See https://github.com/ziglang/zig/pull/18475 for a prior attempt at implementing this --- src/link.zig | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/link.zig b/src/link.zig index 15d9fcb513a8b329d6ebfbc6cf66d92b02352bc8..8278be5eac7af60512123b66cb6883ae3fa3cf24 100644 --- a/src/link.zig +++ b/src/link.zig @@ -2239,6 +2239,63 @@ fn resolveLibInput( return finishResolveLibInput(io, resolved_inputs, archive_dedup, test_path, file, link_mode, name_query.query); } + // In the case of OpenBSD, dynamic libraries are always versioned, without + // unversioned symlinks, so we need to look for the highest-versioned shared + // library. + if (target.isOpenBSDLibC() and link_mode == .dynamic) versioned: { + const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name}); + + var dir = lib_directory.handle.openDir(io, ".", .{ .iterate = true }) catch |err| switch (err) { + error.NotDir, error.FileNotFound => break :versioned, + else => |e| fatal("unable to search for shared library '{s}.*': {s}", .{ prefix, @errorName(e) }), + }; + defer dir.close(io); + + var best_match_version = std.SemanticVersion{ + .major = 0, + .minor = 0, + .patch = 0, + }; + var best_match: ?[]const u8 = null; + + var iter = dir.iterate(); + while (iter.next(io) catch |err| { + fatal("unable to scan library directory '{s}'", .{@errorName(err)}); + }) |entry| { + if (entry.kind != .file) continue; + if (!std.mem.startsWith(u8, entry.name, prefix)) continue; + + const rest = entry.name[prefix.len..]; + var sit = std.mem.splitScalar(u8, rest, '.'); + const major_str = sit.next() orelse continue; + const minor_str = sit.next() orelse continue; + if (sit.next() != null) continue; + const major = std.fmt.parseInt(usize, major_str, 10) catch continue; + const minor = std.fmt.parseInt(usize, minor_str, 10) catch continue; + + if (major > best_match_version.major or (major == best_match_version.major and minor >= best_match_version.minor)) { + best_match_version.major = major; + best_match_version.minor = minor; + best_match = try arena.dupe(u8, entry.name); + } + } + + if (best_match) |found| { + const test_path: Path = .{ + .root_dir = lib_directory, + .sub_path = found, + }; + try checked_paths.print(gpa, "\n {f}", .{test_path}); + switch (try resolvePathInputLib(gpa, arena, io, unresolved_inputs, resolved_inputs, ld_script_bytes, archive_dedup, target, .{ + .path = test_path, + .query = name_query.query, + }, link_mode, color)) { + .no_match => {}, + .ok => return .ok, + } + } + } + return .no_match; } -- 2.54.0 From d3f6408a418e459a0b0f77d63e05e311c4ea71e8 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Thu, 30 Jul 2026 17:48:35 +0200 Subject: [PATCH 2/2] link: simplify OpenBSD versioned shlib search And add a bit more detail about the intent in the comment. --- src/link.zig | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/link.zig b/src/link.zig index 8278be5eac7af60512123b66cb6883ae3fa3cf24..58534f63f8b6e2e2840201eebbe4fd69af07ac33 100644 --- a/src/link.zig +++ b/src/link.zig @@ -2240,8 +2240,8 @@ fn resolveLibInput( } // In the case of OpenBSD, dynamic libraries are always versioned, without - // unversioned symlinks, so we need to look for the highest-versioned shared - // library. + // unversioned symlinks. OpenBSD patches LLD to select the highest-versioned + // shared library, and this code is intended to match that upstream behavior. if (target.isOpenBSDLibC() and link_mode == .dynamic) versioned: { const prefix = try std.fmt.allocPrint(arena, "lib{s}.so.", .{lib_name}); @@ -2251,11 +2251,8 @@ fn resolveLibInput( }; defer dir.close(io); - var best_match_version = std.SemanticVersion{ - .major = 0, - .minor = 0, - .patch = 0, - }; + var best_match_major: u32 = 0; + var best_match_minor: u32 = 0; var best_match: ?[]const u8 = null; var iter = dir.iterate(); @@ -2270,12 +2267,12 @@ fn resolveLibInput( const major_str = sit.next() orelse continue; const minor_str = sit.next() orelse continue; if (sit.next() != null) continue; - const major = std.fmt.parseInt(usize, major_str, 10) catch continue; - const minor = std.fmt.parseInt(usize, minor_str, 10) catch continue; + const major = std.fmt.parseInt(u32, major_str, 10) catch continue; + const minor = std.fmt.parseInt(u32, minor_str, 10) catch continue; - if (major > best_match_version.major or (major == best_match_version.major and minor >= best_match_version.minor)) { - best_match_version.major = major; - best_match_version.minor = minor; + if (major > best_match_major or (major == best_match_major and minor >= best_match_minor)) { + best_match_major = major; + best_match_minor = minor; best_match = try arena.dupe(u8, entry.name); } } -- 2.54.0