Conflicts with custom passes

Hi list,

I am making a simple custom pass that inserts print function calls for each instruction, so that compiled programs print out all llvm instructions on the dynamic path.
For example, (conceptually)

printf(“%s”, “x = x + 1”); // inserted code
x = y + 1; // original code

But it seems that there are some conflicts with existing optimization paths. When I compile a program with the custom pass and -O2, it causes segmentation faults:

1. <eof> parser at end of file
2. Per-module optimization passes
3. Running pass 'Interprocedural Sparse Conditional Constant Propagation' on module '/src/libfuzzer/FuzzerCrossOver.cpp'.
#0 0x0000000001573cfa llvm::sys::PrintStackTrace(llvm::raw_ostream&) (/usr/local/bin/clang-8+0x1573cfa)
#1 0x00000000015721dc llvm::sys::RunSignalHandlers() (/usr/local/bin/clang-8+0x15721dc)
#2 0x0000000001572347 SignalHandler(int) (/usr/local/bin/clang-8+0x1572347)
#3 0x00007f8d810e7390 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x11390)
#4 0x0000000001497e69 (anonymous namespace)::SCCPSolver::getValueState(llvm::Value*) (.constprop.510) (/usr/local/bin/clang-8+0x1497e69)

I have two questions:
- Is there any guide line to write a custom pass to avoid such a conflict?
- Is there any existing framework to print out dynamic path in LLVM-IR format?

I would appreciate if some of you can give comments.

Thanks,
Kihong

Hi Kihong,

- Is there any guide line to write a custom pass to avoid such a conflict?

The most likely cause for the error is that the IR you're generating
is somehow invalid, and causing issues later.

It also looks like you might have an LLVM built without assertions, so
if that's the case you should fix that (e.g. CMake a debug build with
-DCMAKE_BUILD_TYPE=Debug). That might cause an error immediately when
you create the IR, or at should at least give more information about
why SCCP is failing.

- Is there any existing framework to print out dynamic path in LLVM-IR format?

Not as far as I know.

Cheers.

Tim.

Hi Tim,

Thanks for your suggestion!

I built LLVM with Debug and tried this with my custom pass.
But it has the following error

error: unable to load plugin '../../trace-extractor/build/TracePass.so': '../../trace-extractor/build/TracePass.so: undefined symbol: _ZTIN4llvm10ModulePassE’

Originally I used a builtin Clang in the system and it does not produce this issue.
When I search for this issue on Google, it seems to be relevant to “RTTI”. But as I know LLVM is built by default -fno-rtti. I also explicitly turned off it. Here is my cmake command:

$ cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_RTTI=OFF -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm

Any thought?

Best,
- Kihong

When I search for this issue on Google, it seems to be relevant to “RTTI”.

Yep, that's a typeinfo symbol.

But as I know LLVM is built by default -fno-rtti. I also explicitly turned off it. Here is my cmake command:

$ cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_RTTI=OFF -DLLVM_ENABLE_PROJECTS=clang -G "Unix Makefiles" ../llvm

Any thought?

My best guess is that your pass was built *with* RTTI (i.e. no
-fno-rtti) but LLVM without. A quick fix would be to switch to
-DLLVM_ENABLE_RTTI=ON, but longer term you may want to look into your
pass's build system.

Cheers.

tim.

You are right. Thanks!
I fixed that one as well as some other issues.
I built LLVM-8 with Debug + no-rtti. But it now has the following error:

Stack dump:
0. Program arguments: clang-8 -cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-free -main-file-name time-1.7.c -mrelocation-model static -mthread-model posix -mdisable-fp-elim -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -coverage-notes-file /home/khheo/project/parametric-symexe/test/toy1/time-1.7.gcno -resource-dir /home/khheo/project/llvm-8/build/lib/clang/8.0.1 -internal-isystem /usr/local/include -internal-isystem /home/khheo/project/llvm-8/build/lib/clang/8.0.1/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O0 -fdebug-compilation-dir /home/khheo/project/parametric-symexe/test/toy1 -ferror-limit 19 -fmessage-length 364 -fobjc-runtime=gcc -fdiagnostics-show-option -fcolor-diagnostics -load ../../trace-extractor/build/TracePass.so -o time-1.7.ll -x c /home/khheo/project/all/time-1.7.c -faddrsig
1. <eof> parser at end of file
2. Per-module optimization passes
3. Running pass 'Unnamed pass: implement Pass::getPassName()' on module '/home/khheo/project/all/time-1.7.c'.

What does "Running pass 'Unnamed pass: implement Pass::getPassName()' on module” mean?

- Kihong

It is just debugging on crashes, passes can override this method: https://github.com/llvm/llvm-project/blob/master/llvm/include/llvm/Pass.h#L96 to provide a more readable name.
In practice if passes are registered it isn’t needed I think.
In any case this is not the cause of the crash, you can try running the clang -cc1 command in a debugger.

I see. Thanks to your help, I could proceed a lot.

I am wondering how out-of-tree LLVM pass is robust. I am building my custom pass on one machine and want to run this for other machines (e.g., Docker images).
I notice that LLVM IR’s OPCode is different from versions. Also it seems that type matching (e.g., if (llvm::CallInst *CI = llvm::dyn_castllvm::CallInst(&I))) does not work well another machine. But I am not sure whether my guess is correct or not.
Do you have any idea or suggestion?

  • Kihong

You're right, LLVM's C++ API has no stability guarantees and will
almost certainly change between major versions. Your pass will have to
be compiled against the same version of LLVM it will use to run, and
with compatible build settings (e.g. RTTI is an issue, and there is an
LLVM_ENABLE_ABI_BREAKING_CHECKS Cmake variable).

Cheers.

Tim.