LLVM Build, Pass

안상준·2025년 4월 11일

LLVM

목록 보기
4/12

LLVM Build 하기

환경은 24.04 우분투 서버에서 진행하였다.

LLVM 설치

1. 먼저 패키지를 설치

sudo apt update
sudo apt install -y build-essential cmake ninja-build git

2. llvm 소스코드를 다운로드.

git clone https://github.com/llvm/llvm-project.git
cd llvm-project

3. 빌드 디렉토리를 생성하고 CMake를 설정

mkdir build && cd build
cmake -G Ninja ../llvm \
      -DLLVM_ENABLE_PROJECTS="clang;lld" \
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/usr/local/llvm

4. 빌드 및 설치

ninja
sudo ninja install

5. 환경 변수 설정

echo 'export PATH="/usr/local/llvm/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

6. 설치 확인

clang --version
llvm-config --version


정상적으로 설치된 화면이다

사용자 정의 Pass

1. Pass 디렉토리 생성

mkdir -p ~/llvm-project/llvm/lib/Transforms/MyPass
cd ~/llvm-project/llvm/lib/Transforms

2. CMakeLists.txt 수정

vi CMakeLists.txt

맨 아래줄에 add_subdirectory(MyPass) 추가

3. MyPass 디렉토리에 CMakeLists.txt 생성

cd ~/llvm-project/llvm/lib/Transforms/MyPass
cat > CMakeLists.txt << 'EOF'
add_llvm_library(MyPass MODULE
  MyPass.cpp
  
  PLUGIN_TOOL
  opt
)
EOF

4. MyPass.cpp 작성

같은 디렉토리에 cpp 파일을 작성해 준다.

cat > MyPass.cpp << 'EOF'
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {
struct MyPass : public PassInfoMixin<MyPass> {
  // optnone 속성이 있는 함수에서도 실행되도록 설정
  static bool isRequired() { return true; }

  PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
    errs() << "=== Running MyPass on function: " << F.getName() << " ===\n";
    return PreservedAnalyses::all();
  }
};
}

extern "C" LLVM_ATTRIBUTE_WEAK ::llvm::PassPluginLibraryInfo
llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "MyPass", LLVM_VERSION_STRING,
    [](PassBuilder &PB) {
      PB.registerPipelineParsingCallback(
        [](StringRef Name, FunctionPassManager &FPM,
           ArrayRef<PassBuilder::PipelineElement>) {
          if (Name == "mypass") {
            FPM.addPass(MyPass());
            return true;
          }
          return false;
        });
    }
  };
}
EOF

Pass 빌드

cd ~/llvm-project/build
ninja

빌드가 성공하게 되면

다음과 같이 뜨게 된다.

테스트 파일

1. 테스트용 C 파일 작성

홈 디렉토리에 새로운 디렉토리를 만들어 진행해 주었다.

mkdir -p ~/experiment
cd ~/experiment

다음과 같은 C 파일을 만들어 주었다.

#include <stdio.h>
int calc(int a, int b) {
	return a + b;
}
int main() {
	int a = 1, b = 2;
	printf("%d", calc(a, b));
	return 0;
}

2. LLVM IR 생성

clang -S -emit-llvm test.c -o test.ll

3. Pass 실행

/home/$(whoami)/llvm-project/build/bin/opt -load-pass-plugin=/home/$(whoami)/llvm-project/build/lib/MyPass.so -passes="mypass" test.ll -o test_out.ll

결과는 다음과 같이 나오게 된다.

환경 변수 및 스크립트 설정

위의 명령어를 실행해보면 알 수 있듯이 매우 길어 입력하기가 불편하다. 그래서 스크립트와 환경변수를 이용해 수고를 덜도록 하자.

1. 스크립트 생성

mkdir -p ~/bin
cat > ~/bin/llvm_pass << 'EOF'
#!/bin/bash
LLVM_BUILD=~/llvm-project/build
LLVM_PASS=$LLVM_BUILD/lib/MyPass.so

$LLVM_BUILD/bin/opt -load-pass-plugin=$LLVM_PASS -passes="mypass" "$1" -o "${1%.ll}_out.ll"
EOF

chmod +x ~/bin/llvm_pass

2. PATH 생성

bin 파일이 있는지 확인하고 없다면 생성 하여 환경변수 설정

if [[ ":$PATH:" != *":$HOME/bin:"* ]]; then
    echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc
    source ~/.bashrc
fi

3. 테스트

cd ~/experiment
llvm_pass test.ll

결과 화면

정리

LLVM Build 하는 방법에 대해 알아 보았으며, LLVM Pass를 이용하여 명령어 이름을 뽑아 보았다. 하지만 이 외에 다양한 기능을 할 수 있다.

분석

  • 함수 이름 출력 : 오늘 다룬 내용, 선언돼 있는 함수 이름 출력
  • 명령어 개수 계산 : 각 함수에 포함된 명령어 유형과 개수 분석
  • 함수 호출 그래프 분석 : 어떤 함수가 어떤 함수를 호출하는지 분석
  • 메모리 접근 패턴 분석 : 프로그램이 메모리를 어떻게 접근하는지 분석
  • 루프 분석 : 루프 구조와 반복 횟수 분석

변환

  • 최적화 : 불필요한 코드 제거, 루프 최적화
  • 계측 : 성능 측정을 위한 코드 삽입
  • 보안 강화 : 버퍼 오버플로우 검사 코드 삽입
  • 병렬화 : 순차 코들를 병렬 코드로 변환

Todo

찾아본 결과 llvm pass 를 이용하여 루프 분석, 스위치 패턴 분석, 제어 흐름 분석 등이 탐지가 가능할 것 같으며,
llvm pass 를 이용하여 난독화 해제도 일부 가능할 것 같다.
중복 변수 제거 및 단순화, 불필요한 분기 제거, 조건부 분기 단순화, 상태변수 추적 및 단순화 등등...
난독화 된 코드를 분석할 필요가 있을 것 같다.

0개의 댓글