Working with LLVM Passes is really exciting - injecting our own logic into the compilation process and handcrafting LLVM IR that we can force into arbitrary locations in our binary is nothing short of magic.
The goal in this project was to gain some familiarity with writing LLVM Passes and demonstrate how powerful they can be - all in the context of a simple pass.
Some thoughts:
- Writing the pass wasn’t always straightforward, but thankfully many of the problems and pitfalls were Google-friendly (which is often not the case for GCC plugins...). I found the documentation very helpful.
- I was blown away by the extent of the LLVM Pass API. I think it’s a mark of an excellent API that I found myself relying less and less on documentation and more on “API intuition” - i.e. the API was consistent enough in its structure that I was often able to guess the names and features I wanted (making heavy use of Intellisense) without turning to the documentation.
We often find ourselves held back by CPU/OS limitations - not every environment supports the full tracing, instrumentation, or binary modification features we might want.
The pass I implemented has many shortcomings - for example, lack of float/double
support and a
lack of multithreaded support - but it does go to show that LLVM Passes can bridge these
limitations and allow us to implement complex features (in this case, memory access tracing) by
using simple primitives (in this case, standard file-writing logic).
The code in this blog post is available here under the MIT License:
I had a lot of fun writing this blog post and even more fun implementing this project - if you have any comments or questions please reach out 🙂.