Skip to content

Commit

Permalink
[lld-macho] Make relative method lists work on x86-64 (llvm#103905)
Browse files Browse the repository at this point in the history
Local data is referenced in Objective-C metadata via section + offset
relocations on x86-64 rather than via symbols. Without this change, we
would crash on incorrect casts of the referents to `Defined`.

A basic test based on the existing `objc-relative-method-lists-simple.s`
adopted to x86-64 is added.
  • Loading branch information
BertalanD authored Aug 14, 2024
1 parent 23617f2 commit 51ed383
Show file tree
Hide file tree
Showing 5 changed files with 299 additions and 36 deletions.
30 changes: 4 additions & 26 deletions lld/MachO/ObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,28 +186,6 @@ ObjcCategoryChecker::ObjcCategoryChecker()
roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),
methodLayout(target->wordSize) {}

// \p r must point to an offset within a CStringInputSection or a
// ConcatInputSection
static StringRef getReferentString(const Reloc &r) {
if (auto *isec = r.referent.dyn_cast<InputSection *>())
return cast<CStringInputSection>(isec)->getStringRefAtOffset(r.addend);

auto *sym = cast<Defined>(r.referent.get<Symbol *>());
auto *symIsec = sym->isec();
auto symOffset = sym->value + r.addend;

if (auto *s = dyn_cast_or_null<CStringInputSection>(symIsec))
return s->getStringRefAtOffset(symOffset);

if (isa<ConcatInputSection>(symIsec)) {
auto strData = symIsec->data.slice(symOffset);
const char *pszData = reinterpret_cast<const char *>(strData.data());
return StringRef(pszData, strnlen(pszData, strData.size()));
}

llvm_unreachable("unknown reference section in getReferentString");
}

void ObjcCategoryChecker::parseMethods(const ConcatInputSection *methodsIsec,
const Symbol *methodContainerSym,
const ConcatInputSection *containerIsec,
Expand All @@ -219,7 +197,7 @@ void ObjcCategoryChecker::parseMethods(const ConcatInputSection *methodsIsec,
methodLayout.nameOffset)
continue;

CachedHashStringRef methodName(getReferentString(r));
CachedHashStringRef methodName(r.getReferentString());
// +load methods are special: all implementations are called by the runtime
// even if they are part of the same class. Thus there is no need to check
// for duplicates.
Expand Down Expand Up @@ -251,14 +229,14 @@ void ObjcCategoryChecker::parseMethods(const ConcatInputSection *methodsIsec,
->getReferentInputSection();
nameReloc = roIsec->getRelocAt(roClassLayout.nameOffset);
}
StringRef containerName = getReferentString(*nameReloc);
StringRef containerName = nameReloc->getReferentString();
StringRef methPrefix = mKind == MK_Instance ? "-" : "+";

// We should only ever encounter collisions when parsing category methods
// (since the Class struct is parsed before any of its categories).
assert(mcKind == MCK_Category);
StringRef newCatName =
getReferentString(*containerIsec->getRelocAt(catLayout.nameOffset));
containerIsec->getRelocAt(catLayout.nameOffset)->getReferentString();

auto formatObjAndSrcFileName = [](const InputSection *section) {
lld::macho::InputFile *inputFile = section->getFile();
Expand Down Expand Up @@ -809,7 +787,7 @@ void ObjcCategoryMerger::parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
assert(extInfo.objFileForMergeData &&
"Expected to already have valid objextInfo.objFileForMergeData");

StringRef catName = getReferentString(*catNameReloc);
StringRef catName = catNameReloc->getReferentString();
extInfo.mergedContainerName += catName.str();

// Parse base class
Expand Down
25 changes: 25 additions & 0 deletions lld/MachO/Relocations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,31 @@ InputSection *Reloc::getReferentInputSection() const {
}
}
StringRef Reloc::getReferentString() const {
if (auto *isec = referent.dyn_cast<InputSection *>()) {
const auto *cisec = dyn_cast<CStringInputSection>(isec);
assert(cisec && "referent must be a CStringInputSection");
return cisec->getStringRefAtOffset(addend);
}
auto *sym = dyn_cast<Defined>(referent.get<Symbol *>());
assert(sym && "referent must be a Defined symbol");
auto *symIsec = sym->isec();
auto symOffset = sym->value + addend;
if (auto *s = dyn_cast_or_null<CStringInputSection>(symIsec))
return s->getStringRefAtOffset(symOffset);
if (isa<ConcatInputSection>(symIsec)) {
auto strData = symIsec->data.slice(symOffset);
const char *pszData = reinterpret_cast<const char *>(strData.data());
return StringRef(pszData, strnlen(pszData, strData.size()));
}
llvm_unreachable("unknown reference section in getReferentString");
}
bool macho::validateSymbolRelocation(const Symbol *sym,
const InputSection *isec, const Reloc &r) {
const RelocAttrs &relocAttrs = target->getRelocAttrs(r.type);
Expand Down
4 changes: 4 additions & 0 deletions lld/MachO/Relocations.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ struct Reloc {
addend(addend), referent(referent) {}

InputSection *getReferentInputSection() const;

// Must point to an offset within a CStringInputSection or a
// ConcatInputSection.
llvm::StringRef getReferentString() const;
};

bool validateSymbolRelocation(const Symbol *, const InputSection *,
Expand Down
21 changes: 11 additions & 10 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2010,11 +2010,8 @@ void ObjCMethListSection::setUp() {
while (methodNameOff < isec->data.size()) {
const Reloc *reloc = isec->getRelocAt(methodNameOff);
assert(reloc && "Relocation expected at method list name slot");
auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
assert(def && "Expected valid Defined at method list name slot");
auto *cisec = cast<CStringInputSection>(def->isec());
assert(cisec && "Expected method name to be in a CStringInputSection");
auto methname = cisec->getStringRefAtOffset(def->value);

StringRef methname = reloc->getReferentString();
if (!ObjCSelRefsHelper::getSelRef(methname))
ObjCSelRefsHelper::makeSelRef(methname);

Expand Down Expand Up @@ -2114,19 +2111,23 @@ void ObjCMethListSection::writeRelativeOffsetForIsec(
uint32_t &outSecOff, bool useSelRef) const {
const Reloc *reloc = isec->getRelocAt(inSecOff);
assert(reloc && "Relocation expected at __objc_methlist Offset");
auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
assert(def && "Expected all syms in __objc_methlist to be defined");
uint32_t symVA = def->getVA();

uint32_t symVA = 0;
if (useSelRef) {
auto *cisec = cast<CStringInputSection>(def->isec());
auto methname = cisec->getStringRefAtOffset(def->value);
StringRef methname = reloc->getReferentString();
ConcatInputSection *selRef = ObjCSelRefsHelper::getSelRef(methname);
assert(selRef && "Expected all selector names to already be already be "
"present in __objc_selrefs");
symVA = selRef->getVA();
assert(selRef->data.size() == sizeof(target->wordSize) &&
"Expected one selref per ConcatInputSection");
} else if (reloc->referent.is<Symbol *>()) {
auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
assert(def && "Expected all syms in __objc_methlist to be defined");
symVA = def->getVA();
} else {
auto *isec = reloc->referent.get<InputSection *>();
symVA = isec->getVA(reloc->addend);
}

uint32_t currentVA = isec->getVA() + outSecOff;
Expand Down
Loading

0 comments on commit 51ed383

Please sign in to comment.