2

I am trying to print AAAA using c __asm__ as following:

#include <stdio.h>

int main()
{
__asm__("sub $0x150, %rsp\n\t"
        "mov   $0x0,%rax\n\t"
        "lea   -0x140(%rbp), %rax\n\t"
        "movl   $0x41414141,(%rax)\n\t"
        "movb   $0x0, 0x4(%rax)\n\t"
        "lea   -0x140(%rbp), %rax\n\t"
        "mov    %rax, %rdi\n\t"
        "call  printf\n\t");

    return 0;
}

Disassembly:

Dump of assembler code for function main:                                                                        
 0x0000000000400536 <+0>:     push   %rbp                                                                      
 0x0000000000400537 <+1>:     mov    %rsp,%rbp                                                                 
 0x000000000040053a <+4>:     sub    $0x150,%rsp                                                               
 0x0000000000400541 <+11>:    mov    $0x0,%rax                                                                 
 0x0000000000400548 <+18>:    lea    -0x140(%rbp),%rax                                                         
 0x000000000040054f <+25>:    movl   $0x41414141,(%rax)                                                        
 0x0000000000400555 <+31>:    movb   $0x0,0x4(%rax)                                                            
 0x0000000000400559 <+35>:    lea    -0x140(%rbp),%rax                                                         
 0x0000000000400560 <+42>:    mov    %rax,%rdi                                                                 
 0x0000000000400563 <+45>:    callq  0x400410 <printf@plt>                                                     
 0x0000000000400568 <+50>:    mov    $0x0,%eax                                                                 
 0x000000000040056d <+55>:    pop    %rbp                                                                      
 0x000000000040056e <+56>:    retq                                                                             
End of assembler dump.

While running the code, there are basically two issues. #1 that it does not print "AAAA", #1 that when the RIP reaches retq, it throws segmentation fault

is there anything I am missing?

tadman
  • 208,517
  • 23
  • 234
  • 262
  • You may need to readjust the stack in the calling code after `printf` – Govind Parmar Apr 16 '19 at 18:00
  • 2
    Why don't you write a C program that calls `printf` and look at the disassembly? – Barmar Apr 16 '19 at 18:01
  • @Barmar I have, but a `c` program have extra instructions such as `xor(ing)` to protect stack and etc.. but other than that, my assembly is pretty much the same as `c` disassembly except what is after `call printf` – amiTheregroot Apr 16 '19 at 18:06
  • @GovindParmar okay that might help with the segmentation fault i believe, but what about no print the `rdi` even when when I do `x/x $rdi` in gdb, I see `0x41414141`, sooo.. – amiTheregroot Apr 16 '19 at 18:08
  • 4
    My honest suggest: do not call functions from within `asm` statements. That's not what inline assembly is meant for. Inline assembly is a poor tool for learning assembly language, so I advise you to not use it until you are familiar with assembly programming in general. – fuz Apr 16 '19 at 18:09
  • @fuz Appreciate your concern, that's exactly why I am using it. just to learn :) – amiTheregroot Apr 16 '19 at 18:10
  • 2
    @amiTheregroot And it's not a good tool for that! Just write plain assembly code in assembly files. With inline assembler, the compiler can make all sort of weird choices when generating code and it requires decent knowledge of how the compiler works to get it right. That's not good for learning. – fuz Apr 16 '19 at 18:11
  • 3
    Yah, don't use inline assembly to do things you can do in C code and don't use inline assembly to learn assembly. – Ross Ridge Apr 16 '19 at 18:15

1 Answers1

9

Your code has the following problems:

  • The main problem of your code is that you fail to restore the stack pointer to its previous value after calling printf. The compiler does not know that you modified the stack pointer and tries to return to whatever address is at (%rsp), crashing your program. To fix this, restore rsp to the value it had at the beginning of the asm statement.

  • you forgot to set up al with 0 to indicate that no floating point values are being passed to printf. This is needed as printf is a variadic function. While it doesn't cause any problems to set al to a value too high (and all values are less than or equal to 0), it is still good practice to set up al correctly. To fix this, set al to 0 before calling printf.

  • That said, you cannot safely assume that the compiler is going to set up a base pointer. To fix this, make sure to only reference rsp instead of rbp or use extended asm to let the compiler figure this out.

  • take care not to overwrite the stack. Note that the 128 bytes below rsp are called the red zone and must be preserved, too. This is fine in your code as you allocate enough stack space to avoid this issue.

  • your code tacitly assumes that the stack is aligned to a multiple of 16 bytes on entry to the asm statement. This too is an assumption you cannot make. To fix this, align the stack pointer to a multiple of 16 bytes before calling printf.

  • you overwrite a bunch of registers; apart from rax and rdi, the call to printf may overwrite any caller-saved register. The compiler does not know that you did so and may assume that all registers kept the value they had before. To fix this, declare an appropriate clobber list or save and restore all registers you plan to overwrite, including all caller-saved registers.

So TL;DR: don't call functions from inline assembly and don't use inline assembly as a learning tool! It's very hard to get right and does not teach you anything useful about assembly programming.

Example in Pure Assembly

Here is how I would write your code in normal assembly. This is what I suggest you to do:

        .section .rodata     # enter the read-only data section
str:    .string "AAAA"       # and place the string we want to print there

        .section .text       # enter the text section
        .global main         # make main visible to the link editor
main:   push %rbp            # establish...
        mov %rsp, %rbp       # ...a stack frame (and align rsp to 16 bytes)

        lea str(%rip), %rdi  # load the effective address of str to rdi
        xor %al, %al         # tell printf we have no floating point args
        call printf          # call printf(str)

        leave                # tear down the stack frame
        ret                  # return

Example in Inline Assembly

Here is how you could call a function in inline assembly. Understand that you should never ever do this. Not even for educational purposes. It's just terrible to do this. If you want to call a function in C, do it in C code, not inline assembly.

That said, you could do something like this. Note that we use extended assembly to make our life a lot easier:

int main(void)
{
    char fpargs = 0;         /* dummy variable: no fp arguments */
    const char *str = "AAAA";/* the string we want to print */

    __asm__ volatile (       /* volatile means we do more than just
                                returning a result and ban the compiler
                                from optimising our asm statement away */
        "mov %%rsp, %%rbx;"  /* save the stack pointer */
        "and $~0xf, %%rsp;"  /* align stack to 16 bytes */
        "sub $128, %%rsp;"   /* skip red zone */
        "call printf;"       /* do the actual function call */
        "mov %%rbx, %%rsp"   /* restore the stack pointer */
        :                    /* (pseudo) output operands: */
        "+a"(fpargs),        /* al must be 0 (no FP arguments) */
        "+D"(str)            /* rdi contains pointer to string "AAAA" */
        :                    /* input operands: none */
        :                    /* clobber list */
        "rsi", "rdx",        /* all other registers... */
        "rcx", "r8", "r9",   /* ...the function printf... */
        "r10", "r11",        /* ...is allowed to overwrite */
        "rbx",               /* and rbx which we use for scratch space */
        "cc",                /* and flags */
        "memory");           /* and arbitrary memory regions */

    return (0);              /* wrap this up */
}
fuz
  • 88,405
  • 25
  • 200
  • 352
  • sorry for late response. pretty noob to assembly can you also add how my working code would be? – amiTheregroot Apr 18 '19 at 15:25
  • @amiTheregroot I could. But please remember, that this is not in any way reflective of how you would actually call a function in assembly. If you promise me to ditch inline assembly for learning purposes and instead program in normal assembly in an assembly source file, I am going to add an example. – fuz Apr 18 '19 at 16:07
  • lol alright will do so, but just FYI, every drop of knowledge count whether it's different source file or c `asm`. need to learn both to understand the advantage and dis advantages. – amiTheregroot Apr 18 '19 at 16:33
  • I can understand this idealism. However, keep in mind that the code is not going to be very useful as most of it is just needed to make this work in inline assembly which is otherwise not meant for this sort of thing. – fuz Apr 18 '19 at 16:45
  • 1
    @amiTheregroot I have added the requested example. – fuz Apr 18 '19 at 17:21
  • ayy thank you big time, appreciate it :) – amiTheregroot Apr 18 '19 at 17:54