[ELF] Check eh_frame_hdr overflow with PC offsets instead of PC absolute addresses
Reviewers: grimar, ruiu, espindola Subscribers: emaste, arichardson, llvm-commits Differential Revision: https://reviews.llvm.org/D49607 llvm-svn: 337610
This commit is contained in:
parent
346dfbe2bc
commit
a66d77b22b
|
@ -502,16 +502,31 @@ std::vector<EhFrameSection::FdeData> EhFrameSection::getFdeData() const {
|
|||
uint8_t *Buf = getParent()->Loc + OutSecOff;
|
||||
std::vector<FdeData> 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 (!isInt<32>(Pc - VA))
|
||||
fatal(toString(Fde->Sec) + ": PC offset is too large: 0x" +
|
||||
Twine::utohexstr(Pc - VA));
|
||||
Ret.push_back({uint32_t(Pc - VA), uint32_t(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.
|
||||
auto Less = [](const FdeData &A, const FdeData &B) {
|
||||
return A.PcRel < B.PcRel;
|
||||
};
|
||||
std::stable_sort(Ret.begin(), Ret.end(), Less);
|
||||
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 +2547,6 @@ void EhFrameHeader::writeTo(uint8_t *Buf) {
|
|||
|
||||
std::vector<FdeData> 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 +2555,9 @@ void EhFrameHeader::writeTo(uint8_t *Buf) {
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,8 +79,8 @@ public:
|
|||
size_t NumFdes = 0;
|
||||
|
||||
struct FdeData {
|
||||
uint32_t Pc;
|
||||
uint32_t FdeVA;
|
||||
uint32_t PcRel;
|
||||
uint32_t FdeVARel;
|
||||
};
|
||||
|
||||
std::vector<FdeData> getFdeData() const;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue