-
Notifications
You must be signed in to change notification settings - Fork 42
Closed
Labels
bugSomething isn't workingSomething isn't workingdecompRelated to LLVM IR to C decompilerRelated to LLVM IR to C decompiler
Description
Compiling the following program to WebAssembly (no codegen, only IR clang --target=wasm32-unknown-wasi -S -emit-llvm ../test_struct_split.c
)
extern int printf(const char *fmt, ...);
extern int atoi(const char* s);
struct foo {
int x;
int y;
};
static int get_x(struct foo f) {
f.x *= 2;
return f.x;
}
static int get_y(struct foo f) {
f.y *= 3;
return f.y;
}
int main() {
struct foo f = { atoi("1"), atoi("2") };
int x = get_x(f);
int y = get_y(f);
printf("%d %d %d %d\n", x, y, f.x, f.y);
}
produces
; ModuleID = '../test_struct_split.c'
source_filename = "../test_struct_split.c"
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
target triple = "wasm32-unknown-wasi"
%struct.foo = type { i32, i32 }
@.str = private unnamed_addr constant [2 x i8] c"1\00", align 1
@.str.1 = private unnamed_addr constant [2 x i8] c"2\00", align 1
@.str.2 = private unnamed_addr constant [13 x i8] c"%d %d %d %d\0A\00", align 1
@llvm.used = appending global [1 x i8*] [i8* bitcast (i32 ()* @__main_void to i8*)], section "llvm.metadata"
@__main_void = alias i32 (), i32 ()* @main
; Function Attrs: noinline nounwind optnone
define hidden i32 @main() #0 {
entry:
%f = alloca %struct.foo, align 4
%x2 = alloca i32, align 4
%y4 = alloca i32, align 4
%x = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 0
%call = call i32 @atoi(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str, i32 0, i32 0))
store i32 %call, i32* %x, align 4
%y = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 1
%call1 = call i32 @atoi(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str.1, i32 0, i32 0))
store i32 %call1, i32* %y, align 4
%call3 = call i32 @get_x(%struct.foo* byval(%struct.foo) align 4 %f)
store i32 %call3, i32* %x2, align 4
%call5 = call i32 @get_y(%struct.foo* byval(%struct.foo) align 4 %f)
store i32 %call5, i32* %y4, align 4
%0 = load i32, i32* %x2, align 4
%1 = load i32, i32* %y4, align 4
%x6 = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 0
%2 = load i32, i32* %x6, align 4
%y7 = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 1
%3 = load i32, i32* %y7, align 4
%call8 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str.2, i32 0, i32 0), i32 %0, i32 %1, i32 %2, i32 %3)
ret i32 0
}
declare i32 @atoi(i8*) #1
; Function Attrs: noinline nounwind optnone
define internal i32 @get_x(%struct.foo* byval(%struct.foo) align 4 %f) #0 {
entry:
%x = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 0
%0 = load i32, i32* %x, align 4
%mul = mul nsw i32 %0, 2
store i32 %mul, i32* %x, align 4
%x1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 0
%1 = load i32, i32* %x1, align 4
ret i32 %1
}
; Function Attrs: noinline nounwind optnone
define internal i32 @get_y(%struct.foo* byval(%struct.foo) align 4 %f) #0 {
entry:
%y = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 1
%0 = load i32, i32* %y, align 4
%mul = mul nsw i32 %0, 3
store i32 %mul, i32* %y, align 4
%y1 = getelementptr inbounds %struct.foo, %struct.foo* %f, i32 0, i32 1
%1 = load i32, i32* %y1, align 4
ret i32 %1
}
declare i32 @printf(i8*, ...) #1
attributes #0 = { noinline nounwind optnone "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 12.0.1 (https://github.com/microsoft/vcpkg.git 2a31089e777fc187f1cc05338250b8e1810cfb52)"}
whose expected output is 2 6 1 2
.
Decompiling it via Rellic produces
unsigned char _str[2] = "1\000";
unsigned char _str_1[2] = "2\000";
unsigned char _str_2[13] = "%d %d %d %d\n\000";
unsigned int main();
unsigned int atoi(unsigned char *arg0);
struct struct_foo {
unsigned int field0;
unsigned int field1;
};
unsigned int get_x(struct struct_foo *f);
unsigned int get_y(struct struct_foo *f);
unsigned int printf(unsigned char *arg0, ...);
unsigned int main() {
struct struct_foo var0;
unsigned int var1;
unsigned int var2;
unsigned int val3;
unsigned int val4;
unsigned int val5;
unsigned int val6;
unsigned int val7;
val3 = atoi(_str);
var0.field0 = val3;
val4 = atoi(_str_1);
var0.field1 = val4;
val5 = get_x(&var0);
var1 = val5;
val6 = get_y(&var0);
var2 = val6;
val7 = printf(_str_2, var1, var2, var0.field0, var0.field1);
return 0U;
}
unsigned int get_x(struct struct_foo *f) {
f->field0 = f->field0 * 2U;
return f->field0;
}
unsigned int get_y(struct struct_foo *f) {
f->field1 = f->field1 * 3U;
return f->field1;
}
whose output (after a bit of massaging to correct the type inaccuracies) is 2 6 2 6
, which is incorrect as the structs are passed by reference instead of by value
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't workingdecompRelated to LLVM IR to C decompilerRelated to LLVM IR to C decompiler