| ... | @@ -25,10 +25,9 @@ relocs_section_index: u32 = 0, | ... | @@ -25,10 +25,9 @@ relocs_section_index: u32 = 0, |
| 25 | /// Index of this atom in the linker's atoms table. | 25 | /// Index of this atom in the linker's atoms table. |
| 26 | atom_index: Index = 0, | 26 | atom_index: Index = 0, |
| 27 | | 27 | |
| 28 | /// Points to the previous and next neighbors, based on the `text_offset`. | 28 | /// Points to the previous and next neighbors. |
| 29 | /// This can be used to find, for example, the capacity of this `TextBlock`. | 29 | prev_atom_ref: Elf.Ref = .{}, |
| 30 | prev_index: Index = 0, | 30 | next_atom_ref: Elf.Ref = .{}, |
| 31 | next_index: Index = 0, | | |
| 32 | | 31 | |
| 33 | /// Specifies whether this atom is alive or has been garbage collected. | 32 | /// Specifies whether this atom is alive or has been garbage collected. |
| 34 | alive: bool = true, | 33 | alive: bool = true, |
| ... | @@ -52,6 +51,18 @@ pub fn address(self: Atom, elf_file: *Elf) i64 { | ... | @@ -52,6 +51,18 @@ pub fn address(self: Atom, elf_file: *Elf) i64 { |
| 52 | return @as(i64, @intCast(shdr.sh_addr)) + self.value; | 51 | return @as(i64, @intCast(shdr.sh_addr)) + self.value; |
| 53 | } | 52 | } |
| 54 | | 53 | |
| | 54 | pub fn ref(self: Atom) Elf.Ref { |
| | 55 | return .{ .index = self.atom_index, .file = self.file_index }; |
| | 56 | } |
| | 57 | |
| | 58 | pub fn prevAtom(self: Atom, elf_file: *Elf) ?*Atom { |
| | 59 | return elf_file.atom(self.prev_atom_ref); |
| | 60 | } |
| | 61 | |
| | 62 | pub fn nextAtom(self: Atom, elf_file: *Elf) ?*Atom { |
| | 63 | return elf_file.atom(self.next_atom_ref); |
| | 64 | } |
| | 65 | |
| 55 | pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 { | 66 | pub fn debugTombstoneValue(self: Atom, target: Symbol, elf_file: *Elf) ?u64 { |
| 56 | if (target.mergeSubsection(elf_file)) |msub| { | 67 | if (target.mergeSubsection(elf_file)) |msub| { |
| 57 | if (msub.alive) return null; | 68 | if (msub.alive) return null; |
| ... | @@ -95,18 +106,16 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 { | ... | @@ -95,18 +106,16 @@ pub fn priority(self: Atom, elf_file: *Elf) u64 { |
| 95 | /// File offset relocation happens transparently, so it is not included in | 106 | /// File offset relocation happens transparently, so it is not included in |
| 96 | /// this calculation. | 107 | /// this calculation. |
| 97 | pub fn capacity(self: Atom, elf_file: *Elf) u64 { | 108 | pub fn capacity(self: Atom, elf_file: *Elf) u64 { |
| 98 | const zo = elf_file.zigObjectPtr().?; | 109 | const next_addr = if (self.nextAtom(elf_file)) |next_atom| |
| 99 | const next_addr = if (zo.atom(self.next_index)) |next| | 110 | next_atom.address(elf_file) |
| 100 | next.address(elf_file) | | |
| 101 | else | 111 | else |
| 102 | std.math.maxInt(u32); | 112 | std.math.maxInt(u32); |
| 103 | return @intCast(next_addr - self.address(elf_file)); | 113 | return @intCast(next_addr - self.address(elf_file)); |
| 104 | } | 114 | } |
| 105 | | 115 | |
| 106 | pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { | 116 | pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { |
| 107 | const zo = elf_file.zigObjectPtr().?; | | |
| 108 | // No need to keep a free list node for the last block. | 117 | // No need to keep a free list node for the last block. |
| 109 | const next = zo.atom(self.next_index) orelse return false; | 118 | const next = self.nextAtom(elf_file) orelse return false; |
| 110 | const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file)); | 119 | const cap: u64 = @intCast(next.address(elf_file) - self.address(elf_file)); |
| 111 | const ideal_cap = Elf.padToIdeal(self.size); | 120 | const ideal_cap = Elf.padToIdeal(self.size); |
| 112 | if (cap <= ideal_cap) return false; | 121 | if (cap <= ideal_cap) return false; |
| ... | @@ -115,11 +124,10 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { | ... | @@ -115,11 +124,10 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool { |
| 115 | } | 124 | } |
| 116 | | 125 | |
| 117 | pub fn allocate(self: *Atom, elf_file: *Elf) !void { | 126 | pub fn allocate(self: *Atom, elf_file: *Elf) !void { |
| 118 | const zo = elf_file.zigObjectPtr().?; | | |
| 119 | const slice = elf_file.sections.slice(); | 127 | const slice = elf_file.sections.slice(); |
| 120 | const shdr = &slice.items(.shdr)[self.output_section_index]; | 128 | const shdr = &slice.items(.shdr)[self.output_section_index]; |
| 121 | const free_list = &slice.items(.free_list)[self.output_section_index]; | 129 | const free_list = &slice.items(.free_list)[self.output_section_index]; |
| 122 | const last_atom_index = &slice.items(.last_atom_index)[self.output_section_index]; | 130 | const last_atom_ref = &slice.items(.last_atom)[self.output_section_index]; |
| 123 | const new_atom_ideal_capacity = Elf.padToIdeal(self.size); | 131 | const new_atom_ideal_capacity = Elf.padToIdeal(self.size); |
| 124 | | 132 | |
| 125 | // We use these to indicate our intention to update metadata, placing the new atom, | 133 | // We use these to indicate our intention to update metadata, placing the new atom, |
| ... | @@ -127,7 +135,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { | ... | @@ -127,7 +135,7 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { |
| 127 | // It would be simpler to do it inside the for loop below, but that would cause a | 135 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 128 | // problem if an error was returned later in the function. So this action | 136 | // problem if an error was returned later in the function. So this action |
| 129 | // is actually carried out at the end of the function, when errors are no longer possible. | 137 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 130 | var atom_placement: ?Atom.Index = null; | 138 | var atom_placement: ?Elf.Ref = null; |
| 131 | var free_list_removal: ?usize = null; | 139 | var free_list_removal: ?usize = null; |
| 132 | | 140 | |
| 133 | // First we look for an appropriately sized free list node. | 141 | // First we look for an appropriately sized free list node. |
| ... | @@ -135,8 +143,8 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { | ... | @@ -135,8 +143,8 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { |
| 135 | self.value = blk: { | 143 | self.value = blk: { |
| 136 | var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; | 144 | var i: usize = if (elf_file.base.child_pid == null) 0 else free_list.items.len; |
| 137 | while (i < free_list.items.len) { | 145 | while (i < free_list.items.len) { |
| 138 | const big_atom_index = free_list.items[i]; | 146 | const big_atom_ref = free_list.items[i]; |
| 139 | const big_atom = zo.atom(big_atom_index).?; | 147 | const big_atom = elf_file.atom(big_atom_ref).?; |
| 140 | // We now have a pointer to a live atom that has too much capacity. | 148 | // We now have a pointer to a live atom that has too much capacity. |
| 141 | // Is it enough that we could fit this new atom? | 149 | // Is it enough that we could fit this new atom? |
| 142 | const cap = big_atom.capacity(elf_file); | 150 | const cap = big_atom.capacity(elf_file); |
| ... | @@ -163,50 +171,52 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { | ... | @@ -163,50 +171,52 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { |
| 163 | const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; | 171 | const keep_free_list_node = remaining_capacity >= Elf.min_text_capacity; |
| 164 | | 172 | |
| 165 | // Set up the metadata to be updated, after errors are no longer possible. | 173 | // Set up the metadata to be updated, after errors are no longer possible. |
| 166 | atom_placement = big_atom_index; | 174 | atom_placement = big_atom_ref; |
| 167 | if (!keep_free_list_node) { | 175 | if (!keep_free_list_node) { |
| 168 | free_list_removal = i; | 176 | free_list_removal = i; |
| 169 | } | 177 | } |
| 170 | break :blk @intCast(new_start_vaddr); | 178 | break :blk @intCast(new_start_vaddr); |
| 171 | } else if (zo.atom(last_atom_index.*)) |last| { | 179 | } else if (elf_file.atom(last_atom_ref.*)) |last_atom| { |
| 172 | const ideal_capacity = Elf.padToIdeal(last.size); | 180 | const ideal_capacity = Elf.padToIdeal(last_atom.size); |
| 173 | const ideal_capacity_end_vaddr = @as(u64, @intCast(last.value)) + ideal_capacity; | 181 | const ideal_capacity_end_vaddr = @as(u64, @intCast(last_atom.value)) + ideal_capacity; |
| 174 | const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); | 182 | const new_start_vaddr = self.alignment.forward(ideal_capacity_end_vaddr); |
| 175 | // Set up the metadata to be updated, after errors are no longer possible. | 183 | // Set up the metadata to be updated, after errors are no longer possible. |
| 176 | atom_placement = last.atom_index; | 184 | atom_placement = last_atom.ref(); |
| 177 | break :blk @intCast(new_start_vaddr); | 185 | break :blk @intCast(new_start_vaddr); |
| 178 | } else { | 186 | } else { |
| 179 | break :blk 0; | 187 | break :blk 0; |
| 180 | } | 188 | } |
| 181 | }; | 189 | }; |
| 182 | | 190 | |
| 183 | log.debug("allocated atom({d}) : '{s}' at 0x{x} to 0x{x}", .{ | 191 | log.debug("allocated atom({}) : '{s}' at 0x{x} to 0x{x}", .{ |
| 184 | self.atom_index, | 192 | self.ref(), |
| 185 | self.name(elf_file), | 193 | self.name(elf_file), |
| 186 | self.address(elf_file), | 194 | self.address(elf_file), |
| 187 | self.address(elf_file) + @as(i64, @intCast(self.size)), | 195 | self.address(elf_file) + @as(i64, @intCast(self.size)), |
| 188 | }); | 196 | }); |
| 189 | | 197 | |
| 190 | const expand_section = if (atom_placement) |placement_index| | 198 | const expand_section = if (atom_placement) |placement_ref| |
| 191 | zo.atom(placement_index).?.next_index == 0 | 199 | elf_file.atom(placement_ref).?.nextAtom(elf_file) == null |
| 192 | else | 200 | else |
| 193 | true; | 201 | true; |
| 194 | if (expand_section) { | 202 | if (expand_section) { |
| 195 | const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size))); | 203 | const needed_size: u64 = @intCast(self.value + @as(i64, @intCast(self.size))); |
| 196 | try elf_file.growAllocSection(self.output_section_index, needed_size); | 204 | try elf_file.growAllocSection(self.output_section_index, needed_size); |
| 197 | last_atom_index.* = self.atom_index; | 205 | last_atom_ref.* = self.ref(); |
| 198 | | 206 | |
| 199 | const zig_object = elf_file.zigObjectPtr().?; | 207 | switch (self.file(elf_file).?) { |
| 200 | if (zig_object.dwarf) |_| { | 208 | .zig_object => |zo| if (zo.dwarf) |_| { |
| 201 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | 209 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address |
| 202 | // range of the compilation unit. When we expand the text section, this range changes, | 210 | // range of the compilation unit. When we expand the text section, this range changes, |
| 203 | // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. | 211 | // so the DW_TAG.compile_unit tag of the .debug_info section becomes dirty. |
| 204 | zig_object.debug_info_section_dirty = true; | 212 | zo.debug_info_section_dirty = true; |
| 205 | // This becomes dirty for the same reason. We could potentially make this more | 213 | // This becomes dirty for the same reason. We could potentially make this more |
| 206 | // fine-grained with the addition of support for more compilation units. It is planned to | 214 | // fine-grained with the addition of support for more compilation units. It is planned to |
| 207 | // model each package as a different compilation unit. | 215 | // model each package as a different compilation unit. |
| 208 | zig_object.debug_aranges_section_dirty = true; | 216 | zo.debug_aranges_section_dirty = true; |
| 209 | zig_object.debug_rnglists_section_dirty = true; | 217 | zo.debug_rnglists_section_dirty = true; |
| | 218 | }, |
| | 219 | else => {}, |
| 210 | } | 220 | } |
| 211 | } | 221 | } |
| 212 | shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?); | 222 | shdr.sh_addralign = @max(shdr.sh_addralign, self.alignment.toByteUnits().?); |
| ... | @@ -214,21 +224,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { | ... | @@ -214,21 +224,21 @@ pub fn allocate(self: *Atom, elf_file: *Elf) !void { |
| 214 | // This function can also reallocate an atom. | 224 | // This function can also reallocate an atom. |
| 215 | // In this case we need to "unplug" it from its previous location before | 225 | // In this case we need to "unplug" it from its previous location before |
| 216 | // plugging it in to its new location. | 226 | // plugging it in to its new location. |
| 217 | if (zo.atom(self.prev_index)) |prev| { | 227 | if (self.prevAtom(elf_file)) |prev| { |
| 218 | prev.next_index = self.next_index; | 228 | prev.next_atom_ref = self.next_atom_ref; |
| 219 | } | 229 | } |
| 220 | if (zo.atom(self.next_index)) |next| { | 230 | if (self.nextAtom(elf_file)) |next| { |
| 221 | next.prev_index = self.prev_index; | 231 | next.prev_atom_ref = self.prev_atom_ref; |
| 222 | } | 232 | } |
| 223 | | 233 | |
| 224 | if (atom_placement) |big_atom_index| { | 234 | if (atom_placement) |big_atom_ref| { |
| 225 | const big_atom = zo.atom(big_atom_index).?; | 235 | const big_atom = elf_file.atom(big_atom_ref).?; |
| 226 | self.prev_index = big_atom_index; | 236 | self.prev_atom_ref = big_atom_ref; |
| 227 | self.next_index = big_atom.next_index; | 237 | self.next_atom_ref = big_atom.next_atom_ref; |
| 228 | big_atom.next_index = self.atom_index; | 238 | big_atom.next_atom_ref = self.ref(); |
| 229 | } else { | 239 | } else { |
| 230 | self.prev_index = 0; | 240 | self.prev_atom_ref = .{ .index = 0, .file = 0 }; |
| 231 | self.next_index = 0; | 241 | self.next_atom_ref = .{ .index = 0, .file = 0 }; |
| 232 | } | 242 | } |
| 233 | if (free_list_removal) |i| { | 243 | if (free_list_removal) |i| { |
| 234 | _ = free_list.swapRemove(i); | 244 | _ = free_list.swapRemove(i); |
| ... | @@ -248,64 +258,70 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { | ... | @@ -248,64 +258,70 @@ pub fn grow(self: *Atom, elf_file: *Elf) !void { |
| 248 | } | 258 | } |
| 249 | | 259 | |
| 250 | pub fn free(self: *Atom, elf_file: *Elf) void { | 260 | pub fn free(self: *Atom, elf_file: *Elf) void { |
| 251 | log.debug("freeAtom {d} ({s})", .{ self.atom_index, self.name(elf_file) }); | 261 | log.debug("freeAtom atom({}) ({s})", .{ self.ref(), self.name(elf_file) }); |
| 252 | | 262 | |
| 253 | const zo = elf_file.zigObjectPtr().?; | | |
| 254 | const comp = elf_file.base.comp; | 263 | const comp = elf_file.base.comp; |
| 255 | const gpa = comp.gpa; | 264 | const gpa = comp.gpa; |
| 256 | const shndx = self.output_section_index; | 265 | const shndx = self.output_section_index; |
| 257 | const slice = elf_file.sections.slice(); | 266 | const slice = elf_file.sections.slice(); |
| 258 | const free_list = &slice.items(.free_list)[shndx]; | 267 | const free_list = &slice.items(.free_list)[shndx]; |
| 259 | const last_atom_index = &slice.items(.last_atom_index)[shndx]; | 268 | const last_atom_ref = &slice.items(.last_atom)[shndx]; |
| 260 | var already_have_free_list_node = false; | 269 | var already_have_free_list_node = false; |
| 261 | { | 270 | { |
| 262 | var i: usize = 0; | 271 | var i: usize = 0; |
| 263 | // TODO turn free_list into a hash map | 272 | // TODO turn free_list into a hash map |
| 264 | while (i < free_list.items.len) { | 273 | while (i < free_list.items.len) { |
| 265 | if (free_list.items[i] == self.atom_index) { | 274 | if (free_list.items[i].eql(self.ref())) { |
| 266 | _ = free_list.swapRemove(i); | 275 | _ = free_list.swapRemove(i); |
| 267 | continue; | 276 | continue; |
| 268 | } | 277 | } |
| 269 | if (free_list.items[i] == self.prev_index) { | 278 | if (self.prevAtom(elf_file)) |prev_atom| { |
| 270 | already_have_free_list_node = true; | 279 | if (free_list.items[i].eql(prev_atom.ref())) { |
| | 280 | already_have_free_list_node = true; |
| | 281 | } |
| 271 | } | 282 | } |
| 272 | i += 1; | 283 | i += 1; |
| 273 | } | 284 | } |
| 274 | } | 285 | } |
| 275 | | 286 | |
| 276 | if (zo.atom(last_atom_index.*)) |last_atom| { | 287 | if (elf_file.atom(last_atom_ref.*)) |last_atom| { |
| 277 | if (last_atom.atom_index == self.atom_index) { | 288 | if (last_atom.ref().eql(self.ref())) { |
| 278 | if (zo.atom(self.prev_index)) |_| { | 289 | if (self.prevAtom(elf_file)) |prev_atom| { |
| 279 | // TODO shrink the section size here | 290 | // TODO shrink the section size here |
| 280 | last_atom_index.* = self.prev_index; | 291 | last_atom_ref.* = prev_atom.ref(); |
| 281 | } else { | 292 | } else { |
| 282 | last_atom_index.* = 0; | 293 | last_atom_ref.* = .{}; |
| 283 | } | 294 | } |
| 284 | } | 295 | } |
| 285 | } | 296 | } |
| 286 | | 297 | |
| 287 | if (zo.atom(self.prev_index)) |prev| { | 298 | if (self.prevAtom(elf_file)) |prev_atom| { |
| 288 | prev.next_index = self.next_index; | 299 | prev_atom.next_atom_ref = self.next_atom_ref; |
| 289 | if (!already_have_free_list_node and prev.*.freeListEligible(elf_file)) { | 300 | if (!already_have_free_list_node and prev_atom.*.freeListEligible(elf_file)) { |
| 290 | // The free list is heuristics, it doesn't have to be perfect, so we can | 301 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 291 | // ignore the OOM here. | 302 | // ignore the OOM here. |
| 292 | free_list.append(gpa, prev.atom_index) catch {}; | 303 | free_list.append(gpa, prev_atom.ref()) catch {}; |
| 293 | } | 304 | } |
| 294 | } else { | 305 | } else { |
| 295 | self.prev_index = 0; | 306 | self.prev_atom_ref = .{}; |
| 296 | } | 307 | } |
| 297 | | 308 | |
| 298 | if (zo.atom(self.next_index)) |next| { | 309 | if (self.nextAtom(elf_file)) |next_atom| { |
| 299 | next.prev_index = self.prev_index; | 310 | next_atom.prev_atom_ref = self.prev_atom_ref; |
| 300 | } else { | 311 | } else { |
| 301 | self.next_index = 0; | 312 | self.next_atom_ref = .{}; |
| 302 | } | 313 | } |
| 303 | | 314 | |
| 304 | // TODO create relocs free list | 315 | switch (self.file(elf_file).?) { |
| 305 | self.freeRelocs(zo); | 316 | .zig_object => |zo| { |
| 306 | // TODO figure out how to free input section mappind in ZigModule | 317 | // TODO create relocs free list |
| 307 | // const zig_object = elf_file.zigObjectPtr().? | 318 | self.freeRelocs(zo); |
| 308 | // assert(zig_object.atoms.swapRemove(self.atom_index)); | 319 | // TODO figure out how to free input section mappind in ZigModule |
| | 320 | // const zig_object = elf_file.zigObjectPtr().? |
| | 321 | // assert(zig_object.atoms.swapRemove(self.atom_index)); |
| | 322 | }, |
| | 323 | else => {}, |
| | 324 | } |
| 309 | self.* = .{}; | 325 | self.* = .{}; |
| 310 | } | 326 | } |
| 311 | | 327 | |