From 6d94381e20d15aaf56ae8d92fa956479dce6bb18 Mon Sep 17 00:00:00 2001 From: nashi5566 Date: Wed, 1 Jun 2022 14:33:14 +0800 Subject: [PATCH] FIX: README.md ADD: README-zh.md Mention the tutorial is written in legacy pm method, and add an translation of tutorial in Traditional Chinese. Fix the committer. --- README-zh.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 92 +++++++++++++++++++++---------------------- 2 files changed, 154 insertions(+), 46 deletions(-) create mode 100644 README-zh.md diff --git a/README-zh.md b/README-zh.md new file mode 100644 index 00000000..d146a4e5 --- /dev/null +++ b/README-zh.md @@ -0,0 +1,108 @@ +# llvm-sanitizer 教學及文件 +這是一個關於如何建立一個 LLVM Sanitizer 的教學。 + +## Background +LLVM sanitizer 是一個相當強大的插樁以及分析程式的工具。在這個專案中,你可以看到一個範例的 sanitizer ,並跟隨此文件一步一步將 sanitizer 整合進 toolchain 中。此外,這個樣本也可以作為基礎,在此之上開發成更複雜的工具。若想要更了解 sanitizer 是什麼,可以參考相關的文章: + +## 快速開始:如何建立 toolchain 和執行 sanitizer +在這個專案中有三包程式,分別是 LLVM 、 Clang 、以及 compile-rt 。以下的腳本會下載 LLVM-8 、以這些程式為基礎建立,並將新的程式放到對應的正確位置。 + +這裡摘錄一段 .patch 的內容: +``` +#Clone the repo +git clone https://github.com/trailofbits/llvm-sanitizer-tutorial.git && cd llvm-sanitizer-tutorial/llvm +#Make the build dir +mkdir build && cd build +#configure and build, there are a lot of configuration options for LLVM +cmake -DLLVM_TARGETS_TO_BUILD="X86" .. && make +cd bin && ./clang -fsanitize=testsan -g -o malloc_target ../../../target_programs/malloc_target.c +./malloc_target +``` + +接著當執行程式後,你會看到藉由 LLVM pass 以及其他由 runtime component 所產生的輸出內容。這個文件主要基於上述所提到的連結文章內容而生,但稍有不同的地方是這個專案主要會將建立的過程中,繁瑣的步驟一一列出。需要注意的是,這份文件只適用於開發 Linux 平台上的 sanitizer 。 + +## 建立一個 out of source pass +為什麼需要先建立 out of source 呢?從建立一個 out of source 的插樁 pass 開始是建立 sanitizer 時相當好的起頭。如此一來能更方便地在過程中測試、偵錯,並確認功能是否有正常運作。在建立的過程中,你可以使用 `opt` 這個工具去執行你所寫的 pass ,並使用 `llvm-dis` 去閱讀實際產生出來的 IR。 + +``` +./clang -c -emit-llvm ../../../target_programs/malloc_target.c -o malloc_target.bc +./opt -load ../lib/LLVMTestPass.so -testfunc < malloc_target.bc > malloc_instrumented.bc +./llvm-dis < malloc_instrumented.bc | less +``` + +首先先寫你的 pass ,可以參考放在 llvm/lib/Transform/TestPass/TestPass.cpp 這個程式碼。 LLVM module 是編譯步驟中最大的單元,他基本上代表了整個檔案,而 function pass 和 basic block pass 則在相對應的層級運作。 Module pass 只會印出 function name , function pass 檢測 function 入口,而 module pass 則會在 malloc 之後插入 function call 。這些 function symbol 會在我們的 runtime component 中被定義。而最檔案下方有幾行程式則是為了將這些 pass 註冊於 `opt` 使用。接著,這些 function 就會被 pass object 所產生的 function 所取代。之後當你使用 Clang 編譯標注使用 sanitizer 時, LLVM 的 pass manager 就會呼叫這些 function 。而要建立這個 module 的話,你需要在 llvm/lib/Transform 底下新增一個資料夾,並使用 `add_llvm_library` 這個 macro 將其加入 CMakeList.txt 中。你可以直接複製 TeatPass.cpp 的內容,或者參考官方教學的 HelloPass 。 + +## 建立一個 runtime component +Sanitizer 所使用的 runtimes 被放在 llvm/projects/compile-rt/lib 中。 Sanitizer 的 runtime component 最主要的功能就是提供 Transform pass 即將呼叫的 function 。在 testsan 這個資料夾中,是一個基本的 runtime 裡頭定義了一些 function 並展示了如何使用 interceptor interface 。 `INTERCEPTOR` 這個 macro 實際上的機制因 OS 而異,在 Linux 的情況下,則是使用 dlsym 去解析實際的 function address 。這裡有兩點需要注意: + +* `SANITIZER_INTERFACE` 會告訴 compiler-rt 需要 export 該 function symbol 因為接下來此 function 會被插樁程式所呼叫。 +* 而初始函式因為前置處理的緣故,他會在被 load 時立即執行。這個步驟可以藉由將 function 放在 `.pre_init` array 中或者藉由 `constructor` 這個 attribute 來達成。 + +而這裡,還有一些步驟必須完成。先看 `teatsan` 中的 cmake file 作為如何使用 macro 的參考。如果你的開發環境正是 Linux ,或許你可以直接使用這個檔案,並將 testsan 替換成你的 sanitizer 的名稱即可。如果對此有所疑問的話,這些 macro 都被定義在 `compiler-rt/cmake` 中。 + +* 在 `llvm/projects/compiler-rt/lib` 中建立一個放置你的程式碼的資料夾 +* 而在 cmake file 中你必須做以下這些步驟: + * 將 componet 加入 compiler-rt 中 + * 使用 `add_compiler_rt_runtime` 這個 macro 來加入你的 runtime + * 如果你也使用了 RTCommon library 和 interceptor library 這裡也要記得加入 + * 使用 `add_sanitizer_rt_symbols` 來產生 interface symbol 。 + +下一步是編輯 `compiler-rt/cmake/config-ix.make` 的內容。這部分的流程是在 compiler-rt 編譯過程之外的步驟,而這個過程可以為 sanitizer 設定變數作為判斷在目前的 OS 環境或架構下,這個 component 是否可以被建立。這個部分觸及範圍滿廣的,你可以直接以 `TESTSAN` 、 `testsan` 等關鍵字搜尋找到正確的位置。 + * 將你的 sanitizer 名稱加入 sanitizer 的名單。 camke file 會迭代尋找在名單中要被建立的 sanitizer 。 + * 定義你的 sanitizer 支援的架構(例如: x86, x86_64, aarch64 等等) + * 檢查OS 是否支援你的架構並將 build flag 設為 true 。 + +在這個階段,你就能在編譯 toolcahin 時同時建立你的 runtime pass 了。 + + +## 定義 sanitizer 、更改 driver +這些是你要定義 sanitizer 和設定 compiler driver 以備之後整合所需的步驟。 +* 在 `llvm/tools/clang/include/Basic/Sanitizer.def` 中像其它的 sanitizer 一樣,使用 macro 加入你的 sanitizer +* 在 `llvm/tools/clang/lib/Driver/SanitizerArgs.h` 中加入一個 quick helper function 來檢查是否需要使用 runtime 。例如請參考 `needsDfSanRT()` 這個函式。不過這步驟並不是必要的,因為它內容非常簡單,可以需要時 inline 在任何地方。但如果是要開發更加複雜的 sanitizer ,你可以將更複雜的邏輯設計在 SanitizerArgs.cpp 中 +* `clang/lib/CodeGen/BackendUtil.cpp` 中檢查你的 sanitizer 是否正在運行、或者設定 pass 會在之後運行。你可以參考其他的 sanitizer 怎麼寫的。 + +## 將 pass 整合進來 +這裡只有一些步驟,況且因為 pass 在先前的步驟已經寫好了,其實大部分的工作也都完成了。這裡只有一件事就是將它加入內部的編譯流程,以幫助 driver 能夠順利找到它。 +* 把你先前寫好的 pass 放到 `llvm/lib/Transforms/Instrumentation` 底下 +* 將原先底下用來註冊 opt 的程式碼刪除,並改成產生你的 pass 的 function 。 +* 編輯 Camke 來引入你的 pass 。現在你有內部的插樁 pass 了,是時候來將他加入 manager 中了。 +* 將你寫在先前提到的 `llvm/lib/Transforms/Instrumentation` 的 function prototype 定義在 `llvm/include/llvm/Transform/Instrumentation.h` 中。 +* 在 `clang/lib/CodeGen/BackendUtil.cpp` 中加入新的 function 來將你的 pass 加進 Pass Manager 中。你可以參考 addTestSanitizer 。它是一個參考的樣板。 +* 接著在同一個檔案中,加入 CreatePass 這個 function ,他會檢查你的 sanitizer 是否在運行和它是否加入了你的 pass 。 + + +## 整合 runtime component +* 在 `clang/lib/Driver/CommonArgs.cpp` 中有個 driver 叫做 collectSanitizerRuntimes 。它會決定哪個 runtime 該被使用。如果你的 sanitizer 需要使用,加入檢查並確認它是否有加入 staic runtime 的名單。可以參考其他的實作怎麼寫的。 +* 這步驟取決於你的作業系統。在 `lib/Driver/Toolchains/Linux.cpp` 中找到 `getSupportedSanitizer` 這個 function ,如果有支援你的開發環境架構,那就將你的 sanitizer 加入名單。 + +## 一些我在這個過程中學到的東西 +IR pass 或許跟你所開發的 OS 環境無關,但其他會使用到的 toolchain 元件則不一定。當你要整合你的 sanitizer 到其他不同的 OS 時更需要注意這些問題。幸好 compiler-rt 的設計,將一些複雜的過程包裝了起來,不需要自己親自處理。多多使用這些 sanitizer interface ,可以讓你在開發過程中少頭痛些。 + +如果在 cmake 編譯過程中有些問題,我會建議你再次檢查是否有拼字錯誤,譬如說設定 architecture 的部分你寫成 x86 ,事實上 X86 才是正確的寫法。 + +總之這次的實習是一個相當好的經驗,希望我將我的所學寫入這個 repo 之後,諸位往後開發時就不需要再從頭梳理整個 toolchain 的架構。下面是一些我認為相當有用的文章,這些在部落格中的教學也有引用到,我覺得內容相當不錯。 + +## 一些雜談 +其實更深入的 sanitizer 的開發流程我並沒有完全將其收錄在此,我想最好的學習方式是去看其他的 sanitizer 的開發者的 PR 並去觀察他們所做的更改內容。 + +## 有幫助的參考文章 + +https://blog.trailofbits.com/2019/06/25/creating-an-llvm-sanitizer-from-hopes-and-dreams/ + +https://eli.thegreenplace.net/ + +https://www.cs.cornell.edu/~asampson/blog/llvm.html + +https://llvm.org/docs/LangRef.html + +https://llvm.org/devmtg/2018-04/ + +https://reviews.llvm.org/D32199 + +# Maintainer +Carson Harmon carson.harmon@trailofbits.com (@ThatsNotVeryCashMoneyOfYou) + +### 譯者後話 +在這個 repo 事隔多年我才看到這個教學,剛好與我學業論文的內容息息相關,在與專案開發者去信詢問過後,他很高興我能夠在這之後為這份教學新增更多東西。在 LLVM 8 之後, Pass Manager 會使用 New PM 來進行分析,而 New PM 的 pass 和目前這個教學的內容相去甚遠, macro 也跟現在差很多,但整體的運作流程概念是類似的,因此也將原先的教學文章進行了翻譯。有些詞彙我不太清楚中文的翻譯是什麼(ex. macro magic) 因此有些部分可能不是直翻、是照我的理解來解釋,如果有錯誤的部分也希望有人能夠指正我,謝謝。 + +By [@nashi5566](https://github.com/nashi5566) diff --git a/README.md b/README.md index 615128ea..6830f2b9 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# llvm-sanitizer-tutorial and documentation +# llvm-sanitizer-tutorial and documentation (With Legacy PM) -This is a tutorial on how to build an LLVM sanitizer. +This is a tutorial on how to build an LLVM sanitizer. -# Background +# Background An LLVM sanitizer is a powerful tool used to instrument and analyze programs. This github repo holds an example sanitizer and step by step documentation to integrate a sanitizer into the toolchain. This sanitizer can serve as a template towards building more complex tools. For more information on what sanitizers are, see the related blogpost: https://blog.trailofbits.com/2019/06/25/creating-an-llvm-sanitizer-from-hopes-and-dreams/ -# Quickstart: Building the toolchain & running a sanitizer +# Quickstart: Building the toolchain & running a sanitizer -There are three patch files in this repo, one for LLVM, clang, and compiler-rt. The install script will download version 8, apply the patches, and put the new files in their appropriate locations. +There are three patch files in this repo, one for LLVM, clang, and compiler-rt. The install script will download version 8, apply the patches, and put the new files in their appropriate locations. -This will be stored as a .patch shortly +This will be stored as a .patch shortly ``` #Clone the repo -git clone https://github.com/trailofbits/llvm-sanitizer-tutorial.git && cd llvm-sanitizer-tutorial/llvm -#Make the build dir -mkdir build && cd build +git clone https://github.com/trailofbits/llvm-sanitizer-tutorial.git && cd llvm-sanitizer-tutorial/llvm +#Make the build dir +mkdir build && cd build #configure and build, there are a lot of configuration options for LLVM cmake -DLLVM_TARGETS_TO_BUILD="X86" .. && make cd bin && ./clang -fsanitize=testsan -g -o malloc_target ../../../target_programs/malloc_target.c @@ -22,69 +22,69 @@ cd bin && ./clang -fsanitize=testsan -g -o malloc_target ../../../target_program ``` You should see output from the LLVM pass and additional output from the runtime component when the program is executed. Most of this readme will be from the blogpost above, but in this repo, I'm going to list all the tedious technical details that didn't make it past editing. Note that this post only covers on how to build a sanitizer for Linux. -# Building an out of source pass -Why build out of source first? Building your instrumentation pass out of source is a good first step when building your sanitizer. This allows you to debug your pass and determine if it's functioning correctly. When building the LLVM tool chain, you can use the `opt` tool to run your pass on bitcode and use the `llvm-dis` tool to view the actual IR. +# Building an out of source pass +Why build out of source first? Building your instrumentation pass out of source is a good first step when building your sanitizer. This allows you to debug your pass and determine if it's functioning correctly. When building the LLVM tool chain, you can use the `opt` tool to run your pass on bitcode and use the `llvm-dis` tool to view the actual IR. ``` ./clang -c -emit-llvm ../../../target_programs/malloc_target.c -o malloc_target.bc -./opt -load ../lib/LLVMTestPass.so -testfunc < malloc_target.bc > malloc_instrumented.bc +./opt -load ../lib/LLVMTestPass.so -testfunc < malloc_target.bc > malloc_instrumented.bc ./llvm-dis < malloc_instrumented.bc | less ``` -The first thing is to create your pass, check out `llvm/lib/Transform/TestPass/TestPass.cpp` for the code I'm going to be referencing. The LLVM module is the largest unit of compilation, it essentially represents the file. The function and basic block passes operate at those respective levels. The module pass just prints out the function names, the function pass instruments function entries, and the basic block pass inserts function calls after malloc. These function symbols will be defined inside of our runtime component. At the bottom of the file there is a few lines of code to register the pass with `opt`. Later on these will be removed and replaced with functions that create the pass object. These functions will be called by the LLVM pass manager when your specify your sanitizer to clang. To build this module create a new directory in `llvm/lib/Transforms/` and use the `add_llvm_library` macro. You can copy the TestPass or the Hello cmake files for reference. +The first thing is to create your pass, check out `llvm/lib/Transform/TestPass/TestPass.cpp` for the code I'm going to be referencing. The LLVM module is the largest unit of compilation, it essentially represents the file. The function and basic block passes operate at those respective levels. The module pass just prints out the function names, the function pass instruments function entries, and the basic block pass inserts function calls after malloc. These function symbols will be defined inside of our runtime component. At the bottom of the file there is a few lines of code to register the pass with `opt`. Later on these will be removed and replaced with functions that create the pass object. These functions will be called by the LLVM pass manager when your specify your sanitizer to clang. To build this module create a new directory in `llvm/lib/Transforms/` and use the `add_llvm_library` macro. You can copy the TestPass or the Hello cmake files for reference. -# Building a runtime component -Sanitizer runtimes are located in `llvm/projects/compiler-rt/lib/`. The sanitizer runtime component supplies runtime functions that the transformation pass will call into. In the testsan directory, there is an example runtime that defines some functions and shows how to use the interceptor interface. The actual mechanics of the `INTERCEPTOR` macro differs based on the OS, on Linux it replaces the symbol address and uses dlsym to resolve the real function address. There are a two other things to take note of in this example. -* The macro `SANITIZER_INTERFACE` tells compiler-rt that it needs to export that function symbol because it might be called by the instrumented program. -* The init function contains macro magic, it's designed to run immediately upon being loaded. This is either done by placing the function in the `.pre_init` array or with the `constructor` attribute. +# Building a runtime component +Sanitizer runtimes are located in `llvm/projects/compiler-rt/lib/`. The sanitizer runtime component supplies runtime functions that the transformation pass will call into. In the testsan directory, there is an example runtime that defines some functions and shows how to use the interceptor interface. The actual mechanics of the `INTERCEPTOR` macro differs based on the OS, on Linux it replaces the symbol address and uses dlsym to resolve the real function address. There are a two other things to take note of in this example. +* The macro `SANITIZER_INTERFACE` tells compiler-rt that it needs to export that function symbol because it might be called by the instrumented program. +* The init function contains macro magic, it's designed to run immediately upon being loaded. This is either done by placing the function in the `.pre_init` array or with the `constructor` attribute. -There are a few steps required to build the runtime component. Look at the `testsan` cmake file for an example reference on how to use these cmake macros. If you are building on linux you can probably just copy it and replace testsan with the name of your sanitizer. If there is confusion the macros are defined in `compiler-rt/cmake`. +There are a few steps required to build the runtime component. Look at the `testsan` cmake file for an example reference on how to use these cmake macros. If you are building on linux you can probably just copy it and replace testsan with the name of your sanitizer. If there is confusion the macros are defined in `compiler-rt/cmake`. * Create a directory for your source in `llvm/projects/compiler-rt/lib/` -* In the cmake file you need to - * Add the component to compiler-rt +* In the cmake file you need to + * Add the component to compiler-rt * Use the add_compiler_rt_runtime macro to add your runtime * Make sure to include the RTCommon libs and interceptor lib if you use them. - * Use add_sanitizer_rt_symbols to generate the interface symbols - -The next step is modifying the `compiler-rt/cmake/config-ix.make`. This is apart of the compiler-rt build system and sets variables for your sanitizer to decide if your component could be built by checking to see what operating system and architectures you set. The file is actually rather large, feel free to search for TESTSAN and testsan to find the right places. + * Use add_sanitizer_rt_symbols to generate the interface symbols + +The next step is modifying the `compiler-rt/cmake/config-ix.make`. This is apart of the compiler-rt build system and sets variables for your sanitizer to decide if your component could be built by checking to see what operating system and architectures you set. The file is actually rather large, feel free to search for TESTSAN and testsan to find the right places. * add your sanitizer name to the list of all sanitizers. The cmake file in the lib directory iterates over the sanitizers in this list to decide which ones to try and build. -* define your sanitizers supported architectures (X86, X86_64) -* check if the operating system is supported for your architecture and set build flag to true +* define your sanitizers supported architectures (X86, X86_64) +* check if the operating system is supported for your architecture and set build flag to true -At this point you should be able to build your runtime pass by just attempting to build the toolchain. +At this point you should be able to build your runtime pass by just attempting to build the toolchain. -# Defining the sanitizer/Modifying the driver -These steps are what you need to do to define the sanitizer and set up the compiler driver to be ready for integration. -* In `llvm/tools/clang/include/Basic/Sanitizers.def` add your sanitizers using the macro like all the others. +# Defining the sanitizer/Modifying the driver +These steps are what you need to do to define the sanitizer and set up the compiler driver to be ready for integration. +* In `llvm/tools/clang/include/Basic/Sanitizers.def` add your sanitizers using the macro like all the others. * In `llvm/tools/clang/lib/Driver/SanitizersArgs.h` add a quick helper function to check if the runtime is required. For an example check the `needsDfSanRT()` function. This step is not actually needed because you can inline it anywhere since it's simple but for more complex sanitizers you can create complicated logic in `SanitizersArgs.cpp` -* In `clang/lib/CodeGen/BackendUtil.cpp` check if your sanitizer is being run, and if it is set the pass to run last. You can look at any of the other sanitizers for reference, it's just boilerplate. +* In `clang/lib/CodeGen/BackendUtil.cpp` check if your sanitizer is being run, and if it is set the pass to run last. You can look at any of the other sanitizers for reference, it's just boilerplate. -# Integrating a pass -This is just a few steps, the work is mostly done since the pass is already written. The only thing now is to add it to the internal build system and help the driver find it. +# Integrating a pass +This is just a few steps, the work is mostly done since the pass is already written. The only thing now is to add it to the internal build system and help the driver find it. * Copy your out of source pass code into `llvm/lib/Transform/Instrumentation` * Remove the three lines that register with opt and replace them with functions that create your passes. Check the TestPass.cpp file for a reference - * Edit the CMake file to include your pass -Now that you have an internal instrumentation pass, time to add it to the manager -* Define the prototype of the function you just made in `llvm/lib/Transform/Instrumentation` in `llvm/include/llvm/Transforms/Instrumentation.h`. This way the driver can see it. -* Create a new function in `clang/lib/CodeGen/BackendUtil.cpp` that adds your pass to the manager. You can look for the addTestSanitizer function for a reference, it's all boilerplate. + * Edit the CMake file to include your pass +Now that you have an internal instrumentation pass, time to add it to the manager +* Define the prototype of the function you just made in `llvm/lib/Transform/Instrumentation` in `llvm/include/llvm/Transforms/Instrumentation.h`. This way the driver can see it. +* Create a new function in `clang/lib/CodeGen/BackendUtil.cpp` that adds your pass to the manager. You can look for the addTestSanitizer function for a reference, it's all boilerplate. * Later in the same file there is a function called `CreatePasses`, in it check if your sanitizer is being run and if it is add your pass -# Integrating a runtime component -* In `clang/lib/Driver/CommonArgs.cpp` the driver calls `collectSaniitzerRuntimes` to decide which runtimes should be used. Add a check like the others to see if your sanitizer should be used, and if it is add it to the list of static runtimes. -* This part is dependent on your operating system. in `lib/Driver/Toolchains/Linux.cpp` find the `getSupportedSanitizers` function and add your sanitizer to this list of the architectures are correct. +# Integrating a runtime component +* In `clang/lib/Driver/CommonArgs.cpp` the driver calls `collectSaniitzerRuntimes` to decide which runtimes should be used. Add a check like the others to see if your sanitizer should be used, and if it is add it to the list of static runtimes. +* This part is dependent on your operating system. in `lib/Driver/Toolchains/Linux.cpp` find the `getSupportedSanitizers` function and add your sanitizer to this list of the architectures are correct. -# Some other things I learned -Your IR passes will be operating system agnostic but other parts of the toolchain are not. When integrating your sanitizer you will have to perform different build operations for OSX/Windows etc. Fortunately compiler-rt hides a lot of the nastiness from you. Try to use the sanitizer interface, it could save you from some headaches. +# Some other things I learned +Your IR passes will be operating system agnostic but other parts of the toolchain are not. When integrating your sanitizer you will have to perform different build operations for OSX/Windows etc. Fortunately compiler-rt hides a lot of the nastiness from you. Try to use the sanitizer interface, it could save you from some headaches. -If you are having issues with some of the cmake build systems I would double check to see you didn't make any typos. For example if you put the architecture as ${x86}, it needs to be ${X86} etc etc. +If you are having issues with some of the cmake build systems I would double check to see you didn't make any typos. For example if you put the architecture as ${x86}, it needs to be ${X86} etc etc. Overall this internship was a great experience, and I hope that this repo documents what I learned so the rest of you can build sanitizers without needing to comb through the toolchain. Below are some of the helpful resources I linked in the blogpost, they are all really great. -# Other notes -There is actually more that goes into sanitizer development that I didn't cover here. I think the best way to learn is to look at sanitizer pull requests and see what they modify and change. +# Other notes +There is actually more that goes into sanitizer development that I didn't cover here. I think the best way to learn is to look at sanitizer pull requests and see what they modify and change. -# Helpful resources +# Helpful resources https://blog.trailofbits.com/2019/06/25/creating-an-llvm-sanitizer-from-hopes-and-dreams/ https://eli.thegreenplace.net/ @@ -97,5 +97,5 @@ https://llvm.org/devmtg/2018-04/ https://reviews.llvm.org/D32199 -# Maintainer +# Maintainer Carson Harmon carson.harmon@trailofbits.com (@ThatsNotVeryCashMoneyOfYou)