diff --git a/lld/MachO/Arch/ARM64.cpp b/lld/MachO/Arch/ARM64.cpp --- a/lld/MachO/Arch/ARM64.cpp +++ b/lld/MachO/Arch/ARM64.cpp @@ -28,8 +28,8 @@ struct ARM64 : TargetInfo { ARM64(); - uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, - const relocation_info) const override; + int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, + const relocation_info) const override; void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, uint64_t pc) const override; @@ -77,8 +77,8 @@ return relocAttrsArray[type]; } -uint64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, - const relocation_info rel) const { +int64_t ARM64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, + const relocation_info rel) const { if (rel.r_type != ARM64_RELOC_UNSIGNED && rel.r_type != ARM64_RELOC_SUBTRACTOR) { // All other reloc types should use the ADDEND relocation to store their @@ -91,7 +91,7 @@ const uint8_t *loc = buf + sec.offset + rel.r_address; switch (rel.r_length) { case 2: - return read32le(loc); + return static_cast(read32le(loc)); case 3: return read64le(loc); default: @@ -158,21 +158,28 @@ value += r.addend; switch (r.type) { case ARM64_RELOC_BRANCH26: + checkInt(r, value - pc, 28); value = encodeBranch26(base, value - pc); break; case ARM64_RELOC_SUBTRACTOR: case ARM64_RELOC_UNSIGNED: + if (r.length == 2) + checkInt(r, value, 32); break; case ARM64_RELOC_POINTER_TO_GOT: if (r.pcrel) value -= pc; + checkInt(r, value, 32); break; case ARM64_RELOC_PAGE21: case ARM64_RELOC_GOT_LOAD_PAGE21: - case ARM64_RELOC_TLVP_LOAD_PAGE21: + case ARM64_RELOC_TLVP_LOAD_PAGE21: { assert(r.pcrel); - value = encodePage21(base, pageBits(value) - pageBits(pc)); + int64_t pageDiff = pageBits(value) - pageBits(pc); + checkInt(r, pageDiff, 35); + value = encodePage21(base, pageDiff); break; + } case ARM64_RELOC_PAGEOFF12: case ARM64_RELOC_GOT_LOAD_PAGEOFF12: case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: diff --git a/lld/MachO/Arch/X86_64.cpp b/lld/MachO/Arch/X86_64.cpp --- a/lld/MachO/Arch/X86_64.cpp +++ b/lld/MachO/Arch/X86_64.cpp @@ -25,8 +25,8 @@ struct X86_64 : TargetInfo { X86_64(); - uint64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, - const relocation_info) const override; + int64_t getEmbeddedAddend(MemoryBufferRef, const section_64 &, + const relocation_info) const override; void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, uint64_t relocVA) const override; @@ -77,14 +77,14 @@ } } -uint64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, - relocation_info rel) const { +int64_t X86_64::getEmbeddedAddend(MemoryBufferRef mb, const section_64 &sec, + relocation_info rel) const { auto *buf = reinterpret_cast(mb.getBufferStart()); const uint8_t *loc = buf + sec.offset + rel.r_address; switch (rel.r_length) { case 2: - return read32le(loc) + pcrelOffset(rel.r_type); + return static_cast(read32le(loc)) + pcrelOffset(rel.r_type); case 3: return read64le(loc) + pcrelOffset(rel.r_type); default: @@ -102,6 +102,10 @@ switch (r.length) { case 2: + if (r.type == X86_64_RELOC_UNSIGNED) + checkUInt(r, value, 32); + else + checkInt(r, value, 32); write32le(loc, value); break; case 3: diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -264,7 +264,7 @@ // and insert them. Storing addends in the instruction stream is // possible, but inconvenient and more costly at link time. - uint64_t pairedAddend = 0; + int64_t pairedAddend = 0; relocation_info relInfo = relInfos[i]; if (target->hasAttr(relInfo.r_type, RelocAttrBits::ADDEND)) { pairedAddend = SignExtend64<24>(relInfo.r_symbolnum); @@ -276,9 +276,9 @@ if (relInfo.r_address & R_SCATTERED) fatal("TODO: Scattered relocations not supported"); - uint64_t embeddedAddend = target->getEmbeddedAddend(mb, sec, relInfo); + int64_t embeddedAddend = target->getEmbeddedAddend(mb, sec, relInfo); assert(!(embeddedAddend && pairedAddend)); - uint64_t totalAddend = pairedAddend + embeddedAddend; + int64_t totalAddend = pairedAddend + embeddedAddend; Reloc r; r.type = relInfo.r_type; r.pcrel = relInfo.r_pcrel; diff --git a/lld/MachO/Relocations.h b/lld/MachO/Relocations.h --- a/lld/MachO/Relocations.h +++ b/lld/MachO/Relocations.h @@ -59,13 +59,31 @@ uint32_t offset = 0; // Adding this offset to the address of the referent symbol or subsection // gives the destination that this relocation refers to. - uint64_t addend = 0; + int64_t addend = 0; llvm::PointerUnion referent = nullptr; }; bool validateSymbolRelocation(const Symbol *, const InputSection *, const Reloc &); +/* + * v: The value the relocation is attempting to encode + * bits: The number of bits actually available to encode this relocation + */ +void reportRangeError(const Reloc &, const llvm::Twine &v, uint8_t bits, + int64_t min, uint64_t max); + +inline void checkInt(const Reloc &r, int64_t v, int bits) { + if (v != llvm::SignExtend64(v, bits)) + reportRangeError(r, llvm::Twine(v), bits, llvm::minIntN(bits), + llvm::maxIntN(bits)); +} + +inline void checkUInt(const Reloc &r, uint64_t v, int bits) { + if ((v >> bits) != 0) + reportRangeError(r, llvm::Twine(v), bits, 0, llvm::maxUIntN(bits)); +} + extern const RelocAttrs invalidRelocAttrs; } // namespace macho diff --git a/lld/MachO/Relocations.cpp b/lld/MachO/Relocations.cpp --- a/lld/MachO/Relocations.cpp +++ b/lld/MachO/Relocations.cpp @@ -39,4 +39,15 @@ return valid; } +void macho::reportRangeError(const Reloc &r, const Twine &v, uint8_t bits, + int64_t min, uint64_t max) { + std::string hint; + if (auto *sym = r.referent.dyn_cast()) + hint = "; references " + toString(*sym); + // TODO: get location of reloc using something like LLD-ELF's getErrorPlace() + error("relocation " + target->getRelocAttrs(r.type).name + + " is out of range: " + v + " is not in [" + Twine(min) + ", " + + Twine(max) + "]" + hint); +} + const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0}; diff --git a/lld/MachO/Target.h b/lld/MachO/Target.h --- a/lld/MachO/Target.h +++ b/lld/MachO/Target.h @@ -39,7 +39,7 @@ virtual ~TargetInfo() = default; // Validate the relocation structure and get its addend. - virtual uint64_t + virtual int64_t getEmbeddedAddend(llvm::MemoryBufferRef, const llvm::MachO::section_64 &, const llvm::MachO::relocation_info) const = 0; virtual void relocateOne(uint8_t *loc, const Reloc &, uint64_t va, diff --git a/lld/test/MachO/invalid/range-check.s b/lld/test/MachO/invalid/range-check.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/invalid/range-check.s @@ -0,0 +1,19 @@ +# REQUIRES: x86 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o +# RUN: not %lld -lSystem -o %t %t.o 2>&1 | FileCheck %s +# CHECK: error: relocation UNSIGNED is out of range: 8589938688 is not in [0, 4294967295]; references _foo +# CHECK: error: relocation GOT_LOAD is out of range: 4294970473 is not in [-2147483648, 2147483647]; references _foo + +.globl _main, _foo + +_main: + movq _foo@GOTPCREL(%rip), %rax + ret + +.int _foo +.zerofill __TEXT,bss,_zero,0xffffffff + +.data +_foo: + .space 0