3

I'm new in inline assembly. I have the following C function with inline assembly. I'm just trying to see if push %%rbp and mov %%rsp, %%rbp actually operating correctly. My function is following,

test_inlineAssemblyFunction(){

    u64 base, rsp, base1, rsp1;
    asm volatile(

            "mov %%rbp, %0 \n"
            "mov %%rsp, %1 \n"

            "push %%rbp \n"
            "mov %%rbp, %2 \n"

            "mov %%rsp, %%rbp \n" <--- this line is causing the problem
            "mov %%rbp, %3 \n"
            :"=r"(base), "=r"(rsp),"=r"(base1), "=r"(rsp1)
            :
            : "rax","rbx","rsp"
            );
    printf("Before: Base register %x\n", base);
    printf("Before: stack pointer register %x\n", rsp);
    printf("After: (Should be same as previous )Base register %x\n", base1);
    printf("After: (Actually %rsp-8)Base register %x\n", rsp1);

    
}

output:

Before: Base register ee050e50
Before: stack pointer register ee050e20
After: (Should be same as previous )Base register ee050e50
After: (Actually %rsp-8)Base register ee050e18
Segmentation fault (core dumped)

From the output, I can see that all the print statement is printing the desired output. But then the segmentation fault. If my understanding is correct Segmentation Fault occurs when trying to read or write an illegal memory location. So, In my case, "mov %%rsp, %%rbp \n" is causing Segmentation fault, So I'm not allowed to read rsp from userspace?

Update: As @Kuba hasn't forgotten Monica suggested, Following is the disassembly of test_inlineAssemblyFunction function,

(gdb) disas test_inlineAssemblyFunction
Dump of assembler code for function test_inlineAssemblyFunction:
0x0000000000007366 <+0>:    push   %rbp
0x0000000000007367 <+1>:    mov    %rsp,%rbp
0x000000000000736a <+4>:    push   %rbx
0x000000000000736b <+5>:    sub    $0x28,%rsp
0x000000000000736f <+9>:    mov    %rbp,%rdi
0x0000000000007372 <+12>:   mov    %rsp,%rsi
0x0000000000007375 <+15>:   push   %rbp
0x0000000000007376 <+16>:   mov    %rbp,%rcx
0x0000000000007379 <+19>:   mov    %rsp,%rbp
0x000000000000737c <+22>:   mov    %rax,%rdx
0x000000000000737f <+25>:   mov    %rdi,-0x30(%rbp)
0x0000000000007383 <+29>:   mov    %rsi,-0x28(%rbp)
0x0000000000007387 <+33>:   mov    %rcx,-0x20(%rbp)
0x000000000000738b <+37>:   mov    %rdx,-0x18(%rbp)
0x000000000000738f <+41>:   mov    -0x30(%rbp),%rax
0x0000000000007393 <+45>:   mov    %rax,%rsi
0x0000000000007396 <+48>:   lea    0x275ab(%rip),%rdi        # 0x2e948
0x000000000000739d <+55>:   mov    $0x0,%eax
0x00000000000073a2 <+60>:   callq  0x5570 <printf@plt>
0x00000000000073a7 <+65>:   mov    -0x28(%rbp),%rax
0x00000000000073ab <+69>:   mov    %rax,%rsi
0x00000000000073ae <+72>:   lea    0x275b3(%rip),%rdi        # 0x2e968
0x00000000000073b5 <+79>:   mov    $0x0,%eax
0x00000000000073ba <+84>:   callq  0x5570 <printf@plt>
0x00000000000073bf <+89>:   mov    -0x20(%rbp),%rax
0x00000000000073c3 <+93>:   mov    %rax,%rsi
0x00000000000073c6 <+96>:   lea    0x275c3(%rip),%rdi        # 0x2e990
0x00000000000073cd <+103>:  mov    $0x0,%eax
0x00000000000073d2 <+108>:  callq  0x5570 <printf@plt>
0x00000000000073d7 <+113>:  mov    -0x18(%rbp),%rax
0x00000000000073db <+117>:  mov    %rax,%rsi
0x00000000000073de <+120>:  lea    0x275e3(%rip),%rdi        # 0x2e9c8
0x00000000000073e5 <+127>:  mov    $0x0,%eax
0x00000000000073ea <+132>:  callq  0x5570 <printf@plt>
0x00000000000073ef <+137>:  nop
0x00000000000073f0 <+138>:  mov    -0x8(%rbp),%rbx
0x00000000000073f4 <+142>:  leaveq 
0x00000000000073f5 <+143>:  retq   
End of assembler dump.
(gdb)
user45698746
  • 305
  • 2
  • 13
  • Have you tried looking at the compiler output asm, to see how your asm mixed with the compiler's? Depending on `-fomit-frame-pointer` or not, the `"=m"` operands will be referenced relative to RSP or RBP, so messing around with RSP and RBP in an asm statement with memory operands seems like a terrible idea. (Also, I don't think an RSP clobber actually works). Also, it's not safe for inline-asm to step on the red-zone below RSP, but in practice it's safe in non-leaf functions like this: gcc and clang won't use the res-zone because you call printf. – Peter Cordes Jan 13 '21 at 20:39
  • 2
    Reading `rsp` is fine, but modifying `rbp` within inline assembly, without a clobber, is not. It is also not safe to push to the stack in x86-64 inline asm because of the red zone. And it is certainly not safe to *leave* stuff on the stack, so that the stack pointer is in a different position than where it was left (a clobber of `rsp` does not help). – Nate Eldredge Jan 13 '21 at 20:39
  • @PeterCordes Sorry. I fix my code. I was trying with both `=r` & `=m`. `=m` causing stack smash. and `=r` causing segmentation fault – user45698746 Jan 13 '21 at 20:42
  • 1
    You modify RSP without restoring, so when the function tries to return it's probably with RSP not pointing at the return address. The `"rsp"` clobber doesn't make this safe; I'm pretty sure it's supported. Again, look at the whole asm output of the compiler, e.g. [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) – Peter Cordes Jan 13 '21 at 20:45
  • @NateEldredge can you please give an example regarding modifying `rbp` with clobber? – user45698746 Jan 13 '21 at 23:07

2 Answers2

2

There's nothing that guarantees that the compiler you're compiling the code with is using those registers the way you imply it's using them. That's all there's to it. I'm not sure where you got the idea that it should be the way you imply without actually looking at the assembly output generated by the compiler as used to build your program.

When you're writing in assembly, you're interfacing with all the surrounding assembly code, so I'm befuddled why you'd do it in the dark. The first thing you have to do is to stop and figure out how to get assembly output from the compiler i.e. how to pass the options the compiler expects to save such assembly output to a file in addition to saving the object code into a file. Then rebuild your program and see how your assembly fits with the rest of it. Hint: it doesn't :)

If figuring this out on your own system is a bit too much distraction at the moment, then you can use https://godbolt.org to write your code, choose one of the gcc compilers, and then you can both examine the assembly output and run the program in the cloud and observe the effects. All you need for that is a web browser. Nothing else needs to be locally installed.

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • I have added compiler-generated assembly code. As @Nate Eldredge suggested, the problem that I'm facing is due to modifying `rbp`. I have tried replacing `rbp` with `rax`. the function runs without error. – user45698746 Jan 13 '21 at 23:34
  • @user45698746 You're not modifying rbp willy nilly. It's not as if you looked at available registers and threw a dart and came up with rbp and can use something else instead. You're trying to mimic code you've seen before. Except you only got the first half of that code: what you show will work perfectly well if you add the **missing second half**. What you wrote is usually used as a function prologue that establishes a stack frame. Every prologue needs an *epilogue*: the reverse operation before returning from the function. The stack frame you built must be torn down. It'll work then :) – Kuba hasn't forgotten Monica Jan 14 '21 at 01:37
  • @Kubahasn'tforgottenMonica: Making another "stack frame" inside a non-`naked` function will only work if none of your input or output operands are `"m"` or `"=m"`. Otherwise, GCC will fill in your template with `%0` being `-4(%rbp)` or `12(%rsp)` for example, depending on `-fomit-frame-pointer` or not. But yes if you avoid any `"m"` or `"g"` operands, and the function doesn't use the red-zone (e.g. because it's not a leaf function), then you can just barely safely use the stack inside your asm template, as you say as long as you put everything back before the end of the asm statement. – Peter Cordes Jan 14 '21 at 06:19
  • (I mention memory operands because the question originally had them.) – Peter Cordes Jan 14 '21 at 06:20
2

The problem is not that you read rsp. It's that you clobbered rbp (by overwriting it with the value copied from rsp) and rsp (by performing a push without a matching pop) from the asm block without informing the compiler about the clobbers. The former you could do by adding rbp to the clobber list; the latter is not legal at all.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711