为什么如果我立即使用 r0 程序就无法运行,但如果我将 LDR 设置为 r2 然后将 LDR 设置为 r0 它就可以运行?

Why if I use immediately r0 the program doesn't work but if I LDR to r2 and then LDR to r0 it works?

我有这个程序,它只是 returns 我通过命令行传递的值。

这个有效

.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, [r1,#4]        // put the value immediately to r0
        sub     r0, 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) 这是一个错误(在前面的问题中不止一次提到过) )(假设我的所有语法都正确地给出了这个答案)(将指向第一个字符的字符指针类型转换为前四个字符的单词指针并读取它),但是

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

是 return( ((unsigned int)(argv[1])) - 0x30),一个更大的错误(将指向字符串的指针转换为单词并从该地址中减去)(假设我在这里也敲出了正确的语法)。

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

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

尝试

./program 77

你会得到 14087 或类似的数字,而不是 77,你应该可以工作的版本。

所有这些都包含在前面的问题中。你明白二维数组是什么意思吗?字符 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 包含指针数组的地址

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

您不能跳过要访问必须从字符串开头开始的字符串的那一步。

现在,一旦你完成了上面的操作,那么你就可以这样做了:

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

如果你改为

ldr r0,[r4,#0] 

这就是 argv[1][0] 到 argv[1][3] 的所有内容,假设您没有出现对齐错误,因为没有理由 argv[1] 必须指向字对齐地址。

这样会将 0xZZ003737 放入 r0,其中 ZZ 是 argv[1] 字符串之外的 unknown/non-deterministic 字节,例如它可以是 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;
}

假设我们提前知道用户在字符串中传递了一个十进制数。 (错误的假设,又一个 bug 做这样的事情)

这样做:

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,[r1,#4] read 0x1004 r2 = 0x100D
ldrb r0,[r2]  read 0x100D r0 = 0x31
sub r0,r0,#0x30, r0 = 1 (note: which is not equal to 123)

如果你

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

此外,如果启用,这是一个对齐错误。

如果你

ldr r2,[r1,#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

至少对于 arm 工具和 gas 汇编语言,[括号] 表示间接级别,因此 [r2] 表示 r2 中包含的地址处的内容,其中 r2 表示 r2 的内容。

两个完全不同的指令。你应该有指令集的arm文档,其中一种架构的架构参考手册,如果你不知道就从armv5开始。不要为 ARM 的程序员参考手册而烦恼;他们提出的问题多于答案。有关核心的技术参考手册和体系结构参考手册是您在开始进行此类工作之前应该始终拥有的。

ARM 的伪代码做得很好,尤其是较旧的 ARM ARM 与较新的 ARM 相比,后者具有更多功能,因此需要涵盖更多细节。

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