LogFunctionNames 모듈 개발 - 뼈대 작성과 컴파일타임에서의 함수 이름 로깅

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

이 강의글의 컨셉은 LLVM을 깊게 이해한 개발이 아닌 일단 쓰는 법을 익혀보자 이기 때문에 아주 자세한 내용은 다루지 않습니다


ModulePass는 모듈을 변조하거나 최적화할때 사용하고, FunctionPass는 함수, BasicBlockPass는 각 basic block별로 적용합니다

그 중에서 우리는 각 함수의 맨 첫부분에 함수의 이름을 로깅하는 코드를 작성할 것이기 때문에 FunctoinPass를 사용합니다. 해당 개발은 다음 글에서 진행합니다


아래에서 나오는 UtilizedLLVM은 http://clang.llvm.org/get_started.html 을 그대로 따라한 LLVM 3.9.0 버젼입니다. LLVM이 업데이트되어 저 링크를 보고 따라했는데 3.9.0 이상버젼이 나온다면 이전 글에서 3.6.2를 받았을 때와 마찬가지로 branch를 지정해서 받으시면 됩니다


우선 lib/Transforms/ 로 이동합니다

Hello는 dummy module로, LLVM 모듈 개발시 참고할 만 하나 이것보다는 좀더 쉬운 예제로 시작하겠습니다


우선 우리 코드들이 들어갈 Utility 디렉토리를 생성하고 CMakeLists.txt와 LLVMBuild.txt를 수정합니다



Utility 디렉토리에서도 CMakeLists.txt를 작성해야합니다

LLVM Obfuscator 의 CMakeLists.txt를 참고하겠습니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/CMakeLists.txt

LLVM Pass들은 모두 LLVM 프리픽스를 가진 라이브러리 파일로 빌드됩니다

우리 모듈은 LLVMUtility로 정했고 처음 개발해볼 모듈인, 함수가 호출될때 해당 함수의 이름을 로깅하는 모듈의 파일명을 LogFunctionNames.cpp 로 정했습니다


다음으로 LLVMBuild.txt를 작성합니다

마찬가지로 LLVM Obfuscator에서 LLVMBuild.txt를 가져와서 Obfuscation을 Utility로 변경합니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/LLVMBuild.txt


이제 LogFunctionNames.cpp를 작성하면 됩니다

이번에도 뼈대를 LLVM Obfuscator에서 가져오겠습니다

https://github.com/obfuscator-llvm/obfuscator/blob/llvm-3.6.1/lib/Transforms/Obfuscation/BogusControlFlow.cpp

이 파일을 받아 LogFunctionNames.cpp로 파일명을 변경하고 주석과 include, 플래그를 모두 지웁니다


최종적으로 이런 형태가 되면 됩니다

이제 몇가지 수정을 하겠습니다

BogusControlFlow를 LogFunctionNames로 바꾸고

createBogus 를 createLogger로

static RegisterPass<BogusControlFlow> X("boguscf", "inserting bogus control flow"); 를 static RegisterPass<LogFunctionNames> X("logger", "inserting logging routine for functions"); 로 바꿉니다



수정할것이 약간 많아 설명을 건너뛰고 마지막으로 어떤 형태가 돼야 하는지 보여드리겠습니다

#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"

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

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

}

이렇게 되면 뼈대는 우선 작성을 완료했습니다



여기서 include한 파일중 하나인 "llvm/Transforms/Utility/LogFunctionNames.h"을 작성합니다

//===- LogFunctionNames.h - LogFunctionNames Utility pass-------------------------===//

//

//                     The LLVM Compiler Infrastructure

//

// This file is distributed under the University of Illinois Open Source

// License. See LICENSE.TXT for details.

//

//===--------------------------------------------------------------------------------===//

//

// This file contains includes and defines for the LogFunctionNames pass

//

//===--------------------------------------------------------------------------------===//


#ifndef _LOGFUNCTIONNAMES_H_

#define _LOGFUNCTIONNAMES_H_



// LLVM include

#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>


using namespace std;

using namespace llvm;


// Namespace

namespace llvm {

Pass *createLogger ();

Pass *createLogger (bool flag);

}

#endif 


여기까지만 해도 빌드는 되지만 아직 createLogger()를 PassManager에 등록하지 않았습니다


LLVMObfuscator에서는 populateModulePassManager에서 모든 Pass들을 등록했지만 우리는 용도에 맞게 populateFunctionPassManager에서 createLogger를 FunctionPassManager에 추가하겠습니다

lib/Transforms/IPO/PassManagerBuilder.cpp 를 수정합니다

우선 #include "llvm/Transforms/Utility/LogFunctionNames.h" 을 인클루드 하고


void PassManagerBuilder::populateFunctionPassManager(

    legacy::FunctionPassManager &FPM) {

  addExtensionsToPM(EP_EarlyAsPossible, FPM);

  FPM.add(createLogger());

  // Add LibraryInfo if we have some.

  if (LibraryInfo)

    FPM.add(new TargetLibraryInfoWrapperPass(*LibraryInfo));

  ...생략...

}

위와같이 추가해줍니다


이상태에서 빌드하면 이전 글에서 언급했던 오류중에 하나인

/root/UtilizedLLVM/lib/Transforms/IPO/PassManagerBuilder.cpp:214: undefined reference to `llvm::createLogger()'

이런 에러가 발생합니다

해결 하는 방법은 이전 글에 있으니 기억이 안나신다면 다시 보고 오셔서 해결해보시길 바랍니다

지금부터는 이 오류를 해결한 상태에서 진행하기 때문에 꼭 지금 해결하셔야 합니다






http://clang.llvm.org/get_started.html

빌드법은 위 링크 7번 참고하세요


빌드는 꽤 오래걸립니다. 그래도 앞으로는 우리 모듈이 수정되면 우리 모듈만 새로 빌드해서 링크하기 때문에 처음에만 조금 기다리면 됩니다

소스코드는 어떤걸 사용해도 무방하나 제가 자주 테스트용으로 사용하는 코드는 아래와 같습니다. 스샷에서 사용한 코드와 같은 코드입니다

int add(int x, int y){

return x+y;

}

int main(){

printf("5+3=%d\n", add(5,3));

}




문제 하나.

undefined reference to ~~에러

이전 글 참고해주세요


문제 둘.

make 시 ld가 segmentation fault로 죽음

저는 링킹중 메모리 용량이 부족해 이런 문제가 발생했습니다

아래 링크를 참고해 swap file을 설정하시면 됩니다

https://www.digitalocean.com/community/tutorials/how-to-add-swap-on-ubuntu-14-04




첫 유틸리티 모듈은 글의 초반부에서 얘기했던 것 처럼, 프로그램의 각 함수마다 맨 처음에 함수의 이름을 출력하는 기능을 할것이기 때문에 이번 글에서는 함수의 이름을 동적으로 어떻게 가져오는지를 알아보고, 모듈 개발시 자잘한 부분인 구조적 뼈대를 갖추는 법에 대해 이전 글에 이어서 자세히 설명했습니다

다음 글에서는 puts함수를 타겟 모듈에 동적으로 삽입하는 법에 대해 다루겠습니다



LLVM API를 이용해 개발하다 보면 어느 개발에서나 그렇 듯 레퍼런스를 많이 읽어봐야 합니다

다음 글에서 다룰 함수를 동적 삽입하는 경우에도 아주 간단한 함수 하나로 끝낼 수 있지만 레퍼런스를 잘 보지 않아 300줄가량의 코드를 작성했던 적이 있습니다

오늘 코드에서는 Function 클래스의 getName 메소드만을 사용했는데, 

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

위의 function 클래스의 레퍼런스를 보면 getName 메소드가 존재하지 않습니다

ctrl+f를 누르고 검색해보면 Value 클래스에 정의되어있는 것을 알 수 있고, 레퍼런스 맨 위의 inheritance diagram을 보면 Function이 Value를 최상위클래스로써 상속받는 다는 것을 알 수 있습니다

Value 클래스는 앞으로도 항상 보게 될 클래스이므로 이번 글에서 다룬 Function 클래스와 함께 레퍼런스를 훑어보시길 추천드립니다

신고

설정

트랙백

댓글

LLVM 입문 - LLVM Obfuscator의 구조

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

이번 글에서는 LLVM 3.6.1기반의 LLVM Obfuscator와, LLVM 3.6.1을 디핑해 LLVM을 커스터마이즈 하기 위해 어떻게 하면 되는 가를 공부합니다


먼저 LLVM Obfuscator를 클론합니다


git clone -b llvm-3.6.1 https://github.com/obfuscator-llvm/obfuscator.git



이제 LLVM 3.6.1을 받아야하는데, http://clang.llvm.org/get_started.html 을 그대로 따라하면 최신버젼으로 받아지기 때문에 release_36 브랜치를 직접 지정해 받아야합니다.

약간 슬픈일은 release_36브랜치의 버젼이 3.6.1이 아닌 3.6.2입니다

그래서 약간 차이가 더 생기긴 하지만 그래도 3.9.0이랑 비교하는것보단 적게 차이가 존재합니다


예를들어

svn co http://llvm.org/svn/llvm-project/llvm/trunk llvm

이 명령문을

svn co http://llvm.org/svn/llvm-project/llvm/branches/release_36 llvm

으로 바꿔 실행하면됩니다.

그 외에는 모두 동일하게 하면 되는데 libcxx는 건너뛰어주세요


모두 완료하시면 위와 같이 llvm 디렉토리와 obfuscator디렉토리가 존재합니다


diff 결과가 263줄이나 나오지만 이건 LLVM 모듈 개발하면서 겪는 상황보다는 아주 쾌적합니다

그리고 다행히 이중에서 약 10줄정도만 보면 됩니다


`

이 밑으로도 약 200개 가량의 결과가 있지만 총 diff결과(스샷에 있는것 외에도)에서 필요한것만 추려보자면

Only in obfuscator/include/llvm: CryptoUtils.h

Only in obfuscator/include/llvm/Transforms: Obfuscation

Files llvm/lib/Transforms/CMakeLists.txt and obfuscator/lib/Transforms/CMakeLists.txt differ

Files llvm/lib/Transforms/IPO/LLVMBuild.txt and obfuscator/lib/Transforms/IPO/LLVMBuild.txt differ

Files llvm/lib/Transforms/IPO/PassManagerBuilder.cpp and obfuscator/lib/Transforms/IPO/PassManagerBuilder.cpp differ

Files llvm/lib/Transforms/LLVMBuild.txt and obfuscator/lib/Transforms/LLVMBuild.txt differ

Files llvm/lib/Transforms/Makefile and obfuscator/lib/Transforms/Makefile differ

Only in obfuscator/lib/Transforms: Obfuscation

위 7개 결과의 차이점을 분석해보겠습니다

우선 맨 윗 두줄은 include 경로에 있는 차이기 때문에 굳이 보지 않겠습니다


 ✘ ⚡ root@vultr  ~/diffLLVM  diff  llvm/lib/Transforms/CMakeLists.txt  obfuscator/lib/Transforms/CMakeLists.txt

8a9

> add_subdirectory(Obfuscation)

 ✘ ⚡ root@vultr  ~/diffLLVM 

맨처음으로 lib/Transforms/CMakeLists.txt 의 차이가 존재합니다


 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1  cat CMakeLists.txt

add_subdirectory(Utils)

add_subdirectory(Instrumentation)

add_subdirectory(InstCombine)

add_subdirectory(Scalar)

add_subdirectory(IPO)

add_subdirectory(Vectorize)

add_subdirectory(Hello)

add_subdirectory(ObjCARC)

add_subdirectory(Obfuscation)

 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1  ls

CMakeLists.txt  Hello  IPO  InstCombine  Instrumentation  LLVMBuild.txt  Makefile  Obfuscation  ObjCARC  Scalar  Utils  Vectorize

 ⚡ root@vultr  ~/diffLLVM/obfuscator/lib/Transforms   llvm-3.6.1 

lib/Transforms/ 밑에 Obfuscation이라는 폴더가 있고 CMakeLists.txt에서 해당 디렉토리를 add_subdirectory() 를 이용해 추가해주면 됩니다

우리가 새로운 모듈을 제작할때도 예를들어 CustomModules라는 디렉토리를 만들어서 코드를 작성하고, lib/Transforms/CMakeLists.txt에 add_subdirectory(CustomModules)를 추가하면 됩니다



⚡ root@vultr  ~/diffLLVM  diff llvm/lib/Transforms/IPO/LLVMBuild.txt obfuscator/lib/Transforms/IPO/LLVMBuild.txt

23c23

< required_libraries = Analysis Core IPA InstCombine Scalar Support Target TransformUtils Vectorize

---

> required_libraries = Analysis Core IPA InstCombine Scalar Support Target TransformUtils Vectorize Obfuscation

 ✘ ⚡ root@vultr  ~/diffLLVM 

여기서는 required_libraries라는 줄에 Obfuscation이 있냐 없냐가 차이인데,

여기서 한가지 설명이 필요합니다

LLVM은 아주 다양한 용도로 사용되고 있기 때문에 각 상황마다 꼭 필요한 기능이 있고 전혀 필요없는 기능이 있습니다

그래서 각각 기능들은 개별적인 라이브러리 파일로 빌드가 되고

LLVM은 이런 의존성(required_libraries 등등)을 검사해 필요한 라이브러리만 링크시킵니다

여기서 required_libraries에 Obfuscation이 없다면, 빌드시에는 헤더를 잘 참조하기 때문에 문제가 생기지 않지만 링크과정에서 라이브러리가 링크되지 않기 때문에 undefined references for symbol "llvm obfuscator에서 사용하는 함수" 라는 에러가 발생하게 됩니다

우리도 모듈을 개발하면 여기에 의존성을 명시해야 합니다



다음 차이입니다



(사진이 커 생략됨)

✘ ⚡ root@vultr  ~/diffLLVM  diff llvm/lib/Transforms/IPO/PassManagerBuilder.cpp obfuscator/lib/Transforms/IPO/PassManagerBuilder.cpp

31a32,37

> #include "llvm/Transforms/Obfuscation/BogusControlFlow.h"

> #include "llvm/Transforms/Obfuscation/Flattening.h"

> #include "llvm/Transforms/Obfuscation/Split.h"

> #include "llvm/Transforms/Obfuscation/Substitution.h"

> #include "llvm/CryptoUtils.h"

>

80a87,102

> // Flags for obfuscation

> static cl::opt<bool> Flattening("fla", cl::init(false),

>                                 cl::desc("Enable the flattening pass"));

>

> static cl::opt<bool> BogusControlFlow("bcf", cl::init(false),

>                                       cl::desc("Enable bogus control flow"));

>

> static cl::opt<bool> Substitution("sub", cl::init(false),

>                                   cl::desc("Enable instruction substitutions"));

>

> static cl::opt<std::string> AesSeed("aesSeed", cl::init(""),

>                                     cl::desc("seed for the AES-CTR PRNG"));

>

> static cl::opt<bool> Split("spli", cl::init(false),

>                            cl::desc("Enable basic block splitting"));

>

98a121,126

>

>     // Initialization of the global cryptographically

>     // secure pseudo-random generator

>     if(!AesSeed.empty()) {

>         llvm::cryptoutils->prng_seed(AesSeed.c_str());

>     }

161a190,193

>   MPM.add(createSplitBasicBlock(Split));

>   MPM.add(createBogus(BogusControlFlow));

>   MPM.add(createFlattening(Flattening));

>

179a212

>     MPM.add(createSubstitution(Substitution));

373a407

>   MPM.add(createSubstitution(Substitution));

이번엔 모두 필요합니다

우선 맨처음에 include하는것은 자명합니다

그 다음


> // Flags for obfuscation

> static cl::opt<bool> Flattening("fla", cl::init(false),

>                                 cl::desc("Enable the flattening pass"));

>

> static cl::opt<bool> BogusControlFlow("bcf", cl::init(false),

>                                       cl::desc("Enable bogus control flow"));

>

> static cl::opt<bool> Substitution("sub", cl::init(false),

>                                   cl::desc("Enable instruction substitutions"));

>

> static cl::opt<std::string> AesSeed("aesSeed", cl::init(""),

>                                     cl::desc("seed for the AES-CTR PRNG"));

>

> static cl::opt<bool> Split("spli", cl::init(false),

>                            cl::desc("Enable basic block splitting"));

이렇게 flag를 선언해주는데, 첫번재 인자는 커맨드라인 명령이고, 두번째는 초기값, 세번째는 플래그에 대한 설명입니다

Flattening을 초기값(cl::init(false))에서 true로 변경하고 싶다면 -mllvm -fla나 -mllvm -fla=1 을 컴파일 시 추가 하면 됩니다.

Flattening, BogusControlFlow, Substitution 등은 모두 플래그의 값을 갖는 스태틱변수로, 플래그에 해당하는 각각의 Pass들을 실행할지 말지 결정할 수 있습니다


이 플래그들은 바로 그 다음 diff에서 보이는 코드에서 사용됩니다

>

>     // Initialization of the global cryptographically

>     // secure pseudo-random generator

>     if(!AesSeed.empty()) {

>         llvm::cryptoutils->prng_seed(AesSeed.c_str());

>     }

161a190,193

>   MPM.add(createSplitBasicBlock(Split));

>   MPM.add(createBogus(BogusControlFlow));

>   MPM.add(createFlattening(Flattening));

>

179a212

>     MPM.add(createSubstitution(Substitution));

373a407

>   MPM.add(createSubstitution(Substitution));

이 코드들은 지금 diff한 파일인

lib/Transforms/IPO/PassManagerBuilder.cpp 의 populateModulePassManager 함수에 있습니다

여기서는 플래그들을 인자로 넘겨 해당 함수에서 플래그값을 이용해 난독화 적용 여부를 결정합니다



마지막으로 아래 diff 결과는 직접 어떻게 다른지 확인해보시길 바랍니다

Files llvm/lib/Transforms/LLVMBuild.txt and obfuscator/lib/Transforms/LLVMBuild.txt differ

Files llvm/lib/Transforms/Makefile and obfuscator/lib/Transforms/Makefile differ

Only in obfuscator/lib/Transforms: Obfuscation

특히 맨아래의 Only in obfuscator/lib/Transforms: Obfuscation은 LLVM Obfuscator 모듈의 실제 구현 파일들이 존재합니다




중요한 포인트를 정리하자면

1. lib/Transforms/ 밑에 우리 디렉토리를 만들어 작업한다

2. lib/Transforms/CMakeLists.txt 와 lib/Transforms/LLVMBuild.txt에 우리 모듈 디렉토리를 추가해준다

3. lib/Transforms/IPO/LLVMBuild.txt 의 required_libraries에 우리 모듈 이름을 추가한다

4. lib/Transforms/IPO/PassManagerBuilder.txt 의 populateModulePassManager 함수에서 우리 모듈을 ModulePassManager에 추가하도록 한다


이번 글의 내용을 꼭 직접 하나씩 확인해보시면서 진행하시길 권합니다. 새로운 모듈을 개발할 때마다 하나씩 까먹고 고생하는 일이 초반에 종종 생깁니다

신고

설정

트랙백

댓글

LLVM 입문

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

LLVM은 기존의 gcc와 같은 컴파일러가 당시에 너무 모놀리딕한 형태로 개발되어 코드의 재사용이 힘들단 단점을 개선하기 위해 만들어진 정말 기능별로 잘 분리된 컴파일러입니다


(출처 : http://www.aosabook.org/en/llvm.html)

LLVM은 컴파일러의 3요소가 각각 어떤 형태로든 재사용 될 수 있도록 철저히 분리돼있습니다

먼저 frontend에서는 프로그래밍 언어와 같은 추상적인 형태를 Optimizer에서 다루기 쉽도록 CallGraph를 분석하고, 중간언어인 LLVM IR로 변환해줍니다


이런 LLVM IR 형태의 프로그램을 아주 잘 짜여진 LLVM API들을 이용해 최적화를 실행하고, 많은 방식의 코드 변조가 가능해 난독화(LLVM Obfuscator), 개발단계에서의 취약점 탐지(sanitizer류 플러그인들 ex. asan)등이 이 레벨에서 구현됩니다


처리가 끝난 LLVM IR은 이제 Backend로 넘어가고 백엔드에서는 타겟 머신에 맞는 최적화와 더불어 핵심 기능인 실제 머신 코드로 변환하는 역할을 합니다



이제부터 블로그에 정리하고자 하는 내용은 제가 지난 1년간 LLVM을 이용한 프로젝트를 진행하면서 느낀 LLVM을 사용하는 가장 간단한 방법에 대한 것입니다.

저는 컴파일러의 이론적인 부분은 잘 모르기 때문에 LLVM을 사용해 프로그램에 동적으로 기능을 추가하는 과정에 대해 정리하고, 제가 가장 큰 도움을 받은 LLVM Obfuscator 구조를 설명합니다



LLVM 모듈 개발에 관련된 튜토리얼은 구글에 검색해보시면 많이 나옵니다

제가 진행한 프로젝트는 플러그인식 개발이 아닌 LLVM Optimizer 단계 자체에 기능을 적용한것이기 때문에 검색해도 딱히 잘 나오지 않았고, LLVM으로 개발된 프로젝트를 여러개 분석해보니 LLVM Obfuscator가 사용한 방식이 가장 이해하기도 쉬웠고 제 코드에 적용시키기도 간편했습니다


다음 글에서는 LLVM Obfuscator의 구조를 어떻게 파악하는지를 공유하고, 파악된 구조를 활용해 우리가 추가하고자 하는 기능의 뼈대를 잡겠습니다


제가 LLVM을 활용한 프로젝트는 공개하기 난감하기 때문에 약간 가벼운, 

예를 들어 코딩을 하다가 컴파일러에서 이런기능도 지원해줬으면 좋을텐데 싶은 그런 유틸리티성 기능들을 스텝바이 스텝으로 구현해보겠습니다

그리고 각각 기능 구현 글의 마지막에는 제가 그 기능을 직접 구현해보면서 어떤 오류들이 발생했고 이를 어떻게 해결했는지를 정리합니다



p.s.1 제가 쓴 글은 항상 잘못됐을 수 있습니다. 이건 아니다 싶은건 가차 없이 무시하거나 지적해주세요. 

p.s.2 블로그에서 다루는 추가기능들은 https://github.com/cd80/UtilizedLLVM 여기에 통합되어 배포됩니다.

저작자 표시
신고

설정

트랙백

댓글


티스토리 툴바