chopper
chopper copied to clipboard
"Bad signature" LLVM assert raised when replacing calls with wrappers
I have had this LLVM assert raised on the bc
benchmark on several functions, on several functions, such as prog_char and some floating point functions. I get different errors, sometimes the arguments count is bad, sometimes it's the argument type.
Possibly this is related to structs (my best guess).
Minimal steps to reproduce it on a smaller program:
- Write
main.c
:
int main(int argc, char *argv[]) {
_fp_out_narrow(0, 0, 0, 0);
return 0;
}
(I was not able to isolate the part of __fp_out_narrow
that causes this, so this minimal example is not very well self-contained)
- Run
clang
(+opt -mem2reg file.bc -o file.bc
)
$ clang -c -g -emit-llvm main.c -o main.bc -I~/code/chopper/include/
main.c:3:5: warning: implicit declaration of function '_fp_out_narrow' is invalid in C99 [-Wimplicit-function-declaration]
_fp_out_narrow(0, 0, 0, 0);
^
1 warning generated.
- Run legacy Chopper, skip
_fp_out_narrow1
:
$ kleegacy -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -output-module -skip-functions=_fp_out_narrow1 main.bc
KLEE: NOTE: Using klee-uclibc : /home/ubuntu/code/chopper/legacy_build/Release+Asserts/lib/klee-uclibc.bca
kleegacy: /home/ubuntu/code/llvm-project/llvm/lib/IR/Instructions.cpp:281: void llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, const llvm::Twine&): Assertion `(i >= FTy->getNumParams() || FTy->getParamType(i) == Args[i]->getType()) && "Calling a function with a bad signature!"' failed.
0 kleegacy 0x0000555731055992 llvm::sys::PrintStackTrace(_IO_FILE*) + 50
1 kleegacy 0x000055573105528c
2 libpthread.so.0 0x00007f9e9c453890
3 libc.so.6 0x00007f9e9b321e97 gsignal + 199
4 libc.so.6 0x00007f9e9b323801 abort + 321
5 libc.so.6 0x00007f9e9b31339a
6 libc.so.6 0x00007f9e9b313412
7 kleegacy 0x0000555730fc5a3f llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, llvm::Twine const&) + 335
8 kleegacy 0x000055573059c999
9 kleegacy 0x000055573059c688
10 kleegacy 0x000055573068457a
11 kleegacy 0x0000555730683afa klee::ReturnToVoidFunctionPass::replaceCall(llvm::CallInst*, llvm::Function*, llvm::Function*) + 812
12 kleegacy 0x0000555730683712 klee::ReturnToVoidFunctionPass::replaceCalls(llvm::Function*, llvm::Function*, std::vector<unsigned int, std::allocator<unsigned int> > const&) + 480
13 kleegacy 0x0000555730682fcd klee::ReturnToVoidFunctionPass::runOnFunction(llvm::Function&, llvm::Module&) + 469
14 kleegacy 0x0000555730683d18 klee::ReturnToVoidFunctionPass::runOnModule(llvm::Module&) + 138
15 kleegacy 0x0000555730fdcef1 llvm::legacy::PassManagerImpl::run(llvm::Module&) + 849
16 kleegacy 0x000055573066b3c9 klee::KModule::prepare(klee::Interpreter::ModuleOptions const&, std::vector<klee::Interpreter::SkippedFunctionOption, std::allocator<klee::Interpreter::SkippedFunctionOption> > const&, klee::InterpreterHandler*, ReachabilityAnalysis*, Inliner*, AAPass*, ModRefAnalysis*, Cloner*, SliceGenerator*) + 1659
17 kleegacy 0x00005557305b3640 klee::Executor::setModule(llvm::Module*, klee::Interpreter::ModuleOptions const&) + 1656
18 kleegacy 0x0000555730596b35 main + 4654
19 libc.so.6 0x00007f9e9b304b97 __libc_start_main + 231
20 kleegacy 0x000055573059071a _start + 42
Aborted (core dumped)
3bis. You can also directly reproduce it on bc
by skipping prog_char
:
$ kleegacy -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -output-module -skip-functions=prog_char bc.bc
KLEE: NOTE: Using klee-uclibc : /home/ubuntu/code/chopper/legacy_build/Release+Asserts/lib/klee-uclibc.bca
KLEE: output directory is "/home/ubuntu/code/chopper-experiments/bc/klee-out-24"
Using STP solver backend
kleegacy: /home/ubuntu/code/llvm-project/llvm/lib/IR/Instructions.cpp:276: void llvm::CallInst::init(llvm::Value*, llvm::ArrayRef<llvm::Value*>, const llvm::Twine&): Assertion `(Args.size() == FTy->getNumParams() || (FTy->isVarArg() && Args.size() > FTy->getNumParams())) && "Calling a function with bad signature!"' failed.
Dodging the issue
I was not able to fix the issue, and instead I dodged it by aborting the replaceCalls
. The ugly workaround is https://github.com/jordr/chopper/commit/6feb19dd89fded83ad39cd14423353c9a2858c02 and yields:
[4941c8] KLEE: WARNING: Wrapper has bad signature: 'prog_char'! LLVM refuses to create call. Args.size()=3, FTy->getNumParams()=1, FTY->isVarArg()=0
[4941c8] KLEE: WARNING: Wrapper has bad signature: 'input_char'! LLVM refuses to create call. Args.size()=3, FTy->getNumParams()=1, FTY->isVarArg()=0
[eab1d] KLEE: WARNING: Wrapper has bad signature: '_fp_out_narrow'! bad type of Args[2].type=4 =/= wrapper.type=10 LLVM refuses to create call.
Some debugging
Sometimes the mismatch happens on the type of the arguments, sometimes on the count of arguments.
prog_char
Using a modified Chopper, I get:
$ klee -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -skip-functions-not=__errno_location,__h_errno_location,__user_main bc.bc
[988dcb] KLEE: createWrapperFunction(f=prog_char)
[a17b2a] KLEE: WARNING: [createWrapperFunction] Set the arguments
[596ea7] KLEE: WARNING: !!
[6b00f9] KLEE: WARNING: [createWrapperFunction] Create BB entry
[e1f7eb] KLEE: WARNING: replaceCall to call, arg[0]= (2 args) by f=prog_char
[69a30f] KLEE: WARNING: - originalCall.argoperand[0] =
; Function Attrs: nounwind uwtable
define zeroext i8 @prog_char() #0 {
entry:
tail call void @llvm.dbg.value(metadata !3258, i64 0, metadata !3259), !dbg !3261
%0 = load i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 1), align 4, !dbg !3262, !tbaa !3263
%inc.i = add nsw i32 %0, 1, !dbg !3262
store i32 %inc.i, i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 1), align 4, !dbg !3262, !tbaa !3263
%idxprom.i = sext i32 %0 to i64, !dbg !3262
%1 = load i32* getelementptr inbounds (%struct.program_counter* @pc, i64 0, i32 0), align 4, !dbg !3262, !tbaa !3268
%idxprom1.i = sext i32 %1 to i64, !dbg !3262
%2 = load %struct.bc_function** @functions, align 8, !dbg !3262, !tbaa !3269
%f_body.i = getelementptr inbounds %struct.bc_function* %2, i64 %idxprom1.i, i32 1, !dbg !3262
%3 = load i8** %f_body.i, align 8, !dbg !3262, !tbaa !3271
%arrayidx2.i = getelementptr inbounds i8* %3, i64 %idxprom.i, !dbg !3262
%4 = load i8* %arrayidx2.i, align 1, !dbg !3262, !tbaa !3273
ret i8 %4, !dbg !3260
}
(TYPE=i8 ()*)(TID=14)
[69a30f] KLEE: WARNING: - originalCall.argoperand[1] = %const_base.0 = load i32* %const_base.0.in, align 4, !dbg !3362(TYPE=i32)(TID=10)
[21c4f4] KLEE: WARNING: CreateCall! wrapper=__wrap_prog_char, origCallInst->getNumArgOperands()=2,
[1780fd] KLEE: WARNING: getNumParams of wrapper= 1, type = 14
[510366] KLEE: WARNING: f.CONV:0, wrapper.CONV:0
[f81146] KLEE: WARNING: Wrapper has bad signature! LLVM refuses to create call.
[6c15e4] KLEE: WARNING: Failed to replace call to f=prog_char
_fp_out_narrow
With the minimal example from above, I get:
$ klee -libc=uclibc -simplify-sym-indices -search=nurs:covnew -split-search -skip-functions-not=__errno_location,__h_errno_location,__user_main,prog_char,input_char bc.bc
[988dcb] KLEE: createWrapperFunction(f=_fp_out_narrow)
[a17b2a] KLEE: WARNING: [createWrapperFunction] Set the arguments
[6b00f9] KLEE: WARNING: [createWrapperFunction] Create BB entry
[e1f7eb] KLEE: WARNING: replaceCall to call, arg[0]= (4 args) by f=_fp_out_narrow
[69a30f] KLEE: WARNING: - originalCall.argoperand[0] = %struct.__STDIO_FILE_STRUCT.215* %stream(TYPE=%struct.__STDIO_FILE_STRUCT.215*)(TID=14)
[69a30f] KLEE: WARNING: - originalCall.argoperand[1] = %cond194 = phi x86_fp80 [ %42, %cond.true188 ], [ %conv192, %cond.false190 ], !dbg !3435(TYPE=x86_fp80)(TID=4)
[69a30f] KLEE: WARNING: - originalCall.argoperand[2] = %info195 = getelementptr inbounds %struct.ppfs_t* %ppfs, i32 0, i32 1, !dbg !3435(TYPE=%struct.printf_info.315*)(TID=14)
[69a30f] KLEE: WARNING: - originalCall.argoperand[3] =
; Function Attrs: nounwind uwtable
define internal i64 @_fp_out_narrow(%struct.__STDIO_FILE_STRUCT.215* %fp, i64 %type, i64 %len, i64 %buf) #10 {
entry:
call void @llvm.dbg.value(metadata !{%struct.__STDIO_FILE_STRUCT.215* %fp}, i64 0, metadata !3258), !dbg !3259
call void @llvm.dbg.value(metadata !{i64 %type}, i64 0, metadata !3260), !dbg !3259
call void @llvm.dbg.value(metadata !{i64 %len}, i64 0, metadata !3261), !dbg !3259
call void @llvm.dbg.value(metadata !{i64 %buf}, i64 0, metadata !3262), !dbg !3259
call void @llvm.dbg.value(metadata !3263, i64 0, metadata !3264), !dbg !3265
%and = and i64 %type, 128, !dbg !3266
%tobool = icmp ne i64 %and, 0, !dbg !3266
br i1 %tobool, label %if.then, label %if.end12, !dbg !3266
if.then: ; preds = %entry
%0 = inttoptr i64 %buf to i8*, !dbg !3268
%call = call i64 @strlen(i8* %0) #17, !dbg !3268
%conv = trunc i64 %call to i32, !dbg !3268
call void @llvm.dbg.value(metadata !{i32 %conv}, i64 0, metadata !3270), !dbg !3268
%conv1 = sext i32 %conv to i64, !dbg !3271
%sub = sub nsw i64 %len, %conv1, !dbg !3271
call void @llvm.dbg.value(metadata !{i64 %sub}, i64 0, metadata !3261), !dbg !3271
%cmp = icmp sgt i64 %sub, 0, !dbg !3271
br i1 %cmp, label %if.then3, label %if.end10, !dbg !3271
if.then3: ; preds = %if.then
%and4 = and i64 %type, 127, !dbg !3273
%conv5 = trunc i64 %and4 to i32, !dbg !3273
%1 = alloca i64, !dbg !3273
call void @__wrap__charpad(i64* %1, %struct.__STDIO_FILE_STRUCT.215* %fp, i32 %conv5, i64 %sub), !dbg !3273
%2 = load i64* %1, !dbg !3273
call void @llvm.dbg.value(metadata !{i64 %2}, i64 0, metadata !3264), !dbg !3273
%cmp7 = icmp ne i64 %2, %sub, !dbg !3273
br i1 %cmp7, label %if.then9, label %if.end, !dbg !3273
if.then9: ; preds = %if.then3
br label %return, !dbg !3276
if.end: ; preds = %if.then3
br label %if.end10, !dbg !3278
if.end10: ; preds = %if.end, %if.then
%r.0 = phi i64 [ %2, %if.end ], [ 0, %if.then ]
%conv11 = sext i32 %conv to i64, !dbg !3279
call void @llvm.dbg.value(metadata !{i64 %conv11}, i64 0, metadata !3261), !dbg !3279
br label %if.end12, !dbg !3280
if.end12: ; preds = %if.end10, %entry
%len.addr.0 = phi i64 [ %conv11, %if.end10 ], [ %len, %entry ]
%r.1 = phi i64 [ %r.0, %if.end10 ], [ 0, %entry ]
%cmp13 = icmp sgt i64 %len.addr.0, 0, !dbg !3281
br i1 %cmp13, label %cond.true, label %cond.false, !dbg !3281
cond.true: ; preds = %if.end12
%3 = inttoptr i64 %buf to i8*, !dbg !3281
%call15 = call i64 @__stdio_fwrite(i8* %3, i64 %len.addr.0, %struct.__STDIO_FILE_STRUCT.215* %fp) #18, !dbg !3281
br label %cond.end, !dbg !3281
cond.false: ; preds = %if.end12
br label %cond.end, !dbg !3281
cond.end: ; preds = %cond.false, %cond.true
%cond = phi i64 [ %call15, %cond.true ], [ 0, %cond.false ], !dbg !3281
%add = add i64 %r.1, %cond, !dbg !3281
br label %return, !dbg !3281
return: ; preds = %cond.end, %if.then9
%retval.0 = phi i64 [ %2, %if.then9 ], [ %add, %cond.end ]
ret i64 %retval.0, !dbg !3282
}
(TYPE=i64 (%struct.__STDIO_FILE_STRUCT.215*, i64, i64, i64)*)(TID=14)
[21c4f4] KLEE: WARNING: CreateCall! wrapper=__wrap__fp_out_narrow, origCallInst->getNumArgOperands()=4,
[1780fd] KLEE: WARNING: getNumParams of wrapper= 5, type = 14
[510366] KLEE: WARNING: f.CONV:0, wrapper.CONV:0
[4322c4] KLEE: WARNING: Wrapper has bad signature! bad type of Args[2].type=4 =/= wrapper.type=10 LLVM refuses to create call.
[6c15e4] KLEE: WARNING: Failed to replace call to f=_fp_out_narrow
The mismatch happens on the type:
[4322c4] KLEE: WARNING: Wrapper has bad signature! bad type of Args[2].type=4 =/= wrapper.type=10 LLVM refuses to create call.
According to llvm/IR/type.h
, 4 and 10 are:
X86_FP80TyID, ///< 4: 80-bit floating point type (X87)
/* ... */
IntegerTyID, ///< 10: Arbitrary bit width integers
Do you have any clue about this?
Which klee-uclibc commit do you use in the _fp_out_narrow example?
I use klee-uclibc 07c4d2d1aff3858506c81ab1e7e4f4780dfe2a72
I figured this is related to variadic functions, I'll submit a patch when I get everything worked out. In any case, here is some useful assembly.ll from the example above:
; Function Attrs: nounwind uwtable
define internal i32 @__user_main(i32 %argc, i8** %argv) #0 {
entry:
%call = call i32 (i32, i32, i32, i32, ...)* bitcast (i32 (...)* @_fp_out_narrow to i32 (i32, i32, i32, i32, ...)*)(i32 0, i32 0, i32 0, i32 0), !dbg !1271
ret i32 0, !dbg !1272
}