1

I want to test inline asm capabilty on gcc. So, I type and compile following code on ubuntu 12.04 64-bit but system shows ''segmentation fault" on screen when it runs. I don't have any idea what causes the problem.

#include <stdio.h>
char Format[]="Hello world %d\n";
int main()
{
    asm
    (
        "movl $3,4(%esp);"
        "movl $Format,(%esp);"
        "call printf;"
    );
    return 0;
}

Thank you guys for helping me a program newbie. I use Code::blocks as IDE to write this code. I had tried to use 64-bit registers such like %rdx, but logs of Build messages shows " Error: bad register name `%rdx' " when compiling the code. I think this means the gcc invoked by Code::blocks is 32-bit version, hence it can't recognize those registers. I modify the code to reserve the stack space

#include <stdio.h>
char Format[]="Hello world %d\n";
int main()
{
    asm
    (
        "subl $8,%esp;"         //I  don't know  $4, $8, $12, $16, $20 which is correct
                                        //but I had tried them all but results are still  ''segmentation fault."
        "movl $3,4(%esp);"
        "movl $Format,(%esp);"
        "call printf;"
        "movl %ebp,%esp;"
    );
    return 0;
}

and even use -m32 as compiler option, but it still shows ''segmentation fault ".

thanks again for who helps.

Netsphere Wu
  • 45
  • 1
  • 7
  • 2
    Watch your calling conventions. I believe the first operand needs to be `eax`. – Mysticial Jul 07 '12 at 19:56
  • Or, compile with `-m32` and don't give a crap about the calling convention... –  Jul 07 '12 at 20:39
  • To move something into `%rdx` you should use `movq`. Does it print the text before it segfaults? – Hristo Iliev Jul 08 '12 at 18:21
  • You are incorrectly restoring the stack after the call to `printf`. See my updated answer for some additions on how to call `printf` in 64-bit mode and a working 32-bit example. – Hristo Iliev Jul 08 '12 at 19:21

2 Answers2

4

Try first to look at a normal C program and see what asm it gives (you can get it by using gcc -S).

Then, identify the part of ASM which is needed for the printf call and reproduce it in your original program.

What you have here is a calling convention error.

Mihai Maruseac
  • 20,967
  • 7
  • 57
  • 109
4

System V ABI for x64 mandates that the first six integer/pointer arguments to a function should go in registers %rdi, %rsi, %rdx, %rcx, %r8 and %r9. The stack is used to pass further arguments. It also requres that when calling functions with variable number of arguments (like printf), %rax should be set to the total number of floating-point arguments passed in the XMM registers. The right sequence to call printf() in your case is:

xorl %eax, %eax
movl $Format, %edi
movl $3, %esi
call printf

%rax should be set to 0 since no floating-point arguments are being passed. This code also uses the fact that VA of initialised data usually lies somewhere in the first 4 GiB and thus shorter 32-bit instructions are used. Of course printf will still examine the full content of %rdi to determine where the format string is located in memory.

Your code uses the 32-bit calling convention and should theoretically work if cross-compiled as 32-bit with -m32 but you should first reserve stack space for the arguments using something like subl $20, %esp and restore it after the call with addl %20, %esp, otherwise you are either overwriting the stack of main() or ret will pick the wrong return address. Here is a fully working (tested) C/asm code that compiles and run in 32-bit mode:

#include <stdio.h>

char Format[] = "Hello world, %d\n";

int main (void)
{
   asm
   (
      // Make stack space for arguments to printf
      "subl $8, %esp\n"
      "movl $3, 4(%esp)\n"
      "movl $Format, (%esp)\n"
      "call printf\n"
      // Clean-up the stack
      "addl $8, %esp\n"
   );
   return 0;
}

$ gcc -m32 -o test.x test.c
$ ./test.x
Hello world, 3

Remark: I use \n instead of ; at the end of each assembly line only to improve the readability of the compiler assembly output - it is irrelevant to the correctness of the code.

Hristo Iliev
  • 72,659
  • 12
  • 135
  • 186
  • Beware of _register clobbering_ ... the above works for a simple demo program. If you've got a complex piece of code where you're planning to use inline assembly that contains `call` instructions, then you need to save/restore a few registers (one or all of `edi`, `esi`, `ebp` and `ebx`) around the `call`, and declare all other registers clobbered. Otherwise, the compiler may assume register contents to persist over the function call where that would change the contents. – FrankH. Jul 09 '12 at 10:57
  • http://en.wikipedia.org/wiki/X86_calling_conventions I found this wiki page quite useful to solve these kind of questions. – Netsphere Wu Jul 10 '12 at 12:09
  • You forgot to declare clobbers for EAX, ECX, and EDX, not to mention XMM0..7 and ST0..7. (And k0..7 if AVX-512 is available). It's hard to safely call a function from GNU C inline asm. See [Calling printf in extended inline ASM](https://stackoverflow.com/q/37502841) GNU C "Basic" asm statements (no `:` operands and clobbers) inside functions (except `__attribute__((naked))`) are a bad idea. – Peter Cordes Mar 09 '23 at 02:13
  • https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended – Peter Cordes Mar 09 '23 at 02:18