Reputation: 386
I'm doing what should be a fairly simple exercise to print the number of arguments passed to a program, and then print the first argument as a string. I get the number of the arguments, but the printing seems to just give random memory locations. I run the program like this:
argdisplay ab ac ad
which correctly prints the number of arguments, but not the argument string.
Argc: 4
Argc: �
My program is:
.data
stringD: .asciz "Argc: %d\n"
stringS: .asciz "Argc: %s\n"
.text
.global main
main:
no_args:
push {ip, lr}
mov r1, r0
ldr r0, =stringD
bl printf
arg_string:
ldr r1, [sp, #8]
ldr r0, =stringS
bl printf
pop {ip, pc}
.global printf
.end
My understanding of the argv is that it is an array separated by four bytes, so I put in #8 to try and print the second argument.
Upvotes: 1
Views: 477
Reputation: 57922
The argv
argument isn't passed on the stack. It's the second argument to your main
function, so it is in r1
on entry to main. So:
You need to save it (or a pointer to the desired string) before overwriting r1
for the call to printf
You need to save this in a call-preserved register (r4-r8, r10
) (or on the stack, but that is less convenient)
You need to save and restore that register at the beginning and end of your function
Here is a fixed version. Since argv
from r1
is a pointer to the zeroth pointer in the argv
array, then 4 bytes later will be the pointer to the first argument argv[1]
, the one you want to print. We save this in r4
around the first call to print
, and add r4
to the list of registers to be pushed and popped.
main:
push {r4, ip, lr}
ldr r4, [r1, #4]
mov r1, r0
ldr r0, =stringD
bl printf
mov r1, r4
ldr r0, =stringS
bl printf
mov r0, #0 // return 0
pop {r4, ip, pc}
A couple other notes:
We zero out r0
on exit so that our main
function returns 0 indicating success. Returning a random value is annoying to users who want to test the exit code.
You probably don't want to label both outputs as Argc
.
.global printf
at the end should probably be .extern printf
. You aren't defining printf
in this module, you are telling the assembler that it will be found in another module. Also it would make more sense to put this directive before the calls to printf
(maybe at the top of the file).
The labels no_args
and arg_string
aren't used as branch targets anywhere, so they have no effect and might as well be comments (in which case they could be more descriptive).
.end
is unnecessary, unless you are going to put additional stuff after it that you want the assembler to ignore. It's more likely to be an annoyance, if you add more actual code at the end of the file and then wonder why the assembler isn't assembling it.
Since you don't intend to write to stringD
or stringS
, put them in the read-only data section. Replace .data
with .section .rodata
.
It would be nice to handle gracefully the case when no arguments are passed. Currently it sort of works, since if no arguments are present then argv[1]
is null, and printf
on Linux accepts a null pointer with %s
and prints the string (null)
. But you could think about nicer options.
Upvotes: 1