1

I'm working on a small programming language compiled using cranelift. Now, my generated code segfaults whenever I call malloc or even puts. I'll focus on a small example with malloc:

The code I'm trying to compile is roughly equivalent to the following pseudo-C program:

void _start(){
   malloc(8);
   exit(0);
   return;
}

Note that I'm not actually compiling C and therefore using the _start entrypoint, like you would in assembler, instead of Cs main.

The generated Cranelift IR:

function u0:0() system_v {
    sig0 = (i64) -> i64 system_v    # malloc
    sig1 = (i32) system_v           # exit
    fn0 = u0:0 sig0
                                    # fn1 is not defined for some reason?
block0:
    v0 = iconst.i64 8
    v1 = call fn0(v0)  ; v0 = 8
    v2 = iconst.i32 0
    call fn1(v2)  ; v2 = 0
    return
}

Which is compiled to an object file which I link using

ld -pie -O2 --dynamic-linker=/lib64/ld-linux-x86-64.so.2 -o test -lc test.o

I tried omitting the -pie and -O2 with no effect. The default link interpreter is /lib/ld64 for some reason which does not produce a valid executable on my system.

This Cranelift IR is equvalent to the following assembly:

Dissassembled using objdump -d test.o:

<other functions omitted>

00000000000010f0 <_start>:
    10f0:       55                      push   %rbp
    10f1:       48 89 e5                mov    %rsp,%rbp
    10f4:       bf 08 00 00 00          mov    $0x8,%edi
    10f9:       48 8b 15 f0 1e 00 00    mov    0x1ef0(%rip),%rdx        # 2ff0 <malloc@GLIBC_2.2.5>
    1100:       ff d2                   call   *%rdx
    1102:       31 ff                   xor    %edi,%edi
    1104:       48 8b 15 ed 1e 00 00    mov    0x1eed(%rip),%rdx        # 2ff8 <exit@GLIBC_2.2.5>
    110b:       ff d2                   call   *%rdx
    110d:       48 89 ec                mov    %rbp,%rsp
    1110:       5d                      pop    %rbp
    1111:       c3                      ret   

Dissassembled using Cranelift itself:

  pushq   %rbp
  unwind PushFrameRegs { offset_upward_to_caller_sp: 16 }
  movq    %rsp, %rbp
  unwind DefineNewFrame { offset_upward_to_caller_sp: 16, offset_downward_to_clobbers: 0 }
block0:
  movl    $8, %edi
  load_ext_name userextname0+0, %rdx
  call    *%rdx
  xorl    %edi, %edi, %edi
  load_ext_name userextname1+0, %rdx
  call    *%rdx
  movq    %rbp, %rsp
  popq    %rbp
  ret

A GDB-Stacktrace of the segfault:

(gdb) backtrace
#0  0x00007ffff7ca4540 in _int_malloc (av=av@entry=0x7ffff7e19c80 <main_arena>, bytes=bytes@entry=640) at ./malloc/malloc.c:4375
#1  0x00007ffff7ca4a49 in tcache_init () at ./malloc/malloc.c:3245
#2  0x00007ffff7ca525e in tcache_init () at ./malloc/malloc.c:3241
#3  __GI___libc_malloc (bytes=8) at ./malloc/malloc.c:3306
#4  0x0000555555555102 in _start ()

Used Cranelift Options:

use_colocated_libcalls: "false"
is_pic: "true"
opt_level: "speed"
regalloc_checker: "true"
enable_alias_analysis: "true"
enable_verifier: "true"
enable_probestack: "false"

I can update the question with strace and valgrind output if needed. I'm on Ubuntu 22.04 x64 using Cranelift 0.93.1 (using cranelift-object for emitting the obj-file).

nickkoro
  • 374
  • 3
  • 15
  • main should return int not void you even write return 0 that make no sense – Stargateur Mar 11 '23 at 12:41
  • That's why I mentioned "pseudo-C" ;) - and I actually do not return 0, I call exit(0). I'll edit it for clarity. The question is not about C anyway – nickkoro Mar 11 '23 at 12:47
  • 3
    Libc may very well depend on certain stuff being initialized that you skip by not letting its startup code run. First test with properly using `main` and the standard entry point in libc. Also note that `_start` has stack differently aligned than `main` so you might need to adjust `rsp` by 8. Look at the crashing instruction (`x/i $pc`) and if it's some aligned SSE instruction then that is likely your issue. – Jester Mar 11 '23 at 12:56
  • I'm an expert in C who see this question cause you tagged Rust, I know nothing about "Cranelift IR" I just give you problem in your C code you could fix before search other solution, since you transcompile C code I expect have a correct C code is a better start. – Stargateur Mar 11 '23 at 12:56
  • you can't make a question with unclear info "The code I'm trying to compile is roughly equivalent to the following pseudo-C program" is not acceptable. – Stargateur Mar 11 '23 at 12:58
  • Are you statically linking? If so, you need to call libc init functions yourself from `_start` before you can call functions like `malloc` or `printf`. Also, why is your code misaligning RSP? `_start` isn't a function, there's nothing to return to and no caller with an RBP to save. You should `xor %ebp, %ebp` instead of `push %rbp` / `mov %rsp, %rbp` as recommended in the x86-64 System V ABI doc. – Peter Cordes Mar 11 '23 at 13:20
  • I'm linking dynamically (I actually posted the exact command above) - do I still need to init libc? I found assembly snippets which just call `puts` from `_start` without initing anything. As for the misalignment, this code is actually generated by Cranelift - I doubt I can directly influence the registers. It this just bad practice or can it lead to issues? – nickkoro Mar 11 '23 at 13:22
  • 1
    Yeah, dynamic linking lets glibc's hooks run its init functions for you. That just leaves stack misalignment as the likely problem. Use a debugger to see what crashes, like Jester said it's probably a `movaps` or `movdqa` to the stack. – Peter Cordes Mar 11 '23 at 13:24

0 Answers0