
This change adds the ability for lld to remove LEB padding from code section. This effectively shrinks the size of the resulting binary in proportion to the number of code relocations. Since there will be a performance cost this is currently only active for -O1 and above. Some toolchains may instead want to perform this compression as a post linker step (for example running a binary through binaryen will automatically compress these values). I imagine we might want to make this the default in the future. Differential Revision: https://reviews.llvm.org/D46416 llvm-svn: 332783
240 lines
6.7 KiB
C++
240 lines
6.7 KiB
C++
//===- OutputSections.cpp -------------------------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "OutputSections.h"
|
|
#include "InputChunks.h"
|
|
#include "InputFiles.h"
|
|
#include "OutputSegment.h"
|
|
#include "WriterUtils.h"
|
|
#include "lld/Common/ErrorHandler.h"
|
|
#include "lld/Common/Threads.h"
|
|
#include "llvm/ADT/Twine.h"
|
|
#include "llvm/Support/LEB128.h"
|
|
|
|
#define DEBUG_TYPE "lld"
|
|
|
|
using namespace llvm;
|
|
using namespace llvm::wasm;
|
|
using namespace lld;
|
|
using namespace lld::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_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";
|
|
default:
|
|
fatal("invalid section type");
|
|
}
|
|
}
|
|
|
|
// Returns a string, e.g. "FUNCTION(.text)".
|
|
std::string lld::toString(const OutputSection &Sec) {
|
|
if (!Sec.Name.empty())
|
|
return (Sec.getSectionName() + "(" + Sec.Name + ")").str();
|
|
return Sec.getSectionName();
|
|
}
|
|
|
|
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()));
|
|
}
|
|
|
|
CodeSection::CodeSection(ArrayRef<InputFunction *> Functions)
|
|
: OutputSection(WASM_SEC_CODE), Functions(Functions) {
|
|
assert(Functions.size() > 0);
|
|
|
|
raw_string_ostream OS(CodeSectionHeader);
|
|
writeUleb128(OS, Functions.size(), "function count");
|
|
OS.flush();
|
|
BodySize = CodeSectionHeader.size();
|
|
|
|
for (InputFunction *Func : Functions) {
|
|
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
|
|
parallelForEach(Functions,
|
|
[&](const InputChunk *Chunk) { Chunk->writeTo(Buf); });
|
|
}
|
|
|
|
uint32_t CodeSection::numRelocations() const {
|
|
uint32_t Count = 0;
|
|
for (const InputChunk *Func : Functions)
|
|
Count += Func->NumRelocations();
|
|
return Count;
|
|
}
|
|
|
|
void CodeSection::writeRelocations(raw_ostream &OS) const {
|
|
for (const InputChunk *C : Functions)
|
|
C->writeRelocations(OS);
|
|
}
|
|
|
|
DataSection::DataSection(ArrayRef<OutputSegment *> Segments)
|
|
: OutputSection(WASM_SEC_DATA), Segments(Segments) {
|
|
raw_string_ostream OS(DataSectionHeader);
|
|
|
|
writeUleb128(OS, Segments.size(), "data segment count");
|
|
OS.flush();
|
|
BodySize = DataSectionHeader.size();
|
|
|
|
for (OutputSegment *Segment : Segments) {
|
|
raw_string_ostream OS(Segment->Header);
|
|
writeUleb128(OS, 0, "memory index");
|
|
writeUleb128(OS, WASM_OPCODE_I32_CONST, "opcode:i32const");
|
|
writeSleb128(OS, Segment->StartVA, "memory offset");
|
|
writeUleb128(OS, WASM_OPCODE_END, "opcode:end");
|
|
writeUleb128(OS, Segment->Size, "segment size");
|
|
OS.flush();
|
|
|
|
Segment->SectionOffset = BodySize;
|
|
BodySize += Segment->Header.size() + Segment->Size;
|
|
log("Data segment: size=" + Twine(Segment->Size));
|
|
|
|
for (InputSegment *InputSeg : Segment->InputSegments)
|
|
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());
|
|
|
|
parallelForEach(Segments, [&](const OutputSegment *Segment) {
|
|
// 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::numRelocations() const {
|
|
uint32_t Count = 0;
|
|
for (const OutputSegment *Seg : Segments)
|
|
for (const InputChunk *InputSeg : Seg->InputSegments)
|
|
Count += InputSeg->NumRelocations();
|
|
return Count;
|
|
}
|
|
|
|
void DataSection::writeRelocations(raw_ostream &OS) const {
|
|
for (const OutputSegment *Seg : Segments)
|
|
for (const InputChunk *C : Seg->InputSegments)
|
|
C->writeRelocations(OS);
|
|
}
|
|
|
|
CustomSection::CustomSection(std::string Name,
|
|
ArrayRef<InputSection *> InputSections)
|
|
: OutputSection(WASM_SEC_CUSTOM, Name), PayloadSize(0),
|
|
InputSections(InputSections) {
|
|
raw_string_ostream OS(NameData);
|
|
encodeULEB128(Name.size(), OS);
|
|
OS << Name;
|
|
OS.flush();
|
|
|
|
for (InputSection *Section : InputSections) {
|
|
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
|
|
parallelForEach(InputSections,
|
|
[&](const InputSection *Section) { Section->writeTo(Buf); });
|
|
}
|
|
|
|
uint32_t CustomSection::numRelocations() const {
|
|
uint32_t Count = 0;
|
|
for (const InputSection *InputSect : InputSections)
|
|
Count += InputSect->NumRelocations();
|
|
return Count;
|
|
}
|
|
|
|
void CustomSection::writeRelocations(raw_ostream &OS) const {
|
|
for (const InputSection *S : InputSections)
|
|
S->writeRelocations(OS);
|
|
}
|