Home > Software engineering >  Modular programming aarch64 assembly
Modular programming aarch64 assembly

Time:06-03

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.

  • Related