From 53340544c6666d9d35463f509fbe0416f607c91c Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 3 Oct 2023 11:33:03 +0200 Subject: [PATCH] elf: get hello-world with LLVM in Zig working --- src/link/Elf.zig | 89 +++++++++++++++++++++-------------------- src/link/Elf/Object.zig | 49 ++++++++++++++++++++++- 2 files changed, 93 insertions(+), 45 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index e1abf7e120..530b682b15 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1245,6 +1245,9 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node // Generate and emit non-incremental sections. try self.initSections(); try self.sortSections(); + for (self.objects.items) |index| { + try self.file(index).?.object.addAtomsToOutputSections(self); + } try self.updateSectionSizes(); try self.allocateSections(); @@ -1424,7 +1427,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node phdr_table_load.p_filesz = 0; self.phdr_table_dirty = false; - } + } else try self.writePhdrs(); if (self.shdr_table_dirty) { const shsize: u64 = switch (self.ptr_width) { @@ -1476,8 +1479,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node self.shdr_table_dirty = false; } + try self.writeAtoms(); try self.writeSyntheticSections(); - try self.writeObjects(); if (self.entry_addr == null and self.base.options.effectiveOutputMode() == .Exe) { log.debug("flushing. no_entry_point_found = true", .{}); @@ -1485,7 +1488,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node } else { log.debug("flushing. no_entry_point_found = false", .{}); self.error_flags.no_entry_point_found = false; - try self.writeElfHeader(); + try self.writeHeader(); } // Dump the state for easy debugging. @@ -1756,30 +1759,6 @@ fn scanRelocs(self: *Elf) !void { } } -fn writeObjects(self: *Elf) !void { - const gpa = self.base.allocator; - - for (self.objects.items) |index| { - const object = self.file(index).?.object; - for (object.atoms.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - - const shdr = &self.shdrs.items[atom_ptr.outputShndx().?]; - if (shdr.sh_type == elf.SHT_NOBITS) continue; - if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue; // TODO we don't yet know how to handle non-alloc sections - - const file_offset = shdr.sh_offset + atom_ptr.value - shdr.sh_addr; - log.debug("writing atom({d}) at 0x{x}", .{ atom_ptr.atom_index, file_offset }); - const code = try object.codeDecompressAlloc(self, atom_ptr.atom_index); - defer gpa.free(code); - - try atom_ptr.resolveRelocs(self, code); - try self.base.file.?.pwriteAll(code, file_offset); - } - } -} - fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !void { const tracy = trace(@src()); defer tracy.end(); @@ -2480,7 +2459,14 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) } } -fn writeElfHeader(self: *Elf) !void { +fn writePhdrs(self: *Elf) !void { + const phoff = @sizeOf(elf.Elf64_Ehdr); + const phdrs_size = self.phdrs.items.len * @sizeOf(elf.Elf64_Phdr); + log.debug("writing program headers from 0x{x} to 0x{x}", .{ phoff, phoff + phdrs_size }); + try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.phdrs.items), phoff); +} + +fn writeHeader(self: *Elf) !void { var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; var index: usize = 0; @@ -2532,7 +2518,11 @@ fn writeElfHeader(self: *Elf) !void { const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; - const phdr_table_offset = self.phdrs.items[self.phdr_table_index.?].p_offset; + // TODO + const phdr_table_offset = if (self.phdr_table_index) |ind| + self.phdrs.items[ind].p_offset + else + @sizeOf(elf.Elf64_Ehdr); switch (self.ptr_width) { .p32 => { mem.writeInt(u32, hdr_buf[index..][0..4], @as(u32, @intCast(e_entry)), endian); @@ -3401,13 +3391,7 @@ fn initSections(self: *Elf) !void { }; for (self.objects.items) |index| { - const object = self.file(index).?.object; - for (object.atoms.items) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - const shdr = atom_ptr.inputShdr(self); - atom_ptr.output_section_index = try object.initOutputSection(self, shdr); - } + try self.file(index).?.object.initOutputSections(self); } if (self.got.entries.items.len > 0 and self.got_section_index == null) { @@ -3525,14 +3509,6 @@ fn sortSections(self: *Elf) !void { self.shdrs.appendAssumeCapacity(slice[sorted.shndx]); } - for (self.objects.items) |index| { - for (self.file(index).?.atoms()) |atom_index| { - const atom_ptr = self.atom(atom_index) orelse continue; - if (!atom_ptr.flags.alive) continue; - atom_ptr.output_section_index = backlinks[atom_ptr.output_section_index]; - } - } - for (&[_]*?u16{ &self.eh_frame_section_index, &self.eh_frame_hdr_section_index, @@ -3891,6 +3867,31 @@ fn allocateAtoms(self: *Elf) void { } } +fn writeAtoms(self: *Elf) !void { + const gpa = self.base.allocator; + for (self.shdrs.items, 0..) |shdr, shndx| { + if (shdr.sh_type == elf.SHT_NULL) continue; + if (shdr.sh_type == elf.SHT_NOBITS) continue; + + log.debug("writing atoms in '{s}' section", .{self.shstrtab.getAssumeExists(shdr.sh_name)}); + + const buffer = try gpa.alloc(u8, shdr.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 (self.objects.items) |index| { + try self.file(index).?.object.writeAtoms(self, @intCast(shndx), buffer); + } + + try self.base.file.?.pwriteAll(buffer, shdr.sh_offset); + } +} + fn updateSymtabSize(self: *Elf) !void { var sizes = SymtabSize{}; diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index 0c7c3376f9..de89ee822a 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -20,6 +20,7 @@ cies: std.ArrayListUnmanaged(Cie) = .{}, alive: bool = true, num_dynrelocs: u32 = 0, +output_sections: std.AutoArrayHashMapUnmanaged(u16, std.ArrayListUnmanaged(Atom.Index)) = .{}, output_symtab_size: Elf.SymtabSize = .{}, pub fn isObject(file: std.fs.File) bool { @@ -42,6 +43,10 @@ pub fn deinit(self: *Object, allocator: Allocator) void { self.comdat_groups.deinit(allocator); self.fdes.deinit(allocator); self.cies.deinit(allocator); + for (self.output_sections.values()) |*list| { + list.deinit(allocator); + } + self.output_sections.deinit(allocator); } pub fn parse(self: *Object, elf_file: *Elf) !void { @@ -193,7 +198,7 @@ fn addAtom( } } -pub fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 { +fn initOutputSection(self: Object, elf_file: *Elf, shdr: elf.Elf64_Shdr) error{OutOfMemory}!u16 { const name = blk: { const name = self.strings.getAssumeExists(shdr.sh_name); if (shdr.sh_flags & elf.SHF_MERGE != 0) break :blk name; @@ -601,6 +606,30 @@ pub fn convertCommonSymbols(self: *Object, elf_file: *Elf) !void { } } +pub fn initOutputSections(self: Object, elf_file: *Elf) !void { + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const shdr = atom.inputShdr(elf_file); + _ = try self.initOutputSection(elf_file, shdr); + } +} + +pub fn addAtomsToOutputSections(self: *Object, elf_file: *Elf) !void { + for (self.atoms.items) |atom_index| { + const atom = elf_file.atom(atom_index) orelse continue; + if (!atom.flags.alive) continue; + const shdr = atom.inputShdr(elf_file); + atom.output_section_index = self.initOutputSection(elf_file, shdr) catch unreachable; + + if (shdr.sh_type == elf.SHT_NOBITS) continue; + const gpa = elf_file.base.allocator; + const gop = try self.output_sections.getOrPut(gpa, atom.output_section_index); + if (!gop.found_existing) gop.value_ptr.* = .{}; + try gop.value_ptr.append(gpa, atom_index); + } +} + pub fn updateSectionSizes(self: Object, elf_file: *Elf) void { for (self.atoms.items) |atom_index| { const atom = elf_file.atom(atom_index) orelse continue; @@ -640,6 +669,24 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { } } +pub fn writeAtoms(self: Object, elf_file: *Elf, output_section_index: u16, buffer: []u8) !void { + const gpa = elf_file.base.allocator; + const atom_list = self.output_sections.get(output_section_index) orelse return; + const shdr = elf_file.shdrs.items[output_section_index]; + for (atom_list.items) |atom_index| { + const atom = elf_file.atom(atom_index).?; + assert(atom.flags.alive); + const offset = atom.value - shdr.sh_addr; + log.debug("writing atom({d}) at 0x{x}", .{ atom_index, shdr.sh_offset + offset }); + // TODO decompress directly into provided buffer + const out_code = buffer[offset..][0..atom.size]; + const in_code = try self.codeDecompressAlloc(elf_file, atom_index); + defer gpa.free(in_code); + @memcpy(out_code, in_code); + try atom.resolveRelocs(elf_file, out_code); + } +} + pub fn updateSymtabSize(self: *Object, elf_file: *Elf) void { for (self.locals()) |local_index| { const local = elf_file.symbol(local_index);