The counterpart to a high-level function is an assembly function. The counterpart to an assembly macro is a macro or template in a high level language.
Modular Programming with Functions
The easiest way to write structured or modular code is to write functions. A function is very similar to what you wrote, but you need to return from it using a ret
instruction. This print
function takes the address of the string you want to print in x0
and its length in x1
. It follows the AArch64 ABI and trashes registers x0
, x1
, x2
, and x8
.
.text
.type print, @function // these two directives make the function
.globl print // callable from other object files.
print: mov x2, x1 // move length into place for syscall
mov x1, x0 // move string address into place for syscall
mov x0, #1 // print to stdout
mov x8, #0x40 // do a write system call
svc #0
ret // return to caller
You can then call the function from any object file you like:
.data
hello: .ascii "hello world\n"
len= .-hello
.text
ldr x0, =hello // load string address from literal pool
mov x1, #len // load string length
bl print // call our print function
Note that as each function uses the same lr
register to keep track of the return address, you may need to save the lr
register to the stack in functions that call other functions. As registers must be pushed onto the stack in pairs on arm64, I've pushed xzr
as the second register. In practice, you'll probably want to push some other register that needs to be saved.
str lr, xzr, [sp, #-16]! // push lr and xzr onto the stack
...
ldr x0, =hello // load string address from literal pool
mov x1, #len // load string length
bl print // call our print function
...
ldr lr, xzr, [sp], #16 // pop lr and xzr off the stack
ret
Modular Programming with Macros
Your macro approach is almost correct. Here is a slightly improved version. Note how we use \@
to generate unique labels for the strings used. This allows us to not trash the numbered labels, permitting them to be used by code outside of the macro.
.macro print string // define a macro print with argument string
.pushsection .data // temporarily go into .data
str\@: .ascii "\string" // define the string
len\@= .-str\@ // and its length
.popsection // go back to the previous section
mov x0, #1 // print to stdout
ldr x1, =str\@ // print our string
mov x2, #len\@ // of this length
mov x8, #0x40 // with a write system call
svc #0 // do the system call!
.endm
If you want to invoke macros defined in other files, you have to include these file into the other files. This can be done with a .include
directive. For example, suppose you have your macros in a file named macros.inc
, you can include them like this:
.include "macros.inc" // include macros
Refer to the GNU as manual for details.