diff --git a/src/link/Elf2.zig b/src/link/Elf2.zig index d6eb9501e3aa7bae9f6e3667e719d78b76d8f6d2..831db4697aeab89e3e9bfcab3dde58fa7e3b4e6e 100644 --- a/src/link/Elf2.zig +++ b/src/link/Elf2.zig @@ -3645,9 +3645,98 @@ fn initHeaders( const entsize: struct { ph: u32, sh: u32 } = switch (class) { .NONE, _ => unreachable, - inline else => |ct_class| entsize: { + inline else => |ct_class| .{ + .ph = @sizeOf(ct_class.ElfN().Phdr), + .sh = @sizeOf(ct_class.ElfN().Shdr), + }, + }; + + // We want to create the segment nodes *before* the ehdr, because the ehdr should go inside of + // the rodata segment. Although to my knowledge neither ELF nor any ELF-based OS strictly + // requires this, it is highly conventional and therefore sometimes relied upon. + if (@"type" != .REL) { + elf.ni.rodata = try elf.mf.addOnlyChildNode(gpa, elf.ni.elf, .{ + // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node + .alignment = node_block_align.max(addr_align), + // This node will contain the ehdr, which must be at the start of the ELF file, so this + // node must itself be fixed. + .fixed = true, + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata }); + elf.phdrs.items[phndx.rodata] = elf.ni.rodata; + + elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ + .size = @as(u64, phnum) * entsize.ph, + .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above + .moved = true, + .resized = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr }); + elf.phdrs.items[phndx.phdr] = elf.ni.phdr; + + elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ + .alignment = node_block_align, + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text }); + elf.phdrs.items[phndx.text] = elf.ni.text; + + elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ + // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node + .alignment = node_block_align.max(addr_align), + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data }); + elf.phdrs.items[phndx.data] = elf.ni.data; + + if (plt.got_plt == null) { + const plt_ni = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ + .alignment = node_block_align, + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt }); + elf.phdrs.items[phndx.plt] = plt_ni; + } + + elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{ + // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one + // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. + .alignment = node_block_align.max(addr_align), + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro }); + elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro; + + if (comp.config.any_non_single_threaded) { + elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ + .alignment = node_block_align, + .moved = true, + .bubbles_moved = false, + }); + elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls }); + elf.phdrs.items[phndx.tls] = elf.ni.tls; + } + + elf.phdrs.items[phndx.gnu_stack] = .none; + } + + switch (class) { + .NONE, _ => unreachable, + inline else => |ct_class| { const ElfN = ct_class.ElfN(); - elf.ni.ehdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ + // In loadable modules, the ehdr goes in the rodata segment, as described above. + const parent_ni = switch (@"type") { + .REL => elf.ni.elf, + .DYN, .EXEC => elf.ni.rodata, + }; + elf.ni.ehdr = try elf.mf.addFirstChildNode(gpa, parent_ni, .{ .size = @sizeOf(ElfN.Ehdr), .alignment = addr_align, .fixed = true, @@ -3699,10 +3788,8 @@ fn initHeaders( ehdr.shnum = 1; // Only the null shdr initially---will be incremented by `addSection` ehdr.shstrndx = std.elf.SHN_UNDEF; if (elf.targetEndian() != native_endian) std.mem.byteSwapAllFields(ElfN.Ehdr, ehdr); - - break :entsize .{ .ph = @sizeOf(ElfN.Phdr), .sh = @sizeOf(ElfN.Shdr) }; }, - }; + } elf.ni.shdr = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ .size = 1 * entsize.sh, // as above, only the null shdr initially @@ -3712,75 +3799,6 @@ fn initHeaders( }); elf.nodes.appendAssumeCapacity(.shdr); - if (@"type" != .REL) { - elf.ni.rodata = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ - // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node - .alignment = node_block_align.max(addr_align), - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.rodata }); - elf.phdrs.items[phndx.rodata] = elf.ni.rodata; - - elf.ni.phdr = try elf.mf.addOnlyChildNode(gpa, elf.ni.rodata, .{ - .size = @as(u64, phnum) * entsize.ph, - .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above - .moved = true, - .resized = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.phdr }); - elf.phdrs.items[phndx.phdr] = elf.ni.phdr; - - elf.ni.text = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ - .alignment = node_block_align, - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.text }); - elf.phdrs.items[phndx.text] = elf.ni.text; - - elf.ni.data = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ - // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node - .alignment = node_block_align.max(addr_align), - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.data }); - elf.phdrs.items[phndx.data] = elf.ni.data; - - if (plt.got_plt == null) { - const plt_ni = try elf.mf.addLastChildNode(gpa, elf.ni.elf, .{ - .alignment = node_block_align, - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.plt }); - elf.phdrs.items[phndx.plt] = plt_ni; - } - - elf.ni.data_rel_ro = try elf.mf.addOnlyChildNode(gpa, elf.ni.data, .{ - // Must be at least `addr_align` for the `PT_DYNAMIC` node to be placed inside this one - // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above. - .alignment = node_block_align.max(addr_align), - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.relro }); - elf.phdrs.items[phndx.relro] = elf.ni.data_rel_ro; - - if (comp.config.any_non_single_threaded) { - elf.ni.tls = try elf.mf.addLastChildNode(gpa, elf.ni.rodata, .{ - .alignment = node_block_align, - .moved = true, - .bubbles_moved = false, - }); - elf.nodes.appendAssumeCapacity(.{ .segment = phndx.tls }); - elf.phdrs.items[phndx.tls] = elf.ni.tls; - } - - elf.phdrs.items[phndx.gnu_stack] = .none; - } switch (class) { .NONE, _ => unreachable, inline else => |ct_class| { @@ -3818,7 +3836,9 @@ fn initHeaders( // actually `PT_NULL` for now, because we initialize `filesz` and `memsz` to zero. // Any which end up non-empty will have their size populated (and their type set to // `PT_LOAD`) by the segment virtual address space allocation logic. - const phdr: []ElfN.Phdr = @ptrCast(@alignCast(elf.ni.phdr.slice(&elf.mf))); + const phdr: []ElfN.Phdr = @ptrCast(@alignCast( + elf.ni.phdr.slice(&elf.mf)[0 .. phnum * @sizeOf(ElfN.Phdr)], + )); const ph_phdr = &phdr[phndx.phdr]; ph_phdr.* = .{ @@ -4900,14 +4920,11 @@ const PhdrSlice = union(std.elf.CLASS) { }; fn phdrSlice(elf: *Elf) PhdrSlice { assert(elf.ehdrType() != .REL); - const slice = elf.ni.phdr.slice(&elf.mf); return switch (elf.identClass()) { .NONE, _ => unreachable, - inline else => |class| @unionInit( - PhdrSlice, - @tagName(class), - @ptrCast(@alignCast(slice)), - ), + inline else => |class| @unionInit(PhdrSlice, @tagName(class), @ptrCast(@alignCast( + elf.ni.phdr.slice(&elf.mf)[0 .. elf.phdrs.items.len * @sizeOf(class.ElfN().Phdr)], + ))), }; }