0

I'm new to assembly programming, but I've been figuring a lot out by googling and trial and error. I'm trying to write a simple program that prompts the user to enter a number (with _printf), then reads in and saves that number (_scanf), then prints out a message using the stored number (_printf).

I was able to get the _printf code to work under aarch64 (Apple Silicon) assembly, but no matter what I do, I cannot seem to get _scanf to work. I have looked through the ARM Developer docs, looked at the HelloSilicon github page, and googled for hours, and I cannot come up with anything that works.

In my code (included below), if I comment out the "read_from_keyboard" branch in the following code, the printf functions work just fine. But when I include the "read_from_keyboard" code, I get a "Segmentation fault: 11" error.

Where is my mistake?

.global main
.align 4

main:
    // PRINT MESSAGE
    ADRP    X0, message@PAGE
    ADD X0, X0, message@PAGEOFF
    BL  _printf

//  BL read_from_keyboad

    // READ NUMBER FROM DATA AND MOVE TO STACK FOR PRINTING
    ADRP    X10, num@PAGE
    ADD X10, X10, num@PAGEOFF
    LDR X1, [X10]
    STR X1, [SP, #-16]!

    // LOAD THE PRINTF FORMATTED MESSAGE
    ADRP    X0, output_format@PAGE
    ADD X0, X0, output_format@PAGEOFF

end:
    BL  _printf
    mov X16, #1
    svc 0

read_from_keyboard:
    ADRP    X0, input_format@PAGE
    ADD X0, X0, input_format@PAGEOFF

    ADRP    X11, num@PAGE
    ADD X11, X11, num@PAGEOFF
    BL _scanf

    ret


.data
.balign 4
message:    .asciz "What is your favorite number?\n"
.balign 4
num:    .word 32
.balign 4
input_format:   .asciz "%d"
.balign 4
output_format:  .asciz "Your favorite number is %d \n"
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • Have you tried looking at compiler output for a simple C program that scans into a global variable? https://godbolt.org/ only has Linux (and Windows) compilers installed, and `clang -target arm64-macos-something-something` on Godbolt would still be using Linux headers I think, so best to try on your own desktop with `clang -S`. – Peter Cordes Jan 09 '23 at 08:47
  • Thanks for the suggestion, Mr. Cordes. Yes, I did write a simple c program that took in a simple int using scanf and printed it out using printf. However, I couldn't understand what it was doing in ASM code. It was moving all kinds of things back and forth to the stack with various offsets. I simply could not understand the code. I DID try copying-pasting-and-modifying that code, though, but it didn't work for me. I can share the outputted ASM code if that helps... – Jeffrey Shaffer Jan 09 '23 at 08:58
  • Sounds like you forgot to compile with optimization; use at least `-Og`, or `-O2` to just make the necessary function calls, not waste a bunch of instructions spilling/reloading locals to stack memory. [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) – Peter Cordes Jan 09 '23 at 09:17
  • Thanks again, Mr. Cordes. I tried both -0g and -02 and it's less confusing, but still I can't understand what the compiler is doing. I get things like (str w8, [sp, #12]) but I don't understand the #12 offset. Guess I have a lot more to learn... – Jeffrey Shaffer Jan 09 '23 at 09:28
  • It keeps the stack aligned by 16, so if it has a 32-bit word to store, it's going to pick SP + 0, 4, 8, or 12 if it allocated 16 bytes of space. It doesn't much matter which it picks. (And BTW, "Peter" is fine, or just @PeterCordes like normal to reply to people and make sure Stack Overflow notifies them of the comment if you're not writing under one of their posts.) – Peter Cordes Jan 09 '23 at 09:55
  • @PeterCordes Thanks for the further explanation. That helps a lot. Though I still think it's over my head. I'll keep looking at the outputted code and see if I can make any more heads-or-tales of it. Thank you. – Jeffrey Shaffer Jan 09 '23 at 10:02
  • https://godbolt.org/z/85e48h79b has MacOS and Linux output (with `clang -target arm64-macos` which might not be totally realistic, but maybe). Interestingly they're not the same, IDK why clang for MacOS is storing `x8` to `[sp]` before function calls, as well as passing the first arg in the register. It doesn't do that for Linux, so it's doing something different presumably for a reason, perhaps an Apple customization to the calling convention (https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms). Godbolt is useful for matching source lines to asm. – Peter Cordes Jan 09 '23 at 10:32
  • Oh right, both scanf and printf are variadic. And that doc mentions "Assign the variadic argument to the appropriate number of 8-byte stack slots." Not sure exactly what they mean by that, whether that or some other text says you have to store the arg in that assigned memory as well (or instead) of in a register. – Peter Cordes Jan 09 '23 at 10:35
  • @PeterCordes: Thank you so much for all the help, and for the link to godbolt! – Jeffrey Shaffer Jan 09 '23 at 12:46

1 Answers1

0

On the call to _printf, your variadic arg is in [sp]. On the call to _scanf, you put it in x11. Why? Just do the same str xN, [sp, #-16]! that you do on _printf, that'll fix your segfault.

In addition though, you also need a stack frame for read_from_keyboard. The bl _scanf clobbers x30, so the following ret would just get stuck in an infinite loop.

Fix these two issues and your code works:

.global _main
.align 4

_main:
    // PRINT MESSAGE
    ADRP    X0, message@PAGE
    ADD X0, X0, message@PAGEOFF
    BL  _printf

    BL read_from_keyboard

    // READ NUMBER FROM DATA AND MOVE TO STACK FOR PRINTING
    ADRP    X10, num@PAGE
    ADD X10, X10, num@PAGEOFF
    LDR X1, [X10]
    STR X1, [SP, #-16]!

    // LOAD THE PRINTF FORMATTED MESSAGE
    ADRP    X0, output_format@PAGE
    ADD X0, X0, output_format@PAGEOFF

end:
    BL  _printf
    mov X16, #1
    svc 0

read_from_keyboard:
    STP X29, X30, [SP, #-16]!

    ADRP    X0, input_format@PAGE
    ADD X0, X0, input_format@PAGEOFF
    ADRP    X11, num@PAGE
    ADD X11, X11, num@PAGEOFF
    STR X11, [SP, #-16]!
    BL _scanf
    ADD SP, SP, #16

    LDP X29, X30, [SP], #16
    ret


.data
.balign 4
message:    .asciz "What is your favorite number?\n"
.balign 4
num:    .word 32
.balign 4
input_format:   .asciz "%d"
.balign 4
output_format:  .asciz "Your favorite number is %d \n"
Siguza
  • 21,155
  • 6
  • 52
  • 89
  • So variadic args are passed only on the stack, not in the usual register? Is that what "*Assign the variadic argument to the appropriate number of 8-byte stack slots.*" means in https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms or is there some other clearer statement? – Peter Cordes Jan 09 '23 at 10:37
  • @Siguza, What are X29 and X30 doing in the fixed "read_from_keyboard" function? Oh... is it backing up the LR? – Jeffrey Shaffer Jan 09 '23 at 10:53
  • @PeterCordes Yes, that's what it means. Their description is not good though, and my knowledge comes pretty much exclusively from reverse engineering. I'd phrase it this way: "Variadic arguments are passed on the stack (low address to high), each promoted to 8 bytes. Structs that don't fit into 8 bytes have a pointer passed instead. Regular arguments that don't fit into x0-x7 come before variadic arguments on the stack, naturally aligned (the same way they'd be aligned in a struct)." – Siguza Jan 09 '23 at 10:54
  • @JeffreyShaffer setting up a stack frame. I guess you'd have to `mov x29, sp` too if you wanted it to show up in backtrace, but eh. Technically you only need to preserve `x30` though. – Siguza Jan 09 '23 at 10:56
  • @Siguza: an edit to your answer would be the perfect place to explain that for future readers, unless there's a general duplicate about variadic functions on AArch64 MacOS. It sounds vaguely familiar to me that some Apple ARM64 calling-convention difference may have come up before on SO, perhaps even this fact about passing variadic args on the stack. But I haven't searched for a duplicate yet. – Peter Cordes Jan 09 '23 at 11:01
  • @Siguza, Thank you so very much for the clear explanation and the fix! I'm still very much a ASM newbie, so I'll have to sit and ponder it for a bit to make sure it sinks in. I stared (and experimented) with this scanf problem for over five hours, so my brain's a little wonky at the moment. :-) – Jeffrey Shaffer Jan 09 '23 at 11:17
  • 1
    @PeterCordes I guess this might do as dupe target? https://stackoverflow.com/a/69455440/2302862 – Siguza Jan 11 '23 at 19:58
  • @Siguza: That's probably what was ringing faint bells in my memory; I see I'd already upvoted your answer on it. If missing that pass-on-stack calling convention difference is the major bug in this question, it's a duplicate. If this has anything else worth pointing out, the answer to this question can link that for details about the calling convention. – Peter Cordes Jan 12 '23 at 00:55
  • @PeterCordes dupe is probably good. You've got the `[assembly]` hammer, so... all yours. – Siguza Jan 12 '23 at 11:41
  • 1
    Thanks. Since you'd already answered the question, I didn't want to take the time to read all the details of it and your answer, instead wanted your opinion before dup-hammering. – Peter Cordes Jan 12 '23 at 11:43
  • @PeterCordes: Hello again. I was wondering if you really feel my question is a dup. I understand, now, that printf and scanf work in almost the same way. However, I feel it's a legitimate question a newbie (like myself) might ask. I would like to ask you to reconsider NOT closing this question. The main reason is if a newbie searches for "scanf on aarch64", they will not find the other question. However, I will leave it up to your discretion. Thank you so much for helping ME understand the problem! – Jeffrey Shaffer Jan 12 '23 at 23:20
  • @JeffreyShaffer The point of duplicates is that people can find the other post by finding this post. :) – Siguza Jan 12 '23 at 23:25
  • @Siguza: Ah. I thought it meant that this post is no longer searchable. *scratches head* My apologies. I guess the message at the top asking me if I want to delete my question kinda threw me off. Thank you for explaining. I understand better now. Thank you! – Jeffrey Shaffer Jan 12 '23 at 23:31
  • @JeffreyShaffer: That is a valid concern; not-logged-in users get auto-redirected to the duplicate when there's only one duplicate. A lot of SO's traffic is not-logged-in users coming from google searches. It *is* the same basic problem, of calling convention, though, and with the existing answer here not explaining the reasons why the arg goes there, it's maybe not super helpful to beginners without those links to the docs. And this question will show up in the list of linked questions for printf question – Peter Cordes Jan 13 '23 at 01:15
  • @JeffreyShaffer: (My earlier comments were worded to encourage Siguza to improve this answer with a link to docs or the other question, and/or an explanation, in which case it could maybe be useful as a tutorial on its own. But they chose not to do that.) – Peter Cordes Jan 13 '23 at 01:17
  • @PeterCordes: Thank you Mr. Cordes for your follow-up explanation. I appreciate it very much. :-) – Jeffrey Shaffer Jan 13 '23 at 05:17