| 1 | pub const Fde = struct { |
| 2 | /// Includes 4byte size cell. |
| 3 | offset: usize, |
| 4 | size: usize, |
| 5 | cie_index: u32, |
| 6 | rel_index: u32 = 0, |
| 7 | rel_num: u32 = 0, |
| 8 | input_section_index: u32 = 0, |
| 9 | file_index: u32 = 0, |
| 10 | alive: bool = true, |
| 11 | /// Includes 4byte size cell. |
| 12 | out_offset: u64 = 0, |
| 13 | |
| 14 | pub fn address(fde: Fde, elf_file: *Elf) u64 { |
| 15 | const base: u64 = if (elf_file.section_indexes.eh_frame) |shndx| |
| 16 | elf_file.sections.items(.shdr)[shndx].sh_addr |
| 17 | else |
| 18 | 0; |
| 19 | return base + fde.out_offset; |
| 20 | } |
| 21 | |
| 22 | pub fn data(fde: Fde, object: *Object) []u8 { |
| 23 | return object.eh_frame_data.items[fde.offset..][0..fde.calcSize()]; |
| 24 | } |
| 25 | |
| 26 | pub fn cie(fde: Fde, object: *Object) Cie { |
| 27 | return object.cies.items[fde.cie_index]; |
| 28 | } |
| 29 | |
| 30 | pub fn ciePointer(fde: Fde, object: *Object) u32 { |
| 31 | const fde_data = fde.data(object); |
| 32 | return std.mem.readInt(u32, fde_data[4..8], .little); |
| 33 | } |
| 34 | |
| 35 | pub fn calcSize(fde: Fde) usize { |
| 36 | return fde.size + 4; |
| 37 | } |
| 38 | |
| 39 | pub fn atom(fde: Fde, object: *Object) *Atom { |
| 40 | const rel = fde.relocs(object)[0]; |
| 41 | const sym = object.symtab.items[rel.r_sym()]; |
| 42 | const atom_index = object.atoms_indexes.items[sym.st_shndx]; |
| 43 | return object.atom(atom_index).?; |
| 44 | } |
| 45 | |
| 46 | pub fn relocs(fde: Fde, object: *Object) []const elf.Elf64_Rela { |
| 47 | return object.relocs.items[fde.rel_index..][0..fde.rel_num]; |
| 48 | } |
| 49 | |
| 50 | pub fn fmt(fde: Fde, elf_file: *Elf) std.fmt.Alt(Format, Format.default) { |
| 51 | return .{ .data = .{ |
| 52 | .fde = fde, |
| 53 | .elf_file = elf_file, |
| 54 | } }; |
| 55 | } |
| 56 | |
| 57 | const Format = struct { |
| 58 | fde: Fde, |
| 59 | elf_file: *Elf, |
| 60 | |
| 61 | fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 62 | const fde = f.fde; |
| 63 | const elf_file = f.elf_file; |
| 64 | const base_addr = fde.address(elf_file); |
| 65 | const object = elf_file.file(fde.file_index).?.object; |
| 66 | const atom_name = fde.atom(object).name(elf_file); |
| 67 | try writer.print("@{x} : size({x}) : cie({d}) : {s}", .{ |
| 68 | base_addr + fde.out_offset, |
| 69 | fde.calcSize(), |
| 70 | fde.cie_index, |
| 71 | atom_name, |
| 72 | }); |
| 73 | if (!fde.alive) try writer.writeAll(" : [*]"); |
| 74 | } |
| 75 | }; |
| 76 | }; |
| 77 | |
| 78 | pub const Cie = struct { |
| 79 | /// Includes 4byte size cell. |
| 80 | offset: usize, |
| 81 | size: usize, |
| 82 | rel_index: u32 = 0, |
| 83 | rel_num: u32 = 0, |
| 84 | input_section_index: u32 = 0, |
| 85 | file_index: u32 = 0, |
| 86 | /// Includes 4byte size cell. |
| 87 | out_offset: u64 = 0, |
| 88 | alive: bool = false, |
| 89 | |
| 90 | pub fn address(cie: Cie, elf_file: *Elf) u64 { |
| 91 | const base: u64 = if (elf_file.section_indexes.eh_frame) |shndx| |
| 92 | elf_file.sections.items(.shdr)[shndx].sh_addr |
| 93 | else |
| 94 | 0; |
| 95 | return base + cie.out_offset; |
| 96 | } |
| 97 | |
| 98 | pub fn data(cie: Cie, elf_file: *Elf) []u8 { |
| 99 | const object = elf_file.file(cie.file_index).?.object; |
| 100 | return object.eh_frame_data.items[cie.offset..][0..cie.calcSize()]; |
| 101 | } |
| 102 | |
| 103 | pub fn calcSize(cie: Cie) usize { |
| 104 | return cie.size + 4; |
| 105 | } |
| 106 | |
| 107 | pub fn relocs(cie: Cie, elf_file: *Elf) []align(1) const elf.Elf64_Rela { |
| 108 | const object = elf_file.file(cie.file_index).?.object; |
| 109 | return object.relocs.items[cie.rel_index..][0..cie.rel_num]; |
| 110 | } |
| 111 | |
| 112 | pub fn eql(cie: Cie, other: Cie, elf_file: *Elf) bool { |
| 113 | if (!std.mem.eql(u8, cie.data(elf_file), other.data(elf_file))) return false; |
| 114 | |
| 115 | const cie_relocs = cie.relocs(elf_file); |
| 116 | const other_relocs = other.relocs(elf_file); |
| 117 | if (cie_relocs.len != other_relocs.len) return false; |
| 118 | |
| 119 | for (cie_relocs, other_relocs) |cie_rel, other_rel| { |
| 120 | if (cie_rel.r_offset - cie.offset != other_rel.r_offset - other.offset) return false; |
| 121 | if (cie_rel.r_type() != other_rel.r_type()) return false; |
| 122 | if (cie_rel.r_addend != other_rel.r_addend) return false; |
| 123 | |
| 124 | const cie_object = elf_file.file(cie.file_index).?.object; |
| 125 | const cie_ref = cie_object.resolveSymbol(cie_rel.r_sym(), elf_file); |
| 126 | const other_object = elf_file.file(other.file_index).?.object; |
| 127 | const other_ref = other_object.resolveSymbol(other_rel.r_sym(), elf_file); |
| 128 | if (!cie_ref.eql(other_ref)) return false; |
| 129 | } |
| 130 | return true; |
| 131 | } |
| 132 | |
| 133 | pub fn fmt(cie: Cie, elf_file: *Elf) std.fmt.Alt(Format, Format.default) { |
| 134 | return .{ .data = .{ |
| 135 | .cie = cie, |
| 136 | .elf_file = elf_file, |
| 137 | } }; |
| 138 | } |
| 139 | |
| 140 | const Format = struct { |
| 141 | cie: Cie, |
| 142 | elf_file: *Elf, |
| 143 | |
| 144 | fn default(f: Format, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 145 | const cie = f.cie; |
| 146 | const elf_file = f.elf_file; |
| 147 | const base_addr = cie.address(elf_file); |
| 148 | try writer.print("@{x} : size({x})", .{ |
| 149 | base_addr + cie.out_offset, |
| 150 | cie.calcSize(), |
| 151 | }); |
| 152 | if (!cie.alive) try writer.writeAll(" : [*]"); |
| 153 | } |
| 154 | }; |
| 155 | }; |
| 156 | |
| 157 | pub const Iterator = struct { |
| 158 | data: []const u8, |
| 159 | pos: usize = 0, |
| 160 | |
| 161 | pub const Record = struct { |
| 162 | tag: enum { fde, cie }, |
| 163 | offset: usize, |
| 164 | size: usize, |
| 165 | }; |
| 166 | |
| 167 | pub fn next(it: *Iterator) !?Record { |
| 168 | if (it.pos >= it.data.len) return null; |
| 169 | |
| 170 | var reader: std.Io.Reader = .fixed(it.data[it.pos..]); |
| 171 | |
| 172 | const size = try reader.takeInt(u32, .little); |
| 173 | if (size == 0) return null; |
| 174 | if (size == 0xFFFFFFFF) @panic("TODO"); |
| 175 | |
| 176 | const id = try reader.takeInt(u32, .little); |
| 177 | const record: Record = .{ |
| 178 | .tag = if (id == 0) .cie else .fde, |
| 179 | .offset = it.pos, |
| 180 | .size = size, |
| 181 | }; |
| 182 | it.pos += size + 4; |
| 183 | |
| 184 | return record; |
| 185 | } |
| 186 | }; |
| 187 | |
| 188 | pub fn calcEhFrameSize(elf_file: *Elf) !usize { |
| 189 | const comp = elf_file.base.comp; |
| 190 | const gpa = comp.gpa; |
| 191 | |
| 192 | var offset: usize = if (elf_file.zigObjectPtr()) |zo| blk: { |
| 193 | const sym = zo.symbol(zo.eh_frame_index orelse break :blk 0); |
| 194 | break :blk math.cast(usize, sym.atom(elf_file).?.size) orelse return error.Overflow; |
| 195 | } else 0; |
| 196 | |
| 197 | var cies = std.array_list.Managed(Cie).init(gpa); |
| 198 | defer cies.deinit(); |
| 199 | |
| 200 | for (elf_file.objects.items) |index| { |
| 201 | const object = elf_file.file(index).?.object; |
| 202 | |
| 203 | outer: for (object.cies.items) |*cie| { |
| 204 | for (cies.items) |other| { |
| 205 | if (other.eql(cie.*, elf_file)) { |
| 206 | // We already have a CIE record that has the exact same contents, so instead of |
| 207 | // duplicating them, we mark this one dead and set its output offset to be |
| 208 | // equal to that of the alive record. This way, we won't have to rewrite |
| 209 | // Fde.cie_index field when committing the records to file. |
| 210 | cie.out_offset = other.out_offset; |
| 211 | continue :outer; |
| 212 | } |
| 213 | } |
| 214 | cie.alive = true; |
| 215 | cie.out_offset = offset; |
| 216 | offset += cie.calcSize(); |
| 217 | try cies.append(cie.*); |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | for (elf_file.objects.items) |index| { |
| 222 | const object = elf_file.file(index).?.object; |
| 223 | for (object.fdes.items) |*fde| { |
| 224 | if (!fde.alive) continue; |
| 225 | fde.out_offset = offset; |
| 226 | offset += fde.calcSize(); |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | if (!elf_file.base.isRelocatable()) { |
| 231 | offset += 4; // NULL terminator |
| 232 | } |
| 233 | |
| 234 | return offset; |
| 235 | } |
| 236 | |
| 237 | fn haveEhFrameHdrSearchTable(elf_file: *Elf) bool { |
| 238 | // Seach table generation is not implemented for the ZigObject. Also, it would be wasteful to |
| 239 | // re-do this work on every single incremental update. |
| 240 | return elf_file.zigObjectPtr() == null; |
| 241 | } |
| 242 | |
| 243 | pub fn calcEhFrameHdrSize(elf_file: *Elf) usize { |
| 244 | if (!haveEhFrameHdrSearchTable(elf_file)) return 8; |
| 245 | var count: usize = 0; |
| 246 | for (elf_file.objects.items) |index| { |
| 247 | for (elf_file.file(index).?.object.fdes.items) |fde| { |
| 248 | if (!fde.alive) continue; |
| 249 | count += 1; |
| 250 | } |
| 251 | } |
| 252 | return 12 + count * 8; |
| 253 | } |
| 254 | |
| 255 | pub fn calcEhFrameRelocs(elf_file: *Elf) usize { |
| 256 | var count: usize = 0; |
| 257 | if (elf_file.zigObjectPtr()) |zo| zo: { |
| 258 | const sym_index = zo.eh_frame_index orelse break :zo; |
| 259 | const sym = zo.symbol(sym_index); |
| 260 | const atom_ptr = zo.atom(sym.ref.index).?; |
| 261 | if (!atom_ptr.alive) break :zo; |
| 262 | count += atom_ptr.relocs(elf_file).len; |
| 263 | } |
| 264 | for (elf_file.objects.items) |index| { |
| 265 | const object = elf_file.file(index).?.object; |
| 266 | for (object.cies.items) |cie| { |
| 267 | if (!cie.alive) continue; |
| 268 | count += cie.relocs(elf_file).len; |
| 269 | } |
| 270 | for (object.fdes.items) |fde| { |
| 271 | if (!fde.alive) continue; |
| 272 | count += fde.relocs(object).len; |
| 273 | } |
| 274 | } |
| 275 | return count; |
| 276 | } |
| 277 | |
| 278 | fn resolveReloc(rec: anytype, sym: *const Symbol, rel: elf.Elf64_Rela, elf_file: *Elf, contents: []u8) !void { |
| 279 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 280 | const offset = std.math.cast(usize, rel.r_offset - rec.offset) orelse return error.Overflow; |
| 281 | const P = math.cast(i64, rec.address(elf_file) + offset) orelse return error.Overflow; |
| 282 | const S = math.cast(i64, sym.address(.{}, elf_file)) orelse return error.Overflow; |
| 283 | const A = rel.r_addend; |
| 284 | |
| 285 | relocs_log.debug(" {f}: {x}: [{x} => {x}] ({s})", .{ |
| 286 | relocation.fmtRelocType(rel.r_type(), cpu_arch), |
| 287 | offset, |
| 288 | P, |
| 289 | S + A, |
| 290 | sym.name(elf_file), |
| 291 | }); |
| 292 | |
| 293 | switch (cpu_arch) { |
| 294 | .x86_64 => try x86_64.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]), |
| 295 | .aarch64, .aarch64_be => try aarch64.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]), |
| 296 | .riscv64, .riscv64be => try riscv.resolveReloc(rec, elf_file, rel, P, S + A, contents[offset..]), |
| 297 | else => return error.UnsupportedCpuArch, |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | pub fn writeEhFrame(elf_file: *Elf, writer: anytype) !void { |
| 302 | relocs_log.debug("{x}: .eh_frame", .{ |
| 303 | elf_file.sections.items(.shdr)[elf_file.section_indexes.eh_frame.?].sh_addr, |
| 304 | }); |
| 305 | |
| 306 | var has_reloc_errors = false; |
| 307 | |
| 308 | for (elf_file.objects.items) |index| { |
| 309 | const object = elf_file.file(index).?.object; |
| 310 | |
| 311 | for (object.cies.items) |cie| { |
| 312 | if (!cie.alive) continue; |
| 313 | |
| 314 | const contents = cie.data(elf_file); |
| 315 | |
| 316 | for (cie.relocs(elf_file)) |rel| { |
| 317 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 318 | const sym = elf_file.symbol(ref).?; |
| 319 | resolveReloc(cie, sym, rel, elf_file, contents) catch |err| switch (err) { |
| 320 | error.RelocFailure => has_reloc_errors = true, |
| 321 | else => |e| return e, |
| 322 | }; |
| 323 | } |
| 324 | |
| 325 | try writer.writeAll(contents); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | for (elf_file.objects.items) |index| { |
| 330 | const object = elf_file.file(index).?.object; |
| 331 | |
| 332 | for (object.fdes.items) |fde| { |
| 333 | if (!fde.alive) continue; |
| 334 | |
| 335 | const contents = fde.data(object); |
| 336 | |
| 337 | std.mem.writeInt( |
| 338 | i32, |
| 339 | contents[4..8], |
| 340 | @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(object).out_offset))), |
| 341 | .little, |
| 342 | ); |
| 343 | |
| 344 | for (fde.relocs(object)) |rel| { |
| 345 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 346 | const sym = elf_file.symbol(ref).?; |
| 347 | resolveReloc(fde, sym, rel, elf_file, contents) catch |err| switch (err) { |
| 348 | error.RelocFailure => has_reloc_errors = true, |
| 349 | else => |e| return e, |
| 350 | }; |
| 351 | } |
| 352 | |
| 353 | try writer.writeAll(contents); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | try writer.writeInt(u32, 0, .little); |
| 358 | |
| 359 | if (has_reloc_errors) return error.RelocFailure; |
| 360 | } |
| 361 | |
| 362 | pub fn writeEhFrameRelocatable(elf_file: *Elf, writer: anytype) !void { |
| 363 | for (elf_file.objects.items) |index| { |
| 364 | const object = elf_file.file(index).?.object; |
| 365 | |
| 366 | for (object.cies.items) |cie| { |
| 367 | if (!cie.alive) continue; |
| 368 | try writer.writeAll(cie.data(elf_file)); |
| 369 | } |
| 370 | } |
| 371 | |
| 372 | for (elf_file.objects.items) |index| { |
| 373 | const object = elf_file.file(index).?.object; |
| 374 | |
| 375 | for (object.fdes.items) |fde| { |
| 376 | if (!fde.alive) continue; |
| 377 | |
| 378 | const contents = fde.data(object); |
| 379 | |
| 380 | std.mem.writeInt( |
| 381 | i32, |
| 382 | contents[4..8], |
| 383 | @truncate(@as(i64, @intCast(fde.out_offset + 4)) - @as(i64, @intCast(fde.cie(object).out_offset))), |
| 384 | .little, |
| 385 | ); |
| 386 | |
| 387 | try writer.writeAll(contents); |
| 388 | } |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | fn emitReloc(elf_file: *Elf, r_offset: u64, sym: *const Symbol, rel: elf.Elf64_Rela) elf.Elf64_Rela { |
| 393 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 394 | const r_type = rel.r_type(); |
| 395 | var r_addend = rel.r_addend; |
| 396 | var r_sym: u32 = 0; |
| 397 | switch (sym.type(elf_file)) { |
| 398 | elf.STT_SECTION => { |
| 399 | r_addend += @intCast(sym.address(.{}, elf_file)); |
| 400 | r_sym = sym.outputShndx(elf_file).?; |
| 401 | }, |
| 402 | else => { |
| 403 | r_sym = sym.outputSymtabIndex(elf_file) orelse 0; |
| 404 | }, |
| 405 | } |
| 406 | |
| 407 | relocs_log.debug(" {f}: [{x} => {d}({s})] + {x}", .{ |
| 408 | relocation.fmtRelocType(r_type, cpu_arch), |
| 409 | r_offset, |
| 410 | r_sym, |
| 411 | sym.name(elf_file), |
| 412 | r_addend, |
| 413 | }); |
| 414 | |
| 415 | return .{ |
| 416 | .r_offset = r_offset, |
| 417 | .r_addend = r_addend, |
| 418 | .r_info = (@as(u64, @intCast(r_sym)) << 32) | r_type, |
| 419 | }; |
| 420 | } |
| 421 | |
| 422 | pub fn writeEhFrameRelocs(elf_file: *Elf, relocs: *std.array_list.Managed(elf.Elf64_Rela)) !void { |
| 423 | relocs_log.debug("{x}: .eh_frame", .{ |
| 424 | elf_file.sections.items(.shdr)[elf_file.section_indexes.eh_frame.?].sh_addr, |
| 425 | }); |
| 426 | |
| 427 | if (elf_file.zigObjectPtr()) |zo| zo: { |
| 428 | const sym_index = zo.eh_frame_index orelse break :zo; |
| 429 | const sym = zo.symbol(sym_index); |
| 430 | const atom_ptr = zo.atom(sym.ref.index).?; |
| 431 | if (!atom_ptr.alive) break :zo; |
| 432 | for (atom_ptr.relocs(elf_file)) |rel| { |
| 433 | const ref = zo.resolveSymbol(rel.r_sym(), elf_file); |
| 434 | const target = elf_file.symbol(ref).?; |
| 435 | try relocs.append(emitReloc(elf_file, rel.r_offset, target, rel)); |
| 436 | } |
| 437 | } |
| 438 | |
| 439 | for (elf_file.objects.items) |index| { |
| 440 | const object = elf_file.file(index).?.object; |
| 441 | |
| 442 | for (object.cies.items) |cie| { |
| 443 | if (!cie.alive) continue; |
| 444 | for (cie.relocs(elf_file)) |rel| { |
| 445 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 446 | const sym = elf_file.symbol(ref).?; |
| 447 | const r_offset = cie.address(elf_file) + rel.r_offset - cie.offset; |
| 448 | try relocs.append(emitReloc(elf_file, r_offset, sym, rel)); |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | for (object.fdes.items) |fde| { |
| 453 | if (!fde.alive) continue; |
| 454 | for (fde.relocs(object)) |rel| { |
| 455 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 456 | const sym = elf_file.symbol(ref).?; |
| 457 | const r_offset = fde.address(elf_file) + rel.r_offset - fde.offset; |
| 458 | try relocs.append(emitReloc(elf_file, r_offset, sym, rel)); |
| 459 | } |
| 460 | } |
| 461 | } |
| 462 | } |
| 463 | |
| 464 | pub fn writeEhFrameHdr(elf_file: *Elf, writer: anytype) !void { |
| 465 | const endian = elf_file.getTarget().cpu.arch.endian(); |
| 466 | const have_table = haveEhFrameHdrSearchTable(elf_file); |
| 467 | |
| 468 | try writer.writeByte(1); // version |
| 469 | try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .pcrel }))); // eh_frame_ptr_enc |
| 470 | if (have_table) { |
| 471 | try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .udata4, .rel = .abs }))); // fde_count_enc |
| 472 | try writer.writeByte(@bitCast(@as(DW_EH_PE, .{ .type = .sdata4, .rel = .datarel }))); // table_enc |
| 473 | } else { |
| 474 | try writer.writeByte(@bitCast(DW_EH_PE.omit)); // fde_count_enc |
| 475 | try writer.writeByte(@bitCast(DW_EH_PE.omit)); // table_enc |
| 476 | } |
| 477 | |
| 478 | const shdrs = elf_file.sections.items(.shdr); |
| 479 | const eh_frame_shdr = shdrs[elf_file.section_indexes.eh_frame.?]; |
| 480 | const eh_frame_hdr_shdr = shdrs[elf_file.section_indexes.eh_frame_hdr.?]; |
| 481 | // eh_frame_ptr |
| 482 | try writer.writeInt( |
| 483 | u32, |
| 484 | @as(u32, @bitCast(@as( |
| 485 | i32, |
| 486 | @truncate(@as(i64, @intCast(eh_frame_shdr.sh_addr)) - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)) - 4), |
| 487 | ))), |
| 488 | .little, |
| 489 | ); |
| 490 | |
| 491 | if (!have_table) return; |
| 492 | |
| 493 | const gpa = elf_file.base.comp.gpa; |
| 494 | |
| 495 | // This must be an `extern struct` because we will write the bytes directly to the file. |
| 496 | const Entry = extern struct { |
| 497 | first_pc_rel: i32, |
| 498 | fde_addr_rel: i32, |
| 499 | fn lessThan(_: void, lhs: @This(), rhs: @This()) bool { |
| 500 | return lhs.first_pc_rel < rhs.first_pc_rel; |
| 501 | } |
| 502 | }; |
| 503 | // The number of entries was already computed by `calcEhFrameHdrSize`. |
| 504 | const num_fdes: u32 = @intCast(@divExact(eh_frame_hdr_shdr.sh_size - 12, 8)); |
| 505 | try writer.writeInt(u32, num_fdes, endian); |
| 506 | |
| 507 | var entries: std.ArrayList(Entry) = try .initCapacity(gpa, num_fdes); |
| 508 | defer entries.deinit(gpa); |
| 509 | for (elf_file.objects.items) |file_index| { |
| 510 | const object = elf_file.file(file_index).?.object; |
| 511 | for (object.fdes.items) |fde| { |
| 512 | if (!fde.alive) continue; |
| 513 | const relocs = fde.relocs(object); |
| 514 | // Should `relocs.len == 0` be an error? Things are completely broken anyhow in that case... |
| 515 | const rel = relocs[0]; |
| 516 | const ref = object.resolveSymbol(rel.r_sym(), elf_file); |
| 517 | const sym = elf_file.symbol(ref).?; |
| 518 | const fde_addr_abs: i64 = @intCast(fde.address(elf_file)); |
| 519 | const fde_addr_rel: i64 = fde_addr_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)); |
| 520 | const first_pc_abs: i64 = @as(i64, @intCast(sym.address(.{}, elf_file))) + rel.r_addend; |
| 521 | const first_pc_rel: i64 = first_pc_abs - @as(i64, @intCast(eh_frame_hdr_shdr.sh_addr)); |
| 522 | entries.appendAssumeCapacity(.{ |
| 523 | .first_pc_rel = @truncate(first_pc_rel), |
| 524 | .fde_addr_rel = @truncate(fde_addr_rel), |
| 525 | }); |
| 526 | } |
| 527 | } |
| 528 | assert(entries.items.len == num_fdes); |
| 529 | std.mem.sort(Entry, entries.items, {}, Entry.lessThan); |
| 530 | if (endian != builtin.cpu.arch.endian()) { |
| 531 | std.mem.byteSwapAllElements(Entry, entries.items); |
| 532 | } |
| 533 | try writer.writeAll(@ptrCast(entries.items)); |
| 534 | } |
| 535 | |
| 536 | const x86_64 = struct { |
| 537 | fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { |
| 538 | const r_type: elf.R_X86_64 = @fromBackingInt(@intCast(rel.r_type())); |
| 539 | switch (r_type) { |
| 540 | .NONE => {}, |
| 541 | .@"32" => std.mem.writeInt(i32, data[0..4], @as(i32, @truncate(target)), .little), |
| 542 | .@"64" => std.mem.writeInt(i64, data[0..8], target, .little), |
| 543 | .PC32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little), |
| 544 | .PC64 => std.mem.writeInt(i64, data[0..8], target - source, .little), |
| 545 | else => try reportInvalidReloc(rec, elf_file, rel), |
| 546 | } |
| 547 | } |
| 548 | }; |
| 549 | |
| 550 | const aarch64 = struct { |
| 551 | fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { |
| 552 | const r_type: elf.R_AARCH64 = @fromBackingInt(@intCast(rel.r_type())); |
| 553 | switch (r_type) { |
| 554 | .NONE => {}, |
| 555 | .ABS64 => std.mem.writeInt(i64, data[0..8], target, .little), |
| 556 | .PREL32 => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little), |
| 557 | .PREL64 => std.mem.writeInt(i64, data[0..8], target - source, .little), |
| 558 | else => try reportInvalidReloc(rec, elf_file, rel), |
| 559 | } |
| 560 | } |
| 561 | }; |
| 562 | |
| 563 | const riscv = struct { |
| 564 | fn resolveReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela, source: i64, target: i64, data: []u8) !void { |
| 565 | const r_type: elf.R_RISCV = @fromBackingInt(@intCast(rel.r_type())); |
| 566 | switch (r_type) { |
| 567 | .NONE => {}, |
| 568 | .@"32_PCREL" => std.mem.writeInt(i32, data[0..4], @as(i32, @intCast(target - source)), .little), |
| 569 | else => try reportInvalidReloc(rec, elf_file, rel), |
| 570 | } |
| 571 | } |
| 572 | }; |
| 573 | |
| 574 | fn reportInvalidReloc(rec: anytype, elf_file: *Elf, rel: elf.Elf64_Rela) !void { |
| 575 | const diags = &elf_file.base.comp.link_diags; |
| 576 | var err = try diags.addErrorWithNotes(1); |
| 577 | try err.addMsg("invalid relocation type {f} at offset 0x{x}", .{ |
| 578 | relocation.fmtRelocType(rel.r_type(), elf_file.getTarget().cpu.arch), |
| 579 | rel.r_offset, |
| 580 | }); |
| 581 | err.addNote("in {f}:.eh_frame", .{elf_file.file(rec.file_index).?.fmtPath()}); |
| 582 | return error.RelocFailure; |
| 583 | } |
| 584 | |
| 585 | const std = @import("std"); |
| 586 | const assert = std.debug.assert; |
| 587 | const elf = std.elf; |
| 588 | const math = std.math; |
| 589 | const relocs_log = std.log.scoped(.link_relocs); |
| 590 | const relocation = @import("relocation.zig"); |
| 591 | |
| 592 | const Allocator = std.mem.Allocator; |
| 593 | const Atom = @import("Atom.zig"); |
| 594 | const DW_EH_PE = std.dwarf.EH.PE; |
| 595 | const Elf = @import("../Elf.zig"); |
| 596 | const Object = @import("Object.zig"); |
| 597 | const Symbol = @import("Symbol.zig"); |
| 598 | |
| 599 | const builtin = @import("builtin"); |