Index: ELF/SyntheticSections.h =================================================================== --- ELF/SyntheticSections.h +++ ELF/SyntheticSections.h @@ -79,8 +79,8 @@ size_t NumFdes = 0; struct FdeData { - uint32_t Pc; - uint32_t FdeVA; + uint64_t PcRel; + uint64_t FdeVARel; }; std::vector getFdeData() const; Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -502,16 +502,30 @@ uint8_t *Buf = getParent()->Loc + OutSecOff; std::vector Ret; + uint64_t VA = InX::EhFrameHdr->getVA(); for (CieRecord *Rec : CieRecords) { uint8_t Enc = getFdeEncoding(Rec->Cie); for (EhSectionPiece *Fde : Rec->Fdes) { uint64_t Pc = getFdePc(Buf, Fde->OutputOff, Enc); - if (Pc > UINT32_MAX) - fatal(toString(Fde->Sec) + ": PC address is too large: " + Twine(Pc)); - uint32_t FdeVA = getParent()->Addr + Fde->OutputOff; - Ret.push_back({(uint32_t)Pc, FdeVA}); + uint64_t FdeVA = getParent()->Addr + Fde->OutputOff; + if (Pc - VA != (uint64_t)signExtend(Pc - VA, 32)) + fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" + + Twine::utohexstr(Pc - VA)); + Ret.push_back({Pc - VA, FdeVA - VA}); } } + + // Sort the FDE list by their PC and uniqueify. Usually there is only + // one FDE for a PC (i.e. function), but if ICF merges two functions + // into one, there can be more than one FDEs pointing to the address. + std::stable_sort( + Ret.begin(), Ret.end(), + [](const FdeData &A, const FdeData &B) { return A.PcRel < B.PcRel; }); + auto Eq = [](const FdeData &A, const FdeData &B) { + return A.PcRel == B.PcRel; + }; + Ret.erase(std::unique(Ret.begin(), Ret.end(), Eq), Ret.end()); + return Ret; } @@ -2532,14 +2546,6 @@ std::vector Fdes = InX::EhFrame->getFdeData(); - // Sort the FDE list by their PC and uniqueify. Usually there is only - // one FDE for a PC (i.e. function), but if ICF merges two functions - // into one, there can be more than one FDEs pointing to the address. - auto Less = [](const FdeData &A, const FdeData &B) { return A.Pc < B.Pc; }; - std::stable_sort(Fdes.begin(), Fdes.end(), Less); - auto Eq = [](const FdeData &A, const FdeData &B) { return A.Pc == B.Pc; }; - Fdes.erase(std::unique(Fdes.begin(), Fdes.end(), Eq), Fdes.end()); - Buf[0] = 1; Buf[1] = DW_EH_PE_pcrel | DW_EH_PE_sdata4; Buf[2] = DW_EH_PE_udata4; @@ -2548,10 +2554,9 @@ write32(Buf + 8, Fdes.size()); Buf += 12; - uint64_t VA = this->getVA(); for (FdeData &Fde : Fdes) { - write32(Buf, Fde.Pc - VA); - write32(Buf + 4, Fde.FdeVA - VA); + write32(Buf, Fde.PcRel); + write32(Buf + 4, Fde.FdeVARel); Buf += 8; } } Index: test/ELF/Inputs/eh-frame-pcrel-overflow.s =================================================================== --- /dev/null +++ test/ELF/Inputs/eh-frame-pcrel-overflow.s @@ -0,0 +1,25 @@ +.text +.global foo +foo: + ret + +.section .eh_frame, "a" + .long 12 # Size + .long 0x00 # ID + .byte 0x01 # Version. + + .byte 0x52 # Augmentation string: 'R','\0' + .byte 0x00 + + .byte 0x01 + + .byte 0x01 # LEB128 + .byte 0x01 # LEB128 + + .byte 0x00 # DW_EH_PE_absptr + + .byte 0xFF + + .long 12 # Size + .long 0x14 # ID + .quad foo + 0x90000000 Index: test/ELF/eh-frame-pcaddr-overflow.s =================================================================== --- test/ELF/eh-frame-pcaddr-overflow.s +++ /dev/null @@ -1,32 +0,0 @@ -# REQUIRES: x86 - -# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o -# RUN: not ld.lld --eh-frame-hdr --section-start .text=0x1000000000000000 \ -# RUN: %t.o -o /dev/null 2>&1 | FileCheck %s -# CHECK: error: {{.*}}.o:(.eh_frame): PC address is too large: 2387527121043355528 - -.text -.global foo -foo: - nop - -.section .eh_frame, "a" - .long 12 # Size - .long 0x00 # ID - .byte 0x01 # Version. - - .byte 0x52 # Augmentation string: 'R','\0' - .byte 0x00 - - .byte 0x01 - - .byte 0x01 # LEB128 - .byte 0x01 # LEB128 - - .byte 0x00 # DW_EH_PE_absptr - - .byte 0xFF - - .long 12 # Size - .long 0x14 # ID - .quad foo + 0x1122334455667788 Index: test/ELF/eh-frame-pcrel-overflow.s =================================================================== --- /dev/null +++ test/ELF/eh-frame-pcrel-overflow.s @@ -0,0 +1,33 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %p/Inputs/eh-frame-pcrel-overflow.s -o %t1.o +# RUN: ld.lld --eh-frame-hdr -Ttext=0x90000000 %t.o -o /dev/null +# RUN: not ld.lld --eh-frame-hdr %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s +# CHECK: error: {{.*}}.o:(.eh_frame): PC offset is too large: 0x90000eac + +.text +.global _start +_start: + ret + +.section .eh_frame, "a" + .long 12 # Size + .long 0x00 # ID + .byte 0x01 # Version. + + .byte 0x52 # Augmentation string: 'R','\0' + .byte 0x00 + + .byte 0x01 + + .byte 0x01 # LEB128 + .byte 0x01 # LEB128 + + .byte 0x00 # DW_EH_PE_absptr + + .byte 0xFF + + .long 12 # Size + .long 0x14 # ID + .quad _start + 0x70000000