xref: /freebsd/contrib/llvm-project/lld/MachO/Relocations.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1fe6060f1SDimitry Andric //===- Relocations.cpp ----------------------------------------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric 
9fe6060f1SDimitry Andric #include "Relocations.h"
1081ad6265SDimitry Andric #include "ConcatOutputSection.h"
11fe6060f1SDimitry Andric #include "Symbols.h"
12fe6060f1SDimitry Andric #include "SyntheticSections.h"
13fe6060f1SDimitry Andric #include "Target.h"
14fe6060f1SDimitry Andric 
15fe6060f1SDimitry Andric #include "lld/Common/ErrorHandler.h"
16fe6060f1SDimitry Andric 
17fe6060f1SDimitry Andric using namespace llvm;
18fe6060f1SDimitry Andric using namespace lld;
19fe6060f1SDimitry Andric using namespace lld::macho;
20fe6060f1SDimitry Andric 
21349cc55cSDimitry Andric static_assert(sizeof(void *) != 8 || sizeof(Reloc) == 24,
22349cc55cSDimitry Andric               "Try to minimize Reloc's size; we create many instances");
23349cc55cSDimitry Andric 
getReferentInputSection() const2406c3fb27SDimitry Andric InputSection *Reloc::getReferentInputSection() const {
2506c3fb27SDimitry Andric   if (const auto *sym = referent.dyn_cast<Symbol *>()) {
2606c3fb27SDimitry Andric     if (const auto *d = dyn_cast<Defined>(sym))
27*0fca6ea1SDimitry Andric       return d->isec();
2806c3fb27SDimitry Andric     return nullptr;
2906c3fb27SDimitry Andric   } else {
3006c3fb27SDimitry Andric     return referent.get<InputSection *>();
3106c3fb27SDimitry Andric   }
3206c3fb27SDimitry Andric }
3306c3fb27SDimitry Andric 
validateSymbolRelocation(const Symbol * sym,const InputSection * isec,const Reloc & r)34fe6060f1SDimitry Andric bool macho::validateSymbolRelocation(const Symbol *sym,
35fe6060f1SDimitry Andric                                      const InputSection *isec, const Reloc &r) {
36fe6060f1SDimitry Andric   const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
37fe6060f1SDimitry Andric   bool valid = true;
3881ad6265SDimitry Andric   auto message = [&](const Twine &diagnostic) {
39fe6060f1SDimitry Andric     valid = false;
4081ad6265SDimitry Andric     return (isec->getLocation(r.offset) + ": " + relocAttrs.name +
4181ad6265SDimitry Andric             " relocation " + diagnostic)
42fe6060f1SDimitry Andric         .str();
43fe6060f1SDimitry Andric   };
44fe6060f1SDimitry Andric 
45fe6060f1SDimitry Andric   if (relocAttrs.hasAttr(RelocAttrBits::TLV) != sym->isTlv())
4681ad6265SDimitry Andric     error(message(Twine("requires that symbol ") + sym->getName() + " " +
47fe6060f1SDimitry Andric                   (sym->isTlv() ? "not " : "") + "be thread-local"));
48fe6060f1SDimitry Andric 
49fe6060f1SDimitry Andric   return valid;
50fe6060f1SDimitry Andric }
51fe6060f1SDimitry Andric 
5281ad6265SDimitry Andric // Given an offset in the output buffer, figure out which ConcatInputSection (if
5381ad6265SDimitry Andric // any) maps to it. At the same time, update the offset such that it is relative
5481ad6265SDimitry Andric // to the InputSection rather than to the output buffer.
5581ad6265SDimitry Andric //
5681ad6265SDimitry Andric // Obtaining the InputSection allows us to have better error diagnostics.
5781ad6265SDimitry Andric // However, many of our relocation-handling methods do not take the InputSection
5881ad6265SDimitry Andric // as a parameter. Since we are already passing the buffer offsets to our Target
5981ad6265SDimitry Andric // methods, this function allows us to emit better errors without threading an
6081ad6265SDimitry Andric // additional InputSection argument through the call stack.
6181ad6265SDimitry Andric //
6281ad6265SDimitry Andric // This is implemented as a slow linear search through OutputSegments,
6381ad6265SDimitry Andric // OutputSections, and finally the InputSections themselves. However, this
6481ad6265SDimitry Andric // function should be called only on error paths, so some overhead is fine.
offsetToInputSection(uint64_t * off)65bdd1243dSDimitry Andric InputSection *macho::offsetToInputSection(uint64_t *off) {
6681ad6265SDimitry Andric   for (OutputSegment *seg : outputSegments) {
6781ad6265SDimitry Andric     if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize)
6881ad6265SDimitry Andric       continue;
6981ad6265SDimitry Andric 
7081ad6265SDimitry Andric     const std::vector<OutputSection *> &sections = seg->getSections();
7181ad6265SDimitry Andric     size_t osecIdx = 0;
7281ad6265SDimitry Andric     for (; osecIdx < sections.size(); ++osecIdx)
7381ad6265SDimitry Andric       if (*off < sections[osecIdx]->fileOff)
7481ad6265SDimitry Andric         break;
7581ad6265SDimitry Andric     assert(osecIdx > 0);
7681ad6265SDimitry Andric     // We should be only calling this function on offsets that belong to
7781ad6265SDimitry Andric     // ConcatOutputSections.
7881ad6265SDimitry Andric     auto *osec = cast<ConcatOutputSection>(sections[osecIdx - 1]);
7981ad6265SDimitry Andric     *off -= osec->fileOff;
8081ad6265SDimitry Andric 
8181ad6265SDimitry Andric     size_t isecIdx = 0;
8281ad6265SDimitry Andric     for (; isecIdx < osec->inputs.size(); ++isecIdx) {
8381ad6265SDimitry Andric       const ConcatInputSection *isec = osec->inputs[isecIdx];
8481ad6265SDimitry Andric       if (*off < isec->outSecOff)
8581ad6265SDimitry Andric         break;
8681ad6265SDimitry Andric     }
8781ad6265SDimitry Andric     assert(isecIdx > 0);
8881ad6265SDimitry Andric     ConcatInputSection *isec = osec->inputs[isecIdx - 1];
8981ad6265SDimitry Andric     *off -= isec->outSecOff;
9081ad6265SDimitry Andric     return isec;
9181ad6265SDimitry Andric   }
9281ad6265SDimitry Andric   return nullptr;
9381ad6265SDimitry Andric }
9481ad6265SDimitry Andric 
reportRangeError(void * loc,const Reloc & r,const Twine & v,uint8_t bits,int64_t min,uint64_t max)9581ad6265SDimitry Andric void macho::reportRangeError(void *loc, const Reloc &r, const Twine &v,
9681ad6265SDimitry Andric                              uint8_t bits, int64_t min, uint64_t max) {
97fe6060f1SDimitry Andric   std::string hint;
9881ad6265SDimitry Andric   uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
9981ad6265SDimitry Andric   const InputSection *isec = offsetToInputSection(&off);
10081ad6265SDimitry Andric   std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
101fe6060f1SDimitry Andric   if (auto *sym = r.referent.dyn_cast<Symbol *>())
102fe6060f1SDimitry Andric     hint = "; references " + toString(*sym);
10381ad6265SDimitry Andric   error(locStr + ": relocation " + target->getRelocAttrs(r.type).name +
104fe6060f1SDimitry Andric         " is out of range: " + v + " is not in [" + Twine(min) + ", " +
105fe6060f1SDimitry Andric         Twine(max) + "]" + hint);
106fe6060f1SDimitry Andric }
107fe6060f1SDimitry Andric 
reportRangeError(void * loc,SymbolDiagnostic d,const Twine & v,uint8_t bits,int64_t min,uint64_t max)10881ad6265SDimitry Andric void macho::reportRangeError(void *loc, SymbolDiagnostic d, const Twine &v,
10981ad6265SDimitry Andric                              uint8_t bits, int64_t min, uint64_t max) {
11081ad6265SDimitry Andric   // FIXME: should we use `loc` somehow to provide a better error message?
111fe6060f1SDimitry Andric   std::string hint;
112fe6060f1SDimitry Andric   if (d.symbol)
113fe6060f1SDimitry Andric     hint = "; references " + toString(*d.symbol);
114fe6060f1SDimitry Andric   error(d.reason + " is out of range: " + v + " is not in [" + Twine(min) +
115fe6060f1SDimitry Andric         ", " + Twine(max) + "]" + hint);
116fe6060f1SDimitry Andric }
117fe6060f1SDimitry Andric 
118fe6060f1SDimitry Andric const RelocAttrs macho::invalidRelocAttrs{"INVALID", RelocAttrBits::_0};
119