| ... | ... | @@ -316,6 +316,7 @@ pub fn scanRelocsRequiresCode(self: Atom, elf_file: *Elf) bool { |
| 316 | 316 | } |
| 317 | 317 | |
| 318 | 318 | pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype) !void { |
| 319 | const is_static = elf_file.isStatic(); |
| 319 | 320 | const is_dyn_lib = elf_file.isDynLib(); |
| 320 | 321 | const file_ptr = self.file(elf_file).?; |
| 321 | 322 | const rels = self.relocs(elf_file); |
| ... | ... | @@ -348,14 +349,23 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 348 | 349 | // Report an undefined symbol. |
| 349 | 350 | try self.reportUndefined(elf_file, symbol, symbol_index, rel, undefs); |
| 350 | 351 | |
| 352 | if (symbol.isIFunc(elf_file)) { |
| 353 | symbol.flags.needs_got = true; |
| 354 | symbol.flags.needs_plt = true; |
| 355 | } |
| 356 | |
| 351 | 357 | // While traversing relocations, mark symbols that require special handling such as |
| 352 | 358 | // pointer indirection via GOT, or a stub trampoline via PLT. |
| 353 | 359 | switch (rel.r_type()) { |
| 354 | | elf.R_X86_64_64 => {}, |
| 360 | elf.R_X86_64_64 => { |
| 361 | try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 362 | }, |
| 355 | 363 | |
| 356 | 364 | elf.R_X86_64_32, |
| 357 | 365 | elf.R_X86_64_32S, |
| 358 | | => {}, |
| 366 | => { |
| 367 | try self.scanReloc(symbol, rel, dynAbsRelocAction(symbol, elf_file), elf_file); |
| 368 | }, |
| 359 | 369 | |
| 360 | 370 | elf.R_X86_64_GOT32, |
| 361 | 371 | elf.R_X86_64_GOT64, |
| ... | ... | @@ -377,23 +387,14 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 377 | 387 | } |
| 378 | 388 | }, |
| 379 | 389 | |
| 380 | | elf.R_X86_64_PC32 => {}, |
| 381 | | |
| 382 | | elf.R_X86_64_TPOFF32, |
| 383 | | elf.R_X86_64_TPOFF64, |
| 384 | | => { |
| 385 | | if (is_dyn_lib) { |
| 386 | | // TODO |
| 387 | | // self.picError(symbol, rel, elf_file); |
| 388 | | } |
| 390 | elf.R_X86_64_PC32 => { |
| 391 | try self.scanReloc(symbol, rel, pcRelocAction(symbol, elf_file), elf_file); |
| 389 | 392 | }, |
| 390 | 393 | |
| 391 | 394 | elf.R_X86_64_TLSGD => { |
| 392 | 395 | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 393 | 396 | |
| 394 | | if (elf_file.isStatic() or |
| 395 | | (!symbol.flags.import and !is_dyn_lib)) |
| 396 | | { |
| 397 | if (is_static or (!symbol.flags.import and !is_dyn_lib)) { |
| 397 | 398 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 398 | 399 | // We skip the next relocation. |
| 399 | 400 | i += 1; |
| ... | ... | @@ -405,9 +406,21 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 405 | 406 | } |
| 406 | 407 | }, |
| 407 | 408 | |
| 409 | elf.R_X86_64_TLSLD => { |
| 410 | // TODO verify followed by appropriate relocation such as PLT32 __tls_get_addr |
| 411 | |
| 412 | if (is_static or !is_dyn_lib) { |
| 413 | // Relax if building with -static flag as __tls_get_addr() will not be present in libc.a |
| 414 | // We skip the next relocation. |
| 415 | i += 1; |
| 416 | } else { |
| 417 | elf_file.got.flags.needs_tlsld = true; |
| 418 | } |
| 419 | }, |
| 420 | |
| 408 | 421 | elf.R_X86_64_GOTTPOFF => { |
| 409 | 422 | const should_relax = blk: { |
| 410 | | // if (!elf_file.options.relax or is_shared or symbol.flags.import) break :blk false; |
| 423 | if (is_dyn_lib or symbol.flags.import) break :blk false; |
| 411 | 424 | if (!x86_64.canRelaxGotTpOff(code.?[r_offset - 3 ..])) break :blk false; |
| 412 | 425 | break :blk true; |
| 413 | 426 | }; |
| ... | ... | @@ -416,21 +429,245 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 416 | 429 | } |
| 417 | 430 | }, |
| 418 | 431 | |
| 419 | | else => { |
| 420 | | var err = try elf_file.addErrorWithNotes(1); |
| 421 | | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{ |
| 422 | | fmtRelocType(rel.r_type()), |
| 423 | | }); |
| 424 | | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 425 | | self.file(elf_file).?.fmtPath(), |
| 426 | | self.name(elf_file), |
| 427 | | r_offset, |
| 428 | | }); |
| 432 | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 433 | const should_relax = is_static or (!is_dyn_lib and !symbol.flags.import); |
| 434 | if (!should_relax) { |
| 435 | symbol.flags.needs_tlsdesc = true; |
| 436 | } |
| 429 | 437 | }, |
| 438 | |
| 439 | elf.R_X86_64_TPOFF32, |
| 440 | elf.R_X86_64_TPOFF64, |
| 441 | => { |
| 442 | if (is_dyn_lib) try self.reportPicError(symbol, rel, elf_file); |
| 443 | }, |
| 444 | |
| 445 | elf.R_X86_64_GOTOFF64, |
| 446 | elf.R_X86_64_DTPOFF32, |
| 447 | elf.R_X86_64_DTPOFF64, |
| 448 | elf.R_X86_64_SIZE32, |
| 449 | elf.R_X86_64_SIZE64, |
| 450 | elf.R_X86_64_TLSDESC_CALL, |
| 451 | => {}, |
| 452 | |
| 453 | else => try self.reportUnhandledRelocError(rel, elf_file), |
| 430 | 454 | } |
| 431 | 455 | } |
| 432 | 456 | } |
| 433 | 457 | |
| 458 | fn scanReloc( |
| 459 | self: Atom, |
| 460 | symbol: *Symbol, |
| 461 | rel: elf.Elf64_Rela, |
| 462 | action: RelocAction, |
| 463 | elf_file: *Elf, |
| 464 | ) error{OutOfMemory}!void { |
| 465 | const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; |
| 466 | const object = self.file(elf_file).?.object; |
| 467 | |
| 468 | switch (action) { |
| 469 | .none => {}, |
| 470 | |
| 471 | .@"error" => if (symbol.isAbs(elf_file)) |
| 472 | try self.reportNoPicError(symbol, rel, elf_file) |
| 473 | else |
| 474 | try self.reportPicError(symbol, rel, elf_file), |
| 475 | |
| 476 | .copyrel => { |
| 477 | if (elf_file.base.options.z_nocopyreloc) { |
| 478 | if (symbol.isAbs(elf_file)) |
| 479 | try self.reportNoPicError(symbol, rel, elf_file) |
| 480 | else |
| 481 | try self.reportPicError(symbol, rel, elf_file); |
| 482 | } else { |
| 483 | symbol.flags.needs_copy_rel = true; |
| 484 | } |
| 485 | }, |
| 486 | |
| 487 | .dyn_copyrel => { |
| 488 | if (is_writeable or elf_file.base.options.z_nocopyreloc) { |
| 489 | if (!is_writeable) { |
| 490 | if (elf_file.base.options.z_notext) { |
| 491 | elf_file.has_text_reloc = true; |
| 492 | } else { |
| 493 | try self.reportTextRelocError(symbol, rel, elf_file); |
| 494 | } |
| 495 | } |
| 496 | object.num_dynrelocs += 1; |
| 497 | } else { |
| 498 | symbol.flags.needs_copy_rel = true; |
| 499 | } |
| 500 | }, |
| 501 | |
| 502 | .plt => { |
| 503 | symbol.flags.needs_plt = true; |
| 504 | }, |
| 505 | |
| 506 | .cplt => { |
| 507 | symbol.flags.needs_plt = true; |
| 508 | symbol.flags.is_canonical = true; |
| 509 | }, |
| 510 | |
| 511 | .dyn_cplt => { |
| 512 | if (is_writeable) { |
| 513 | object.num_dynrelocs += 1; |
| 514 | } else { |
| 515 | symbol.flags.needs_plt = true; |
| 516 | symbol.flags.is_canonical = true; |
| 517 | } |
| 518 | }, |
| 519 | |
| 520 | .dynrel, .baserel, .ifunc => { |
| 521 | if (!is_writeable) { |
| 522 | if (elf_file.base.options.z_notext) { |
| 523 | elf_file.has_text_reloc = true; |
| 524 | } else { |
| 525 | try self.reportTextRelocError(symbol, rel, elf_file); |
| 526 | } |
| 527 | } |
| 528 | object.num_dynrelocs += 1; |
| 529 | |
| 530 | if (action == .ifunc) elf_file.num_ifunc_dynrelocs += 1; |
| 531 | }, |
| 532 | } |
| 533 | } |
| 534 | |
| 535 | const RelocAction = enum { |
| 536 | none, |
| 537 | @"error", |
| 538 | copyrel, |
| 539 | dyn_copyrel, |
| 540 | plt, |
| 541 | dyn_cplt, |
| 542 | cplt, |
| 543 | dynrel, |
| 544 | baserel, |
| 545 | ifunc, |
| 546 | }; |
| 547 | |
| 548 | fn pcRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { |
| 549 | // zig fmt: off |
| 550 | const table: [3][4]RelocAction = .{ |
| 551 | // Abs Local Import data Import func |
| 552 | .{ .@"error", .none, .@"error", .plt }, // Shared object |
| 553 | .{ .@"error", .none, .copyrel, .plt }, // PIE |
| 554 | .{ .none, .none, .copyrel, .cplt }, // Non-PIE |
| 555 | }; |
| 556 | // zig fmt: on |
| 557 | const output = outputType(elf_file); |
| 558 | const data = dataType(symbol, elf_file); |
| 559 | return table[output][data]; |
| 560 | } |
| 561 | |
| 562 | fn absRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { |
| 563 | // zig fmt: off |
| 564 | const table: [3][4]RelocAction = .{ |
| 565 | // Abs Local Import data Import func |
| 566 | .{ .none, .@"error", .@"error", .@"error" }, // Shared object |
| 567 | .{ .none, .@"error", .@"error", .@"error" }, // PIE |
| 568 | .{ .none, .none, .copyrel, .cplt }, // Non-PIE |
| 569 | }; |
| 570 | // zig fmt: on |
| 571 | const output = outputType(elf_file); |
| 572 | const data = dataType(symbol, elf_file); |
| 573 | return table[output][data]; |
| 574 | } |
| 575 | |
| 576 | fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction { |
| 577 | if (symbol.isIFunc(elf_file)) return .ifunc; |
| 578 | // zig fmt: off |
| 579 | const table: [3][4]RelocAction = .{ |
| 580 | // Abs Local Import data Import func |
| 581 | .{ .none, .baserel, .dynrel, .dynrel }, // Shared object |
| 582 | .{ .none, .baserel, .dynrel, .dynrel }, // PIE |
| 583 | .{ .none, .none, .dyn_copyrel, .dyn_cplt }, // Non-PIE |
| 584 | }; |
| 585 | // zig fmt: on |
| 586 | const output = outputType(elf_file); |
| 587 | const data = dataType(symbol, elf_file); |
| 588 | return table[output][data]; |
| 589 | } |
| 590 | |
| 591 | fn outputType(elf_file: *Elf) u2 { |
| 592 | return switch (elf_file.base.options.output_mode) { |
| 593 | .Obj => unreachable, |
| 594 | .Lib => 0, |
| 595 | .Exe => if (elf_file.base.options.pie) 1 else 2, |
| 596 | }; |
| 597 | } |
| 598 | |
| 599 | fn dataType(symbol: *const Symbol, elf_file: *Elf) u2 { |
| 600 | if (symbol.isAbs(elf_file)) return 0; |
| 601 | if (!symbol.flags.import) return 1; |
| 602 | if (symbol.type(elf_file) != elf.STT_FUNC) return 2; |
| 603 | return 3; |
| 604 | } |
| 605 | |
| 606 | fn reportUnhandledRelocError(self: Atom, rel: elf.Elf64_Rela, elf_file: *Elf) error{OutOfMemory}!void { |
| 607 | var err = try elf_file.addErrorWithNotes(1); |
| 608 | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {} at offset 0x{x}", .{ |
| 609 | fmtRelocType(rel.r_type()), |
| 610 | rel.r_offset, |
| 611 | }); |
| 612 | try err.addNote(elf_file, "in {}:{s}", .{ |
| 613 | self.file(elf_file).?.fmtPath(), |
| 614 | self.name(elf_file), |
| 615 | }); |
| 616 | } |
| 617 | |
| 618 | fn reportTextRelocError( |
| 619 | self: Atom, |
| 620 | symbol: *const Symbol, |
| 621 | rel: elf.Elf64_Rela, |
| 622 | elf_file: *Elf, |
| 623 | ) error{OutOfMemory}!void { |
| 624 | var err = try elf_file.addErrorWithNotes(1); |
| 625 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 626 | rel.r_offset, |
| 627 | symbol.name(elf_file), |
| 628 | }); |
| 629 | try err.addNote(elf_file, "in {}:{s}", .{ |
| 630 | self.file(elf_file).?.fmtPath(), |
| 631 | self.name(elf_file), |
| 632 | }); |
| 633 | } |
| 634 | |
| 635 | fn reportPicError( |
| 636 | self: Atom, |
| 637 | symbol: *const Symbol, |
| 638 | rel: elf.Elf64_Rela, |
| 639 | elf_file: *Elf, |
| 640 | ) error{OutOfMemory}!void { |
| 641 | var err = try elf_file.addErrorWithNotes(2); |
| 642 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 643 | rel.r_offset, |
| 644 | symbol.name(elf_file), |
| 645 | }); |
| 646 | try err.addNote(elf_file, "in {}:{s}", .{ |
| 647 | self.file(elf_file).?.fmtPath(), |
| 648 | self.name(elf_file), |
| 649 | }); |
| 650 | try err.addNote(elf_file, "recompile with -fPIC", .{}); |
| 651 | } |
| 652 | |
| 653 | fn reportNoPicError( |
| 654 | self: Atom, |
| 655 | symbol: *const Symbol, |
| 656 | rel: elf.Elf64_Rela, |
| 657 | elf_file: *Elf, |
| 658 | ) error{OutOfMemory}!void { |
| 659 | var err = try elf_file.addErrorWithNotes(2); |
| 660 | try err.addMsg(elf_file, "relocation at offset 0x{x} against symbol '{s}' cannot be used", .{ |
| 661 | rel.r_offset, |
| 662 | symbol.name(elf_file), |
| 663 | }); |
| 664 | try err.addNote(elf_file, "in {}:{s}", .{ |
| 665 | self.file(elf_file).?.fmtPath(), |
| 666 | self.name(elf_file), |
| 667 | }); |
| 668 | try err.addNote(elf_file, "recompile with -fno-PIC", .{}); |
| 669 | } |
| 670 | |
| 434 | 671 | // This function will report any undefined non-weak symbols that are not imports. |
| 435 | 672 | fn reportUndefined( |
| 436 | 673 | self: Atom, |
| ... | ... | @@ -504,7 +741,6 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 504 | 741 | const TP = @as(i64, @intCast(elf_file.tpAddress())); |
| 505 | 742 | // Address of the dynamic thread pointer. |
| 506 | 743 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 507 | | _ = DTP; |
| 508 | 744 | |
| 509 | 745 | relocs_log.debug(" {s}: {x}: [{x} => {x}] G({x}) ({s})", .{ |
| 510 | 746 | fmtRelocType(r_type), |
| ... | ... | @@ -520,18 +756,20 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 520 | 756 | switch (rel.r_type()) { |
| 521 | 757 | elf.R_X86_64_NONE => unreachable, |
| 522 | 758 | |
| 523 | | elf.R_X86_64_64 => try cwriter.writeIntLittle(i64, S + A), |
| 524 | | |
| 525 | | elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))), |
| 526 | | elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))), |
| 759 | elf.R_X86_64_64 => { |
| 760 | try self.resolveDynAbsReloc( |
| 761 | target, |
| 762 | rel, |
| 763 | dynAbsRelocAction(target, elf_file), |
| 764 | elf_file, |
| 765 | cwriter, |
| 766 | ); |
| 767 | }, |
| 527 | 768 | |
| 528 | 769 | elf.R_X86_64_PLT32, |
| 529 | 770 | elf.R_X86_64_PC32, |
| 530 | 771 | => try cwriter.writeIntLittle(i32, @as(i32, @intCast(S + A - P))), |
| 531 | 772 | |
| 532 | | elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))), |
| 533 | | elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))), |
| 534 | | |
| 535 | 773 | elf.R_X86_64_GOTPCREL => try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))), |
| 536 | 774 | elf.R_X86_64_GOTPC32 => try cwriter.writeIntLittle(i32, @as(i32, @intCast(GOT + A - P))), |
| 537 | 775 | elf.R_X86_64_GOTPC64 => try cwriter.writeIntLittle(i64, GOT + A - P), |
| ... | ... | @@ -554,18 +792,25 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 554 | 792 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(G + GOT + A - P))); |
| 555 | 793 | }, |
| 556 | 794 | |
| 795 | elf.R_X86_64_32 => try cwriter.writeIntLittle(u32, @as(u32, @truncate(@as(u64, @intCast(S + A))))), |
| 796 | elf.R_X86_64_32S => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A))), |
| 797 | |
| 798 | elf.R_X86_64_GOT32 => try cwriter.writeIntLittle(u32, @as(u32, @intCast(G + GOT + A))), |
| 799 | elf.R_X86_64_GOT64 => try cwriter.writeIntLittle(u64, @as(u64, @intCast(G + GOT + A))), |
| 800 | |
| 557 | 801 | elf.R_X86_64_TPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - TP))), |
| 558 | 802 | elf.R_X86_64_TPOFF64 => try cwriter.writeIntLittle(i64, S + A - TP), |
| 559 | 803 | |
| 804 | elf.R_X86_64_DTPOFF32 => try cwriter.writeIntLittle(i32, @as(i32, @truncate(S + A - DTP))), |
| 805 | elf.R_X86_64_DTPOFF64 => try cwriter.writeIntLittle(i64, S + A - DTP), |
| 806 | |
| 560 | 807 | elf.R_X86_64_TLSGD => { |
| 561 | 808 | if (target.flags.has_tlsgd) { |
| 562 | | // TODO |
| 563 | | // const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file))); |
| 564 | | // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 809 | const S_ = @as(i64, @intCast(target.tlsGdAddress(elf_file))); |
| 810 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 565 | 811 | } else if (target.flags.has_gottp) { |
| 566 | | // TODO |
| 567 | | // const S_ = @as(i64, @intCast(target.getGotTpAddress(elf_file))); |
| 568 | | // try relaxTlsGdToIe(relocs[i .. i + 2], @intCast(S_ - P), elf_file, &stream); |
| 812 | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 813 | try x86_64.relaxTlsGdToIe(self, rels[i .. i + 2], @intCast(S_ - P), elf_file, &stream); |
| 569 | 814 | i += 1; |
| 570 | 815 | } else { |
| 571 | 816 | try x86_64.relaxTlsGdToLe( |
| ... | ... | @@ -579,11 +824,42 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 579 | 824 | } |
| 580 | 825 | }, |
| 581 | 826 | |
| 827 | elf.R_X86_64_TLSLD => { |
| 828 | if (elf_file.got.tlsld_index) |entry_index| { |
| 829 | const tlsld_entry = elf_file.got.entries.items[entry_index]; |
| 830 | const S_ = @as(i64, @intCast(tlsld_entry.address(elf_file))); |
| 831 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 832 | } else { |
| 833 | try x86_64.relaxTlsLdToLe( |
| 834 | self, |
| 835 | rels[i .. i + 2], |
| 836 | @as(i32, @intCast(TP - @as(i64, @intCast(elf_file.tlsAddress())))), |
| 837 | elf_file, |
| 838 | &stream, |
| 839 | ); |
| 840 | i += 1; |
| 841 | } |
| 842 | }, |
| 843 | |
| 844 | elf.R_X86_64_GOTPC32_TLSDESC => { |
| 845 | if (target.flags.has_tlsdesc) { |
| 846 | const S_ = @as(i64, @intCast(target.tlsDescAddress(elf_file))); |
| 847 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 848 | } else { |
| 849 | try x86_64.relaxGotPcTlsDesc(code[rel.r_offset - 3 ..]); |
| 850 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP))); |
| 851 | } |
| 852 | }, |
| 853 | |
| 854 | elf.R_X86_64_TLSDESC_CALL => if (!target.flags.has_tlsdesc) { |
| 855 | // call -> nop |
| 856 | try cwriter.writeAll(&.{ 0x66, 0x90 }); |
| 857 | }, |
| 858 | |
| 582 | 859 | elf.R_X86_64_GOTTPOFF => { |
| 583 | 860 | if (target.flags.has_gottp) { |
| 584 | | // TODO |
| 585 | | // const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 586 | | // try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 861 | const S_ = @as(i64, @intCast(target.gotTpAddress(elf_file))); |
| 862 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S_ + A - P))); |
| 587 | 863 | } else { |
| 588 | 864 | x86_64.relaxGotTpOff(code[r_offset - 3 ..]) catch unreachable; |
| 589 | 865 | try cwriter.writeIntLittle(i32, @as(i32, @intCast(S - TP))); |
| ... | ... | @@ -595,6 +871,98 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) !void { |
| 595 | 871 | } |
| 596 | 872 | } |
| 597 | 873 | |
| 874 | fn resolveDynAbsReloc( |
| 875 | self: Atom, |
| 876 | target: *const Symbol, |
| 877 | rel: elf.Elf64_Rela, |
| 878 | action: RelocAction, |
| 879 | elf_file: *Elf, |
| 880 | writer: anytype, |
| 881 | ) !void { |
| 882 | const P = self.value + rel.r_offset; |
| 883 | const A = rel.r_addend; |
| 884 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 885 | const is_writeable = self.inputShdr(elf_file).sh_flags & elf.SHF_WRITE != 0; |
| 886 | const object = self.file(elf_file).?.object; |
| 887 | |
| 888 | try elf_file.rela_dyn.ensureUnusedCapacity(elf_file.base.allocator, object.num_dynrelocs); |
| 889 | |
| 890 | switch (action) { |
| 891 | .@"error", |
| 892 | .plt, |
| 893 | => unreachable, |
| 894 | |
| 895 | .copyrel, |
| 896 | .cplt, |
| 897 | .none, |
| 898 | => try writer.writeIntLittle(i32, @as(i32, @truncate(S + A))), |
| 899 | |
| 900 | .dyn_copyrel => { |
| 901 | if (is_writeable or elf_file.base.options.z_nocopyreloc) { |
| 902 | elf_file.addRelaDynAssumeCapacity(.{ |
| 903 | .offset = P, |
| 904 | .sym = target.extra(elf_file).?.dynamic, |
| 905 | .type = elf.R_X86_64_64, |
| 906 | .addend = A, |
| 907 | }); |
| 908 | try applyDynamicReloc(A, elf_file, writer); |
| 909 | } else { |
| 910 | try writer.writeIntLittle(i32, @as(i32, @truncate(S + A))); |
| 911 | } |
| 912 | }, |
| 913 | |
| 914 | .dyn_cplt => { |
| 915 | if (is_writeable) { |
| 916 | elf_file.addRelaDynAssumeCapacity(.{ |
| 917 | .offset = P, |
| 918 | .sym = target.extra(elf_file).?.dynamic, |
| 919 | .type = elf.R_X86_64_64, |
| 920 | .addend = A, |
| 921 | }); |
| 922 | try applyDynamicReloc(A, elf_file, writer); |
| 923 | } else { |
| 924 | try writer.writeIntLittle(i32, @as(i32, @truncate(S + A))); |
| 925 | } |
| 926 | }, |
| 927 | |
| 928 | .dynrel => { |
| 929 | elf_file.addRelaDynAssumeCapacity(.{ |
| 930 | .offset = P, |
| 931 | .sym = target.extra(elf_file).?.dynamic, |
| 932 | .type = elf.R_X86_64_64, |
| 933 | .addend = A, |
| 934 | }); |
| 935 | try applyDynamicReloc(A, elf_file, writer); |
| 936 | }, |
| 937 | |
| 938 | .baserel => { |
| 939 | elf_file.addRelaDynAssumeCapacity(.{ |
| 940 | .offset = P, |
| 941 | .type = elf.R_X86_64_RELATIVE, |
| 942 | .addend = S + A, |
| 943 | }); |
| 944 | try applyDynamicReloc(S + A, elf_file, writer); |
| 945 | }, |
| 946 | |
| 947 | .ifunc => { |
| 948 | const S_ = @as(i64, @intCast(target.address(.{ .plt = false }, elf_file))); |
| 949 | elf_file.addRelaDynAssumeCapacity(.{ |
| 950 | .offset = P, |
| 951 | .type = elf.R_X86_64_IRELATIVE, |
| 952 | .addend = S_ + A, |
| 953 | }); |
| 954 | try applyDynamicReloc(S_ + A, elf_file, writer); |
| 955 | }, |
| 956 | } |
| 957 | } |
| 958 | |
| 959 | fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { |
| 960 | _ = elf_file; |
| 961 | // if (elf_file.options.apply_dynamic_relocs) { |
| 962 | try writer.writeIntLittle(i64, value); |
| 963 | // } |
| 964 | } |
| 965 | |
| 598 | 966 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 599 | 967 | relocs_log.debug("0x{x}: {s}", .{ self.value, self.name(elf_file) }); |
| 600 | 968 | |
| ... | ... | @@ -682,17 +1050,7 @@ pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: any |
| 682 | 1050 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 683 | 1051 | try cwriter.writeIntLittle(i64, @as(i64, @intCast(size + A))); |
| 684 | 1052 | }, |
| 685 | | else => { |
| 686 | | var err = try elf_file.addErrorWithNotes(1); |
| 687 | | try err.addMsg(elf_file, "fatal linker error: unhandled relocation type {}", .{ |
| 688 | | fmtRelocType(r_type), |
| 689 | | }); |
| 690 | | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 691 | | self.file(elf_file).?.fmtPath(), |
| 692 | | self.name(elf_file), |
| 693 | | r_offset, |
| 694 | | }); |
| 695 | | }, |
| 1053 | else => try self.reportUnhandledRelocError(rel, elf_file), |
| 696 | 1054 | } |
| 697 | 1055 | } |
| 698 | 1056 | } |
| ... | ... | @@ -854,6 +1212,95 @@ const x86_64 = struct { |
| 854 | 1212 | } |
| 855 | 1213 | } |
| 856 | 1214 | |
| 1215 | pub fn relaxTlsGdToIe( |
| 1216 | self: Atom, |
| 1217 | rels: []align(1) const elf.Elf64_Rela, |
| 1218 | value: i32, |
| 1219 | elf_file: *Elf, |
| 1220 | stream: anytype, |
| 1221 | ) !void { |
| 1222 | assert(rels.len == 2); |
| 1223 | const writer = stream.writer(); |
| 1224 | switch (rels[1].r_type()) { |
| 1225 | elf.R_X86_64_PC32, |
| 1226 | elf.R_X86_64_PLT32, |
| 1227 | => { |
| 1228 | var insts = [_]u8{ |
| 1229 | 0x64, 0x48, 0x8b, 0x04, 0x25, 0, 0, 0, 0, // movq %fs:0,%rax |
| 1230 | 0x48, 0x03, 0x05, 0, 0, 0, 0, // add foo@gottpoff(%rip), %rax |
| 1231 | }; |
| 1232 | std.mem.writeIntLittle(i32, insts[12..][0..4], value - 12); |
| 1233 | try stream.seekBy(-4); |
| 1234 | try writer.writeAll(&insts); |
| 1235 | }, |
| 1236 | |
| 1237 | else => { |
| 1238 | var err = try elf_file.addErrorWithNotes(1); |
| 1239 | try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{ |
| 1240 | fmtRelocType(rels[0].r_type()), |
| 1241 | fmtRelocType(rels[1].r_type()), |
| 1242 | }); |
| 1243 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 1244 | self.file(elf_file).?.fmtPath(), |
| 1245 | self.name(elf_file), |
| 1246 | rels[0].r_offset, |
| 1247 | }); |
| 1248 | }, |
| 1249 | } |
| 1250 | } |
| 1251 | |
| 1252 | pub fn relaxTlsLdToLe( |
| 1253 | self: Atom, |
| 1254 | rels: []align(1) const elf.Elf64_Rela, |
| 1255 | value: i32, |
| 1256 | elf_file: *Elf, |
| 1257 | stream: anytype, |
| 1258 | ) !void { |
| 1259 | assert(rels.len == 2); |
| 1260 | const writer = stream.writer(); |
| 1261 | switch (rels[1].r_type()) { |
| 1262 | elf.R_X86_64_PC32, |
| 1263 | elf.R_X86_64_PLT32, |
| 1264 | => { |
| 1265 | var insts = [_]u8{ |
| 1266 | 0x31, 0xc0, // xor %eax, %eax |
| 1267 | 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax |
| 1268 | 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax |
| 1269 | }; |
| 1270 | std.mem.writeIntLittle(i32, insts[8..][0..4], value); |
| 1271 | try stream.seekBy(-3); |
| 1272 | try writer.writeAll(&insts); |
| 1273 | }, |
| 1274 | |
| 1275 | elf.R_X86_64_GOTPCREL, |
| 1276 | elf.R_X86_64_GOTPCRELX, |
| 1277 | => { |
| 1278 | var insts = [_]u8{ |
| 1279 | 0x31, 0xc0, // xor %eax, %eax |
| 1280 | 0x64, 0x48, 0x8b, 0, // mov %fs:(%rax), %rax |
| 1281 | 0x48, 0x2d, 0, 0, 0, 0, // sub $tls_size, %rax |
| 1282 | 0x90, // nop |
| 1283 | }; |
| 1284 | std.mem.writeIntLittle(i32, insts[8..][0..4], value); |
| 1285 | try stream.seekBy(-3); |
| 1286 | try writer.writeAll(&insts); |
| 1287 | }, |
| 1288 | |
| 1289 | else => { |
| 1290 | var err = try elf_file.addErrorWithNotes(1); |
| 1291 | try err.addMsg(elf_file, "fatal linker error: rewrite {} when followed by {}", .{ |
| 1292 | fmtRelocType(rels[0].r_type()), |
| 1293 | fmtRelocType(rels[1].r_type()), |
| 1294 | }); |
| 1295 | try err.addNote(elf_file, "in {}:{s} at offset 0x{x}", .{ |
| 1296 | self.file(elf_file).?.fmtPath(), |
| 1297 | self.name(elf_file), |
| 1298 | rels[0].r_offset, |
| 1299 | }); |
| 1300 | }, |
| 1301 | } |
| 1302 | } |
| 1303 | |
| 857 | 1304 | pub fn canRelaxGotTpOff(code: []const u8) bool { |
| 858 | 1305 | const old_inst = disassemble(code) orelse return false; |
| 859 | 1306 | switch (old_inst.encoding.mnemonic) { |
| ... | ... | @@ -885,6 +1332,22 @@ const x86_64 = struct { |
| 885 | 1332 | } |
| 886 | 1333 | } |
| 887 | 1334 | |
| 1335 | pub fn relaxGotPcTlsDesc(code: []u8) !void { |
| 1336 | const old_inst = disassemble(code) orelse return error.RelaxFail; |
| 1337 | switch (old_inst.encoding.mnemonic) { |
| 1338 | .lea => { |
| 1339 | const inst = try Instruction.new(old_inst.prefix, .mov, &.{ |
| 1340 | old_inst.ops[0], |
| 1341 | // TODO: hack to force imm32s in the assembler |
| 1342 | .{ .imm = Immediate.s(-129) }, |
| 1343 | }); |
| 1344 | relocs_log.debug(" relaxing {} => {}", .{ old_inst.encoding, inst.encoding }); |
| 1345 | encode(&.{inst}, code) catch return error.RelaxFail; |
| 1346 | }, |
| 1347 | else => return error.RelaxFail, |
| 1348 | } |
| 1349 | } |
| 1350 | |
| 888 | 1351 | pub fn relaxTlsGdToLe( |
| 889 | 1352 | self: Atom, |
| 890 | 1353 | rels: []align(1) const elf.Elf64_Rela, |