Compare commits

..

3 Commits

Author SHA1 Message Date
c545a8f2bb minor 2025-12-01 16:56:19 +01:00
ba6ab39f91 simplify mapping 2025-12-01 15:41:27 +01:00
53522a6e5d remove unnecessary logging 2025-12-01 15:40:47 +01:00
2 changed files with 10 additions and 48 deletions

View File

@@ -32,6 +32,8 @@ pub fn compileTestApplications(
.pic = pie, .pic = pie,
}), }),
.linkage = if (link_libc) .dynamic else .static, .linkage = if (link_libc) .dynamic else .static,
.use_llvm = true,
.use_lld = true,
}); });
test_executable.pie = pie; test_executable.pie = pie;
b.installArtifact(test_executable); b.installArtifact(test_executable);

View File

@@ -1,4 +1,3 @@
const faller = @import("faller");
const std = @import("std"); const std = @import("std");
const elf = std.elf; const elf = std.elf;
@@ -6,11 +5,7 @@ const mem = std.mem;
const posix = std.posix; const posix = std.posix;
const testing = std.testing; const testing = std.testing;
pub const faller_options: faller.Options = .{ .tags_disabled = &.{.debug} };
const Logger = faller.Logger(&.{.loader});
const assert = std.debug.assert; const assert = std.debug.assert;
const log = Logger.log;
const page_size = std.heap.pageSize(); const page_size = std.heap.pageSize();
const max_interp_path_length = 128; const max_interp_path_length = 128;
@@ -45,11 +40,9 @@ pub fn main() !void {
const file = try lookupFile(mem.sliceTo(std.os.argv[arg_index], 0)); const file = try lookupFile(mem.sliceTo(std.os.argv[arg_index], 0));
var file_buffer: [128]u8 = undefined; var file_buffer: [128]u8 = undefined;
var file_reader = file.reader(&file_buffer); var file_reader = file.reader(&file_buffer);
log(.info, "--- Loading executable: {s} ---", .{std.os.argv[arg_index]});
const ehdr = try elf.Header.read(&file_reader.interface); const ehdr = try elf.Header.read(&file_reader.interface);
const base = try loadStaticElf(ehdr, &file_reader); const base = try loadStaticElf(ehdr, &file_reader);
const entry = ehdr.entry + if (ehdr.type == .DYN) base else 0; const entry = ehdr.entry + if (ehdr.type == .DYN) base else 0;
log(.info, "Executable loaded: base=0x{x}, entry=0x{x}", .{ base, entry });
// Check for dynamic linker // Check for dynamic linker
var maybe_interp_base: ?usize = null; var maybe_interp_base: ?usize = null;
@@ -63,13 +56,11 @@ pub fn main() !void {
if (try file_reader.read(interp_path[0..phdr.p_filesz]) != phdr.p_filesz) if (try file_reader.read(interp_path[0..phdr.p_filesz]) != phdr.p_filesz)
return UnfinishedReadError.UnfinishedRead; return UnfinishedReadError.UnfinishedRead;
assert(interp_path[phdr.p_filesz - 1] == 0); // Must be zero terminated assert(interp_path[phdr.p_filesz - 1] == 0); // Must be zero terminated
log(.info, "Found interpreter path: {s}", .{interp_path[0 .. phdr.p_filesz - 1]});
const interp = try std.fs.cwd().openFile( const interp = try std.fs.cwd().openFile(
interp_path[0 .. phdr.p_filesz - 1], interp_path[0 .. phdr.p_filesz - 1],
.{ .mode = .read_only }, .{ .mode = .read_only },
); );
log(.info, "--- Loading interpreter ---", .{});
var interp_buffer: [128]u8 = undefined; var interp_buffer: [128]u8 = undefined;
var interp_reader = interp.reader(&interp_buffer); var interp_reader = interp.reader(&interp_buffer);
const interp_ehdr = try elf.Header.read(&interp_reader.interface); const interp_ehdr = try elf.Header.read(&interp_reader.interface);
@@ -77,7 +68,6 @@ pub fn main() !void {
const interp_base = try loadStaticElf(interp_ehdr, &interp_reader); const interp_base = try loadStaticElf(interp_ehdr, &interp_reader);
maybe_interp_base = interp_base; maybe_interp_base = interp_base;
maybe_interp_entry = interp_ehdr.entry + if (interp_ehdr.type == .DYN) interp_base else 0; maybe_interp_entry = interp_ehdr.entry + if (interp_ehdr.type == .DYN) interp_base else 0;
log(.info, "Interpreter loaded: base=0x{x}, entry=0x{x}", .{ interp_base, maybe_interp_entry.? });
interp.close(); interp.close();
} }
@@ -105,22 +95,15 @@ pub fn main() !void {
const dest_ptr = @as([*]u8, @ptrCast(std.os.argv.ptr)); const dest_ptr = @as([*]u8, @ptrCast(std.os.argv.ptr));
const src_ptr = @as([*]u8, @ptrCast(&std.os.argv[arg_index])); const src_ptr = @as([*]u8, @ptrCast(&std.os.argv[arg_index]));
const len = @intFromPtr(end_of_auxv) - @intFromPtr(src_ptr); const len = @intFromPtr(end_of_auxv) - @intFromPtr(src_ptr);
log(
.debug,
"Copying stack from {*} to {*} with length 0x{x}",
.{ src_ptr, dest_ptr, len },
);
assert(@intFromPtr(dest_ptr) < @intFromPtr(src_ptr)); assert(@intFromPtr(dest_ptr) < @intFromPtr(src_ptr));
std.mem.copyForwards(u8, dest_ptr[0..len], src_ptr[0..len]); @memmove(dest_ptr[0..len], src_ptr[0..len]);
// `std.os.argv.ptr` points to the argv pointers. The word just before it is argc and also the // `std.os.argv.ptr` points to the argv pointers. The word just before it is argc and also the
// start of the stack. // start of the stack.
const argc: [*]usize = @as([*]usize, @ptrCast(@alignCast(&std.os.argv.ptr[0]))) - 1; const argc: [*]usize = @as([*]usize, @ptrCast(@alignCast(&std.os.argv.ptr[0]))) - 1;
argc[0] = std.os.argv.len - arg_index; argc[0] = std.os.argv.len - arg_index;
log(.debug, "new argc: {x}", .{argc[0]});
const final_entry = maybe_interp_entry orelse entry; const final_entry = maybe_interp_entry orelse entry;
log(.info, "Trampolining to final entry: 0x{x} with sp: {*}", .{ final_entry, argc });
trampoline(final_entry, argc); trampoline(final_entry, argc);
} }
@@ -146,31 +129,24 @@ fn loadStaticElf(ehdr: elf.Header, file_reader: *std.fs.File.Reader) !usize {
} }
minva = mem.alignBackward(usize, minva, page_size); minva = mem.alignBackward(usize, minva, page_size);
maxva = mem.alignForward(usize, maxva, page_size); maxva = mem.alignForward(usize, maxva, page_size);
log(.debug, "Calculated bounds: minva=0x{x}, maxva=0x{x}", .{ minva, maxva });
break :bounds .{ minva, maxva }; break :bounds .{ minva, maxva };
}; };
// Check, that the needed memory region can be allocated as a whole. We do this // Check, that the needed memory region can be allocated as a whole.
const dynamic = ehdr.type == elf.ET.DYN; const dynamic = ehdr.type == elf.ET.DYN;
log(.debug, "ELF type is {s}", .{if (dynamic) "DYN" else "EXEC (static)"});
const hint = if (dynamic) null else @as(?[*]align(page_size) u8, @ptrFromInt(minva)); const hint = if (dynamic) null else @as(?[*]align(page_size) u8, @ptrFromInt(minva));
log(.debug, "mmap pre-flight hint: {*}", .{hint});
const base = try posix.mmap( const base = try posix.mmap(
hint, hint,
maxva - minva, maxva - minva,
posix.PROT.NONE, posix.PROT.WRITE,
.{ .TYPE = .PRIVATE, .ANONYMOUS = true, .FIXED = !dynamic }, .{ .TYPE = .PRIVATE, .ANONYMOUS = true, .FIXED_NOREPLACE = !dynamic },
-1, -1,
0, 0,
); );
log(.debug, "Pre-flight reservation successful at: {*}, size: 0x{x}", .{ base.ptr, base.len });
posix.munmap(base);
const flags = posix.MAP{ .TYPE = .PRIVATE, .ANONYMOUS = true, .FIXED = true };
var phdrs = ehdr.iterateProgramHeaders(file_reader); var phdrs = ehdr.iterateProgramHeaders(file_reader);
var phdr_idx: u32 = 0;
errdefer posix.munmap(base); errdefer posix.munmap(base);
while (try phdrs.next()) |phdr| : (phdr_idx += 1) { while (try phdrs.next()) |phdr| {
if (phdr.p_type != elf.PT_LOAD) continue; if (phdr.p_type != elf.PT_LOAD) continue;
if (phdr.p_memsz == 0) continue; if (phdr.p_memsz == 0) continue;
@@ -179,28 +155,12 @@ fn loadStaticElf(ehdr: elf.Header, file_reader: *std.fs.File.Reader) !usize {
var start = mem.alignBackward(usize, phdr.p_vaddr, page_size); var start = mem.alignBackward(usize, phdr.p_vaddr, page_size);
const base_for_dyn = if (dynamic) @intFromPtr(base.ptr) else 0; const base_for_dyn = if (dynamic) @intFromPtr(base.ptr) else 0;
start += base_for_dyn; start += base_for_dyn;
log( const ptr: []align(page_size) u8 = @as([*]align(page_size) u8, @ptrFromInt(start))[0..size];
.debug,
" - phdr[{}]: mapping 0x{x} bytes at 0x{x} (vaddr=0x{x}, dyn_base=0x{x})",
.{ phdr_idx, size, start, phdr.p_vaddr, base_for_dyn },
);
// NOTE: We can't use a single file-backed mmap for the segment, because p_memsz may be
// larger than p_filesz. This difference accounts for the .bss section, which must be
// zero-initialized.
const ptr = try posix.mmap(
@as(?[*]align(page_size) u8, @ptrFromInt(start)),
size,
posix.PROT.WRITE,
flags,
-1,
0,
);
try file_reader.seekTo(phdr.p_offset); try file_reader.seekTo(phdr.p_offset);
if (try file_reader.read(ptr[offset..][0..phdr.p_filesz]) != phdr.p_filesz) if (try file_reader.read(ptr[offset..][0..phdr.p_filesz]) != phdr.p_filesz)
return UnfinishedReadError.UnfinishedRead; return UnfinishedReadError.UnfinishedRead;
try posix.mprotect(ptr, elfToMmapProt(phdr.p_flags)); try posix.mprotect(ptr, elfToMmapProt(phdr.p_flags));
} }
log(.debug, "loadElf returning base: 0x{x}", .{@intFromPtr(base.ptr)});
return @intFromPtr(base.ptr); return @intFromPtr(base.ptr);
} }
@@ -312,8 +272,8 @@ fn testHelper(
}); });
defer testing.allocator.free(result.stdout); defer testing.allocator.free(result.stdout);
defer testing.allocator.free(result.stderr); defer testing.allocator.free(result.stderr);
errdefer log(.err, "term: {}", .{result.term}); errdefer std.log.err("term: {}", .{result.term});
errdefer log(.err, "stdout: {s}", .{result.stdout}); errdefer std.log.err("stdout: {s}", .{result.stdout});
try testing.expectEqualStrings(expected_stdout, result.stdout); try testing.expectEqualStrings(expected_stdout, result.stdout);
try testing.expect(result.term == .Exited); try testing.expect(result.term == .Exited);