Skip to content

Commit a5ca34e

Browse files
committed
[WebAssebmly] Add support for --wrap
The code for implementing this features is taken almost verbatim from the ELF backend. Fixes: https://bugs.llvm.org/show_bug.cgi?id=41681 Differential Revision: https://reviews.llvm.org/D62380 llvm-svn: 361639
1 parent e1947b8 commit a5ca34e

File tree

11 files changed

+172
-11
lines changed

11 files changed

+172
-11
lines changed

lld/ELF/Driver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1522,7 +1522,7 @@ static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
15221522

15231523
// Update pointers in input files.
15241524
parallelForEach(ObjectFiles, [&](InputFile *File) {
1525-
std::vector<Symbol *> &Syms = File->getMutableSymbols();
1525+
MutableArrayRef<Symbol *> Syms = File->getMutableSymbols();
15261526
for (size_t I = 0, E = Syms.size(); I != E; ++I)
15271527
if (Symbol *S = Map.lookup(Syms[I]))
15281528
Syms[I] = S;

lld/ELF/InputFiles.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ class InputFile {
9090
// function on files of other types.
9191
ArrayRef<Symbol *> getSymbols() { return getMutableSymbols(); }
9292

93-
std::vector<Symbol *> &getMutableSymbols() {
93+
MutableArrayRef<Symbol *> getMutableSymbols() {
9494
assert(FileKind == BinaryKind || FileKind == ObjKind ||
9595
FileKind == BitcodeKind);
9696
return Symbols;

lld/include/lld/Common/LLVM.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Twine;
2929
class MemoryBuffer;
3030
class MemoryBufferRef;
3131
template <typename T> class ArrayRef;
32+
template <typename T> class MutableArrayRef;
3233
template <unsigned InternalLen> class SmallString;
3334
template <typename T, unsigned N> class SmallVector;
3435
template <typename T> class ErrorOr;
@@ -62,6 +63,7 @@ using llvm::isa;
6263

6364
// ADT's.
6465
using llvm::ArrayRef;
66+
using llvm::MutableArrayRef;
6567
using llvm::Error;
6668
using llvm::ErrorOr;
6769
using llvm::Expected;

lld/test/wasm/wrap.ll

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
; RUN: llc -filetype=obj %s -o %t.o
2+
; RUN: wasm-ld -wrap nosuchsym -wrap foo -o %t.wasm %t.o
3+
; RUN: wasm-ld -emit-relocs -wrap foo -o %t.wasm %t.o
4+
; RUN: obj2yaml %t.wasm | FileCheck %s
5+
6+
target triple = "wasm32-unknown-unknown"
7+
8+
define i32 @foo() {
9+
ret i32 1
10+
}
11+
12+
define void @_start() {
13+
entry:
14+
call i32 @foo()
15+
ret void
16+
}
17+
18+
declare i32 @__real_foo()
19+
20+
define i32 @__wrap_foo() {
21+
%rtn = call i32 @__real_foo()
22+
ret i32 %rtn
23+
}
24+
25+
; CHECK: - Type: CODE
26+
; CHECK-NEXT: Relocations:
27+
; CHECK-NEXT: - Type: R_WASM_FUNCTION_INDEX_LEB
28+
; CHECK-NEXT: Index: 2
29+
; CHECK-NEXT: Offset: 0x00000009
30+
; CHECK-NEXT: - Type: R_WASM_FUNCTION_INDEX_LEB
31+
; CHECK-NEXT: Index: 0
32+
; CHECK-NEXT: Offset: 0x00000013
33+
34+
; CHECK: FunctionNames:
35+
; CHECK-NEXT: - Index: 0
36+
; CHECK-NEXT: Name: foo
37+
; CHECK-NEXT: - Index: 1
38+
; CHECK-NEXT: Name: _start
39+
; CHECK-NEXT: - Index: 2
40+
; CHECK-NEXT: Name: __wrap_foo

lld/wasm/Driver.cpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,84 @@ static std::string createResponseFile(const opt::InputArgList &Args) {
535535
return Data.str();
536536
}
537537

538+
// The --wrap option is a feature to rename symbols so that you can write
539+
// wrappers for existing functions. If you pass `-wrap=foo`, all
540+
// occurrences of symbol `foo` are resolved to `wrap_foo` (so, you are
541+
// expected to write `wrap_foo` function as a wrapper). The original
542+
// symbol becomes accessible as `real_foo`, so you can call that from your
543+
// wrapper.
544+
//
545+
// This data structure is instantiated for each -wrap option.
546+
struct WrappedSymbol {
547+
Symbol *Sym;
548+
Symbol *Real;
549+
Symbol *Wrap;
550+
};
551+
552+
static Symbol *addUndefined(StringRef Name) {
553+
return Symtab->addUndefinedFunction(Name, "", "", 0, nullptr, nullptr);
554+
}
555+
556+
// Handles -wrap option.
557+
//
558+
// This function instantiates wrapper symbols. At this point, they seem
559+
// like they are not being used at all, so we explicitly set some flags so
560+
// that LTO won't eliminate them.
561+
static std::vector<WrappedSymbol> addWrappedSymbols(opt::InputArgList &Args) {
562+
std::vector<WrappedSymbol> V;
563+
DenseSet<StringRef> Seen;
564+
565+
for (auto *Arg : Args.filtered(OPT_wrap)) {
566+
StringRef Name = Arg->getValue();
567+
if (!Seen.insert(Name).second)
568+
continue;
569+
570+
Symbol *Sym = Symtab->find(Name);
571+
if (!Sym)
572+
continue;
573+
574+
Symbol *Real = addUndefined(Saver.save("__real_" + Name));
575+
Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name));
576+
V.push_back({Sym, Real, Wrap});
577+
578+
// We want to tell LTO not to inline symbols to be overwritten
579+
// because LTO doesn't know the final symbol contents after renaming.
580+
Real->CanInline = false;
581+
Sym->CanInline = false;
582+
583+
// Tell LTO not to eliminate these symbols.
584+
Sym->IsUsedInRegularObj = true;
585+
Wrap->IsUsedInRegularObj = true;
586+
Real->IsUsedInRegularObj = false;
587+
}
588+
return V;
589+
}
590+
591+
// Do renaming for -wrap by updating pointers to symbols.
592+
//
593+
// When this function is executed, only InputFiles and symbol table
594+
// contain pointers to symbol objects. We visit them to replace pointers,
595+
// so that wrapped symbols are swapped as instructed by the command line.
596+
static void wrapSymbols(ArrayRef<WrappedSymbol> Wrapped) {
597+
DenseMap<Symbol *, Symbol *> Map;
598+
for (const WrappedSymbol &W : Wrapped) {
599+
Map[W.Sym] = W.Wrap;
600+
Map[W.Real] = W.Sym;
601+
}
602+
603+
// Update pointers in input files.
604+
parallelForEach(Symtab->ObjectFiles, [&](InputFile *File) {
605+
MutableArrayRef<Symbol *> Syms = File->getMutableSymbols();
606+
for (size_t I = 0, E = Syms.size(); I != E; ++I)
607+
if (Symbol *S = Map.lookup(Syms[I]))
608+
Syms[I] = S;
609+
});
610+
611+
// Update pointers in the symbol table.
612+
for (const WrappedSymbol &W : Wrapped)
613+
Symtab->wrap(W.Sym, W.Real, W.Wrap);
614+
}
615+
538616
void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
539617
WasmOptTable Parser;
540618
opt::InputArgList Args = Parser.parse(ArgsArr.slice(1));
@@ -628,6 +706,9 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
628706
for (auto *Arg : Args.filtered(OPT_export))
629707
handleUndefined(Arg->getValue());
630708

709+
// Create wrapped symbols for -wrap option.
710+
std::vector<WrappedSymbol> Wrapped = addWrappedSymbols(Args);
711+
631712
// Do link-time optimization if given files are LLVM bitcode files.
632713
// This compiles bitcode files into real object files.
633714
Symtab->addCombinedLTOObject();
@@ -640,6 +721,10 @@ void LinkerDriver::link(ArrayRef<const char *> ArgsArr) {
640721
if (errorCount())
641722
return;
642723

724+
// Apply symbol renames for -wrap.
725+
if (!Wrapped.empty())
726+
wrapSymbols(Wrapped);
727+
643728
for (auto *Arg : Args.filtered(OPT_export)) {
644729
Symbol *Sym = Symtab->find(Arg->getValue());
645730
if (Sym && Sym->isDefined())

lld/wasm/InputFiles.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ class InputFile {
6161

6262
ArrayRef<Symbol *> getSymbols() const { return Symbols; }
6363

64+
MutableArrayRef<Symbol *> getMutableSymbols() { return Symbols; }
65+
6466
protected:
6567
InputFile(Kind K, MemoryBufferRef M) : MB(M), FileKind(K) {}
6668
MemoryBufferRef MB;

lld/wasm/LTO.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,11 @@ void BitcodeCompiler::add(BitcodeFile &F) {
108108
(R.Prevailing && Sym->isExported());
109109
if (R.Prevailing)
110110
undefine(Sym);
111+
112+
// We tell LTO to not apply interprocedural optimization for wrapped
113+
// (with --wrap) symbols because otherwise LTO would inline them while
114+
// their values are still not final.
115+
R.LinkerRedefined = !Sym->CanInline;
111116
}
112117
checkError(LTOObj->add(std::move(F.Obj), Resols));
113118
}

lld/wasm/Options.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,9 @@ def version: F<"version">, HelpText<"Display the version number and exit">;
112112
def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"<option>">,
113113
HelpText<"Linker option extensions">;
114114

115+
defm wrap: Eq<"wrap", "Use wrapper functions for symbol">,
116+
MetaVarName<"<symbol>=<symbol>">;
117+
115118
// The follow flags are unique to wasm
116119

117120
def allow_undefined: F<"allow-undefined">,

lld/wasm/SymbolTable.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ void SymbolTable::addCombinedLTOObject() {
6565
}
6666

6767
void SymbolTable::reportRemainingUndefines() {
68-
for (Symbol *Sym : SymVector) {
68+
for (const auto& Pair : SymMap) {
69+
const Symbol *Sym = SymVector[Pair.second];
6970
if (!Sym->isUndefined() || Sym->isWeak())
7071
continue;
7172
if (Config->AllowUndefinedSymbols.count(Sym->getName()) != 0)
@@ -104,6 +105,7 @@ std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) {
104105

105106
Symbol *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
106107
Sym->IsUsedInRegularObj = false;
108+
Sym->CanInline = true;
107109
Sym->Traced = Trace;
108110
SymVector.emplace_back(Sym);
109111
return {Sym, true};
@@ -539,6 +541,19 @@ void SymbolTable::trace(StringRef Name) {
539541
SymMap.insert({CachedHashStringRef(Name), -1});
540542
}
541543

544+
void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) {
545+
// Swap symbols as instructed by -wrap.
546+
int &OrigIdx = SymMap[CachedHashStringRef(Sym->getName())];
547+
int &RealIdx= SymMap[CachedHashStringRef(Real->getName())];
548+
int &WrapIdx = SymMap[CachedHashStringRef(Wrap->getName())];
549+
LLVM_DEBUG(dbgs() << "wrap: " << Sym->getName() << "\n");
550+
551+
// Anyone looking up __real symbols should get the original
552+
RealIdx = OrigIdx;
553+
// Anyone looking up the original should get the __wrap symbol
554+
OrigIdx = WrapIdx;
555+
}
556+
542557
static const uint8_t UnreachableFn[] = {
543558
0x03 /* ULEB length */, 0x00 /* ULEB num locals */,
544559
0x00 /* opcode unreachable */, 0x0b /* opcode end */

lld/wasm/SymbolTable.h

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,11 @@ class InputSegment;
3535
// There is one add* function per symbol type.
3636
class SymbolTable {
3737
public:
38+
void wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap);
39+
3840
void addFile(InputFile *File);
39-
void addCombinedLTOObject();
4041

41-
std::vector<ObjFile *> ObjectFiles;
42-
std::vector<SharedFile *> SharedFiles;
43-
std::vector<BitcodeFile *> BitcodeFiles;
44-
std::vector<InputFunction *> SyntheticFunctions;
45-
std::vector<InputGlobal *> SyntheticGlobals;
42+
void addCombinedLTOObject();
4643

4744
void reportRemainingUndefines();
4845

@@ -87,6 +84,12 @@ class SymbolTable {
8784
void handleSymbolVariants();
8885
void handleWeakUndefines();
8986

87+
std::vector<ObjFile *> ObjectFiles;
88+
std::vector<SharedFile *> SharedFiles;
89+
std::vector<BitcodeFile *> BitcodeFiles;
90+
std::vector<InputFunction *> SyntheticFunctions;
91+
std::vector<InputGlobal *> SyntheticGlobals;
92+
9093
private:
9194
std::pair<Symbol *, bool> insert(StringRef Name, const InputFile *File);
9295
std::pair<Symbol *, bool> insertName(StringRef Name);

0 commit comments

Comments
 (0)