为什么如果我立即使用r0,该程序将无法运行,但是如果我将LDR转换为r2,然后将LDR转换为r0,则该程序可以运行?

问题描述

我有这个程序,它仅返回我通过命令行传递的值。

有效

.global main

main:
        ldr     r2,[r1,#4]    // get the argv[1] and put it in r2
        ldr     r0,[r2]       // put it in r0 from r2
        sub     r0,r0,#48    // from ascii value to actual decimal value
        bx      lr

我不清楚的是,如果我使用r0而不是r2,为什么它不起作用?像这样不起作用

.global main

main:
        ldr     r0,#4]        // put the value immediately to r0
        sub     r0,#48        // ascii to actual value
        bx      lr

如果我以7个值执行程序:

./program 7
echo $?

在第一种情况下,我得到了实际值(7),但在第二种情况下,我得到了(3)...

解决方法

您尝试执行return(argv [1] [0] -0x30),这是将 通常为字符串,但仅适用于一个字符,但是您可以:

    ldr     r2,[r1,#4]    // address of argv[1]
    ldr     r0,[r2]       // read first four characters in argv[1]
                           // argv[1][0..3]
    sub     r0,r0,#48    // convert the first one to decimal
                           // leaving the other three unmodified
    bx      lr

这是return((*((unsigned int *)(&argv [1] [0])))-0x30),这是一个bug(如上一个问题中多次提到)(假设我已经了解了所有我的语法正确地敲出了这个答案)(将第一个字符的char指针转换为前四个字符的单词指针并将其读取),但是

    ldr     r2,#4]    // address of argv[1]
    sub     r0,#48    // modify address to argv[1]
    bx      lr

是return((((unsigned int)(argv [1]))-0x30),一个更大的bug(将指向字符串的指针转换为单词并减去该地址)(假设我撞出了右边语法)。

在第二种情况下,您正在修改地址而不是任何字符串数据。

您需要涵盖两个间接级别,而不仅仅是一个级别。字符串是字节数组,而不是单词数组。

尝试

./program 77

使用您可以使用的版本,您将得到14087或类似的数字,而不是77。

所有这些都在前面的问题中讨论过。您了解二维数组的含义吗? char argv [] []?

./program 77

argv本身指向一个指针数组

argv[0]
argv[1]
argv[2]

然后每个指向一个字符串

argv[0][0]='.'
argv[0][1]='/'
argv[0][2]='p'
argv[0][3]='r'
argv[0][4]='o'
...
argv[0][n]=0

argv[1][0]='7'
argv[1][1]='7'
argv[1][2]=0

r0 is argc
r1 is argv

所以r1包含指向PORAYS的地址

ldr r3,#0] //pointer to argv[0] string
ldr r4,#4] //pointer to argv[1] string
ldr r5,#8] //pointer to argv[2] string
...

您无法跳过要访问必须从字符串开头开始的字符串的步骤。

现在,完成上述操作之后,即可执行以下操作:

ldrb r0,[r4,#0] // argv[1][0] = '7'
ldrb r1,#1] // argv[1][1] = '7'
ldrb r2,#2] // argv[1][2] = 0

如果您相反

ldr r0,#0] 

假设您没有对齐错误,则一次就完成了argv [1] [0]到argv [1] [3]的全部,因为没有理由为什么argv [1]不必指向一个单词对齐的地址。

因此会将0xZZ003737放入r0,其中ZZ是argv [1]字符串之外的未知/不确定字节,例如,可以为argv [2] [0]。如果您这样做的话,会遇到一些愚蠢的运气

./program 7

并通过使用错误的指令和错误的方法获得0x00000037(第n次阅读并理解Frant对另一个问题的回答)。

如果要拥有这个

char mystring[]="1234567";

您会使用

mystring[0]-=0x30;

要将其从字符串(0x31,0x32,0x33,... 0x37,0x00)转换为值1234567(0x12d687)?当然不是,那根本行不通。您将需要使用atoi,atol,strtol等(阅读Frant的回答)或自己动手。

rb=0;
for(ra=0;mystring[ra];ra++)
{
    rb*=10;
    rb+=mystring[ra]-=0x30;
}

假设我们提前知道用户要在字符串中输入十进制数字。 (错误的假设,还有另一个像这样的错误)

这样做:

mystring[0]-=0x30;

仅修改一项不会将字符串转换为数字。

为进一步说明所有这些,操作系统加载程序将在您有权访问的某些内存中为您填充argv [] []。

例如

./so 123

我将为演示目的编造地址

[address] data
[0x00001000] 0x00001008  pointer to argv[0]
[0x00001004] 0x0000100D  pointer to argv[1]
[0x00001008] 0x2E '.'
[0x00001009] 0x2F '/'
[0x0000100A] 0x73 's'
[0x0000100B] 0x6F 'o'
[0x0000100C] 0x00 string termination
[0x0000100D] 0x31 '1'
[0x0000100E] 0x32 '2'
[0x0000100F] 0x33 '3' 
[0x00001010] 0x00 string termination

因此,在这种情况下,在调用main之前,r1将被设置为0x00001000。

所以

ldr r2,#4] read 0x1004 r2 = 0x100D
ldrb r0,[r2]  read 0x100D r0 = 0x31
sub r0,#0x30,r0 = 1 (note: which is not equal to 123)

如果您

ldr r2,#4] read 0x1004 r2 = 0x100D
ldr r0,[r2]  read 0x100D r0 = 0x00333231
sub r0,#0x30  r0 = 0x00333201 (note: which is not equal to 123 = 0x7B)

如果启用,则这是对齐错误。

如果您

ldr r2,#4] read 0x1004 r2 = 0x100D
sub r0,r2,#0x30   r0 = 0xFDD

那显然是错误的,那毫无价值。使用错误的字符串转换解决方案将指针指向字符串。

注意:

ldr     r0,[r2]  // read word from address in r2 and put in r0

不等于

mov     r0,r2    // copy contents of r2 into r0

至少对于臂工具和气体装配语言,[括号]表示间接级别,因此[r2]表示r2中包含的地址处的事物,其中r2表示r2的内容。

两条完全不同的指令。您应该具有该指令集的arm文档,一种体系结构的体系结构参考手册,如果不知道,请从armv5开始。不要理会《 ARM程序员参考手册》;他们提出的问题多于答案。有关核心的技术参考手册和体系结构参考手册是您开始进行此类工作之前应始终具备的条件。

ARM的伪代码相当不错,尤其是旧版ARM与新版ARM相比,后者具有更多功能,因此需要介绍更多细节。

由于我们中有些人在修改之前已经看到了您先前/原始问题的原始内容,并且您已经从main调用了C函数:然后阅读Frants用您现在知道的答案回答,而只需调用另一个C函数。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...