These relocations represent offsets from the __tls_base symbol. Previously we were just using normal MEMORY_ADDR relocations and relying on the linker to select a segment-offset rather and absolute value in Symbol::getVirtualAddress(). Using an explicit relocation type allows allow us to clearly distinguish absolute from relative relocations based on the relocation information alone. One place this is useful is being able to reject absolute relocation in the PIC case, but still accept TLS relocations. Differential Revision: https://reviews.llvm.org/D91276
280 lines
7.7 KiB
C++
280 lines
7.7 KiB
C++
//===- OutputSections.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 "OutputSections.h"
|
|
#include "InputChunks.h"
|
|
#include "InputFiles.h"
|
|
#include "OutputSegment.h"
|
|
#include "WriterUtils.h"
|
|
#include "lld/Common/ErrorHandler.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
#include "llvm/Support/Parallel.h"
|
|
|
|
#define DEBUG_TYPE "lld"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::wasm;
|
|
|
|
namespace lld {
|
|
|
|
// Returns a string, e.g. "FUNCTION(.text)".
|
|
std::string toString(const wasm::OutputSection &sec) {
|
|
if (!sec.name.empty())
|
|
return (sec.getSectionName() + "(" + sec.name + ")").str();
|
|
return std::string(sec.getSectionName());
|
|
}
|
|
|
|
namespace wasm {
|
|
static StringRef sectionTypeToString(uint32_t sectionType) {
|
|
switch (sectionType) {
|
|
case WASM_SEC_CUSTOM:
|
|
return "CUSTOM";
|
|
case WASM_SEC_TYPE:
|
|
return "TYPE";
|
|
case WASM_SEC_IMPORT:
|
|
return "IMPORT";
|
|
case WASM_SEC_FUNCTION:
|
|
return "FUNCTION";
|
|
case WASM_SEC_TABLE:
|
|
return "TABLE";
|
|
case WASM_SEC_MEMORY:
|
|
return "MEMORY";
|
|
case WASM_SEC_GLOBAL:
|
|
return "GLOBAL";
|
|
case WASM_SEC_EVENT:
|
|
return "EVENT";
|
|
case WASM_SEC_EXPORT:
|
|
return "EXPORT";
|
|
case WASM_SEC_START:
|
|
return "START";
|
|
case WASM_SEC_ELEM:
|
|
return "ELEM";
|
|
case WASM_SEC_CODE:
|
|
return "CODE";
|
|
case WASM_SEC_DATA:
|
|
return "DATA";
|
|
case WASM_SEC_DATACOUNT:
|
|
return "DATACOUNT";
|
|
default:
|
|
fatal("invalid section type");
|
|
}
|
|
}
|
|
|
|
StringRef OutputSection::getSectionName() const {
|
|
return sectionTypeToString(type);
|
|
}
|
|
|
|
void OutputSection::createHeader(size_t bodySize) {
|
|
raw_string_ostream os(header);
|
|
debugWrite(os.tell(), "section type [" + getSectionName() + "]");
|
|
encodeULEB128(type, os);
|
|
writeUleb128(os, bodySize, "section size");
|
|
os.flush();
|
|
log("createHeader: " + toString(*this) + " body=" + Twine(bodySize) +
|
|
" total=" + Twine(getSize()));
|
|
}
|
|
|
|
void CodeSection::finalizeContents() {
|
|
raw_string_ostream os(codeSectionHeader);
|
|
writeUleb128(os, functions.size(), "function count");
|
|
os.flush();
|
|
bodySize = codeSectionHeader.size();
|
|
|
|
for (InputFunction *func : functions) {
|
|
func->outputSec = this;
|
|
func->outputOffset = bodySize;
|
|
func->calculateSize();
|
|
bodySize += func->getSize();
|
|
}
|
|
|
|
createHeader(bodySize);
|
|
}
|
|
|
|
void CodeSection::writeTo(uint8_t *buf) {
|
|
log("writing " + toString(*this));
|
|
log(" size=" + Twine(getSize()));
|
|
log(" headersize=" + Twine(header.size()));
|
|
log(" codeheadersize=" + Twine(codeSectionHeader.size()));
|
|
buf += offset;
|
|
|
|
// Write section header
|
|
memcpy(buf, header.data(), header.size());
|
|
buf += header.size();
|
|
|
|
// Write code section headers
|
|
memcpy(buf, codeSectionHeader.data(), codeSectionHeader.size());
|
|
|
|
// Write code section bodies
|
|
for (const InputChunk *chunk : functions)
|
|
chunk->writeTo(buf);
|
|
}
|
|
|
|
uint32_t CodeSection::getNumRelocations() const {
|
|
uint32_t count = 0;
|
|
for (const InputChunk *func : functions)
|
|
count += func->getNumRelocations();
|
|
return count;
|
|
}
|
|
|
|
void CodeSection::writeRelocations(raw_ostream &os) const {
|
|
for (const InputChunk *c : functions)
|
|
c->writeRelocations(os);
|
|
}
|
|
|
|
void DataSection::finalizeContents() {
|
|
raw_string_ostream os(dataSectionHeader);
|
|
unsigned segmentCount =
|
|
std::count_if(segments.begin(), segments.end(),
|
|
[](OutputSegment *segment) { return !segment->isBss; });
|
|
|
|
#ifndef NDEBUG
|
|
unsigned activeCount = std::count_if(
|
|
segments.begin(), segments.end(), [](OutputSegment *segment) {
|
|
return (segment->initFlags & WASM_SEGMENT_IS_PASSIVE) == 0;
|
|
});
|
|
#endif
|
|
|
|
assert((!config->isPic || activeCount <= 1) &&
|
|
"Currenly only a single data segment is supported in PIC mode");
|
|
|
|
writeUleb128(os, segmentCount, "data segment count");
|
|
os.flush();
|
|
bodySize = dataSectionHeader.size();
|
|
|
|
for (OutputSegment *segment : segments) {
|
|
if (segment->isBss)
|
|
continue;
|
|
raw_string_ostream os(segment->header);
|
|
writeUleb128(os, segment->initFlags, "init flags");
|
|
if (segment->initFlags & WASM_SEGMENT_HAS_MEMINDEX)
|
|
writeUleb128(os, 0, "memory index");
|
|
if ((segment->initFlags & WASM_SEGMENT_IS_PASSIVE) == 0) {
|
|
WasmInitExpr initExpr;
|
|
if (config->isPic) {
|
|
initExpr.Opcode = WASM_OPCODE_GLOBAL_GET;
|
|
initExpr.Value.Global = WasmSym::memoryBase->getGlobalIndex();
|
|
} else {
|
|
// FIXME(wvo): I64?
|
|
initExpr.Opcode = WASM_OPCODE_I32_CONST;
|
|
initExpr.Value.Int32 = segment->startVA;
|
|
}
|
|
writeInitExpr(os, initExpr);
|
|
}
|
|
writeUleb128(os, segment->size, "segment size");
|
|
os.flush();
|
|
|
|
segment->sectionOffset = bodySize;
|
|
bodySize += segment->header.size() + segment->size;
|
|
log("Data segment: size=" + Twine(segment->size) + ", startVA=" +
|
|
Twine::utohexstr(segment->startVA) + ", name=" + segment->name);
|
|
|
|
for (InputSegment *inputSeg : segment->inputSegments) {
|
|
inputSeg->outputSec = this;
|
|
inputSeg->outputOffset = segment->sectionOffset + segment->header.size() +
|
|
inputSeg->outputSegmentOffset;
|
|
}
|
|
}
|
|
|
|
createHeader(bodySize);
|
|
}
|
|
|
|
void DataSection::writeTo(uint8_t *buf) {
|
|
log("writing " + toString(*this) + " size=" + Twine(getSize()) +
|
|
" body=" + Twine(bodySize));
|
|
buf += offset;
|
|
|
|
// Write section header
|
|
memcpy(buf, header.data(), header.size());
|
|
buf += header.size();
|
|
|
|
// Write data section headers
|
|
memcpy(buf, dataSectionHeader.data(), dataSectionHeader.size());
|
|
|
|
for (const OutputSegment *segment : segments) {
|
|
if (segment->isBss)
|
|
continue;
|
|
// Write data segment header
|
|
uint8_t *segStart = buf + segment->sectionOffset;
|
|
memcpy(segStart, segment->header.data(), segment->header.size());
|
|
|
|
// Write segment data payload
|
|
for (const InputChunk *chunk : segment->inputSegments)
|
|
chunk->writeTo(buf);
|
|
}
|
|
}
|
|
|
|
uint32_t DataSection::getNumRelocations() const {
|
|
uint32_t count = 0;
|
|
for (const OutputSegment *seg : segments)
|
|
for (const InputChunk *inputSeg : seg->inputSegments)
|
|
count += inputSeg->getNumRelocations();
|
|
return count;
|
|
}
|
|
|
|
void DataSection::writeRelocations(raw_ostream &os) const {
|
|
for (const OutputSegment *seg : segments)
|
|
for (const InputChunk *c : seg->inputSegments)
|
|
c->writeRelocations(os);
|
|
}
|
|
|
|
bool DataSection::isNeeded() const {
|
|
for (const OutputSegment *seg : segments)
|
|
if (!seg->isBss)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void CustomSection::finalizeContents() {
|
|
raw_string_ostream os(nameData);
|
|
encodeULEB128(name.size(), os);
|
|
os << name;
|
|
os.flush();
|
|
|
|
for (InputSection *section : inputSections) {
|
|
section->outputSec = this;
|
|
section->outputOffset = payloadSize;
|
|
payloadSize += section->getSize();
|
|
}
|
|
|
|
createHeader(payloadSize + nameData.size());
|
|
}
|
|
|
|
void CustomSection::writeTo(uint8_t *buf) {
|
|
log("writing " + toString(*this) + " size=" + Twine(getSize()) +
|
|
" chunks=" + Twine(inputSections.size()));
|
|
|
|
assert(offset);
|
|
buf += offset;
|
|
|
|
// Write section header
|
|
memcpy(buf, header.data(), header.size());
|
|
buf += header.size();
|
|
memcpy(buf, nameData.data(), nameData.size());
|
|
buf += nameData.size();
|
|
|
|
// Write custom sections payload
|
|
for (const InputSection *section : inputSections)
|
|
section->writeTo(buf);
|
|
}
|
|
|
|
uint32_t CustomSection::getNumRelocations() const {
|
|
uint32_t count = 0;
|
|
for (const InputSection *inputSect : inputSections)
|
|
count += inputSect->getNumRelocations();
|
|
return count;
|
|
}
|
|
|
|
void CustomSection::writeRelocations(raw_ostream &os) const {
|
|
for (const InputSection *s : inputSections)
|
|
s->writeRelocations(os);
|
|
}
|
|
|
|
} // namespace wasm
|
|
} // namespace lld
|