This is easy if you know how to do it, but it can be a pain to search for if you don’t, so here’s a short how-to.
First of all, we’ll want to write a bit of C code which will invoke our assembly routine:
#include <stdio.h>
extern int my_pow(int base, int exp);
int main(int argc, char const *argv[]) {
int base, exp, result;
base = 2;
exp = 8;
result = my_pow(2,8);
printf("Result: %d\n", result);
return 0;
}
Using clang on Mac OS X (you should’ve the latest Xcode installed), you can compile (but not link) this piece of code using the command
clang -o test.o -c test.c
This assumes that your C source file is called test.c. Clang’s -c compiles the given source file to an object file for later linking, and defaults to 64-bit mode. If you’d rather create a 32-bit object file, you can supply the -arch i386 parameter.
So now we’ve compiled our C source, so it’s time to write some assembly. But how does the C function pass the parameters, and how do we return the result? To answer such questions, it is often useful to look at the assembly generated from our C code, which can be obtained with clang -S test.c. The interesting part is the following (this is AT&T syntax):
movl $2, %eax
movl $8, %ecx
...
movl %eax, %edi
movl %ecx, %esi
callq _my_pow
...
movl %eax, -28(%rbp)
movl -28(%rbp), %esi
movb $0, %al
callq _printf
So our operands, 2 and 8, are moved into eax and ecx respectively, which are free-for-use registers. In 32-bit C functions, operands are passed on the stack, but in the System V AMD64 ABI the first integer or pointer operands are passed in registers instead. As a result, our operands are moved to edi and esi, and our _my_pow procedure is called. The return value of the procedure is always stored in the eax (or rax) register.
So now it’s time to write some assembly (this is NASM Syntax now, which uses the Intel assembly style):
global _my_pow
section .text
_my_pow:
push rbp ; create stack frame
mov rbp, rsp
cmp edi, 0 ; Check if base is negative
mov eax, 0 ; and return 0 if so
jl end
mov eax, edi ; grab the "base" argument
mov edx, esi ; grab the "exponent" argument
multiply:
imul eax, edi ; eax * base
sub esi, 1 ; exponent - 1
cmp esi, 1 ; Loop if exponent > 1
jg multiply
end:
pop rbp ; restore the base pointer
ret ; return from procedure
First of all, we have to define the _my_pow label to be global, because otherwise C won’t be able to call it. Note the underscore, since the C convention is to prepend one to all function labels. The “create stack frame” and “restore base pointer” parts are actually not necessary for our use case, since we do not use the stack, but I’ve added them for completeness.
As we’ve previously seen in the assembly generated from our C code, the “base” argument is passed in the edi register, and the “exponent” argument is passed in the esi register, so we start by moving the base to the result register eax, and then multiply eax and the base exponent - 1 times.
Assuming that you saved the assembly under the name asm.s, you can now generate an object file using the following NASM command, which will generate an asm.o object file:
nasm -f macho64 asm.s
After having generated both object files, you can now link them by invoking
clang -o pow asm.o test.o
This should generate an executable called pow, which prints “Result: 256”. For reference, here are my resulting files, including a simple Makefile.