Commit 59fcf16732

Jakub Konka <kubkon@jakubkonka.com>
2023-10-10 13:20:42
elf: move creation of PT_PHDR out of initPhdrs and initMetadata
1 parent e571208
Changed files (1)
src
link
src/link/Elf.zig
@@ -35,7 +35,10 @@ phdr_table_index: ?u16 = null,
 /// The index into the program headers of the PT_LOAD program header containing the phdr
 /// Most linkers would merge this with phdr_load_ro_index,
 /// but incremental linking means we can't ensure they are consecutive.
+/// If this segment is not needed, it won't get emitted.
 phdr_table_load_index: ?u16 = null,
+/// The index into the program headers of the PT_TLS program header.
+phdr_tls_index: ?u16 = null,
 /// The index into the program headers of a PT_LOAD program header with Read and Execute flags
 phdr_load_re_zig_index: ?u16 = null,
 /// The index into the program headers of the global offset table.
@@ -47,8 +50,6 @@ phdr_load_ro_zig_index: ?u16 = null,
 phdr_load_rw_zig_index: ?u16 = null,
 /// The index into the program headers of a PT_LOAD program header with zerofill data.
 phdr_load_zerofill_zig_index: ?u16 = null,
-/// The index into the program headers of the PT_TLS program header.
-phdr_tls_index: ?u16 = null,
 
 entry_index: ?Symbol.Index = null,
 page_size: u32,
@@ -264,7 +265,29 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
     // Append null byte to string tables
     try self.shstrtab.buffer.append(allocator, 0);
     try self.strtab.buffer.append(allocator, 0);
-    try self.dynstrtab.buffer.append(allocator, 0);
+
+    const is_obj_or_ar = switch (options.output_mode) {
+        .Obj => true,
+        .Lib => options.link_mode == .Static,
+        else => false,
+    };
+    if (!is_obj_or_ar) {
+        try self.dynstrtab.buffer.append(allocator, 0);
+
+        // Initialize PT_PHDR program header
+        const p_align: u16 = switch (self.ptr_width) {
+            .p32 => @alignOf(elf.Elf32_Phdr),
+            .p64 => @alignOf(elf.Elf64_Phdr),
+        };
+        self.phdr_table_index = try self.addPhdr(.{
+            .type = elf.PT_PHDR,
+            .flags = elf.PF_R,
+            .@"align" = p_align,
+            .addr = self.calcImageBase() + @sizeOf(elf.Elf64_Ehdr),
+            .offset = @sizeOf(elf.Elf64_Ehdr),
+        });
+        // self.phdr_table_dirty = true;
+    }
 
     if (options.module != null and !options.use_llvm) {
         if (!options.strip) {
@@ -675,30 +698,10 @@ pub fn initMetadata(self: *Elf) !void {
     const gpa = self.base.allocator;
     const ptr_size: u8 = self.ptrWidthBytes();
     const is_linux = self.base.options.target.os.tag == .linux;
-    const image_base = self.calcImageBase();
-
-    if (self.phdr_table_index == null) {
-        self.phdr_table_index = @intCast(self.phdrs.items.len);
-        const p_align: u16 = switch (self.ptr_width) {
-            .p32 => @alignOf(elf.Elf32_Phdr),
-            .p64 => @alignOf(elf.Elf64_Phdr),
-        };
-        try self.phdrs.append(gpa, .{
-            .p_type = elf.PT_PHDR,
-            .p_offset = 0,
-            .p_filesz = 0,
-            .p_vaddr = image_base,
-            .p_paddr = image_base,
-            .p_memsz = 0,
-            .p_align = p_align,
-            .p_flags = elf.PF_R,
-        });
-        self.phdr_table_dirty = true;
-    }
 
     if (self.phdr_table_load_index == null) {
         self.phdr_table_load_index = try self.allocateSegment(.{
-            .addr = image_base,
+            .addr = self.calcImageBase(),
             .size = 0,
             .alignment = self.page_size,
         });
@@ -1705,67 +1708,67 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         }
     }
 
-    if (self.phdr_table_dirty) {
-        const phsize: u64 = switch (self.ptr_width) {
-            .p32 => @sizeOf(elf.Elf32_Phdr),
-            .p64 => @sizeOf(elf.Elf64_Phdr),
-        };
-
-        const phdr_table_index = self.phdr_table_index.?;
-        const phdr_table = &self.phdrs.items[phdr_table_index];
-        const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
-
-        const allocated_size = self.allocatedSize(phdr_table.p_offset);
-        const needed_size = self.phdrs.items.len * phsize;
-
-        if (needed_size > allocated_size) {
-            phdr_table.p_offset = 0; // free the space
-            phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align)));
-        }
-
-        phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
-        const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
-        phdr_table_load.p_filesz = load_align_offset + needed_size;
-        phdr_table_load.p_memsz = load_align_offset + needed_size;
-
-        phdr_table.p_filesz = needed_size;
-        phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
-        phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
-        phdr_table.p_memsz = needed_size;
-
-        switch (self.ptr_width) {
-            .p32 => {
-                const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
-                defer gpa.free(buf);
-
-                for (buf, 0..) |*phdr, i| {
-                    phdr.* = phdrTo32(self.phdrs.items[i]);
-                    if (foreign_endian) {
-                        mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
-                    }
-                }
-                try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
-            },
-            .p64 => {
-                const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
-                defer gpa.free(buf);
-
-                for (buf, 0..) |*phdr, i| {
-                    phdr.* = self.phdrs.items[i];
-                    if (foreign_endian) {
-                        mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
-                    }
-                }
-                try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
-            },
-        }
-
-        // We don't actually care if the phdr load section overlaps, only the phdr section matters.
-        phdr_table_load.p_offset = 0;
-        phdr_table_load.p_filesz = 0;
-
-        self.phdr_table_dirty = false;
-    } else try self.writePhdrs();
+    // if (self.phdr_table_dirty) {
+    //     const phsize: u64 = switch (self.ptr_width) {
+    //         .p32 => @sizeOf(elf.Elf32_Phdr),
+    //         .p64 => @sizeOf(elf.Elf64_Phdr),
+    //     };
+
+    //     const phdr_table_index = self.phdr_table_index.?;
+    //     const phdr_table = &self.phdrs.items[phdr_table_index];
+    //     const phdr_table_load = &self.phdrs.items[self.phdr_table_load_index.?];
+
+    //     const allocated_size = self.allocatedSize(phdr_table.p_offset);
+    //     const needed_size = self.phdrs.items.len * phsize;
+
+    //     if (needed_size > allocated_size) {
+    //         phdr_table.p_offset = 0; // free the space
+    //         phdr_table.p_offset = self.findFreeSpace(needed_size, @as(u32, @intCast(phdr_table.p_align)));
+    //     }
+
+    //     phdr_table_load.p_offset = mem.alignBackward(u64, phdr_table.p_offset, phdr_table_load.p_align);
+    //     const load_align_offset = phdr_table.p_offset - phdr_table_load.p_offset;
+    //     phdr_table_load.p_filesz = load_align_offset + needed_size;
+    //     phdr_table_load.p_memsz = load_align_offset + needed_size;
+
+    //     phdr_table.p_filesz = needed_size;
+    //     phdr_table.p_vaddr = phdr_table_load.p_vaddr + load_align_offset;
+    //     phdr_table.p_paddr = phdr_table_load.p_paddr + load_align_offset;
+    //     phdr_table.p_memsz = needed_size;
+
+    //     switch (self.ptr_width) {
+    //         .p32 => {
+    //             const buf = try gpa.alloc(elf.Elf32_Phdr, self.phdrs.items.len);
+    //             defer gpa.free(buf);
+
+    //             for (buf, 0..) |*phdr, i| {
+    //                 phdr.* = phdrTo32(self.phdrs.items[i]);
+    //                 if (foreign_endian) {
+    //                     mem.byteSwapAllFields(elf.Elf32_Phdr, phdr);
+    //                 }
+    //             }
+    //             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
+    //         },
+    //         .p64 => {
+    //             const buf = try gpa.alloc(elf.Elf64_Phdr, self.phdrs.items.len);
+    //             defer gpa.free(buf);
+
+    //             for (buf, 0..) |*phdr, i| {
+    //                 phdr.* = self.phdrs.items[i];
+    //                 if (foreign_endian) {
+    //                     mem.byteSwapAllFields(elf.Elf64_Phdr, phdr);
+    //                 }
+    //             }
+    //             try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), phdr_table.p_offset);
+    //         },
+    //     }
+
+    //     // We don't actually care if the phdr load section overlaps, only the phdr section matters.
+    //     phdr_table_load.p_offset = 0;
+    //     phdr_table_load.p_filesz = 0;
+
+    //     self.phdr_table_dirty = false;
+    // }
 
     if (self.shdr_table_dirty) {
         const shsize: u64 = switch (self.ptr_width) {
@@ -1817,6 +1820,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
         self.shdr_table_dirty = false;
     }
 
+    try self.writePhdrs();
     try self.writeAtoms();
     try self.writeSyntheticSections();
 
@@ -2963,12 +2967,7 @@ fn writeHeader(self: *Elf) !void {
     index += 4;
 
     const e_entry = if (self.entry_index) |entry_index| self.symbol(entry_index).value else 0;
-
-    // TODO
-    const phdr_table_offset = if (self.phdr_table_index) |ind|
-        self.phdrs.items[ind].p_offset
-    else
-        @sizeOf(elf.Elf64_Ehdr);
+    const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset;
     switch (self.ptr_width) {
         .p32 => {
             mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian);
@@ -4417,16 +4416,17 @@ fn updateSectionSizes(self: *Elf) !void {
     }
 }
 
-fn initPhdrs(self: *Elf) !void {
-    // Add PHDR phdr
-    const phdr_index = try self.addPhdr(.{
-        .type = elf.PT_PHDR,
-        .flags = elf.PF_R,
-        .@"align" = @alignOf(elf.Elf64_Phdr),
-        .addr = self.calcImageBase() + @sizeOf(elf.Elf64_Ehdr),
-        .offset = @sizeOf(elf.Elf64_Ehdr),
-    });
+fn resetPhdrs(self: *Elf) !void {
+    const gpa = self.base.allocator;
+    const phdrs = try self.phdrs.toOwnedSlice(gpa);
+    try self.phdrs.ensureUnusedCapacity(gpa, phdrs.len);
 
+    if (self.phdr_table_index) |phndx| {
+        self.phdrs.appendAssumeCapacity(phdrs[phndx]);
+    }
+}
+
+fn initPhdrs(self: *Elf) !void {
     // Add INTERP phdr if required
     if (self.interp_section_index) |index| {
         const shdr = self.shdrs.items[index];
@@ -4543,7 +4543,7 @@ fn initPhdrs(self: *Elf) !void {
 
     // Backpatch size of the PHDR phdr
     {
-        const phdr = &self.phdrs.items[phdr_index];
+        const phdr = &self.phdrs.items[self.phdr_table_index.?];
         const size = @sizeOf(elf.Elf64_Phdr) * self.phdrs.items.len;
         phdr.p_filesz = size;
         phdr.p_memsz = size;
@@ -4588,7 +4588,7 @@ pub inline fn shdrIsTls(shdr: *const elf.Elf64_Shdr) bool {
     return shdr.sh_flags & elf.SHF_TLS != 0;
 }
 
-fn allocateSectionsInMemory(self: *Elf, base_offset: u64) !void {
+fn allocateSectionsInMemory(self: *Elf, base_offset: u64) void {
     // We use this struct to track maximum alignment of all TLS sections.
     // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
     // in-file offsets have to be aligned against the start of TLS program header.
@@ -4713,9 +4713,9 @@ fn allocateSections(self: *Elf) !void {
     while (true) {
         const nphdrs = self.phdrs.items.len;
         const base_offset: u64 = @sizeOf(elf.Elf64_Ehdr) + nphdrs * @sizeOf(elf.Elf64_Phdr);
-        try self.allocateSectionsInMemory(base_offset);
+        self.allocateSectionsInMemory(base_offset);
         self.allocateSectionsInFile(base_offset);
-        self.phdrs.clearRetainingCapacity();
+        try self.resetPhdrs();
         try self.initPhdrs();
         if (nphdrs == self.phdrs.items.len) break;
     }