Skip to content

Commit

Permalink
[lld-macho] Warn on method name collisions from category definitions
Browse files Browse the repository at this point in the history
This implements ld64's checks for duplicate method names in categories &
classes.

In addition, this sets us up for implementing Obj-C category merging.
This diff handles the most of the parsing work; what's left is rewriting
those category / class structures.

Numbers for chromium_framework:

             base           diff           difference (95% CI)
  sys_time   2.182 ± 0.027  2.200 ± 0.047  [  -0.2% ..   +1.8%]
  user_time  6.451 ± 0.034  6.479 ± 0.062  [  -0.0% ..   +0.9%]
  wall_time  6.841 ± 0.048  6.885 ± 0.105  [  -0.1% ..   +1.4%]
  samples    33             22

Fixes #54912.

Reviewed By: #lld-macho, thevinster, oontvoo

Differential Revision: https://reviews.llvm.org/D142916
  • Loading branch information
int3 committed Mar 7, 2023
1 parent b461398 commit ef12275
Show file tree
Hide file tree
Showing 13 changed files with 629 additions and 55 deletions.
2 changes: 2 additions & 0 deletions lld/MachO/Driver.cpp
Expand Up @@ -1920,6 +1920,8 @@ bool macho::link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
if (config->deadStrip)
markLive();

objc::checkCategories();

// ICF assumes that all literals have been folded already, so we must run
// foldIdenticalLiterals before foldIdenticalSections.
foldIdenticalLiterals();
Expand Down
9 changes: 3 additions & 6 deletions lld/MachO/InputFiles.cpp
Expand Up @@ -1272,13 +1272,10 @@ static CIE parseCIE(const InputSection *isec, const EhReader &reader,
}
}
if (personalityAddrOff != 0) {
auto personalityRelocIt =
llvm::find_if(isec->relocs, [=](const macho::Reloc &r) {
return r.offset == personalityAddrOff;
});
if (personalityRelocIt == isec->relocs.end())
const auto *personalityReloc = isec->getRelocAt(personalityAddrOff);
if (!personalityReloc)
reader.failOn(off, "Failed to locate relocation for personality symbol");
cie.personalitySymbol = personalityRelocIt->referent.get<macho::Symbol *>();
cie.personalitySymbol = personalityReloc->referent.get<macho::Symbol *>();
}
return cie;
}
Expand Down
17 changes: 17 additions & 0 deletions lld/MachO/InputSection.cpp
Expand Up @@ -135,6 +135,14 @@ std::string InputSection::getSourceLocation(uint64_t off) const {
return {};
}

const Reloc *InputSection::getRelocAt(uint32_t off) const {
auto it = llvm::find_if(
relocs, [=](const macho::Reloc &r) { return r.offset == off; });
if (it == relocs.end())
return nullptr;
return &*it;
}

void ConcatInputSection::foldIdentical(ConcatInputSection *copy) {
align = std::max(align, copy->align);
copy->live = false;
Expand Down Expand Up @@ -259,6 +267,15 @@ const StringPiece &CStringInputSection::getStringPiece(uint64_t off) const {
return const_cast<CStringInputSection *>(this)->getStringPiece(off);
}

size_t CStringInputSection::getStringPieceIndex(uint64_t off) const {
if (off >= data.size())
fatal(toString(this) + ": offset is outside the section");

auto it =
partition_point(pieces, [=](StringPiece p) { return p.inSecOff <= off; });
return std::distance(pieces.begin(), it) - 1;
}

uint64_t CStringInputSection::getOffset(uint64_t off) const {
const StringPiece &piece = getStringPiece(off);
uint64_t addend = off - piece.inSecOff;
Expand Down
9 changes: 9 additions & 0 deletions lld/MachO/InputSection.h
Expand Up @@ -55,6 +55,8 @@ class InputSection {
// Return the source line corresponding to an address, or the empty string.
// Format: Source.cpp:123 (/path/to/Source.cpp:123)
std::string getSourceLocation(uint64_t off) const;
// Return the relocation at \p off, if it exists. This does a linear search.
const Reloc *getRelocAt(uint32_t off) const;
// Whether the data at \p off in this InputSection is live.
virtual bool isLive(uint64_t off) const = 0;
virtual void markLive(uint64_t off) = 0;
Expand Down Expand Up @@ -218,6 +220,10 @@ class CStringInputSection final : public InputSection {
return toStringRef(data.slice(begin, end - begin));
}

StringRef getStringRefAtOffset(uint64_t off) const {
return getStringRef(getStringPieceIndex(off));
}

// Returns i'th piece as a CachedHashStringRef. This function is very hot when
// string merging is enabled, so we want to inline.
LLVM_ATTRIBUTE_ALWAYS_INLINE
Expand All @@ -232,6 +238,9 @@ class CStringInputSection final : public InputSection {

bool deduplicateLiterals = false;
std::vector<StringPiece> pieces;

private:
size_t getStringPieceIndex(uint64_t off) const;
};

class WordLiteralInputSection final : public InputSection {
Expand Down
74 changes: 74 additions & 0 deletions lld/MachO/Layout.h
@@ -0,0 +1,74 @@
//===- Layout.h -----------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// Convenience macros for obtaining offsets of members in structs.
//
// Usage:
//
// #define FOR_EACH_FOO_FIELD(DO) \
// DO(Ptr, bar) \
// DO(uint32_t, baz) \
// CREATE_LAYOUT_CLASS(Foo, FOR_EACH_FOO_FIELD)
// #undef FOR_EACH_FOO_FIELD
//
// This will generate
//
// struct FooLayout {
// uint32_t barOffset;
// uint32_t bazOffset;
// uint32_t totalSize;
//
// FooLayout(size_t wordSize) {
// if (wordSize == 8)
// init<uint64_t>();
// else {
// assert(wordSize == 4);
// init<uint32_t>();
// }
// }
//
// private:
// template <class Ptr> void init() {
// FOR_EACH_FIELD(_INIT_OFFSET);
// barOffset = offsetof(Layout<Ptr>, bar);
// bazOffset = offsetof(Layout<Ptr>, baz);
// totalSize = sizeof(Layout<Ptr>);
// }
// template <class Ptr> struct Layout {
// Ptr bar;
// uint32_t baz;
// };
// };

#define _OFFSET_FOR_FIELD(_, name) uint32_t name##Offset;
#define _INIT_OFFSET(type, name) name##Offset = offsetof(Layout<Ptr>, name);
#define _LAYOUT_ENTRY(type, name) type name;

#define CREATE_LAYOUT_CLASS(className, FOR_EACH_FIELD) \
struct className##Layout { \
FOR_EACH_FIELD(_OFFSET_FOR_FIELD) \
uint32_t totalSize; \
\
className##Layout(size_t wordSize) { \
if (wordSize == 8) \
init<uint64_t>(); \
else { \
assert(wordSize == 4); \
init<uint32_t>(); \
} \
} \
\
private: \
template <class Ptr> void init() { \
FOR_EACH_FIELD(_INIT_OFFSET); \
totalSize = sizeof(Layout<Ptr>); \
} \
template <class Ptr> struct Layout { \
FOR_EACH_FIELD(_LAYOUT_ENTRY) \
}; \
}

0 comments on commit ef12275

Please sign in to comment.