I have this simple assembly for arm architecture 64 that will print "Hello World\n" from macro I made.
We know macro
is analogy with function
in high level programming.
So I want separate macro to another file. Here is my assembly code I made.
.data
.text
.globl _start
_start:
.macro print string
b 2f // jump to label 2 forward (so it will not bother var)
1: // label 1 (var declaration)
.asciz "\string" // store argumen (string) in memory
len_string = . - 1b // get len of string
udf #0 // if i dont use this it will branch error :(
2: // label 2 (main macro code)
mov x0, #1 // file descriptor (1 means stdout)
ldr x1, =1b // data to print (from label 1 back)
ldr x2, =len_string //len of data
mov x8, #0x40 // kernel convention 0x40 means writing
svc #0 // SuperVisorCall kernel
.endm
print "Hello " // call macro
print "World\n" //call again
_exit: // exit code return 0
mov x0,#0
mov x8,#0x5d
svc #0
To verify it, compile and run that code. Save as hello.s
and run this in aarch64 devices for example android or raspi
as hello.s -o hello.o && ld hello.o -o hello && ./hello
So from above code, I made macro named print
with parameter string
. But as you see I define macro in main program. I expect I can import that print
in another source file. What should I do?
CodePudding user response:
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.