| ... | ... | @@ -2349,6 +2349,9 @@ const SymbolReloc = struct { |
| 2349 | 2349 | assert(elf.ehdrType() != .REL); |
| 2350 | 2350 | const reloc_node = reloc.node.unwrap().?; // deleted |
| 2351 | 2351 | |
| 2352 | const trace = tracy.trace(@src()); |
| 2353 | defer trace.end(); |
| 2354 | |
| 2352 | 2355 | // Delete any current output relocation or error. |
| 2353 | 2356 | switch (reloc.bits.result) { |
| 2354 | 2357 | .ok, .overflowed, .misaligned => { |
| ... | ... | @@ -3086,6 +3089,10 @@ const AddLocalSymbolOptions = struct { |
| 3086 | 3089 | fn addLocalSymbol(elf: *Elf, opts: AddLocalSymbolOptions) Error!Symbol.LocalIndex { |
| 3087 | 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 | 3096 | assert(opts.shndx != .UNDEF); |
| 3090 | 3097 | |
| 3091 | 3098 | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| ... | ... | @@ -3162,6 +3169,10 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{ |
| 3162 | 3169 | } || Error)!Symbol.Global.Index { |
| 3163 | 3170 | _ = opts.lib_name; // TODO |
| 3164 | 3171 | |
| 3172 | const trace = tracy.trace(@src()); |
| 3173 | defer trace.end(); |
| 3174 | trace.addText(opts.name); |
| 3175 | |
| 3165 | 3176 | log.debug("addGlobalSymbol({q})", .{opts.name}); |
| 3166 | 3177 | |
| 3167 | 3178 | const gpa = elf.base.comp.gpa; |
| ... | ... | @@ -4347,6 +4358,9 @@ const Symbol = struct { |
| 4347 | 4358 | /// Returns `true` if any relocations were updated, in which case relocations targeting |
| 4348 | 4359 | /// `gsi` must be re-applied by the caller. |
| 4349 | 4360 | fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool { |
| 4361 | const trace = tracy.trace(@src()); |
| 4362 | defer trace.end(); |
| 4363 | |
| 4350 | 4364 | if (elf.shndx.dynamic == .UNDEF) return false; |
| 4351 | 4365 | const sym_id: Symbol.Id = .global(gsi); |
| 4352 | 4366 | |
| ... | ... | @@ -4517,20 +4531,8 @@ const Symbol = struct { |
| 4517 | 4531 | } |
| 4518 | 4532 | |
| 4519 | 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 | 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 | 4536 | switch (elf.symPtr(sym_index)) { |
| 4535 | 4537 | inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)), |
| 4536 | 4538 | } |
| ... | ... | @@ -4555,6 +4557,9 @@ const Symbol = struct { |
| 4555 | 4557 | } |
| 4556 | 4558 | |
| 4557 | 4559 | fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void { |
| 4560 | const trace = tracy.trace(@src()); |
| 4561 | defer trace.end(); |
| 4562 | |
| 4558 | 4563 | var ri = sym_id.index(elf).ptr(elf).first_target_reloc; |
| 4559 | 4564 | while (ri != .none) { |
| 4560 | 4565 | const reloc = ri.get(elf); |
| ... | ... | @@ -7709,6 +7714,10 @@ fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void |
| 7709 | 7714 | } |
| 7710 | 7715 | } |
| 7711 | 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 | 7721 | const comp = elf.base.comp; |
| 7713 | 7722 | const gpa = comp.gpa; |
| 7714 | 7723 | const diags = &comp.link_diags; |
| ... | ... | @@ -7788,6 +7797,10 @@ fn loadObject( |
| 7788 | 7797 | fr: *Io.File.Reader, |
| 7789 | 7798 | fl: MappedFile.Node.FileLocation, |
| 7790 | 7799 | ) LoadParseInputError!void { |
| 7800 | const trace = tracy.trace(@src()); |
| 7801 | defer trace.end(); |
| 7802 | trace.addTextFmt("{f}{f}", .{ path.fmtEscapeString(), fmtMemberString(member) }); |
| 7803 | |
| 7791 | 7804 | const comp = elf.base.comp; |
| 7792 | 7805 | const gpa = comp.gpa; |
| 7793 | 7806 | const diags = &comp.link_diags; |
| ... | ... | @@ -7939,443 +7952,492 @@ fn loadObject( |
| 7939 | 7952 | defer gpa.free(shstrtab); |
| 7940 | 7953 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 7941 | 7954 | 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( |
| 7965 | 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 | 8025 | .{name}, |
| 7968 | 8026 | ); |
| 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. |
| 7988 | 8031 | 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 | }, |
| 7999 | 8097 | }; |
| 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); |
| 8012 | 8109 | } |
| 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 | } |
| 8040 | 8132 | }; |
| 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, |
| 8056 | 8143 | }, |
| 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; |
| 8061 | 8188 | }; |
| 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 }, |
| 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) { |
| 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 | }; |
| 8076 | 8281 | }, |
| 8077 | 8282 | }; |
| 8078 | 8283 | |
| 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 | }, |
| 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) { |
| 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( |
| 8180 | 8351 | 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, |
| 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 | 8367 | .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( |
| 8243 | 8370 | 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)}, |
| 8246 | 8373 | ), |
| 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 | }; |
| 8312 | 8375 | |
| 8313 | | elf.resetNodeRelocs(loc_node); |
| 8376 | elf.resetNodeRelocs(loc_node); |
| 8314 | 8377 | |
| 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( |
| 8320 | 8394 | 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}, |
| 8323 | 8397 | ); |
| 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( |
| 8359 | 8405 | 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; |
| 8374 | 8411 | } |
| 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 | } |
| 8379 | 8441 | |
| 8380 | 8442 | if (input_has_bss) { |
| 8381 | 8443 | try elf.bss_inputs.append(gpa, input_index); |
| ... | ... | @@ -8435,6 +8497,10 @@ fn loadDso( |
| 8435 | 8497 | fallback_soname: link.Input.Dso.FallbackSoname, |
| 8436 | 8498 | fr: *Io.File.Reader, |
| 8437 | 8499 | ) (LoadParseInputError || error{BadMagic})!void { |
| 8500 | const trace = tracy.trace(@src()); |
| 8501 | defer trace.end(); |
| 8502 | trace.addTextFmt("{f}", .{path.fmtEscapeString()}); |
| 8503 | |
| 8438 | 8504 | const comp = elf.base.comp; |
| 8439 | 8505 | const gpa = comp.gpa; |
| 8440 | 8506 | const diags = &comp.link_diags; |
| ... | ... | @@ -8580,6 +8646,9 @@ fn loadDso( |
| 8580 | 8646 | const verdef: []DsoGlobals.String.Optional = verdef: { |
| 8581 | 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 | 8652 | // This parsing is a bit of a mess because it appears that at this moment in history |
| 8584 | 8653 | // nobody at GNU was yet aware of the concept of an "array". |
| 8585 | 8654 | |
| ... | ... | @@ -8663,6 +8732,9 @@ fn loadDso( |
| 8663 | 8732 | const soname = try elf.string(.dynstr, soname_slice); |
| 8664 | 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 | 8738 | // Scan the symbol table and populate `elf.dso_globals`. |
| 8667 | 8739 | const first_global = @min(shdr.dynsym.info, symnum); |
| 8668 | 8740 | try elf.dso_globals.symbols.ensureUnusedCapacity(gpa, symnum - first_global); |
| ... | ... | @@ -9544,6 +9616,9 @@ fn addRelocAssumeCapacity( |
| 9544 | 9616 | addend: i64, |
| 9545 | 9617 | @"type": MachineRelocType, |
| 9546 | 9618 | ) (Error || error{ UnknownRelocation, NonStaticRelocation, UnimplementedRelocation })!void { |
| 9619 | const trace = tracy.trace(@src()); |
| 9620 | defer trace.end(); |
| 9621 | |
| 9547 | 9622 | switch (elf.ehdrType()) { |
| 9548 | 9623 | .REL => { |
| 9549 | 9624 | const rela_shndx = elf.getNodeShndx(node).get(elf).rela.shndx; |
| ... | ... | @@ -9925,6 +10000,9 @@ fn addSymbolRelocAssumeCapacityInner( |
| 9925 | 10000 | ) Error!void { |
| 9926 | 10001 | assert(elf.ehdrType() != .REL); |
| 9927 | 10002 | |
| 10003 | const trace = tracy.trace(@src()); |
| 10004 | defer trace.end(); |
| 10005 | |
| 9928 | 10006 | const gpa = elf.base.comp.gpa; |
| 9929 | 10007 | |
| 9930 | 10008 | request_static_target: { |