Homework 5
Using The LLVM Plugin To Embed Modified Bitcode In Executable
For this homework I decided to write a pass which runs at the very end of the optimization pipeline that does the following: 1. It first clones the module being worked on. 2. It then finds all outlined functions and global variables annotated with the keyword patch_point
and removes their definitions. 3. Embeds the cloned bitcode into a section of the final emitted ELF.
Embedding the LLVM IR bitcode of the ELF allows for easier generation of patched and instrumented binaries at runtime because it retains important information like the calling convention of the functions to be patched.
I ran the compiler plugin on the following program:
#include <iostream>
template <typename T>
__attribute__((used, noinline, annotate("patch_point"))) T myPathPoint(T a,
) {
T breturn a + b;
}
int main() {
static constexpr int myArray[]{1, 4, 5, 6, 10};
int mySum{0};
for (const auto &el : myArray) {
+= el * myPathPoint(mySum, el);
mySum }
std::cout << "Sum of my array: " << mySum << std::endl;
return 0;
}
The original intercepted llvm::Module
by the compiler plugin for this program was the following:
; ModuleID = '/home/matinraayai/CLionProjects/bril/llvm-plugin/example/Example.cpp'
source_filename = "/home/matinraayai/CLionProjects/bril/llvm-plugin/example/Example.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
module asm ".globl _ZSt21ios_base_library_initv"
%"class.std::basic_ostream" = type { ptr, %"class.std::basic_ios" }
%"class.std::basic_ios" = type { %"class.std::ios_base", ptr, i8, i8, ptr, ptr, ptr, ptr }
%"class.std::ios_base" = type { ptr, i64, i64, i32, i32, i32, ptr, %"struct.std::ios_base::_Words", [8 x %"struct.std::ios_base::_Words"], i32, ptr, %"class.std::locale" }
%"struct.std::ios_base::_Words" = type { ptr, i64 }
%"class.std::locale" = type { ptr }
$_Z11myPathPointIiET_S0_S0_ = comdat any
@_ZZ4mainE7myArray = internal constant [5 x i32] [i32 1, i32 4, i32 5, i32 6, i32 10], align 16
@_ZSt4cout = external dso_local global %"class.std::basic_ostream", align 8
@.str = private unnamed_addr constant [18 x i8] c"Sum of my array: \00", align 1
@.str.1 = private unnamed_addr constant [12 x i8] c"patch_point\00", section "llvm.metadata"
@.str.2 = private unnamed_addr constant [69 x i8] c"/home/matinraayai/CLionProjects/bril/llvm-plugin/example/Example.cpp\00", section "llvm.metadata"
@llvm.global.annotations = appending global [1 x { ptr, ptr, ptr, i32, ptr }] [{ ptr, ptr, ptr, i32, ptr } { ptr @_Z11myPathPointIiET_S0_S0_, ptr @.str.1, ptr @.str.2, i32 4, ptr null }], section "llvm.metadata"
@llvm.compiler.used = appending global [1 x ptr] [ptr @_Z11myPathPointIiET_S0_S0_], section "llvm.metadata"
; Function Attrs: mustprogress noinline norecurse optnone uwtable
define dso_local noundef i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca ptr, align 8
%4 = alloca ptr, align 8
%5 = alloca ptr, align 8
%6 = alloca ptr, align 8
store i32 0, ptr %1, align 4
store i32 0, ptr %2, align 4
store ptr @_ZZ4mainE7myArray, ptr %3, align 8
store ptr @_ZZ4mainE7myArray, ptr %4, align 8
store ptr getelementptr inbounds (i32, ptr @_ZZ4mainE7myArray, i64 5), ptr %5, align 8
br label %7
7: ; preds = %22, %0
%8 = load ptr, ptr %4, align 8
%9 = load ptr, ptr %5, align 8
%10 = icmp ne ptr %8, %9
br i1 %10, label %11, label %25
11: ; preds = %7
%12 = load ptr, ptr %4, align 8
store ptr %12, ptr %6, align 8
%13 = load ptr, ptr %6, align 8
%14 = load i32, ptr %13, align 4
%15 = load i32, ptr %2, align 4
%16 = load ptr, ptr %6, align 8
%17 = load i32, ptr %16, align 4
%18 = call noundef i32 @_Z11myPathPointIiET_S0_S0_(i32 noundef %15, i32 noundef %17)
%19 = mul nsw i32 %14, %18
%20 = load i32, ptr %2, align 4
%21 = add nsw i32 %20, %19
store i32 %21, ptr %2, align 4
br label %22
22: ; preds = %11
%23 = load ptr, ptr %4, align 8
%24 = getelementptr inbounds i32, ptr %23, i32 1
store ptr %24, ptr %4, align 8
br label %7
25: ; preds = %7
%26 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, ptr noundef @.str)
%27 = load i32, ptr %2, align 4
%28 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8) %26, i32 noundef %27)
%29 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8) %28, ptr noundef @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_)
ret i32 0
}
; Function Attrs: mustprogress noinline nounwind optnone uwtable
define linkonce_odr dso_local noundef i32 @_Z11myPathPointIiET_S0_S0_(i32 noundef %0, i32 noundef %1) #1 comdat {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
store i32 %0, ptr %3, align 4
store i32 %1, ptr %4, align 4
%5 = load i32, ptr %3, align 4
%6 = load i32, ptr %4, align 4
%7 = add nsw i32 %5, %6
ret i32 %7
}
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8), i32 noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(ptr noundef nonnull align 8 dereferenceable(8)) #2
attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { mustprogress noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
!llvm.linker.options = !{}
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 2}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{!"AMD clang version 18.0.0git (https://github.com/RadeonOpenCompute/llvm-project roc-6.2.0 24292 26466ce804ac523b398608f17388eb6d605a3f09)"}
The modified bitcode looks like the following, with _Z11myPathPointIiET_S0_S0_
being identified as a patch point and its definition removed:
; ModuleID = '/home/matinraayai/CLionProjects/bril/llvm-plugin/example/Example.cpp'
source_filename = "/home/matinraayai/CLionProjects/bril/llvm-plugin/example/Example.cpp"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
module asm ".globl _ZSt21ios_base_library_initv"
%"class.std::basic_ostream" = type { ptr, %"class.std::basic_ios" }
%"class.std::basic_ios" = type { %"class.std::ios_base", ptr, i8, i8, ptr, ptr, ptr, ptr }
%"class.std::ios_base" = type { ptr, i64, i64, i32, i32, i32, ptr, %"struct.std::ios_base::_Words", [8 x %"struct.std::ios_base::_Words"], i32, ptr, %"class.std::locale" }
%"struct.std::ios_base::_Words" = type { ptr, i64 }
%"class.std::locale" = type { ptr }
@_ZZ4mainE7myArray = external constant [5 x i32], align 16
@_ZSt4cout = external global %"class.std::basic_ostream", align 8
@.str = external unnamed_addr constant [18 x i8], align 1
@.str.1 = external unnamed_addr constant [12 x i8], section "llvm.metadata"
@.str.2 = external unnamed_addr constant [69 x i8], section "llvm.metadata"
; Function Attrs: mustprogress noinline norecurse optnone uwtable
define dso_local noundef i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca ptr, align 8
%4 = alloca ptr, align 8
%5 = alloca ptr, align 8
%6 = alloca ptr, align 8
store i32 0, ptr %1, align 4
store i32 0, ptr %2, align 4
store ptr @_ZZ4mainE7myArray, ptr %3, align 8
store ptr @_ZZ4mainE7myArray, ptr %4, align 8
store ptr getelementptr inbounds (i32, ptr @_ZZ4mainE7myArray, i64 5), ptr %5, align 8
br label %7
7: ; preds = %22, %0
%8 = load ptr, ptr %4, align 8
%9 = load ptr, ptr %5, align 8
%10 = icmp ne ptr %8, %9
br i1 %10, label %11, label %25
11: ; preds = %7
%12 = load ptr, ptr %4, align 8
store ptr %12, ptr %6, align 8
%13 = load ptr, ptr %6, align 8
%14 = load i32, ptr %13, align 4
%15 = load i32, ptr %2, align 4
%16 = load ptr, ptr %6, align 8
%17 = load i32, ptr %16, align 4
%18 = call noundef i32 @_Z11myPathPointIiET_S0_S0_(i32 noundef %15, i32 noundef %17)
%19 = mul nsw i32 %14, %18
%20 = load i32, ptr %2, align 4
%21 = add nsw i32 %20, %19
store i32 %21, ptr %2, align 4
br label %22
22: ; preds = %11
%23 = load ptr, ptr %4, align 8
%24 = getelementptr inbounds i32, ptr %23, i32 1
store ptr %24, ptr %4, align 8
br label %7
25: ; preds = %7
%26 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8) @_ZSt4cout, ptr noundef @.str)
%27 = load i32, ptr %2, align 4
%28 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8) %26, i32 noundef %27)
%29 = call noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8) %28, ptr noundef @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_)
ret i32 0
}
; Function Attrs: mustprogress noinline nounwind uwtable
declare dso_local anyregcc noundef i32 @_Z11myPathPointIiET_S0_S0_(i32 noundef, i32 noundef) #1
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEi(ptr noundef nonnull align 8 dereferenceable(8), i32 noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZNSolsEPFRSoS_E(ptr noundef nonnull align 8 dereferenceable(8), ptr noundef) #2
declare dso_local noundef nonnull align 8 dereferenceable(8) ptr @_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_(ptr noundef nonnull align 8 dereferenceable(8)) #2
attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "patch_point" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
!llvm.linker.options = !{}
!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"uwtable", i32 2}
!2 = !{i32 7, !"frame-pointer", i32 2}
!3 = !{!"AMD clang version 18.0.0git (https://github.com/RadeonOpenCompute/llvm-project roc-6.2.0 24292 26466ce804ac523b398608f17388eb6d605a3f09)"}
Inspecting the compiled executable shows the addition of the .llvmbc
section, indicating the LLVM bitcode has been successfully embedded inside the final executable and can be accessed by a runtime/loader:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000002002a8 000002a8
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000002002c4 000002c4
0000000000000020 0000000000000000 A 0 0 4
[ 3] .dynsym DYNSYM 00000000002002e8 000002e8
0000000000000120 0000000000000018 A 7 1 8
[ 4] .gnu.version VERSYM 0000000000200408 00000408
0000000000000018 0000000000000002 A 3 0 2
[ 5] .gnu.version_r VERNEED 0000000000200420 00000420
0000000000000080 0000000000000000 A 7 3 4
[ 6] .gnu.hash GNU_HASH 00000000002004a0 000004a0
0000000000000024 0000000000000000 A 3 0 8
[ 7] .dynstr STRTAB 00000000002004c4 000004c4
000000000000017f 0000000000000000 A 0 0 1
[ 8] .rela.dyn RELA 0000000000200648 00000648
0000000000000090 0000000000000018 A 3 0 8
[ 9] .rela.plt RELA 00000000002006d8 000006d8
00000000000000a8 0000000000000018 AI 3 23 8
[10] .rodata PROGBITS 0000000000200780 00000780
0000000000000036 0000000000000000 AMS 0 0 16
[11] .eh_frame_hdr PROGBITS 00000000002007b8 000007b8
000000000000003c 0000000000000000 A 0 0 4
[12] .eh_frame PROGBITS 00000000002007f8 000007f8
00000000000000c8 0000000000000000 A 0 0 8
[13] .text PROGBITS 00000000002018c0 000008c0
00000000000001e4 0000000000000000 AX 0 0 16
[14] .init PROGBITS 0000000000201aa4 00000aa4
000000000000001b 0000000000000000 AX 0 0 4
[15] .fini PROGBITS 0000000000201ac0 00000ac0
000000000000000d 0000000000000000 AX 0 0 4
[16] .plt PROGBITS 0000000000201ad0 00000ad0
0000000000000080 0000000000000000 AX 0 0 16
[17] .init_array INIT_ARRAY 0000000000202b50 00000b50
0000000000000008 0000000000000000 WA 0 0 8
[18] .fini_array FINI_ARRAY 0000000000202b58 00000b58
0000000000000008 0000000000000000 WA 0 0 8
[19] .dynamic DYNAMIC 0000000000202b60 00000b60
00000000000001b0 0000000000000010 WA 7 0 8
[20] .got PROGBITS 0000000000202d10 00000d10
0000000000000028 0000000000000000 WA 0 0 8
[21] .relro_padding NOBITS 0000000000202d38 00000d38
00000000000002c8 0000000000000000 WA 0 0 1
[22] .data PROGBITS 0000000000203d38 00000d38
0000000000000010 0000000000000000 WA 0 0 8
[23] .got.plt PROGBITS 0000000000203d48 00000d48
0000000000000050 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000203dc0 00000d98
0000000000000190 0000000000000000 WA 0 0 64
[25] .comment PROGBITS 0000000000000000 00000d98
00000000000000a1 0000000000000001 MS 0 0 1
[26] .llvmbc PROGBITS 0000000000000000 00000e39
00000000000010c4 0000000000000000 0 0 1
[27] .symtab SYMTAB 0000000000000000 00001f00
0000000000000390 0000000000000018 29 21 8
[28] .shstrtab STRTAB 0000000000000000 00002290
000000000000010c 0000000000000000 0 0 1
[29] .strtab STRTAB 0000000000000000 0000239c
0000000000000279 0000000000000000 0 0 1