arm汇编语言访问ARGV
Access ARGV in arm assembly language
我正在做一个应该相当简单的练习来打印传递给程序的参数数量,然后将第一个参数打印为字符串。我得到了参数的数量,但打印似乎只是给出了随机的内存位置。我运行程序是这样的:
argdisplay ab ac ad
它正确地打印了参数的数量,但不是参数字符串。
Argc: 4
Argc: �
我的程序是:
.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
我对 argv 的理解是它是一个由四个字节分隔的数组,所以我输入 #8 来尝试打印第二个参数。
argv
参数未在堆栈上传递。它是 main
函数的第二个参数,所以它在 r1
中进入 main。所以:
您需要在覆盖 r1
以调用 printf
之前保存它(或指向所需字符串的指针)
你需要将其保存在调用保留寄存器(r4-r8, r10
)(或堆栈上,但不太方便)
您需要在函数的开始和结束时保存和恢复该寄存器
这是一个固定版本。由于 r1
中的 argv
是指向 argv
数组中第 0 个指针的指针,因此 4 个字节后将是指向第一个参数 argv[1]
的指针,即您想要的打印。我们在第一次调用 print
时将其保存在 r4
中,并将 r4
添加到要压入和弹出的寄存器列表中。
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}
一些其他注意事项:
我们在退出时将 r0
清零,这样我们的 main
函数 returns 0 表示成功。返回一个随机值对于想要测试退出代码的用户来说很烦人。
您可能不想将两个输出都标记为 Argc
。
最后的.global printf
应该是.extern printf
。您没有在这个模块中定义 printf
,而是告诉汇编程序它将在另一个模块中找到。此外,将此指令 放在 对 printf
的调用之前(可能在文件顶部)也更有意义。
标签 no_args
和 arg_string
没有在任何地方用作分支目标,因此它们没有任何效果,也可能是注释(在这种情况下它们可能是更具描述性)。
.end
是不必要的,除非您要在它后面添加您希望汇编程序忽略的其他内容。如果您在文件末尾添加了更多实际代码,然后想知道为什么汇编器不汇编它,则更有可能令人烦恼。
由于您不打算写入stringD
或stringS
,因此将它们放在只读数据部分。将 .data
替换为 .section .rodata
.
没有传递参数的情况下优雅地处理会很好。目前它有点工作,因为如果不存在参数则 argv[1]
为空,并且 Linux 上的 printf
接受带有 %s
的空指针并打印字符串 (null)
.但你可以考虑更好的选择。
我正在做一个应该相当简单的练习来打印传递给程序的参数数量,然后将第一个参数打印为字符串。我得到了参数的数量,但打印似乎只是给出了随机的内存位置。我运行程序是这样的:
argdisplay ab ac ad
它正确地打印了参数的数量,但不是参数字符串。
Argc: 4
Argc: �
我的程序是:
.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
我对 argv 的理解是它是一个由四个字节分隔的数组,所以我输入 #8 来尝试打印第二个参数。
argv
参数未在堆栈上传递。它是 main
函数的第二个参数,所以它在 r1
中进入 main。所以:
您需要在覆盖
之前保存它(或指向所需字符串的指针)r1
以调用printf
你需要将其保存在调用保留寄存器(
r4-r8, r10
)(或堆栈上,但不太方便)您需要在函数的开始和结束时保存和恢复该寄存器
这是一个固定版本。由于 r1
中的 argv
是指向 argv
数组中第 0 个指针的指针,因此 4 个字节后将是指向第一个参数 argv[1]
的指针,即您想要的打印。我们在第一次调用 print
时将其保存在 r4
中,并将 r4
添加到要压入和弹出的寄存器列表中。
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}
一些其他注意事项:
我们在退出时将
r0
清零,这样我们的main
函数 returns 0 表示成功。返回一个随机值对于想要测试退出代码的用户来说很烦人。您可能不想将两个输出都标记为
Argc
。
最后的.global printf
应该是.extern printf
。您没有在这个模块中定义printf
,而是告诉汇编程序它将在另一个模块中找到。此外,将此指令 放在 对printf
的调用之前(可能在文件顶部)也更有意义。标签
no_args
和arg_string
没有在任何地方用作分支目标,因此它们没有任何效果,也可能是注释(在这种情况下它们可能是更具描述性)。.end
是不必要的,除非您要在它后面添加您希望汇编程序忽略的其他内容。如果您在文件末尾添加了更多实际代码,然后想知道为什么汇编器不汇编它,则更有可能令人烦恼。由于您不打算写入
stringD
或stringS
,因此将它们放在只读数据部分。将.data
替换为.section .rodata
.没有传递参数的情况下优雅地处理会很好。目前它有点工作,因为如果不存在参数则
argv[1]
为空,并且 Linux 上的printf
接受带有%s
的空指针并打印字符串(null)
.但你可以考虑更好的选择。