authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-24 13:34:12+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-25 00:21:55+01:00
log50752ce0f53961b300dee8dcd00ce7a96865add2
tree96eda645cd1a2bef4e97240392819e698dbe51de
parent1cdcac604f5408f06689c6c9866aef355d1b2f72
signature Commit is signed but in an unrecognized format.

Elf2: slightly refactor object processing, and add more tracy calls


2 files changed, 530 insertions(+), 418 deletions(-)

src/link/Elf2.zig+496-418
......@@ -2349,6 +2349,9 @@ const SymbolReloc = struct {
23492349 assert(elf.ehdrType() != .REL);
23502350 const reloc_node = reloc.node.unwrap().?; // deleted
23512351
2352 const trace = tracy.trace(@src());
2353 defer trace.end();
2354
23522355 // Delete any current output relocation or error.
23532356 switch (reloc.bits.result) {
23542357 .ok, .overflowed, .misaligned => {
......@@ -3086,6 +3089,10 @@ const AddLocalSymbolOptions = struct {
30863089fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex {
30873090 const gpa = elf.base.comp.gpa;
30883091
3092 const trace = tracy.trace(@src());
3093 defer trace.end();
3094 trace.addText(opts.name);
3095
30893096 assert(opts.shndx != .UNDEF);
30903097
30913098 try elf.symtab.ensureUnusedCapacity(gpa, 1);
......@@ -3162,6 +3169,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
31623169} || Error)!Symbol.Global.Index {
31633170 _ = opts.lib_name; // TODO
31643171
3172 const trace = tracy.trace(@src());
3173 defer trace.end();
3174 trace.addText(opts.name);
3175
31653176 log.debug("addGlobalSymbol({q})", .{opts.name});
31663177
31673178 const gpa = elf.base.comp.gpa;
......@@ -4347,6 +4358,9 @@ const Symbol = struct {
43474358 /// Returns `true` if any relocations were updated, in which case relocations targeting
43484359 /// `gsi` must be re-applied by the caller.
43494360 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4361 const trace = tracy.trace(@src());
4362 defer trace.end();
4363
43504364 if (elf.shndx.dynamic == .UNDEF) return false;
43514365 const sym_id: Symbol.Id = .global(gsi);
43524366
......@@ -4517,20 +4531,8 @@ const Symbol = struct {
45174531 }
45184532
45194533 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
4520 const trace = tracy.trace(@src());
4521 defer trace.end();
4522
45234534 const sym_index = sym_id.index(elf);
45244535
4525 trace.addText(name: {
4526 // There isn't a general `name` method on `Symbol.Id` because it would be incorrect
4527 // to ever actually care about the name of a local symbol.
4528 const name: String(.strtab) = switch (elf.symPtr(sym_index)) {
4529 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
4530 };
4531 break :name name.slice(elf);
4532 });
4533
45344536 switch (elf.symPtr(sym_index)) {
45354537 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
45364538 }
......@@ -4555,6 +4557,9 @@ const Symbol = struct {
45554557 }
45564558
45574559 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
4560 const trace = tracy.trace(@src());
4561 defer trace.end();
4562
45584563 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
45594564 while (ri != .none) {
45604565 const reloc = ri.get(elf);
......@@ -7709,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void
77097714 }
77107715}
77117716fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void {
7717 const trace = tracy.trace(@src());
7718 defer trace.end();
7719 trace.addTextFmt("{f}", .{path.fmtEscapeString()});
7720
77127721 const comp = elf.base.comp;
77137722 const gpa = comp.gpa;
77147723 const diags = &comp.link_diags;
......@@ -7788,6 +7797,10 @@ fn loadObject(
77887797 fr: *Io.File.Reader,
77897798 fl: MappedFile.Node.FileLocation,
77907799) LoadParseInputError!void {
7800 const trace = tracy.trace(@src());
7801 defer trace.end();
7802 trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) });
7803
77917804 const comp = elf.base.comp;
77927805 const gpa = comp.gpa;
77937806 const diags = &comp.link_diags;
......@@ -7939,443 +7952,492 @@ fn loadObject(
79397952 defer gpa.free(shstrtab);
79407953 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
79417954 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
7942 for (sections[1..]) |*section| {
7943 if (section.shdr.name >= shstrtab.len) continue;
7944 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7945 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7946 const sect_type = section.shdr.type;
7947 const opts: struct {
7948 shndx: Section.Index,
7949 node_fixed: bool,
7950 } = switch (sect_type) {
7951 else => continue,
7952 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7953 const shndx = elf.mapInputSection(.{
7954 .name = name,
7955 .flags = section.shdr.flags.shf,
7956 .entsize = section.shdr.entsize,
7957 .type = section.shdr.type,
7958 }) catch |err| switch (err) {
7959 else => |e| return e,
7960 error.StripSection => continue,
7961 error.TlsSectionUnavailable => {
7962 if (section.shdr.size == 0) continue;
7963
7964 return diags.failParse(
7955
7956 {
7957 const sections_trace = tracy.traceNamed(@src(), "scan input sections");
7958 defer sections_trace.end();
7959 for (sections[1..]) |*section| {
7960 const section_trace = tracy.traceNamed(@src(), "input section");
7961 defer section_trace.end();
7962 if (section.shdr.name >= shstrtab.len) continue;
7963 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
7964 section_trace.addText(name);
7965 if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue;
7966 const sect_type = section.shdr.type;
7967 const opts: struct {
7968 shndx: Section.Index,
7969 node_fixed: bool,
7970 } = switch (sect_type) {
7971 else => continue,
7972 .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: {
7973 const shndx = elf.mapInputSection(.{
7974 .name = name,
7975 .flags = section.shdr.flags.shf,
7976 .entsize = section.shdr.entsize,
7977 .type = section.shdr.type,
7978 }) catch |err| switch (err) {
7979 else => |e| return e,
7980 error.StripSection => continue,
7981 error.TlsSectionUnavailable => {
7982 if (section.shdr.size == 0) continue;
7983
7984 return diags.failParse(
7985 path,
7986 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
7987 .{name},
7988 );
7989 },
7990 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
7991 // It probably doesn't matter, just skip this section.
7992 continue;
7993 } else return diags.failParse(
7994 path,
7995 "unsupported flags for section '{s}'",
7996 .{name},
7997 ),
7998 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
7999 // It probably doesn't matter, just skip this section.
8000 continue;
8001 } else return diags.failParse(
8002 path,
8003 "type of section '{s}' conflicts with other inputs",
8004 .{name},
8005 ),
8006 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
8007 // It probably doesn't matter, just skip this section.
8008 continue;
8009 } else return diags.failParse(
8010 path,
8011 "flags of section '{s}' conflict with other inputs",
8012 .{name},
8013 ),
8014 error.UnsupportedNobitsSection => return diags.failParse(
79658015 path,
7966 "thread-local storage section '{s}' is incompatible with '-fsingle-threaded'",
8016 "unsupported section type SHT_NOBITS for non-bss section '{s}'",
8017 .{name},
8018 ),
8019 };
8020 if (section.shdr.flags.shf.COMPRESSED) {
8021 // SHF_COMPRESSED is only allowed on non-alloc sections.
8022 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
8023 path,
8024 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
79678025 .{name},
79688026 );
7969 },
7970 error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) {
7971 // It probably doesn't matter, just skip this section.
7972 continue;
7973 } else return diags.failParse(
7974 path,
7975 "unsupported flags for section '{s}'",
7976 .{name},
7977 ),
7978 error.SectionTypeConflict => if (!section.shdr.flags.shf.ALLOC) {
7979 // It probably doesn't matter, just skip this section.
7980 continue;
7981 } else return diags.failParse(
7982 path,
7983 "type of section '{s}' conflicts with other inputs",
7984 .{name},
7985 ),
7986 error.SectionFlagsConflict => if (!section.shdr.flags.shf.ALLOC) {
7987 // It probably doesn't matter, just skip this section.
8027 // TODO: handle compressed input sections. We'll need to set a flag to
8028 // indicate that `flushInputSection` needs to decompress the section.
8029 // But because this section isn't SHF_ALLOC, it's probably okay to just
8030 // skip it for now.
79888031 continue;
7989 } else return diags.failParse(
7990 path,
7991 "flags of section '{s}' conflict with other inputs",
7992 .{name},
7993 ),
7994 error.UnsupportedNobitsSection => return diags.failParse(
7995 path,
7996 "unsupported section type SHT_NOBITS for non-bss section '{s}'",
7997 .{name},
7998 ),
8032 }
8033 break :opts .{
8034 .shndx = shndx,
8035 // For well-known sections, we know that it's fine to have e.g. random
8036 // padding, so there's no need to make the sections fixed. For custom
8037 // sections, however, we do want fixed nodes to avoid padding.
8038 .node_fixed = shndx != .text and
8039 shndx != .rodata and
8040 shndx != .data and
8041 shndx != .data_rel_ro and
8042 shndx != elf.shndx.tdata,
8043 };
8044 },
8045 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
8046 .shndx = shndx: {
8047 // TODO: the input section name may include a "priority" value between 1
8048 // and 65535 which should affect the order we assemble input sections in
8049 const init_fini_section_name: []const u8 = switch (@"type") {
8050 .INIT_ARRAY => "init_array",
8051 .FINI_ARRAY => "fini_array",
8052 .PREINIT_ARRAY => "preinit_array",
8053 else => comptime unreachable,
8054 };
8055 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
8056 const need_addralign: u8 = switch (class) {
8057 .NONE, _ => unreachable,
8058 .@"32" => 4,
8059 .@"64" => 8,
8060 };
8061 if (section.shdr.addralign != need_addralign) {
8062 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
8063 }
8064 if (shndx.* == .UNDEF) {
8065 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
8066 }
8067 switch (elf.shdrPtr(shndx.*)) {
8068 inline else => |shdr| {
8069 const old_size = elf.targetLoad(&shdr.size);
8070 const new_size = old_size + section.shdr.size;
8071 elf.targetStore(&shdr.size, @intCast(new_size));
8072 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
8073 },
8074 }
8075 break :shndx shndx.*;
8076 },
8077 // This node must be fixed to prevent padding from being added between different
8078 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
8079 .node_fixed = true,
8080 },
8081 };
8082 const need_align: Alignment = .fromByteUnits(
8083 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
8084 );
8085
8086 try opts.shndx.ensureAligned(elf, need_align);
8087
8088 if (sect_type == .NOBITS) {
8089 const offset: u64 = switch (elf.shdrPtr(opts.shndx)) {
8090 inline else => |shdr| offset: {
8091 // Bump our BSS output section size
8092 const old_size: u64 = elf.targetLoad(&shdr.size);
8093 const offset = need_align.forward(old_size);
8094 elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size));
8095 break :offset offset;
8096 },
79998097 };
8000 if (section.shdr.flags.shf.COMPRESSED) {
8001 // SHF_COMPRESSED is only allowed on non-alloc sections.
8002 if (section.shdr.flags.shf.ALLOC) return diags.failParse(
8003 path,
8004 "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED",
8005 .{name},
8006 );
8007 // TODO: handle compressed input sections. We'll need to set a flag to
8008 // indicate that `flushInputSection` needs to decompress the section.
8009 // But because this section isn't SHF_ALLOC, it's probably okay to just
8010 // skip it for now.
8011 continue;
8098
8099 section.placement = .{ .nobits = .{
8100 .shndx = opts.shndx,
8101 .offset = offset,
8102 } };
8103 if (opts.shndx == elf.shndx.bss) {
8104 input_has_bss = true;
8105 } else if (opts.shndx == elf.shndx.tbss) {
8106 input_has_tbss = true;
8107 } else {
8108 assert(elf.ehdrType() == .REL);
80128109 }
8013 break :opts .{
8014 .shndx = shndx,
8015 // For well-known sections, we know that it's fine to have e.g. random
8016 // padding, so there's no need to make the sections fixed. For custom
8017 // sections, however, we do want fixed nodes to avoid padding.
8018 .node_fixed = shndx != .text and
8019 shndx != .rodata and
8020 shndx != .data and
8021 shndx != .data_rel_ro and
8022 shndx != elf.shndx.tdata,
8023 };
8024 },
8025 inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{
8026 .shndx = shndx: {
8027 // TODO: the input section name may include a "priority" value between 1
8028 // and 65535 which should affect the order we assemble input sections in
8029 const init_fini_section_name: []const u8 = switch (@"type") {
8030 .INIT_ARRAY => "init_array",
8031 .FINI_ARRAY => "fini_array",
8032 .PREINIT_ARRAY => "preinit_array",
8033 else => comptime unreachable,
8034 };
8035 const shndx: *Section.Index = &@field(elf.shndx, init_fini_section_name);
8036 const need_addralign: u8 = switch (class) {
8037 .NONE, _ => unreachable,
8038 .@"32" => 4,
8039 .@"64" => 8,
8110
8111 continue;
8112 }
8113
8114 const add_node_opts: MappedFile.Node.AddOptions = .{
8115 .size = need_align.forward(section.shdr.size),
8116 .alignment = need_align,
8117 .moved = true, // see assert at end of `flushInputSection`
8118 };
8119 const ni = elf.addNodeAssumeCapacity(
8120 if (opts.node_fixed) ni: {
8121 const shndx_ni = opts.shndx.node(elf);
8122 const after_oni: MappedFile.Node.Index.Optional = after: {
8123 var node_it = shndx_ni.last(&elf.mf);
8124 while (node_it.unwrap()) |cur_ni| {
8125 switch (cur_ni.position(&elf.mf)) {
8126 .header => break :after .wrap(cur_ni), // last header
8127 .footer, .floating => node_it = cur_ni.prev(&elf.mf),
8128 }
8129 } else {
8130 break :after .none;
8131 }
80408132 };
8041 if (section.shdr.addralign != need_addralign) {
8042 return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"});
8043 }
8044 if (shndx.* == .UNDEF) {
8045 try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type");
8046 }
8047 switch (elf.shdrPtr(shndx.*)) {
8048 inline else => |shdr| {
8049 const old_size = elf.targetLoad(&shdr.size);
8050 const new_size = old_size + section.shdr.size;
8051 elf.targetStore(&shdr.size, @intCast(new_size));
8052 elf.updateInitFiniArraySectionSize(shndx.*, init_fini_section_name);
8053 },
8054 }
8055 break :shndx shndx.*;
8133 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
8134 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
8135 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
8136 );
8137 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
8138 elf.input_sections.addOneAssumeCapacity().* = .{
8139 .input = input_index,
8140 .file_location = .{
8141 .offset = fl.offset + section.shdr.offset,
8142 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
80568143 },
8057 // This node must be fixed to prevent padding from being added between different
8058 // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections.
8059 .node_fixed = true,
8060 },
8144 // The section vaddr is initially 0, because the symbol addresses are
8145 // zero-based. This will eventually be updated by `flushMoved`.
8146 .vaddr = 0,
8147 .node = ni,
8148 .first_symbol_reloc = .none,
8149 };
8150 elf.input_prog_node.increaseEstimatedTotalItems(1);
8151 }
8152 }
8153
8154 // This array will exclude the "null" symbol (symtab index 0).
8155 var symmap: std.ArrayList(Symbol.Id) = .empty;
8156 defer symmap.deinit(gpa);
8157
8158 const maybe_symtab_shndx: ?usize = for (sections[1..], 1..) |*symtab, symtab_shndx| {
8159 if (symtab.shdr.type == .SYMTAB) break symtab_shndx;
8160 } else null;
8161
8162 if (maybe_symtab_shndx) |symtab_shndx| {
8163 const symtab = &sections[symtab_shndx];
8164
8165 const symbols_trace = tracy.traceNamed(@src(), "scan input symbols");
8166 defer symbols_trace.end();
8167
8168 for (sections[symtab_shndx + 1 ..]) |*other_section| {
8169 if (other_section.shdr.type == .SYMTAB) {
8170 return diags.failParse(path, "multiple SHT_SYMTAB sections", .{});
8171 }
8172 }
8173
8174 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
8175 return diags.failParse(path, "unsupported symtab entsize", .{});
8176
8177 const strtab = strtab: {
8178 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
8179 return diags.failParse(path, "missing symbol names", .{});
8180 const shdr = &sections[symtab.shdr.link].shdr;
8181 if (shdr.type != .STRTAB)
8182 return diags.failParse(path, "invalid strtab type", .{});
8183 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
8184 errdefer gpa.free(strtab);
8185 try fr.seekTo(fl.offset + shdr.offset);
8186 try r.readSliceAll(strtab);
8187 break :strtab strtab;
80618188 };
8062 const need_align: Alignment = .fromByteUnits(
8063 std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))),
8189 defer gpa.free(strtab);
8190
8191 const symnum = std.math.divExact(
8192 u32,
8193 @intCast(symtab.shdr.size),
8194 @intCast(symtab.shdr.entsize),
8195 ) catch return diags.failParse(
8196 path,
8197 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
8198 .{ symtab.shdr.size, symtab.shdr.entsize },
80648199 );
80658200
8066 try opts.shndx.ensureAligned(elf, need_align);
8201 try symmap.resize(gpa, symnum -| 1); // -1 to exclude "null" symbol
8202 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize); // +entsize to skip "null" symbol
8203 for (symmap.items) |*si| {
8204 si.* = .null;
8205 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
8206 try r.discardAll64(symtab.shdr.entsize);
8207 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
8208
8209 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
8210
8211 const symbol_trace = tracy.traceNamed(@src(), "input symbol");
8212 defer symbol_trace.end();
8213 symbol_trace.addText(name);
8214
8215 const sym_type: std.elf.STT = switch (input_sym.info.type) {
8216 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
8217 .SECTION => .NOTYPE,
8218 .FILE, .COMMON, _ => continue,
8219 };
8220
8221 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
8222 else => |bind| return diags.failParse(
8223 path,
8224 "symbol '{s}' has unsupported binding (0x{x})",
8225 .{ name, bind },
8226 ),
8227 .LOCAL => continue,
8228 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8229 si.* = .global(elf.addGlobalSymbol(.{
8230 .node = .none,
8231 .name = name,
8232 .value = input_sym.value,
8233 .size = input_sym.size,
8234 .type = sym_type,
8235 .bind = switch (bind) {
8236 .WEAK, .GNU_UNIQUE => .weak,
8237 .GLOBAL => .strong,
8238 else => unreachable,
8239 },
8240 .visibility = input_sym.other.visibility,
8241 .shndx = .UNDEF,
8242 }) catch |err| switch (err) {
8243 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
8244 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
8245 error.UndefinedDefaultVersion => return diags.failParse(
8246 path,
8247 "symbol '{s}' specifies default version without defining it",
8248 .{name},
8249 ),
8250 else => |e| return e,
8251 });
8252 continue;
8253 },
8254 };
80678255
8068 if (sect_type == .NOBITS) {
8069 const offset: u64 = switch (elf.shdrPtr(opts.shndx)) {
8070 inline else => |shdr| offset: {
8071 // Bump our BSS output section size
8072 const old_size: u64 = elf.targetLoad(&shdr.size);
8073 const offset = need_align.forward(old_size);
8074 elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size));
8075 break :offset offset;
8256 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) {
8257 .none => continue,
8258 .progbits => |isi| .{
8259 .wrap(isi.node(elf)),
8260 elf.getNodeShndx(isi.node(elf)),
8261 input_sym.value,
8262 },
8263 .nobits => |nobits| sym: {
8264 const section_base: u64 = switch (elf.ehdrType()) {
8265 .REL => 0,
8266 .EXEC, .DYN => base: {
8267 if (elf.ni.tls != .none and
8268 nobits.shndx == elf.shndx.tbss)
8269 {
8270 break :base elf.tbssOffset();
8271 }
8272 assert(nobits.shndx == elf.shndx.bss);
8273 break :base nobits.shndx.vaddr(elf);
8274 },
8275 };
8276 break :sym .{
8277 .none,
8278 nobits.shndx,
8279 section_base + nobits.offset + input_sym.value,
8280 };
80768281 },
80778282 };
80788283
8079 section.placement = .{ .nobits = .{
8080 .shndx = opts.shndx,
8081 .offset = offset,
8082 } };
8083 if (opts.shndx == elf.shndx.bss) {
8084 input_has_bss = true;
8085 } else if (opts.shndx == elf.shndx.tbss) {
8086 input_has_tbss = true;
8087 } else {
8088 assert(elf.ehdrType() == .REL);
8284 switch (input_sym.info.bind) {
8285 else => |bind| return diags.failParse(
8286 path,
8287 "symbol '{s}' has unsupported binding (0x{x})",
8288 .{ name, bind },
8289 ),
8290 .LOCAL => {
8291 const lsi = try elf.addLocalSymbol(.{
8292 .node = symbol_oni,
8293 .name = name,
8294 .value = symbol_value,
8295 .size = input_sym.size,
8296 .type = sym_type,
8297 .shndx = output_shndx,
8298 });
8299 si.* = .local(lsi);
8300 },
8301 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8302 si.* = .global(elf.addGlobalSymbol(.{
8303 .node = symbol_oni,
8304 .name = name,
8305 .value = symbol_value,
8306 .size = input_sym.size,
8307 .type = sym_type,
8308 .bind = switch (bind) {
8309 .WEAK, .GNU_UNIQUE => .weak,
8310 .GLOBAL => .strong,
8311 else => unreachable,
8312 },
8313 .visibility = input_sym.other.visibility,
8314 .shndx = output_shndx,
8315 }) catch |err| switch (err) {
8316 error.MultipleDefinitions => return diags.failParse(
8317 path,
8318 "multiple definitions of '{s}'",
8319 .{name},
8320 ),
8321 error.MultipleDefaultVersions => return diags.failParse(
8322 path,
8323 "multiple default versions of '{s}'",
8324 .{parseVersionedSymbolName(name).name},
8325 ),
8326 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
8327 else => |e| return e,
8328 });
8329 },
80898330 }
8090
8091 continue;
80928331 }
8093
8094 const add_node_opts: MappedFile.Node.AddOptions = .{
8095 .size = need_align.forward(section.shdr.size),
8096 .alignment = need_align,
8097 .moved = true, // see assert at end of `flushInputSection`
8098 };
8099 const ni = elf.addNodeAssumeCapacity(
8100 if (opts.node_fixed) ni: {
8101 const shndx_ni = opts.shndx.node(elf);
8102 const after_oni: MappedFile.Node.Index.Optional = after: {
8103 var node_it = shndx_ni.last(&elf.mf);
8104 while (node_it.unwrap()) |cur_ni| {
8105 switch (cur_ni.position(&elf.mf)) {
8106 .header => break :after .wrap(cur_ni), // last header
8107 .footer, .floating => node_it = cur_ni.prev(&elf.mf),
8108 }
8109 } else {
8110 break :after .none;
8111 }
8112 };
8113 break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts);
8114 } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts),
8115 .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) },
8116 );
8117 section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) };
8118 elf.input_sections.addOneAssumeCapacity().* = .{
8119 .input = input_index,
8120 .file_location = .{
8121 .offset = fl.offset + section.shdr.offset,
8122 .size = if (section.shdr.type == .NOBITS) 0 else section.shdr.size,
8123 },
8124 // The section vaddr is initially 0, because the symbol addresses are
8125 // zero-based. This will eventually be updated by `flushMoved`.
8126 .vaddr = 0,
8127 .node = ni,
8128 .first_symbol_reloc = .none,
8129 };
8130 elf.input_prog_node.increaseEstimatedTotalItems(1);
81318332 }
8132 var symmap: std.ArrayList(Symbol.Id) = .empty;
8133 defer symmap.deinit(gpa);
8134 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
8135 else => {},
8136 .SYMTAB => {
8137 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
8138 return diags.failParse(path, "unsupported symtab entsize", .{});
8139 const strtab = strtab: {
8140 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
8141 return diags.failParse(path, "missing symbol names", .{});
8142 const shdr = &sections[symtab.shdr.link].shdr;
8143 if (shdr.type != .STRTAB)
8144 return diags.failParse(path, "invalid strtab type", .{});
8145 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
8146 errdefer gpa.free(strtab);
8147 try fr.seekTo(fl.offset + shdr.offset);
8148 try r.readSliceAll(strtab);
8149 break :strtab strtab;
8150 };
8151 defer gpa.free(strtab);
8152 const symnum = std.math.sub(u32, std.math.divExact(
8153 u32,
8154 @intCast(symtab.shdr.size),
8155 @intCast(symtab.shdr.entsize),
8156 ) catch return diags.failParse(
8157 path,
8158 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
8159 .{ symtab.shdr.size, symtab.shdr.entsize },
8160 ), 1) catch continue;
8161 symmap.clearRetainingCapacity();
8162 try symmap.resize(gpa, symnum);
8163 try fr.seekTo(fl.offset + symtab.shdr.offset + symtab.shdr.entsize);
8164 for (symmap.items) |*si| {
8165 si.* = .null;
8166 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
8167 try r.discardAll64(symtab.shdr.entsize);
8168 if (input_sym.name >= strtab.len or input_sym.shndx >= ehdr.shnum) continue;
8169
8170 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
8171
8172 const sym_type: std.elf.STT = switch (input_sym.info.type) {
8173 .NOTYPE, .OBJECT, .FUNC, .TLS => |t| t,
8174 .SECTION => .NOTYPE,
8175 .FILE, .COMMON, _ => continue,
8176 };
81778333
8178 if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) {
8179 else => |bind| return diags.failParse(
8334 {
8335 const all_relocs_trace = tracy.traceNamed(@src(), "scan input relocations");
8336 defer all_relocs_trace.end();
8337 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
8338 else => {},
8339 inline .REL, .RELA => |sht| {
8340 const relocs_trace = tracy.traceNamed(@src(), "input relocation section");
8341 defer relocs_trace.end();
8342
8343 const link_is_symtab: bool = if (maybe_symtab_shndx) |symtab_shndx| s: {
8344 break :s rel_sec.shdr.link == symtab_shndx;
8345 } else false;
8346 if (!link_is_symtab) {
8347 return diags.failParse(path, "relocation section does not link to SHT_SYMTAB section", .{});
8348 }
8349 if (rel_sec.shdr.info == std.elf.SHN_UNDEF or rel_sec.shdr.info >= ehdr.shnum) {
8350 return diags.failParse(
81808351 path,
8181 "symbol '{s}' has unsupported binding (0x{x})",
8182 .{ name, bind },
8183 ),
8184 .LOCAL => continue,
8185 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8186 si.* = .global(elf.addGlobalSymbol(.{
8187 .node = .none,
8188 .name = name,
8189 .value = input_sym.value,
8190 .size = input_sym.size,
8191 .type = sym_type,
8192 .bind = switch (bind) {
8193 .WEAK, .GNU_UNIQUE => .weak,
8194 .GLOBAL => .strong,
8195 else => unreachable,
8196 },
8197 .visibility = input_sym.other.visibility,
8198 .shndx = .UNDEF,
8199 }) catch |err| switch (err) {
8200 error.MultipleDefinitions => unreachable, // shndx is .UNDEF
8201 error.MultipleDefaultVersions => unreachable, // shndx is .UNDEF
8202 error.UndefinedDefaultVersion => return diags.failParse(
8203 path,
8204 "symbol '{s}' specifies default version without defining it",
8205 .{name},
8206 ),
8207 else => |e| return e,
8208 });
8209 continue;
8210 },
8352 "relocation section 'sh_info' refers to invalid section index '{d}'",
8353 .{rel_sec.shdr.info},
8354 );
8355 }
8356 const Rel = switch (sht) {
8357 else => comptime unreachable,
8358 .REL => ElfN.Rel,
8359 .RELA => ElfN.Rela,
82118360 };
8361 if (rel_sec.shdr.entsize < @sizeOf(Rel))
8362 return diags.failParse(path, "unsupported rel entsize", .{});
82128363
8213 const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) {
8364 const loc_sec = &sections[rel_sec.shdr.info];
8365 relocs_trace.addText(std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0));
8366 const loc_node = switch (loc_sec.placement) {
82148367 .none => continue,
8215 .progbits => |isi| .{
8216 .wrap(isi.node(elf)),
8217 elf.getNodeShndx(isi.node(elf)),
8218 input_sym.value,
8219 },
8220 .nobits => |nobits| sym: {
8221 const section_base: u64 = switch (elf.ehdrType()) {
8222 .REL => 0,
8223 .EXEC, .DYN => base: {
8224 if (elf.ni.tls != .none and
8225 nobits.shndx == elf.shndx.tbss)
8226 {
8227 break :base elf.tbssOffset();
8228 }
8229 assert(nobits.shndx == elf.shndx.bss);
8230 break :base nobits.shndx.vaddr(elf);
8231 },
8232 };
8233 break :sym .{
8234 .none,
8235 nobits.shndx,
8236 section_base + nobits.offset + input_sym.value,
8237 };
8238 },
8239 };
8240
8241 switch (input_sym.info.bind) {
8242 else => |bind| return diags.failParse(
8368 .progbits => |isi| isi.node(elf),
8369 .nobits => return diags.failParse(
82438370 path,
8244 "symbol '{s}' has unsupported binding (0x{x})",
8245 .{ name, bind },
8371 "cannot apply relocation to SHT_NOBITS section '{s}'",
8372 .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)},
82468373 ),
8247 .LOCAL => {
8248 const lsi = try elf.addLocalSymbol(.{
8249 .node = symbol_oni,
8250 .name = name,
8251 .value = symbol_value,
8252 .size = input_sym.size,
8253 .type = sym_type,
8254 .shndx = output_shndx,
8255 });
8256 si.* = .local(lsi);
8257 },
8258 .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| {
8259 si.* = .global(elf.addGlobalSymbol(.{
8260 .node = symbol_oni,
8261 .name = name,
8262 .value = symbol_value,
8263 .size = input_sym.size,
8264 .type = sym_type,
8265 .bind = switch (bind) {
8266 .WEAK, .GNU_UNIQUE => .weak,
8267 .GLOBAL => .strong,
8268 else => unreachable,
8269 },
8270 .visibility = input_sym.other.visibility,
8271 .shndx = output_shndx,
8272 }) catch |err| switch (err) {
8273 error.MultipleDefinitions => return diags.failParse(
8274 path,
8275 "multiple definitions of '{s}'",
8276 .{name},
8277 ),
8278 error.MultipleDefaultVersions => return diags.failParse(
8279 path,
8280 "multiple default versions of '{s}'",
8281 .{parseVersionedSymbolName(name).name},
8282 ),
8283 error.UndefinedDefaultVersion => unreachable, // shndx is not `.UNDEF` (i.e. the symbol is defined)
8284 else => |e| return e,
8285 });
8286 },
8287 }
8288 }
8289 for (sections[1..]) |*rel_sec| switch (rel_sec.shdr.type) {
8290 else => {},
8291 inline .REL, .RELA => |sht| {
8292 if (rel_sec.shdr.link != symtab_shndx or rel_sec.shdr.info == std.elf.SHN_UNDEF or
8293 rel_sec.shdr.info >= ehdr.shnum) continue;
8294 const Rel = switch (sht) {
8295 else => comptime unreachable,
8296 .REL => ElfN.Rel,
8297 .RELA => ElfN.Rela,
8298 };
8299 if (rel_sec.shdr.entsize < @sizeOf(Rel))
8300 return diags.failParse(path, "unsupported rel entsize", .{});
8301
8302 const loc_sec = &sections[rel_sec.shdr.info];
8303 const loc_node = switch (loc_sec.placement) {
8304 .none => continue,
8305 .progbits => |isi| isi.node(elf),
8306 .nobits => return diags.failParse(
8307 path,
8308 "cannot apply relocation to SHT_NOBITS section '{s}'",
8309 .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)},
8310 ),
8311 };
8374 };
83128375
8313 elf.resetNodeRelocs(loc_node);
8376 elf.resetNodeRelocs(loc_node);
83148377
8315 const relnum = std.math.divExact(
8316 u32,
8317 @intCast(rel_sec.shdr.size),
8318 @intCast(rel_sec.shdr.entsize),
8319 ) catch return diags.failParse(
8378 const relnum = std.math.divExact(
8379 u32,
8380 @intCast(rel_sec.shdr.size),
8381 @intCast(rel_sec.shdr.entsize),
8382 ) catch return diags.failParse(
8383 path,
8384 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
8385 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
8386 );
8387 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
8388 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
8389 for (0..relnum) |_| {
8390 const rel = try r.peekStruct(Rel, target_endian);
8391 try r.discardAll64(rel_sec.shdr.entsize);
8392 if (rel.info.sym == 0) continue;
8393 if (rel.info.sym - 1 > symmap.items.len) return diags.failParse(
83208394 path,
8321 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
8322 .{ rel_sec.shdr.size, rel_sec.shdr.entsize },
8395 "relocation target symbol index {d} exceeds symtab size",
8396 .{rel.info.sym},
83238397 );
8324 try elf.ensureUnusedRelocCapacity(loc_node, relnum);
8325 try fr.seekTo(fl.offset + rel_sec.shdr.offset);
8326 for (0..relnum) |_| {
8327 const rel = try r.peekStruct(Rel, target_endian);
8328 try r.discardAll64(rel_sec.shdr.entsize);
8329 if (rel.info.sym == 0) continue;
8330 if (rel.info.sym > symnum) return diags.failParse(
8331 path,
8332 "relocation target symbol index {d} exceeds symtab size",
8333 .{rel.info.sym},
8334 );
8335 const target = symmap.items[rel.info.sym - 1];
8336 if (target == Symbol.Id.null) {
8337 // If this is not an SHF_ALLOC section, then let's not report
8338 // this for now, because it probably doesn't affect the final
8339 // binary's functionality for this section to be a bit broken.
8340 if (loc_sec.shdr.flags.shf.ALLOC) {
8341 diags.addParseError(
8342 path,
8343 "unsupported symbol at index {d} required for relocation",
8344 .{rel.info.sym},
8345 );
8346 }
8347 continue;
8348 }
8349 const rt: MachineRelocType = .wrap(rel.info.type, elf);
8350 elf.addRelocAssumeCapacity(
8351 loc_node,
8352 rel.offset - loc_sec.shdr.addr,
8353 target,
8354 rel.addend,
8355 rt,
8356 ) catch |err| switch (err) {
8357 else => |e| return e,
8358 error.UnknownRelocation => diags.addParseError(
8398 const target = symmap.items[rel.info.sym - 1];
8399 if (target == Symbol.Id.null) {
8400 // If this is not an SHF_ALLOC section, then let's not report
8401 // this for now, because it probably doesn't affect the final
8402 // binary's functionality for this section to be a bit broken.
8403 if (loc_sec.shdr.flags.shf.ALLOC) {
8404 diags.addParseError(
83598405 path,
8360 "unknown relocation type '{f}'",
8361 .{rt.fmt(elf)},
8362 ),
8363 error.NonStaticRelocation => diags.addParseError(
8364 path,
8365 "non-static relocation type '{f}'",
8366 .{rt.fmt(elf)},
8367 ),
8368 error.UnimplementedRelocation => diags.addParseError(
8369 path,
8370 "TODO(Elf2): unimplemented relocation type '{f}'",
8371 .{rt.fmt(elf)},
8372 ),
8373 };
8406 "unsupported symbol at index {d} required for relocation",
8407 .{rel.info.sym},
8408 );
8409 }
8410 continue;
83748411 }
8375 },
8376 };
8377 },
8378 };
8412 const rt: MachineRelocType = .wrap(rel.info.type, elf);
8413 elf.addRelocAssumeCapacity(
8414 loc_node,
8415 rel.offset - loc_sec.shdr.addr,
8416 target,
8417 rel.addend,
8418 rt,
8419 ) catch |err| switch (err) {
8420 else => |e| return e,
8421 error.UnknownRelocation => diags.addParseError(
8422 path,
8423 "unknown relocation type '{f}'",
8424 .{rt.fmt(elf)},
8425 ),
8426 error.NonStaticRelocation => diags.addParseError(
8427 path,
8428 "non-static relocation type '{f}'",
8429 .{rt.fmt(elf)},
8430 ),
8431 error.UnimplementedRelocation => diags.addParseError(
8432 path,
8433 "TODO(Elf2): unimplemented relocation type '{f}'",
8434 .{rt.fmt(elf)},
8435 ),
8436 };
8437 }
8438 },
8439 };
8440 }
83798441
83808442 if (input_has_bss) {
83818443 try elf.bss_inputs.append(gpa, input_index);
......@@ -8435,6 +8497,10 @@ fn loadDso(
84358497 fallback_soname: link.Input.Dso.FallbackSoname,
84368498 fr: *Io.File.Reader,
84378499) (LoadParseInputError || error{BadMagic})!void {
8500 const trace = tracy.trace(@src());
8501 defer trace.end();
8502 trace.addTextFmt("{f}", .{path.fmtEscapeString()});
8503
84388504 const comp = elf.base.comp;
84398505 const gpa = comp.gpa;
84408506 const diags = &comp.link_diags;
......@@ -8580,6 +8646,9 @@ fn loadDso(
85808646 const verdef: []DsoGlobals.String.Optional = verdef: {
85818647 const verdef_sh = shdr.verdef orelse break :verdef &.{};
85828648
8649 const versions_trace = tracy.traceNamed(@src(), "dso versions");
8650 defer versions_trace.end();
8651
85838652 // This parsing is a bit of a mess because it appears that at this moment in history
85848653 // nobody at GNU was yet aware of the concept of an "array".
85858654
......@@ -8663,6 +8732,9 @@ fn loadDso(
86638732 const soname = try elf.string(.dynstr, soname_slice);
86648733 try elf.needed.put(gpa, soname, {});
86658734
8735 const symbols_trace = tracy.traceNamed(@src(), "dso symbols");
8736 defer symbols_trace.end();
8737
86668738 // Scan the symbol table and populate `elf.dso_globals`.
86678739 const first_global = @min(shdr.dynsym.info, symnum);
86688740 try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global);
......@@ -9544,6 +9616,9 @@ fn addRelocAssumeCapacity(
95449616 addend: i64,
95459617 @"type": MachineRelocType,
95469618) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void {
9619 const trace = tracy.trace(@src());
9620 defer trace.end();
9621
95479622 switch (elf.ehdrType()) {
95489623 .REL => {
95499624 const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx;
......@@ -9925,6 +10000,9 @@ fn addSymbolRelocAssumeCapacityInner(
992510000) Error!void {
992610001 assert(elf.ehdrType() != .REL);
992710002
10003 const trace = tracy.trace(@src());
10004 defer trace.end();
10005
992810006 const gpa = elf.base.comp.gpa;
992910007
993010008 request_static_target: {
src/link/MappedFile.zig+34
......@@ -11,6 +11,8 @@ const assert = std.debug.assert;
1111const linux = std.os.linux;
1212const windows = std.os.windows;
1313
14const tracy = @import("../tracy.zig");
15
1416io: Io,
1517flags: packed struct {
1618 block_size: Alignment,
......@@ -892,6 +894,9 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
892894}) Error!Node.Index {
893895 mf.nodes_lock.assertUnlocked();
894896
897 const trace = tracy.trace(@src());
898 defer trace.end();
899
895900 try mf.nodes.ensureUnusedCapacity(gpa, 1);
896901 try mf.large.ensureUnusedCapacity(gpa, 2);
897902
......@@ -1014,6 +1019,9 @@ fn shrinkLeafNode(
10141019) Error!void {
10151020 mf.nodes_lock.assertUnlocked();
10161021
1022 const trace = tracy.trace(@src());
1023 defer trace.end();
1024
10171025 const old_offset, const old_size = ni.location(mf).resolve(mf);
10181026
10191027 assert(new_size < old_size);
......@@ -1152,6 +1160,9 @@ fn growNode(
11521160) Error!void {
11531161 mf.nodes_lock.assertUnlocked();
11541162
1163 const trace = tracy.trace(@src());
1164 defer trace.end();
1165
11551166 const node = ni.get(mf);
11561167
11571168 const old_offset, const old_size = node.location().resolve(mf);
......@@ -1569,6 +1580,9 @@ fn growFloatingNodeWithAlignment(
15691580) Error!void {
15701581 mf.nodes_lock.assertUnlocked();
15711582
1583 const trace = tracy.trace(@src());
1584 defer trace.end();
1585
15721586 const parent_ni = ni.parent(mf).unwrap().?; // `ni` cannot be `.root`
15731587 const old_offset, const old_size = ni.location(mf).resolve(mf);
15741588
......@@ -1791,6 +1805,9 @@ fn growNodeViaInsertRange(
17911805 new_size: u64,
17921806 grow_options: GrowOptions,
17931807) Error!bool {
1808 const trace = tracy.trace(@src());
1809 defer trace.end();
1810
17941811 if (!is_linux or mf.flags.fallocate_insert_range_unsupported) {
17951812 return false;
17961813 }
......@@ -1999,6 +2016,9 @@ fn ensureAdditionalHeaderCapacity(
19992016 parent_ni: Node.Index,
20002017 extra_capacity: u64,
20012018) Error!void {
2019 const trace = tracy.trace(@src());
2020 defer trace.end();
2021
20022022 _, const parent_size = parent_ni.location(mf).resolve(mf);
20032023
20042024 const last_header_oni = parent_ni.lastHeader(mf);
......@@ -2274,6 +2294,9 @@ fn realignNode(
22742294) Error!void {
22752295 mf.nodes_lock.assertUnlocked();
22762296
2297 const trace = tracy.trace(@src());
2298 defer trace.end();
2299
22772300 const old_offset, const old_size = ni.location(mf).resolve(mf);
22782301
22792302 if (ni == .root or ni.position(mf) != .floating) {
......@@ -2310,6 +2333,10 @@ fn updateWriters(mf: *MappedFile) void {
23102333fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: u64) Error!void {
23112334 if (old_file_offset == new_file_offset) return;
23122335
2336 const trace = tracy.trace(@src());
2337 defer trace.end();
2338 trace.addTextFmt("{Bi} (0x{X} -> 0x{X})", .{ size, old_file_offset, new_file_offset });
2339
23132340 if (old_file_offset >= new_file_offset + size or
23142341 new_file_offset >= old_file_offset + size)
23152342 {
......@@ -2348,6 +2375,10 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size:
23482375 }
23492376}
23502377fn zeroRange(mf: *MappedFile, file_offset: u64, size: u64) Error!void {
2378 const trace = tracy.trace(@src());
2379 defer trace.end();
2380 trace.addTextFmt("{Bi} (at 0x{X})", .{ size, file_offset });
2381
23512382 if (is_linux and
23522383 !mf.flags.fallocate_punch_hole_unsupported and
23532384 size >= mf.flags.block_size.toByteUnits() * 2 - 1)
......@@ -2395,6 +2426,9 @@ fn copyFileRange(
23952426 return 0;
23962427 }
23972428
2429 const trace = tracy.trace(@src());
2430 defer trace.end();
2431
23982432 const min_size = mf.flags.block_size.toByteUnits() * 2 - 1;
23992433 if (size < min_size) return 0;
24002434