authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-09-19 15:35:06-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2026-09-19 15:35:06-04:00
log6ec487d91da45787cf94f02997fe195aac58aef5
tree7adf0a2624be60decbdf3b3b56fa2a2d2a52f520
parent96c654941c98017e9e59fe38e06d9a334fb6c80c

Coff: fix several lifetime issues with slices into `string_bytes`


1 files changed, 69 insertions(+), 49 deletions(-)

src/link/Coff.zig+69-49
......@@ -647,15 +647,12 @@ pub const SymbolTable = struct {
647647 };
648648
649649 pub const SymbolName = union(enum) {
650 short: []const u8,
650 short: [8]u8,
651651 long: StringIndex,
652652
653653 pub fn store(name: SymbolName, coff: *const Coff, field: *[8]u8) void {
654654 switch (name) {
655 .short => |s| {
656 @memcpy(field[0..s.len], s);
657 @memset(field[s.len..], 0);
658 },
655 .short => |s| field.* = s,
659656 .long => |l| {
660657 @memset(field[0..4], 0);
661658 std.mem.writePackedInt(u32, field[4..], 0, @backingInt(l), coff.targetEndian());
......@@ -2728,29 +2725,42 @@ fn getString(coff: *Coff, string: []const u8) String.Optional {
27282725 return .none;
27292726}
27302727
2731/// If the name does not fit in the symbol header, adds it to the symbol table string table.
2732/// If the caller knows this name already has a String associated with it, they can avoid
2733/// a redundant call to `getOrPutString` by specifying `opt_string`.
2734/// The lifetime of the return value matches that of `name`.
2735fn getOrPutSymbolName(coff: *Coff, name: []const u8, opt_string: ?String) !SymbolTable.SymbolName {
2728/// If the name does not fit within inline storage, adds it to the symbol table string table.
2729fn getOrPutSymbolName(coff: *Coff, name: union(enum) {
2730 slice: []const u8,
2731 string: String,
2732}) !SymbolTable.SymbolName {
27362733 assert(!coff.isImage());
27372734 const gpa = coff.base.comp.gpa;
2735 const slice = switch (name) {
2736 .slice => |s| s,
2737 .string => |s| s.toSlice(coff),
2738 };
2739
2740 if (slice.len > header_name_max_len) {
2741 const string = switch (name) {
2742 .slice => |s| try coff.getOrPutString(s),
2743 .string => |s| s,
2744 };
27382745
2739 return if (name.len > header_name_max_len) name: {
2740 const string = opt_string orelse try coff.getOrPutString(name);
27412746 const string_gop = try coff.symbol_table.strings.getOrPut(gpa, string);
27422747 if (!string_gop.found_existing) {
27432748 const string_index = coff.symbol_table.strings_ni.location(&coff.mf).resolve(&coff.mf)[1];
27442749 string_gop.value_ptr.* = @fromBackingInt(@intCast(string_index));
27452750
2746 try coff.symbol_table.strings_ni.resizeLeaf(gpa, &coff.mf, string_index + name.len + 1);
2747 const slice = coff.symbol_table.strings_ni.slice(&coff.mf);
2748 @memcpy(slice[@intCast(string_index)..][0..name.len], name);
2749 slice[@intCast(string_index + name.len)] = 0;
2751 try coff.symbol_table.strings_ni.resizeLeaf(gpa, &coff.mf, string_index + slice.len + 1);
2752 const storage = coff.symbol_table.strings_ni.slice(&coff.mf);
2753 @memcpy(storage[@intCast(string_index)..][0..slice.len], slice);
2754 storage[@intCast(string_index + slice.len)] = 0;
27502755 }
27512756
2752 break :name .{ .long = string_gop.value_ptr.* };
2753 } else .{ .short = name };
2757 return .{ .long = string_gop.value_ptr.* };
2758 } else {
2759 var symbol_name: SymbolTable.SymbolName = .{ .short = undefined };
2760 @memcpy(symbol_name.short[0..slice.len], slice);
2761 @memset(symbol_name.short[slice.len..], 0);
2762 return symbol_name;
2763 }
27542764}
27552765
27562766/// `len` does not include null terminators
......@@ -2862,7 +2872,7 @@ pub fn globalSymbol(coff: *Coff, opts: struct {
28622872 break :lib_name .lib_c;
28632873 }
28642874
2865 try coff.ensureManyUnusedStringCapacity(2, opts.name.len + lib_name.len);
2875 try coff.ensureManyUnusedStringCapacity(2, opts.name.len + lib_name.len + 2);
28662876 break :lib_name .{ .explicit = coff.getOrPutStringAssumeCapacity(lib_name) };
28672877 } else .any;
28682878
......@@ -3146,7 +3156,7 @@ fn ensureMemberSymbol(coff: *Coff, mi: Member.Index, name: String) !void {
31463156 coff.targetStore(coff.secondLinkerMemberNumSymbolsPtr(), @backingInt(mfli) + 1);
31473157 if (!needs_sort) {
31483158 @memmove(slice[new_header_size..][0..coff.lib_string_len], slice[old_header_size..][0..coff.lib_string_len]);
3149 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice[0..name_slice.len]);
3159 @memcpy(slice[new_header_size + coff.lib_string_len ..][0..name_slice.len], name_slice);
31503160 slice[new_header_size + coff.lib_string_len + name_slice.len] = 0;
31513161 } else if (!old_needs_sort) {
31523162 // The entire string table is rebuilt in flushMember after sorting
......@@ -3175,9 +3185,8 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31753185 const entry = coff.symbolTableEntryPtr(sti.*) orelse entry: {
31763186 const symbol_name, const num_aux_symbols: u8, const complex_type: std.coff.ComplexType =
31773187 if (sym.gmi != .none) blk: {
3178 const name = sym.gmi.name(coff);
31793188 break :blk .{
3180 try coff.getOrPutSymbolName(name.toSlice(coff), name),
3189 try coff.getOrPutSymbolName(.{ .string = sym.gmi.name(coff) }),
31813190 @intFromBool(sym.flags.weak_external_strat != .none),
31823191 if (Symbol.Index.text.get(coff).section_number == sym.section_number)
31833192 .FUNCTION
......@@ -3186,7 +3195,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31863195 };
31873196 } else blk: switch (coff.getNode(sym.ni.unwrap().?)) {
31883197 .image_section => .{
3189 try coff.getOrPutSymbolName(&sym.section_number.header(coff).name, null),
3198 try coff.getOrPutSymbolName(.{ .slice = &sym.section_number.header(coff).name }),
31903199 1,
31913200 .NULL,
31923201 },
......@@ -3195,7 +3204,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
31953204 const ip = &zcu.intern_pool;
31963205 const nav = ip.getNav(nmi.navIndex(coff));
31973206 break :blk .{
3198 try coff.getOrPutSymbolName(nav.fqn.toSlice(ip), null),
3207 try coff.getOrPutSymbolName(.{ .slice = nav.fqn.toSlice(ip) }),
31993208 0,
32003209 if (ip.isFunctionType(nav.resolved.?.type)) .FUNCTION else .NULL,
32013210 };
......@@ -3204,7 +3213,7 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
32043213 var name_buf: [std.fmt.count("__anon_{d}", .{std.math.maxInt(u32)})]u8 = undefined;
32053214 const name = std.mem.print(&name_buf, "__anon_{d}", .{umi}) catch unreachable;
32063215 break :blk .{
3207 try coff.getOrPutSymbolName(name, null),
3216 try coff.getOrPutSymbolName(.{ .slice = name }),
32083217 0,
32093218 .NULL,
32103219 };
......@@ -3218,9 +3227,8 @@ fn flushSymbolTableEntry(coff: *Coff, index: u32) !void {
32183227 lazy_sym.kind, mi,
32193228 }) catch unreachable;
32203229
3221 const string = try coff.getOrPutString(name);
32223230 break :blk .{
3223 try coff.getOrPutSymbolName(string.toSlice(coff), string),
3231 try coff.getOrPutSymbolName(.{ .slice = name }),
32243232 0,
32253233 if (tag == .lazy_code) .FUNCTION else .NULL,
32263234 };
......@@ -3451,8 +3459,8 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
34513459 if (coff.targetEndian() != native_endian)
34523460 std.mem.byteSwapAllFields(std.coff.SectionHeader, section);
34533461
3454 const name_slice = name.toSlice(coff);
34553462 if (coff.isImage()) {
3463 const name_slice = name.toSlice(coff);
34563464 @memcpy(section.name[0..name_slice.len], name_slice);
34573465 @memset(section.name[name_slice.len..], 0);
34583466 switch (coff.optionalHeaderPtr()) {
......@@ -3462,7 +3470,7 @@ fn addSection(coff: *Coff, name: String, flags: std.coff.SectionHeader.Flags) !S
34623470 ),
34633471 }
34643472 } else {
3465 (try coff.getOrPutSymbolName(name_slice, name)).store(coff, &section.name);
3473 (try coff.getOrPutSymbolName(.{ .string = name })).store(coff, &section.name);
34663474 try coff.pendingSymbolTableEntry(si);
34673475 }
34683476
......@@ -3585,12 +3593,21 @@ fn objectSectionMapIndex(
35853593 const object_section_gop = try coff.object_section_table.getOrPut(gpa, name);
35863594 const osmi: Node.ObjectSectionMapIndex = @fromBackingInt(@intCast(object_section_gop.index));
35873595 const sym = if (!object_section_gop.found_existing) sym: {
3588 const name_slice = name.toSlice(coff);
3589 const parent_name_slice = coff.objectSectionParentName(name_slice);
3590 const parent_name = if (parent_name_slice.len < name_slice.len) parent_name: {
3591 try coff.ensureUnusedStringCapacity(parent_name_slice.len);
3592 break :parent_name coff.getOrPutStringAssumeCapacity(name.toSlice(coff)[0..parent_name_slice.len]);
3593 } else name;
3596 const name_slice, const parent_name = blk: {
3597 var name_slice = name.toSlice(coff);
3598 const parent_name_slice = coff.objectSectionParentName(name_slice);
3599 const parent_name = if (parent_name_slice.len < name_slice.len) parent_name: {
3600 try coff.ensureUnusedStringCapacity(parent_name_slice.len);
3601 name_slice = name.toSlice(coff);
3602 break :parent_name coff.getOrPutStringAssumeCapacity(name_slice[0..parent_name_slice.len]);
3603 } else name;
3604
3605 break :blk .{ name_slice, parent_name };
3606 };
3607
3608 coff.string_bytes.lockPointers();
3609 defer coff.string_bytes.unlockPointers();
3610
35943611 const parent = (try coff.pseudoSectionMapIndex(parent_name, alignment, attributes)).symbol(coff);
35953612 try coff.nodes.ensureUnusedCapacity(gpa, 1);
35963613 try coff.symbols.ensureUnusedCapacity(gpa, 1);
......@@ -5998,12 +6015,12 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
59986015 break :task;
59996016 }
60006017 if (coff.pending_input) |pending_iami| {
6001 const name_slice = pending_iami.member(coff).name.toSlice(coff);
60026018 const sub_prog_node = coff.input_prog_node.start(
6003 name_slice,
6019 pending_iami.member(coff).name.toSlice(coff),
60046020 0,
60056021 );
60066022 defer sub_prog_node.end();
6023
60076024 coff.pending_input = null;
60086025 coff.flushInputMember(pending_iami) catch |err| switch (err) {
60096026 error.OutOfMemory => return error.OutOfMemory,
......@@ -6011,7 +6028,7 @@ fn resolve(coff: *Coff, tid: Zcu.PerThread.Id) !bool {
60116028 "linker failed to load archive member '{f}{f}': {t}",
60126029 .{
60136030 pending_iami.member(coff).iai.path(coff),
6014 fmtMemberNameString(name_slice),
6031 fmtMemberNameString(pending_iami.member(coff).name.toSlice(coff)),
60156032 e,
60166033 },
60176034 ),
......@@ -6357,16 +6374,18 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
63576374
63586375 const import: Import = import: {
63596376 const sym = si.get(coff);
6360 const name_slice = name.toSlice(coff);
6361 const imp_match = std.mem.startsWith(u8, name_slice, imp_prefix);
6377 const imp_match, const name_len = blk: {
6378 const name_slice = name.toSlice(coff);
6379 break :blk .{ std.mem.startsWith(u8, name_slice, imp_prefix), name_slice.len };
6380 };
63626381
63636382 // Globals may have the __imp_ prefix already if they are undef externals from another input.
63646383 assert(sym.flags.dll_storage_class != .dllexport);
63656384 const search_name, const is_imp = if (imp_match or sym.flags.dll_storage_class != .dllimport)
63666385 .{ name, imp_match }
63676386 else name: {
6368 try coff.ensureUnusedStringCapacity(imp_prefix.len + name_slice.len);
6369 const imp_name = try gpa.print(imp_prefix ++ "{s}", .{name_slice});
6387 try coff.ensureUnusedStringCapacity(imp_prefix.len + name_len);
6388 const imp_name = try gpa.print(imp_prefix ++ "{s}", .{name.toSlice(coff)});
63706389 defer gpa.free(imp_name);
63716390 break :name .{ coff.getOrPutStringAssumeCapacity(imp_name), true };
63726391 };
......@@ -6429,6 +6448,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
64296448 .NAME_UNDECORATE,
64306449 => |tag| {
64316450 const symbol_name: []const u8 = import.symbol_name.toSlice(coff);
6451 const name_slice = name.toSlice(coff);
64326452 const end_match = std.mem.endsWith(u8, name_slice, symbol_name);
64336453 const len_delta = name_slice.len -% symbol_name.len;
64346454 if (!end_match or
......@@ -6520,10 +6540,10 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65206540
65216541 const target_endian = coff.targetEndian();
65226542 const addr_info = coff.targetAddrInfo();
6523 const lib_name = import.lib_name.toSlice(coff);
6543 const lib_name_slice = import.lib_name.toSlice(coff);
65246544 const gop = try coff.import_table.entries.getOrPutAdapted(
65256545 gpa,
6526 lib_name,
6546 lib_name_slice,
65276547 ImportTable.Adapter{ .coff = coff },
65286548 );
65296549 const import_hint_name_align: Alignment = .@"2";
......@@ -6535,7 +6555,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65356555 @sizeOf(std.coff.ImportDirectoryEntry) * (gop.index + 2),
65366556 );
65376557 const import_hint_name_table_len =
6538 import_hint_name_align.forward(lib_name.len + ".dll".len + 1);
6558 import_hint_name_align.forward(lib_name_slice.len + ".dll".len + 1);
65396559 const idata_section_ni = coff.import_table.ni.parent(&coff.mf).unwrap().?;
65406560 const import_lookup_table_ni = try idata_section_ni.addFloatingChild(gpa, &coff.mf, .{
65416561 .size = addr_info.size * 2,
......@@ -6570,9 +6590,9 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65706590 .hint_name_len = @intCast(import_hint_name_table_len),
65716591 };
65726592 const import_hint_name_slice = import_hint_name_table_ni.slice(&coff.mf);
6573 @memcpy(import_hint_name_slice[0..lib_name.len], lib_name);
6574 @memcpy(import_hint_name_slice[lib_name.len..][0..".dll".len], ".dll");
6575 @memset(import_hint_name_slice[lib_name.len + ".dll".len ..], 0);
6593 @memcpy(import_hint_name_slice[0..lib_name_slice.len], lib_name_slice);
6594 @memcpy(import_hint_name_slice[lib_name_slice.len..][0..".dll".len], ".dll");
6595 @memset(import_hint_name_slice[lib_name_slice.len + ".dll".len ..], 0);
65766596 coff.nodes.appendAssumeCapacity(.{ .import_lookup_table = @fromBackingInt(@intCast(gop.index)) });
65776597 coff.nodes.appendAssumeCapacity(.{ .import_address_table = @fromBackingInt(@intCast(gop.index)) });
65786598 coff.nodes.appendAssumeCapacity(.{ .import_hint_name_table = @fromBackingInt(@intCast(gop.index)) });
......@@ -6597,7 +6617,7 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !bool {
65976617
65986618 log.debug(
65996619 "flushGlobalImport({s}, {?s}, {d}, {s})",
6600 .{ name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name },
6620 .{ name.toSlice(coff), import.name.toSlice(coff), import.ordinal_hint, lib_name_slice },
66016621 );
66026622
66036623 const iat_symbol_gop = try coff.import_table.iat_symbol_indices.getOrPut(gpa, .{