From 87dbba3b9c321569f05ccc6913836087c233e0f8 Mon Sep 17 00:00:00 2001 From: Pascal Zittlau Date: Mon, 20 Oct 2025 10:29:32 +0200 Subject: [PATCH] PATH lookup --- README.md | 6 ++++++ src/main.zig | 36 ++++++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6d69c5e..3fe9896 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ zig build Alternatively use something like this to run directly: ```sh zig build run -- /bin/ls +# or +zig build run -- ls ``` This runs the tests: @@ -44,6 +46,10 @@ passed through to the target executable. # Run a test executable that prints its arguments ./zig-out/bin/loader ./zig-out/bin/test_nolibc_pie_printArgs foo bar baz # Output: ./zig-out/bin/test_nolibc_pie_printArgs foo bar baz + +# Run echo +./zig-out/bin/loader echo Hello There +# Output: Hello There ``` ## License diff --git a/src/main.zig b/src/main.zig index 80fd880..2960bba 100644 --- a/src/main.zig +++ b/src/main.zig @@ -39,12 +39,8 @@ pub fn main() !void { return; } - // TODO: maybe search for the file in PATH // Map file into memory - const file = try std.fs.cwd().openFile( - mem.sliceTo(std.os.argv[arg_index], 0), - .{ .mode = .read_only }, - ); + const file = try lookupFile(mem.sliceTo(std.os.argv[arg_index], 0)); var buffer: [128]u8 = undefined; var file_reader = file.reader(&buffer); log.info("--- Loading executable: {s} ---", .{std.os.argv[arg_index]}); @@ -73,13 +69,15 @@ pub fn main() !void { }; // We don't need the file anymore. But we reuse the buffer if we need to load the interpreter. - // Therefore deinit everything. + // Therefore deinit everything to make sure we don't use it anymore. file_reader = undefined; file.close(); var maybe_interp_base: ?usize = null; var maybe_interp_entry: ?usize = null; if (maybe_interp) |interp| { + // TODO: If we have an interpreter we could/should unload the elf file because it will be + // loaded by the dynamic linker anyway. log.info("--- Loading interpreter ---", .{}); var interp_reader = interp.reader(&buffer); const interp_ehdr = try elf.Header.read(&interp_reader.interface); @@ -221,6 +219,32 @@ fn elfToMmapProt(elf_prot: u64) u32 { return result; } +/// Opens the file by either opening via a (absolute or relative) path or searching through `PATH` +/// for a file with the name. +fn lookupFile(path_or_name: []const u8) !std.fs.File { + // If filename contains a slash ("/"), then it is interpreted as a pathname. + if (std.mem.indexOfScalarPos(u8, path_or_name, 0, '/')) |_| { + const fd = try posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0); + return .{ .handle = fd }; + } + + // If it has no slash we need to look it up in PATH. + if (posix.getenvZ("PATH")) |env_path| { + var paths = std.mem.tokenizeScalar(u8, env_path, ':'); + while (paths.next()) |p| { + var dir = std.fs.openDirAbsolute(p, .{}) catch continue; + defer dir.close(); + const fd = posix.openat(dir.fd, path_or_name, .{ + .ACCMODE = .RDONLY, + .CLOEXEC = true, + }, 0) catch continue; + return .{ .handle = fd }; + } + } + + return error.FileNotFound; +} + /// This function performs the final jump into the loaded program (amd64) // TODO: support more architectures fn trampoline(entry: usize, sp: [*]usize) noreturn {