Compare commits

...

2 Commits

Author SHA1 Message Date
4382dda192 Merge pull request 'PATH lookup' (#1) from path_lookup into main
Reviewed-on: #1
2025-10-20 08:31:03 +00:00
87dbba3b9c PATH lookup 2025-10-20 10:29:32 +02:00
2 changed files with 36 additions and 6 deletions

View File

@@ -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

View File

@@ -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 {