You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
4.6 KiB
C++
128 lines
4.6 KiB
C++
#include "llvm/Pass.h"
|
|
#include "llvm/Function.h"
|
|
#include "llvm/Module.h"
|
|
#include "llvm/Instructions.h"
|
|
#include "llvm/GlobalAlias.h"
|
|
#include "llvm/GlobalVariable.h"
|
|
#include "llvm/Constants.h"
|
|
#include <string>
|
|
|
|
using namespace llvm;
|
|
using std::string;
|
|
|
|
namespace
|
|
{
|
|
class GNUNonfragileIvarPass : public FunctionPass
|
|
{
|
|
|
|
public:
|
|
static char ID;
|
|
GNUNonfragileIvarPass() : FunctionPass((intptr_t)&ID) {}
|
|
|
|
Module *M;
|
|
size_t PointerSize;
|
|
virtual bool doInitialization(Module &Mod) {
|
|
M = &Mod;
|
|
PointerSize = 8;
|
|
if (M->getPointerSize() == Module::Pointer32)
|
|
PointerSize = 4;
|
|
return false;
|
|
}
|
|
|
|
std::string getSuperName(Constant *ClsStruct) {
|
|
GlobalVariable *name =
|
|
cast<GlobalVariable>(ClsStruct->getOperand(1)->getOperand(0));
|
|
return cast<ConstantArray>(name->getInitializer())->getAsString();
|
|
}
|
|
|
|
size_t sizeOfClass(const std::string &className) {
|
|
if (className.compare(0, 8, "NSObject") == 0 ||
|
|
className.compare(0, 6, "Object") == 0) {
|
|
return PointerSize;
|
|
}
|
|
GlobalVariable *Cls = M->getGlobalVariable("_OBJC_CLASS_" + className);
|
|
if (!Cls) return 0;
|
|
Constant *ClsStruct = Cls->getInitializer();
|
|
// Size is initialized to be negative.
|
|
ConstantInt *Size = cast<ConstantInt>(ClsStruct->getOperand(5));
|
|
return sizeOfClass(getSuperName(ClsStruct)) - Size->getSExtValue();
|
|
}
|
|
|
|
size_t hardCodedOffset(const StringRef &className,
|
|
const StringRef &ivarName) {
|
|
GlobalVariable *Cls = M->getGlobalVariable(("_OBJC_CLASS_" + className).str(), true);
|
|
if (!Cls) return 0;
|
|
Constant *ClsStruct = Cls->getInitializer();
|
|
size_t superSize = sizeOfClass(getSuperName(ClsStruct));
|
|
if (!superSize) return 0;
|
|
ConstantStruct *IvarStruct = cast<ConstantStruct>(
|
|
cast<GlobalVariable>(ClsStruct->getOperand(6))->getInitializer());
|
|
int ivarCount = cast<ConstantInt>(IvarStruct->getOperand(0))->getSExtValue();
|
|
Constant *ivars = IvarStruct->getOperand(1);
|
|
for (int i=0 ; i<ivarCount ; i++) {
|
|
Constant *ivar = cast<Constant>(ivars->getOperand(i));
|
|
GlobalVariable *name =
|
|
cast<GlobalVariable>(ivar->getOperand(0)->getOperand(0));
|
|
std::string ivarNameStr =
|
|
cast<ConstantArray>(name->getInitializer())->getAsString();
|
|
if (ivarNameStr.compare(0, ivarName.size(), ivarName.str()) == 0)
|
|
return superSize +
|
|
cast<ConstantInt>(ivar->getOperand(2))->getSExtValue();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
virtual bool runOnFunction(Function &F) {
|
|
bool modified = false;
|
|
//llvm::cerr << "IvarPass: " << F.getName() << "\n";
|
|
for (Function::iterator i=F.begin(), end=F.end() ;
|
|
i != end ; ++i) {
|
|
for (BasicBlock::iterator b=i->begin(), last=i->end() ;
|
|
b != last ; ++b) {
|
|
if (LoadInst *indirectload = dyn_cast<LoadInst>(b)) {
|
|
if (LoadInst *load = dyn_cast<LoadInst>(indirectload->getOperand(0))) {
|
|
if (GlobalVariable *ivar =
|
|
dyn_cast<GlobalVariable>(load->getOperand(0))) {
|
|
StringRef variableName = ivar->getName();
|
|
if (!variableName.startswith("__objc_ivar_offset_"))
|
|
break;
|
|
size_t prefixLength = strlen("__objc_ivar_offset_");
|
|
|
|
StringRef suffix = variableName.substr(prefixLength,
|
|
variableName.size()-prefixLength);
|
|
|
|
std::pair<StringRef,StringRef> parts = suffix.split('.');
|
|
StringRef className = parts.first;
|
|
StringRef ivarName = parts.second;
|
|
|
|
// If the class, and all superclasses, are visible in this module
|
|
// then we can hard-code the ivar offset
|
|
if (size_t offset = hardCodedOffset(className, ivarName)) {
|
|
indirectload->replaceAllUsesWith(ConstantInt::get(indirectload->getType(), offset));
|
|
modified = true;
|
|
} else {
|
|
// If the class was compiled with the new
|
|
if (Value *offset =
|
|
M->getGlobalVariable(("__objc_ivar_offset_value_" + suffix).str())) {
|
|
load->replaceAllUsesWith(offset);
|
|
modified = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return modified;
|
|
}
|
|
};
|
|
|
|
char GNUNonfragileIvarPass::ID = 0;
|
|
RegisterPass<GNUNonfragileIvarPass> X("gnu-nonfragile-ivar", "Ivar fragility pass");
|
|
}
|
|
|
|
FunctionPass *createGNUNonfragileIvarPass(void)
|
|
{
|
|
return new GNUNonfragileIvarPass();
|
|
}
|