This commit is contained in:
Loris Cro 2025-05-03 20:46:40 +02:00
parent 0b9d6ad042
commit 7889c08151
5 changed files with 96 additions and 14 deletions

View file

@ -152,7 +152,8 @@ fn mainServer() !void {
const index = try server.receiveBody_u32(); const index = try server.receiveBody_u32();
const test_fn = builtin.test_functions[index]; const test_fn = builtin.test_functions[index];
const entry_addr = @intFromPtr(test_fn.func); const entry_addr = @intFromPtr(test_fn.func);
try server.serveU64Message(.fuzz_start_addr, entry_addr); const offset = std.c._dyld_get_image_vmaddr_slide(0);
try server.serveU64Message(.fuzz_start_addr, entry_addr - offset);
defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1); defer if (testing.allocator_instance.deinit() == .leak) std.process.exit(1);
is_fuzz_test = false; is_fuzz_test = false;
fuzzer_set_name(test_fn.name.ptr, test_fn.name.len); fuzzer_set_name(test_fn.name.ptr, test_fn.name.len);

View file

@ -208,7 +208,12 @@ const Fuzzer = struct {
}; };
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header)); f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize)); f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));
f.seen_pcs.appendSliceAssumeCapacity(std.mem.sliceAsBytes(pcs));
const offset = std.c._dyld_get_image_vmaddr_slide(0);
for (pcs) |pc| {
const value = pc - offset;
f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&value));
}
} }
} }
@ -310,19 +315,21 @@ const Fuzzer = struct {
f.input.clearRetainingCapacity(); f.input.clearRetainingCapacity();
const old_input = f.corpus.items[corpus_index].bytes; const old_input = f.corpus.items[corpus_index].bytes;
f.input.ensureTotalCapacity(old_input.len + 1) catch @panic("mmap file resize failed"); f.input.ensureTotalCapacity(old_input.len + 1) catch @panic("mmap file resize failed");
switch (mutation) { mut: switch (mutation) {
.remove_byte => { .remove_byte => {
if (old_input.len == 0) continue :mut .add_byte;
const omitted_index = rng.uintLessThanBiased(usize, old_input.len); const omitted_index = rng.uintLessThanBiased(usize, old_input.len);
f.input.appendSliceAssumeCapacity(old_input[0..omitted_index]); f.input.appendSliceAssumeCapacity(old_input[0..omitted_index]);
f.input.appendSliceAssumeCapacity(old_input[omitted_index + 1 ..]); f.input.appendSliceAssumeCapacity(old_input[omitted_index + 1 ..]);
}, },
.modify_byte => { .modify_byte => {
if (old_input.len == 0) continue :mut .add_byte;
const modified_index = rng.uintLessThanBiased(usize, old_input.len); const modified_index = rng.uintLessThanBiased(usize, old_input.len);
f.input.appendSliceAssumeCapacity(old_input); f.input.appendSliceAssumeCapacity(old_input);
f.input.items[modified_index] = rng.int(u8); f.input.items[modified_index] = rng.int(u8);
}, },
.add_byte => { .add_byte => {
const modified_index = rng.uintLessThanBiased(usize, old_input.len); const modified_index = if (old_input.len == 0) 0 else rng.uintLessThanBiased(usize, old_input.len);
f.input.appendSliceAssumeCapacity(old_input[0..modified_index]); f.input.appendSliceAssumeCapacity(old_input[0..modified_index]);
f.input.appendAssumeCapacity(rng.int(u8)); f.input.appendAssumeCapacity(rng.int(u8));
f.input.appendSliceAssumeCapacity(old_input[modified_index..]); f.input.appendSliceAssumeCapacity(old_input[modified_index..]);

View file

@ -7,6 +7,7 @@
//! properties. //! properties.
const std = @import("../std.zig"); const std = @import("../std.zig");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const Path = std.Build.Cache.Path; const Path = std.Build.Cache.Path;
const Dwarf = std.debug.Dwarf; const Dwarf = std.debug.Dwarf;
@ -17,7 +18,7 @@ const SourceLocation = std.debug.Coverage.SourceLocation;
const Info = @This(); const Info = @This();
/// Sorted by key, ascending. /// Sorted by key, ascending.
address_map: std.AutoArrayHashMapUnmanaged(u64, Dwarf.ElfModule), address_map: std.AutoArrayHashMapUnmanaged(u64, std.debug.SelfInfo.Module),
/// Externally managed, outlives this `Info` instance. /// Externally managed, outlives this `Info` instance.
coverage: *Coverage, coverage: *Coverage,
@ -25,19 +26,40 @@ pub const LoadError = Dwarf.ElfModule.LoadError;
pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info { pub fn load(gpa: Allocator, path: Path, coverage: *Coverage) LoadError!Info {
var sections: Dwarf.SectionArray = Dwarf.null_section_array; var sections: Dwarf.SectionArray = Dwarf.null_section_array;
var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null);
try elf_module.dwarf.populateRanges(gpa);
var info: Info = .{ var info: Info = .{
.address_map = .{}, .address_map = .{},
.coverage = coverage, .coverage = coverage,
}; };
try info.address_map.put(gpa, elf_module.base_address, elf_module); switch (builtin.os.tag) {
.linux => {
var elf_module = try Dwarf.ElfModule.loadPath(gpa, path, null, null, &sections, null);
try elf_module.dwarf.populateRanges(gpa);
try info.address_map.put(gpa, elf_module.base_address, elf_module);
},
.macos => {
const macho_file = path.root_dir.handle.openFile(path.sub_path, .{}) catch |err| switch (err) {
error.FileNotFound => return error.MissingDebugInfo,
else => return error.InvalidDebugInfo,
};
// readMachoDebugInfo takes ownership of the file
// defer elf_file.close();
var module = std.debug.SelfInfo.readMachODebugInfo(gpa, macho_file) catch {
return error.InvalidDebugInfo;
};
module.base_address = 0;
module.vmaddr_slide = 0;
try info.address_map.put(gpa, 0, module);
},
else => @compileError("TODO: implement debug info loading for the target platform"),
}
return info; return info;
} }
pub fn deinit(info: *Info, gpa: Allocator) void { pub fn deinit(info: *Info, gpa: Allocator) void {
for (info.address_map.values()) |*elf_module| { for (info.address_map.values()) |*module| {
elf_module.dwarf.deinit(gpa); module.deinit(gpa);
} }
info.address_map.deinit(gpa); info.address_map.deinit(gpa);
info.* = undefined; info.* = undefined;
@ -57,6 +79,53 @@ pub fn resolveAddresses(
) ResolveAddressesError!void { ) ResolveAddressesError!void {
assert(sorted_pc_addrs.len == output.len); assert(sorted_pc_addrs.len == output.len);
if (info.address_map.entries.len != 1) @panic("TODO"); if (info.address_map.entries.len != 1) @panic("TODO");
const elf_module = &info.address_map.values()[0];
return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf); switch (builtin.os.tag) {
else => @compileError("unsupported"),
.linux => {
const elf_module = &info.address_map.values()[0];
return info.coverage.resolveAddressesDwarf(gpa, sorted_pc_addrs, output, &elf_module.dwarf);
},
.macos => {
const module = &info.address_map.values()[0];
var idx: usize = 0;
while (idx < sorted_pc_addrs.len) : (idx += 1) {
const ofile = (module.getOFileInfoForAddress(gpa, sorted_pc_addrs[idx]) catch return error.InvalidDebugInfo);
if (ofile.o_file_info.?.di.ranges.items.len == 0) {
try ofile.o_file_info.?.di.populateRanges(gpa);
}
// const last = ofile.ranges.getLastOrNull() orelse return;
// var end_idx = idx;
// while (end_idx < sorted_pc_addrs.len and
// sorted_pc_addrs[end_idx] < last.end) end_idx += 1;
// if (end_idx == idx) {
// std.debug.panic("made no progress", .{});
// }
//
const stab_symbol = std.mem.sliceTo(module.strings[ofile.symbol.?.strx..], 0);
const offset = ofile.relocated_address - ofile.symbol.?.addr;
// Translate again the address, this time into an address inside the
// .o file
const relocated_address_o = ofile.o_file_info.?.addr_table.get(stab_symbol) orelse @panic("error");
try info.coverage.resolveAddressesDwarf(
gpa,
&.{relocated_address_o + offset},
output[idx..][0..1],
&ofile.o_file_info.?.di,
);
// std.debug.print("{x} -> {x} -> {}\n", .{
// sorted_pc_addrs[idx],
// relocated_address_o + offset,
// output[idx],
// });
// idx = end_idx;
}
},
}
} }

View file

@ -860,7 +860,7 @@ pub const WindowsModule = struct {
/// This takes ownership of macho_file: users of this function should not close /// This takes ownership of macho_file: users of this function should not close
/// it themselves, even on error. /// it themselves, even on error.
/// TODO it's weird to take ownership even on error, rework this code. /// TODO it's weird to take ownership even on error, rework this code.
fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module { pub fn readMachODebugInfo(allocator: Allocator, macho_file: File) !Module {
const mapped_mem = try mapWholeFile(macho_file); const mapped_mem = try mapWholeFile(macho_file);
const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr)); const hdr: *const macho.mach_header_64 = @ptrCast(@alignCast(mapped_mem.ptr));

View file

@ -416,7 +416,12 @@ pub fn flushModule(
} }
if (comp.config.any_fuzz) { if (comp.config.any_fuzz) {
try positionals.append(try link.openObjectInput(diags, comp.fuzzer_lib.?.full_object_path)); try positionals.append(try link.openArchiveInput(
diags,
comp.fuzzer_lib.?.full_object_path,
true,
false,
));
} }
if (comp.ubsan_rt_lib) |crt_file| { if (comp.ubsan_rt_lib) |crt_file| {