int $0x80

LogFunctionNames 모듈 개발 - puts함수 호출코드 삽입 본문

컴퓨터공부/엘엘비엠

LogFunctionNames 모듈 개발 - puts함수 호출코드 삽입

cd80 cd80 2016.07.22 20:26

LogFunctionNames의 첫 버젼인 단순한 함수 이름 로깅 기능 구현을 마무리합니다

이전 글까지의 내용은

https://github.com/cd80/UtilizedLLVM/commit/4f397fa7b81eb0e3397041bf12ff4f4a48080709 

여기 정리되어 있습니다


이번 글에서는 puts함수가 항상 존재하는 상태에서 시작합니다. puts함수가 있다면 좋은것이고 없어도 동적으로 삽입해주니 이제부터 puts함수는 항상 존재합니다


이번에도 llc -march=cpp puts.ll에 의해 생성된 puts.cpp를 참고해 진행합니다

저번 글에서 만든 puts.cpp에서 main함수를 봅시다



 // Function: main (func_main)

 {


  BasicBlock* label_entry = BasicBlock::Create(mod->getContext(), "entry",func_main,0);


  // Block entry (label_entry)

  CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call", label_entry);

  int32_call->setCallingConv(CallingConv::C);

  int32_call->setTailCall(false);

  AttributeSet int32_call_PAL;

  int32_call->setAttributes(int32_call_PAL);


  ReturnInst::Create(mod->getContext(), const_int32_9, label_entry);


 }


BasicBlock은 항상 하나 이상이 존재합니다. 이 이유는 Instruction의 parent가 BasicBlock이여야 하기 때문입니다


puts.c를 다시 봅시다

우리는 puts를 호출하는 코드밖에 작성하지 않았습니다

따라서

위의 cpp코드의 일부인

// Block entry (label_entry)

CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call", label_entry);

int32_call->setCallingConv(CallingConv::C);

int32_call->setTailCall(false);

AttributeSet int32_call_PAL;

int32_call->setAttributes(int32_call_PAL);

위 코드는 모두 call puts를 만들기 위함임을 알 수 있습니다

저번 글에서 한것처럼 이걸 그대로 베껴봅시다


우선 우리는 저번 글에서 코드를 아래 스샷까지 작성했습니다

이 아래 

이렇게 puts함수를 호출하는 코드를 삽입하는 코드(편의상 이제부터는 puts함수를 호출하는 코드라고 부르겠습니다)

그럼 필요한 재료는 const_ptr_10, const_ptr_8, label_entry  이렇게 세가지가 됩니다


const_ptr_10부터 시작해봅시다

이걸 쓰기위해선 PointerTy_4가 필요하네요

또 FuncTy_5도 필요합니다

FuncTy_5는 PointerTy_3이 다시 필요한데 이건 다행히 붙어있네요

그럼 이 스샷 세개에 있는 코드를 합쳐봅시다



이제 const_ptr_8을 구해옵시다

이런게 지루할수도 있지만 하다보면 요리하는데 재료 구하러 산가서 나물캐오는 느낌이라 나름 재밌습니다

이번엔 gvar_array__str 이랑 const_ptr_8_indices가 필요합니다

gvar_array__str 부터 구해보죠

그런데 다행히 const_ptr_8_indices와 gvar_array__str이 모두 모여있네요



이 하나 떨어져있는거랑 위의 덩어리랑 합쳐봅시다




다했는데 빌드를 돌려보니 에러가 나네요

ArrayTy_0을 못봤습니다

ArrayTy_0도 넣어줍시다


이래도 빌드가 안됩니다

바로 이전 글들에서 언급했던, LLVM은 업데이트가 될때마다 API가 휙휙 바뀌는 문제 때문입니다

getGetElementPtr함수가 바뀌었는데요, 

LLVM 3.6.x까지는 getGetElementPtr이 우리가 사용한 형태인

static Constant *getGetElementPtr(Constant *C, ArrayRef<Constant *> IdxList,
                                    bool InBounds = false,
                                    Type *OnlyIfReducedTy = nullptr)

이렇게 생겼었습니다


3.7.x부터는 이게

static Constant *getGetElementPtr(Type *Ty, Constant *C,
                                    ArrayRef<Constant *> IdxList,
                                    bool InBounds = false,
                                    Type *OnlyIfReducedTy = nullptr)

맨 앞에 Type *Ty가 추가된 버젼으로 바뀝니다

우리가 테스트로 빌드할때 사용한 clang버젼도 3.8.0, llc버젼도 3.8.0인데 3.6버젼대의 API를 사용하는 코드로 변환된것입니다

그러려니합시다..


따라서 

여기서 사용한 getGetElementPtr에 맨 첫번째 인자로 Type을 넘겨줘야 하는데

여기서 이 Type에는 대체 어떻게 넣어야 하는지 알 수가 없으니 getGetElementPtr의 코드를 보면

이렇게 Ty가 0이면 그냥 알아서 설정해주는 부분이 있습니다



Type 인자에 0을 넣었습니다



그다음에 또 빌드를 해봅니다

label_entry를 까먹고 안넣었습니다

label_entry는 이 부분에서 사용하는데, 


http://llvm.org/docs/doxygen/html/classllvm_1_1CallInst.html

레퍼런스를 보면 우리는 

static CallInst * Create (Value *Func, ArrayRefValue * > Args, const Twine &NameStr, BasicBlock *InsertAtEnd)

이 함수를 사용했습니다

static CallInst * 

Create (Value *Func, ArrayRefValue * > Args, const Twine &NameStr, Instruction *InsertBefore=nullptr)

이거를 사용하도록 바꿀건데, InsertBefore는 기본으로 nullptr로 초기화 되므로,

이렇게 바꿔줍니다


이제 이 int32_call이라는 CallInst를 현재 함수의 첫번째 인스트럭션으로 지정해줄건데 쉽습니다

이렇게 함수의 첫번째 BasicBlock을 가져오고, Instruction List의 맨 처음에 int32_call을 푸쉬하면 됩니다


이제 중간 점검으로 빌드하고 돌려봅시다

지금까지 작성한 코드는 아래와 같습니다.

아직 끝난게 아니기 때문에 이전 글에서 말씀드린것과 마찬가지로 빌드 하면서 다른일을 못하신다면 굳이 지금 빌드하실 필요는 없습니다


#include "llvm/Pass.h"

#include "llvm/IR/Module.h"

#include "llvm/IR/Function.h"

#include "llvm/IR/BasicBlock.h"

#include "llvm/IR/Instructions.h"

#include "llvm/IR/InstrTypes.h"

#include "llvm/IR/Constants.h"

#include "llvm/IR/Type.h"

#include "llvm/ADT/Statistic.h"

#include "llvm/IR/GlobalValue.h"

#include "llvm/IR/LLVMContext.h"

#include "llvm/Transforms/Utils/Cloning.h"

#include "llvm/Transforms/Utils/BasicBlockUtils.h"

#include "llvm/CodeGen/ISDOpcodes.h"

#include "llvm/Support/raw_ostream.h"

#include "llvm/Support/Debug.h"

#include "llvm/Support/CommandLine.h"

#include "llvm/Transforms/IPO.h"

#include <list>


#include "llvm/Transforms/Utility/LogFunctionNames.h"

#include "llvm/IRReader/IRReader.h"

#include "llvm/Linker/Linker.h"

#include "llvm/Support/SourceMgr.h"

using namespace llvm;

namespace {

struct LogFunctionNames : public FunctionPass {

static char ID; // Pass identification

bool flag;

LogFunctionNames() : FunctionPass(ID) {}

LogFunctionNames(bool flag) : FunctionPass(ID) {this->flag = flag; LogFunctionNames();}


virtual bool runOnFunction(Function &F){

errs() << "Current function name: [" << F.getName() << "]\n";

errs() << "Current module name: [" << F.getParent()->getName() << "]\n";

Module *mod = F.getParent();

std::vector<Type*>FuncTy_6_args;

FunctionType* FuncTy_6 = FunctionType::get(/*Result=*/IntegerType::get(mod->getContext(), 32),

/*Params=*/FuncTy_6_args,

/*isVarArg=*/true);

Function* func_puts = mod->getFunction("puts");

if (!func_puts) {

func_puts = Function::Create(/*Type=*/FuncTy_6,

/*Linkage=*/GlobalValue::ExternalLinkage,

/*Name=*/"puts", mod); // (external, no body)

func_puts->setCallingConv(CallingConv::C);

}

AttributeSet func_puts_PAL;

{

SmallVector<AttributeSet, 4> Attrs;

AttributeSet PAS;

{

AttrBuilder B;

PAS = AttributeSet::get(mod->getContext(), ~0U, B);

}

Attrs.push_back(PAS);

func_puts_PAL = AttributeSet::get(mod->getContext(), Attrs);

}

func_puts->setAttributes(func_puts_PAL);


// prerequisites for const_ptr_10

PointerType* PointerTy_3 = PointerType::get(IntegerType::get(mod->getContext(), 8), 0);

std::vector<Type*>FuncTy_5_args;

FuncTy_5_args.push_back(PointerTy_3);

FunctionType* FuncTy_5 = FunctionType::get(/*Result=*/IntegerType::get(mod->getContext(), 32),

/*Params=*/FuncTy_5_args,

/*isVarArg=*/true);

PointerType* PointerTy_4 = PointerType::get(FuncTy_5, 0);

Constant* const_ptr_10 = ConstantExpr::getCast(Instruction::BitCast, func_puts, PointerTy_4);


// prerequisites for const_ptr_8

ArrayType* ArrayTy_0 = ArrayType::get(IntegerType::get(mod->getContext(), 8), 4);

GlobalVariable* gvar_array__str = new GlobalVariable(/*Module=*/*mod,

/*Type=*/ArrayTy_0,

/*isConstant=*/true,

/*Linkage=*/GlobalValue::PrivateLinkage,

/*Initializer=*/0, // has initializer, specified below

/*Name=*/".str");

gvar_array__str->setAlignment(1);


Constant *const_array_7 = ConstantDataArray::getString(mod->getContext(), "hi~", true);

std::vector<Constant*> const_ptr_8_indices;

ConstantInt* const_int32_9 = ConstantInt::get(mod->getContext(), APInt(32, StringRef("0"), 10));

const_ptr_8_indices.push_back(const_int32_9);

const_ptr_8_indices.push_back(const_int32_9);

Constant* const_ptr_8 = ConstantExpr::getGetElementPtr(0, gvar_array__str, const_ptr_8_indices);

gvar_array__str->setInitializer(const_array_7);


CallInst* int32_call = CallInst::Create(const_ptr_10, const_ptr_8, "call");

int32_call->setCallingConv(CallingConv::C);

int32_call->setTailCall(false);

AttributeSet int32_call_PAL;

int32_call->setAttributes(int32_call_PAL);


F.getEntryBlock().getInstList().push_front(int32_call);

return false;

}

};

}


char LogFunctionNames::ID = 0;

static RegisterPass<LogFunctionNames> X("logger", "inserting logging routine for functions");


Pass *llvm::createLogger() {

return new LogFunctionNames();

}


Pass *llvm::createLogger(bool flag) {

return new LogFunctionNames(flag);

}




빌드하고 돌려보세요

또잉~~ 함수이름이 출력된게 아니라 hi~ 가 출력이 됐네요

그 이유가 궁금하신분은 puts.c를 읽어보세요

puts("hi~"); 라고 적혀있을겁니다

우리는 그걸 그대로 베껴왔으니 puts("hi~"); 가 함수마다 삽입된겁니다

그래도 puts함수 호출에는 성공했으니 거의다 왔습니다 조금만 더 해봅시다


hi~는 어딨는걸까요?

아까 gvar_array__str 를 설정한 부분입니다

맨위에 hi~가 있죠

메세지를 어떻게 하는지는 자유지만 저는 아래처럼 했습니다

free까지 해주면 완벽하겠지만 쉽게쉽게 합시다

코드의 가독성을 위해 메모리 관리를 포기하는 미친짓을 해도 괜찮습니다. 제품을 내려는게 아니라 공부하려는 거니까요 하하

한가지 더 덧붙일건, 지금 현재 코딩 스타일은 굉장히 쓰레기라는 겁니다. 최대한 설명하기에 편하도록 예제코드에서 그대로 복붙하는 방식을 쓰고 있는데, 실제로 코드를 릴리즈 할땐 이쁘게 작성하세요


이대로 빌드하면 빌드는 잘되지만 컴파일할때 에러가 생깁니다

이번 에러는 블로그에선 다루지 않겠지만, 코드를 보면 바로 이해하실 수 있습니다

스스로 해결하는 시간이 굉장히 중요하기 때문에 스스로 해결해보시고, 해결을 완료하거나 도저히 모르겠다 싶을 땐

https://github.com/cd80/UtilizedLLVM/commit/01241a782506a485220b48bb68654c843d8d4a3e

이 커밋을 참고해보세요. 글에서 설명한것과 코드가 어떻게 다른가 보면 됩니다




완성했습니다~_~_~

우리가 개발한건 완전히 초기버젼으로, 앞으로 이 기능을 좀더 간지나게 개선하긴 하겠지만

우선 지금은 간지나게 개선하는 것보단 새로운 기능을 또 구현해보는게 더 재밌으니 개선은 미루고 다음 글에서는 새로운 기능을 시작해보겠습니다

신고
0 Comments
댓글쓰기 폼