Commit b1d46339b5
Changed files (1)
src
link
src/link/Elf2.zig
@@ -3,6 +3,7 @@ options: link.File.OpenOptions,
mf: MappedFile,
ni: Node.Known,
nodes: std.MultiArrayList(Node),
+shdrs: std.ArrayList(Section),
phdrs: std.ArrayList(MappedFile.Node.Index),
si: Symbol.Known,
symtab: std.ArrayList(Symbol),
@@ -163,9 +164,10 @@ pub const Node = union(enum) {
}
};
+pub const Section = struct { si: Symbol.Index, rela_si: Symbol.Index };
+
pub const StringTable = struct {
map: std.HashMapUnmanaged(u32, void, StringTable.Context, std.hash_map.default_max_load_percentage),
- size: u32,
const Context = struct {
slice: []const u8,
@@ -193,14 +195,10 @@ pub const StringTable = struct {
}
};
- pub fn get(
- st: *StringTable,
- gpa: std.mem.Allocator,
- mf: *MappedFile,
- ni: MappedFile.Node.Index,
- key: []const u8,
- ) !u32 {
- const slice_const = ni.sliceConst(mf);
+ pub fn get(st: *StringTable, elf: *Elf, si: Symbol.Index, key: []const u8) !u32 {
+ const gpa = elf.base.comp.gpa;
+ const ni = si.node(elf);
+ const slice_const = ni.sliceConst(&elf.mf);
const gop = try st.map.getOrPutContextAdapted(
gpa,
key,
@@ -208,11 +206,18 @@ pub const StringTable = struct {
.{ .slice = slice_const },
);
if (gop.found_existing) return gop.key_ptr.*;
- const old_size = st.size;
- const new_size: u32 = @intCast(old_size + key.len + 1);
- st.size = new_size;
- try ni.resize(mf, gpa, new_size);
- const slice = ni.slice(mf)[old_size..];
+ const old_size, const new_size = size: switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| {
+ const old_size: u32 = @intCast(elf.targetLoad(&shdr.size));
+ const new_size: u32 = @intCast(old_size + key.len + 1);
+ elf.targetStore(&shdr.size, new_size);
+ break :size .{ old_size, new_size };
+ },
+ };
+ _, const node_size = ni.location(&elf.mf).resolve(&elf.mf);
+ if (new_size > node_size)
+ try ni.resize(&elf.mf, gpa, new_size +| new_size / MappedFile.growth_factor);
+ const slice = ni.slice(&elf.mf)[old_size..];
@memcpy(slice[0..key.len], key);
slice[key.len] = 0;
gop.key_ptr.* = old_size;
@@ -226,7 +231,7 @@ pub const Symbol = struct {
loc_relocs: Reloc.Index,
/// Relocations targeting this symbol
target_relocs: Reloc.Index,
- unused: u32 = 0,
+ unused: u32,
pub const Index = enum(u32) {
null,
@@ -252,6 +257,52 @@ pub const Symbol = struct {
return @enumFromInt(@intFromEnum(si) + 1);
}
+ pub const Shndx = enum(Tag) {
+ UNDEF = std.elf.SHN_UNDEF,
+ LIVEPATCH = reserve(std.elf.SHN_LIVEPATCH),
+ ABS = reserve(std.elf.SHN_ABS),
+ COMMON = reserve(std.elf.SHN_COMMON),
+ _,
+
+ pub const Tag = u32;
+
+ pub const LORESERVE: Shndx = .fromSection(std.elf.SHN_LORESERVE);
+ pub const HIRESERVE: Shndx = .fromSection(std.elf.SHN_HIRESERVE);
+ comptime {
+ assert(@intFromEnum(HIRESERVE) == std.math.maxInt(Tag));
+ }
+
+ fn reserve(sec: std.elf.Section) Tag {
+ assert(sec >= std.elf.SHN_LORESERVE and sec <= std.elf.SHN_HIRESERVE);
+ return @as(Tag, std.math.maxInt(Tag) - std.elf.SHN_HIRESERVE) + sec;
+ }
+
+ pub fn fromSection(sec: std.elf.Section) Shndx {
+ return switch (sec) {
+ std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => @enumFromInt(sec),
+ std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => @enumFromInt(reserve(sec)),
+ };
+ }
+ pub fn toSection(s: Shndx) ?std.elf.Section {
+ return switch (@intFromEnum(s)) {
+ std.elf.SHN_UNDEF...std.elf.SHN_LORESERVE - 1 => |sec| @intCast(sec),
+ std.elf.SHN_LORESERVE...reserve(std.elf.SHN_LORESERVE) - 1 => null,
+ reserve(std.elf.SHN_LORESERVE)...reserve(std.elf.SHN_HIRESERVE) => |sec| @intCast(
+ sec - reserve(std.elf.SHN_LORESERVE) + std.elf.SHN_LORESERVE,
+ ),
+ };
+ }
+
+ pub fn get(s: Shndx, elf: *Elf) *Section {
+ return &elf.shdrs.items[@intFromEnum(s)];
+ }
+ };
+ pub fn shndx(si: Symbol.Index, elf: *Elf) Shndx {
+ return .fromSection(switch (elf.symPtr(si)) {
+ inline else => |sym| elf.targetLoad(&sym.shndx),
+ });
+ }
+
pub const InitOptions = struct {
name: []const u8 = "",
lib_name: ?[]const u8 = null,
@@ -260,48 +311,83 @@ pub const Symbol = struct {
type: std.elf.STT,
bind: std.elf.STB = .LOCAL,
visibility: std.elf.STV = .DEFAULT,
- shndx: std.elf.Section = std.elf.SHN_UNDEF,
+ shndx: Shndx = .UNDEF,
};
pub fn init(si: Symbol.Index, elf: *Elf, opts: InitOptions) !void {
const gpa = elf.base.comp.gpa;
const target_endian = elf.targetEndian();
- const sym_size: usize = switch (elf.identClass()) {
- .NONE, _ => unreachable,
- inline else => |class| @sizeOf(class.ElfN().Sym),
- };
const name_strtab_entry = try elf.string(.strtab, opts.name);
- try elf.si.symtab.node(elf).resize(&elf.mf, gpa, sym_size * elf.symtab.items.len);
+ switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) {
+ inline else => |shdr| {
+ const old_size = elf.targetLoad(&shdr.size);
+ const ent_size = elf.targetLoad(&shdr.entsize);
+ const new_size = ent_size * elf.symtab.items.len;
+ if (new_size > old_size) {
+ elf.targetStore(&shdr.size, @intCast(new_size));
+ const symtab_ni = elf.si.symtab.node(elf);
+ _, const node_size = symtab_ni.location(&elf.mf).resolve(&elf.mf);
+ if (new_size > node_size) try symtab_ni.resize(
+ &elf.mf,
+ gpa,
+ new_size +| new_size / MappedFile.growth_factor,
+ );
+ }
+ },
+ }
switch (elf.symPtr(si)) {
inline else => |sym, class| {
+ const Sym = class.ElfN().Sym;
sym.* = .{
.name = name_strtab_entry,
.value = @intCast(opts.value),
.size = @intCast(opts.size),
.info = .{ .type = opts.type, .bind = opts.bind },
.other = .{ .visibility = opts.visibility },
- .shndx = opts.shndx,
+ .shndx = opts.shndx.toSection().?,
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(class.ElfN().Sym, sym);
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, sym);
},
}
+ switch (elf.shdrPtr(elf.si.symtab.shndx(elf))) {
+ inline else => |shdr| elf.targetStore(&shdr.info, @max(
+ elf.targetLoad(&shdr.info),
+ @intFromEnum(si) + 1,
+ )),
+ }
if (opts.bind == .LOCAL or elf.si.dynsym == .null) return;
const dsi = elf.dynsym.items.len;
try elf.dynsym.append(gpa, si);
- const dynsym_ni = elf.si.dynsym.node(elf);
const name_dynstr_entry = try elf.string(.dynstr, opts.name);
- try dynsym_ni.resize(&elf.mf, gpa, sym_size * elf.dynsym.items.len);
+ switch (elf.shdrPtr(elf.si.dynsym.shndx(elf))) {
+ inline else => |shdr| {
+ const old_size = elf.targetLoad(&shdr.size);
+ const ent_size = elf.targetLoad(&shdr.entsize);
+ const new_size = ent_size * elf.dynsym.items.len;
+ if (new_size > old_size) {
+ elf.targetStore(&shdr.size, @intCast(new_size));
+ const dynsym_ni = elf.si.dynsym.node(elf);
+ _, const node_size = dynsym_ni.location(&elf.mf).resolve(&elf.mf);
+ if (new_size > node_size) try dynsym_ni.resize(
+ &elf.mf,
+ gpa,
+ new_size +| new_size / MappedFile.growth_factor,
+ );
+ }
+ },
+ }
switch (elf.dynsymSlice()) {
- inline else => |dynsym, class| {
- const dsym = &dynsym[dsi];
- dsym.* = .{
+ inline else => |dynsyms, class| {
+ const Sym = class.ElfN().Sym;
+ const dynsym = &dynsyms[dsi];
+ dynsym.* = .{
.name = name_dynstr_entry,
.value = @intCast(opts.value),
.size = @intCast(opts.size),
.info = .{ .type = opts.type, .bind = opts.bind },
.other = .{ .visibility = opts.visibility },
- .shndx = opts.shndx,
+ .shndx = opts.shndx.toSection().?,
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(class.ElfN().Sym, dsym);
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(Sym, dynsym);
},
}
}
@@ -321,6 +407,7 @@ pub const Symbol = struct {
}
pub fn applyLocationRelocs(si: Symbol.Index, elf: *Elf) void {
+ if (elf.ehdrField(.type) == .REL) return;
switch (si.get(elf).loc_relocs) {
.none => {},
else => |loc_relocs| for (elf.relocs.items[@intFromEnum(loc_relocs)..]) |*reloc| {
@@ -331,6 +418,7 @@ pub const Symbol = struct {
}
pub fn applyTargetRelocs(si: Symbol.Index, elf: *Elf) void {
+ if (elf.ehdrField(.type) == .REL) return;
var ri = si.get(elf).target_relocs;
while (ri != .none) {
const reloc = ri.get(elf);
@@ -374,7 +462,7 @@ pub const Reloc = extern struct {
next: Reloc.Index,
loc: Symbol.Index,
target: Symbol.Index,
- unused: u32,
+ rel_index: u32,
offset: u64,
addend: i64,
@@ -411,6 +499,7 @@ pub const Reloc = extern struct {
};
pub fn apply(reloc: *const Reloc, elf: *Elf) void {
+ assert(elf.ehdrField(.type) != .REL);
const loc_ni = reloc.loc.get(elf).ni;
switch (loc_ni) {
.none => return,
@@ -601,6 +690,7 @@ fn create(
.tls = .none,
},
.nodes = .empty,
+ .shdrs = .empty,
.phdrs = .empty,
.si = .{
.dynsym = .null,
@@ -611,16 +701,13 @@ fn create(
.symtab = .empty,
.shstrtab = .{
.map = .empty,
- .size = 1,
},
.strtab = .{
.map = .empty,
- .size = 1,
},
.dynsym = .empty,
.dynstr = .{
.map = .empty,
- .size = 1,
},
.needed = .empty,
.inputs = .empty,
@@ -650,6 +737,7 @@ pub fn deinit(elf: *Elf) void {
const gpa = elf.base.comp.gpa;
elf.mf.deinit(gpa);
elf.nodes.deinit(gpa);
+ elf.shdrs.deinit(gpa);
elf.phdrs.deinit(gpa);
elf.symtab.deinit(gpa);
elf.shstrtab.map.deinit(gpa);
@@ -681,11 +769,10 @@ fn initHeaders(
const comp = elf.base.comp;
const gpa = comp.gpa;
const have_dynamic_section = switch (@"type") {
- .NONE => unreachable,
+ .NONE, .CORE, _ => unreachable,
.REL => false,
.EXEC => comp.config.link_mode == .dynamic,
.DYN => true,
- .CORE, _ => unreachable,
};
const addr_align: std.mem.Alignment = switch (class) {
.NONE, _ => unreachable,
@@ -693,6 +780,7 @@ fn initHeaders(
.@"64" => .@"8",
};
+ const shnum: u32 = 1;
var phnum: u32 = 0;
const phdr_phndx = phnum;
phnum += 1;
@@ -716,16 +804,16 @@ fn initHeaders(
} else undefined;
const expected_nodes_len = expected_nodes_len: switch (@"type") {
- .NONE => unreachable,
+ .NONE, .CORE, _ => unreachable,
.REL => {
defer phnum = 0;
break :expected_nodes_len 5 + phnum;
},
.EXEC, .DYN => break :expected_nodes_len 5 + phnum * 2 +
@as(usize, 2) * @intFromBool(have_dynamic_section),
- .CORE, _ => unreachable,
};
try elf.nodes.ensureTotalCapacity(gpa, expected_nodes_len);
+ try elf.shdrs.ensureTotalCapacity(gpa, shnum);
try elf.phdrs.resize(gpa, phnum);
elf.nodes.appendAssumeCapacity(.file);
@@ -760,7 +848,7 @@ fn initHeaders(
ehdr.phentsize = @sizeOf(ElfN.Phdr);
ehdr.phnum = @min(phnum, std.elf.PN_XNUM);
ehdr.shentsize = @sizeOf(ElfN.Shdr);
- ehdr.shnum = 1;
+ ehdr.shnum = if (shnum < std.elf.SHN_LORESERVE) shnum else 0;
ehdr.shstrndx = std.elf.SHN_UNDEF;
if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr);
},
@@ -768,7 +856,7 @@ fn initHeaders(
assert(elf.ni.shdr == try elf.mf.addLastChildNode(gpa, elf.ni.file, .{
.size = elf.ehdrField(.shentsize) * elf.ehdrField(.shnum),
- .alignment = addr_align,
+ .alignment = elf.mf.flags.block_size,
.moved = true,
.resized = true,
}));
@@ -810,7 +898,8 @@ fn initHeaders(
elf.phdrs.items[data_phndx] = elf.ni.data;
break :ph_vaddr switch (elf.ehdrField(.type)) {
- else => 0,
+ .NONE, .CORE, _ => unreachable,
+ .REL, .DYN => 0,
.EXEC => switch (elf.ehdrField(.machine)) {
.@"386" => 0x400000,
.AARCH64, .X86_64 => 0x200000,
@@ -933,20 +1022,21 @@ fn initHeaders(
}
}
- const sh_null: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
- sh_null.* = .{
+ const sh_undef: *ElfN.Shdr = @ptrCast(@alignCast(elf.ni.shdr.slice(&elf.mf)));
+ sh_undef.* = .{
.name = try elf.string(.shstrtab, ""),
.type = .NULL,
.flags = .{ .shf = .{} },
.addr = 0,
.offset = 0,
- .size = 0,
+ .size = if (shnum < std.elf.SHN_LORESERVE) 0 else shnum,
.link = 0,
- .info = if (phnum >= std.elf.PN_XNUM) phnum else 0,
+ .info = if (phnum < std.elf.PN_XNUM) 0 else phnum,
.addralign = 0,
.entsize = 0,
};
- if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_null);
+ if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Shdr, sh_undef);
+ elf.shdrs.appendAssumeCapacity(.{ .si = .null, .rela_si = .null });
try elf.symtab.ensureTotalCapacity(gpa, 1);
elf.symtab.addOneAssumeCapacity().* = .{
@@ -960,6 +1050,7 @@ fn initHeaders(
.size = @sizeOf(ElfN.Sym) * 1,
.addralign = addr_align,
.entsize = @sizeOf(ElfN.Sym),
+ .node_align = elf.mf.flags.block_size,
}));
const symtab_null = @field(elf.symPtr(.null), @tagName(ct_class));
symtab_null.* = .{
@@ -973,13 +1064,14 @@ fn initHeaders(
if (target_endian != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, symtab_null);
const ehdr = @field(elf.ehdrPtr(), @tagName(ct_class));
- ehdr.shstrndx = ehdr.shnum;
+ elf.targetStore(&ehdr.shstrndx, ehdr.shnum);
},
}
assert(elf.si.shstrtab == try elf.addSection(elf.ni.file, .{
.type = .STRTAB,
- .addralign = elf.mf.flags.block_size,
+ .size = 1,
.entsize = 1,
+ .node_align = elf.mf.flags.block_size,
}));
try elf.renameSection(.symtab, ".symtab");
try elf.renameSection(.shstrtab, ".shstrtab");
@@ -989,96 +1081,100 @@ fn initHeaders(
.name = ".strtab",
.type = .STRTAB,
.size = 1,
- .addralign = elf.mf.flags.block_size,
.entsize = 1,
+ .node_align = elf.mf.flags.block_size,
}));
try elf.linkSections(.symtab, .strtab);
elf.si.strtab.node(elf).slice(&elf.mf)[0] = 0;
- assert(elf.si.rodata == try elf.addSection(if (@"type" != .REL) elf.ni.rodata else elf.ni.file, .{
+ assert(elf.si.rodata == try elf.addSection(elf.ni.rodata, .{
.name = ".rodata",
.flags = .{ .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}));
- assert(elf.si.text == try elf.addSection(if (@"type" != .REL) elf.ni.text else elf.ni.file, .{
+ assert(elf.si.text == try elf.addSection(elf.ni.text, .{
.name = ".text",
.flags = .{ .ALLOC = true, .EXECINSTR = true },
.addralign = elf.mf.flags.block_size,
}));
- assert(elf.si.data == try elf.addSection(if (@"type" != .REL) elf.ni.data else elf.ni.file, .{
+ assert(elf.si.data == try elf.addSection(elf.ni.data, .{
.name = ".data",
.flags = .{ .WRITE = true, .ALLOC = true },
.addralign = elf.mf.flags.block_size,
}));
- if (maybe_interp) |interp| {
- const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
- .size = interp.len + 1,
- .moved = true,
- .resized = true,
- .bubbles_moved = false,
- });
- elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
- elf.phdrs.items[interp_phndx] = interp_ni;
-
- const sec_interp_si = try elf.addSection(interp_ni, .{
- .type = .PROGBITS,
- .name = ".interp",
- .flags = .{ .ALLOC = true },
- .size = @intCast(interp.len + 1),
- });
- const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
- @memcpy(sec_interp[0..interp.len], interp);
- sec_interp[interp.len] = 0;
- }
- if (have_dynamic_section) {
- const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data, .{
- .moved = true,
- .bubbles_moved = false,
- });
- elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });
- elf.phdrs.items[dynamic_phndx] = dynamic_ni;
-
- switch (class) {
- .NONE, _ => unreachable,
- inline else => |ct_class| {
- const ElfN = ct_class.ElfN();
- elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{
- .name = ".dynsym",
- .type = .DYNSYM,
- .size = @sizeOf(ElfN.Sym) * 1,
- .addralign = addr_align,
- .entsize = @sizeOf(ElfN.Sym),
- });
- const dynsym_null = &@field(elf.dynsymSlice(), @tagName(ct_class))[0];
- dynsym_null.* = .{
- .name = try elf.string(.dynstr, ""),
- .value = 0,
- .size = 0,
- .info = .{ .type = .NOTYPE, .bind = .LOCAL },
- .other = .{ .visibility = .DEFAULT },
- .shndx = std.elf.SHN_UNDEF,
- };
- if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Sym, dynsym_null);
- },
+ if (@"type" != .REL) {
+ if (maybe_interp) |interp| {
+ const interp_ni = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
+ .size = interp.len + 1,
+ .moved = true,
+ .resized = true,
+ .bubbles_moved = false,
+ });
+ elf.nodes.appendAssumeCapacity(.{ .segment = interp_phndx });
+ elf.phdrs.items[interp_phndx] = interp_ni;
+
+ const sec_interp_si = try elf.addSection(interp_ni, .{
+ .type = .PROGBITS,
+ .name = ".interp",
+ .flags = .{ .ALLOC = true },
+ .size = @intCast(interp.len + 1),
+ });
+ const sec_interp = sec_interp_si.node(elf).slice(&elf.mf);
+ @memcpy(sec_interp[0..interp.len], interp);
+ sec_interp[interp.len] = 0;
}
- elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{
- .name = ".dynstr",
- .type = .STRTAB,
- .size = 1,
- .addralign = elf.mf.flags.block_size,
- .entsize = 1,
- });
- elf.si.dynamic = try elf.addSection(dynamic_ni, .{
- .name = ".dynamic",
- .type = .DYNAMIC,
- .flags = .{ .ALLOC = true, .WRITE = true },
- .addralign = addr_align,
- });
- try elf.linkSections(elf.si.dynamic, elf.si.dynstr);
- try elf.linkSections(elf.si.dynsym, elf.si.dynstr);
- }
- if (comp.config.any_non_single_threaded) {
- if (@"type" != .REL) {
+ if (have_dynamic_section) {
+ const dynamic_ni = try elf.mf.addLastChildNode(gpa, elf.ni.data, .{
+ .moved = true,
+ .bubbles_moved = false,
+ });
+ elf.nodes.appendAssumeCapacity(.{ .segment = dynamic_phndx });
+ elf.phdrs.items[dynamic_phndx] = dynamic_ni;
+
+ switch (class) {
+ .NONE, _ => unreachable,
+ inline else => |ct_class| {
+ const Sym = ct_class.ElfN().Sym;
+ elf.si.dynsym = try elf.addSection(elf.ni.rodata, .{
+ .name = ".dynsym",
+ .type = .DYNSYM,
+ .size = @sizeOf(Sym) * 1,
+ .addralign = addr_align,
+ .entsize = @sizeOf(Sym),
+ .node_align = elf.mf.flags.block_size,
+ });
+ const dynsym_null = &@field(elf.dynsymSlice(), @tagName(ct_class))[0];
+ dynsym_null.* = .{
+ .name = try elf.string(.dynstr, ""),
+ .value = 0,
+ .size = 0,
+ .info = .{ .type = .NOTYPE, .bind = .LOCAL },
+ .other = .{ .visibility = .DEFAULT },
+ .shndx = std.elf.SHN_UNDEF,
+ };
+ if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(
+ Sym,
+ dynsym_null,
+ );
+ },
+ }
+ elf.si.dynstr = try elf.addSection(elf.ni.rodata, .{
+ .name = ".dynstr",
+ .type = .STRTAB,
+ .size = 1,
+ .entsize = 1,
+ .node_align = elf.mf.flags.block_size,
+ });
+ elf.si.dynamic = try elf.addSection(dynamic_ni, .{
+ .name = ".dynamic",
+ .type = .DYNAMIC,
+ .flags = .{ .ALLOC = true, .WRITE = true },
+ .node_align = addr_align,
+ });
+ try elf.linkSections(elf.si.dynamic, elf.si.dynstr);
+ try elf.linkSections(elf.si.dynsym, elf.si.dynstr);
+ }
+ if (comp.config.any_non_single_threaded) {
elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{
.alignment = elf.mf.flags.block_size,
.moved = true,
@@ -1087,13 +1183,15 @@ fn initHeaders(
elf.nodes.appendAssumeCapacity(.{ .segment = tls_phndx });
elf.phdrs.items[tls_phndx] = elf.ni.tls;
}
-
- elf.si.tdata = try elf.addSection(if (@"type" != .REL) elf.ni.tls else elf.ni.file, .{
- .name = ".tdata",
- .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
- .addralign = elf.mf.flags.block_size,
- });
+ } else {
+ assert(maybe_interp == null);
+ assert(!have_dynamic_section);
}
+ if (comp.config.any_non_single_threaded) elf.si.tdata = try elf.addSection(elf.ni.tls, .{
+ .name = ".tdata",
+ .flags = .{ .WRITE = true, .ALLOC = true, .TLS = true },
+ .addralign = elf.mf.flags.block_size,
+ });
assert(elf.nodes.len == expected_nodes_len);
}
@@ -1218,6 +1316,7 @@ pub const PhdrSlice = union(std.elf.CLASS) {
@"64": []std.elf.Elf64.Phdr,
};
pub fn phdrSlice(elf: *Elf) PhdrSlice {
+ assert(elf.ehdrField(.type) != .REL);
const slice = elf.ni.phdr.slice(&elf.mf);
return switch (elf.identClass()) {
.NONE, _ => unreachable,
@@ -1246,6 +1345,17 @@ pub fn shdrSlice(elf: *Elf) ShdrSlice {
};
}
+pub const ShdrPtr = union(std.elf.CLASS) {
+ NONE: noreturn,
+ @"32": *std.elf.Elf32.Shdr,
+ @"64": *std.elf.Elf64.Shdr,
+};
+pub fn shdrPtr(elf: *Elf, shndx: Symbol.Index.Shndx) ShdrPtr {
+ return switch (elf.shdrSlice()) {
+ inline else => |shdrs, class| @unionInit(ShdrPtr, @tagName(class), &shdrs[@intFromEnum(shndx)]),
+ };
+}
+
pub const SymtabSlice = union(std.elf.CLASS) {
NONE: noreturn,
@"32": []std.elf.Elf32.Sym,
@@ -1258,7 +1368,11 @@ pub fn symtabSlice(elf: *Elf) SymtabSlice {
inline else => |class| @unionInit(
SymtabSlice,
@tagName(class),
- @ptrCast(@alignCast(slice)),
+ @ptrCast(@alignCast(slice[0..std.mem.alignBackwardAnyAlign(
+ usize,
+ slice.len,
+ @sizeOf(class.ElfN().Sym),
+ )])),
),
};
}
@@ -1270,7 +1384,7 @@ pub const SymPtr = union(std.elf.CLASS) {
};
pub fn symPtr(elf: *Elf, si: Symbol.Index) SymPtr {
return switch (elf.symtabSlice()) {
- inline else => |sym, class| @unionInit(SymPtr, @tagName(class), &sym[@intFromEnum(si)]),
+ inline else => |syms, class| @unionInit(SymPtr, @tagName(class), &syms[@intFromEnum(si)]),
};
}
@@ -1560,7 +1674,7 @@ fn loadObject(
.si = try elf.initSymbolAssumeCapacity(.{
.name = std.fs.path.stem(member orelse path.sub_path),
.type = .FILE,
- .shndx = std.elf.SHN_ABS,
+ .shndx = .ABS,
}),
};
const target_endian = elf.targetEndian();
@@ -1626,7 +1740,7 @@ fn loadObject(
});
section.si = try elf.initSymbolAssumeCapacity(.{
.type = .SECTION,
- .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx),
+ .shndx = parent_si.shndx(elf),
});
section.si.get(elf).ni = ni;
elf.input_sections.addOneAssumeCapacity().* = .{
@@ -1698,9 +1812,7 @@ fn loadObject(
.type = input_sym.info.type,
.bind = input_sym.info.bind,
.visibility = input_sym.other.visibility,
- .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) {
- inline else => |parent_sym| &parent_sym.shndx,
- }),
+ .shndx = parent_si.shndx(elf),
});
si.get(elf).ni = parent_si.get(elf).ni;
switch (input_sym.info.bind) {
@@ -1755,7 +1867,7 @@ fn loadObject(
"relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
.{ rels.shdr.size, rels.shdr.entsize },
);
- try elf.relocs.ensureUnusedCapacity(gpa, relnum);
+ try elf.ensureUnusedRelocCapacity(loc_sec.si, relnum);
try fr.seekTo(fl.offset + rels.shdr.offset);
for (0..relnum) |_| {
const rel = try r.peekStruct(Rel, target_endian);
@@ -1832,13 +1944,13 @@ fn loadDso(elf: *Elf, path: std.Build.Cache.Path, fr: *std.Io.File.Reader) !void
var soname: ?ElfN.Addr = null;
try fr.seekTo(dynamic_ph.offset);
for (0..dynnum) |_| {
- const key = try r.takeInt(ElfN.Addr, target_endian);
- const value = try r.takeInt(ElfN.Addr, target_endian);
- switch (key) {
+ const tag = try r.takeInt(ElfN.Addr, target_endian);
+ const val = try r.takeInt(ElfN.Addr, target_endian);
+ switch (tag) {
else => {},
- std.elf.DT_STRTAB => strtab = value,
- std.elf.DT_STRSZ => strsz = value,
- std.elf.DT_SONAME => soname = value,
+ std.elf.DT_STRTAB => strtab = val,
+ std.elf.DT_STRSZ => strsz = val,
+ std.elf.DT_SONAME => soname = val,
}
}
if (strtab == null or soname == null)
@@ -1883,11 +1995,7 @@ fn prelinkInner(elf: *Elf) !void {
std.fs.path.stem(elf.base.emit.sub_path),
});
defer gpa.free(zcu_name);
- const si = try elf.initSymbolAssumeCapacity(.{
- .name = zcu_name,
- .type = .FILE,
- .shndx = std.elf.SHN_ABS,
- });
+ const si = try elf.initSymbolAssumeCapacity(.{ .name = zcu_name, .type = .FILE, .shndx = .ABS });
elf.inputs.addOneAssumeCapacity().* = .{
.path = elf.base.emit,
.member = null,
@@ -1989,9 +2097,12 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
name: []const u8 = "",
type: std.elf.SHT = .NULL,
flags: std.elf.SHF = .{},
- size: std.elf.Word = 0,
+ size: std.elf.Xword = 0,
+ link: std.elf.Word = 0,
+ info: std.elf.Word = 0,
addralign: std.mem.Alignment = .@"1",
entsize: std.elf.Word = 0,
+ node_align: std.mem.Alignment = .@"1",
}) !Symbol.Index {
switch (opts.type) {
.NULL => assert(opts.size == 0),
@@ -2000,50 +2111,71 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
}
const gpa = elf.base.comp.gpa;
try elf.nodes.ensureUnusedCapacity(gpa, 1);
+ try elf.shdrs.ensureUnusedCapacity(gpa, 1);
try elf.symtab.ensureUnusedCapacity(gpa, 1);
const shstrtab_entry = try elf.string(.shstrtab, opts.name);
- const shndx, const shdr_size = shndx: switch (elf.ehdrPtr()) {
- inline else => |ehdr| {
- const shndx = elf.targetLoad(&ehdr.shnum);
- const shnum = shndx + 1;
- elf.targetStore(&ehdr.shnum, shnum);
- break :shndx .{ shndx, elf.targetLoad(&ehdr.shentsize) * shnum };
+ const shndx: Symbol.Index.Shndx, const new_shdr_size = shndx: switch (elf.ehdrPtr()) {
+ inline else => |ehdr, class| {
+ const shndx, const shnum = alloc_shndx: switch (elf.targetLoad(&ehdr.shnum)) {
+ 1...std.elf.SHN_LORESERVE - 2 => |shndx| {
+ const shnum = shndx + 1;
+ elf.targetStore(&ehdr.shnum, shnum);
+ break :alloc_shndx .{ shndx, shnum };
+ },
+ std.elf.SHN_LORESERVE - 1 => |shndx| {
+ const shnum = shndx + 1;
+ elf.targetStore(&ehdr.shnum, 0);
+ elf.targetStore(&@field(elf.shdrPtr(.UNDEF), @tagName(class)).size, shnum);
+ break :alloc_shndx .{ shndx, shnum };
+ },
+ std.elf.SHN_LORESERVE...std.elf.SHN_HIRESERVE => unreachable,
+ 0 => {
+ const shnum_ptr = &@field(elf.shdrPtr(.UNDEF), @tagName(class)).size;
+ const shndx: u32 = @intCast(elf.targetLoad(shnum_ptr));
+ const shnum = shndx + 1;
+ elf.targetStore(shnum_ptr, shnum);
+ break :alloc_shndx .{ shndx, shnum };
+ },
+ };
+ assert(shndx < @intFromEnum(Symbol.Index.Shndx.LORESERVE));
+ break :shndx .{ @enumFromInt(shndx), elf.targetLoad(&ehdr.shentsize) * shnum };
},
};
- try elf.ni.shdr.resize(&elf.mf, gpa, shdr_size);
- const ni = try elf.mf.addLastChildNode(gpa, segment_ni, .{
- .alignment = opts.addralign,
+ _, const shdr_node_size = elf.ni.shdr.location(&elf.mf).resolve(&elf.mf);
+ if (new_shdr_size > shdr_node_size)
+ try elf.ni.shdr.resize(&elf.mf, gpa, new_shdr_size +| new_shdr_size / MappedFile.growth_factor);
+ const ni = try elf.mf.addLastChildNode(gpa, switch (elf.ehdrField(.type)) {
+ .NONE, .CORE, _ => unreachable,
+ .REL => elf.ni.file,
+ .EXEC, .DYN => segment_ni,
+ }, .{
+ .alignment = opts.addralign.max(opts.node_align),
.size = opts.size,
.resized = opts.size > 0,
});
const si = elf.addSymbolAssumeCapacity();
elf.nodes.appendAssumeCapacity(.{ .section = si });
+ elf.shdrs.appendAssumeCapacity(.{ .si = si, .rela_si = .null });
si.get(elf).ni = ni;
const addr = elf.computeNodeVAddr(ni);
const offset = ni.fileLocation(&elf.mf, false).offset;
- try si.init(elf, .{
- .value = addr,
- .size = opts.size,
- .type = .SECTION,
- .shndx = shndx,
- });
- switch (elf.shdrSlice()) {
- inline else => |shdr| {
- const sh = &shdr[shndx];
- sh.* = .{
+ try si.init(elf, .{ .value = addr, .type = .SECTION, .shndx = shndx });
+ switch (elf.shdrPtr(shndx)) {
+ inline else => |shdr, class| {
+ shdr.* = .{
.name = shstrtab_entry,
.type = opts.type,
.flags = .{ .shf = opts.flags },
.addr = @intCast(addr),
.offset = @intCast(offset),
- .size = opts.size,
- .link = 0,
- .info = 0,
+ .size = @intCast(opts.size),
+ .link = opts.link,
+ .info = opts.info,
.addralign = @intCast(opts.addralign.toByteUnits()),
.entsize = opts.entsize,
};
- if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(@TypeOf(sh.*), sh);
+ if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(class.ElfN().Shdr, shdr);
},
}
return si;
@@ -2051,39 +2183,27 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
fn renameSection(elf: *Elf, si: Symbol.Index, name: []const u8) !void {
const shstrtab_entry = try elf.string(.shstrtab, name);
- switch (elf.shdrSlice()) {
- inline else => |shdr, class| elf.targetStore(
- &shdr[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name,
- shstrtab_entry,
- ),
+ switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| elf.targetStore(&shdr.name, shstrtab_entry),
}
}
fn linkSections(elf: *Elf, si: Symbol.Index, link_si: Symbol.Index) !void {
- switch (elf.shdrSlice()) {
- inline else => |shdr, class| shdr[
- elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)
- ].link = @field(elf.symPtr(link_si), @tagName(class)).shndx,
+ switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| elf.targetStore(&shdr.link, @intFromEnum(link_si.shndx(elf))),
}
}
fn sectionName(elf: *Elf, si: Symbol.Index) [:0]const u8 {
- const name = elf.si.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrSlice()) {
- inline else => |shndx, class| elf.targetLoad(
- &shndx[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].name,
- ),
+ const name = elf.si.shstrtab.node(elf).slice(&elf.mf)[switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| elf.targetLoad(&shdr.name),
}..];
return name[0..std.mem.indexOfScalar(u8, name, 0).? :0];
}
fn string(elf: *Elf, comptime section: enum { shstrtab, strtab, dynstr }, key: []const u8) !u32 {
if (key.len == 0) return 0;
- return @field(elf, @tagName(section)).get(
- elf.base.comp.gpa,
- &elf.mf,
- @field(elf.si, @tagName(section)).node(elf),
- key,
- );
+ return @field(elf, @tagName(section)).get(elf, @field(elf.si, @tagName(section)), key);
}
pub fn addReloc(
@@ -2094,9 +2214,51 @@ pub fn addReloc(
addend: i64,
@"type": Reloc.Type,
) !void {
- try elf.relocs.ensureUnusedCapacity(elf.base.comp.gpa, 1);
+ try elf.ensureUnusedRelocCapacity(loc_si, 1);
elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type");
}
+pub fn ensureUnusedRelocCapacity(elf: *Elf, loc_si: Symbol.Index, len: usize) !void {
+ if (len == 0) return;
+ const gpa = elf.base.comp.gpa;
+
+ try elf.relocs.ensureUnusedCapacity(gpa, len);
+ if (elf.ehdrField(.type) != .REL) return;
+
+ const shndx = loc_si.shndx(elf);
+ const sh = shndx.get(elf);
+ if (sh.rela_si == .null) {
+ var stack = std.heap.stackFallback(32, gpa);
+ const allocator = stack.get();
+
+ const rela_name = try std.fmt.allocPrint(allocator, ".rela{s}", .{elf.sectionName(sh.si)});
+ defer allocator.free(rela_name);
+
+ const class = elf.identClass();
+ sh.rela_si = try elf.addSection(.none, .{
+ .name = rela_name,
+ .type = .RELA,
+ .link = @intFromEnum(elf.si.symtab.shndx(elf)),
+ .info = @intFromEnum(shndx),
+ .addralign = switch (class) {
+ .NONE, _ => unreachable,
+ .@"32" => .@"4",
+ .@"64" => .@"8",
+ },
+ .entsize = switch (class) {
+ .NONE, _ => unreachable,
+ inline else => |ct_class| @sizeOf(ct_class.ElfN().Rela),
+ },
+ .node_align = elf.mf.flags.block_size,
+ });
+ }
+ const rela_ni = sh.rela_si.node(elf);
+ _, const rela_node_size = rela_ni.location(&elf.mf).resolve(&elf.mf);
+ const rela_size = switch (elf.shdrPtr(sh.rela_si.shndx(elf))) {
+ inline else => |shdr| elf.targetLoad(&shdr.size) + elf.targetLoad(&shdr.entsize) * len,
+ };
+ if (rela_size > rela_node_size)
+ try rela_ni.resize(&elf.mf, gpa, rela_size +| rela_size / MappedFile.growth_factor);
+}
pub fn addRelocAssumeCapacity(
elf: *Elf,
loc_si: Symbol.Index,
@@ -2113,7 +2275,40 @@ pub fn addRelocAssumeCapacity(
.next = target.target_relocs,
.loc = loc_si,
.target = target_si,
- .unused = 0,
+ .rel_index = switch (elf.ehdrField(.type)) {
+ .NONE, .CORE, _ => unreachable,
+ .REL => rel_index: {
+ const rela_si = loc_si.shndx(elf).get(elf).rela_si;
+ switch (elf.shdrPtr(rela_si.shndx(elf))) {
+ inline else => |shdr, class| {
+ const Rela = class.ElfN().Rela;
+ const old_size = elf.targetLoad(&shdr.size);
+ const ent_size = elf.targetLoad(&shdr.entsize);
+ const new_size = old_size + ent_size;
+ elf.targetStore(&shdr.size, @intCast(new_size));
+ const rela: *Rela = @ptrCast(@alignCast(
+ rela_si.node(elf).slice(&elf.mf)[@intCast(old_size)..@intCast(new_size)],
+ ));
+ rela.* = .{
+ .offset = @intCast(offset),
+ .info = .{
+ .type = switch (elf.ehdrField(.machine)) {
+ else => |machine| @panic(@tagName(machine)),
+ inline .X86_64, .AARCH64, .RISCV, .PPC64 => |machine| @intCast(
+ @intFromEnum(@field(@"type", @tagName(machine))),
+ ),
+ },
+ .sym = @intCast(@intFromEnum(target_si)),
+ },
+ .addend = @intCast(addend),
+ };
+ if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(Rela, rela);
+ break :rel_index @intCast(@divExact(old_size, ent_size));
+ },
+ }
+ },
+ .EXEC, .DYN => 0,
+ },
.offset = offset,
.addend = addend,
};
@@ -2479,7 +2674,8 @@ fn flushUav(
switch (sym.ni) {
.none => {
try elf.nodes.ensureUnusedCapacity(gpa, 1);
- const ni = try elf.mf.addLastChildNode(gpa, elf.si.data.node(elf), .{
+ const sec_si = elf.si.data;
+ const ni = try elf.mf.addLastChildNode(gpa, sec_si.node(elf), .{
.alignment = uav_align.toStdMem(),
.moved = true,
});
@@ -2487,7 +2683,7 @@ fn flushUav(
sym.ni = ni;
switch (elf.symPtr(si)) {
inline else => |sym_ptr, class| sym_ptr.shndx =
- @field(elf.symPtr(.data), @tagName(class)).shndx,
+ @field(elf.symPtr(sec_si), @tagName(class)).shndx,
}
},
else => {
@@ -2611,11 +2807,10 @@ fn flushFileOffset(elf: *Elf, ni: MappedFile.Node.Index) !void {
var child_it = ni.children(&elf.mf);
while (child_it.next()) |child_ni| try elf.flushFileOffset(child_ni);
},
- .section => |si| switch (elf.shdrSlice()) {
- inline else => |shdr, class| elf.targetStore(
- &shdr[elf.targetLoad(&@field(elf.symPtr(si), @tagName(class)).shndx)].offset,
- @intCast(ni.fileLocation(&elf.mf, false).offset),
- ),
+ .section => |si| switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| elf.targetStore(&shdr.offset, @intCast(
+ ni.fileLocation(&elf.mf, false).offset,
+ )),
},
}
}
@@ -2644,14 +2839,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
.section => |si| {
try elf.flushFileOffset(ni);
const addr = elf.computeNodeVAddr(ni);
- switch (elf.shdrSlice()) {
+ switch (elf.shdrPtr(si.shndx(elf))) {
inline else => |shdr, class| {
- const sym = @field(elf.symPtr(si), @tagName(class));
- const sh = &shdr[elf.targetLoad(&sym.shndx)];
- const flags = elf.targetLoad(&sh.flags).shf;
+ const flags = elf.targetLoad(&shdr.flags).shf;
if (flags.ALLOC) {
- elf.targetStore(&sh.addr, @intCast(addr));
- sym.value = sh.addr;
+ elf.targetStore(&shdr.addr, @intCast(addr));
+ @field(elf.symPtr(si), @tagName(class)).value = shdr.addr;
}
},
}
@@ -2732,20 +2925,15 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) !void {
}
},
},
- .section => |si| switch (elf.symPtr(si)) {
- inline else => |sym, class| {
- const sh = &@field(elf.shdrSlice(), @tagName(class))[elf.targetLoad(&sym.shndx)];
- elf.targetStore(&sh.size, @intCast(size));
- switch (elf.targetLoad(&sh.type)) {
+ .section => |si| switch (elf.shdrPtr(si.shndx(elf))) {
+ inline else => |shdr| {
+ switch (elf.targetLoad(&shdr.type)) {
else => unreachable,
- .NULL => if (size > 0) elf.targetStore(&sh.type, .PROGBITS),
- .PROGBITS => if (size == 0) elf.targetStore(&sh.type, .NULL),
- .SYMTAB, .DYNSYM => elf.targetStore(
- &sh.info,
- @intCast(@divExact(size, elf.targetLoad(&sh.entsize))),
- ),
- .STRTAB, .DYNAMIC => {},
+ .NULL => if (size > 0) elf.targetStore(&shdr.type, .PROGBITS),
+ .PROGBITS => if (size == 0) elf.targetStore(&shdr.type, .NULL),
+ .SYMTAB, .STRTAB, .RELA, .DYNAMIC, .REL, .DYNSYM => return,
}
+ elf.targetStore(&shdr.size, @intCast(size));
},
},
.input_section => {},