Commit 3df53d1722

Jakub Konka <kubkon@jakubkonka.com>
2023-11-07 11:19:55
elf: create skeleton of required changes for supporting -r mode
1 parent b3462b7
Changed files (3)
src/link/Elf/Object.zig
@@ -654,6 +654,21 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void {
     }
 }
 
+pub fn initRelaSections(self: Object, elf_file: *Elf) !void {
+    _ = self;
+    _ = elf_file;
+}
+
+pub fn updateRelaSectionsSizes(self: Object, elf_file: *Elf) void {
+    _ = self;
+    _ = elf_file;
+}
+
+pub fn writeRelaSections(self: Object, elf_file: *Elf) !void {
+    _ = self;
+    _ = elf_file;
+}
+
 pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void {
     const gpa = elf_file.base.allocator;
     const start = self.first_global orelse self.symtab.items.len;
src/link/Elf/ZigObject.zig
@@ -549,7 +549,7 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {
     try writer.writeAll(contents);
 }
 
-pub fn updateRelaSectionSizes(self: ZigObject, elf_file: *Elf) void {
+pub fn updateRelaSectionsSizes(self: ZigObject, elf_file: *Elf) void {
     _ = self;
 
     for (&[_]?u16{
src/link/Elf.zig
@@ -216,10 +216,6 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
                 sub_path, options.target.ofmt.fileExt(options.target.cpu.arch),
             });
         }
-        if (is_obj) {
-            // TODO until we implement -r option, we don't want to open a file at this stage.
-            return self;
-        }
     }
     errdefer if (self.base.intermediary_basename) |path| allocator.free(path);
 
@@ -642,7 +638,7 @@ pub fn initMetadata(self: *Elf) !void {
             .name = ".data.rel.ro.zig",
             .type = elf.SHT_PROGBITS,
             .addralign = 1,
-            .flags = elf.SHF_ALLOC | elf.SHF_WRITE, // TODO rename this section to .data.rel.ro
+            .flags = elf.SHF_ALLOC | elf.SHF_WRITE,
             .offset = std.math.maxInt(u64),
         });
         const shdr = &self.shdrs.items[self.zig_data_rel_ro_section_index.?];
@@ -942,31 +938,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     } else null;
     const gc_sections = self.base.options.gc_sections orelse false;
 
-    if (self.isObject() and self.zig_object_index == null) {
-        // TODO this will become -r route I guess. For now, just copy the object file.
-        const the_object_path = blk: {
-            if (self.base.options.objects.len != 0) {
-                break :blk self.base.options.objects[0].path;
-            }
-
-            if (comp.c_object_table.count() != 0)
-                break :blk comp.c_object_table.keys()[0].status.success.object_path;
-
-            if (module_obj_path) |p|
-                break :blk p;
-
-            // TODO I think this is unreachable. Audit this situation when solving the above TODO
-            // regarding eliding redundant object -> object transformations.
-            return error.NoObjectsToLink;
-        };
-        // This can happen when using --enable-cache and using the stage1 backend. In this case
-        // we can skip the file copy.
-        if (!mem.eql(u8, the_object_path, full_out_path)) {
-            try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{});
-        }
-        return;
-    }
-
     var csu = try CsuObjects.init(arena, self.base.options, comp);
     const compiler_rt_path: ?[]const u8 = blk: {
         if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
@@ -1388,7 +1359,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
             try self.handleAndReportParseError(obj.path, err, &parse_ctx);
     }
 
-    if (self.isStaticLib()) return self.flushStaticLib(comp);
+    if (self.isStaticLib()) return self.flushStaticLib();
 
     // Init all objects
     for (self.objects.items) |index| {
@@ -1432,7 +1403,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     self.resolveSymbols();
     self.markEhFrameAtomsDead();
 
-    if (self.isObject()) return self.flushObject(comp);
+    if (self.isObject()) return self.flushObject();
 
     try self.convertCommonSymbols();
     self.markImportsExports();
@@ -1527,8 +1498,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
     }
 }
 
-pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void {
-    _ = comp;
+pub fn flushStaticLib(self: *Elf) link.File.FlushError!void {
     const gpa = self.base.allocator;
 
     // First, we flush relocatable object file generated with our backends.
@@ -1539,7 +1509,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void
         try self.initSymtab();
         try self.initShStrtab();
         try self.sortShdrs();
-        zig_object.updateRelaSectionSizes(self);
+        zig_object.updateRelaSectionsSizes(self);
         self.updateSymtabSizeObject(zig_object);
         self.updateShStrtabSize();
 
@@ -1639,30 +1609,27 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void
     try self.base.file.?.pwriteAll(buffer.items, 0);
 }
 
-pub fn flushObject(self: *Elf, comp: *Compilation) link.File.FlushError!void {
-    _ = comp;
-
-    if (self.objects.items.len > 0) {
-        var err = try self.addErrorWithNotes(1);
-        try err.addMsg(self, "fatal linker error: too many input positionals", .{});
-        try err.addNote(self, "TODO implement '-r' option", .{});
-        return;
-    }
-
+pub fn flushObject(self: *Elf) link.File.FlushError!void {
     self.claimUnresolvedObject();
 
-    try self.initSections();
+    try self.initSectionsObject();
     try self.sortShdrs();
-    try self.updateSectionSizes();
+    for (self.objects.items) |index| {
+        try self.file(index).?.object.addAtomsToOutputSections(self);
+    }
+    try self.updateSectionSizesObject();
 
+    try self.allocateAllocSectionsObject();
     try self.allocateNonAllocSections();
+    self.allocateAtoms();
 
     if (build_options.enable_logging) {
         state_log.debug("{}", .{self.dumpState()});
     }
 
+    try self.writeAtomsObject();
+    try self.writeSyntheticSectionsObject();
     try self.writeShdrTable();
-    try self.writeSyntheticSections();
     try self.writeElfHeader();
 }
 
@@ -3460,6 +3427,32 @@ fn initSections(self: *Elf) !void {
     try self.initShStrtab();
 }
 
+fn initSectionsObject(self: *Elf) !void {
+    const ptr_size = self.ptrWidthBytes();
+
+    for (self.objects.items) |index| {
+        const object = self.file(index).?.object;
+        try object.initOutputSections(self);
+        try object.initRelaSections(self);
+    }
+
+    const needs_eh_frame = for (self.objects.items) |index| {
+        if (self.file(index).?.object.cies.items.len > 0) break true;
+    } else false;
+    if (needs_eh_frame) {
+        self.eh_frame_section_index = try self.addSection(.{
+            .name = ".eh_frame",
+            .type = elf.SHT_PROGBITS,
+            .flags = elf.SHF_ALLOC,
+            .addralign = ptr_size,
+            .offset = std.math.maxInt(u64),
+        });
+    }
+
+    try self.initSymtab();
+    try self.initShStrtab();
+}
+
 fn initSymtab(self: *Elf) !void {
     const small_ptr = switch (self.ptr_width) {
         .p32 => true,
@@ -3982,10 +3975,6 @@ fn updateSectionSizes(self: *Elf) !void {
         }
     }
 
-    if (self.zigObjectPtr()) |zig_object| {
-        zig_object.updateRelaSectionSizes(self);
-    }
-
     if (self.eh_frame_section_index) |index| {
         self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
     }
@@ -4065,6 +4054,37 @@ fn updateSectionSizes(self: *Elf) !void {
     self.updateShStrtabSize();
 }
 
+fn updateSectionSizesObject(self: *Elf) !void {
+    for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
+        if (atom_list.items.len == 0) continue;
+        const shdr = &self.shdrs.items[shndx];
+        for (atom_list.items) |atom_index| {
+            const atom_ptr = self.atom(atom_index) orelse continue;
+            if (!atom_ptr.flags.alive) continue;
+            const offset = atom_ptr.alignment.forward(shdr.sh_size);
+            const padding = offset - shdr.sh_size;
+            atom_ptr.value = offset;
+            shdr.sh_size += padding + atom_ptr.size;
+            shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
+        }
+    }
+
+    if (self.zigObjectPtr()) |zig_object| {
+        zig_object.updateRelaSectionsSizes(self);
+    }
+
+    for (self.objects.items) |index| {
+        self.file(index).?.object.updateRelaSectionsSizes(self);
+    }
+
+    if (self.eh_frame_section_index) |index| {
+        self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
+    }
+
+    self.updateSymtabSize();
+    self.updateShStrtabSize();
+}
+
 fn updateShStrtabSize(self: *Elf) void {
     if (self.shstrtab_section_index) |index| {
         self.shdrs.items[index].sh_size = self.shstrtab.items.len;
@@ -4294,6 +4314,12 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
     }
 }
 
+/// Allocates alloc sections when merging relocatable objects files together.
+fn allocateAllocSectionsObject(self: *Elf) !void {
+    _ = self;
+    @panic("TODO");
+}
+
 /// Allocates non-alloc sections (debug info, symtabs, etc.).
 fn allocateNonAllocSections(self: *Elf) !void {
     for (self.shdrs.items, 0..) |*shdr, shndx| {
@@ -4484,6 +4510,67 @@ fn writeAtoms(self: *Elf) !void {
     try self.reportUndefined(&undefs);
 }
 
+fn writeAtomsObject(self: *Elf) !void {
+    const gpa = self.base.allocator;
+
+    // TODO iterate over `output_sections` directly
+    for (self.shdrs.items, 0..) |shdr, shndx| {
+        if (shdr.sh_type == elf.SHT_NULL) continue;
+        if (shdr.sh_type == elf.SHT_NOBITS) continue;
+
+        const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
+
+        log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
+
+        // TODO really, really handle debug section separately
+        const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
+            const zig_object = self.zigObjectPtr().?;
+            if (shndx == self.debug_info_section_index.?)
+                break :blk zig_object.debug_info_section_zig_size;
+            if (shndx == self.debug_abbrev_section_index.?)
+                break :blk zig_object.debug_abbrev_section_zig_size;
+            if (shndx == self.debug_str_section_index.?)
+                break :blk zig_object.debug_str_section_zig_size;
+            if (shndx == self.debug_aranges_section_index.?)
+                break :blk zig_object.debug_aranges_section_zig_size;
+            if (shndx == self.debug_line_section_index.?)
+                break :blk zig_object.debug_line_section_zig_size;
+            unreachable;
+        } else 0;
+        const sh_offset = shdr.sh_offset + base_offset;
+        const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
+
+        const buffer = try gpa.alloc(u8, sh_size);
+        defer gpa.free(buffer);
+        const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
+            shdr.sh_flags & elf.SHF_EXECINSTR != 0)
+            0xcc // int3
+        else
+            0;
+        @memset(buffer, padding_byte);
+
+        for (atom_list.items) |atom_index| {
+            const atom_ptr = self.atom(atom_index).?;
+            assert(atom_ptr.flags.alive);
+
+            const object = atom_ptr.file(self).?.object;
+            const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
+                return error.Overflow;
+            const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
+
+            log.debug("writing atom({d}) at 0x{x}", .{ atom_index, sh_offset + offset });
+
+            // TODO decompress directly into provided buffer
+            const out_code = buffer[offset..][0..size];
+            const in_code = try object.codeDecompressAlloc(self, atom_index);
+            defer gpa.free(in_code);
+            @memcpy(out_code, in_code);
+        }
+
+        try self.base.file.?.pwriteAll(buffer, sh_offset);
+    }
+}
+
 fn updateSymtabSize(self: *Elf) void {
     var sizes = SymtabSize{};
 
@@ -4567,10 +4654,6 @@ fn updateSymtabSizeObject(self: *Elf, zig_object: *ZigObject) void {
 fn writeSyntheticSections(self: *Elf) !void {
     const gpa = self.base.allocator;
 
-    if (self.zigObjectPtr()) |zig_object| {
-        try zig_object.writeRelaSections(self);
-    }
-
     if (self.interp_section_index) |shndx| {
         const shdr = self.shdrs.items[shndx];
         const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
@@ -4698,6 +4781,30 @@ fn writeSyntheticSections(self: *Elf) !void {
     try self.writeShStrtab();
 }
 
+fn writeSyntheticSectionsObject(self: *Elf) !void {
+    const gpa = self.base.allocator;
+
+    if (self.zigObjectPtr()) |zig_object| {
+        try zig_object.writeRelaSections(self);
+    }
+
+    for (self.objects.items) |index| {
+        try self.file(index).?.object.writeRelaSections(self);
+    }
+
+    if (self.eh_frame_section_index) |shndx| {
+        const shdr = self.shdrs.items[shndx];
+        const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
+        var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
+        defer buffer.deinit();
+        try eh_frame.writeEhFrame(self, buffer.writer());
+        try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
+    }
+
+    try self.writeSymtab();
+    try self.writeShStrtab();
+}
+
 fn writeShStrtab(self: *Elf) !void {
     if (self.shstrtab_section_index) |index| {
         const shdr = self.shdrs.items[index];