diff --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h --- a/lld/ELF/InputSection.h +++ b/lld/ELF/InputSection.h @@ -325,6 +325,15 @@ SmallVector pieces; SyntheticSection *getParent() const; + // Translate an offset in the input section to an offset in the parent + // MergeSyntheticSection. + uint64_t getParentOffset(uint64_t offset) const; + + // Returns the SectionPiece at a given input section offset. + EhSectionPiece *getSectionPiece(uint64_t offset); + const EhSectionPiece *getSectionPiece(uint64_t offset) const { + return const_cast(this)->getSectionPiece(offset); + } }; // This is a section that is added directly to an output section diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -160,11 +160,15 @@ case Regular: case Synthetic: return cast(this)->outSecOff + offset; - case EHFrame: + case EHFrame: { // The file crtbeginT.o has relocations pointing to the start of an empty // .eh_frame that is known to be the first in the link. It does that to // identify the start of the output .eh_frame. - return offset; + const EhInputSection *es = cast(this); + if (InputSection *isec = es->getParent()) + return isec->outSecOff + es->getParentOffset(offset); + return es->getParentOffset(offset); + } case Merge: const MergeInputSection *ms = cast(this); if (InputSection *isec = ms->getParent()) @@ -1334,6 +1338,29 @@ getObjMsg(d.data() - rawData.data())); } +EhSectionPiece *EhInputSection::getSectionPiece(uint64_t offset) { + if (this->data().size() <= offset) + fatal(toString(this) + ": offset is outside the section"); + + // If Offset is not at beginning of a section piece, it is not in the map. + // In that case we need to do a binary search of the original section piece + // vector. + auto it = partition_point( + pieces, [=](EhSectionPiece p) { return p.inputOff <= offset; }); + return &it[-1]; +} + +// Returns the offset in an output section for a given input offset. +// Because contents of an eh_frame section is not contiguous in input, +// it is not just an addition to a base output offset. +uint64_t EhInputSection::getParentOffset(uint64_t offset) const { + // If Offset is not at beginning of a section piece, it is not in the map. + // In that case we need to search from the original section piece vector. + const EhSectionPiece &piece = *getSectionPiece(offset); + uint64_t addend = offset - piece.inputOff; + return piece.outputOff + addend; +} + static size_t findNull(StringRef s, size_t entSize) { for (unsigned i = 0, n = s.size(); i != n; i += entSize) { const char *b = s.begin() + i; diff --git a/lld/test/ELF/mips-eh_frame-offset.s b/lld/test/ELF/mips-eh_frame-offset.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/mips-eh_frame-offset.s @@ -0,0 +1,18 @@ +# REQUIRES: mips +## Check that offsets for emitted relocations are correct when linking multiple +## .eh_frame sections + +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t-1.o +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t-2.o +# RUN: ld.lld --emit-relocs --entry=0 %t-1.o %t-2.o -o %t.exe +# RUN: llvm-readobj -r %t.exe | FileCheck %s --check-prefix=RELOCS + +# RELOCS: [[OFFSET:0x[0-9,A-F]+]] R_MIPS_64/R_MIPS_NONE/R_MIPS_NONE .text 0x0 +# RELOCS-NOT: [[OFFSET]] R_MIPS_64/R_MIPS_NONE/R_MIPS_NONE .text 0x10 + +.ent func +func: + .cfi_startproc + nop + .cfi_endproc +.end func