[lld][WebAssembly] Delay the merging of data section when dynamic linking

With dynamic linking we have the current limitation that there can be
only a single active data segment (since we use __memory_base as the
load address and we can't do arithmetic in constant expresions).

This change delays the merging of active segments until a little later
in the linking process which means that the grouping of data by section,
and the magic __start/__end symbols work as expected under dynamic
linking.

Differential Revision: https://reviews.llvm.org/D96453
This commit is contained in:
Sam Clegg 2021-02-10 10:11:52 -08:00
parent a680bc3a31
commit 70f3c6e9e6
8 changed files with 86 additions and 26 deletions

View File

@ -121,8 +121,8 @@
; PASSIVE32-PIC-NEXT: - Type: I32
; PASSIVE64-PIC-NEXT: - Type: I64
; PASSIVE-PIC-NEXT: Count: 1
; PASSIVE32-PIC-NEXT: Body: 230141B4CE006A2100200041004101FE480200044020004101427FFE0102001A05410023016A410041B1CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE64-PIC-NEXT: Body: 230142B4CE006A2100200041004101FE480200044020004101427FFE0102001A05420023016A410041B1CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE32-PIC-NEXT: Body: 230141B4CE006A2100200041004101FE480200044020004101427FFE0102001A05410023016A410041B4CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE64-PIC-NEXT: Body: 230142B4CE006A2100200041004101FE480200044020004101427FFE0102001A05420023016A410041B4CE00FC08000020004102FE1702002000417FFE0002001A0BFC09000B
; PASSIVE-PIC-NEXT: - Index: 3
; PASSIVE-PIC-NEXT: Locals: []
; PASSIVE-PIC-NEXT: Body: 0B

View File

@ -54,7 +54,7 @@ tls1:
# CHECK-NEXT: Mutable: false
# CHECK-NEXT: InitExpr:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1028
# CHECK-NEXT: Value: 1024
# CHECK-NEXT: - Type: EXPORT
# CHECK: - Type: DATA
@ -65,11 +65,11 @@ tls1:
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1024
# CHECK-NEXT: Content: 2A000000
# CHECK-NEXT: Content: 2B000000
# .tdata
# CHECK-NEXT: - SectionOffset: 17
# CHECK-NEXT: InitFlags: 0
# CHECK-NEXT: Offset:
# CHECK-NEXT: Opcode: I32_CONST
# CHECK-NEXT: Value: 1028
# CHECK-NEXT: Content: 2B000000
# CHECK-NEXT: Content: 2A000000

View File

@ -353,6 +353,10 @@ void InputFunction::writeTo(uint8_t *buf) const {
LLVM_DEBUG(dbgs() << " total: " << (buf + chunkSize - orig) << "\n");
}
uint64_t InputSegment::getVA() const {
return outputSeg->startVA + outputSegmentOffset;
}
// Generate code to apply relocations to the data section at runtime.
// This is only called when generating shared libaries (PIC) where address are
// not known at static link time.
@ -370,10 +374,9 @@ void InputSegment::generateRelocationCode(raw_ostream &os) const {
auto tombstone = getTombstone();
// TODO(sbc): Encode the relocations in the data section and write a loop
// here to apply them.
uint64_t segmentVA = outputSeg->startVA + outputSegmentOffset;
for (const WasmRelocation &rel : relocations) {
uint64_t offset = rel.Offset - getInputSectionOffset();
uint64_t outputOffset = segmentVA + offset;
uint64_t outputOffset = getVA() + offset;
LLVM_DEBUG(dbgs() << "gen reloc: type=" << relocTypeToString(rel.Type)
<< " addend=" << rel.Addend << " index=" << rel.Index

View File

@ -108,6 +108,7 @@ public:
uint32_t getInputSectionOffset() const override {
return segment.SectionOffset;
}
uint64_t getVA() const;
const OutputSegment *outputSeg = nullptr;
int32_t outputSegmentOffset = 0;

View File

@ -138,7 +138,7 @@ void lld::wasm::writeMapFile(ArrayRef<OutputSection *> outputSections) {
oseg->size);
os << oseg->name << '\n';
for (auto *chunk : oseg->inputSegments) {
writeHeader(os, oseg->startVA + chunk->outputSegmentOffset,
writeHeader(os, chunk->getVA(),
chunk->outputSec->getOffset() + chunk->outputOffset,
chunk->getSize());
os.indent(8) << toString(chunk) << '\n';

View File

@ -22,10 +22,11 @@ class OutputSegment {
public:
OutputSegment(StringRef n) : name(n) {}
void addInputSegment(InputSegment *inSeg) {
alignment = std::max(alignment, inSeg->getAlignment());
void addInputSegment(InputSegment *inSeg, uint32_t forceAlignment = 0) {
uint32_t segAlign = std::max(forceAlignment, inSeg->getAlignment());
alignment = std::max(alignment, segAlign);
inputSegments.push_back(inSeg);
size = llvm::alignTo(size, 1ULL << inSeg->getAlignment());
size = llvm::alignTo(size, 1ULL << segAlign);
inSeg->outputSeg = this;
inSeg->outputSegmentOffset = size;
size += inSeg->getSize();

View File

@ -279,7 +279,7 @@ DefinedFunction::DefinedFunction(StringRef name, uint32_t flags, InputFile *f,
uint64_t DefinedData::getVirtualAddress() const {
LLVM_DEBUG(dbgs() << "getVirtualAddress: " << getName() << "\n");
if (segment)
return segment->outputSeg->startVA + segment->outputSegmentOffset + offset;
return segment->getVA() + offset;
return offset;
}

View File

@ -77,6 +77,7 @@ private:
void calculateCustomSections();
void calculateTypes();
void createOutputSegments();
void combineOutputSegments();
void layoutMemory();
void createHeader();
@ -86,6 +87,7 @@ private:
void createCustomSections();
void createSyntheticSections();
void createSyntheticSectionsPostLayout();
void finalizeSections();
// Custom sections
@ -795,10 +797,6 @@ static StringRef getOutputDataSegmentName(StringRef name) {
// We also need to merge .tbss into .tdata so they share the same offsets.
if (name.startswith(".tdata") || name.startswith(".tbss"))
return ".tdata";
// With PIC code we currently only support a single data segment since
// we only have a single __memory_base to use as our base address.
if (config->isPic)
return ".data";
if (!config->mergeDataSegments)
return name;
if (name.startswith(".text."))
@ -843,9 +841,9 @@ void Writer::createOutputSegments() {
[](const OutputSegment *a, const OutputSegment *b) {
auto order = [](StringRef name) {
return StringSwitch<int>(name)
.StartsWith(".rodata", 0)
.StartsWith(".data", 1)
.StartsWith(".tdata", 2)
.StartsWith(".tdata", 0)
.StartsWith(".rodata", 1)
.StartsWith(".data", 2)
.StartsWith(".bss", 4)
.Default(3);
};
@ -856,6 +854,52 @@ void Writer::createOutputSegments() {
segments[i]->index = i;
}
void Writer::combineOutputSegments() {
// With PIC code we currently only support a single data segment since
// we only have a single __memory_base to use as our base address.
// This pass combines all non-TLS data segments into a single .data
// segment.
// This restructions can be relaxed once we have extended constant
// expressions available:
// https://github.com/WebAssembly/extended-const
assert(config->isPic);
if (segments.size() <= 1)
return;
OutputSegment *combined = nullptr;
std::vector<OutputSegment *> new_segments;
for (OutputSegment *s : segments) {
if (s->name == ".tdata") {
new_segments.push_back(s);
} else {
if (!combined) {
combined = make<OutputSegment>(".data");
combined->startVA = s->startVA;
if (config->sharedMemory)
combined->initFlags = WASM_DATA_SEGMENT_IS_PASSIVE;
}
bool first = true;
for (InputSegment *inSeg : s->inputSegments) {
uint32_t alignment = first ? s->alignment : 0;
first = false;
#ifndef NDEBUG
uint64_t oldVA = inSeg->getVA();
#endif
combined->addInputSegment(inSeg, alignment);
#ifndef NDEBUG
uint64_t newVA = inSeg->getVA();
assert(oldVA == newVA);
#endif
}
}
}
if (combined) {
new_segments.push_back(combined);
segments = new_segments;
for (size_t i = 0; i < segments.size(); ++i)
segments[i]->index = i;
}
}
static void createFunction(DefinedFunction *func, StringRef bodyContent) {
std::string functionBody;
{
@ -1295,11 +1339,14 @@ void Writer::createSyntheticSections() {
out.exportSec = make<ExportSection>();
out.startSec = make<StartSection>();
out.elemSec = make<ElemSection>();
out.producersSec = make<ProducersSection>();
out.targetFeaturesSec = make<TargetFeaturesSection>();
}
void Writer::createSyntheticSectionsPostLayout() {
out.dataCountSec = make<DataCountSection>(segments);
out.linkingSec = make<LinkingSection>(initFunctions, segments);
out.nameSec = make<NameSection>(segments);
out.producersSec = make<ProducersSection>();
out.targetFeaturesSec = make<TargetFeaturesSection>();
}
void Writer::run() {
@ -1318,18 +1365,15 @@ void Writer::run() {
createOutputSegments();
log("-- createSyntheticSections");
createSyntheticSections();
log("-- populateProducers");
populateProducers();
log("-- calculateImports");
calculateImports();
log("-- layoutMemory");
layoutMemory();
if (!config->relocatable) {
// Create linker synthesized __start_SECNAME/__stop_SECNAME symbols
// This has to be done after memory layout is performed.
for (const OutputSegment *seg : segments)
for (const OutputSegment *seg : segments) {
addStartStopSymbols(seg);
}
}
// Delay reporting error about explict exports until after addStartStopSymbols
@ -1345,6 +1389,17 @@ void Writer::run() {
warn(Twine("symbol exported via --export not found: ") + name);
}
if (config->isPic) {
log("-- combineOutputSegments");
combineOutputSegments();
}
log("-- createSyntheticSectionsPostLayout");
createSyntheticSectionsPostLayout();
log("-- populateProducers");
populateProducers();
log("-- calculateImports");
calculateImports();
log("-- scanRelocations");
scanRelocations();
log("-- finalizeIndirectFunctionTable");