1

I'm slowly getting used to aarch64 by translating trivial C programs to asm and learn on the go but is stuck here:

/*
int power(int i): a function to calculate exponential power
*/
.global     power       //function prologue to declare it as global
.p2align    2           // align for better access - align 2^2 bytes
power:                  // a label to start the proc
    .cfi_startproc      // declare proc start
    cmp     w1, #0      // check if base is 0
    b.eq    end         // if true then just return 0
    mov     x8, x1      // i = exp, x8 is i
    mov     x9, x0      // use x0 as result, so x9 is base
    subs    x8, x8, #1  // since we count down to 0, sub 1
loop:
    mul     w0, w0, w9  // result = result * base (w0 is part of x0)
    subs    w8, w8, #1  // i = i - 1 (w0 is part of x8)
    b.ne    loop        // if i != 0 then loop
end:
    ret                 // return to caller
    .cfi_endproc        // end proc

/**
Main function
*/
.global     _main      // Provide program starting address to linker
.align      4           // Make sure everything is aligned properly

// Setup the parameters to print hello world
// and then call the Kernel to do it.
_main:
    .cfi_startproc
    .cfi_def_cfa_offset     16

// print the power
    mov     x0, #2
    mov     x1, #3
    bl      power
    bl      print_number
    bl      println // <--------- omit this line and the output will be gone

// exit
    mov     X0, #0          // return 0
    mov     X16, #1         // syscall 1 terminate program
    svc     #0x80           // call kernel
    .cfi_endproc

/* static functions*/
.p2align    2
print_number:
    .cfi_startproc
    // stack stuff
    sub     sp, sp, #32             // substract 32 from sp
    stp     x29, x30, [sp, #16]     // store ?
    add     x29, sp, #16
    // call frame stuff
    .cfi_def_cfa    w29, 16
    .cfi_offset     w30, -8
    .cfi_offset     w29, -16
    // grab the args
    mov     x8, x0
    str     x8, [sp]
    // load string using two instructions
    // load the 4kb page from this instruction
    // can either use LDR or STR to read or write address inside
    // this page or ADD to calculate the final address using
    // the last 12 bit denoted by @PAGEOFF
    adrp    x0, format_string@PAGE
    add     x0, x0, format_string@PAGEOFF
    bl      _printf

    // refresh stack stuff
    ldp     x29,x30, [sp, #16]
    add     sp, sp, #32
    ret
    .cfi_endproc

// println: print a newline
.p2align    2
println:
    .cfi_startproc
    //stackstuff
    sub     sp, sp, #32         // sp = sp - 32
    stp     x29, x30, [sp, #16] // store stuff
    add     x29, sp, #16        // x29 = sp + 16
    //callframe
    .cfi_def_cfa    w29, 16
    .cfi_offset     w30, -8
    .cfi_offset     w29, -16

    mov     x8, x0
    str     x8, [sp]
    adrp   x0, newline@PAGE
    add     x0, x0, newline@PAGEOFF
    bl      _printf
    
    //refresh stack
    ldp x29, x30, [sp, #16]
    add     sp, sp, #32
    ret
    .cfi_endproc

/** Data */
.data
format_string:      .asciz  "Number is: %d"
newline:            .asciz  "\n"

basically I will get no output at all if I omit line 38. And when compile a C file to test it out, my printf turns out like this:

Number is: 3%

The % is not supposed to be there. I suspect that I simplified the call too much by taking out some instructions but I'm not familiar enough with aarch64 to figure out what's wrong. Can you help point me to the right direction?

Thanks,

stanle
  • 307
  • 1
  • 9
  • 1
    IDK why you'd be getting a trailing `%`, but getting no output is normal if you make an exit system call without flushing stdio buffers. [Using printf in assembly leads to empty output when piping, but works on the terminal](https://stackoverflow.com/q/38379553) is the same problem but with a newline with full-buffered. Use `bl _exit` in code that uses `printf` or other C library functions. What you're doing is the equivalent of C `_exit(0)` which skips all atexit functions. – Peter Cordes Dec 05 '22 at 22:06
  • 3
    zsh does some trickery that adds a `%` maker after a command's output if the output doesn't end with a newline. It might be that the newline you're trying to print never gets flushed. – Gordon Davisson Dec 05 '22 at 22:29
  • Thank you very much guys! I had a hunch that the shell have something to do with it but not sure. And didn't know about the proper exit process, that's new! – stanle Dec 06 '22 at 00:19
  • @GordonDavisson: I don't know why / how the stdio buffer would get flushed before `_exit` without printing a newline (since this isn't printing like 4 or 8 KiB of data that would fill it). Unless that output with a trailing `%` isn't from this program at all, but from a separate C program? This one contains a `main`, so doesn't need a C caller to test the asm functions, so I guess that's probably what was meant. Very confusing since there's no [mcve] for that part of the question. But I assume it was equivalent to `main(){ printf("Number is: %d", 3); }` which flushes without a newline. – Peter Cordes Dec 06 '22 at 03:39
  • @PeterCordes See [this Unix&Linux question](https://unix.stackexchange.com/questions/167582/why-zsh-ends-a-line-with-a-highlighted-percent-symbol) (especially Stéphane Chazelas' answer) for more info about how zsh adds that percent sign to improperly terminated output. – Gordon Davisson Dec 06 '22 at 05:22
  • 1
    I think it's reasonable to close this as a dupe of [Using printf in assembly leads to empty output when piping, but works on the terminal](https://stackoverflow.com/questions/38379553/using-printf-in-assembly-leads-to-empty-output-when-piping-but-works-on-the-ter) – Siguza Dec 07 '22 at 15:14

0 Answers0