Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling C functions from x86 assembly language

Tags:

c

x86

assembly

Is it possible to generate assembly language functions from C functions using GCC, so that they can be invoked from an assembly language program? I know that gcc compiles C to machine code (which can easily be disassembled into assembly language), and I already know that it's possible to inline assembly language functions in C, but I haven't yet found a way to invoke C functions from assembly language programs, which is basically the inverse of this.

Here, I'm attempting to inline a C function in an x86 assembly program. If inlining isn't possible, then is there some other way to invoke a C function from an assembly language program?

.686p .model flat,stdcall .stack 2048  .data  .code start:  invoke  ExitProcess, 0  printSomething PROC ;now I'm attempting to inline a C function here     void printSomething(thingToPrint){         printf("This is a C function that I want to invoke from an assembly language program.");         printf("There must be some way to do this - is it possible somehow?");     } printSomething ENDP  end start 
like image 287
Anderson Green Avatar asked Apr 27 '13 18:04

Anderson Green


People also ask

How do you call a function in assembly?

To call an external function, such as NetRun's "print_int", or a standard C library function like "exit", you need to tell the assembler the function is "extern". "extern" isn't actually an instruction--it doesn't show up in the disassembly--it's just a message to the assembler, often called a pseudoinstruction.

How are arguments passed in x86?

Arguments are passed on the stack in Right-to-Left order, and return values are passed in eax. The calling function cleans the stack.


2 Answers

I'm going from memory here, so I may be off slightly on a detail or two. However, it should I hope be enough to get you going in the right direction.

You're going to need to tell the GCC assembler that your routine printSomething() is not defined in your assembly file. In 'C' you would use the extern keyword. For assembly you will need to use .globl.

.globl printSomething 

If you are using a different assembler than GCC's, the keyword may be different.

The next big question is 'how do I pass the arguments'? This very much depends upon your processor AND OS. Since the title of your question indicates x86, I am going to assume that you are using either the 16-bit or 32-bit modes and the standard x86 ABI (as opposed to x86-64 which is also differs between Windows and Linux). The C parameters are passed to the called routine by pushing them onto the stack. They are pushed onto the stack from right to left.

Thus,

printSomething (arg1, arg2, arg3, arg4); 

translates to ...

pushl arg4 pushl arg3 pushl arg2 pushl arg1 call  printSomething addl  $0x10, %esp 

You may be asking yourself, what is this

addl $0x10, %esp 

? We passed (aka pushed) four 32-bit arguments to the routine (onto the stack). Although the routine knows to expect those arguments, it is NOT responsible for popping them off the stack. The caller is responsible for that. So, after we return from the routine, we adjust the stack pointer to discard the four 32-bit arguments we previously pushed onto the stack.

In the above example, I am assuming that we are operating in 32-bit mode. If it were 16-bit mode, it would be ...

pushw arg4 pushw arg3 pushw arg2 pushw arg1 call  printSomething addw  $0x8, %sp 

I realize that in your example, printSomething() only takes one (1) argument and in my example I used four (4). Just adjust my example as is needed.

For the final steps, you will need to compile both your C and assembly files into object files, link the object files and then execute.

I hope this helps.

like image 120
Sparky Avatar answered Sep 23 '22 07:09

Sparky


For x86_64, note that you have to be careful with some extra things:

  • the stack must be 16-bit aligned before making C calls.

    If you define your function in assembly, the function that called your function put the return value on stack, misaligning it, and so you have to subtract 8 more byte somehow, usually push %rbp.

    This got me here for example: Why does calling the C abort() function from an x86_64 assembly function lead to segmentation fault (SIGSEGV) instead of an abort signal?

  • if you are doing a call from assembly for some reason (TODO why would you ever want to do that?) you have to worry about:

    • marking all the non-callee saved registers as clobbered
    • the red zone for your arguments

    Here is an example: Calling printf in extended inline ASM