| ... | @@ -2349,6 +2349,9 @@ const SymbolReloc = struct { | ... | @@ -2349,6 +2349,9 @@ const SymbolReloc = struct { |
| 2349 | assert(elf.ehdrType() != .REL); | 2349 | assert(elf.ehdrType() != .REL); |
| 2350 | const reloc_node = reloc.node.unwrap().?; // deleted | 2350 | const reloc_node = reloc.node.unwrap().?; // deleted |
| 2351 | | 2351 | |
| | 2352 | const trace = tracy.trace(@src()); |
| | 2353 | defer trace.end(); |
| | 2354 | |
| 2352 | // Delete any current output relocation or error. | 2355 | // Delete any current output relocation or error. |
| 2353 | switch (reloc.bits.result) { | 2356 | switch (reloc.bits.result) { |
| 2354 | .ok, .overflowed, .misaligned => { | 2357 | .ok, .overflowed, .misaligned => { |
| ... | @@ -3086,6 +3089,10 @@ const AddLocalSymbolOptions = struct { | ... | @@ -3086,6 +3089,10 @@ const AddLocalSymbolOptions = struct { |
| 3086 | fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex { | 3089 | fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex { |
| 3087 | const gpa = elf.base.comp.gpa; | 3090 | const gpa = elf.base.comp.gpa; |
| 3088 | | 3091 | |
| | 3092 | const trace = tracy.trace(@src()); |
| | 3093 | defer trace.end(); |
| | 3094 | trace.addText(opts.name); |
| | 3095 | |
| 3089 | assert(opts.shndx != .UNDEF); | 3096 | assert(opts.shndx != .UNDEF); |
| 3090 | | 3097 | |
| 3091 | try elf.symtab.ensureUnusedCapacity(gpa, 1); | 3098 | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| ... | @@ -3162,6 +3169,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ | ... | @@ -3162,6 +3169,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 3162 | } || Error)!Symbol.Global.Index { | 3169 | } || Error)!Symbol.Global.Index { |
| 3163 | _ = opts.lib_name; // TODO | 3170 | _ = opts.lib_name; // TODO |
| 3164 | | 3171 | |
| | 3172 | const trace = tracy.trace(@src()); |
| | 3173 | defer trace.end(); |
| | 3174 | trace.addText(opts.name); |
| | 3175 | |
| 3165 | log.debug("addGlobalSymbol({q})", .{opts.name}); | 3176 | log.debug("addGlobalSymbol({q})", .{opts.name}); |
| 3166 | | 3177 | |
| 3167 | const gpa = elf.base.comp.gpa; | 3178 | const gpa = elf.base.comp.gpa; |
| ... | @@ -4347,6 +4358,9 @@ const Symbol = struct { | ... | @@ -4347,6 +4358,9 @@ const Symbol = struct { |
| 4347 | /// Returns `true` if any relocations were updated, in which case relocations targeting | 4358 | /// Returns `true` if any relocations were updated, in which case relocations targeting |
| 4348 | /// `gsi` must be re-applied by the caller. | 4359 | /// `gsi` must be re-applied by the caller. |
| 4349 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { | 4360 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { |
| | 4361 | const trace = tracy.trace(@src()); |
| | 4362 | defer trace.end(); |
| | 4363 | |
| 4350 | if (elf.shndx.dynamic == .UNDEF) return false; | 4364 | if (elf.shndx.dynamic == .UNDEF) return false; |
| 4351 | const sym_id: Symbol.Id = .global(gsi); | 4365 | const sym_id: Symbol.Id = .global(gsi); |
| 4352 | | 4366 | |
| ... | @@ -4517,20 +4531,8 @@ const Symbol = struct { | ... | @@ -4517,20 +4531,8 @@ const Symbol = struct { |
| 4517 | } | 4531 | } |
| 4518 | | 4532 | |
| 4519 | fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void { | 4533 | fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void { |
| 4520 | const trace = tracy.trace(@src()); | | |
| 4521 | defer trace.end(); | | |
| 4522 | | | |
| 4523 | const sym_index = sym_id.index(elf); | 4534 | const sym_index = sym_id.index(elf); |
| 4524 | | 4535 | |
| 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 | | | |
| 4534 | switch (elf.symPtr(sym_index)) { | 4536 | switch (elf.symPtr(sym_index)) { |
| 4535 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), | 4537 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 4536 | } | 4538 | } |
| ... | @@ -4555,6 +4557,9 @@ const Symbol = struct { | ... | @@ -4555,6 +4557,9 @@ const Symbol = struct { |
| 4555 | } | 4557 | } |
| 4556 | | 4558 | |
| 4557 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { | 4559 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| | 4560 | const trace = tracy.trace(@src()); |
| | 4561 | defer trace.end(); |
| | 4562 | |
| 4558 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; | 4563 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4559 | while (ri != .none) { | 4564 | while (ri != .none) { |
| 4560 | const reloc = ri.get(elf); | 4565 | const reloc = ri.get(elf); |
| ... | @@ -7709,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void | ... | @@ -7709,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void |
| 7709 | } | 7714 | } |
| 7710 | } | 7715 | } |
| 7711 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fr: *Io.File.Reader) (LoadParseInputError || error{BadMagic})!void { | 7716 | fn 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 | |
| 7712 | const comp = elf.base.comp; | 7721 | const comp = elf.base.comp; |
| 7713 | const gpa = comp.gpa; | 7722 | const gpa = comp.gpa; |
| 7714 | const diags = &comp.link_diags; | 7723 | const diags = &comp.link_diags; |
| ... | @@ -7788,6 +7797,10 @@ fn loadObject( | ... | @@ -7788,6 +7797,10 @@ fn loadObject( |
| 7788 | fr: *Io.File.Reader, | 7797 | fr: *Io.File.Reader, |
| 7789 | fl: MappedFile.Node.FileLocation, | 7798 | fl: MappedFile.Node.FileLocation, |
| 7790 | ) LoadParseInputError!void { | 7799 | ) LoadParseInputError!void { |
| | 7800 | const trace = tracy.trace(@src()); |
| | 7801 | defer trace.end(); |
| | 7802 | trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) }); |
| | 7803 | |
| 7791 | const comp = elf.base.comp; | 7804 | const comp = elf.base.comp; |
| 7792 | const gpa = comp.gpa; | 7805 | const gpa = comp.gpa; |
| 7793 | const diags = &comp.link_diags; | 7806 | const diags = &comp.link_diags; |
| ... | @@ -7939,443 +7952,492 @@ fn loadObject( | ... | @@ -7939,443 +7952,492 @@ fn loadObject( |
| 7939 | defer gpa.free(shstrtab); | 7952 | defer gpa.free(shstrtab); |
| 7940 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); | 7953 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 7941 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); | 7954 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 7942 | for (sections[1..]) |*section| { | 7955 | |
| 7943 | if (section.shdr.name >= shstrtab.len) continue; | 7956 | { |
| 7944 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); | 7957 | const sections_trace = tracy.traceNamed(@src(), "scan input sections"); |
| 7945 | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; | 7958 | defer sections_trace.end(); |
| 7946 | const sect_type = section.shdr.type; | 7959 | for (sections[1..]) |*section| { |
| 7947 | const opts: struct { | 7960 | const section_trace = tracy.traceNamed(@src(), "input section"); |
| 7948 | shndx: Section.Index, | 7961 | defer section_trace.end(); |
| 7949 | node_fixed: bool, | 7962 | if (section.shdr.name >= shstrtab.len) continue; |
| 7950 | } = switch (sect_type) { | 7963 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 7951 | else => continue, | 7964 | section_trace.addText(name); |
| 7952 | .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { | 7965 | if (!comp.config.any_unwind_tables and std.mem.eql(u8, name, ".eh_frame")) continue; |
| 7953 | const shndx = elf.mapInputSection(.{ | 7966 | const sect_type = section.shdr.type; |
| 7954 | .name = name, | 7967 | const opts: struct { |
| 7955 | .flags = section.shdr.flags.shf, | 7968 | shndx: Section.Index, |
| 7956 | .entsize = section.shdr.entsize, | 7969 | node_fixed: bool, |
| 7957 | .type = section.shdr.type, | 7970 | } = switch (sect_type) { |
| 7958 | }) catch |err| switch (err) { | 7971 | else => continue, |
| 7959 | else => |e| return e, | 7972 | .PROGBITS, .NOBITS, .X86_64_UNWIND => opts: { |
| 7960 | error.StripSection => continue, | 7973 | const shndx = elf.mapInputSection(.{ |
| 7961 | error.TlsSectionUnavailable => { | 7974 | .name = name, |
| 7962 | if (section.shdr.size == 0) continue; | 7975 | .flags = section.shdr.flags.shf, |
| 7963 | | 7976 | .entsize = section.shdr.entsize, |
| 7964 | return diags.failParse( | 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( |
| 7965 | path, | 8015 | 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", |
| 7967 | .{name}, | 8025 | .{name}, |
| 7968 | ); | 8026 | ); |
| 7969 | }, | 8027 | // TODO: handle compressed input sections. We'll need to set a flag to |
| 7970 | error.UnsupportedSectionFlags => if (!section.shdr.flags.shf.ALLOC) { | 8028 | // indicate that `flushInputSection` needs to decompress the section. |
| 7971 | // It probably doesn't matter, just skip this section. | 8029 | // But because this section isn't SHF_ALLOC, it's probably okay to just |
| 7972 | continue; | 8030 | // skip it for now. |
| 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. | | |
| 7988 | continue; | 8031 | continue; |
| 7989 | } else return diags.failParse( | 8032 | } |
| 7990 | path, | 8033 | break :opts .{ |
| 7991 | "flags of section '{s}' conflict with other inputs", | 8034 | .shndx = shndx, |
| 7992 | .{name}, | 8035 | // For well-known sections, we know that it's fine to have e.g. random |
| 7993 | ), | 8036 | // padding, so there's no need to make the sections fixed. For custom |
| 7994 | error.UnsupportedNobitsSection => return diags.failParse( | 8037 | // sections, however, we do want fixed nodes to avoid padding. |
| 7995 | path, | 8038 | .node_fixed = shndx != .text and |
| 7996 | "unsupported section type SHT_NOBITS for non-bss section '{s}'", | 8039 | shndx != .rodata and |
| 7997 | .{name}, | 8040 | shndx != .data and |
| 7998 | ), | 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 | }, |
| 7999 | }; | 8097 | }; |
| 8000 | if (section.shdr.flags.shf.COMPRESSED) { | 8098 | |
| 8001 | // SHF_COMPRESSED is only allowed on non-alloc sections. | 8099 | section.placement = .{ .nobits = .{ |
| 8002 | if (section.shdr.flags.shf.ALLOC) return diags.failParse( | 8100 | .shndx = opts.shndx, |
| 8003 | path, | 8101 | .offset = offset, |
| 8004 | "section '{s}' has conflicting flags SHF_ALLOC and SHF_COMPRESSED", | 8102 | } }; |
| 8005 | .{name}, | 8103 | if (opts.shndx == elf.shndx.bss) { |
| 8006 | ); | 8104 | input_has_bss = true; |
| 8007 | // TODO: handle compressed input sections. We'll need to set a flag to | 8105 | } else if (opts.shndx == elf.shndx.tbss) { |
| 8008 | // indicate that `flushInputSection` needs to decompress the section. | 8106 | input_has_tbss = true; |
| 8009 | // But because this section isn't SHF_ALLOC, it's probably okay to just | 8107 | } else { |
| 8010 | // skip it for now. | 8108 | assert(elf.ehdrType() == .REL); |
| 8011 | continue; | | |
| 8012 | } | 8109 | } |
| 8013 | break :opts .{ | 8110 | |
| 8014 | .shndx = shndx, | 8111 | continue; |
| 8015 | // For well-known sections, we know that it's fine to have e.g. random | 8112 | } |
| 8016 | // padding, so there's no need to make the sections fixed. For custom | 8113 | |
| 8017 | // sections, however, we do want fixed nodes to avoid padding. | 8114 | const add_node_opts: MappedFile.Node.AddOptions = .{ |
| 8018 | .node_fixed = shndx != .text and | 8115 | .size = need_align.forward(section.shdr.size), |
| 8019 | shndx != .rodata and | 8116 | .alignment = need_align, |
| 8020 | shndx != .data and | 8117 | .moved = true, // see assert at end of `flushInputSection` |
| 8021 | shndx != .data_rel_ro and | 8118 | }; |
| 8022 | shndx != elf.shndx.tdata, | 8119 | const ni = elf.addNodeAssumeCapacity( |
| 8023 | }; | 8120 | if (opts.node_fixed) ni: { |
| 8024 | }, | 8121 | const shndx_ni = opts.shndx.node(elf); |
| 8025 | inline .INIT_ARRAY, .FINI_ARRAY, .PREINIT_ARRAY => |@"type"| .{ | 8122 | const after_oni: MappedFile.Node.Index.Optional = after: { |
| 8026 | .shndx = shndx: { | 8123 | var node_it = shndx_ni.last(&elf.mf); |
| 8027 | // TODO: the input section name may include a "priority" value between 1 | 8124 | while (node_it.unwrap()) |cur_ni| { |
| 8028 | // and 65535 which should affect the order we assemble input sections in | 8125 | switch (cur_ni.position(&elf.mf)) { |
| 8029 | const init_fini_section_name: []const u8 = switch (@"type") { | 8126 | .header => break :after .wrap(cur_ni), // last header |
| 8030 | .INIT_ARRAY => "init_array", | 8127 | .footer, .floating => node_it = cur_ni.prev(&elf.mf), |
| 8031 | .FINI_ARRAY => "fini_array", | 8128 | } |
| 8032 | .PREINIT_ARRAY => "preinit_array", | 8129 | } else { |
| 8033 | else => comptime unreachable, | 8130 | break :after .none; |
| 8034 | }; | 8131 | } |
| 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, | | |
| 8040 | }; | 8132 | }; |
| 8041 | if (section.shdr.addralign != need_addralign) { | 8133 | break :ni try shndx_ni.addHeaderChildAfter(gpa, &elf.mf, after_oni, add_node_opts); |
| 8042 | return diags.failParse(path, "bad addralign on {t} shdr", .{@"type"}); | 8134 | } else try opts.shndx.node(elf).addFloatingChild(gpa, &elf.mf, add_node_opts), |
| 8043 | } | 8135 | .{ .input_section = @fromBackingInt(@intCast(elf.input_sections.items.len)) }, |
| 8044 | if (shndx.* == .UNDEF) { | 8136 | ); |
| 8045 | try elf.createInitFiniArraySection(shndx, init_fini_section_name, @"type"); | 8137 | section.placement = .{ .progbits = @fromBackingInt(@intCast(elf.input_sections.items.len)) }; |
| 8046 | } | 8138 | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 8047 | switch (elf.shdrPtr(shndx.*)) { | 8139 | .input = input_index, |
| 8048 | inline else => |shdr| { | 8140 | .file_location = .{ |
| 8049 | const old_size = elf.targetLoad(&shdr.size); | 8141 | .offset = fl.offset + section.shdr.offset, |
| 8050 | const new_size = old_size + section.shdr.size; | 8142 | .size = if (section.shdr.type == .NOBITS) 0 else 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.*; | | |
| 8056 | }, | 8143 | }, |
| 8057 | // This node must be fixed to prevent padding from being added between different | 8144 | // The section vaddr is initially 0, because the symbol addresses are |
| 8058 | // INIT_ARRAY/FINI_ARRAY/PREINIT_ARRAY input sections. | 8145 | // zero-based. This will eventually be updated by `flushMoved`. |
| 8059 | .node_fixed = true, | 8146 | .vaddr = 0, |
| 8060 | }, | 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; |
| 8061 | }; | 8188 | }; |
| 8062 | const need_align: Alignment = .fromByteUnits( | 8189 | defer gpa.free(strtab); |
| 8063 | std.math.ceilPowerOfTwoAssert(usize, @intCast(@max(section.shdr.addralign, 1))), | 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 }, |
| 8064 | ); | 8199 | ); |
| 8065 | | 8200 | |
| 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 | }; |
| 8067 | | 8255 | |
| 8068 | if (sect_type == .NOBITS) { | 8256 | const symbol_oni: MappedFile.Node.Index.Optional, const output_shndx: Section.Index, const symbol_value: u64 = switch (sections[input_sym.shndx].placement) { |
| 8069 | const offset: u64 = switch (elf.shdrPtr(opts.shndx)) { | 8257 | .none => continue, |
| 8070 | inline else => |shdr| offset: { | 8258 | .progbits => |isi| .{ |
| 8071 | // Bump our BSS output section size | 8259 | .wrap(isi.node(elf)), |
| 8072 | const old_size: u64 = elf.targetLoad(&shdr.size); | 8260 | elf.getNodeShndx(isi.node(elf)), |
| 8073 | const offset = need_align.forward(old_size); | 8261 | input_sym.value, |
| 8074 | elf.targetStore(&shdr.size, @intCast(offset + section.shdr.size)); | 8262 | }, |
| 8075 | break :offset offset; | 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 | }; |
| 8076 | }, | 8281 | }, |
| 8077 | }; | 8282 | }; |
| 8078 | | 8283 | |
| 8079 | section.placement = .{ .nobits = .{ | 8284 | switch (input_sym.info.bind) { |
| 8080 | .shndx = opts.shndx, | 8285 | else => |bind| return diags.failParse( |
| 8081 | .offset = offset, | 8286 | path, |
| 8082 | } }; | 8287 | "symbol '{s}' has unsupported binding (0x{x})", |
| 8083 | if (opts.shndx == elf.shndx.bss) { | 8288 | .{ name, bind }, |
| 8084 | input_has_bss = true; | 8289 | ), |
| 8085 | } else if (opts.shndx == elf.shndx.tbss) { | 8290 | .LOCAL => { |
| 8086 | input_has_tbss = true; | 8291 | const lsi = try elf.addLocalSymbol(.{ |
| 8087 | } else { | 8292 | .node = symbol_oni, |
| 8088 | assert(elf.ehdrType() == .REL); | 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 | }, |
| 8089 | } | 8330 | } |
| 8090 | | | |
| 8091 | continue; | | |
| 8092 | } | 8331 | } |
| 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); | | |
| 8131 | } | 8332 | } |
| 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 | }; | | |
| 8177 | | 8333 | |
| 8178 | if (input_sym.shndx == std.elf.SHN_UNDEF) switch (input_sym.info.bind) { | 8334 | { |
| 8179 | else => |bind| return diags.failParse( | 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( |
| 8180 | path, | 8351 | path, |
| 8181 | "symbol '{s}' has unsupported binding (0x{x})", | 8352 | "relocation section 'sh_info' refers to invalid section index '{d}'", |
| 8182 | .{ name, bind }, | 8353 | .{rel_sec.shdr.info}, |
| 8183 | ), | 8354 | ); |
| 8184 | .LOCAL => continue, | 8355 | } |
| 8185 | .GLOBAL, .WEAK, .GNU_UNIQUE => |bind| { | 8356 | const Rel = switch (sht) { |
| 8186 | si.* = .global(elf.addGlobalSymbol(.{ | 8357 | else => comptime unreachable, |
| 8187 | .node = .none, | 8358 | .REL => ElfN.Rel, |
| 8188 | .name = name, | 8359 | .RELA => ElfN.Rela, |
| 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 | }, | | |
| 8211 | }; | 8360 | }; |
| | 8361 | if (rel_sec.shdr.entsize < @sizeOf(Rel)) |
| | 8362 | return diags.failParse(path, "unsupported rel entsize", .{}); |
| 8212 | | 8363 | |
| 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) { |
| 8214 | .none => continue, | 8367 | .none => continue, |
| 8215 | .progbits => |isi| .{ | 8368 | .progbits => |isi| isi.node(elf), |
| 8216 | .wrap(isi.node(elf)), | 8369 | .nobits => return diags.failParse( |
| 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( | | |
| 8243 | path, | 8370 | path, |
| 8244 | "symbol '{s}' has unsupported binding (0x{x})", | 8371 | "cannot apply relocation to SHT_NOBITS section '{s}'", |
| 8245 | .{ name, bind }, | 8372 | .{std.mem.sliceTo(shstrtab[loc_sec.shdr.name..], 0)}, |
| 8246 | ), | 8373 | ), |
| 8247 | .LOCAL => { | 8374 | }; |
| 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 | }; | | |
| 8312 | | 8375 | |
| 8313 | elf.resetNodeRelocs(loc_node); | 8376 | elf.resetNodeRelocs(loc_node); |
| 8314 | | 8377 | |
| 8315 | const relnum = std.math.divExact( | 8378 | const relnum = std.math.divExact( |
| 8316 | u32, | 8379 | u32, |
| 8317 | @intCast(rel_sec.shdr.size), | 8380 | @intCast(rel_sec.shdr.size), |
| 8318 | @intCast(rel_sec.shdr.entsize), | 8381 | @intCast(rel_sec.shdr.entsize), |
| 8319 | ) catch return diags.failParse( | 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( |
| 8320 | path, | 8394 | path, |
| 8321 | "relocation section size (0x{x}) is not a multiple of entsize (0x{x})", | 8395 | "relocation target symbol index {d} exceeds symtab size", |
| 8322 | .{ rel_sec.shdr.size, rel_sec.shdr.entsize }, | 8396 | .{rel.info.sym}, |
| 8323 | ); | 8397 | ); |
| 8324 | try elf.ensureUnusedRelocCapacity(loc_node, relnum); | 8398 | const target = symmap.items[rel.info.sym - 1]; |
| 8325 | try fr.seekTo(fl.offset + rel_sec.shdr.offset); | 8399 | if (target == Symbol.Id.null) { |
| 8326 | for (0..relnum) |_| { | 8400 | // If this is not an SHF_ALLOC section, then let's not report |
| 8327 | const rel = try r.peekStruct(Rel, target_endian); | 8401 | // this for now, because it probably doesn't affect the final |
| 8328 | try r.discardAll64(rel_sec.shdr.entsize); | 8402 | // binary's functionality for this section to be a bit broken. |
| 8329 | if (rel.info.sym == 0) continue; | 8403 | if (loc_sec.shdr.flags.shf.ALLOC) { |
| 8330 | if (rel.info.sym > symnum) return diags.failParse( | 8404 | diags.addParseError( |
| 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( | | |
| 8359 | path, | 8405 | path, |
| 8360 | "unknown relocation type '{f}'", | 8406 | "unsupported symbol at index {d} required for relocation", |
| 8361 | .{rt.fmt(elf)}, | 8407 | .{rel.info.sym}, |
| 8362 | ), | 8408 | ); |
| 8363 | error.NonStaticRelocation => diags.addParseError( | 8409 | } |
| 8364 | path, | 8410 | continue; |
| 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 | }; | | |
| 8374 | } | 8411 | } |
| 8375 | }, | 8412 | const rt: MachineRelocType = .wrap(rel.info.type, elf); |
| 8376 | }; | 8413 | elf.addRelocAssumeCapacity( |
| 8377 | }, | 8414 | loc_node, |
| 8378 | }; | 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 | } |
| 8379 | | 8441 | |
| 8380 | if (input_has_bss) { | 8442 | if (input_has_bss) { |
| 8381 | try elf.bss_inputs.append(gpa, input_index); | 8443 | try elf.bss_inputs.append(gpa, input_index); |
| ... | @@ -8435,6 +8497,10 @@ fn loadDso( | ... | @@ -8435,6 +8497,10 @@ fn loadDso( |
| 8435 | fallback_soname: link.Input.Dso.FallbackSoname, | 8497 | fallback_soname: link.Input.Dso.FallbackSoname, |
| 8436 | fr: *Io.File.Reader, | 8498 | fr: *Io.File.Reader, |
| 8437 | ) (LoadParseInputError || error{BadMagic})!void { | 8499 | ) (LoadParseInputError || error{BadMagic})!void { |
| | 8500 | const trace = tracy.trace(@src()); |
| | 8501 | defer trace.end(); |
| | 8502 | trace.addTextFmt("{f}", .{path.fmtEscapeString()}); |
| | 8503 | |
| 8438 | const comp = elf.base.comp; | 8504 | const comp = elf.base.comp; |
| 8439 | const gpa = comp.gpa; | 8505 | const gpa = comp.gpa; |
| 8440 | const diags = &comp.link_diags; | 8506 | const diags = &comp.link_diags; |
| ... | @@ -8580,6 +8646,9 @@ fn loadDso( | ... | @@ -8580,6 +8646,9 @@ fn loadDso( |
| 8580 | const verdef: []DsoGlobals.String.Optional = verdef: { | 8646 | const verdef: []DsoGlobals.String.Optional = verdef: { |
| 8581 | const verdef_sh = shdr.verdef orelse break :verdef &.{}; | 8647 | const verdef_sh = shdr.verdef orelse break :verdef &.{}; |
| 8582 | | 8648 | |
| | 8649 | const versions_trace = tracy.traceNamed(@src(), "dso versions"); |
| | 8650 | defer versions_trace.end(); |
| | 8651 | |
| 8583 | // This parsing is a bit of a mess because it appears that at this moment in history | 8652 | // This parsing is a bit of a mess because it appears that at this moment in history |
| 8584 | // nobody at GNU was yet aware of the concept of an "array". | 8653 | // nobody at GNU was yet aware of the concept of an "array". |
| 8585 | | 8654 | |
| ... | @@ -8663,6 +8732,9 @@ fn loadDso( | ... | @@ -8663,6 +8732,9 @@ fn loadDso( |
| 8663 | const soname = try elf.string(.dynstr, soname_slice); | 8732 | const soname = try elf.string(.dynstr, soname_slice); |
| 8664 | try elf.needed.put(gpa, soname, {}); | 8733 | try elf.needed.put(gpa, soname, {}); |
| 8665 | | 8734 | |
| | 8735 | const symbols_trace = tracy.traceNamed(@src(), "dso symbols"); |
| | 8736 | defer symbols_trace.end(); |
| | 8737 | |
| 8666 | // Scan the symbol table and populate `elf.dso_globals`. | 8738 | // Scan the symbol table and populate `elf.dso_globals`. |
| 8667 | const first_global = @min(shdr.dynsym.info, symnum); | 8739 | const first_global = @min(shdr.dynsym.info, symnum); |
| 8668 | try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global); | 8740 | try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global); |
| ... | @@ -9544,6 +9616,9 @@ fn addRelocAssumeCapacity( | ... | @@ -9544,6 +9616,9 @@ fn addRelocAssumeCapacity( |
| 9544 | addend: i64, | 9616 | addend: i64, |
| 9545 | @"type": MachineRelocType, | 9617 | @"type": MachineRelocType, |
| 9546 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { | 9618 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { |
| | 9619 | const trace = tracy.trace(@src()); |
| | 9620 | defer trace.end(); |
| | 9621 | |
| 9547 | switch (elf.ehdrType()) { | 9622 | switch (elf.ehdrType()) { |
| 9548 | .REL => { | 9623 | .REL => { |
| 9549 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; | 9624 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| ... | @@ -9925,6 +10000,9 @@ fn addSymbolRelocAssumeCapacityInner( | ... | @@ -9925,6 +10000,9 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9925 | ) Error!void { | 10000 | ) Error!void { |
| 9926 | assert(elf.ehdrType() != .REL); | 10001 | assert(elf.ehdrType() != .REL); |
| 9927 | | 10002 | |
| | 10003 | const trace = tracy.trace(@src()); |
| | 10004 | defer trace.end(); |
| | 10005 | |
| 9928 | const gpa = elf.base.comp.gpa; | 10006 | const gpa = elf.base.comp.gpa; |
| 9929 | | 10007 | |
| 9930 | request_static_target: { | 10008 | request_static_target: { |