mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 07:16:40 +00:00
Revert "[lld-macho] Implement ObjC category merging (-objc_category_merging) (#82928)"
This reverts commit ece2903ce730392e5236d27f1f387fa8067fcb1b, https://github.com/llvm/llvm-project/pull/82928. https://github.com/llvm/llvm-project/pull/82928
This commit is contained in:
parent
6ae77eca6c
commit
5373daad94
@ -1437,8 +1437,6 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
||||
resetOutputSegments();
|
||||
resetWriter();
|
||||
InputFile::resetIdCount();
|
||||
|
||||
objc::doCleanup();
|
||||
};
|
||||
|
||||
ctx->e.logName = args::getFilenameWithoutExe(argsArr[0]);
|
||||
@ -1981,16 +1979,9 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
|
||||
if (config->deadStrip)
|
||||
markLive();
|
||||
|
||||
// Categories are not subject to dead-strip. The __objc_catlist section is
|
||||
// marked as NO_DEAD_STRIP and that propagates into all category data.
|
||||
if (args.hasArg(OPT_check_category_conflicts))
|
||||
objc::checkCategories();
|
||||
|
||||
// Category merging uses "->live = false" to erase old category data, so
|
||||
// it has to run after dead-stripping (markLive).
|
||||
if (args.hasArg(OPT_objc_category_merging, OPT_no_objc_category_merging))
|
||||
objc::mergeCategories();
|
||||
|
||||
// ICF assumes that all literals have been folded already, so we must run
|
||||
// foldIdenticalLiterals before foldIdenticalSections.
|
||||
foldIdenticalLiterals();
|
||||
|
@ -93,9 +93,9 @@ public:
|
||||
// .subsections_via_symbols, there is typically only one element here.
|
||||
llvm::TinyPtrVector<Defined *> symbols;
|
||||
|
||||
protected:
|
||||
const Section §ion;
|
||||
|
||||
protected:
|
||||
const Defined *getContainingSymbol(uint64_t off) const;
|
||||
};
|
||||
|
||||
|
@ -7,19 +7,16 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "ObjC.h"
|
||||
#include "ConcatOutputSection.h"
|
||||
#include "InputFiles.h"
|
||||
#include "InputSection.h"
|
||||
#include "Layout.h"
|
||||
#include "OutputSegment.h"
|
||||
#include "SyntheticSections.h"
|
||||
#include "Target.h"
|
||||
|
||||
#include "lld/Common/ErrorHandler.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/BinaryFormat/MachO.h"
|
||||
#include "llvm/Bitcode/BitcodeReader.h"
|
||||
#include "llvm/Support/TimeProfiler.h"
|
||||
|
||||
using namespace llvm;
|
||||
using namespace llvm::MachO;
|
||||
@ -81,8 +78,7 @@ namespace {
|
||||
DO(Ptr, classMethods) \
|
||||
DO(Ptr, protocols) \
|
||||
DO(Ptr, instanceProps) \
|
||||
DO(Ptr, classProps) \
|
||||
DO(uint32_t, size)
|
||||
DO(Ptr, classProps)
|
||||
|
||||
CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD);
|
||||
|
||||
@ -116,19 +112,13 @@ CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD);
|
||||
#undef FOR_EACH_RO_CLASS_FIELD
|
||||
|
||||
#define FOR_EACH_LIST_HEADER(DO) \
|
||||
DO(uint32_t, structSize) \
|
||||
DO(uint32_t, structCount)
|
||||
DO(uint32_t, size) \
|
||||
DO(uint32_t, count)
|
||||
|
||||
CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER);
|
||||
|
||||
#undef FOR_EACH_LIST_HEADER
|
||||
|
||||
#define FOR_EACH_PROTOCOL_LIST_HEADER(DO) DO(Ptr, protocolCount)
|
||||
|
||||
CREATE_LAYOUT_CLASS(ProtocolListHeader, FOR_EACH_PROTOCOL_LIST_HEADER);
|
||||
|
||||
#undef FOR_EACH_PROTOCOL_LIST_HEADER
|
||||
|
||||
#define FOR_EACH_METHOD(DO) \
|
||||
DO(Ptr, name) \
|
||||
DO(Ptr, type) \
|
||||
@ -321,8 +311,6 @@ void ObjcCategoryChecker::parseClass(const Defined *classSym) {
|
||||
}
|
||||
|
||||
void objc::checkCategories() {
|
||||
TimeTraceScope timeScope("ObjcCategoryChecker");
|
||||
|
||||
ObjcCategoryChecker checker;
|
||||
for (const InputSection *isec : inputSections) {
|
||||
if (isec->getName() == section_names::objcCatList)
|
||||
@ -332,905 +320,3 @@ void objc::checkCategories() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ObjcCategoryMerger {
|
||||
// Information about an input category
|
||||
struct InfoInputCategory {
|
||||
ConcatInputSection *catListIsec;
|
||||
ConcatInputSection *catBodyIsec;
|
||||
uint32_t offCatListIsec = 0;
|
||||
|
||||
bool wasMerged = false;
|
||||
};
|
||||
|
||||
// To write new (merged) categories or classes, we will try make limited
|
||||
// assumptions about the alignment and the sections the various class/category
|
||||
// info are stored in and . So we'll just reuse the same sections and
|
||||
// alignment as already used in existing (input) categories. To do this we
|
||||
// have InfoCategoryWriter which contains the various sections that the
|
||||
// generated categories will be written to.
|
||||
template <typename T> struct InfoWriteSection {
|
||||
bool valid = false; // Data has been successfully collected from input
|
||||
uint32_t align = 0;
|
||||
Section *inputSection;
|
||||
Reloc relocTemplate;
|
||||
T *outputSection;
|
||||
};
|
||||
|
||||
struct InfoCategoryWriter {
|
||||
InfoWriteSection<ConcatOutputSection> catListInfo;
|
||||
InfoWriteSection<ConcatOutputSection> catBodyInfo;
|
||||
InfoWriteSection<CStringSection> catNameInfo;
|
||||
InfoWriteSection<ConcatOutputSection> catPtrListInfo;
|
||||
};
|
||||
|
||||
// Information about a pointer list in the original categories (method lists,
|
||||
// protocol lists, etc)
|
||||
struct PointerListInfo {
|
||||
PointerListInfo(const char *_categoryPrefix, uint32_t _categoryOffset,
|
||||
uint32_t _pointersPerStruct)
|
||||
: categoryPrefix(_categoryPrefix), categoryOffset(_categoryOffset),
|
||||
pointersPerStruct(_pointersPerStruct) {}
|
||||
const char *categoryPrefix;
|
||||
uint32_t categoryOffset = 0;
|
||||
|
||||
uint32_t pointersPerStruct = 0;
|
||||
|
||||
uint32_t structSize = 0;
|
||||
uint32_t structCount = 0;
|
||||
|
||||
std::vector<Symbol *> allPtrs;
|
||||
};
|
||||
|
||||
// Full information about all the categories that extend a class. This will
|
||||
// include all the additional methods, protocols, and properties that are
|
||||
// contained in all the categories that extend a particular class.
|
||||
struct ClassExtensionInfo {
|
||||
ClassExtensionInfo(CategoryLayout &_catLayout) : catLayout(_catLayout){};
|
||||
|
||||
// Merged names of containers. Ex: base|firstCategory|secondCategory|...
|
||||
std::string mergedContainerName;
|
||||
std::string baseClassName;
|
||||
Symbol *baseClass = nullptr;
|
||||
CategoryLayout &catLayout;
|
||||
|
||||
// In case we generate new data, mark the new data as belonging to this file
|
||||
ObjFile *objFileForMergeData = nullptr;
|
||||
|
||||
PointerListInfo instanceMethods = {
|
||||
objc::symbol_names::categoryInstanceMethods,
|
||||
/*_categoryOffset=*/catLayout.instanceMethodsOffset,
|
||||
/*pointersPerStruct=*/3};
|
||||
PointerListInfo classMethods = {
|
||||
objc::symbol_names::categoryClassMethods,
|
||||
/*_categoryOffset=*/catLayout.classMethodsOffset,
|
||||
/*pointersPerStruct=*/3};
|
||||
PointerListInfo protocols = {objc::symbol_names::categoryProtocols,
|
||||
/*_categoryOffset=*/catLayout.protocolsOffset,
|
||||
/*pointersPerStruct=*/0};
|
||||
PointerListInfo instanceProps = {
|
||||
objc::symbol_names::listProprieties,
|
||||
/*_categoryOffset=*/catLayout.instancePropsOffset,
|
||||
/*pointersPerStruct=*/2};
|
||||
PointerListInfo classProps = {
|
||||
objc::symbol_names::klassPropList,
|
||||
/*_categoryOffset=*/catLayout.classPropsOffset,
|
||||
/*pointersPerStruct=*/2};
|
||||
};
|
||||
|
||||
public:
|
||||
ObjcCategoryMerger(std::vector<ConcatInputSection *> &_allInputSections);
|
||||
void doMerge();
|
||||
static void doCleanup();
|
||||
|
||||
private:
|
||||
void collectAndValidateCategoriesData();
|
||||
void
|
||||
mergeCategoriesIntoSingleCategory(std::vector<InfoInputCategory> &categories);
|
||||
|
||||
void eraseISec(ConcatInputSection *isec);
|
||||
void eraseMergedCategories();
|
||||
|
||||
void generateCatListForNonErasedCategories(
|
||||
std::map<ConcatInputSection *, std::set<uint64_t>>
|
||||
catListToErasedOffsets);
|
||||
template <typename T>
|
||||
void collectSectionWriteInfoFromIsec(const InputSection *isec,
|
||||
InfoWriteSection<T> &catWriteInfo);
|
||||
void collectCategoryWriterInfoFromCategory(const InfoInputCategory &catInfo);
|
||||
void parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
|
||||
ClassExtensionInfo &extInfo);
|
||||
|
||||
void parseProtocolListInfo(const ConcatInputSection *isec, uint32_t secOffset,
|
||||
PointerListInfo &ptrList);
|
||||
|
||||
void parsePointerListInfo(const ConcatInputSection *isec, uint32_t secOffset,
|
||||
PointerListInfo &ptrList);
|
||||
|
||||
void emitAndLinkPointerList(Defined *parentSym, uint32_t linkAtOffset,
|
||||
const ClassExtensionInfo &extInfo,
|
||||
const PointerListInfo &ptrList);
|
||||
|
||||
void emitAndLinkProtocolList(Defined *parentSym, uint32_t linkAtOffset,
|
||||
const ClassExtensionInfo &extInfo,
|
||||
const PointerListInfo &ptrList);
|
||||
|
||||
Defined *emitCategory(const ClassExtensionInfo &extInfo);
|
||||
Defined *emitCatListEntrySec(const std::string &forCateogryName,
|
||||
const std::string &forBaseClassName,
|
||||
ObjFile *objFile);
|
||||
Defined *emitCategoryBody(const std::string &name, const Defined *nameSym,
|
||||
const Symbol *baseClassSym,
|
||||
const std::string &baseClassName, ObjFile *objFile);
|
||||
Defined *emitCategoryName(const std::string &name, ObjFile *objFile);
|
||||
void createSymbolReference(Defined *refFrom, const Symbol *refTo,
|
||||
uint32_t offset, const Reloc &relocTemplate);
|
||||
Symbol *tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,
|
||||
uint32_t offset);
|
||||
Defined *tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,
|
||||
uint32_t offset);
|
||||
void tryEraseDefinedAtIsecOffset(const ConcatInputSection *isec,
|
||||
uint32_t offset);
|
||||
|
||||
// Allocate a null-terminated StringRef backed by generatedSectionData
|
||||
StringRef newStringData(const char *str);
|
||||
// Allocate section data, backed by generatedSectionData
|
||||
SmallVector<uint8_t> &newSectionData(uint32_t size);
|
||||
|
||||
CategoryLayout catLayout;
|
||||
ClassLayout classLayout;
|
||||
ROClassLayout roClassLayout;
|
||||
ListHeaderLayout listHeaderLayout;
|
||||
MethodLayout methodLayout;
|
||||
ProtocolListHeaderLayout protocolListHeaderLayout;
|
||||
|
||||
InfoCategoryWriter infoCategoryWriter;
|
||||
std::vector<ConcatInputSection *> &allInputSections;
|
||||
// Map of base class Symbol to list of InfoInputCategory's for it
|
||||
DenseMap<const Symbol *, std::vector<InfoInputCategory>> categoryMap;
|
||||
|
||||
// Normally, the binary data comes from the input files, but since we're
|
||||
// generating binary data ourselves, we use the below array to store it in.
|
||||
// Need this to be 'static' so the data survives past the ObjcCategoryMerger
|
||||
// object, as the data will be read by the Writer when the final binary is
|
||||
// generated.
|
||||
static SmallVector<SmallVector<uint8_t>> generatedSectionData;
|
||||
};
|
||||
|
||||
SmallVector<SmallVector<uint8_t>> ObjcCategoryMerger::generatedSectionData;
|
||||
|
||||
ObjcCategoryMerger::ObjcCategoryMerger(
|
||||
std::vector<ConcatInputSection *> &_allInputSections)
|
||||
: catLayout(target->wordSize), classLayout(target->wordSize),
|
||||
roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),
|
||||
methodLayout(target->wordSize),
|
||||
protocolListHeaderLayout(target->wordSize),
|
||||
allInputSections(_allInputSections) {}
|
||||
|
||||
// This is a template so that it can be used both for CStringSection and
|
||||
// ConcatOutputSection
|
||||
template <typename T>
|
||||
void ObjcCategoryMerger::collectSectionWriteInfoFromIsec(
|
||||
const InputSection *isec, InfoWriteSection<T> &catWriteInfo) {
|
||||
|
||||
catWriteInfo.inputSection = const_cast<Section *>(&isec->section);
|
||||
catWriteInfo.align = isec->align;
|
||||
catWriteInfo.outputSection = dyn_cast_or_null<T>(isec->parent);
|
||||
|
||||
assert(catWriteInfo.outputSection &&
|
||||
"outputSection may not be null in collectSectionWriteInfoFromIsec.");
|
||||
|
||||
if (isec->relocs.size())
|
||||
catWriteInfo.relocTemplate = isec->relocs[0];
|
||||
|
||||
catWriteInfo.valid = true;
|
||||
}
|
||||
|
||||
Symbol *
|
||||
ObjcCategoryMerger::tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,
|
||||
uint32_t offset) {
|
||||
const Reloc *reloc = isec->getRelocAt(offset);
|
||||
|
||||
if (!reloc)
|
||||
return nullptr;
|
||||
|
||||
return reloc->referent.get<Symbol *>();
|
||||
}
|
||||
|
||||
Defined *
|
||||
ObjcCategoryMerger::tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,
|
||||
uint32_t offset) {
|
||||
Symbol *sym = tryGetSymbolAtIsecOffset(isec, offset);
|
||||
return dyn_cast_or_null<Defined>(sym);
|
||||
}
|
||||
|
||||
// Given an ConcatInputSection or CStringInputSection and an offset, if there is
|
||||
// a symbol(Defined) at that offset, then erase the symbol (mark it not live)
|
||||
void ObjcCategoryMerger::tryEraseDefinedAtIsecOffset(
|
||||
const ConcatInputSection *isec, uint32_t offset) {
|
||||
const Reloc *reloc = isec->getRelocAt(offset);
|
||||
|
||||
if (!reloc)
|
||||
return;
|
||||
|
||||
Defined *sym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
|
||||
if (!sym)
|
||||
return;
|
||||
|
||||
if (auto *cisec = dyn_cast_or_null<ConcatInputSection>(sym->isec))
|
||||
eraseISec(cisec);
|
||||
else if (auto *csisec = dyn_cast_or_null<CStringInputSection>(sym->isec)) {
|
||||
uint32_t totalOffset = sym->value + reloc->addend;
|
||||
StringPiece &piece = csisec->getStringPiece(totalOffset);
|
||||
piece.live = false;
|
||||
} else {
|
||||
llvm_unreachable("erased symbol has to be Defined or CStringInputSection");
|
||||
}
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::collectCategoryWriterInfoFromCategory(
|
||||
const InfoInputCategory &catInfo) {
|
||||
|
||||
if (!infoCategoryWriter.catListInfo.valid)
|
||||
collectSectionWriteInfoFromIsec<ConcatOutputSection>(
|
||||
catInfo.catListIsec, infoCategoryWriter.catListInfo);
|
||||
if (!infoCategoryWriter.catBodyInfo.valid)
|
||||
collectSectionWriteInfoFromIsec<ConcatOutputSection>(
|
||||
catInfo.catBodyIsec, infoCategoryWriter.catBodyInfo);
|
||||
|
||||
if (!infoCategoryWriter.catNameInfo.valid) {
|
||||
lld::macho::Defined *catNameSym =
|
||||
tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
|
||||
assert(catNameSym && "Category does not have a valid name Symbol");
|
||||
|
||||
collectSectionWriteInfoFromIsec<CStringSection>(
|
||||
catNameSym->isec, infoCategoryWriter.catNameInfo);
|
||||
}
|
||||
|
||||
// Collect writer info from all the category lists (we're assuming they all
|
||||
// would provide the same info)
|
||||
if (!infoCategoryWriter.catPtrListInfo.valid) {
|
||||
for (uint32_t off = catLayout.instanceMethodsOffset;
|
||||
off <= catLayout.classPropsOffset; off += target->wordSize) {
|
||||
if (Defined *ptrList =
|
||||
tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, off)) {
|
||||
collectSectionWriteInfoFromIsec<ConcatOutputSection>(
|
||||
ptrList->isec, infoCategoryWriter.catPtrListInfo);
|
||||
// we've successfully collected data, so we can break
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a protocol list that might be linked to ConcatInputSection at a given
|
||||
// offset. The format of the protocol list is different than other lists (prop
|
||||
// lists, method lists) so we need to parse it differently
|
||||
void ObjcCategoryMerger::parseProtocolListInfo(const ConcatInputSection *isec,
|
||||
uint32_t secOffset,
|
||||
PointerListInfo &ptrList) {
|
||||
if (!isec || (secOffset + target->wordSize > isec->data.size()))
|
||||
assert("Tried to read pointer list beyond protocol section end");
|
||||
|
||||
const Reloc *reloc = isec->getRelocAt(secOffset);
|
||||
if (!reloc)
|
||||
return;
|
||||
|
||||
auto *ptrListSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
|
||||
assert(ptrListSym && "Protocol list reloc does not have a valid Defined");
|
||||
|
||||
// Theoretically protocol count can be either 32b or 64b, depending on
|
||||
// platform pointer size, but to simplify implementation we always just read
|
||||
// the lower 32b which should be good enough.
|
||||
uint32_t protocolCount = *reinterpret_cast<const uint32_t *>(
|
||||
ptrListSym->isec->data.data() + listHeaderLayout.structSizeOffset);
|
||||
|
||||
ptrList.structCount += protocolCount;
|
||||
ptrList.structSize = target->wordSize;
|
||||
|
||||
uint32_t expectedListSize =
|
||||
(protocolCount * target->wordSize) +
|
||||
/*header(count)*/ protocolListHeaderLayout.totalSize +
|
||||
/*extra null value*/ target->wordSize;
|
||||
assert(expectedListSize == ptrListSym->isec->data.size() &&
|
||||
"Protocol list does not match expected size");
|
||||
|
||||
uint32_t off = protocolListHeaderLayout.totalSize;
|
||||
for (uint32_t inx = 0; inx < protocolCount; ++inx) {
|
||||
const Reloc *reloc = ptrListSym->isec->getRelocAt(off);
|
||||
assert(reloc && "No reloc found at protocol list offset");
|
||||
|
||||
auto *listSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
|
||||
assert(listSym && "Protocol list reloc does not have a valid Defined");
|
||||
|
||||
ptrList.allPtrs.push_back(listSym);
|
||||
off += target->wordSize;
|
||||
}
|
||||
assert((ptrListSym->isec->getRelocAt(off) == nullptr) &&
|
||||
"expected null terminating protocol");
|
||||
assert(off + /*extra null value*/ target->wordSize == expectedListSize &&
|
||||
"Protocol list end offset does not match expected size");
|
||||
}
|
||||
|
||||
// Parse a pointer list that might be linked to ConcatInputSection at a given
|
||||
// offset. This can be used for instance methods, class methods, instance props
|
||||
// and class props since they have the same format.
|
||||
void ObjcCategoryMerger::parsePointerListInfo(const ConcatInputSection *isec,
|
||||
uint32_t secOffset,
|
||||
PointerListInfo &ptrList) {
|
||||
assert(ptrList.pointersPerStruct == 2 || ptrList.pointersPerStruct == 3);
|
||||
assert(isec && "Trying to parse pointer list from null isec");
|
||||
assert(secOffset + target->wordSize <= isec->data.size() &&
|
||||
"Trying to read pointer list beyond section end");
|
||||
|
||||
const Reloc *reloc = isec->getRelocAt(secOffset);
|
||||
if (!reloc)
|
||||
return;
|
||||
|
||||
auto *ptrListSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
|
||||
assert(ptrListSym && "Reloc does not have a valid Defined");
|
||||
|
||||
uint32_t thisStructSize = *reinterpret_cast<const uint32_t *>(
|
||||
ptrListSym->isec->data.data() + listHeaderLayout.structSizeOffset);
|
||||
uint32_t thisStructCount = *reinterpret_cast<const uint32_t *>(
|
||||
ptrListSym->isec->data.data() + listHeaderLayout.structCountOffset);
|
||||
assert(thisStructSize == ptrList.pointersPerStruct * target->wordSize);
|
||||
|
||||
assert(!ptrList.structSize || (thisStructSize == ptrList.structSize));
|
||||
|
||||
ptrList.structCount += thisStructCount;
|
||||
ptrList.structSize = thisStructSize;
|
||||
|
||||
uint32_t expectedListSize =
|
||||
listHeaderLayout.totalSize + (thisStructSize * thisStructCount);
|
||||
assert(expectedListSize == ptrListSym->isec->data.size() &&
|
||||
"Pointer list does not match expected size");
|
||||
|
||||
for (uint32_t off = listHeaderLayout.totalSize; off < expectedListSize;
|
||||
off += target->wordSize) {
|
||||
const Reloc *reloc = ptrListSym->isec->getRelocAt(off);
|
||||
assert(reloc && "No reloc found at pointer list offset");
|
||||
|
||||
auto *listSym = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
|
||||
assert(listSym && "Reloc does not have a valid Defined");
|
||||
|
||||
ptrList.allPtrs.push_back(listSym);
|
||||
}
|
||||
}
|
||||
|
||||
// Here we parse all the information of an input category (catInfo) and
|
||||
// append the parsed info into the structure which will contain all the
|
||||
// information about how a class is extended (extInfo)
|
||||
void ObjcCategoryMerger::parseCatInfoToExtInfo(const InfoInputCategory &catInfo,
|
||||
ClassExtensionInfo &extInfo) {
|
||||
const Reloc *catNameReloc =
|
||||
catInfo.catBodyIsec->getRelocAt(catLayout.nameOffset);
|
||||
|
||||
// Parse name
|
||||
assert(catNameReloc && "Category does not have a reloc at 'nameOffset'");
|
||||
|
||||
// is this the first category we are parsing?
|
||||
if (extInfo.mergedContainerName.empty())
|
||||
extInfo.objFileForMergeData =
|
||||
dyn_cast_or_null<ObjFile>(catInfo.catBodyIsec->getFile());
|
||||
else
|
||||
extInfo.mergedContainerName += "|";
|
||||
|
||||
assert(extInfo.objFileForMergeData &&
|
||||
"Expected to already have valid objextInfo.objFileForMergeData");
|
||||
|
||||
StringRef catName = getReferentString(*catNameReloc);
|
||||
extInfo.mergedContainerName += catName.str();
|
||||
|
||||
// Parse base class
|
||||
if (!extInfo.baseClass) {
|
||||
Symbol *classSym =
|
||||
tryGetSymbolAtIsecOffset(catInfo.catBodyIsec, catLayout.klassOffset);
|
||||
assert(extInfo.baseClassName.empty());
|
||||
extInfo.baseClass = classSym;
|
||||
llvm::StringRef classPrefix(objc::symbol_names::klass);
|
||||
assert(classSym->getName().starts_with(classPrefix) &&
|
||||
"Base class symbol does not start with expected prefix");
|
||||
extInfo.baseClassName = classSym->getName().substr(classPrefix.size());
|
||||
} else {
|
||||
assert((extInfo.baseClass ==
|
||||
tryGetSymbolAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.klassOffset)) &&
|
||||
"Trying to parse category info into container with different base "
|
||||
"class");
|
||||
}
|
||||
|
||||
parsePointerListInfo(catInfo.catBodyIsec, catLayout.instanceMethodsOffset,
|
||||
extInfo.instanceMethods);
|
||||
|
||||
parsePointerListInfo(catInfo.catBodyIsec, catLayout.classMethodsOffset,
|
||||
extInfo.classMethods);
|
||||
|
||||
parseProtocolListInfo(catInfo.catBodyIsec, catLayout.protocolsOffset,
|
||||
extInfo.protocols);
|
||||
|
||||
parsePointerListInfo(catInfo.catBodyIsec, catLayout.instancePropsOffset,
|
||||
extInfo.instanceProps);
|
||||
|
||||
parsePointerListInfo(catInfo.catBodyIsec, catLayout.classPropsOffset,
|
||||
extInfo.classProps);
|
||||
}
|
||||
|
||||
// Generate a protocol list (including header) and link it into the parent at
|
||||
// the specified offset.
|
||||
void ObjcCategoryMerger::emitAndLinkProtocolList(
|
||||
Defined *parentSym, uint32_t linkAtOffset,
|
||||
const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {
|
||||
if (ptrList.allPtrs.empty())
|
||||
return;
|
||||
|
||||
assert(ptrList.allPtrs.size() == ptrList.structCount);
|
||||
|
||||
uint32_t bodySize = (ptrList.structCount * target->wordSize) +
|
||||
/*header(count)*/ protocolListHeaderLayout.totalSize +
|
||||
/*extra null value*/ target->wordSize;
|
||||
llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);
|
||||
|
||||
// This theoretically can be either 32b or 64b, but writing just the first 32b
|
||||
// is good enough
|
||||
const uint32_t *ptrProtoCount = reinterpret_cast<const uint32_t *>(
|
||||
bodyData.data() + protocolListHeaderLayout.protocolCountOffset);
|
||||
|
||||
*const_cast<uint32_t *>(ptrProtoCount) = ptrList.allPtrs.size();
|
||||
|
||||
ConcatInputSection *listSec = make<ConcatInputSection>(
|
||||
*infoCategoryWriter.catPtrListInfo.inputSection, bodyData,
|
||||
infoCategoryWriter.catPtrListInfo.align);
|
||||
listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
|
||||
listSec->live = true;
|
||||
allInputSections.push_back(listSec);
|
||||
|
||||
listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
|
||||
|
||||
std::string symName = ptrList.categoryPrefix;
|
||||
symName += extInfo.baseClassName + "_$_(" + extInfo.mergedContainerName + ")";
|
||||
|
||||
Defined *ptrListSym = make<Defined>(
|
||||
newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),
|
||||
listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,
|
||||
/*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
|
||||
/*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
|
||||
/*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
ptrListSym->used = true;
|
||||
parentSym->getObjectFile()->symbols.push_back(ptrListSym);
|
||||
|
||||
createSymbolReference(parentSym, ptrListSym, linkAtOffset,
|
||||
infoCategoryWriter.catBodyInfo.relocTemplate);
|
||||
|
||||
uint32_t offset = protocolListHeaderLayout.totalSize;
|
||||
for (Symbol *symbol : ptrList.allPtrs) {
|
||||
createSymbolReference(ptrListSym, symbol, offset,
|
||||
infoCategoryWriter.catPtrListInfo.relocTemplate);
|
||||
offset += target->wordSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a pointer list (including header) and link it into the parent at the
|
||||
// specified offset. This is used for instance and class methods and
|
||||
// proprieties.
|
||||
void ObjcCategoryMerger::emitAndLinkPointerList(
|
||||
Defined *parentSym, uint32_t linkAtOffset,
|
||||
const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {
|
||||
if (ptrList.allPtrs.empty())
|
||||
return;
|
||||
|
||||
assert(ptrList.allPtrs.size() * target->wordSize ==
|
||||
ptrList.structCount * ptrList.structSize);
|
||||
|
||||
// Generate body
|
||||
uint32_t bodySize =
|
||||
listHeaderLayout.totalSize + (ptrList.structSize * ptrList.structCount);
|
||||
llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);
|
||||
|
||||
const uint32_t *ptrStructSize = reinterpret_cast<const uint32_t *>(
|
||||
bodyData.data() + listHeaderLayout.structSizeOffset);
|
||||
const uint32_t *ptrStructCount = reinterpret_cast<const uint32_t *>(
|
||||
bodyData.data() + listHeaderLayout.structCountOffset);
|
||||
|
||||
*const_cast<uint32_t *>(ptrStructSize) = ptrList.structSize;
|
||||
*const_cast<uint32_t *>(ptrStructCount) = ptrList.structCount;
|
||||
|
||||
ConcatInputSection *listSec = make<ConcatInputSection>(
|
||||
*infoCategoryWriter.catPtrListInfo.inputSection, bodyData,
|
||||
infoCategoryWriter.catPtrListInfo.align);
|
||||
listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
|
||||
listSec->live = true;
|
||||
allInputSections.push_back(listSec);
|
||||
|
||||
listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;
|
||||
|
||||
std::string symName = ptrList.categoryPrefix;
|
||||
symName += extInfo.baseClassName + "_$_" + extInfo.mergedContainerName;
|
||||
|
||||
Defined *ptrListSym = make<Defined>(
|
||||
newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),
|
||||
listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,
|
||||
/*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,
|
||||
/*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
|
||||
/*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
ptrListSym->used = true;
|
||||
parentSym->getObjectFile()->symbols.push_back(ptrListSym);
|
||||
|
||||
createSymbolReference(parentSym, ptrListSym, linkAtOffset,
|
||||
infoCategoryWriter.catBodyInfo.relocTemplate);
|
||||
|
||||
uint32_t offset = listHeaderLayout.totalSize;
|
||||
for (Symbol *symbol : ptrList.allPtrs) {
|
||||
createSymbolReference(ptrListSym, symbol, offset,
|
||||
infoCategoryWriter.catPtrListInfo.relocTemplate);
|
||||
offset += target->wordSize;
|
||||
}
|
||||
}
|
||||
|
||||
// This method creates an __objc_catlist ConcatInputSection with a single slot
|
||||
Defined *
|
||||
ObjcCategoryMerger::emitCatListEntrySec(const std::string &forCateogryName,
|
||||
const std::string &forBaseClassName,
|
||||
ObjFile *objFile) {
|
||||
uint32_t sectionSize = target->wordSize;
|
||||
llvm::ArrayRef<uint8_t> bodyData = newSectionData(sectionSize);
|
||||
|
||||
ConcatInputSection *newCatList =
|
||||
make<ConcatInputSection>(*infoCategoryWriter.catListInfo.inputSection,
|
||||
bodyData, infoCategoryWriter.catListInfo.align);
|
||||
newCatList->parent = infoCategoryWriter.catListInfo.outputSection;
|
||||
newCatList->live = true;
|
||||
allInputSections.push_back(newCatList);
|
||||
|
||||
newCatList->parent = infoCategoryWriter.catListInfo.outputSection;
|
||||
|
||||
std::string catSymName = "<__objc_catlist slot for merged category ";
|
||||
catSymName += forBaseClassName + "(" + forCateogryName + ")>";
|
||||
|
||||
Defined *catListSym = make<Defined>(
|
||||
newStringData(catSymName.c_str()), /*file=*/objFile, newCatList,
|
||||
/*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,
|
||||
/*isPrivateExtern=*/false, /*includeInSymtab=*/false,
|
||||
/*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
|
||||
/*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
catListSym->used = true;
|
||||
objFile->symbols.push_back(catListSym);
|
||||
return catListSym;
|
||||
}
|
||||
|
||||
// Here we generate the main category body and link the name and base class into
|
||||
// it. We don't link any other info yet like the protocol and class/instance
|
||||
// methods/props.
|
||||
Defined *ObjcCategoryMerger::emitCategoryBody(const std::string &name,
|
||||
const Defined *nameSym,
|
||||
const Symbol *baseClassSym,
|
||||
const std::string &baseClassName,
|
||||
ObjFile *objFile) {
|
||||
llvm::ArrayRef<uint8_t> bodyData = newSectionData(catLayout.totalSize);
|
||||
|
||||
uint32_t *ptrSize = (uint32_t *)(const_cast<uint8_t *>(bodyData.data()) +
|
||||
catLayout.sizeOffset);
|
||||
*ptrSize = catLayout.totalSize;
|
||||
|
||||
ConcatInputSection *newBodySec =
|
||||
make<ConcatInputSection>(*infoCategoryWriter.catBodyInfo.inputSection,
|
||||
bodyData, infoCategoryWriter.catBodyInfo.align);
|
||||
newBodySec->parent = infoCategoryWriter.catBodyInfo.outputSection;
|
||||
newBodySec->live = true;
|
||||
allInputSections.push_back(newBodySec);
|
||||
|
||||
std::string symName =
|
||||
objc::symbol_names::category + baseClassName + "_$_(" + name + ")";
|
||||
Defined *catBodySym = make<Defined>(
|
||||
newStringData(symName.c_str()), /*file=*/objFile, newBodySec,
|
||||
/*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,
|
||||
/*isPrivateExtern=*/false, /*includeInSymtab=*/true,
|
||||
/*isReferencedDynamically=*/false, /*noDeadStrip=*/false,
|
||||
/*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
catBodySym->used = true;
|
||||
objFile->symbols.push_back(catBodySym);
|
||||
|
||||
createSymbolReference(catBodySym, nameSym, catLayout.nameOffset,
|
||||
infoCategoryWriter.catBodyInfo.relocTemplate);
|
||||
|
||||
// Create a reloc to the base class (either external or internal)
|
||||
createSymbolReference(catBodySym, baseClassSym, catLayout.klassOffset,
|
||||
infoCategoryWriter.catBodyInfo.relocTemplate);
|
||||
|
||||
return catBodySym;
|
||||
}
|
||||
|
||||
// This writes the new category name (for the merged category) into the binary
|
||||
// and returns the sybmol for it.
|
||||
Defined *ObjcCategoryMerger::emitCategoryName(const std::string &name,
|
||||
ObjFile *objFile) {
|
||||
StringRef nameStrData = newStringData(name.c_str());
|
||||
// We use +1 below to include the null terminator
|
||||
llvm::ArrayRef<uint8_t> nameData(
|
||||
reinterpret_cast<const uint8_t *>(nameStrData.data()),
|
||||
nameStrData.size() + 1);
|
||||
|
||||
auto *parentSection = infoCategoryWriter.catNameInfo.inputSection;
|
||||
CStringInputSection *newStringSec = make<CStringInputSection>(
|
||||
*infoCategoryWriter.catNameInfo.inputSection, nameData,
|
||||
infoCategoryWriter.catNameInfo.align, /*dedupLiterals=*/true);
|
||||
|
||||
parentSection->subsections.push_back({0, newStringSec});
|
||||
|
||||
newStringSec->splitIntoPieces();
|
||||
newStringSec->pieces[0].live = true;
|
||||
newStringSec->parent = infoCategoryWriter.catNameInfo.outputSection;
|
||||
in.cStringSection->addInput(newStringSec);
|
||||
assert(newStringSec->pieces.size() == 1);
|
||||
|
||||
Defined *catNameSym = make<Defined>(
|
||||
"<merged category name>", /*file=*/objFile, newStringSec,
|
||||
/*value=*/0, nameData.size(),
|
||||
/*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,
|
||||
/*includeInSymtab=*/false, /*isReferencedDynamically=*/false,
|
||||
/*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
catNameSym->used = true;
|
||||
objFile->symbols.push_back(catNameSym);
|
||||
return catNameSym;
|
||||
}
|
||||
|
||||
// This method fully creates a new category from the given ClassExtensionInfo.
|
||||
// It creates the category name, body and method/protocol/prop lists and links
|
||||
// them all together. Then it creates a new __objc_catlist entry and adds the
|
||||
// category to it. Calling this method will fully generate a category which will
|
||||
// be available in the final binary.
|
||||
Defined *ObjcCategoryMerger::emitCategory(const ClassExtensionInfo &extInfo) {
|
||||
Defined *catNameSym = emitCategoryName(extInfo.mergedContainerName,
|
||||
extInfo.objFileForMergeData);
|
||||
|
||||
Defined *catBodySym = emitCategoryBody(
|
||||
extInfo.mergedContainerName, catNameSym, extInfo.baseClass,
|
||||
extInfo.baseClassName, extInfo.objFileForMergeData);
|
||||
|
||||
Defined *catListSym =
|
||||
emitCatListEntrySec(extInfo.mergedContainerName, extInfo.baseClassName,
|
||||
extInfo.objFileForMergeData);
|
||||
|
||||
// Add the single category body to the category list at the offset 0.
|
||||
createSymbolReference(catListSym, catBodySym, /*offset=*/0,
|
||||
infoCategoryWriter.catListInfo.relocTemplate);
|
||||
|
||||
emitAndLinkPointerList(catBodySym, catLayout.instanceMethodsOffset, extInfo,
|
||||
extInfo.instanceMethods);
|
||||
|
||||
emitAndLinkPointerList(catBodySym, catLayout.classMethodsOffset, extInfo,
|
||||
extInfo.classMethods);
|
||||
|
||||
emitAndLinkProtocolList(catBodySym, catLayout.protocolsOffset, extInfo,
|
||||
extInfo.protocols);
|
||||
|
||||
emitAndLinkPointerList(catBodySym, catLayout.instancePropsOffset, extInfo,
|
||||
extInfo.instanceProps);
|
||||
|
||||
emitAndLinkPointerList(catBodySym, catLayout.classPropsOffset, extInfo,
|
||||
extInfo.classProps);
|
||||
|
||||
return catBodySym;
|
||||
}
|
||||
|
||||
// This method merges all the categories (sharing a base class) into a single
|
||||
// category.
|
||||
void ObjcCategoryMerger::mergeCategoriesIntoSingleCategory(
|
||||
std::vector<InfoInputCategory> &categories) {
|
||||
assert(categories.size() > 1 && "Expected at least 2 categories");
|
||||
|
||||
ClassExtensionInfo extInfo(catLayout);
|
||||
|
||||
for (auto &catInfo : categories)
|
||||
parseCatInfoToExtInfo(catInfo, extInfo);
|
||||
|
||||
Defined *newCatDef = emitCategory(extInfo);
|
||||
assert(newCatDef && "Failed to create a new category");
|
||||
|
||||
for (auto &catInfo : categories)
|
||||
catInfo.wasMerged = true;
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,
|
||||
const Symbol *refTo,
|
||||
uint32_t offset,
|
||||
const Reloc &relocTemplate) {
|
||||
Reloc r = relocTemplate;
|
||||
r.offset = offset;
|
||||
r.addend = 0;
|
||||
r.referent = const_cast<Symbol *>(refTo);
|
||||
refFrom->isec->relocs.push_back(r);
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::collectAndValidateCategoriesData() {
|
||||
for (InputSection *sec : allInputSections) {
|
||||
if (sec->getName() != section_names::objcCatList)
|
||||
continue;
|
||||
ConcatInputSection *catListCisec = dyn_cast<ConcatInputSection>(sec);
|
||||
assert(catListCisec &&
|
||||
"__objc_catList InputSection is not a ConcatInputSection");
|
||||
|
||||
for (uint32_t off = 0; off < catListCisec->getSize();
|
||||
off += target->wordSize) {
|
||||
Defined *categorySym = tryGetDefinedAtIsecOffset(catListCisec, off);
|
||||
assert(categorySym &&
|
||||
"Failed to get a valid cateogry at __objc_catlit offset");
|
||||
|
||||
// We only support ObjC categories (no swift + @objc)
|
||||
// TODO: Support swift + @objc categories also
|
||||
if (!categorySym->getName().starts_with(objc::symbol_names::category))
|
||||
continue;
|
||||
|
||||
auto *catBodyIsec = dyn_cast<ConcatInputSection>(categorySym->isec);
|
||||
assert(catBodyIsec &&
|
||||
"Category data section is not an ConcatInputSection");
|
||||
|
||||
// Check that the category has a reloc at 'klassOffset' (which is
|
||||
// a pointer to the class symbol)
|
||||
|
||||
Symbol *classSym =
|
||||
tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);
|
||||
assert(classSym && "Category does not have a valid base class");
|
||||
|
||||
InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off};
|
||||
categoryMap[classSym].push_back(catInputInfo);
|
||||
|
||||
collectCategoryWriterInfoFromCategory(catInputInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In the input we have multiple __objc_catlist InputSection, each of which may
|
||||
// contain links to multiple categories. Of these categories, we will merge (and
|
||||
// erase) only some. There will be some categories that will remain untouched
|
||||
// (not erased). For these not erased categories, we generate new __objc_catlist
|
||||
// entries since the parent __objc_catlist entry will be erased
|
||||
void ObjcCategoryMerger::generateCatListForNonErasedCategories(
|
||||
const std::map<ConcatInputSection *, std::set<uint64_t>>
|
||||
catListToErasedOffsets) {
|
||||
|
||||
// Go through all offsets of all __objc_catlist's that we process and if there
|
||||
// are categories that we didn't process - generate a new __objc_catlist for
|
||||
// each.
|
||||
for (auto &mapEntry : catListToErasedOffsets) {
|
||||
ConcatInputSection *catListIsec = mapEntry.first;
|
||||
for (uint32_t catListIsecOffset = 0;
|
||||
catListIsecOffset < catListIsec->data.size();
|
||||
catListIsecOffset += target->wordSize) {
|
||||
// This slot was erased, we can just skip it
|
||||
if (mapEntry.second.count(catListIsecOffset))
|
||||
continue;
|
||||
|
||||
Defined *nonErasedCatBody =
|
||||
tryGetDefinedAtIsecOffset(catListIsec, catListIsecOffset);
|
||||
assert(nonErasedCatBody && "Failed to relocate non-deleted category");
|
||||
|
||||
// Allocate data for the new __objc_catlist slot
|
||||
auto bodyData = newSectionData(target->wordSize);
|
||||
|
||||
// We mark the __objc_catlist slot as belonging to the same file as the
|
||||
// category
|
||||
ObjFile *objFile = dyn_cast<ObjFile>(nonErasedCatBody->getFile());
|
||||
|
||||
ConcatInputSection *listSec = make<ConcatInputSection>(
|
||||
*infoCategoryWriter.catListInfo.inputSection, bodyData,
|
||||
infoCategoryWriter.catListInfo.align);
|
||||
listSec->parent = infoCategoryWriter.catListInfo.outputSection;
|
||||
listSec->live = true;
|
||||
allInputSections.push_back(listSec);
|
||||
|
||||
std::string slotSymName = "<__objc_catlist slot for category ";
|
||||
slotSymName += nonErasedCatBody->getName();
|
||||
slotSymName += ">";
|
||||
|
||||
Defined *catListSlotSym = make<Defined>(
|
||||
newStringData(slotSymName.c_str()), /*file=*/objFile, listSec,
|
||||
/*value=*/0, bodyData.size(),
|
||||
/*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,
|
||||
/*includeInSymtab=*/false, /*isReferencedDynamically=*/false,
|
||||
/*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);
|
||||
|
||||
catListSlotSym->used = true;
|
||||
objFile->symbols.push_back(catListSlotSym);
|
||||
|
||||
// Now link the category body into the newly created slot
|
||||
createSymbolReference(catListSlotSym, nonErasedCatBody, 0,
|
||||
infoCategoryWriter.catListInfo.relocTemplate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::eraseISec(ConcatInputSection *isec) {
|
||||
isec->live = false;
|
||||
for (auto &sym : isec->symbols)
|
||||
sym->used = false;
|
||||
}
|
||||
|
||||
// This fully erases the merged categories, including their body, their names,
|
||||
// their method/protocol/prop lists and the __objc_catlist entries that link to
|
||||
// them.
|
||||
void ObjcCategoryMerger::eraseMergedCategories() {
|
||||
// Map of InputSection to a set of offsets of the categories that were merged
|
||||
std::map<ConcatInputSection *, std::set<uint64_t>> catListToErasedOffsets;
|
||||
|
||||
for (auto &mapEntry : categoryMap) {
|
||||
for (InfoInputCategory &catInfo : mapEntry.second) {
|
||||
if (catInfo.wasMerged) {
|
||||
eraseISec(catInfo.catListIsec);
|
||||
catListToErasedOffsets[catInfo.catListIsec].insert(
|
||||
catInfo.offCatListIsec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there were categories that we did not erase, we need to generate a new
|
||||
// __objc_catList that contains only the un-merged categories, and get rid of
|
||||
// the references to the ones we merged.
|
||||
generateCatListForNonErasedCategories(catListToErasedOffsets);
|
||||
|
||||
// Erase the old method lists & names of the categories that were merged
|
||||
for (auto &mapEntry : categoryMap) {
|
||||
for (InfoInputCategory &catInfo : mapEntry.second) {
|
||||
if (!catInfo.wasMerged)
|
||||
continue;
|
||||
|
||||
eraseISec(catInfo.catBodyIsec);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.instanceMethodsOffset);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.classMethodsOffset);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.protocolsOffset);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.classPropsOffset);
|
||||
tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,
|
||||
catLayout.instancePropsOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::doMerge() {
|
||||
collectAndValidateCategoriesData();
|
||||
|
||||
for (auto &entry : categoryMap)
|
||||
if (entry.second.size() > 1)
|
||||
// Merge all categories into a new, single category
|
||||
mergeCategoriesIntoSingleCategory(entry.second);
|
||||
|
||||
// Erase all categories that were merged
|
||||
eraseMergedCategories();
|
||||
}
|
||||
|
||||
void ObjcCategoryMerger::doCleanup() { generatedSectionData.clear(); }
|
||||
|
||||
StringRef ObjcCategoryMerger::newStringData(const char *str) {
|
||||
uint32_t len = strlen(str);
|
||||
auto &data = newSectionData(len + 1);
|
||||
char *strData = reinterpret_cast<char *>(data.data());
|
||||
strncpy(strData, str, len);
|
||||
return StringRef(strData, len);
|
||||
}
|
||||
|
||||
SmallVector<uint8_t> &ObjcCategoryMerger::newSectionData(uint32_t size) {
|
||||
generatedSectionData.push_back(SmallVector<uint8_t>(size, 0));
|
||||
return generatedSectionData.back();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void objc::mergeCategories() {
|
||||
TimeTraceScope timeScope("ObjcCategoryMerger");
|
||||
|
||||
ObjcCategoryMerger merger(inputSections);
|
||||
merger.doMerge();
|
||||
}
|
||||
|
||||
void objc::doCleanup() { ObjcCategoryMerger::doCleanup(); }
|
||||
|
@ -17,26 +17,14 @@ namespace objc {
|
||||
|
||||
namespace symbol_names {
|
||||
constexpr const char klass[] = "_OBJC_CLASS_$_";
|
||||
constexpr const char klassPropList[] = "__OBJC_$_CLASS_PROP_LIST_";
|
||||
|
||||
constexpr const char metaclass[] = "_OBJC_METACLASS_$_";
|
||||
constexpr const char ehtype[] = "_OBJC_EHTYPE_$_";
|
||||
constexpr const char ivar[] = "_OBJC_IVAR_$_";
|
||||
constexpr const char listProprieties[] = "__OBJC_$_PROP_LIST_";
|
||||
|
||||
constexpr const char category[] = "__OBJC_$_CATEGORY_";
|
||||
constexpr const char categoryInstanceMethods[] =
|
||||
"__OBJC_$_CATEGORY_INSTANCE_METHODS_";
|
||||
constexpr const char categoryClassMethods[] =
|
||||
"__OBJC_$_CATEGORY_CLASS_METHODS_";
|
||||
constexpr const char categoryProtocols[] = "__OBJC_CATEGORY_PROTOCOLS_$_";
|
||||
} // namespace symbol_names
|
||||
|
||||
// Check for duplicate method names within related categories / classes.
|
||||
void checkCategories();
|
||||
void mergeCategories();
|
||||
|
||||
void doCleanup();
|
||||
} // namespace objc
|
||||
|
||||
bool hasObjCSection(llvm::MemoryBufferRef);
|
||||
|
@ -129,12 +129,6 @@ def strict_auto_link : Flag<["--"], "strict-auto-link">,
|
||||
def check_category_conflicts : Flag<["--"], "check-category-conflicts">,
|
||||
HelpText<"Check for conflicts between category & class methods">,
|
||||
Group<grp_lld>;
|
||||
def objc_category_merging : Flag<["-"], "objc_category_merging">,
|
||||
HelpText<"Merge Objective-C categories that share the same base class">,
|
||||
Group<grp_lld>;
|
||||
def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
|
||||
HelpText<"Do not merge Objective-C categories">,
|
||||
Group<grp_lld>;
|
||||
def lto_debug_pass_manager: Flag<["--"], "lto-debug-pass-manager">,
|
||||
HelpText<"Debug new pass manager">, Group<grp_lld>;
|
||||
def cs_profile_generate: Flag<["--"], "cs-profile-generate">,
|
||||
@ -972,6 +966,10 @@ def interposable_list : Separate<["-"], "interposable_list">,
|
||||
def no_function_starts : Flag<["-"], "no_function_starts">,
|
||||
HelpText<"Do not create table of function start addresses">,
|
||||
Group<grp_rare>;
|
||||
def no_objc_category_merging : Flag<["-"], "no_objc_category_merging">,
|
||||
HelpText<"Do not merge Objective-C categories into their classes">,
|
||||
Flags<[HelpHidden]>,
|
||||
Group<grp_rare>;
|
||||
def object_path_lto : Separate<["-"], "object_path_lto">,
|
||||
MetaVarName<"<path>">,
|
||||
HelpText<"Retain any temporary mach-o file in <path> that would otherwise be deleted during LTO">,
|
||||
|
@ -1,762 +0,0 @@
|
||||
# REQUIRES: aarch64
|
||||
# RUN: rm -rf %t; split-file %s %t && cd %t
|
||||
|
||||
## Create a dylib to link against(a64_file1.dylib) and merge categories in the main binary (file2_merge_a64.exe)
|
||||
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_file1.o a64_file1.s
|
||||
# RUN: %lld -arch arm64 a64_file1.o -o a64_file1.dylib -dylib
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_file2.o a64_file2.s
|
||||
# RUN: %lld -arch arm64 -o a64_file2_no_merge.exe a64_file1.dylib a64_file2.o
|
||||
# RUN: %lld -arch arm64 -o a64_file2_merge.exe -objc_category_merging a64_file1.dylib a64_file2.o
|
||||
|
||||
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_no_merge.exe | FileCheck %s --check-prefixes=NO_MERGE_CATS
|
||||
# RUN: llvm-objdump --objc-meta-data --macho a64_file2_merge.exe | FileCheck %s --check-prefixes=MERGE_CATS
|
||||
|
||||
|
||||
MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_(Category02|Category03)
|
||||
MERGE_CATS-NEXT: name {{.*}} Category02|Category03
|
||||
MERGE_CATS: instanceMethods
|
||||
MERGE_CATS-NEXT: entsize 24
|
||||
MERGE_CATS-NEXT: count 4
|
||||
MERGE_CATS-NEXT: name {{.*}} class02InstanceMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) class02InstanceMethod]
|
||||
MERGE_CATS-NEXT: name {{.*}} myProtocol02Method
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) myProtocol02Method]
|
||||
MERGE_CATS-NEXT: name {{.*}} class03InstanceMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category03) class03InstanceMethod]
|
||||
MERGE_CATS-NEXT: name {{.*}} myProtocol03Method
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category03) myProtocol03Method]
|
||||
MERGE_CATS-NEXT: classMethods {{.*}}
|
||||
MERGE_CATS-NEXT: entsize 24
|
||||
MERGE_CATS-NEXT: count 4
|
||||
MERGE_CATS-NEXT: name {{.*}} class02ClassMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp +[MyBaseClass(Category02) class02ClassMethod]
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
|
||||
MERGE_CATS-NEXT: types {{.*}} i16@0:8
|
||||
MERGE_CATS-NEXT: imp +[MyBaseClass(Category02) MyProtocol02Prop]
|
||||
MERGE_CATS-NEXT: name {{.*}} class03ClassMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp +[MyBaseClass(Category03) class03ClassMethod]
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
|
||||
MERGE_CATS-NEXT: types {{.*}} i16@0:8
|
||||
MERGE_CATS-NEXT: imp +[MyBaseClass(Category03) MyProtocol03Prop]
|
||||
MERGE_CATS-NEXT: protocols
|
||||
MERGE_CATS-NEXT: count 2
|
||||
MERGE_CATS-NEXT: list[0] {{.*}} (struct protocol_t *)
|
||||
MERGE_CATS-NEXT: isa 0x0
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol02
|
||||
MERGE_CATS-NEXT: protocols 0x0
|
||||
MERGE_CATS-NEXT: instanceMethods
|
||||
MERGE_CATS-NEXT: entsize 24
|
||||
MERGE_CATS-NEXT: count 2
|
||||
MERGE_CATS-NEXT: name {{.*}} myProtocol02Method
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp 0x0
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
|
||||
MERGE_CATS-NEXT: types {{.*}} i16@0:8
|
||||
MERGE_CATS-NEXT: imp 0x0
|
||||
MERGE_CATS-NEXT: classMethods
|
||||
MERGE_CATS-NEXT: optionalInstanceMethods 0x0
|
||||
MERGE_CATS-NEXT: optionalClassMethods 0x0
|
||||
MERGE_CATS-NEXT: instanceProperties {{.*}}
|
||||
MERGE_CATS-NEXT: list[1] {{.*}}
|
||||
MERGE_CATS-NEXT: isa 0x0
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol03
|
||||
MERGE_CATS-NEXT: protocols 0x0
|
||||
MERGE_CATS-NEXT: instanceMethods
|
||||
MERGE_CATS-NEXT: entsize 24
|
||||
MERGE_CATS-NEXT: count 2
|
||||
MERGE_CATS-NEXT: name {{.*}} myProtocol03Method
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp 0x0
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
|
||||
MERGE_CATS-NEXT: types {{.*}} i16@0:8
|
||||
MERGE_CATS-NEXT: imp 0x0
|
||||
MERGE_CATS-NEXT: classMethods 0x0
|
||||
MERGE_CATS-NEXT: optionalInstanceMethods 0x0
|
||||
MERGE_CATS-NEXT: optionalClassMethods 0x0
|
||||
MERGE_CATS-NEXT: instanceProperties {{.*}}
|
||||
MERGE_CATS-NEXT: instanceProperties
|
||||
MERGE_CATS-NEXT: entsize 16
|
||||
MERGE_CATS-NEXT: count 2
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol02Prop
|
||||
MERGE_CATS-NEXT: attributes {{.*}} Ti,R,D
|
||||
MERGE_CATS-NEXT: name {{.*}} MyProtocol03Prop
|
||||
MERGE_CATS-NEXT: attributes {{.*}} Ti,R,D
|
||||
|
||||
|
||||
NO_MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_(Category02|Category03)
|
||||
NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
|
||||
NO_MERGE_CATS: instanceMethods
|
||||
NO_MERGE_CATS-NEXT: 24
|
||||
NO_MERGE_CATS-NEXT: 2
|
||||
NO_MERGE_CATS: classMethods
|
||||
NO_MERGE_CATS-NEXT: 24
|
||||
NO_MERGE_CATS-NEXT: 2
|
||||
|
||||
|
||||
#--- a64_file1.s
|
||||
|
||||
## @protocol MyProtocol01
|
||||
## - (void)myProtocol01Method;
|
||||
## @property (nonatomic) int MyProtocol01Prop;
|
||||
## @end
|
||||
##
|
||||
## __attribute__((objc_root_class))
|
||||
## @interface MyBaseClass<MyProtocol01>
|
||||
## - (void)baseInstanceMethod;
|
||||
## - (void)myProtocol01Method;
|
||||
## + (void)baseClassMethod;
|
||||
## @end
|
||||
##
|
||||
## @implementation MyBaseClass
|
||||
## @synthesize MyProtocol01Prop;
|
||||
## - (void)baseInstanceMethod {}
|
||||
## - (void)myProtocol01Method {}
|
||||
## + (void)baseClassMethod {}
|
||||
## @end
|
||||
##
|
||||
## void *_objc_empty_cache;
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass baseInstanceMethod]
|
||||
"-[MyBaseClass baseInstanceMethod]": ; @"\01-[MyBaseClass baseInstanceMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass myProtocol01Method]
|
||||
"-[MyBaseClass myProtocol01Method]": ; @"\01-[MyBaseClass myProtocol01Method]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function +[MyBaseClass baseClassMethod]
|
||||
"+[MyBaseClass baseClassMethod]": ; @"\01+[MyBaseClass baseClassMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass MyProtocol01Prop]
|
||||
"-[MyBaseClass MyProtocol01Prop]": ; @"\01-[MyBaseClass MyProtocol01Prop]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
Lloh0:
|
||||
adrp x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop@PAGE
|
||||
Lloh1:
|
||||
ldrsw x8, [x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop@PAGEOFF]
|
||||
ldr w0, [x0, x8]
|
||||
ret
|
||||
.loh AdrpLdr Lloh0, Lloh1
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass setMyProtocol01Prop:]
|
||||
"-[MyBaseClass setMyProtocol01Prop:]": ; @"\01-[MyBaseClass setMyProtocol01Prop:]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
Lloh2:
|
||||
adrp x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop@PAGE
|
||||
Lloh3:
|
||||
ldrsw x8, [x8, _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop@PAGEOFF]
|
||||
str w2, [x0, x8]
|
||||
ret
|
||||
.loh AdrpLdr Lloh2, Lloh3
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.private_extern _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop ; @"OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop"
|
||||
.section __DATA,__objc_ivar
|
||||
.globl _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop
|
||||
.p2align 2, 0x0
|
||||
_OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop:
|
||||
.long 0 ; 0x0
|
||||
.section __DATA,__objc_data
|
||||
.globl _OBJC_CLASS_$_MyBaseClass ; @"OBJC_CLASS_$_MyBaseClass"
|
||||
.p2align 3, 0x0
|
||||
_OBJC_CLASS_$_MyBaseClass:
|
||||
.quad _OBJC_METACLASS_$_MyBaseClass
|
||||
.quad 0
|
||||
.quad __objc_empty_cache
|
||||
.quad 0
|
||||
.quad __OBJC_CLASS_RO_$_MyBaseClass
|
||||
.globl _OBJC_METACLASS_$_MyBaseClass ; @"OBJC_METACLASS_$_MyBaseClass"
|
||||
.p2align 3, 0x0
|
||||
_OBJC_METACLASS_$_MyBaseClass:
|
||||
.quad _OBJC_METACLASS_$_MyBaseClass
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.quad __objc_empty_cache
|
||||
.quad 0
|
||||
.quad __OBJC_METACLASS_RO_$_MyBaseClass
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
|
||||
.asciz "MyBaseClass"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
|
||||
.asciz "baseClassMethod"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
|
||||
.asciz "v16@0:8"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CLASS_METHODS_MyBaseClass"
|
||||
__OBJC_$_CLASS_METHODS_MyBaseClass:
|
||||
.long 24 ; 0x18
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_METH_VAR_NAME_
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "+[MyBaseClass baseClassMethod]"
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.1: ; @OBJC_CLASS_NAME_.1
|
||||
.asciz "MyProtocol01"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
|
||||
.asciz "myProtocol01Method"
|
||||
l_OBJC_METH_VAR_NAME_.3: ; @OBJC_METH_VAR_NAME_.3
|
||||
.asciz "MyProtocol01Prop"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_.4: ; @OBJC_METH_VAR_TYPE_.4
|
||||
.asciz "i16@0:8"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.5: ; @OBJC_METH_VAR_NAME_.5
|
||||
.asciz "setMyProtocol01Prop:"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_.6: ; @OBJC_METH_VAR_TYPE_.6
|
||||
.asciz "v20@0:8i16"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01"
|
||||
__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01:
|
||||
.long 24 ; 0x18
|
||||
.long 3 ; 0x3
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad 0
|
||||
.quad l_OBJC_METH_VAR_NAME_.3
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad 0
|
||||
.quad l_OBJC_METH_VAR_NAME_.5
|
||||
.quad l_OBJC_METH_VAR_TYPE_.6
|
||||
.quad 0
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_PROP_NAME_ATTR_: ; @OBJC_PROP_NAME_ATTR_
|
||||
.asciz "MyProtocol01Prop"
|
||||
l_OBJC_PROP_NAME_ATTR_.7: ; @OBJC_PROP_NAME_ATTR_.7
|
||||
.asciz "Ti,N"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol01"
|
||||
__OBJC_$_PROP_LIST_MyProtocol01:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.7
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01"
|
||||
__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01:
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad l_OBJC_METH_VAR_TYPE_.6
|
||||
.private_extern __OBJC_PROTOCOL_$_MyProtocol01 ; @"_OBJC_PROTOCOL_$_MyProtocol01"
|
||||
.section __DATA,__data
|
||||
.globl __OBJC_PROTOCOL_$_MyProtocol01
|
||||
.weak_definition __OBJC_PROTOCOL_$_MyProtocol01
|
||||
.p2align 3, 0x0
|
||||
__OBJC_PROTOCOL_$_MyProtocol01:
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_.1
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol01
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROP_LIST_MyProtocol01
|
||||
.long 96 ; 0x60
|
||||
.long 0 ; 0x0
|
||||
.quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol01
|
||||
.quad 0
|
||||
.quad 0
|
||||
.private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol01 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol01"
|
||||
.section __DATA,__objc_protolist,coalesced,no_dead_strip
|
||||
.globl __OBJC_LABEL_PROTOCOL_$_MyProtocol01
|
||||
.weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol01
|
||||
.p2align 3, 0x0
|
||||
__OBJC_LABEL_PROTOCOL_$_MyProtocol01:
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol01
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_CLASS_PROTOCOLS_$_MyBaseClass"
|
||||
__OBJC_CLASS_PROTOCOLS_$_MyBaseClass:
|
||||
.quad 1 ; 0x1
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol01
|
||||
.quad 0
|
||||
.p2align 3, 0x0 ; @"_OBJC_METACLASS_RO_$_MyBaseClass"
|
||||
__OBJC_METACLASS_RO_$_MyBaseClass:
|
||||
.long 3 ; 0x3
|
||||
.long 40 ; 0x28
|
||||
.long 40 ; 0x28
|
||||
.space 4
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_
|
||||
.quad __OBJC_$_CLASS_METHODS_MyBaseClass
|
||||
.quad __OBJC_CLASS_PROTOCOLS_$_MyBaseClass
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.8: ; @OBJC_METH_VAR_NAME_.8
|
||||
.asciz "baseInstanceMethod"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_INSTANCE_METHODS_MyBaseClass"
|
||||
__OBJC_$_INSTANCE_METHODS_MyBaseClass:
|
||||
.long 24 ; 0x18
|
||||
.long 4 ; 0x4
|
||||
.quad l_OBJC_METH_VAR_NAME_.8
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass baseInstanceMethod]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass myProtocol01Method]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.3
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad "-[MyBaseClass MyProtocol01Prop]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.5
|
||||
.quad l_OBJC_METH_VAR_TYPE_.6
|
||||
.quad "-[MyBaseClass setMyProtocol01Prop:]"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_.9: ; @OBJC_METH_VAR_TYPE_.9
|
||||
.asciz "i"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_INSTANCE_VARIABLES_MyBaseClass"
|
||||
__OBJC_$_INSTANCE_VARIABLES_MyBaseClass:
|
||||
.long 32 ; 0x20
|
||||
.long 1 ; 0x1
|
||||
.quad _OBJC_IVAR_$_MyBaseClass.MyProtocol01Prop
|
||||
.quad l_OBJC_METH_VAR_NAME_.3
|
||||
.quad l_OBJC_METH_VAR_TYPE_.9
|
||||
.long 2 ; 0x2
|
||||
.long 4 ; 0x4
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_PROP_NAME_ATTR_.10: ; @OBJC_PROP_NAME_ATTR_.10
|
||||
.asciz "Ti,N,VMyProtocol01Prop"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass"
|
||||
__OBJC_$_PROP_LIST_MyBaseClass:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.10
|
||||
.p2align 3, 0x0 ; @"_OBJC_CLASS_RO_$_MyBaseClass"
|
||||
__OBJC_CLASS_RO_$_MyBaseClass:
|
||||
.long 2 ; 0x2
|
||||
.long 0 ; 0x0
|
||||
.long 4 ; 0x4
|
||||
.space 4
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_
|
||||
.quad __OBJC_$_INSTANCE_METHODS_MyBaseClass
|
||||
.quad __OBJC_CLASS_PROTOCOLS_$_MyBaseClass
|
||||
.quad __OBJC_$_INSTANCE_VARIABLES_MyBaseClass
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROP_LIST_MyBaseClass
|
||||
.globl __objc_empty_cache ; @_objc_empty_cache
|
||||
.zerofill __DATA,__common,__objc_empty_cache,8,3
|
||||
.section __DATA,__objc_classlist,regular,no_dead_strip
|
||||
.p2align 3, 0x0 ; @"OBJC_LABEL_CLASS_$"
|
||||
l_OBJC_LABEL_CLASS_$:
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol01
|
||||
.no_dead_strip __OBJC_PROTOCOL_$_MyProtocol01
|
||||
.section __DATA,__objc_imageinfo,regular,no_dead_strip
|
||||
L_OBJC_IMAGE_INFO:
|
||||
.long 0
|
||||
.long 96
|
||||
.subsections_via_symbols
|
||||
|
||||
|
||||
#--- a64_file2.s
|
||||
|
||||
## @protocol MyProtocol01
|
||||
## - (void)myProtocol01Method;
|
||||
## @end
|
||||
##
|
||||
## @protocol MyProtocol02
|
||||
## - (void)myProtocol02Method;
|
||||
## @property(readonly) int MyProtocol02Prop;
|
||||
## @end
|
||||
##
|
||||
## @protocol MyProtocol03
|
||||
## - (void)myProtocol03Method;
|
||||
## @property(readonly) int MyProtocol03Prop;
|
||||
## @end
|
||||
##
|
||||
##
|
||||
## __attribute__((objc_root_class))
|
||||
## @interface MyBaseClass<MyProtocol01>
|
||||
## - (void)baseInstanceMethod;
|
||||
## - (void)myProtocol01Method;
|
||||
## + (void)baseClassMethod;
|
||||
## @end
|
||||
##
|
||||
##
|
||||
##
|
||||
## @interface MyBaseClass(Category02)<MyProtocol02>
|
||||
## - (void)class02InstanceMethod;
|
||||
## - (void)myProtocol02Method;
|
||||
## + (void)class02ClassMethod;
|
||||
## + (int)MyProtocol02Prop;
|
||||
## @end
|
||||
##
|
||||
## @implementation MyBaseClass(Category02)
|
||||
## - (void)class02InstanceMethod {}
|
||||
## - (void)myProtocol02Method {}
|
||||
## + (void)class02ClassMethod {}
|
||||
## + (int)MyProtocol02Prop { return 0;}
|
||||
## @dynamic MyProtocol02Prop;
|
||||
## @end
|
||||
##
|
||||
## @interface MyBaseClass(Category03)<MyProtocol03>
|
||||
## - (void)class03InstanceMethod;
|
||||
## - (void)myProtocol03Method;
|
||||
## + (void)class03ClassMethod;
|
||||
## + (int)MyProtocol03Prop;
|
||||
## @end
|
||||
##
|
||||
## @implementation MyBaseClass(Category03)
|
||||
## - (void)class03InstanceMethod {}
|
||||
## - (void)myProtocol03Method {}
|
||||
## + (void)class03ClassMethod {}
|
||||
## + (int)MyProtocol03Prop { return 0;}
|
||||
## @dynamic MyProtocol03Prop;
|
||||
## @end
|
||||
##
|
||||
## int main() {
|
||||
## return 0;
|
||||
## }
|
||||
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category02) class02InstanceMethod]
|
||||
"-[MyBaseClass(Category02) class02InstanceMethod]": ; @"\01-[MyBaseClass(Category02) class02InstanceMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category02) myProtocol02Method]
|
||||
"-[MyBaseClass(Category02) myProtocol02Method]": ; @"\01-[MyBaseClass(Category02) myProtocol02Method]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function +[MyBaseClass(Category02) class02ClassMethod]
|
||||
"+[MyBaseClass(Category02) class02ClassMethod]": ; @"\01+[MyBaseClass(Category02) class02ClassMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function +[MyBaseClass(Category02) MyProtocol02Prop]
|
||||
"+[MyBaseClass(Category02) MyProtocol02Prop]": ; @"\01+[MyBaseClass(Category02) MyProtocol02Prop]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
b _OUTLINED_FUNCTION_0
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category03) class03InstanceMethod]
|
||||
"-[MyBaseClass(Category03) class03InstanceMethod]": ; @"\01-[MyBaseClass(Category03) class03InstanceMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category03) myProtocol03Method]
|
||||
"-[MyBaseClass(Category03) myProtocol03Method]": ; @"\01-[MyBaseClass(Category03) myProtocol03Method]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function +[MyBaseClass(Category03) class03ClassMethod]
|
||||
"+[MyBaseClass(Category03) class03ClassMethod]": ; @"\01+[MyBaseClass(Category03) class03ClassMethod]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function +[MyBaseClass(Category03) MyProtocol03Prop]
|
||||
"+[MyBaseClass(Category03) MyProtocol03Prop]": ; @"\01+[MyBaseClass(Category03) MyProtocol03Prop]"
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
b _OUTLINED_FUNCTION_0
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.globl _main ; -- Begin function main
|
||||
.p2align 2
|
||||
_main: ; @main
|
||||
.cfi_startproc
|
||||
; %bb.0: ; %entry
|
||||
b _OUTLINED_FUNCTION_0
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function OUTLINED_FUNCTION_0
|
||||
_OUTLINED_FUNCTION_0: ; @OUTLINED_FUNCTION_0 Tail Call
|
||||
.cfi_startproc
|
||||
; %bb.0:
|
||||
mov w0, #0
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
|
||||
.asciz "Category02"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
|
||||
.asciz "class02InstanceMethod"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
|
||||
.asciz "v16@0:8"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.1: ; @OBJC_METH_VAR_NAME_.1
|
||||
.asciz "myProtocol02Method"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02"
|
||||
__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category02) class02InstanceMethod]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.1
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category02) myProtocol02Method]"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
|
||||
.asciz "class02ClassMethod"
|
||||
l_OBJC_METH_VAR_NAME_.3: ; @OBJC_METH_VAR_NAME_.3
|
||||
.asciz "MyProtocol02Prop"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_.4: ; @OBJC_METH_VAR_TYPE_.4
|
||||
.asciz "i16@0:8"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02"
|
||||
__OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "+[MyBaseClass(Category02) class02ClassMethod]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.3
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad "+[MyBaseClass(Category02) MyProtocol02Prop]"
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.5: ; @OBJC_CLASS_NAME_.5
|
||||
.asciz "MyProtocol02"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02"
|
||||
__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_.1
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad 0
|
||||
.quad l_OBJC_METH_VAR_NAME_.3
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad 0
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_PROP_NAME_ATTR_: ; @OBJC_PROP_NAME_ATTR_
|
||||
.asciz "MyProtocol02Prop"
|
||||
l_OBJC_PROP_NAME_ATTR_.6: ; @OBJC_PROP_NAME_ATTR_.6
|
||||
.asciz "Ti,R"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol02"
|
||||
__OBJC_$_PROP_LIST_MyProtocol02:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.6
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02"
|
||||
__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02:
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.private_extern __OBJC_PROTOCOL_$_MyProtocol02 ; @"_OBJC_PROTOCOL_$_MyProtocol02"
|
||||
.section __DATA,__data
|
||||
.globl __OBJC_PROTOCOL_$_MyProtocol02
|
||||
.weak_definition __OBJC_PROTOCOL_$_MyProtocol02
|
||||
.p2align 3, 0x0
|
||||
__OBJC_PROTOCOL_$_MyProtocol02:
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_.5
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol02
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROP_LIST_MyProtocol02
|
||||
.long 96 ; 0x60
|
||||
.long 0 ; 0x0
|
||||
.quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol02
|
||||
.quad 0
|
||||
.quad 0
|
||||
.private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol02 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol02"
|
||||
.section __DATA,__objc_protolist,coalesced,no_dead_strip
|
||||
.globl __OBJC_LABEL_PROTOCOL_$_MyProtocol02
|
||||
.weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol02
|
||||
.p2align 3, 0x0
|
||||
__OBJC_LABEL_PROTOCOL_$_MyProtocol02:
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol02
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02"
|
||||
__OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02:
|
||||
.quad 1 ; 0x1
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol02
|
||||
.quad 0
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_PROP_NAME_ATTR_.7: ; @OBJC_PROP_NAME_ATTR_.7
|
||||
.asciz "Ti,R,D"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass_$_Category02"
|
||||
__OBJC_$_PROP_LIST_MyBaseClass_$_Category02:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.7
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category02"
|
||||
__OBJC_$_CATEGORY_MyBaseClass_$_Category02:
|
||||
.quad l_OBJC_CLASS_NAME_
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02
|
||||
.quad __OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category02
|
||||
.quad __OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category02
|
||||
.quad __OBJC_$_PROP_LIST_MyBaseClass_$_Category02
|
||||
.quad 0
|
||||
.long 64 ; 0x40
|
||||
.space 4
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.8: ; @OBJC_CLASS_NAME_.8
|
||||
.asciz "Category03"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.9: ; @OBJC_METH_VAR_NAME_.9
|
||||
.asciz "class03InstanceMethod"
|
||||
l_OBJC_METH_VAR_NAME_.10: ; @OBJC_METH_VAR_NAME_.10
|
||||
.asciz "myProtocol03Method"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03"
|
||||
__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_.9
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category03) class03InstanceMethod]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.10
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category03) myProtocol03Method]"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.11: ; @OBJC_METH_VAR_NAME_.11
|
||||
.asciz "class03ClassMethod"
|
||||
l_OBJC_METH_VAR_NAME_.12: ; @OBJC_METH_VAR_NAME_.12
|
||||
.asciz "MyProtocol03Prop"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03"
|
||||
__OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_.11
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "+[MyBaseClass(Category03) class03ClassMethod]"
|
||||
.quad l_OBJC_METH_VAR_NAME_.12
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad "+[MyBaseClass(Category03) MyProtocol03Prop]"
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.13: ; @OBJC_CLASS_NAME_.13
|
||||
.asciz "MyProtocol03"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03"
|
||||
__OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03:
|
||||
.long 24 ; 0x18
|
||||
.long 2 ; 0x2
|
||||
.quad l_OBJC_METH_VAR_NAME_.10
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad 0
|
||||
.quad l_OBJC_METH_VAR_NAME_.12
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.quad 0
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_PROP_NAME_ATTR_.14: ; @OBJC_PROP_NAME_ATTR_.14
|
||||
.asciz "MyProtocol03Prop"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyProtocol03"
|
||||
__OBJC_$_PROP_LIST_MyProtocol03:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.14
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.6
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03"
|
||||
__OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03:
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad l_OBJC_METH_VAR_TYPE_.4
|
||||
.private_extern __OBJC_PROTOCOL_$_MyProtocol03 ; @"_OBJC_PROTOCOL_$_MyProtocol03"
|
||||
.section __DATA,__data
|
||||
.globl __OBJC_PROTOCOL_$_MyProtocol03
|
||||
.weak_definition __OBJC_PROTOCOL_$_MyProtocol03
|
||||
.p2align 3, 0x0
|
||||
__OBJC_PROTOCOL_$_MyProtocol03:
|
||||
.quad 0
|
||||
.quad l_OBJC_CLASS_NAME_.13
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROTOCOL_INSTANCE_METHODS_MyProtocol03
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad __OBJC_$_PROP_LIST_MyProtocol03
|
||||
.long 96 ; 0x60
|
||||
.long 0 ; 0x0
|
||||
.quad __OBJC_$_PROTOCOL_METHOD_TYPES_MyProtocol03
|
||||
.quad 0
|
||||
.quad 0
|
||||
.private_extern __OBJC_LABEL_PROTOCOL_$_MyProtocol03 ; @"_OBJC_LABEL_PROTOCOL_$_MyProtocol03"
|
||||
.section __DATA,__objc_protolist,coalesced,no_dead_strip
|
||||
.globl __OBJC_LABEL_PROTOCOL_$_MyProtocol03
|
||||
.weak_definition __OBJC_LABEL_PROTOCOL_$_MyProtocol03
|
||||
.p2align 3, 0x0
|
||||
__OBJC_LABEL_PROTOCOL_$_MyProtocol03:
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol03
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03"
|
||||
__OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03:
|
||||
.quad 1 ; 0x1
|
||||
.quad __OBJC_PROTOCOL_$_MyProtocol03
|
||||
.quad 0
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_PROP_LIST_MyBaseClass_$_Category03"
|
||||
__OBJC_$_PROP_LIST_MyBaseClass_$_Category03:
|
||||
.long 16 ; 0x10
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.14
|
||||
.quad l_OBJC_PROP_NAME_ATTR_.7
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category03"
|
||||
__OBJC_$_CATEGORY_MyBaseClass_$_Category03:
|
||||
.quad l_OBJC_CLASS_NAME_.8
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category03
|
||||
.quad __OBJC_$_CATEGORY_CLASS_METHODS_MyBaseClass_$_Category03
|
||||
.quad __OBJC_CATEGORY_PROTOCOLS_$_MyBaseClass_$_Category03
|
||||
.quad __OBJC_$_PROP_LIST_MyBaseClass_$_Category03
|
||||
.quad 0
|
||||
.long 64 ; 0x40
|
||||
.space 4
|
||||
.section __DATA,__objc_catlist,regular,no_dead_strip
|
||||
.p2align 3, 0x0 ; @"OBJC_LABEL_CATEGORY_$"
|
||||
l_OBJC_LABEL_CATEGORY_$:
|
||||
.quad __OBJC_$_CATEGORY_MyBaseClass_$_Category02
|
||||
.quad __OBJC_$_CATEGORY_MyBaseClass_$_Category03
|
||||
.no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol02
|
||||
.no_dead_strip __OBJC_LABEL_PROTOCOL_$_MyProtocol03
|
||||
.no_dead_strip __OBJC_PROTOCOL_$_MyProtocol02
|
||||
.no_dead_strip __OBJC_PROTOCOL_$_MyProtocol03
|
||||
.section __DATA,__objc_imageinfo,regular,no_dead_strip
|
||||
L_OBJC_IMAGE_INFO:
|
||||
.long 0
|
||||
.long 96
|
||||
.subsections_via_symbols
|
@ -1,155 +0,0 @@
|
||||
# REQUIRES: aarch64
|
||||
# RUN: rm -rf %t; split-file %s %t && cd %t
|
||||
|
||||
## Create a dylib with a fake base class to link against
|
||||
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_fakedylib.o a64_fakedylib.s
|
||||
# RUN: %lld -arch arm64 a64_fakedylib.o -o a64_fakedylib.dylib -dylib
|
||||
|
||||
## Create our main testing dylib - linking against the fake dylib above
|
||||
# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o merge_cat_minimal.o merge_cat_minimal.s
|
||||
# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_no_merge.dylib a64_fakedylib.dylib merge_cat_minimal.o
|
||||
# RUN: %lld -arch arm64 -dylib -o merge_cat_minimal_merge.dylib -objc_category_merging a64_fakedylib.dylib merge_cat_minimal.o
|
||||
|
||||
## Now verify that the flag caused category merging to happen appropriatelly
|
||||
# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_no_merge.dylib | FileCheck %s --check-prefixes=NO_MERGE_CATS
|
||||
# RUN: llvm-objdump --objc-meta-data --macho merge_cat_minimal_merge.dylib | FileCheck %s --check-prefixes=MERGE_CATS
|
||||
|
||||
#### Check merge categories enabled ###
|
||||
# Check that the original categories are not there
|
||||
MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category01
|
||||
MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
|
||||
|
||||
# Check that the merged cateogry is there, in the correct format
|
||||
MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_(Category01|Category02)
|
||||
MERGE_CATS-NEXT: name {{.*}} Category01|Category02
|
||||
MERGE_CATS: instanceMethods
|
||||
MERGE_CATS-NEXT: 24
|
||||
MERGE_CATS-NEXT: 2
|
||||
MERGE_CATS-NEXT: name {{.*}} cat01_InstanceMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category01) cat01_InstanceMethod]
|
||||
MERGE_CATS-NEXT: name {{.*}} cat02_InstanceMethod
|
||||
MERGE_CATS-NEXT: types {{.*}} v16@0:8
|
||||
MERGE_CATS-NEXT: imp -[MyBaseClass(Category02) cat02_InstanceMethod]
|
||||
MERGE_CATS-NEXT: classMethods 0x0
|
||||
MERGE_CATS-NEXT: protocols 0x0
|
||||
MERGE_CATS-NEXT: instanceProperties 0x0
|
||||
|
||||
#### Check merge categories disabled ###
|
||||
# Check that the merged category is not there
|
||||
NO_MERGE_CATS-NOT: __OBJC_$_CATEGORY_MyBaseClass_$_(Category01|Category02)
|
||||
|
||||
# Check that the original categories are there
|
||||
NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category01
|
||||
NO_MERGE_CATS: __OBJC_$_CATEGORY_MyBaseClass_$_Category02
|
||||
|
||||
|
||||
|
||||
#--- a64_fakedylib.s
|
||||
|
||||
.section __DATA,__objc_data
|
||||
.globl _OBJC_CLASS_$_MyBaseClass
|
||||
_OBJC_CLASS_$_MyBaseClass:
|
||||
.quad 0
|
||||
|
||||
#--- merge_cat_minimal.s
|
||||
|
||||
; ================== Generated from ObjC: ==================
|
||||
; __attribute__((objc_root_class))
|
||||
; @interface MyBaseClass
|
||||
; - (void)baseInstanceMethod;
|
||||
; @end
|
||||
;
|
||||
; @interface MyBaseClass(Category01)
|
||||
; - (void)cat01_InstanceMethod;
|
||||
; @end
|
||||
;
|
||||
; @implementation MyBaseClass(Category01)
|
||||
; - (void)cat01_InstanceMethod {}
|
||||
; @end
|
||||
;
|
||||
; @interface MyBaseClass(Category02)
|
||||
; - (void)cat02_InstanceMethod;
|
||||
; @end
|
||||
;
|
||||
; @implementation MyBaseClass(Category02)
|
||||
; - (void)cat02_InstanceMethod {}
|
||||
; @end
|
||||
; ================== Generated from ObjC: ==================
|
||||
|
||||
.section __TEXT,__text,regular,pure_instructions
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category01) cat01_InstanceMethod]
|
||||
"-[MyBaseClass(Category01) cat01_InstanceMethod]": ; @"\01-[MyBaseClass(Category01) cat01_InstanceMethod]"
|
||||
.cfi_startproc
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.p2align 2 ; -- Begin function -[MyBaseClass(Category02) cat02_InstanceMethod]
|
||||
"-[MyBaseClass(Category02) cat02_InstanceMethod]": ; @"\01-[MyBaseClass(Category02) cat02_InstanceMethod]"
|
||||
.cfi_startproc
|
||||
ret
|
||||
.cfi_endproc
|
||||
; -- End function
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_: ; @OBJC_CLASS_NAME_
|
||||
.asciz "Category01"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_: ; @OBJC_METH_VAR_NAME_
|
||||
.asciz "cat01_InstanceMethod"
|
||||
.section __TEXT,__objc_methtype,cstring_literals
|
||||
l_OBJC_METH_VAR_TYPE_: ; @OBJC_METH_VAR_TYPE_
|
||||
.asciz "v16@0:8"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01"
|
||||
__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01:
|
||||
.long 24 ; 0x18
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_METH_VAR_NAME_
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category01) cat01_InstanceMethod]"
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category01"
|
||||
__OBJC_$_CATEGORY_MyBaseClass_$_Category01:
|
||||
.quad l_OBJC_CLASS_NAME_
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category01
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.long 64 ; 0x40
|
||||
.space 4
|
||||
.section __TEXT,__objc_classname,cstring_literals
|
||||
l_OBJC_CLASS_NAME_.1: ; @OBJC_CLASS_NAME_.1
|
||||
.asciz "Category02"
|
||||
.section __TEXT,__objc_methname,cstring_literals
|
||||
l_OBJC_METH_VAR_NAME_.2: ; @OBJC_METH_VAR_NAME_.2
|
||||
.asciz "cat02_InstanceMethod"
|
||||
.section __DATA,__objc_const
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02"
|
||||
__OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02:
|
||||
.long 24 ; 0x18
|
||||
.long 1 ; 0x1
|
||||
.quad l_OBJC_METH_VAR_NAME_.2
|
||||
.quad l_OBJC_METH_VAR_TYPE_
|
||||
.quad "-[MyBaseClass(Category02) cat02_InstanceMethod]"
|
||||
.p2align 3, 0x0 ; @"_OBJC_$_CATEGORY_MyBaseClass_$_Category02"
|
||||
__OBJC_$_CATEGORY_MyBaseClass_$_Category02:
|
||||
.quad l_OBJC_CLASS_NAME_.1
|
||||
.quad _OBJC_CLASS_$_MyBaseClass
|
||||
.quad __OBJC_$_CATEGORY_INSTANCE_METHODS_MyBaseClass_$_Category02
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.quad 0
|
||||
.long 64 ; 0x40
|
||||
.space 4
|
||||
.section __DATA,__objc_catlist,regular,no_dead_strip
|
||||
.p2align 3, 0x0 ; @"OBJC_LABEL_CATEGORY_$"
|
||||
l_OBJC_LABEL_CATEGORY_$:
|
||||
.quad __OBJC_$_CATEGORY_MyBaseClass_$_Category01
|
||||
.quad __OBJC_$_CATEGORY_MyBaseClass_$_Category02
|
||||
.section __DATA,__objc_imageinfo,regular,no_dead_strip
|
||||
L_OBJC_IMAGE_INFO:
|
||||
.long 0
|
||||
.long 96
|
||||
.subsections_via_symbols
|
Loading…
x
Reference in New Issue
Block a user