1

this is a snippet of a coding project I'm working on that's having a few issues I'm struggling to understand. The goal of this project is to learn how to use Push/Pop to transfer data between the main function and a subroutine. Everything seems to be working alright, but the code exits prematurely after the print statement in 'and_op' right before moving onto 'end_Prompt'. Any ideas on why this is failing would be very helpful!

Here's the code snippet (missing all functions tied to ORR/EOR/BIC although they are written similarly to AND)

.equ READERROR, 0 @Used to check for scanf read error. 

.global main 
main:        @Must use this label where to start executing the code. 

@*******************
welcome:
@*******************
    ldr r0, =welcomePrompt  @print first prompt of script
    bl  printf    

@*******************
get_hex1:
@*******************
@Get user input for Hexadecimal 1

    ldr r0, =opp1InputPrompt  @print prompt for second input
    bl  printf 

    ldr r0, =hexInputPattern @ Setup to read in one number.
    ldr r1, =hexInput1       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =hexInput1       @ Have to reload r1 because it gets wiped out. 
    ldr r4, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 
    cmp r0, #1               @Check that hex number entered is correct
    beq get_hex2
     
    b readerror              @branch to read error if the input is not correct

@*******************
get_hex2:
@*******************
@Get user input for Hexadecimal 2

    ldr r0, =opp2InputPrompt  @print prompt for second input
    bl  printf 

    ldr r0, =hexInputPattern @ Setup to read in one number.
    ldr r1, =hexInput2       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =hexInput2       @ Have to reload r1 because it gets wiped out. 
    ldr r5, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 
    cmp r0, #1               @Check that hex number entered is correct
    beq get_operation
     
    b readerror              @branch to read error if the input is not correct

@*******************
get_operation:
@*******************
@Get user input for Hexadecimal 2

    ldr r0, =opInputPrompt   @print prompt for second input
    bl  printf 

    ldr r0, =opInputPattern  @ Setup to read in one number.
    ldr r1, =operation       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =operation       @ Have to reload r1 because it gets wiped out. 
    ldr r1, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 

    cmp r1, #1               @ Branch to appropriate operation
    beq and_op

    @cmp r1, #2               @ Branch to appropriate operation
    @beq orr_op

    @cmp r1, #3               @ Branch to appropriate operation
    @beq eor_op

    @cmp r1, #4               @ Branch to appropriate operation
    @beq bic_op
     
    b readerror              @branch to read error if the input is not correct

@***********
and_op:
@***********
@complete AND operation
@r4 = opperand1
@r5 = opperand2           

   push {r4, r5}        @push user input

   bl _AND              @call subroutine

   pop {r6}             @restore final value

   ldr r0, =andOutput   @set string to be printed
   mov r1, r4           @temp storage of opp1 for print
   mov r2, r5           @temp storage of opp2 for print
   mov r3, r6           @temp storage of output for print

   bl  printf  

   b end_Prompt         @branch to end prompt
@***********
end_Prompt:
@***********
@Prompt user to complete another calculation

    ldr r0, =endPrompt        @print end prompt
    bl  printf  

    ldr r0, =charInputPattern @ Setup to read in one number.
    ldr r1, =cont             @ load r1 with the address of where the
                              @ input value will be stored. 
    bl  scanf                 @ scan the keyboard.

    cmp r0, #READERROR        @ Check for a read error.
    beq readerror             @ If there was a read error go handle it. 

    ldr r1, =cont             @ Have to reload r1 because it gets wiped out. 
    ldr r1, [r1]              @ Read the contents of intInput and store in r1 so that
                              @ it can be printed. 

    cmp r1, #'y'              @Branch to start if the user says yes
    beq welcome

    b myexit                  @Branch to exit if the user says no     
 
@*******************
myexit:
@*******************
@ End of my code. Force the exit and return control to OS

   mov r7, #0x01 @ SVC call to exit
   svc 0         @ Make the system call.

@***********
readerror:
@***********
@ Got a read error from the scanf routine. Clear out the input buffer then
@ branch back for the user to enter a value. 
@ Since an invalid entry was made we now have to clear out the input buffer by
@ reading with this format %[^\n] which will read the buffer until the user 
@ presses the CR. 
    
   ldr r0, =strInputPattern
   ldr r1, =strInputError   @ Put address into r1 for read.
   bl scanf                 @ scan the keyboard.
@  Not going to do anything with the input. This just cleans up the input buffer.  
@  The input buffer should now be clear so get another input.

   ldr r0, =readerErrorOutput @Prompts user for a valid input
   bl printf                  @ Call the C printf to display input prompt. 

   b get_hex1

@***********
_AND:
@***********
@AND subroutine

   pop {r10, r11}            @restore user input to the following registers (r10 = r4 & r11 = r5)

   AND r0, r10, r11          @preform operation

   push {r0}                 @store final value

   bx lr                     @branch back
.data

@ Declare the strings and data needed

.balign 4
welcomePrompt: .asciz "Welcome!\nTo properly use this program please follow the instructions... \n"

.balign 4
opp1InputPrompt: .asciz "\nPlease enter a 32 bit (8 digit) hexadecimal digit: "

.balign 4
opp2InputPrompt: .asciz "Please enter a second 32 bit (8 digit) hexadecimal digit: "

.balign 4
hexInputPrompt: .asciz "Please enter a second 8 digit (32 bit) hexadecimal digit: "

.balign 4
opInputPrompt: .asciz "\nPlease choose one of the following options:\n 1: AND \n 2: OR \n 3: XOR \n 4: BIC \n\n"

.balign 4
readerErrorOutput: .asciz "Your Input was invalid, please try again...\n\n"


.balign 4
andOutput: .asciz "\n%x AND %x = %x\n"

.balign 4
orrOutput: .asciz "\n%x OR %x = %x\n"

.balign 4
eorOutput: .asciz "\n%x XOR %x = %x\n"

.balign 4
bicOutput: .asciz "\n%x BIC %x = %x\n"

.balign 4
endPrompt: .asciz "\nWould you like to perform another calculation (y/n)? "

@ Format pattern for scanf call.

.balign 4
hexInputPattern: .asciz "%x"  @ hex format for read. 

.balign 4
opInputPattern: .asciz "%d"  @ integer format for read. 

.balign 4
charInputPattern: .asciz "%c"  @ char format for read. 

.balign 4
strInputPattern: .asciz "%[^\n]" @ Used to clear the input buffer for invalid input. 

.balign 4
strInputError: .skip 100*4  @ User to clear the input buffer for invalid input. 

.balign 4
hexInput1: .word 0   @ Location used to store the user input. 

.balign 4
hexInput2: .word 0   @ Location used to store the user input. 

.balign 4
operation: .word 0   @ Location used to store the user input.

.balign 4
cont: .word 0   @ Location used to store the user input.

.global printf

.global scanf

@ End of code and end of file. Leave a blank line after this.

The code spits out the following:

Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: F0F0F0F0
Please enter a second 32 bit (8 digit) hexadecimal digit: AFAFAFAF

Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC

1

f0f0f0f0 AND afafafaf = a0a0a0a0

1
  • 1
    I don't see you saving `lr` anywhere. If you don't save `lr` on the stack before calling a function, you won't be able to return properly. – fuz Feb 17 '21 at 15:55
  • Could you please augment your code with the missing code for `eor_op` and `bic_op` ? your program does not assemble without them. This would facilitate providing you with an answer. – Frant Feb 17 '21 at 15:56
  • @Frant I've updated the code so that now the only option for the operation is AND (1) – Taylor Watson Feb 17 '21 at 16:03
  • @fuz do you know how I could do this properly? I've tried it several ways, including pushing it alongside r4/r5 and popping it right before the 'bx lr' call but always get a segmentation fault? – Taylor Watson Feb 17 '21 at 16:13
  • @TaylorWatson Please show your attempts so I can understand what exactly you did. Your code looks really weird on second thought. – fuz Feb 17 '21 at 16:18
  • @fuz I've updated the code with my most recent attempt to save lr to the stack! Sorry I know the code is spaghetti, this isn't how I would personally like to approach the problem but it is how the exercise asks for it. – Taylor Watson Feb 17 '21 at 16:23
  • @TaylorWatson popping into `pc` will return to the caller immediately. Are you sure this is what you want? What confuses me about your code is that your highlighted labels `welcome`, `get_hex1`, ... are not used as function entry points but rather just labels. This is very misleading. Why do you highlight the labels like this if they are just ordinary labels? – fuz Feb 17 '21 at 16:35
  • @fuz I picked that up syntax up from a tutorial, I'm fairly new to ARM (being as my degrees have pushed me more towards Java and C++ so my syntax here is not up to par as of yet). No that's not quite what I want, I want to print the answer "f0f0f0f0 AND afafafaf = a0a0a0a0" and then jump to 'end_Prompt' so that I can prompt the user to perform a second calculation. I've been able to print the answer, but something is prematurely terminiating the program right before the 'b end_Prompt' call – Taylor Watson Feb 17 '21 at 16:40
  • @TaylorWatson Have you tried using a debugger to find out what happens? – fuz Feb 17 '21 at 16:44
  • @fuz yes with little luck – Taylor Watson Feb 17 '21 at 17:09
  • @Taylor Watson: did I miss any error messages you could have mentioned ? I am getting a `Segmentation fault` error message when running your program with parameters `F0F0F0F0`, `AFAFAFAF` and `1: AND` on a `Cortex-A7` `Linux` system. By the way, what is the platform you are running your program on? not that is does really matter, just would like to know. – Frant Feb 17 '21 at 18:28
  • 2
    popping into `pc` is probably not what you want to do after returning from a function. That will cause the program to jump to whatever was on the stack. Also, because _AND is a leaf function (does not call any subroutines), it does not need to preserve `lr`. In general, while within a function, if your stack pointer is not the same on the way out as it was on the way in, you are really REALLY asking for trouble. – Michael Dorgan Feb 17 '21 at 18:28
  • @Frant I've returned the code to what it was before (no saving of lr on the stack) so the segmentation fault should be no more. I'm currently running this on a Raspberry Pi Zero with the following commands: (as -o Script.o Script.s) (gcc -o Script Script.o) (./Script ;echo $?) – Taylor Watson Feb 17 '21 at 19:58

1 Answers1

2

Your main code seems to be working well overall from what I have seen, but there are several changes you may want to make - but first of all:

Do not make any changes to your program, and create a response file named input.txt with the following content:

F0F0F0F0
AFAFAFAF
1y
AFAFAFAF
F0F0F0F0
1n

Execute the program using the content of the file as an input:

./op < input.txt 
Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: Please enter a second 32 bit (8 digit) hexadecimal digit: 
Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 


f0f0f0f0 AND afafafaf = a0a0a0a0

Would you like to perform another calculation (y/n)? Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: Please enter a second 32 bit (8 digit) hexadecimal digit: 
Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 


afafafaf AND f0f0f0f0 = a0a0a0a0

Note that the second Would you like to perform another calculation (y/n)? message is lost: since you are forcing the exit to the operating system, the latter does not have any chance to flush the output buffer. That means some messages you program actually displayed may be lost, which is a bad thing in general and more specifically when debugging.

In C/C++, you could call fflush(stdout) prior to call _exit(), or better just call libc exit() which flushes all output streams like fflush(NULL) before exiting. Mixing stdio with raw exit system calls is usually something to avoid. For the sake of simplicity, I called fflush(NULL) (see fflush(3)) at the very beginning of myexit():

   mov r0, #0                 @ call fflush(NULL)
   bl fflush

The modified version of the program would then display:

./op < input.txt 
Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: Please enter a second 32 bit (8 digit) hexadecimal digit: 
Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 


f0f0f0f0 AND afafafaf = a0a0a0a0

Would you like to perform another calculation (y/n)? Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: Please enter a second 32 bit (8 digit) hexadecimal digit: 
Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 


afafafaf AND f0f0f0f0 = a0a0a0a0

Would you like to perform another calculation (y/n)? 

The last message is not being lost anymore because we are now calling fflush() before exiting.

I would now suggest to replace:

ldr r1, =cont             @ Have to reload r1 because it gets wiped out.
ldr r1, [r1]              @ Read the contents of intInput and store in r1 so that
                          @ it can be printed.
cmp r1, #'y'              @Branch to start if the user says yes

by

ldr r1, =cont             @ Have to reload r1 because it gets wiped out.
ldrb r1, [r1]             @ Read the contents of intInput and store in r1 so that
                          @ it can be printed.
cmp r1, #'y'              @Branch to start if the user says yes

The reason why is that you are loading a full 4 bytes word into r1 from address cont, and then comparing with 0x00000079 ('y'), and the comparison may therefore fail depending on the values of the 3 remaining bytes.

But this is not why the program is exiting prematurely when the user is interacting with it normally: for some reason, the call to scanf() for reading a yes or no after line #123 does result in a line feed (\n/0x0a) to reside at the first byte of cont:

# display the address of cont after ldr r1, =cont
 (gdb) p/x $r1
$4 = 0x7e66c
# dump memory at cont before calling scanf()
(gdb) x/4b 0x7e66c
0x7e66c:        0x00    0x00    0x00    0x00

# dump memory at cont after returning from scanf()
(gdb) x/4xb 0x7e66c
0x7e66c:        0x0a    0x00    0x00    0x00

The comparison with 'y' / 0x79 cannot therefore succeed, and your program is exiting.

The exact reason why is not clear to me right now and should probably require to ask a specific question.

In between, if you use getchar() instead of scanf(), the program starts working - we need to skip '\n' characters that may still be in the input buffer (this may well be related to the scanf() issue described above):

@Prompt user to complete another calculation

    ldr r0, =endPrompt        @print end prompt
    bl  printf

loop:
    bl  getchar               @ scan the keyboard.

    cmp r0, #'\0'             @ Check for a read error.
    beq readerror             @ If there was a read error go handle it.

    cmp r0, #'\n'             @Branch to start if we did read a line feed
    beq  loop

    cmp r0, #'y'              @Branch to start if the user says yes
    beq welcome

The modified version is now working as you expected:

./op
Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: 00000000
Please enter a second 32 bit (8 digit) hexadecimal digit: 00000000

Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 

1

0 AND 0 = 0

Would you like to perform another calculation (y/n)? y
Welcome!
To properly use this program please follow the instructions... 

Please enter a 32 bit (8 digit) hexadecimal digit: 00000000
Please enter a second 32 bit (8 digit) hexadecimal digit: 00000000

Please choose one of the following options:
 1: AND 
 2: OR 
 3: XOR 
 4: BIC 

1

0 AND 0 = 0

Would you like to perform another calculation (y/n)? n

You will have to dig deeper into scanf() and buffered input/outputs in order to find out why it is not working as expected. The answer is likely obvious/well known, but I decided answering it was out of the scope of this answer for today.

The modified version of your program is provided hereafter - note that a symbol used in assembly program is reputed global when using GNU AS/GNU LD, there is there for no need to use .global statements for printf(), scanf() or getchar(), and .extern should be used instead- see comment from Peter Cordes hereafter.

You can of course compare your version and the modified one using diff on Linux, or with meld on Linux/Windows, or with Winmerge on Windows.


.equ READERROR, 0 @Used to check for scanf read error. 

.global main 
main:        @Must use this label where to start executing the code. 

@*******************
welcome:
@*******************
    ldr r0, =welcomePrompt  @print first prompt of script
    bl  printf    

@*******************
get_hex1:
@*******************
@Get user input for Hexadecimal 1

    ldr r0, =opp1InputPrompt  @print prompt for second input
    bl  printf 

    ldr r0, =hexInputPattern @ Setup to read in one number.
    ldr r1, =hexInput1       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =hexInput1       @ Have to reload r1 because it gets wiped out. 
    ldr r4, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 
    cmp r0, #1               @Check that hex number entered is correct
    beq get_hex2
     
    b readerror              @branch to read error if the input is not correct

@*******************
get_hex2:
@*******************
@Get user input for Hexadecimal 2

    ldr r0, =opp2InputPrompt  @print prompt for second input
    bl  printf 

    ldr r0, =hexInputPattern @ Setup to read in one number.
    ldr r1, =hexInput2       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =hexInput2       @ Have to reload r1 because it gets wiped out. 
    ldr r5, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 
    cmp r0, #1               @Check that hex number entered is correct
    beq get_operation
     
    b readerror              @branch to read error if the input is not correct

@*******************
get_operation:
@*******************
@Get user input for Hexadecimal 2

    ldr r0, =opInputPrompt   @print prompt for second input
    bl  printf 

    ldr r0, =opInputPattern  @ Setup to read in one number.
    ldr r1, =operation       @ load r1 with the address of where the
                             @ input value will be stored. 
    bl  scanf                @ scan the keyboard.

    cmp r0, #READERROR       @ Check for a read error.
    beq readerror            @ If there was a read error go handle it. 

    ldr r1, =operation       @ Have to reload r1 because it gets wiped out. 
    ldr r1, [r1]             @ Read the contents of intInput and store in r1 so that
                             @ it can be printed. 

    cmp r1, #1               @ Branch to appropriate operation
    beq and_op

    @cmp r1, #2               @ Branch to appropriate operation
    @beq orr_op

    @cmp r1, #3               @ Branch to appropriate operation
    @beq eor_op

    @cmp r1, #4               @ Branch to appropriate operation
    @beq bic_op
     
    b readerror              @branch to read error if the input is not correct

@***********
and_op:
@***********
@complete AND operation
@r4 = opperand1
@r5 = opperand2           

   push {r4, r5}        @push user input

   bl _AND              @call subroutine

   pop {r6}             @restore final value

   ldr r0, =andOutput   @set string to be printed
   mov r1, r4           @temp storage of opp1 for print
   mov r2, r5           @temp storage of opp2 for print
   mov r3, r6           @temp storage of output for print

   bl  printf  

   b end_Prompt         @branch to end prompt
@***********
end_Prompt:
@***********
@Prompt user to complete another calculation

    ldr r0, =endPrompt        @print end prompt
    bl  printf  

loop:
    bl  getchar               @ scan the keyboard.

    cmp r0, #'\0'             @ Check for a read error.
    beq readerror             @ If there was a read error go handle it. 

    cmp r0, #'\n'             @Branch to start if we did read a line feed
    beq  loop

    cmp r0, #'y'              @Branch to start if the user says yes
    beq welcome

    b myexit                  @Branch to exit if the user says no     
 
@*******************
myexit:
@*******************
@ Flush all output buffers, so that no messages will be lost
   mov r0, #0                 @ call fflush(NULL)
   bl fflush

@ End of my code. Force the exit and return control to OS

   mov r7, #0x01 @ SVC call to exit
   svc 0         @ Make the system call.

@***********
readerror:
@***********
@ Got a read error from the scanf routine. Clear out the input buffer then
@ branch back for the user to enter a value. 
@ Since an invalid entry was made we now have to clear out the input buffer by
@ reading with this format %[^\n] which will read the buffer until the user 
@ presses the CR. 
    
   ldr r0, =strInputPattern
   ldr r1, =strInputError   @ Put address into r1 for read.
   bl scanf                 @ scan the keyboard.
@  Not going to do anything with the input. This just cleans up the input buffer.  
@  The input buffer should now be clear so get another input.

   ldr r0, =readerErrorOutput @Prompts user for a valid input
   bl printf                  @ Call the C printf to display input prompt. 

   b get_hex1

@***********
_AND:
@***********
@AND subroutine

   pop {r10, r11}            @restore user input to the following registers (r10 = r4 & r11 = r5)

   AND r0, r10, r11          @preform operation

   push {r0}                 @store final value

   bx lr                     @branch back
.data

@ Declare the strings and data needed

.balign 4
welcomePrompt: .asciz "Welcome!\nTo properly use this program please follow the instructions... \n"

.balign 4
opp1InputPrompt: .asciz "\nPlease enter a 32 bit (8 digit) hexadecimal digit: "

.balign 4
opp2InputPrompt: .asciz "Please enter a second 32 bit (8 digit) hexadecimal digit: "

.balign 4
hexInputPrompt: .asciz "Please enter a second 8 digit (32 bit) hexadecimal digit: "

.balign 4
opInputPrompt: .asciz "\nPlease choose one of the following options:\n 1: AND \n 2: OR \n 3: XOR \n 4: BIC \n\n"

.balign 4
readerErrorOutput: .asciz "Your Input was invalid, please try again...\n\n"


.balign 4
andOutput: .asciz "\n%x AND %x = %x\n"

.balign 4
orrOutput: .asciz "\n%x OR %x = %x\n"

.balign 4
eorOutput: .asciz "\n%x XOR %x = %x\n"

.balign 4
bicOutput: .asciz "\n%x BIC %x = %x\n"

.balign 4
endPrompt: .asciz "\nWould you like to perform another calculation (y/n)? "

@ Format pattern for scanf call.

.balign 4
hexInputPattern: .asciz "%x"  @ hex format for read. 

.balign 4
opInputPattern: .asciz "%d"  @ integer format for read. 

.balign 4
charInputPattern: .asciz "%c"  @ char format for read. 

.balign 4
strInputPattern: .asciz "%[^\n]" @ Used to clear the input buffer for invalid input. 

.balign 4
strInputError: .skip 100*4  @ User to clear the input buffer for invalid input. 

.balign 4
hexInput1: .word 0   @ Location used to store the user input. 

.balign 4
hexInput2: .word 0   @ Location used to store the user input. 

.balign 4
operation: .word 0   @ Location used to store the user input.

.balign 4
cont: .word 0   @ Location used to store the user input.
    
@ End of code and end of file. Leave a blank line after this.

Update

As suggested by Peter Cordes in a comment hereafter, replacing

charInputPattern: .asciz "%c" 

by:

charInputPattern: .asciz " %c" 

solves the scanf()-related issue.

Frant
  • 5,382
  • 1
  • 16
  • 22
  • scanf %c doesn't skip whitespace, unlike other conversions. As explained in [scanf() leaves the new line char in the buffer](https://stackoverflow.com/q/5240789) , use `" %c"` to explicitly (via the space) tell scanf to skip leading whitespace before processing the `%c` conversion. – Peter Cordes Feb 18 '21 at 02:19
  • `.global` isn't just unnecessary, it's the wrong directive. It affects the nature of your *own* symbols defined in this file (with labels or `.set`). `.extern scanf` is what you would use if it wasn't already the default for undeclared symbols. For compatibility, GAS ignores it so you actually can use it if you want. – Peter Cordes Feb 18 '21 at 02:22
  • Yes, thanks - I am usually using `.extern` myself, but did not even notice - I updated the answer accordingly. – Frant Feb 18 '21 at 02:28
  • Good to know, never had to use the `" %c"` format before. – Frant Feb 18 '21 at 02:29
  • Hi @Taylor Watson if this or any answer has solved your question please consider [accepting it](https://meta.stackexchange.com/q/5234/179419) by clicking the check-mark. This indicates to the wider community that you've found a solution and gives some reputation to both the answerer and yourself. There is of course no obligation to do this. – Frant Feb 19 '21 at 13:51