Files
revng-revng/variablemanager.h
T
Alessandro Di Federico 6a6cfff17d Introduce support for dynamic objects (PIE)
This commit introduces support for dynamic objects. We do not support
translating dynamic libraries yet, therefore this commit introduces
support for PIE programs.

At the current stage, QEMU does not provide us explicit information
about an instruction using the program counter, but introduces its value
as an immediate. As a consequence, we cannot support arbitrary
relocation. For this reason, we statically relocate the program to a
fixed address (`0x50000000` by default, but it can be customized through
the `--base` argument). Therefore, all the addresses read from ELF data
structure need to be relocated.

Code compiled with `-fPIC` cannot store in global data the address of a
function, since it will be relocated at run-time. This means that the
global data harvesting won't bring any benefit. On the other hand, going
through dynamic symbols can be hugely beneficial. Same argument for
`*_RELATIVE` relocations.

The `merge-dynamic.py` script has been improved to find the appropriate
spot to put the rewritten program/section and headers and the dynamic
sections (the kernel is peeky on them).

Finally the `setRegister` function has been introduced in the module
produced by `revamb`. This function allows to keep CSVs static and, at
the same time, it allow `support.c` to set them. This is particularly
useful when we want to call the `root` function with specific values in
the registers (e.g., during for fuzzing purposes) or, as it's the case
for PIE, to synchronize the value of the FS register, which is
initialized by the dynamic loader, before execution gets to the `main`
function in `support.c`.
2018-08-17 21:55:48 +02:00

266 lines
8.9 KiB
C++

#ifndef _VARIABLEMANAGER_H
#define _VARIABLEMANAGER_H
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
// Standard includes
#include <cstdint>
#include <map>
#include <string>
// LLVM includes
#include "llvm/IR/IRBuilder.h"
#include "llvm/Pass.h"
// Local includes
#include "ptcdump.h"
#include "cpustateaccessanalysis.h"
#include "revamb.h"
namespace llvm {
class AllocaInst;
class BasicBlock;
class DataLayout;
class GlobalVariable;
class Module;
class StructType;
class Value;
}
class VariableManager;
class CPUStateAccessAnalysisPass;
/// \brief Maintain the list of variables required by PTC
///
/// It can be queried for a variable, which, if not already existing, will be
/// created on the fly.
class VariableManager {
public:
VariableManager(llvm::Module& TheModule,
llvm::Module& HelpersModule,
Architecture &TargetArchitecture);
/// \brief Get or create the LLVM value associated to a PTC temporary
///
/// Given a PTC temporary identifier, checks if it already exists in the
/// generated LLVM IR, and, if not, it creates it.
///
/// \param TemporaryId the PTC temporary identifier.
///
/// \return a `Value` wrapping the requested global or local variable.
// TODO: rename to getByTemporaryId
llvm::Value *getOrCreate(unsigned TemporaryId, bool Reading);
/// \brief Return the global variable corresponding to \p Offset in the CPU
/// state.
///
/// \param Offset the offset in the CPU state (the `env` PTC variable).
/// \param Name an optional name to force for the associate global variable.
///
/// \return a pair composed by the request global variable and the offset in
/// it corresponding to \p Offset. For instance, if you're accessing
/// the third byte of a 32-bit integer it will 2.
std::pair<llvm::GlobalVariable*,
unsigned> getByEnvOffset(intptr_t Offset,
std::string Name="") {
return getByCPUStateOffsetInternal(EnvOffset + Offset, Name);
}
/// \brief Notify VariableManager to reset all the "function"-specific
/// information
///
/// Informs the VariableManager that a new function has begun, so it can
/// discard function- and basic block-level variables.
///
/// Note: by "function" here we mean a function in PTC terms, i.e. a run of
/// code translated in a single shot by the TCG. Do not confuse this
/// function concept with other meanings.
///
/// \param Delimiter the new point where to insert allocations for local
/// variables.
/// \param Instructions the new PTCInstructionList to use from now on.
void newFunction(llvm::Instruction *Delimiter=nullptr,
PTCInstructionList *Instructions=nullptr);
/// Informs the VariableManager that a new basic block has begun, so it can
/// discard basic block-level variables.
///
/// \param Delimiter the new point where to insert allocations for local
/// variables.
/// \param Instructions the new PTCInstructionList to use from now on.
void newBasicBlock(llvm::Instruction *Delimiter=nullptr,
PTCInstructionList *Instructions=nullptr);
void newBasicBlock(llvm::BasicBlock *Delimiter,
PTCInstructionList *Instructions=nullptr);
/// Returns true if the given variable is the env variable
bool isEnv(llvm::Value *TheValue);
CPUStateAccessAnalysisPass *createCPUStateAccessAnalysisPass() {
return new CPUStateAccessAnalysisPass(this);
}
llvm::Value *computeEnvAddress(llvm::Type *TargetType,
llvm::Instruction *InsertBefore,
unsigned Offset = 0);
void setDataLayout(const llvm::DataLayout *NewLayout) {
ModuleLayout = NewLayout;
}
template<typename T>
T *setAliasScope(T *Instruction);
template<typename T>
T *setNoAlias(T *Instruction);
std::vector<llvm::AllocaInst *> locals() {
std::vector<llvm::AllocaInst *> Locals;
for (auto Pair : LocalTemporaries)
Locals.push_back(Pair.second);
return Locals;
}
llvm::Value *loadFromEnvOffset(llvm::IRBuilder<> &Builder,
unsigned LoadSize,
unsigned Offset) {
return loadFromCPUStateOffset(Builder, LoadSize, EnvOffset + Offset);
}
bool storeToEnvOffset(llvm::IRBuilder<> &Builder,
unsigned StoreSize,
unsigned Offset,
llvm::Value *ToStore) {
unsigned ActualOffset = EnvOffset + Offset;
return storeToCPUStateOffset(Builder, StoreSize, ActualOffset, ToStore);
}
bool memcpyAtEnvOffset(llvm::IRBuilder<> &Builder,
llvm::CallInst *CallMemcpy,
unsigned Offset,
bool EnvIsSrc);
/// \brief Perform finalization steps on variables
///
/// \param ExternalCSVs true if CSVs linkage should not be turned into static.
void finalize(bool ExternalCSVs) {
using namespace llvm;
if (!ExternalCSVs) {
for (auto &P : CPUStateGlobals)
P.second->setLinkage(GlobalValue::InternalLinkage);
for (auto &P : OtherGlobals)
P.second->setLinkage(GlobalValue::InternalLinkage);
}
LLVMContext &Context = getContext(&TheModule);
IRBuilder<> Builder(Context);
// Create the setRegister function
auto *SetRegisterTy = FunctionType::get(Builder.getVoidTy(),
{
Builder.getInt32Ty(),
Builder.getInt64Ty()
},
false);
auto *Temp = TheModule.getOrInsertFunction("set_register", SetRegisterTy);
auto *SetRegister = cast<Function>(Temp);
SetRegister->setLinkage(GlobalValue::ExternalLinkage);
// Collect arguments
auto ArgIt = SetRegister->arg_begin();
auto ArgEnd = SetRegister->arg_end();
assert(ArgIt != ArgEnd);
Argument *RegisterID = &*ArgIt;
ArgIt++;
assert(ArgIt != ArgEnd);
Argument *NewValue = &*ArgIt;
ArgIt++;
assert(ArgIt == ArgEnd);
// Create main basic blocks
using BasicBlock = BasicBlock;
auto *EntryBB = BasicBlock::Create(Context, "", SetRegister);
auto *DefaultBB = BasicBlock::Create(Context, "", SetRegister);
auto *ReturnBB = BasicBlock::Create(Context, "", SetRegister);
// Populate the default case of the switch
Builder.SetInsertPoint(DefaultBB);
Builder.CreateCall(TheModule.getFunction("abort"));
Builder.CreateUnreachable();
// Create the switch statement
Builder.SetInsertPoint(EntryBB);
auto *Switch = Builder.CreateSwitch(RegisterID,
DefaultBB,
CPUStateGlobals.size());
for (auto &P : CPUStateGlobals) {
Type *CSVTy = P.second->getType();
auto *CSVIntTy = cast<IntegerType>(CSVTy->getPointerElementType());
if (CSVIntTy->getBitWidth() <= 64) {
// Set the value of the CSV
auto *SetRegisterBB = BasicBlock::Create(Context, "", SetRegister);
Builder.SetInsertPoint(SetRegisterBB);
Builder.CreateStore(Builder.CreateTrunc(NewValue, CSVIntTy),
P.second);
Builder.CreateBr(ReturnBB);
// Add the case to the switch
Switch->addCase(Builder.getInt32(P.first), SetRegisterBB);
}
}
// Finally, populate the return basic block
Builder.SetInsertPoint(ReturnBB);
Builder.CreateRetVoid();
}
/// \brief Gets the CPUStateType
llvm::StructType *getCPUStateType() const {
return CPUStateType;
}
private:
llvm::Value *loadFromCPUStateOffset(llvm::IRBuilder<> &Builder,
unsigned LoadSize,
unsigned Offset);
bool storeToCPUStateOffset(llvm::IRBuilder<> &Builder,
unsigned StoreSize,
unsigned Offset,
llvm::Value *ToStore);
llvm::GlobalVariable *getByCPUStateOffset(intptr_t Offset,
std::string Name="");
std::pair<llvm::GlobalVariable*, unsigned>
getByCPUStateOffsetInternal(intptr_t Offset,
std::string Name="");
private:
llvm::Module &TheModule;
llvm::IRBuilder<> Builder;
using TemporariesMap = std::map<unsigned int, llvm::AllocaInst *>;
using GlobalsMap = std::map<intptr_t, llvm::GlobalVariable *>;
GlobalsMap CPUStateGlobals;
GlobalsMap OtherGlobals;
TemporariesMap Temporaries;
TemporariesMap LocalTemporaries;
PTCInstructionList *Instructions;
llvm::StructType *CPUStateType;
const llvm::DataLayout *ModuleLayout;
unsigned EnvOffset;
llvm::Value *Env;
unsigned AliasScopeMDKindID;
unsigned NoAliasMDKindID;
llvm::MDNode *CPUStateScopeSet;
Architecture &TargetArchitecture;
};
#endif // _VARIABLEMANAGER_H