blob: 97398870b4006b9a881fb2852cb861c065dfd66d [file] [log] [blame]
//===- 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) {
if (GlobalVariable *Var = M.getNamedGlobal(Name)) {
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(ArrayTy, 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();
}