[lld] 6cb0731 - [lld] Merge Mach-O input sections
Jez Ng via llvm-commits
llvm-commits at lists.llvm.org
Fri May 1 16:59:40 PDT 2020
Author: Kellie Medlin
Date: 2020-05-01T16:57:18-07:00
New Revision: 6cb073133c564e94c0b4293ad85a2c80ee3d6397
URL: https://github.com/llvm/llvm-project/commit/6cb073133c564e94c0b4293ad85a2c80ee3d6397
DIFF: https://github.com/llvm/llvm-project/commit/6cb073133c564e94c0b4293ad85a2c80ee3d6397.diff
LOG: [lld] Merge Mach-O input sections
Summary: Similar to other formats, input sections in the MachO
implementation are now grouped under output sections. This is primarily
a refactor, although there's some new logic (like resolving the output
section's flags based on its inputs).
Differential Revision: https://reviews.llvm.org/D77893
Added:
lld/MachO/MergedOutputSection.cpp
lld/MachO/MergedOutputSection.h
lld/MachO/OutputSection.cpp
lld/MachO/OutputSection.h
lld/test/MachO/Inputs/libfunction.s
lld/test/MachO/section-merge.s
Modified:
lld/MachO/CMakeLists.txt
lld/MachO/Driver.cpp
lld/MachO/ExportTrie.cpp
lld/MachO/ExportTrie.h
lld/MachO/InputFiles.cpp
lld/MachO/InputSection.cpp
lld/MachO/InputSection.h
lld/MachO/OutputSegment.cpp
lld/MachO/OutputSegment.h
lld/MachO/Symbols.h
lld/MachO/SyntheticSections.cpp
lld/MachO/SyntheticSections.h
lld/MachO/Writer.cpp
Removed:
################################################################################
diff --git a/lld/MachO/CMakeLists.txt b/lld/MachO/CMakeLists.txt
index f999c87f93ff..a4dc69ea3593 100644
--- a/lld/MachO/CMakeLists.txt
+++ b/lld/MachO/CMakeLists.txt
@@ -8,6 +8,8 @@ add_lld_library(lldMachO2
ExportTrie.cpp
InputFiles.cpp
InputSection.cpp
+ MergedOutputSection.cpp
+ OutputSection.cpp
OutputSegment.cpp
SymbolTable.cpp
Symbols.cpp
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index e230fb2c2b88..aabdc2ed5320 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -9,6 +9,7 @@
#include "Driver.h"
#include "Config.h"
#include "InputFiles.h"
+#include "OutputSection.h"
#include "OutputSegment.h"
#include "SymbolTable.h"
#include "Symbols.h"
diff --git a/lld/MachO/ExportTrie.cpp b/lld/MachO/ExportTrie.cpp
index 2f352bc94278..871cf334d616 100644
--- a/lld/MachO/ExportTrie.cpp
+++ b/lld/MachO/ExportTrie.cpp
@@ -76,7 +76,7 @@ struct TrieNode {
// Returns whether the new estimated offset
diff ers from the old one.
bool updateOffset(size_t &nextOffset);
- void writeTo(uint8_t *buf);
+ void writeTo(uint8_t *buf) const;
};
bool TrieNode::updateOffset(size_t &nextOffset) {
@@ -108,7 +108,7 @@ bool TrieNode::updateOffset(size_t &nextOffset) {
return result;
}
-void TrieNode::writeTo(uint8_t *buf) {
+void TrieNode::writeTo(uint8_t *buf) const {
buf += offset;
if (info) {
// TrieNodes with Symbol info: size, flags address
@@ -227,7 +227,7 @@ size_t TrieBuilder::build() {
return offset;
}
-void TrieBuilder::writeTo(uint8_t *buf) {
+void TrieBuilder::writeTo(uint8_t *buf) const {
for (TrieNode *node : nodes)
node->writeTo(buf);
}
diff --git a/lld/MachO/ExportTrie.h b/lld/MachO/ExportTrie.h
index 22cf06d12893..a85728c59955 100644
--- a/lld/MachO/ExportTrie.h
+++ b/lld/MachO/ExportTrie.h
@@ -24,7 +24,7 @@ class TrieBuilder {
void addSymbol(const Symbol &sym) { exported.push_back(&sym); }
// Returns the size in bytes of the serialized trie.
size_t build();
- void writeTo(uint8_t *buf);
+ void writeTo(uint8_t *buf) const;
private:
TrieNode *makeNode();
diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp
index 1c56418bc69e..02e7d1c55221 100644
--- a/lld/MachO/InputFiles.cpp
+++ b/lld/MachO/InputFiles.cpp
@@ -43,7 +43,7 @@
#include "InputFiles.h"
#include "InputSection.h"
-#include "OutputSegment.h"
+#include "OutputSection.h"
#include "SymbolTable.h"
#include "Symbols.h"
#include "Target.h"
diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index 146c6e7aba1c..55e17b606549 100644
--- a/lld/MachO/InputSection.cpp
+++ b/lld/MachO/InputSection.cpp
@@ -22,9 +22,11 @@ using namespace lld::macho;
std::vector<InputSection *> macho::inputSections;
uint64_t InputSection::getFileOffset() const {
- return parent->fileOff + addr - parent->firstSection()->addr;
+ return parent->fileOff + outSecFileOff;
}
+uint64_t InputSection::getVA() const { return parent->addr + outSecOff; }
+
void InputSection::writeTo(uint8_t *buf) {
if (!data.empty())
memcpy(buf, data.data(), data.size());
@@ -38,14 +40,14 @@ void InputSection::writeTo(uint8_t *buf) {
va = s->getVA();
}
} else if (auto *isec = r.target.dyn_cast<InputSection *>()) {
- va = isec->addr;
+ va = isec->getVA();
} else {
llvm_unreachable("Unknown relocation target");
}
uint64_t val = va + r.addend;
if (1) // TODO: handle non-pcrel relocations
- val -= addr + r.offset;
+ val -= getVA() + r.offset;
target->relocateOne(buf + r.offset, r.type, val);
}
}
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 30d51537a0e2..a945b79ad3a0 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -19,7 +19,7 @@ namespace macho {
class InputFile;
class InputSection;
-class OutputSegment;
+class OutputSection;
class Symbol;
struct Reloc {
@@ -35,26 +35,22 @@ class InputSection {
virtual size_t getSize() const { return data.size(); }
virtual uint64_t getFileSize() const { return getSize(); }
uint64_t getFileOffset() const;
- // Don't emit section_64 headers for hidden sections.
- virtual bool isHidden() const { return false; }
- // Unneeded sections are omitted entirely (header and body).
- virtual bool isNeeded() const { return true; }
+ uint64_t getVA() const;
+
virtual void writeTo(uint8_t *buf);
InputFile *file = nullptr;
- OutputSegment *parent = nullptr;
StringRef name;
StringRef segname;
- ArrayRef<uint8_t> data;
+ OutputSection *parent = nullptr;
+ uint64_t outSecOff = 0;
+ uint64_t outSecFileOff = 0;
- // TODO these properties ought to live in an OutputSection class.
- // Move them once available.
- uint64_t addr = 0;
uint32_t align = 1;
- uint32_t sectionIndex = 0;
uint32_t flags = 0;
+ ArrayRef<uint8_t> data;
std::vector<Reloc> relocs;
};
diff --git a/lld/MachO/MergedOutputSection.cpp b/lld/MachO/MergedOutputSection.cpp
new file mode 100644
index 000000000000..1983736d7879
--- /dev/null
+++ b/lld/MachO/MergedOutputSection.cpp
@@ -0,0 +1,72 @@
+//===- OutputSection.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "MergedOutputSection.h"
+#include "lld/Common/ErrorHandler.h"
+#include "lld/Common/Memory.h"
+#include "llvm/BinaryFormat/MachO.h"
+
+using namespace llvm;
+using namespace llvm::MachO;
+using namespace lld;
+using namespace lld::macho;
+
+void MergedOutputSection::mergeInput(InputSection *input) {
+ if (inputs.empty()) {
+ align = input->align;
+ flags = input->flags;
+ } else {
+ mergeFlags(input->flags);
+ align = std::max(align, input->align);
+ }
+
+ inputs.push_back(input);
+ input->parent = this;
+}
+
+void MergedOutputSection::finalize() {
+ uint64_t isecAddr = addr;
+ uint64_t isecFileOff = fileOff;
+ for (InputSection *i : inputs) {
+ i->outSecOff = alignTo(isecAddr, i->align) - addr;
+ i->outSecFileOff = alignTo(isecFileOff, i->align) - fileOff;
+ isecAddr += i->getSize();
+ isecFileOff += i->getFileSize();
+ }
+ size = isecAddr - addr;
+ fileSize = isecFileOff - fileOff;
+}
+
+void MergedOutputSection::writeTo(uint8_t *buf) const {
+ for (InputSection *isec : inputs) {
+ isec->writeTo(buf + isec->outSecFileOff);
+ }
+}
+
+// TODO: this is most likely wrong; reconsider how section flags
+// are actually merged. The logic presented here was written without
+// any form of informed research.
+void MergedOutputSection::mergeFlags(uint32_t inputFlags) {
+ uint8_t sectionFlag = MachO::SECTION_TYPE & inputFlags;
+ if (sectionFlag != (MachO::SECTION_TYPE & flags))
+ error("Cannot add merge section; inconsistent type flags " +
+ Twine(sectionFlag));
+
+ uint32_t inconsistentFlags =
+ MachO::S_ATTR_DEBUG | MachO::S_ATTR_STRIP_STATIC_SYMS |
+ MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_ATTR_LIVE_SUPPORT;
+ if ((inputFlags ^ flags) & inconsistentFlags)
+ error("Cannot add merge section; cannot merge inconsistent flags");
+
+ // Negate pure instruction presence if any section isn't pure.
+ uint32_t pureMask = ~MachO::S_ATTR_PURE_INSTRUCTIONS | (inputFlags & flags);
+
+ // Merge the rest
+ flags |= inputFlags;
+ flags &= pureMask;
+}
diff --git a/lld/MachO/MergedOutputSection.h b/lld/MachO/MergedOutputSection.h
new file mode 100644
index 000000000000..272b7525b3bc
--- /dev/null
+++ b/lld/MachO/MergedOutputSection.h
@@ -0,0 +1,51 @@
+//===- OutputSection.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_MERGED_OUTPUT_SECTION_H
+#define LLD_MACHO_MERGED_OUTPUT_SECTION_H
+
+#include "InputSection.h"
+#include "OutputSection.h"
+#include "lld/Common/LLVM.h"
+
+namespace lld {
+namespace macho {
+
+// Linking multiple files will inevitably mean resolving sections in
diff erent
+// files that are labeled with the same segment and section name. This class
+// contains all such sections and writes the data from each section sequentially
+// in the final binary.
+class MergedOutputSection : public OutputSection {
+public:
+ MergedOutputSection(StringRef name) : OutputSection(name) {}
+
+ const InputSection *firstSection() const { return inputs.front(); }
+ const InputSection *lastSection() const { return inputs.back(); }
+
+ // These accessors will only be valid after finalizing the section
+ size_t getSize() const override { return size; }
+ uint64_t getFileSize() const override { return fileSize; }
+
+ void mergeInput(InputSection *input) override;
+ void finalize() override;
+
+ void writeTo(uint8_t *buf) const override;
+
+ std::vector<InputSection *> inputs;
+
+private:
+ void mergeFlags(uint32_t inputFlags);
+
+ size_t size = 0;
+ uint64_t fileSize = 0;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/lld/MachO/OutputSection.cpp b/lld/MachO/OutputSection.cpp
new file mode 100644
index 000000000000..d4c24f6b7345
--- /dev/null
+++ b/lld/MachO/OutputSection.cpp
@@ -0,0 +1,23 @@
+//===- OutputSection.cpp --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "OutputSection.h"
+#include "OutputSegment.h"
+#include "lld/Common/ErrorHandler.h"
+
+using namespace llvm;
+using namespace lld;
+using namespace lld::macho;
+
+uint64_t OutputSection::getSegmentOffset() const {
+ return addr - parent->firstSection()->addr;
+}
+
+void OutputSection::mergeInput(InputSection *input) {
+ llvm_unreachable("Cannot merge input section into unmergable output section");
+}
diff --git a/lld/MachO/OutputSection.h b/lld/MachO/OutputSection.h
new file mode 100644
index 000000000000..90f8a841a7f2
--- /dev/null
+++ b/lld/MachO/OutputSection.h
@@ -0,0 +1,100 @@
+//===- OutputSection.h ------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLD_MACHO_OUTPUT_SECTION_H
+#define LLD_MACHO_OUTPUT_SECTION_H
+
+#include "lld/Common/LLVM.h"
+#include "llvm/ADT/DenseMap.h"
+
+namespace lld {
+namespace macho {
+
+class InputSection;
+class OutputSegment;
+
+// Output sections represent the finalized sections present within the final
+// linked executable. They can represent special sections (like the symbol
+// table), or represent coalesced sections from the various inputs given to the
+// linker with the same segment / section name.
+class OutputSection {
+public:
+ OutputSection(StringRef name) : name(name) {}
+ virtual ~OutputSection() = default;
+
+ // These accessors will only be valid after finalizing the section.
+ uint64_t getSegmentOffset() const;
+
+ // How much space the section occupies in the address space.
+ virtual size_t getSize() const = 0;
+ // How much space the section occupies in the file. Most sections are copied
+ // as-is so their file size is the same as their address space size.
+ virtual uint64_t getFileSize() const { return getSize(); }
+
+ // Hidden sections omit header content, but body content is still present.
+ virtual bool isHidden() const { return !this->isNeeded(); }
+ // Unneeded sections are omitted entirely (header and body).
+ virtual bool isNeeded() const { return true; }
+
+ // Some sections may allow coalescing other raw input sections.
+ virtual void mergeInput(InputSection *input);
+
+ // Specifically finalizes addresses and section size, not content.
+ virtual void finalize() {
+ // TODO investigate refactoring synthetic section finalization logic into
+ // overrides of this function.
+ }
+
+ virtual void writeTo(uint8_t *buf) const = 0;
+
+ StringRef name;
+ OutputSegment *parent = nullptr;
+
+ uint32_t index = 0;
+ uint64_t addr = 0;
+ uint64_t fileOff = 0;
+ uint32_t align = 1;
+ uint32_t flags = 0;
+};
+
+class OutputSectionComparator {
+public:
+ OutputSectionComparator(uint32_t segmentOrder,
+ const std::vector<StringRef> §Ordering)
+ : segmentOrder(segmentOrder) {
+ for (uint32_t j = 0, m = sectOrdering.size(); j < m; ++j)
+ sectionOrdering[sectOrdering[j]] = j;
+ }
+
+ uint32_t sectionOrder(StringRef secname) {
+ auto sectIt = sectionOrdering.find(secname);
+ if (sectIt != sectionOrdering.end())
+ return sectIt->second;
+ return sectionOrdering.size();
+ }
+
+ // Sort sections within a common segment, which stores them in
+ // a MapVector of section name -> section
+ bool operator()(const std::pair<StringRef, OutputSection *> &a,
+ const std::pair<StringRef, OutputSection *> &b) {
+ return sectionOrder(a.first) < sectionOrder(b.first);
+ }
+
+ bool operator<(const OutputSectionComparator &b) {
+ return segmentOrder < b.segmentOrder;
+ }
+
+private:
+ uint32_t segmentOrder;
+ llvm::DenseMap<StringRef, uint32_t> sectionOrdering;
+};
+
+} // namespace macho
+} // namespace lld
+
+#endif
diff --git a/lld/MachO/OutputSegment.cpp b/lld/MachO/OutputSegment.cpp
index c9a87189c85e..1512e3f302f8 100644
--- a/lld/MachO/OutputSegment.cpp
+++ b/lld/MachO/OutputSegment.cpp
@@ -8,7 +8,10 @@
#include "OutputSegment.h"
#include "InputSection.h"
+#include "MergedOutputSection.h"
+#include "SyntheticSections.h"
+#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "llvm/BinaryFormat/MachO.h"
@@ -33,13 +36,71 @@ static uint32_t maxProt(StringRef name) {
return VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
}
-void OutputSegment::addSection(InputSection *isec) {
- isec->parent = this;
- std::vector<InputSection *> &vec = sections[isec->name];
- if (vec.empty() && !isec->isHidden()) {
- ++numNonHiddenSections;
+size_t OutputSegment::numNonHiddenSections() const {
+ size_t count = 0;
+ for (const OutputSegment::SectionMapEntry &i : sections) {
+ OutputSection *os = i.second;
+ count += (os->isHidden() ? 0 : 1);
}
- vec.push_back(isec);
+ return count;
+}
+
+void OutputSegment::addOutputSection(OutputSection *os) {
+ os->parent = this;
+ std::pair<SectionMap::iterator, bool> result =
+ sections.insert(SectionMapEntry(os->name, os));
+ if (!result.second) {
+ llvm_unreachable("Attempted to set section, but a section with the same "
+ "name already exists");
+ }
+}
+
+OutputSection *OutputSegment::getOrCreateOutputSection(StringRef name) {
+ OutputSegment::SectionMap::iterator i = sections.find(name);
+ if (i != sections.end()) {
+ return i->second;
+ }
+
+ auto *os = make<MergedOutputSection>(name);
+ addOutputSection(os);
+ return os;
+}
+
+void OutputSegment::sortOutputSections(OutputSegmentComparator *comparator) {
+ llvm::stable_sort(sections, *comparator->sectionComparator(this));
+}
+
+OutputSegmentComparator::OutputSegmentComparator() {
+ // This defines the order of segments and the sections within each segment.
+ // Segments that are not mentioned here will end up at defaultPosition;
+ // sections that are not mentioned will end up at the end of the section
+ // list for their given segment.
+ std::vector<std::pair<StringRef, std::vector<StringRef>>> ordering{
+ {segment_names::pageZero, {}},
+ {segment_names::text, {section_names::header}},
+ {defaultPosition, {}},
+ // Make sure __LINKEDIT is the last segment (i.e. all its hidden
+ // sections must be ordered after other sections).
+ {segment_names::linkEdit,
+ {
+ section_names::binding,
+ section_names::export_,
+ section_names::symbolTable,
+ section_names::stringTable,
+ }},
+ };
+
+ for (uint32_t i = 0, n = ordering.size(); i < n; ++i) {
+ auto &p = ordering[i];
+ StringRef segname = p.first;
+ const std::vector<StringRef> §Ordering = p.second;
+ orderMap.insert(std::pair<StringRef, OutputSectionComparator>(
+ segname, OutputSectionComparator(i, sectOrdering)));
+ }
+
+ // Cache the position for the default comparator since this is the likely
+ // scenario.
+ defaultPositionComparator = &orderMap.find(defaultPosition)->second;
}
static llvm::DenseMap<StringRef, OutputSegment *> nameToOutputSegment;
@@ -62,3 +123,24 @@ OutputSegment *macho::getOrCreateOutputSegment(StringRef name) {
outputSegments.push_back(segRef);
return segRef;
}
+
+void macho::sortOutputSegmentsAndSections() {
+ // Sorting only can happen once all outputs have been collected.
+ // Since output sections are grouped by segment, sorting happens
+ // first over all segments, then over sections per segment.
+ auto comparator = OutputSegmentComparator();
+ llvm::stable_sort(outputSegments, comparator);
+
+ // Now that the output sections are sorted, assign the final
+ // output section indices.
+ uint32_t sectionIndex = 0;
+ for (OutputSegment *seg : outputSegments) {
+ seg->sortOutputSections(&comparator);
+ for (auto &p : seg->getSections()) {
+ OutputSection *section = p.second;
+ if (!section->isHidden()) {
+ section->index = ++sectionIndex;
+ }
+ }
+ }
+}
diff --git a/lld/MachO/OutputSegment.h b/lld/MachO/OutputSegment.h
index ec8437529b41..3a801430245c 100644
--- a/lld/MachO/OutputSegment.h
+++ b/lld/MachO/OutputSegment.h
@@ -9,6 +9,7 @@
#ifndef LLD_MACHO_OUTPUT_SEGMENT_H
#define LLD_MACHO_OUTPUT_SEGMENT_H
+#include "OutputSection.h"
#include "lld/Common/LLVM.h"
#include "llvm/ADT/MapVector.h"
@@ -20,43 +21,77 @@ namespace segment_names {
constexpr const char *text = "__TEXT";
constexpr const char *pageZero = "__PAGEZERO";
constexpr const char *linkEdit = "__LINKEDIT";
+constexpr const char *dataConst = "__DATA_CONST";
} // namespace segment_names
+class OutputSection;
+class OutputSegmentComparator;
class InputSection;
class OutputSegment {
public:
- InputSection *firstSection() const { return sections.front().second.at(0); }
+ using SectionMap = typename llvm::MapVector<StringRef, OutputSection *>;
+ using SectionMapEntry = typename std::pair<StringRef, OutputSection *>;
- InputSection *lastSection() const { return sections.back().second.back(); }
+ const OutputSection *firstSection() const { return sections.front().second; }
+ const OutputSection *lastSection() const { return sections.back().second; }
bool isNeeded() const {
- return !sections.empty() || name == segment_names::linkEdit;
+ if (name == segment_names::linkEdit)
+ return true;
+ for (const SectionMapEntry &i : sections) {
+ OutputSection *os = i.second;
+ if (os->isNeeded())
+ return true;
+ }
+ return false;
}
- void addSection(InputSection *);
+ OutputSection *getOrCreateOutputSection(StringRef name);
+ void addOutputSection(OutputSection *os);
+ void sortOutputSections(OutputSegmentComparator *comparator);
- const llvm::MapVector<StringRef, std::vector<InputSection *>> &
- getSections() const {
- return sections;
- }
+ const SectionMap &getSections() const { return sections; }
+ size_t numNonHiddenSections() const;
uint64_t fileOff = 0;
StringRef name;
- uint32_t numNonHiddenSections = 0;
uint32_t maxProt = 0;
uint32_t initProt = 0;
uint8_t index;
private:
- llvm::MapVector<StringRef, std::vector<InputSection *>> sections;
+ SectionMap sections;
+};
+
+class OutputSegmentComparator {
+public:
+ OutputSegmentComparator();
+
+ OutputSectionComparator *sectionComparator(const OutputSegment *os) {
+ auto it = orderMap.find(os->name);
+ if (it == orderMap.end()) {
+ return defaultPositionComparator;
+ }
+ return &it->second;
+ }
+
+ bool operator()(const OutputSegment *a, const OutputSegment *b) {
+ return *sectionComparator(a) < *sectionComparator(b);
+ }
+
+private:
+ const StringRef defaultPosition = StringRef();
+ llvm::DenseMap<StringRef, OutputSectionComparator> orderMap;
+ OutputSectionComparator *defaultPositionComparator;
};
extern std::vector<OutputSegment *> outputSegments;
OutputSegment *getOutputSegment(StringRef name);
OutputSegment *getOrCreateOutputSegment(StringRef name);
+void sortOutputSegmentsAndSections();
} // namespace macho
} // namespace lld
diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h
index c9d9527a7b0a..1f5817117e80 100644
--- a/lld/MachO/Symbols.h
+++ b/lld/MachO/Symbols.h
@@ -81,7 +81,7 @@ class DylibSymbol : public Symbol {
inline uint64_t Symbol::getVA() const {
if (auto *d = dyn_cast<Defined>(this))
- return d->isec->addr + d->value;
+ return d->isec->getVA() + d->value;
return 0;
}
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index a6d22ed82e1a..c23f6abdb564 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -26,13 +26,18 @@ using namespace llvm::support;
namespace lld {
namespace macho {
-MachHeaderSection::MachHeaderSection() {
- // dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts
- // from the beginning of the file (i.e. the header).
- segname = segment_names::text;
- name = section_names::header;
+SyntheticSection::SyntheticSection(const char *segname, const char *name)
+ : OutputSection(name) {
+ // Synthetic sections always know which segment they belong to so hook
+ // them up when they're made
+ getOrCreateOutputSegment(segname)->addOutputSection(this);
}
+// dyld3's MachOLoaded::getSlide() assumes that the __TEXT segment starts
+// from the beginning of the file (i.e. the header).
+MachHeaderSection::MachHeaderSection()
+ : SyntheticSection(segment_names::text, section_names::header) {}
+
void MachHeaderSection::addLoadCommand(LoadCommand *lc) {
loadCommands.push_back(lc);
sizeOfCmds += lc->getSize();
@@ -42,7 +47,7 @@ size_t MachHeaderSection::getSize() const {
return sizeof(mach_header_64) + sizeOfCmds;
}
-void MachHeaderSection::writeTo(uint8_t *buf) {
+void MachHeaderSection::writeTo(uint8_t *buf) const {
auto *hdr = reinterpret_cast<mach_header_64 *>(buf);
hdr->magic = MH_MAGIC_64;
hdr->cputype = CPU_TYPE_X86_64;
@@ -59,14 +64,11 @@ void MachHeaderSection::writeTo(uint8_t *buf) {
}
}
-PageZeroSection::PageZeroSection() {
- segname = segment_names::pageZero;
- name = section_names::pageZero;
-}
+PageZeroSection::PageZeroSection()
+ : SyntheticSection(segment_names::pageZero, section_names::pageZero) {}
-GotSection::GotSection() {
- segname = "__DATA_CONST";
- name = "__got";
+GotSection::GotSection()
+ : SyntheticSection(segment_names::dataConst, section_names::got) {
align = 8;
flags = S_NON_LAZY_SYMBOL_POINTERS;
@@ -80,10 +82,8 @@ void GotSection::addEntry(DylibSymbol &sym) {
}
}
-BindingSection::BindingSection() {
- segname = segment_names::linkEdit;
- name = section_names::binding;
-}
+BindingSection::BindingSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::binding) {}
bool BindingSection::isNeeded() const { return in.got->isNeeded(); }
@@ -107,7 +107,7 @@ void BindingSection::finalizeContents() {
raw_svector_ostream os{contents};
os << static_cast<uint8_t>(BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB |
in.got->parent->index);
- encodeULEB128(in.got->addr - in.got->parent->firstSection()->addr, os);
+ encodeULEB128(in.got->getSegmentOffset(), os);
for (const DylibSymbol *sym : in.got->getEntries()) {
// TODO: Implement compact encoding -- we only need to encode the
//
diff erences between consecutive symbol entries.
@@ -127,14 +127,12 @@ void BindingSection::finalizeContents() {
os << static_cast<uint8_t>(BIND_OPCODE_DONE);
}
-void BindingSection::writeTo(uint8_t *buf) {
+void BindingSection::writeTo(uint8_t *buf) const {
memcpy(buf, contents.data(), contents.size());
}
-ExportSection::ExportSection() {
- segname = segment_names::linkEdit;
- name = section_names::export_;
-}
+ExportSection::ExportSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::export_) {}
void ExportSection::finalizeContents() {
// TODO: We should check symbol visibility.
@@ -144,12 +142,11 @@ void ExportSection::finalizeContents() {
size = trieBuilder.build();
}
-void ExportSection::writeTo(uint8_t *buf) { trieBuilder.writeTo(buf); }
+void ExportSection::writeTo(uint8_t *buf) const { trieBuilder.writeTo(buf); }
SymtabSection::SymtabSection(StringTableSection &stringTableSection)
- : stringTableSection(stringTableSection) {
- segname = segment_names::linkEdit;
- name = section_names::symbolTable;
+ : SyntheticSection(segment_names::linkEdit, section_names::symbolTable),
+ stringTableSection(stringTableSection) {
// TODO: When we introduce the SyntheticSections superclass, we should make
// all synthetic sections aligned to WordSize by default.
align = WordSize;
@@ -166,7 +163,7 @@ void SymtabSection::finalizeContents() {
symbols.push_back({sym, stringTableSection.addString(sym->getName())});
}
-void SymtabSection::writeTo(uint8_t *buf) {
+void SymtabSection::writeTo(uint8_t *buf) const {
auto *nList = reinterpret_cast<nlist_64 *>(buf);
for (const SymtabEntry &entry : symbols) {
nList->n_strx = entry.strx;
@@ -174,18 +171,16 @@ void SymtabSection::writeTo(uint8_t *buf) {
// TODO populate n_desc
if (auto defined = dyn_cast<Defined>(entry.sym)) {
nList->n_type = N_EXT | N_SECT;
- nList->n_sect = defined->isec->sectionIndex;
+ nList->n_sect = defined->isec->parent->index;
// For the N_SECT symbol type, n_value is the address of the symbol
- nList->n_value = defined->value + defined->isec->addr;
+ nList->n_value = defined->value + defined->isec->getVA();
}
++nList;
}
}
-StringTableSection::StringTableSection() {
- segname = segment_names::linkEdit;
- name = section_names::stringTable;
-}
+StringTableSection::StringTableSection()
+ : SyntheticSection(segment_names::linkEdit, section_names::stringTable) {}
uint32_t StringTableSection::addString(StringRef str) {
uint32_t strx = size;
@@ -194,7 +189,7 @@ uint32_t StringTableSection::addString(StringRef str) {
return strx;
}
-void StringTableSection::writeTo(uint8_t *buf) {
+void StringTableSection::writeTo(uint8_t *buf) const {
uint32_t off = 0;
for (StringRef str : strings) {
memcpy(buf + off, str.data(), str.size());
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index df53200e5c28..c8dadf9bb0de 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -10,12 +10,10 @@
#define LLD_MACHO_SYNTHETIC_SECTIONS_H
#include "ExportTrie.h"
-#include "InputSection.h"
+#include "OutputSection.h"
#include "Target.h"
#include "llvm/ADT/SetVector.h"
-using namespace llvm::MachO;
-
namespace lld {
namespace macho {
@@ -27,20 +25,27 @@ constexpr const char *binding = "__binding";
constexpr const char *export_ = "__export";
constexpr const char *symbolTable = "__symbol_table";
constexpr const char *stringTable = "__string_table";
+constexpr const char *got = "__got";
} // namespace section_names
class DylibSymbol;
class LoadCommand;
+class SyntheticSection : public OutputSection {
+public:
+ SyntheticSection(const char *segname, const char *name);
+ virtual ~SyntheticSection() = default;
+};
+
// The header of the Mach-O file, which must have a file offset of zero.
-class MachHeaderSection : public InputSection {
+class MachHeaderSection : public SyntheticSection {
public:
MachHeaderSection();
void addLoadCommand(LoadCommand *);
bool isHidden() const override { return true; }
size_t getSize() const override;
- void writeTo(uint8_t *buf) override;
+ void writeTo(uint8_t *buf) const override;
private:
std::vector<LoadCommand *> loadCommands;
@@ -49,17 +54,18 @@ class MachHeaderSection : public InputSection {
// A hidden section that exists solely for the purpose of creating the
// __PAGEZERO segment, which is used to catch null pointer dereferences.
-class PageZeroSection : public InputSection {
+class PageZeroSection : public SyntheticSection {
public:
PageZeroSection();
bool isHidden() const override { return true; }
size_t getSize() const override { return ImageBase; }
uint64_t getFileSize() const override { return 0; }
+ void writeTo(uint8_t *buf) const override {}
};
// This section will be populated by dyld with addresses to non-lazily-loaded
// dylib symbols.
-class GotSection : public InputSection {
+class GotSection : public SyntheticSection {
public:
GotSection();
@@ -68,11 +74,11 @@ class GotSection : public InputSection {
return entries;
}
- size_t getSize() const override { return entries.size() * WordSize; }
-
bool isNeeded() const override { return !entries.empty(); }
- void writeTo(uint8_t *buf) override {
+ size_t getSize() const override { return entries.size() * WordSize; }
+
+ void writeTo(uint8_t *buf) const override {
// Nothing to write, GOT contains all zeros at link time; it's populated at
// runtime by dyld.
}
@@ -82,7 +88,7 @@ class GotSection : public InputSection {
};
// Stores bind opcodes for telling dyld which symbols to load non-lazily.
-class BindingSection : public InputSection {
+class BindingSection : public SyntheticSection {
public:
BindingSection();
void finalizeContents();
@@ -92,13 +98,13 @@ class BindingSection : public InputSection {
// section headers.
bool isHidden() const override { return true; }
bool isNeeded() const override;
- void writeTo(uint8_t *buf) override;
+ void writeTo(uint8_t *buf) const override;
SmallVector<char, 128> contents;
};
// Stores a trie that describes the set of exported symbols.
-class ExportSection : public InputSection {
+class ExportSection : public SyntheticSection {
public:
ExportSection();
void finalizeContents();
@@ -107,7 +113,7 @@ class ExportSection : public InputSection {
// offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
// section headers.
bool isHidden() const override { return true; }
- void writeTo(uint8_t *buf) override;
+ void writeTo(uint8_t *buf) const override;
private:
TrieBuilder trieBuilder;
@@ -115,7 +121,7 @@ class ExportSection : public InputSection {
};
// Stores the strings referenced by the symbol table.
-class StringTableSection : public InputSection {
+class StringTableSection : public SyntheticSection {
public:
StringTableSection();
// Returns the start offset of the added string.
@@ -125,7 +131,7 @@ class StringTableSection : public InputSection {
// offsets are recorded in the LC_SYMTAB load command, instead of in section
// headers.
bool isHidden() const override { return true; }
- void writeTo(uint8_t *buf) override;
+ void writeTo(uint8_t *buf) const override;
private:
// An n_strx value of 0 always indicates the empty string, so we must locate
@@ -140,7 +146,7 @@ struct SymtabEntry {
size_t strx;
};
-class SymtabSection : public InputSection {
+class SymtabSection : public SyntheticSection {
public:
SymtabSection(StringTableSection &);
void finalizeContents();
@@ -150,7 +156,7 @@ class SymtabSection : public InputSection {
// offsets are recorded in the LC_SYMTAB load command, instead of in section
// headers.
bool isHidden() const override { return true; }
- void writeTo(uint8_t *buf) override;
+ void writeTo(uint8_t *buf) const override;
private:
StringTableSection &stringTableSection;
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 5499529a3107..80915528e11d 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -37,8 +37,7 @@ class Writer {
Writer() : buffer(errorHandler().outputBuffer) {}
void scanRelocations();
- void createHiddenSections();
- void sortSections();
+ void createOutputSections();
void createLoadCommands();
void assignAddresses(OutputSegment *);
void createSymtabContents();
@@ -71,11 +70,11 @@ class LCDyldInfo : public LoadCommand {
c->cmd = LC_DYLD_INFO_ONLY;
c->cmdsize = getSize();
if (bindingSection->isNeeded()) {
- c->bind_off = bindingSection->getFileOffset();
+ c->bind_off = bindingSection->fileOff;
c->bind_size = bindingSection->getFileSize();
}
if (exportSection->isNeeded()) {
- c->export_off = exportSection->getFileOffset();
+ c->export_off = exportSection->fileOff;
c->export_size = exportSection->getFileSize();
}
}
@@ -101,7 +100,7 @@ class LCSegment : public LoadCommand {
uint32_t getSize() const override {
return sizeof(segment_command_64) +
- seg->numNonHiddenSections * sizeof(section_64);
+ seg->numNonHiddenSections() * sizeof(section_64);
}
void writeTo(uint8_t *buf) const override {
@@ -115,20 +114,19 @@ class LCSegment : public LoadCommand {
c->maxprot = seg->maxProt;
c->initprot = seg->initProt;
- if (seg->getSections().empty())
+ if (!seg->isNeeded())
return;
c->vmaddr = seg->firstSection()->addr;
c->vmsize =
seg->lastSection()->addr + seg->lastSection()->getSize() - c->vmaddr;
- c->nsects = seg->numNonHiddenSections;
+ c->nsects = seg->numNonHiddenSections();
for (auto &p : seg->getSections()) {
StringRef s = p.first;
- ArrayRef<InputSection *> sections = p.second;
- for (InputSection *isec : sections)
- c->filesize += isec->getFileSize();
- if (sections[0]->isHidden())
+ OutputSection *section = p.second;
+ c->filesize += section->getFileSize();
+ if (section->isHidden())
continue;
auto *sectHdr = reinterpret_cast<section_64 *>(buf);
@@ -137,16 +135,11 @@ class LCSegment : public LoadCommand {
memcpy(sectHdr->sectname, s.data(), s.size());
memcpy(sectHdr->segname, name.data(), name.size());
- sectHdr->addr = sections[0]->addr;
- sectHdr->offset = sections[0]->getFileOffset();
- sectHdr->align = sections[0]->align;
- uint32_t maxAlign = 0;
- for (const InputSection *section : sections)
- maxAlign = std::max(maxAlign, section->align);
- sectHdr->align = Log2_32(maxAlign);
- sectHdr->flags = sections[0]->flags;
- sectHdr->size = sections.back()->addr + sections.back()->getSize() -
- sections[0]->addr;
+ sectHdr->addr = section->addr;
+ sectHdr->offset = section->fileOff;
+ sectHdr->align = Log2_32(section->align);
+ sectHdr->flags = section->flags;
+ sectHdr->size = section->getSize();
}
}
@@ -178,9 +171,9 @@ class LCSymtab : public LoadCommand {
auto *c = reinterpret_cast<symtab_command *>(buf);
c->cmd = LC_SYMTAB;
c->cmdsize = getSize();
- c->symoff = symtabSection->getFileOffset();
+ c->symoff = symtabSection->fileOff;
c->nsyms = symtabSection->getNumSymbols();
- c->stroff = stringTableSection->getFileOffset();
+ c->stroff = stringTableSection->fileOff;
c->strsize = stringTableSection->getFileSize();
}
@@ -259,76 +252,8 @@ class LCLoadDylinker : public LoadCommand {
//
diff erent location.
const StringRef path = "/usr/lib/dyld";
};
-
-class SectionComparator {
-public:
- struct OrderInfo {
- uint32_t segmentOrder;
- DenseMap<StringRef, uint32_t> sectionOrdering;
- };
-
- SectionComparator() {
- // This defines the order of segments and the sections within each segment.
- // Segments that are not mentioned here will end up at defaultPosition;
- // sections that are not mentioned will end up at the end of the section
- // list for their given segment.
- std::vector<std::pair<StringRef, std::vector<StringRef>>> ordering{
- {segment_names::pageZero, {}},
- {segment_names::text, {section_names::header}},
- {defaultPosition, {}},
- // Make sure __LINKEDIT is the last segment (i.e. all its hidden
- // sections must be ordered after other sections).
- {segment_names::linkEdit,
- {
- section_names::binding,
- section_names::export_,
- section_names::symbolTable,
- section_names::stringTable,
- }},
- };
-
- for (uint32_t i = 0, n = ordering.size(); i < n; ++i) {
- auto &p = ordering[i];
- StringRef segname = p.first;
- const std::vector<StringRef> §Ordering = p.second;
- OrderInfo &info = orderMap[segname];
- info.segmentOrder = i;
- for (uint32_t j = 0, m = sectOrdering.size(); j < m; ++j)
- info.sectionOrdering[sectOrdering[j]] = j;
- }
- }
-
- // Return a {segmentOrder, sectionOrder} pair. Using this as a key will
- // ensure that all sections in the same segment are sorted contiguously.
- std::pair<uint32_t, uint32_t> order(const InputSection *isec) {
- auto it = orderMap.find(isec->segname);
- if (it == orderMap.end())
- return {orderMap[defaultPosition].segmentOrder, 0};
- OrderInfo &info = it->second;
- auto sectIt = info.sectionOrdering.find(isec->name);
- if (sectIt != info.sectionOrdering.end())
- return {info.segmentOrder, sectIt->second};
- return {info.segmentOrder, info.sectionOrdering.size()};
- }
-
- bool operator()(const InputSection *a, const InputSection *b) {
- return order(a) < order(b);
- }
-
-private:
- const StringRef defaultPosition = StringRef();
- DenseMap<StringRef, OrderInfo> orderMap;
-};
-
} // namespace
-template <typename SectionType, typename... ArgT>
-SectionType *createInputSection(ArgT &&... args) {
- auto *section = make<SectionType>(std::forward<ArgT>(args)...);
- inputSections.push_back(section);
- return section;
-}
-
void Writer::scanRelocations() {
for (InputSection *sect : inputSections)
for (Reloc &r : sect->relocs)
@@ -378,37 +303,29 @@ void Writer::createLoadCommands() {
make<LCLoadDylib>("/usr/lib/libSystem.B.dylib"));
}
-void Writer::createHiddenSections() {
- headerSection = createInputSection<MachHeaderSection>();
- bindingSection = createInputSection<BindingSection>();
- stringTableSection = createInputSection<StringTableSection>();
- symtabSection = createInputSection<SymtabSection>(*stringTableSection);
- exportSection = createInputSection<ExportSection>();
+void Writer::createOutputSections() {
+ // First, create hidden sections
+ headerSection = make<MachHeaderSection>();
+ bindingSection = make<BindingSection>();
+ stringTableSection = make<StringTableSection>();
+ symtabSection = make<SymtabSection>(*stringTableSection);
+ exportSection = make<ExportSection>();
switch (config->outputType) {
case MH_EXECUTE:
- createInputSection<PageZeroSection>();
+ make<PageZeroSection>();
break;
case MH_DYLIB:
break;
default:
llvm_unreachable("unhandled output file type");
}
-}
-
-void Writer::sortSections() {
- llvm::stable_sort(inputSections, SectionComparator());
- // TODO This is wrong; input sections ought to be grouped into
- // output sections, which are then organized like this.
- uint32_t sectionIndex = 0;
- // Add input sections to output segments.
+ // Then merge input sections into output sections/segments.
for (InputSection *isec : inputSections) {
- if (isec->isNeeded()) {
- if (!isec->isHidden())
- isec->sectionIndex = ++sectionIndex;
- getOrCreateOutputSegment(isec->segname)->addSection(isec);
- }
+ getOrCreateOutputSegment(isec->segname)
+ ->getOrCreateOutputSection(isec->name)
+ ->mergeInput(isec);
}
}
@@ -418,16 +335,17 @@ void Writer::assignAddresses(OutputSegment *seg) {
seg->fileOff = fileOff;
for (auto &p : seg->getSections()) {
- ArrayRef<InputSection *> sections = p.second;
- for (InputSection *isec : sections) {
- addr = alignTo(addr, isec->align);
- // We must align the file offsets too to avoid misaligned writes of
- // structs.
- fileOff = alignTo(fileOff, isec->align);
- isec->addr = addr;
- addr += isec->getSize();
- fileOff += isec->getFileSize();
- }
+ OutputSection *section = p.second;
+ addr = alignTo(addr, section->align);
+ // We must align the file offsets too to avoid misaligned writes of
+ // structs.
+ fileOff = alignTo(fileOff, section->align);
+ section->addr = addr;
+ section->fileOff = fileOff;
+ section->finalize();
+
+ addr += section->getSize();
+ fileOff += section->getFileSize();
}
}
@@ -446,26 +364,25 @@ void Writer::openFile() {
void Writer::writeSections() {
uint8_t *buf = buffer->getBufferStart();
for (OutputSegment *seg : outputSegments) {
- uint64_t fileOff = seg->fileOff;
- for (auto § : seg->getSections()) {
- for (InputSection *isec : sect.second) {
- fileOff = alignTo(fileOff, isec->align);
- isec->writeTo(buf + fileOff);
- fileOff += isec->getFileSize();
- }
+ for (auto &p : seg->getSections()) {
+ OutputSection *section = p.second;
+ section->writeTo(buf + section->fileOff);
}
}
}
void Writer::run() {
- scanRelocations();
- createHiddenSections();
- // Sort and assign sections to their respective segments. No more sections can
- // be created after this method runs.
- sortSections();
// dyld requires __LINKEDIT segment to always exist (even if empty).
- getOrCreateOutputSegment(segment_names::linkEdit);
- // No more segments can be created after this method runs.
+ OutputSegment *linkEditSegment =
+ getOrCreateOutputSegment(segment_names::linkEdit);
+
+ scanRelocations();
+
+ // Sort and assign sections to their respective segments. No more sections nor
+ // segments may be created after this method runs.
+ createOutputSections();
+ sortOutputSegmentsAndSections();
+
createLoadCommands();
// Ensure that segments (and the sections they contain) are allocated
@@ -475,7 +392,8 @@ void Writer::run() {
// determine addresses of other segments/sections before generating its
// contents.
for (OutputSegment *seg : outputSegments)
- assignAddresses(seg);
+ if (seg != linkEditSegment)
+ assignAddresses(seg);
// Fill __LINKEDIT contents.
bindingSection->finalizeContents();
@@ -483,9 +401,8 @@ void Writer::run() {
symtabSection->finalizeContents();
// Now that __LINKEDIT is filled out, do a proper calculation of its
- // addresses and offsets. We don't have to recalculate the other segments
- // since sortSections() ensures that __LINKEDIT is the last segment.
- assignAddresses(getOutputSegment(segment_names::linkEdit));
+ // addresses and offsets.
+ assignAddresses(linkEditSegment);
openFile();
if (errorCount())
@@ -499,6 +416,4 @@ void Writer::run() {
void macho::writeResult() { Writer().run(); }
-void macho::createSyntheticSections() {
- in.got = createInputSection<GotSection>();
-}
+void macho::createSyntheticSections() { in.got = make<GotSection>(); }
diff --git a/lld/test/MachO/Inputs/libfunction.s b/lld/test/MachO/Inputs/libfunction.s
new file mode 100644
index 000000000000..fe0b3879a41a
--- /dev/null
+++ b/lld/test/MachO/Inputs/libfunction.s
@@ -0,0 +1,6 @@
+.section __TEXT,__text
+.globl _some_function
+
+_some_function:
+ mov $1, %rax
+ ret
diff --git a/lld/test/MachO/section-merge.s b/lld/test/MachO/section-merge.s
new file mode 100644
index 000000000000..bd3563718aa9
--- /dev/null
+++ b/lld/test/MachO/section-merge.s
@@ -0,0 +1,35 @@
+# REQUIRES: x86
+# RUN: mkdir -p %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libhello.s \
+# RUN: -o %t/libhello.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libgoodbye.s \
+# RUN: -o %t/libgoodbye.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %p/Inputs/libfunction.s \
+# RUN: -o %t/libfunction.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s \
+# RUN: -o %t/main.o
+# RUN: lld -flavor darwinnew -o %t/output %t/libfunction.o %t/libgoodbye.o %t/libhello.o %t/main.o
+
+# RUN: llvm-objdump --syms %t/output | FileCheck %s
+# CHECK: SYMBOL TABLE:
+# CHECK-DAG: {{[0-9a-z]+}} g O __TEXT,__cstring _goodbye_world
+# CHECK-DAG: {{[0-9a-z]+}} g O __TEXT,__cstring _hello_its_me
+# CHECK-DAG: {{[0-9a-z]+}} g O __TEXT,__cstring _hello_world
+# CHECK-DAG: {{[0-9a-z]+}} g F __TEXT,__text _main
+# CHECK-DAG: {{[0-9a-z]+}} g F __TEXT,__text _some_function
+
+# RUN: llvm-objdump -d %t/output | FileCheck %s --check-prefix DATA
+# DATA: Disassembly of section __TEXT,__text:
+# DATA: {{0*}}[[#%x,BASE:]] <_some_function>:
+# DATA-NEXT: [[#BASE]]: 48 c7 c0 01 00 00 00 movq $1, %rax
+# DATA-NEXT: [[#BASE + 0x7]]: c3 retq
+# DATA: {{0*}}[[#BASE + 0x8]] <_main>:
+# DATA-NEXT: [[#BASE + 0x8]]: 48 c7 c0 00 00 00 00 movq $0, %rax
+# DATA-NEXT: [[#BASE + 0xf]]: c3 retq
+
+.section __TEXT,__text
+.global _main
+
+_main:
+ mov $0, %rax
+ ret
More information about the llvm-commits
mailing list