Skip to content

Commit

Permalink
[lld-macho] Fold __objc_imageinfo sections
Browse files Browse the repository at this point in the history
Previously, we treated it as a regular ConcatInputSection. However, ld64
actually parses its contents and uses that to synthesize a single image
info struct, generating one 8-byte section instead of `8 * number of
object files with ObjC code`.

I'm not entirely sure what impact this section has on the runtime, so I
just tried to follow ld64's semantics as closely as possible in this
diff. My main motivation though was to reduce binary size.

No significant perf change on chromium_framework on my 16-core Mac Pro:

             base           diff           difference (95% CI)
  sys_time   1.764 ± 0.062  1.748 ± 0.032  [  -2.4% ..   +0.5%]
  user_time  5.112 ± 0.104  5.106 ± 0.046  [  -0.9% ..   +0.7%]
  wall_time  6.111 ± 0.184  6.085 ± 0.076  [  -1.6% ..   +0.8%]
  samples    30             32

Reviewed By: #lld-macho, thakis

Differential Revision: https://reviews.llvm.org/D130125
  • Loading branch information
int3 committed Jul 23, 2022
1 parent 676a03d commit d23da0e
Show file tree
Hide file tree
Showing 9 changed files with 296 additions and 8 deletions.
4 changes: 3 additions & 1 deletion lld/MachO/Driver.cpp
Expand Up @@ -586,7 +586,7 @@ static void initializeSectionRenameMap() {
section_names::objcCatList,
section_names::objcNonLazyCatList,
section_names::objcProtoList,
section_names::objcImageInfo};
section_names::objCImageInfo};
for (StringRef s : v)
config->sectionRenameMap[{segment_names::data, s}] = {
segment_names::dataConst, s};
Expand Down Expand Up @@ -1102,6 +1102,8 @@ static void gatherInputSections() {
}
}
}
if (!file->objCImageInfo.empty())
in.objCImageInfo->addFile(file);
}
assert(inputOrder <= UnspecifiedInputOrder);
}
Expand Down
3 changes: 3 additions & 0 deletions lld/MachO/InputFiles.cpp
Expand Up @@ -363,6 +363,9 @@ void ObjFile::parseSections(ArrayRef<SectionHeader> sectionHeaders) {
// have the same name without causing duplicate symbol errors. To avoid
// spurious duplicate symbol errors, we do not parse these sections.
// TODO: Evaluate whether the bitcode metadata is needed.
} else if (name == section_names::objCImageInfo &&
segname == segment_names::data) {
objCImageInfo = data;
} else {
if (name == section_names::addrSig)
addrSigSection = sections.back();
Expand Down
1 change: 1 addition & 0 deletions lld/MachO/InputFiles.h
Expand Up @@ -120,6 +120,7 @@ class InputFile {

std::vector<Symbol *> symbols;
std::vector<Section *> sections;
ArrayRef<uint8_t> objCImageInfo;

// If not empty, this stores the name of the archive containing this file.
// We use this string for creating error messages.
Expand Down
2 changes: 1 addition & 1 deletion lld/MachO/InputSection.h
Expand Up @@ -321,7 +321,7 @@ constexpr const char objcCatList[] = "__objc_catlist";
constexpr const char objcClassList[] = "__objc_classlist";
constexpr const char objcClassRefs[] = "__objc_classrefs";
constexpr const char objcConst[] = "__objc_const";
constexpr const char objcImageInfo[] = "__objc_imageinfo";
constexpr const char objCImageInfo[] = "__objc_imageinfo";
constexpr const char objcNonLazyCatList[] = "__objc_nlcatlist";
constexpr const char objcNonLazyClassList[] = "__objc_nlclslist";
constexpr const char objcProtoList[] = "__objc_protolist";
Expand Down
80 changes: 80 additions & 0 deletions lld/MachO/SyntheticSections.cpp
Expand Up @@ -1621,6 +1621,86 @@ void WordLiteralSection::writeTo(uint8_t *buf) const {
memcpy(buf + p.second * 4, &p.first, 4);
}

ObjCImageInfoSection::ObjCImageInfoSection()
: SyntheticSection(segment_names::data, section_names::objCImageInfo) {}

ObjCImageInfoSection::ImageInfo
ObjCImageInfoSection::parseImageInfo(const InputFile *file) {
ImageInfo info;
ArrayRef<uint8_t> data = file->objCImageInfo;
// The image info struct has the following layout:
// struct {
// uint32_t version;
// uint32_t flags;
// };
if (data.size() < 8) {
warn(toString(file) + ": invalid __objc_imageinfo size");
return info;
}

auto *buf = reinterpret_cast<const uint32_t *>(data.data());
if (read32le(buf) != 0) {
warn(toString(file) + ": invalid __objc_imageinfo version");
return info;
}

uint32_t flags = read32le(buf + 1);
info.swiftVersion = (flags >> 8) & 0xff;
info.hasCategoryClassProperties = flags & 0x40;
return info;
}

static std::string swiftVersionString(uint8_t version) {
switch (version) {
case 1:
return "1.0";
case 2:
return "1.1";
case 3:
return "2.0";
case 4:
return "3.0";
case 5:
return "4.0";
default:
return ("0x" + Twine::utohexstr(version)).str();
}
}

// Validate each object file's __objc_imageinfo and use them to generate the
// image info for the output binary. Only two pieces of info are relevant:
// 1. The Swift version (should be identical across inputs)
// 2. `bool hasCategoryClassProperties` (true only if true for all inputs)
void ObjCImageInfoSection::finalizeContents() {
assert(files.size() != 0); // should have already been checked via isNeeded()

info.hasCategoryClassProperties = true;
const InputFile *firstFile;
for (auto file : files) {
ImageInfo inputInfo = parseImageInfo(file);
info.hasCategoryClassProperties &= inputInfo.hasCategoryClassProperties;

if (inputInfo.swiftVersion != 0) {
if (info.swiftVersion != 0 &&
info.swiftVersion != inputInfo.swiftVersion) {
error("Swift version mismatch: " + toString(firstFile) +
" has version " + swiftVersionString(info.swiftVersion) +
" but " + toString(file) + " has version " +
swiftVersionString(inputInfo.swiftVersion));
} else {
info.swiftVersion = inputInfo.swiftVersion;
firstFile = file;
}
}
}
}

void ObjCImageInfoSection::writeTo(uint8_t *buf) const {
uint32_t flags = info.hasCategoryClassProperties ? 0x40 : 0x0;
flags |= info.swiftVersion << 8;
write32le(buf + 4, flags);
}

void macho::createSyntheticSymbols() {
auto addHeaderSymbol = [](const char *name) {
symtab->addSynthetic(name, in.header->isec, /*value=*/0,
Expand Down
23 changes: 23 additions & 0 deletions lld/MachO/SyntheticSections.h
Expand Up @@ -19,6 +19,7 @@

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Support/MathExtras.h"
Expand Down Expand Up @@ -600,6 +601,27 @@ class WordLiteralSection final : public SyntheticSection {
std::unordered_map<uint32_t, uint64_t> literal4Map;
};

class ObjCImageInfoSection final : public SyntheticSection {
public:
ObjCImageInfoSection();
bool isNeeded() const override { return !files.empty(); }
uint64_t getSize() const override { return 8; }
void addFile(const InputFile *file) {
assert(!file->objCImageInfo.empty());
files.push_back(file);
}
void finalizeContents();
void writeTo(uint8_t *buf) const override;

private:
struct ImageInfo {
uint8_t swiftVersion = 0;
bool hasCategoryClassProperties = false;
} info;
static ImageInfo parseImageInfo(const InputFile *);
std::vector<const InputFile *> files; // files with image info
};

struct InStruct {
const uint8_t *bufferStart = nullptr;
MachHeaderSection *header = nullptr;
Expand All @@ -616,6 +638,7 @@ struct InStruct {
StubsSection *stubs = nullptr;
StubHelperSection *stubHelper = nullptr;
UnwindInfoSection *unwindInfo = nullptr;
ObjCImageInfoSection *objCImageInfo = nullptr;
ConcatInputSection *imageLoaderCache = nullptr;
};

Expand Down
5 changes: 5 additions & 0 deletions lld/MachO/Writer.cpp
Expand Up @@ -1164,6 +1164,10 @@ template <class LP> void Writer::run() {

if (in.stubHelper->isNeeded())
in.stubHelper->setup();

if (in.objCImageInfo->isNeeded())
in.objCImageInfo->finalizeContents();

// At this point, we should know exactly which output sections are needed,
// courtesy of scanSymbols() and scanRelocations().
createOutputSections<LP>();
Expand Down Expand Up @@ -1210,6 +1214,7 @@ void macho::createSyntheticSections() {
in.stubs = make<StubsSection>();
in.stubHelper = make<StubHelperSection>();
in.unwindInfo = makeUnwindInfoSection();
in.objCImageInfo = make<ObjCImageInfoSection>();

// This section contains space for just a single word, and will be used by
// dyld to cache an address to the image loader it uses.
Expand Down
14 changes: 8 additions & 6 deletions lld/test/MachO/builtin-rename.s
Expand Up @@ -37,7 +37,6 @@
# NDATA-DAG: __DATA,__objc_catlist __DATA__objc_catlist
# NDATA-DAG: __DATA,__objc_nlcatlist __DATA__objc_nlcatlist
# NDATA-DAG: __DATA,__objc_protolist __DATA__objc_protolist
# NDATA-DAG: __DATA,__objc_imageinfo __DATA__objc_imageinfo
# NDATA-DAG: __DATA,__nl_symbol_ptr __IMPORT__pointers

# YDATA-DAG: __DATA_CONST,__auth_got __DATA__auth_got
Expand All @@ -52,7 +51,6 @@
# YDATA-DAG: __DATA_CONST,__objc_catlist __DATA__objc_catlist
# YDATA-DAG: __DATA_CONST,__objc_nlcatlist __DATA__objc_nlcatlist
# YDATA-DAG: __DATA_CONST,__objc_protolist __DATA__objc_protolist
# YDATA-DAG: __DATA_CONST,__objc_imageinfo __DATA__objc_imageinfo
# YDATA-DAG: __DATA_CONST,__nl_symbol_ptr __IMPORT__pointers

## LLD doesn't support defining symbols in synthetic sections, so we test them
Expand Down Expand Up @@ -133,10 +131,14 @@ __DATA__objc_nlcatlist:
__DATA__objc_protolist:
.space 8

.section __DATA,__objc_imageinfo
.global __DATA__objc_imageinfo
__DATA__objc_imageinfo:
.space 8
## __objc_imageinfo should get moved under __DATA_CONST as well, but symbols
## within __objc_imageinfo get dropped during link, so we are cannot test this
## case using the output of `llvm-objdump --syms`. TODO: rewrite test to use
## `llvm-readobj --section-headers`, which will avoid this issue.
# .section __DATA,__objc_imageinfo
# .global __DATA__objc_imageinfo
# __DATA__objc_imageinfo:
# .space 8

.section __IMPORT,__pointers,non_lazy_symbol_pointers
.global __IMPORT__pointers
Expand Down

0 comments on commit d23da0e

Please sign in to comment.