std.dynamic_library: use a global static single threaded Io

See #30150
This commit is contained in:
Andrew Kelley 2025-12-18 21:32:34 -08:00
parent 8632a28ca9
commit 21d0264c61
7 changed files with 41 additions and 41 deletions

View file

@ -684,7 +684,7 @@ const Os = switch (builtin.os.tag) {
path.root_dir.handle.handle
else
posix.openat(path.root_dir.handle.handle, path.sub_path, dir_open_flags, 0) catch |err| {
fatal("failed to open directory {f}: {s}", .{ path, @errorName(err) });
fatal("failed to open directory {f}: {t}", .{ path, err });
};
// Empirically the dir has to stay open or else no events are triggered.
errdefer if (!skip_open_dir) posix.close(dir_fd);

View file

@ -43,7 +43,9 @@ pub const Error = error{
/// In WASI, this error occurs when the file descriptor does
/// not hold the required rights to read from it.
AccessDenied,
/// Unable to read file due to lock.
/// Unable to read file due to lock. Depending on the `Io` implementation,
/// reading from a locked file may return this error, or may ignore the
/// lock.
LockViolation,
} || Io.Cancelable || Io.UnexpectedError;

View file

@ -2838,7 +2838,7 @@ test "discarding sendFile" {
const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
defer file.close(io);
var r_buffer: [256]u8 = undefined;
var file_writer: File.Writer = .init(file, &r_buffer);
var file_writer: File.Writer = .init(file, io, &r_buffer);
try file_writer.interface.writeByte('h');
try file_writer.interface.flush();
@ -2860,7 +2860,7 @@ test "allocating sendFile" {
const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
defer file.close(io);
var r_buffer: [2]u8 = undefined;
var file_writer: File.Writer = .init(file, &r_buffer);
var file_writer: File.Writer = .init(file, io, &r_buffer);
try file_writer.interface.writeAll("abcd");
try file_writer.interface.flush();
@ -2884,7 +2884,7 @@ test sendFileReading {
const file = try tmp_dir.dir.createFile(io, "input.txt", .{ .read = true });
defer file.close(io);
var r_buffer: [2]u8 = undefined;
var file_writer: File.Writer = .init(file, &r_buffer);
var file_writer: File.Writer = .init(file, io, &r_buffer);
try file_writer.interface.writeAll("abcd");
try file_writer.interface.flush();

View file

@ -1610,14 +1610,8 @@ test "manage resources correctly" {
var discarding: Writer.Discarding = .init(&.{});
var di: SelfInfo = .init;
defer di.deinit(gpa);
try printSourceAtAddress(
gpa,
io,
&di,
&discarding.writer,
S.showMyTrace(),
.no_color,
);
const t: Io.Terminal = .{ .writer = &discarding.writer, .mode = .no_color };
try printSourceAtAddress(gpa, io, &di, t, S.showMyTrace());
}
/// This API helps you track where a value originated and where it was mutated,

View file

@ -55,11 +55,11 @@ pub const DynLib = struct {
// An iterator is provided in order to traverse the linked list in a idiomatic
// fashion.
const LinkMap = extern struct {
l_addr: usize,
l_name: [*:0]const u8,
l_ld: ?*elf.Dyn,
l_next: ?*LinkMap,
l_prev: ?*LinkMap,
addr: usize,
name: [*:0]const u8,
ld: ?*elf.Dyn,
next: ?*LinkMap,
prev: ?*LinkMap,
pub const Iterator = struct {
current: ?*LinkMap,
@ -70,7 +70,7 @@ const LinkMap = extern struct {
pub fn next(self: *Iterator) ?*LinkMap {
if (self.current) |it| {
self.current = it.l_next;
self.current = it.next;
return it;
}
return null;
@ -79,10 +79,10 @@ const LinkMap = extern struct {
};
const RDebug = extern struct {
r_version: i32,
r_map: ?*LinkMap,
r_brk: usize,
r_ldbase: usize,
version: i32,
map: ?*LinkMap,
brk: usize,
ldbase: usize,
};
/// TODO fix comparisons of extern symbol pointers so we don't need this helper function.
@ -107,8 +107,8 @@ pub fn linkmap_iterator() error{InvalidExe}!LinkMap.Iterator {
elf.DT_DEBUG => {
const ptr = @as(?*RDebug, @ptrFromInt(_DYNAMIC[i].d_val));
if (ptr) |r_debug| {
if (r_debug.r_version != 1) return error.InvalidExe;
break :init r_debug.r_map;
if (r_debug.version != 1) return error.InvalidExe;
break :init r_debug.map;
}
},
elf.DT_PLTGOT => {
@ -142,6 +142,8 @@ const ElfDynLibError = error{
Streaming,
} || posix.OpenError || posix.MMapError;
var static_single_threaded_io: Io.Threaded = .init_single_threaded;
pub const ElfDynLib = struct {
strings: [*:0]u8,
syms: [*]elf.Sym,
@ -157,7 +159,7 @@ pub const ElfDynLib = struct {
dt_gnu_hash: *elf.gnu_hash.Header,
};
fn openPath(path: []const u8, io: Io) !Io.Dir {
fn openPath(io: Io, path: []const u8) !Io.Dir {
if (path.len == 0) return error.NotDir;
var parts = std.mem.tokenizeScalar(u8, path, '/');
var parent = if (path[0] == '/') try Io.Dir.cwd().openDir(io, "/", .{}) else Io.Dir.cwd();
@ -172,7 +174,7 @@ pub const ElfDynLib = struct {
fn resolveFromSearchPath(io: Io, search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {
var paths = std.mem.tokenizeScalar(u8, search_path, delim);
while (paths.next()) |p| {
var dir = openPath(p) catch continue;
var dir = openPath(io, p) catch continue;
defer dir.close(io);
const fd = posix.openat(dir.handle, file_name, .{
.ACCMODE = .RDONLY,
@ -221,7 +223,9 @@ pub const ElfDynLib = struct {
}
/// Trusts the file. Malicious file will be able to execute arbitrary code.
pub fn open(io: Io, path: []const u8) Error!ElfDynLib {
pub fn open(path: []const u8) Error!ElfDynLib {
const io = static_single_threaded_io.ioBasic();
const fd = try resolveFromName(io, path);
defer posix.close(fd);
@ -551,11 +555,9 @@ fn checkver(def_arg: *elf.Verdef, vsym_arg: elf.Versym, vername: []const u8, str
}
test "ElfDynLib" {
if (native_os != .linux) {
return error.SkipZigTest;
}
if (native_os != .linux) return error.SkipZigTest;
try testing.expectError(error.FileNotFound, ElfDynLib.open("invalid_so.so"));
try testing.expectError(error.FileNotFound, ElfDynLib.openZ("invalid_so.so"));
}
/// Separated to avoid referencing `WindowsDynLib`, because its field types may not

View file

@ -1796,7 +1796,7 @@ test "read from locked file" {
const f = try ctx.dir.createFile(io, filename, .{ .read = true });
defer f.close(io);
var buffer: [1]u8 = undefined;
_ = try f.read(&buffer);
_ = try f.readPositional(io, &.{&buffer}, 0);
}
{
const f = try ctx.dir.createFile(io, filename, .{
@ -1806,11 +1806,13 @@ test "read from locked file" {
defer f.close(io);
const f2 = try ctx.dir.openFile(io, filename, .{});
defer f2.close(io);
// On POSIX locks may be ignored, however on Windows they cause
// LockViolation.
var buffer: [1]u8 = undefined;
if (builtin.os.tag == .windows) {
try expectError(error.LockViolation, f2.read(&buffer));
try expectError(error.LockViolation, f2.readPositional(io, &.{&buffer}, 0));
} else {
try expectEqual(0, f2.read(&buffer));
try expectEqual(0, f2.readPositional(io, &.{&buffer}, 0));
}
}
}

View file

@ -777,7 +777,7 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: O, mode: mode_t) O
.NFILE => return error.SystemFdQuotaExceeded,
.NODEV => return error.NoDevice,
.NOENT => return error.FileNotFound,
.SRCH => return error.ProcessNotFound,
.SRCH => return error.FileNotFound,
.NOMEM => return error.SystemResources,
.NOSPC => return error.NoSpaceLeft,
.NOTDIR => return error.NotDir,
@ -2759,16 +2759,16 @@ pub fn dl_iterate_phdr(
// Last return value from the callback function.
while (it.next()) |entry| {
const phdrs: []elf.ElfN.Phdr = if (entry.l_addr != 0) phdrs: {
const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.l_addr);
const phdrs: []elf.ElfN.Phdr = if (entry.addr != 0) phdrs: {
const ehdr: *elf.ElfN.Ehdr = @ptrFromInt(entry.addr);
assert(mem.eql(u8, ehdr.ident[0..4], elf.MAGIC));
const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.l_addr + ehdr.phoff);
const phdrs: [*]elf.ElfN.Phdr = @ptrFromInt(entry.addr + ehdr.phoff);
break :phdrs phdrs[0..ehdr.phnum];
} else getSelfPhdrs();
var info: dl_phdr_info = .{
.addr = entry.l_addr,
.name = entry.l_name,
.addr = entry.addr,
.name = entry.name,
.phdr = phdrs.ptr,
.phnum = @intCast(phdrs.len),
};