[WebAssembly] Fix symbol exports under -r/--relocatable

This change cleans up the way wasm exports and globals
are generated, particualrly for -r/--relocatable where
globals need to be created and exported in order for
output relocations which reference them.

Remove the need for a per file GlobalIndexOffset and
instead set the output index for each symbol directly.
This simplifies the code in several places.

Differential Revision: https://reviews.llvm.org/D40859

llvm-svn: 320001
This commit is contained in:
Sam Clegg 2017-12-07 01:51:24 +00:00
parent c325d30d2c
commit 74fe0ba105
9 changed files with 94 additions and 117 deletions

View File

@ -23,6 +23,11 @@ target triple = "wasm32-unknown-unknown-wasm"
; CHECK-NEXT: Mutable: false ; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr: ; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1052
; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1024 ; CHECK-NEXT: Value: 1024
; CHECK-NEXT: - Type: I32 ; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Mutable: false ; CHECK-NEXT: Mutable: false
@ -34,16 +39,11 @@ target triple = "wasm32-unknown-unknown-wasm"
; CHECK-NEXT: InitExpr: ; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST ; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1048 ; CHECK-NEXT: Value: 1048
; CHECK-NEXT: - Type: I32
; CHECK-NEXT: Mutable: false
; CHECK-NEXT: InitExpr:
; CHECK-NEXT: Opcode: I32_CONST
; CHECK-NEXT: Value: 1052
; CHECK: - Type: DATA ; CHECK: - Type: DATA
; CHECK-NEXT: Relocations: ; CHECK-NEXT: Relocations:
; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32 ; CHECK-NEXT: - Type: R_WEBASSEMBLY_MEMORY_ADDR_I32
; CHECK-NEXT: Index: 4 ; CHECK-NEXT: Index: 1
; CHECK-NEXT: Offset: 0x0000001F ; CHECK-NEXT: Offset: 0x0000001F
; CHECK-NEXT: Segments: ; CHECK-NEXT: Segments:
; CHECK-NEXT: - SectionOffset: 7 ; CHECK-NEXT: - SectionOffset: 7

View File

@ -93,6 +93,18 @@ declare i32 @foo_import() local_unnamed_addr
; CHECK-NEXT: - Name: my_func ; CHECK-NEXT: - Name: my_func
; CHECK-NEXT: Kind: FUNCTION ; CHECK-NEXT: Kind: FUNCTION
; CHECK-NEXT: Index: 3 ; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: hello_str
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 1
; CHECK-NEXT: - Name: func_addr1
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 2
; CHECK-NEXT: - Name: func_addr2
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 3
; CHECK-NEXT: - Name: data_addr1
; CHECK-NEXT: Kind: GLOBAL
; CHECK-NEXT: Index: 4
; CHECK-NEXT: - Type: ELEM ; CHECK-NEXT: - Type: ELEM
; CHECK-NEXT: Segments: ; CHECK-NEXT: Segments:
; CHECK-NEXT: - Offset: ; CHECK-NEXT: - Offset:

View File

@ -142,7 +142,6 @@ static Symbol* addSyntheticGlobal(StringRef Name, int32_t Value) {
log("injecting global: " + Name); log("injecting global: " + Name);
Symbol *S = Symtab->addDefinedGlobal(Name); Symbol *S = Symtab->addDefinedGlobal(Name);
S->setVirtualAddress(Value); S->setVirtualAddress(Value);
S->setOutputIndex(Config->SyntheticGlobals.size());
Config->SyntheticGlobals.emplace_back(S); Config->SyntheticGlobals.emplace_back(S);
return S; return S;
} }

View File

@ -47,7 +47,6 @@ void ObjFile::dumpInfo() const {
" FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" + " FunctionIndexOffset : " + Twine(FunctionIndexOffset) + "\n" +
" NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" + " NumFunctionImports : " + Twine(NumFunctionImports()) + "\n" +
" TableIndexOffset : " + Twine(TableIndexOffset) + "\n" + " TableIndexOffset : " + Twine(TableIndexOffset) + "\n" +
" GlobalIndexOffset : " + Twine(GlobalIndexOffset) + "\n" +
" NumGlobalImports : " + Twine(NumGlobalImports()) + "\n"); " NumGlobalImports : " + Twine(NumGlobalImports()) + "\n");
} }
@ -68,15 +67,10 @@ uint32_t ObjFile::getRelocatedAddress(uint32_t Index) const {
} }
uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const { uint32_t ObjFile::relocateFunctionIndex(uint32_t Original) const {
DEBUG(dbgs() << "relocateFunctionIndex: " << Original);
const Symbol *Sym = getFunctionSymbol(Original); const Symbol *Sym = getFunctionSymbol(Original);
uint32_t Index; uint32_t Index = Sym->getOutputIndex();
if (Sym) DEBUG(dbgs() << "relocateFunctionIndex: " << toString(*Sym) << ": "
Index = Sym->getOutputIndex(); << Original << " -> " << Index << "\n");
else
Index = Original + FunctionIndexOffset;
DEBUG(dbgs() << " -> " << Index << "\n");
return Index; return Index;
} }
@ -89,15 +83,10 @@ uint32_t ObjFile::relocateTableIndex(uint32_t Original) const {
} }
uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const { uint32_t ObjFile::relocateGlobalIndex(uint32_t Original) const {
DEBUG(dbgs() << "relocateGlobalIndex: " << Original);
uint32_t Index;
const Symbol *Sym = getGlobalSymbol(Original); const Symbol *Sym = getGlobalSymbol(Original);
if (Sym) uint32_t Index = Sym->getOutputIndex();
Index = Sym->getOutputIndex(); DEBUG(dbgs() << "relocateGlobalIndex: " << toString(*Sym) << ": " << Original
else << " -> " << Index << "\n");
Index = Original + GlobalIndexOffset;
DEBUG(dbgs() << " -> " << Index << "\n");
return Index; return Index;
} }

View File

@ -103,7 +103,6 @@ public:
size_t NumGlobalImports() const { return GlobalImports; } size_t NumGlobalImports() const { return GlobalImports; }
int32_t FunctionIndexOffset = 0; int32_t FunctionIndexOffset = 0;
int32_t GlobalIndexOffset = 0;
int32_t TableIndexOffset = 0; int32_t TableIndexOffset = 0;
const WasmSection *CodeSection = nullptr; const WasmSection *CodeSection = nullptr;
std::vector<OutputRelocation> CodeRelocations; std::vector<OutputRelocation> CodeRelocations;

View File

@ -72,9 +72,8 @@ std::string lld::toString(OutputSection *Section) {
static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) { static void applyRelocation(uint8_t *Buf, const OutputRelocation &Reloc) {
DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type DEBUG(dbgs() << "write reloc: type=" << Reloc.Reloc.Type
<< " index=" << Reloc.Reloc.Index << " new=" << Reloc.NewIndex << " index=" << Reloc.Reloc.Index << " value=" << Reloc.Value
<< " value=" << Reloc.Value << " offset=" << Reloc.Reloc.Offset << " offset=" << Reloc.Reloc.Offset << "\n");
<< "\n");
Buf += Reloc.Reloc.Offset; Buf += Reloc.Reloc.Offset;
int64_t ExistingValue; int64_t ExistingValue;
switch (Reloc.Reloc.Type) { switch (Reloc.Reloc.Type) {
@ -149,15 +148,18 @@ static void calcRelocations(const ObjFile &File,
int32_t OutputOffset) { int32_t OutputOffset) {
log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset)); log("calcRelocations: " + File.getName() + " offset=" + Twine(OutputOffset));
for (const WasmRelocation &Reloc : Relocs) { for (const WasmRelocation &Reloc : Relocs) {
int64_t NewIndex = calcNewIndex(File, Reloc);
OutputRelocation NewReloc; OutputRelocation NewReloc;
NewReloc.Reloc = Reloc; NewReloc.Reloc = Reloc;
NewReloc.Reloc.Offset += OutputOffset; NewReloc.Reloc.Offset += OutputOffset;
NewReloc.NewIndex = NewIndex;
DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index DEBUG(dbgs() << "reloc: type=" << Reloc.Type << " index=" << Reloc.Index
<< " offset=" << Reloc.Offset << " new=" << NewIndex << " offset=" << Reloc.Offset
<< " newOffset=" << NewReloc.Reloc.Offset << "\n"); << " newOffset=" << NewReloc.Reloc.Offset << "\n");
if (Config->EmitRelocs)
NewReloc.NewIndex = calcNewIndex(File, Reloc);
else
NewReloc.NewIndex = UINT32_MAX;
switch (Reloc.Type) { switch (Reloc.Type) {
case R_WEBASSEMBLY_MEMORY_ADDR_SLEB: case R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case R_WEBASSEMBLY_MEMORY_ADDR_I32: case R_WEBASSEMBLY_MEMORY_ADDR_I32:
@ -167,7 +169,8 @@ static void calcRelocations(const ObjFile &File,
NewReloc.Value += Reloc.Addend; NewReloc.Value += Reloc.Addend;
break; break;
default: default:
NewReloc.Value = NewIndex; NewReloc.Value = calcNewIndex(File, Reloc);
break;
} }
OutputRelocs.emplace_back(NewReloc); OutputRelocs.emplace_back(NewReloc);

View File

@ -34,8 +34,7 @@ void SymbolTable::addFile(InputFile *File) {
void SymbolTable::reportRemainingUndefines() { void SymbolTable::reportRemainingUndefines() {
std::unordered_set<Symbol *> Undefs; std::unordered_set<Symbol *> Undefs;
for (auto &I : SymMap) { for (Symbol *Sym : SymVector) {
Symbol *Sym = I.second;
if (Sym->isUndefined() && !Sym->isWeak() && if (Sym->isUndefined() && !Sym->isWeak() &&
Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) { Config->AllowUndefinedSymbols.count(Sym->getName()) == 0) {
Undefs.insert(Sym); Undefs.insert(Sym);
@ -67,6 +66,7 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name) {
if (Sym) if (Sym)
return {Sym, false}; return {Sym, false};
Sym = make<Symbol>(Name, false); Sym = make<Symbol>(Name, false);
SymVector.emplace_back(Sym);
return {Sym, true}; return {Sym, true};
} }

View File

@ -47,6 +47,7 @@ public:
void reportDuplicate(Symbol *Existing, InputFile *NewFile); void reportDuplicate(Symbol *Existing, InputFile *NewFile);
void reportRemainingUndefines(); void reportRemainingUndefines();
ArrayRef<Symbol *> getSymbols() const { return SymVector; }
Symbol *find(StringRef Name); Symbol *find(StringRef Name);
Symbol *addDefined(InputFile *F, const WasmSymbol *Sym, Symbol *addDefined(InputFile *F, const WasmSymbol *Sym,
@ -60,6 +61,7 @@ private:
std::pair<Symbol *, bool> insert(StringRef Name); std::pair<Symbol *, bool> insert(StringRef Name);
llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap; llvm::DenseMap<llvm::CachedHashStringRef, Symbol *> SymMap;
std::vector<Symbol *> SymVector;
}; };
extern SymbolTable *Symtab; extern SymbolTable *Symtab;

View File

@ -111,8 +111,9 @@ private:
std::vector<const WasmSignature *> Types; std::vector<const WasmSignature *> Types;
DenseMap<WasmSignature, int32_t, WasmSignatureDenseMapInfo> TypeIndices; DenseMap<WasmSignature, int32_t, WasmSignatureDenseMapInfo> TypeIndices;
std::vector<Symbol *> FunctionImports; std::vector<const Symbol *> FunctionImports;
std::vector<Symbol *> GlobalImports; std::vector<const Symbol *> GlobalImports;
std::vector<const Symbol *> DefinedGlobals;
// Elements that are used to construct the final output // Elements that are used to construct the final output
std::string Header; std::string Header;
@ -217,11 +218,14 @@ void Writer::createMemorySection() {
} }
void Writer::createGlobalSection() { void Writer::createGlobalSection() {
if (DefinedGlobals.empty())
return;
SyntheticSection *Section = createSyntheticSection(WASM_SEC_GLOBAL); SyntheticSection *Section = createSyntheticSection(WASM_SEC_GLOBAL);
raw_ostream &OS = Section->getStream(); raw_ostream &OS = Section->getStream();
writeUleb128(OS, NumGlobals, "global count"); writeUleb128(OS, DefinedGlobals.size(), "global count");
for (const Symbol *Sym : Config->SyntheticGlobals) { for (const Symbol *Sym : DefinedGlobals) {
WasmGlobal Global; WasmGlobal Global;
Global.Type = WASM_TYPE_I32; Global.Type = WASM_TYPE_I32;
Global.Mutable = Sym == Config->StackPointerSymbol; Global.Mutable = Sym == Config->StackPointerSymbol;
@ -229,24 +233,6 @@ void Writer::createGlobalSection() {
Global.InitExpr.Value.Int32 = Sym->getVirtualAddress(); Global.InitExpr.Value.Int32 = Sym->getVirtualAddress();
writeGlobal(OS, Global); writeGlobal(OS, Global);
} }
if (Config->EmitRelocs) {
for (ObjFile *File : Symtab->ObjectFiles) {
uint32_t GlobalIndex = File->NumGlobalImports();
for (const WasmGlobal &Global : File->getWasmObj()->globals()) {
WasmGlobal RelocatedGlobal(Global);
if (Global.Type != WASM_TYPE_I32)
fatal("unsupported global type: " + Twine(Global.Type));
if (Global.InitExpr.Opcode != WASM_OPCODE_I32_CONST)
fatal("unsupported global init opcode: " +
Twine(Global.InitExpr.Opcode));
RelocatedGlobal.InitExpr.Value.Int32 =
File->getRelocatedAddress(GlobalIndex);
writeGlobal(OS, RelocatedGlobal);
++GlobalIndex;
}
}
}
} }
void Writer::createTableSection() { void Writer::createTableSection() {
@ -261,35 +247,36 @@ void Writer::createTableSection() {
} }
void Writer::createExportSection() { void Writer::createExportSection() {
// Memory is and main function are exported for executables.
bool ExportMemory = !Config->Relocatable && !Config->ImportMemory; bool ExportMemory = !Config->Relocatable && !Config->ImportMemory;
bool ExportOther = true; // ??? TODO Config->Relocatable;
bool ExportHidden = Config->Relocatable;
Symbol *EntrySym = Symtab->find(Config->Entry); Symbol *EntrySym = Symtab->find(Config->Entry);
bool ExportEntry = !Config->Relocatable && EntrySym && EntrySym->isDefined(); bool ExportEntry = !Config->Relocatable && EntrySym && EntrySym->isDefined();
bool ExportHidden = Config->EmitRelocs;
uint32_t NumExports = 0; uint32_t NumExports = ExportMemory ? 1 : 0;
if (ExportMemory)
++NumExports;
std::vector<const Symbol *> SymbolExports;
if (ExportEntry) if (ExportEntry)
++NumExports; SymbolExports.emplace_back(EntrySym);
if (ExportOther) { for (const Symbol *Sym : Symtab->getSymbols()) {
for (ObjFile *File : Symtab->ObjectFiles) { if (Sym->isUndefined() || Sym->isGlobal())
for (Symbol *Sym : File->getSymbols()) {
if (!Sym->isFunction() || Sym->isLocal() || Sym->isUndefined() ||
(Sym->isHidden() && !ExportHidden) || Sym->WrittenToSymtab)
continue; continue;
if (Sym == EntrySym) if (Sym->isHidden() && !ExportHidden)
continue; continue;
Sym->WrittenToSymtab = true; if (ExportEntry && Sym == EntrySym)
++NumExports; continue;
} SymbolExports.emplace_back(Sym);
}
} }
for (const Symbol *Sym : DefinedGlobals) {
// Can't export the SP right now because it mutable and mutable globals
// connot be exported.
if (Sym == Config->StackPointerSymbol)
continue;
SymbolExports.emplace_back(Sym);
}
NumExports += SymbolExports.size();
if (!NumExports) if (!NumExports)
return; return;
@ -306,23 +293,7 @@ void Writer::createExportSection() {
writeExport(OS, MemoryExport); writeExport(OS, MemoryExport);
} }
if (ExportEntry) { for (const Symbol *Sym : SymbolExports) {
WasmExport EntryExport;
EntryExport.Name = Config->Entry;
EntryExport.Kind = WASM_EXTERNAL_FUNCTION;
EntryExport.Index = EntrySym->getOutputIndex();
writeExport(OS, EntryExport);
}
if (ExportOther) {
for (ObjFile *File : Symtab->ObjectFiles) {
for (Symbol *Sym : File->getSymbols()) {
if (!Sym->isFunction() || Sym->isLocal() || Sym->isUndefined() ||
(Sym->isHidden() && !ExportHidden) || !Sym->WrittenToSymtab)
continue;
if (Sym == EntrySym)
continue;
Sym->WrittenToSymtab = false;
log("Export: " + Sym->getName()); log("Export: " + Sym->getName());
WasmExport Export; WasmExport Export;
Export.Name = Sym->getName(); Export.Name = Sym->getName();
@ -334,8 +305,6 @@ void Writer::createExportSection() {
writeExport(OS, Export); writeExport(OS, Export);
} }
} }
}
}
void Writer::createStartSection() {} void Writer::createStartSection() {}
@ -557,7 +526,6 @@ void Writer::createSections() {
} }
void Writer::calculateOffsets() { void Writer::calculateOffsets() {
NumGlobals = Config->SyntheticGlobals.size();
NumTableElems = InitialTableOffset; NumTableElems = InitialTableOffset;
for (ObjFile *File : Symtab->ObjectFiles) { for (ObjFile *File : Symtab->ObjectFiles) {
@ -568,13 +536,6 @@ void Writer::calculateOffsets() {
FunctionImports.size() - File->NumFunctionImports() + NumFunctions; FunctionImports.size() - File->NumFunctionImports() + NumFunctions;
NumFunctions += WasmFile->functions().size(); NumFunctions += WasmFile->functions().size();
// Global Index
if (Config->EmitRelocs) {
File->GlobalIndexOffset =
GlobalImports.size() - File->NumGlobalImports() + NumGlobals;
NumGlobals += WasmFile->globals().size();
}
// Memory // Memory
if (WasmFile->memories().size()) { if (WasmFile->memories().size()) {
if (WasmFile->memories().size() > 1) { if (WasmFile->memories().size() > 1) {
@ -640,19 +601,31 @@ void Writer::calculateTypes() {
} }
void Writer::assignSymbolIndexes() { void Writer::assignSymbolIndexes() {
uint32_t GlobalIndex = GlobalImports.size();
if (Config->StackPointerSymbol) {
DefinedGlobals.emplace_back(Config->StackPointerSymbol);
Config->StackPointerSymbol->setOutputIndex(GlobalIndex++);
}
if (Config->EmitRelocs)
DefinedGlobals.reserve(Symtab->getSymbols().size());
for (ObjFile *File : Symtab->ObjectFiles) { for (ObjFile *File : Symtab->ObjectFiles) {
DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n"); DEBUG(dbgs() << "assignSymbolIndexes: " << File->getName() << "\n");
for (Symbol *Sym : File->getSymbols()) { for (Symbol *Sym : File->getSymbols()) {
if (Sym->hasOutputIndex() || !Sym->isDefined()) if (Sym->hasOutputIndex() || !Sym->isDefined())
continue; continue;
if (Sym->isFunction()) {
if (Sym->getFile() && isa<ObjFile>(Sym->getFile())) { if (Sym->getFile() && isa<ObjFile>(Sym->getFile())) {
auto *Obj = cast<ObjFile>(Sym->getFile()); auto *Obj = cast<ObjFile>(Sym->getFile());
if (Sym->isFunction())
Sym->setOutputIndex(Obj->FunctionIndexOffset + Sym->setOutputIndex(Obj->FunctionIndexOffset +
Sym->getFunctionIndex()); Sym->getFunctionIndex());
else }
Sym->setOutputIndex(Obj->GlobalIndexOffset + Sym->getGlobalIndex()); } else if (Config->EmitRelocs) {
DefinedGlobals.emplace_back(Sym);
Sym->setOutputIndex(GlobalIndex++);
} }
} }
} }