| 1 | value: i64 = 0, |
| 2 | size: u64 = 0, |
| 3 | alignment: Atom.Alignment = .@"1", |
| 4 | output_section_index: u32 = 0, |
| 5 | // atoms: std.ArrayList(Elf.Ref) = .empty, |
| 6 | atoms: std.array_hash_map.Auto(Elf.Ref, void) = .empty, |
| 7 | |
| 8 | dirty: bool = true, |
| 9 | |
| 10 | pub fn deinit(list: *AtomList, allocator: Allocator) void { |
| 11 | list.atoms.deinit(allocator); |
| 12 | } |
| 13 | |
| 14 | pub fn address(list: AtomList, elf_file: *Elf) i64 { |
| 15 | const shdr = elf_file.sections.items(.shdr)[list.output_section_index]; |
| 16 | return @as(i64, @intCast(shdr.sh_addr)) + list.value; |
| 17 | } |
| 18 | |
| 19 | pub fn offset(list: AtomList, elf_file: *Elf) u64 { |
| 20 | const shdr = elf_file.sections.items(.shdr)[list.output_section_index]; |
| 21 | return shdr.sh_offset + @as(u64, @intCast(list.value)); |
| 22 | } |
| 23 | |
| 24 | pub fn updateSize(list: *AtomList, elf_file: *Elf) void { |
| 25 | assert(list.dirty); |
| 26 | for (list.atoms.keys()) |ref| { |
| 27 | const atom_ptr = elf_file.atom(ref).?; |
| 28 | assert(atom_ptr.alive); |
| 29 | const off = atom_ptr.alignment.forward(list.size); |
| 30 | const padding = off - list.size; |
| 31 | atom_ptr.value = @intCast(off); |
| 32 | list.size += padding + atom_ptr.size; |
| 33 | list.alignment = list.alignment.max(atom_ptr.alignment); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | pub fn allocate(list: *AtomList, elf_file: *Elf) !void { |
| 38 | assert(list.dirty); |
| 39 | |
| 40 | const alloc_res = try elf_file.allocateChunk(.{ |
| 41 | .shndx = list.output_section_index, |
| 42 | .size = list.size, |
| 43 | .alignment = list.alignment, |
| 44 | .requires_padding = false, |
| 45 | }); |
| 46 | list.value = @intCast(alloc_res.value); |
| 47 | |
| 48 | log.debug("allocated atom_list({d}) at 0x{x}", .{ list.output_section_index, list.address(elf_file) }); |
| 49 | |
| 50 | const slice = elf_file.sections.slice(); |
| 51 | const shdr = &slice.items(.shdr)[list.output_section_index]; |
| 52 | const last_atom_ref = &slice.items(.last_atom)[list.output_section_index]; |
| 53 | |
| 54 | const expand_section = if (elf_file.atom(alloc_res.placement)) |placement_atom| |
| 55 | placement_atom.nextAtom(elf_file) == null |
| 56 | else |
| 57 | true; |
| 58 | if (expand_section) last_atom_ref.* = list.lastAtom(elf_file).ref(); |
| 59 | shdr.sh_addralign = @max(shdr.sh_addralign, list.alignment.toByteUnits().?); |
| 60 | |
| 61 | // This currently ignores Thunks as valid chunks. |
| 62 | { |
| 63 | var idx: usize = 0; |
| 64 | while (idx < list.atoms.keys().len) : (idx += 1) { |
| 65 | const curr_atom_ptr = elf_file.atom(list.atoms.keys()[idx]).?; |
| 66 | if (idx > 0) { |
| 67 | curr_atom_ptr.prev_atom_ref = list.atoms.keys()[idx - 1]; |
| 68 | } |
| 69 | if (idx + 1 < list.atoms.keys().len) { |
| 70 | curr_atom_ptr.next_atom_ref = list.atoms.keys()[idx + 1]; |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | if (elf_file.atom(alloc_res.placement)) |placement_atom| { |
| 76 | list.firstAtom(elf_file).prev_atom_ref = placement_atom.ref(); |
| 77 | list.lastAtom(elf_file).next_atom_ref = placement_atom.next_atom_ref; |
| 78 | placement_atom.next_atom_ref = list.firstAtom(elf_file).ref(); |
| 79 | } |
| 80 | |
| 81 | // If we had a link from Atom to parent AtomList we would not need to |
| 82 | // update Atom's value or osec index. |
| 83 | for (list.atoms.keys()) |ref| { |
| 84 | const atom_ptr = elf_file.atom(ref).?; |
| 85 | atom_ptr.output_section_index = list.output_section_index; |
| 86 | atom_ptr.value += list.value; |
| 87 | } |
| 88 | |
| 89 | list.dirty = false; |
| 90 | } |
| 91 | |
| 92 | pub fn write(list: AtomList, buffer: *std.Io.Writer.Allocating, undefs: anytype, elf_file: *Elf) !void { |
| 93 | const comp = elf_file.base.comp; |
| 94 | const gpa = comp.gpa; |
| 95 | const io = comp.io; |
| 96 | const osec = elf_file.sections.items(.shdr)[list.output_section_index]; |
| 97 | assert(osec.sh_type != elf.SHT_NOBITS); |
| 98 | assert(!list.dirty); |
| 99 | |
| 100 | log.debug("writing atoms in section '{s}'", .{elf_file.getShString(osec.sh_name)}); |
| 101 | |
| 102 | const list_size = math.cast(usize, list.size) orelse return error.Overflow; |
| 103 | try buffer.writer.splatByteAll(0, list_size); |
| 104 | |
| 105 | for (list.atoms.keys()) |ref| { |
| 106 | const atom_ptr = elf_file.atom(ref).?; |
| 107 | assert(atom_ptr.alive); |
| 108 | |
| 109 | const off = math.cast(usize, atom_ptr.value - list.value) orelse return error.Overflow; |
| 110 | const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; |
| 111 | |
| 112 | log.debug(" atom({f}) at 0x{x}", .{ ref, list.offset(elf_file) + off }); |
| 113 | |
| 114 | const object = atom_ptr.file(elf_file).?.object; |
| 115 | const code = try object.codeDecompressAlloc(elf_file, ref.index); |
| 116 | defer gpa.free(code); |
| 117 | const out_code = buffer.written()[off..][0..size]; |
| 118 | @memcpy(out_code, code); |
| 119 | |
| 120 | if (osec.sh_flags & elf.SHF_ALLOC == 0) |
| 121 | try atom_ptr.resolveRelocsNonAlloc(elf_file, out_code, undefs) |
| 122 | else |
| 123 | try atom_ptr.resolveRelocsAlloc(elf_file, out_code); |
| 124 | } |
| 125 | |
| 126 | try elf_file.base.file.?.writePositionalAll(io, buffer.written(), list.offset(elf_file)); |
| 127 | buffer.clearRetainingCapacity(); |
| 128 | } |
| 129 | |
| 130 | pub fn writeRelocatable(list: AtomList, buffer: *std.array_list.Managed(u8), elf_file: *Elf) !void { |
| 131 | const comp = elf_file.base.comp; |
| 132 | const gpa = comp.gpa; |
| 133 | const io = comp.io; |
| 134 | const osec = elf_file.sections.items(.shdr)[list.output_section_index]; |
| 135 | assert(osec.sh_type != elf.SHT_NOBITS); |
| 136 | |
| 137 | log.debug("writing atoms in section '{s}'", .{elf_file.getShString(osec.sh_name)}); |
| 138 | |
| 139 | const list_size = math.cast(usize, list.size) orelse return error.Overflow; |
| 140 | try buffer.ensureUnusedCapacity(list_size); |
| 141 | buffer.appendNTimesAssumeCapacity(0, list_size); |
| 142 | |
| 143 | for (list.atoms.keys()) |ref| { |
| 144 | const atom_ptr = elf_file.atom(ref).?; |
| 145 | assert(atom_ptr.alive); |
| 146 | |
| 147 | const off = math.cast(usize, atom_ptr.value - list.value) orelse return error.Overflow; |
| 148 | const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow; |
| 149 | |
| 150 | log.debug(" atom({f}) at 0x{x}", .{ ref, list.offset(elf_file) + off }); |
| 151 | |
| 152 | const object = atom_ptr.file(elf_file).?.object; |
| 153 | const code = try object.codeDecompressAlloc(elf_file, ref.index); |
| 154 | defer gpa.free(code); |
| 155 | const out_code = buffer.items[off..][0..size]; |
| 156 | @memcpy(out_code, code); |
| 157 | } |
| 158 | |
| 159 | try elf_file.base.file.?.writePositionalAll(io, buffer.items, list.offset(elf_file)); |
| 160 | buffer.clearRetainingCapacity(); |
| 161 | } |
| 162 | |
| 163 | pub fn firstAtom(list: AtomList, elf_file: *Elf) *Atom { |
| 164 | assert(list.atoms.keys().len > 0); |
| 165 | return elf_file.atom(list.atoms.keys()[0]).?; |
| 166 | } |
| 167 | |
| 168 | pub fn lastAtom(list: AtomList, elf_file: *Elf) *Atom { |
| 169 | assert(list.atoms.keys().len > 0); |
| 170 | return elf_file.atom(list.atoms.keys()[list.atoms.keys().len - 1]).?; |
| 171 | } |
| 172 | |
| 173 | const Format = struct { |
| 174 | atom_list: AtomList, |
| 175 | elf_file: *Elf, |
| 176 | |
| 177 | fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 178 | const list = f.atom_list; |
| 179 | try writer.print("list : @{x} : shdr({d}) : align({x}) : size({x})", .{ |
| 180 | list.address(f.elf_file), |
| 181 | list.output_section_index, |
| 182 | list.alignment.toByteUnits() orelse 0, |
| 183 | list.size, |
| 184 | }); |
| 185 | try writer.writeAll(" : atoms{ "); |
| 186 | for (list.atoms.keys(), 0..) |ref, i| { |
| 187 | try writer.print("{f}", .{ref}); |
| 188 | if (i < list.atoms.keys().len - 1) try writer.writeAll(", "); |
| 189 | } |
| 190 | try writer.writeAll(" }"); |
| 191 | } |
| 192 | }; |
| 193 | |
| 194 | pub fn fmt(atom_list: AtomList, elf_file: *Elf) std.fmt.Alt(Format, Format.default) { |
| 195 | return .{ .data = .{ .atom_list = atom_list, .elf_file = elf_file } }; |
| 196 | } |
| 197 | |
| 198 | const assert = std.debug.assert; |
| 199 | const elf = std.elf; |
| 200 | const log = std.log.scoped(.link); |
| 201 | const math = std.math; |
| 202 | const std = @import("std"); |
| 203 | |
| 204 | const Allocator = std.mem.Allocator; |
| 205 | const Atom = @import("Atom.zig"); |
| 206 | const AtomList = @This(); |
| 207 | const Elf = @import("../Elf.zig"); |
| 208 | const Object = @import("Object.zig"); |