1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
//===- ExpandCtors.cpp - Convert ctors/dtors to concrete arrays -----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass converts LLVM's special symbols llvm.global_ctors and
// llvm.global_dtors to concrete arrays, __init_array_start/end and
// __fini_array_start/end, that are usable by a C library.
//
// This pass sorts the contents of global_ctors/dtors according to the
// priority values they contain and removes the priority values.
//
//===----------------------------------------------------------------------===//
#include <vector>
#include "llvm/Pass.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/TypeBuilder.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/NaCl.h"
using namespace llvm;
namespace {
struct ExpandCtors : public ModulePass {
static char ID; // Pass identification, replacement for typeid
ExpandCtors() : ModulePass(ID) {
initializeExpandCtorsPass(*PassRegistry::getPassRegistry());
}
virtual bool runOnModule(Module &M);
};
}
char ExpandCtors::ID = 0;
INITIALIZE_PASS(ExpandCtors, "nacl-expand-ctors",
"Hook up constructor and destructor arrays to libc",
false, false)
static void setGlobalVariableValue(Module &M, const char *Name,
Constant *Value) {
GlobalVariable *Var = M.getNamedGlobal(Name);
if (!Var) {
// This warning can happen in a program that does not use a libc
// and so does not call the functions in __init_array_start or
// __fini_array_end. Such a program might be linked with
// "-nostdlib".
// XXX EMSCRIPTEN: not surprising for us when this happens errs() << "Warning: Variable " << Name << " not referenced\n";
} else {
if (Var->hasInitializer()) {
report_fatal_error(std::string("Variable ") + Name +
" already has an initializer");
}
Var->replaceAllUsesWith(ConstantExpr::getBitCast(Value, Var->getType()));
Var->eraseFromParent();
}
}
struct FuncArrayEntry {
uint64_t priority;
Constant *func;
};
static bool compareEntries(FuncArrayEntry Entry1, FuncArrayEntry Entry2) {
return Entry1.priority < Entry2.priority;
}
static void readFuncList(GlobalVariable *Array, std::vector<Constant*> *Funcs) {
if (!Array->hasInitializer())
return;
Constant *Init = Array->getInitializer();
ArrayType *Ty = dyn_cast<ArrayType>(Init->getType());
if (!Ty) {
errs() << "Initializer: " << *Array->getInitializer() << "\n";
report_fatal_error("ExpandCtors: Initializer is not of array type");
}
if (Ty->getNumElements() == 0)
return;
ConstantArray *InitList = dyn_cast<ConstantArray>(Init);
if (!InitList) {
errs() << "Initializer: " << *Array->getInitializer() << "\n";
report_fatal_error("ExpandCtors: Unexpected initializer ConstantExpr");
}
std::vector<FuncArrayEntry> FuncsToSort;
for (unsigned Index = 0; Index < InitList->getNumOperands(); ++Index) {
ConstantStruct *CS = cast<ConstantStruct>(InitList->getOperand(Index));
FuncArrayEntry Entry;
Entry.priority = cast<ConstantInt>(CS->getOperand(0))->getZExtValue();
Entry.func = CS->getOperand(1);
FuncsToSort.push_back(Entry);
}
std::sort(FuncsToSort.begin(), FuncsToSort.end(), compareEntries);
for (std::vector<FuncArrayEntry>::iterator Iter = FuncsToSort.begin();
Iter != FuncsToSort.end();
++Iter) {
Funcs->push_back(Iter->func);
}
}
static void defineFuncArray(Module &M, const char *LlvmArrayName,
const char *StartSymbol,
const char *EndSymbol) {
std::vector<Constant*> Funcs;
GlobalVariable *Array = M.getNamedGlobal(LlvmArrayName);
if (Array) {
readFuncList(Array, &Funcs);
// No code should be referencing global_ctors/global_dtors,
// because this symbol is internal to LLVM.
Array->eraseFromParent();
}
Type *FuncTy = FunctionType::get(Type::getVoidTy(M.getContext()), false);
Type *FuncPtrTy = FuncTy->getPointerTo();
ArrayType *ArrayTy = ArrayType::get(FuncPtrTy, Funcs.size());
GlobalVariable *NewArray =
new GlobalVariable(M, ArrayTy, /* isConstant= */ true,
GlobalValue::InternalLinkage,
ConstantArray::get(ArrayTy, Funcs));
setGlobalVariableValue(M, StartSymbol, NewArray);
// We do this last so that LLVM gives NewArray the name
// "__{init,fini}_array_start" without adding any suffixes to
// disambiguate from the original GlobalVariable's name. This is
// not essential -- it just makes the output easier to understand
// when looking at symbols for debugging.
NewArray->setName(StartSymbol);
// We replace "__{init,fini}_array_end" with the address of the end
// of NewArray. This removes the name "__{init,fini}_array_end"
// from the output, which is not ideal for debugging. Ideally we
// would convert "__{init,fini}_array_end" to being a GlobalAlias
// that points to the end of the array. However, unfortunately LLVM
// does not generate correct code when a GlobalAlias contains a
// GetElementPtr ConstantExpr.
Constant *NewArrayEnd =
ConstantExpr::getGetElementPtr(NewArray,
ConstantInt::get(M.getContext(),
APInt(32, 1)));
setGlobalVariableValue(M, EndSymbol, NewArrayEnd);
}
bool ExpandCtors::runOnModule(Module &M) {
defineFuncArray(M, "llvm.global_ctors",
"__init_array_start", "__init_array_end");
defineFuncArray(M, "llvm.global_dtors",
"__fini_array_start", "__fini_array_end");
return true;
}
ModulePass *llvm::createExpandCtorsPass() {
return new ExpandCtors();
}
|