대략적인 구조

크리에이티브 커먼즈 라이선스
Creative Commons License

1. 코드 표절탐지 알고리즘으로 유사도 비교 -> 기존 취약코드와 가장 유사한 코드들 리스팅

2. 자연어 처리를 이용해 해당 코드의 역할 분석 (함수명, 변수명 등) -> 유저 인풋을 다룰 확률이 얼마나 되는지 예측

3. micro execution(patrice godefroid)를 이용해 해당 코드에서 취약점이 발생할 수 있는 잠재적 인풋을 계산함

4. concolic execution을 이용해 해당 코드까지 도달하면서 잠재적 인풋이 실제로 발생할 수 있는 인풋인지 알아냄

-> 취약점 발견

신고

설정

트랙백

댓글

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

컴퓨터공부/엘엘비엠 2016.07.22 20:26
크리에이티브 커먼즈 라이선스
Creative Commons License

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

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




완성했습니다~_~_~

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

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

신고

설정

트랙백

댓글

LogFunctionNames 모듈 개발 - puts함수 동적 삽입

컴퓨터공부/엘엘비엠 2016.07.22 20:26
크리에이티브 커먼즈 라이선스
Creative Commons License

우리는 저번 글에서 컴파일 할 때 함수 이름들을 로깅하는것을 테스트 해봤습니다

이번 글과 다음 글에서는 함수 이름 로깅 기능을 컴파일러에서 수행하는 것이 아닌, 해당 기능을 수행하는 코드를 컴파일되는 프로그램에 삽입합니다

하다가 중간결과를 확인하고 싶으실 때는 직접 빌드해서 돌려보시면 됩니다. 언제 빌드해서 확인해볼만 한지는 글 중간 중간 말씀드리겠습니다



위 사진은 우리가 가장 많이 사용할 각각 요소에 대한 포함관계를 설명합니다


좀더 쉬운 이해를 위해 아래 아이다 사진을 참고해주세요

우선 Module은 아이다에서 이해하기 쉽지는 않습니다. LLVM 에서 모듈이란 각 파일마다 구성됩니다.

예를 들어 이전 글에서 ./clang -o test test.c로 컴파일한 코드에 F.getParent()->getName() 을 로깅해보면 test.c가 출력됩니다


그 다음 Function은 이름 그대로 각각 함수마다 구성됩니다.


이 설명을 시작한 이유는, 다른건 모두 직관적으로 알 수 있지만 Basic Block에 대한 개념을 모르시는 분이 계실까봐입니다

BasicBlock은 일반적으로 분기와 분기 사이의 코드를 뜻합니다

즉 항상 선형으로 동작되는 코드들의 모음을 BasicBlock이라고 하고, IDA에서 그래프뷰로 볼때 각각 직사각형은 모두 Basic Block들입니다



이전 글에서 세팅한 뼈대 코드와 함수 이름을 로깅하는 코드에

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

위 코드를 추가하고 테스트 해보세요 


getParent()함수는 GlobalValue 클래스에서 정의합니다


Parent 멤버 선언

http://llvm.org/docs/doxygen/html/GlobalValue_8h_source.html#l00115

getParent 함수 구현체

http://llvm.org/docs/doxygen/html/GlobalValue_8h_source.html#l00361


즉 Parent는 Module * 형으로 선언돼있고 getParent()는 Parent를 리턴하기 때문에, getParent()의 리턴 형은 Module *입니다

그러면 Instruction이나 BasicBlock 클래스에서도 getParent가 Module을 리턴하느냐 하면 아닙니다. Instruction과 BasicBlock 클래스에서는 함수들이 모두 오버라이딩 돼있습니다

각각 레퍼런스를 확인해보면 맨위의 다이어그램에 맞게 parent를 리턴하는 것을 볼 수 있습니다



이제 첫 주제인 puts함수 삽입을 어떻게 하는가를 알아보겠습니다

그전에 우리는 LLVM IR파일에 좀더 익숙해져야 합니다


단순한 코드를 작성합니다

우리가 처음 쉘코드를 공부할 때 gcc -S write.c 와 같이 하면 write.S가 생성이 되고, 여긴 어셈블리어 코드가 들어가 있었습니다

llvm 에서도 똑같이 할 수 있는데, -emit-llvm이라는 옵션을 추가로 주면 어셈블리 파일이 아닌 LLVM IR파일을 생성해줍니다



IR파일을 읽으면 아래와 비슷하게 나옵니다

; ModuleID = 'puts.c'

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [4 x i8] c"hi~\00", align 1


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 (i8*, ...) bitcast (i32 (...)* @puts to i32 (i8*, ...)*)(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))

  ret i32 0

}


declare i32 @puts(...) #1


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.8.0 (trunk 247122)"}

지금 제 시스템에 깔려있는 LLVM 버젼이 3.8.0이기 때문에 다른 버젼으로 컴파일하신다면 결과가 약간 다르게 나올 순 있지만 지금은 상관없습니다

IR 결과에서 함수만 추려보면

define i32 @main() #0 {

entry:

  %call = call i32 (i8*, ...) bitcast (i32 (...)* @puts to i32 (i8*, ...)*)(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str, i32 0, i32 0))

  ret i32 0

}


declare i32 @puts(...) #1

이렇게 main과 puts가 존재하는것을 알 수 있습니다

두 함수의 차이점은 하나는 define된 함수고 하나는 declare된 함수라는 것입니다

정확히 알진 못하더라도 느낌상 define된 함수는 현재 코드에서 함수가 정의된단걸 알 수 있고

declare된 함수는 다른 소스파일이나 라이브러리에 정의돼있는 함수란걸 알 수 있습니다

puts함수도 현재 코드에서 정의되는 함수가 아닌 라이브러리에서 정의된 함수죠


그러면 declare 될 함수를 어떻게 LLVM Pass에서 삽입할 수 있을까요?

이런 애매한걸 해결하기 가장 쉬운 방법은 IR파일을 LLVM API를 이용한 c++코드로 바꾸는 것입니다

저도 이 기능을 발견했을 때 매우 행복했는데, 어떤 기능인지 보여드리겠습니다


생성된 puts.cpp를 읽어서 puts 를 검색해보세요

막연하게만 느껴지던 puts함수 삽입을 이렇게 친절한 예제로 볼 수 있습니다

한줄한줄 완벽한 이해는 안되더라도 이렇게하면  위의 IR코드 처럼

declare i32 @puts(...) #1

이 한 줄을 IR파일에 자동으로 추가할 수 있겠죠


코드에 대한 약간의 설명을 드리자면

우선 mod는 현재 모듈을 가르키는 변수입니다. 우리의 경우엔 F.getParent()를 변수로 저장해서 사용하면 되겠죠

FuncTy_6은 잠시 후에 보도록 하고

GlobalValue::ExternalLinkage 는 http://llvm.org/docs/doxygen/html/classllvm_1_1GlobalValue.html#aedfa75f0c85c4aa85b257f066fbea57c 이 링크를 참고해주세요. 

그다음에 Attributes에 관련되어 보이는 코드들은 

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

이 부분을 만들어주는 코드입니다

그러려니 합시다..


위에서 스킵한 FuncTy_6은 여기있습니다

뭔진 모르겠지만 puts함수의 타입은 이런가봅니다

왜 isVarArg 가 true인진 모르겠지만 그러려니합시다..



        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);

            return false;

        }

FuncTy_6과 puts를 삽입하는 코드를 그대로 옮겼습니다

임의로 추가한 부분은 4번째줄의 Module *mod = F.getParent(); 입니다


자 그럼 이제 테스트 해봅시다

빌드를 이미 완전히 해보셨다면 금방 빌드가 완료됩니다

컴퓨터 사양에따라 중간 중간 빌드하는게 무리다 싶으시면 제가 보여드리는 결과만 보고 넘어가셔도 괜찮습니다. 그래도 직접 확인해보는게 훨씬 와닿겠죠



⚡ root@vultr  ~/build/bin  clang -S -emit-llvm test.c -w

 ⚡ root@vultr  ~/build/bin  cat test.ll

; ModuleID = 'test.c'

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [8 x i8] c"5+3=%d\0A\00", align 1


; Function Attrs: nounwind uwtable

define i32 @add(i32 %x, i32 %y) #0 {

entry:

  %x.addr = alloca i32, align 4

  %y.addr = alloca i32, align 4

  store i32 %x, i32* %x.addr, align 4

  store i32 %y, i32* %y.addr, align 4

  %0 = load i32, i32* %x.addr, align 4

  %1 = load i32, i32* %y.addr, align 4

  %add = add nsw i32 %0, %1

  ret i32 %add

}


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 @add(i32 5, i32 3)

  %call1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %call)

  ret i32 0

}


declare i32 @printf(i8*, ...) #1


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.8.0 (trunk 247122)"}

 ⚡ root@vultr  ~/build/bin 

지금 이건 시스템에 설치돼있던 LLVM, 즉 puts함수를 자동으로 삽입하는 코드가 없는 LLVM입니다


 ✘ ⚡ root@vultr  ~/build/bin  ./clang -S -emit-llvm test.c -w

Current function name: [add]

Current module name: [test.c]

Current function name: [main]

Current module name: [test.c]

 ⚡ root@vultr  ~/build/bin  cat test.ll

; ModuleID = 'test.c'

source_filename = "test.c"

target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"

target triple = "x86_64-unknown-linux-gnu"


@.str = private unnamed_addr constant [8 x i8] c"5+3=%d\0A\00", align 1


; Function Attrs: nounwind uwtable

define i32 @add(i32 %x, i32 %y) #0 {

entry:

  %x.addr = alloca i32, align 4

  %y.addr = alloca i32, align 4

  store i32 %x, i32* %x.addr, align 4

  store i32 %y, i32* %y.addr, align 4

  %0 = load i32, i32* %x.addr, align 4

  %1 = load i32, i32* %y.addr, align 4

  %add = add nsw i32 %0, %1

  ret i32 %add

}


; Function Attrs: nounwind uwtable

define i32 @main() #0 {

entry:

  %call = call i32 @add(i32 5, i32 3)

  %call1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str, i32 0, i32 0), i32 %call)

  ret i32 0

}


declare i32 @printf(i8*, ...) #1


declare i32 @puts(...)


attributes #0 = { nounwind uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }

attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }


!llvm.ident = !{!0}


!0 = !{!"clang version 3.9.0 (trunk 275527) (https://github.com/cd80/UtilizedLLVM.git 2587b3df7a08ff644b1b31139619798829d7f95e)"}

 ⚡ root@vultr  ~/build/bin 

puts가 생겼습니다!


지금 우리가 어떤 지식을 기반으로 했다기 보다 일종의 예제코드를 보고 복사붙여넣기를 한것이기 떄문에 아직 우리가 LLVM에 대해서 안다! 라고는 할 수 없는 단계지만

이 방식으로도 충분히 우리가 원하는 어떤 기능이든지 다 구현할 수 있습니다



쓰는게 힘에부쳐서 한 글에 많은 내용을 담지는 못합니다

다음 글에서는 동적으로 삽입된 puts함수에 우리가 원하는 문자열을 인자로 전달하고 호출하는 코드를 작성한 뒤 이를 각 함수의 맨 처음 인스트럭션으로 삽입하는 과정을 다루겠습니다

직접 구글과 레퍼런스를 참고하면서 구현해보시고 저는 어떻게 구현하는지 보러오시는것도 좋은 공부가 될것 같습니다

신고

설정

트랙백

댓글

1 2 3 4 5 ... 26

티스토리 툴바