Homework 5

Author

matinraayai

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 b) {
  return a + b;
}



int main() {
  static constexpr int myArray[]{1, 4, 5, 6, 10};
  int mySum{0};
  for (const auto &el : myArray) {
    mySum += el * myPathPoint(mySum, el);
  }
  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
Back to top