Assembly 为什么如果我立即使用r0,程序就不工作了,但是如果我从LDR到r2,然后从LDR到r0,程序就工作了?

Assembly 为什么如果我立即使用r0,程序就不工作了,但是如果我从LDR到r2,然后从LDR到r0,程序就工作了?,assembly,parameters,arm,parameter-passing,cpu-registers,Assembly,Parameters,Arm,Parameter Passing,Cpu Registers,我有一个程序,它只返回我通过命令行传递的值 这项工作: 我不清楚的是,如果我使用r0而不是r2,为什么它不起作用?这样不行: 如果我用7值执行程序: ./program 7 echo $? 在第一种情况下,我得到了实际值7,但在第二种情况下,我得到了3…您正在尝试执行returnargv[1][0]-0x30,这是一个转换 字符串,但只适用于一个字符,但您是: ldr r2, [r1,#4] // address of argv[1] ldr r0, [r

我有一个程序,它只返回我通过命令行传递的值

这项工作:

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

如果我用7值执行程序:

./program 7
echo $?

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

您正在尝试执行returnargv[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,在前面的问题中不止一次提到过,假设我的所有语法都正确,敲出这个答案类型将指向第一个字符的字符指针转换为前四个字符的单词指针,然后读取,但是

    ldr     r2, [r1,#4]    // address of argv[1]
    sub     r0, r0, #48    // modify address to argv[1]
    bx      lr
返回unsigned intargv[1]-0x30,这是一个更大的错误,它将字符串指针转换为一个单词,并从该地址中减去,假设我在这里也使用了正确的语法

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

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

试一试

而不是77你会得到14087或类似的数字,与你的假设工作版本

所有这些都包含在前面的问题中。你明白二维数组的意思吗?字符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]字符串之外,例如,它可以是argv[2][0]。如果你正在做,你已经经历了一些愚蠢的运气

./program 7
第n次使用错误的指令和错误的方法获得0x00000037阅读并理解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
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 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
这显然是错误的,没有任何价值。使用错误的字符串转换解决方案来填充指向字符串的指针

注:

不等于

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

两种完全不同的说明。您应该有用于说明集的arm文档,其中一种体系结构的体系结构参考手册,如果您不知道,可以从armv5开始。不要为ARM的程序员参考手册而烦恼;它们产生的问题比答案多。关于核心的技术参考手册和架构参考手册是您在开始这样的工作之前应该拥有的

ARM在伪代码方面做得非常好,尤其是较旧的ARM,与较新的ARM相比,后者具有更多的特性,因此需要涵盖更多的细节


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

看起来您的工作示例做了额外的指针解引用。如果你在第二个例子中像ldr r0一样做了同样的事情,[r0]可能会以同样的方式工作。如果真的把这个问题作为另一个问题的副本来结束,其他人可以阅读前面的问题、这个问题和答案,然后决定是否是这样,或者当然可以添加他们自己的答案或注释。我认为操作系统或shell可能会限制
e价值单位:美元?到一个字节,用255屏蔽。这就解释了他们在这个问题上的第一个例子是如何工作的:更重要的字节可能是非零的,但不是通过$?。@ecm-yeah无法完全理解它是如何被屏蔽的……这是有意义的。同样,如果得到一个3,那么低字节是0x33是愚蠢的运气。@old_timer我不知道我该如何感谢你。。。。你真的帮了我。互联网太棒了。非常有趣的东西!对于愚蠢的问题,我也很抱歉,我正在尝试学习:我明白这一点,但是当您试图编写汇编语言或从反汇编中读取它时,您还需要投入一些精力,拥有并阅读指令集文档。想想二维数组意味着什么,以及需要多少级别的间接寻址才能到达所讨论的项目。@ecm:实际上,Unix和Linux只使用传递给_exitint的arg的低位字节,在父级中使用WEXITSTATUSstatus从整数中提取该位字段,该整数包括其他字段,如信号编号(如果该字段因未捕获信号而消失)。解释了实际上可以使用waitid而不是wait/waitpid来恢复完整的32位退出状态,但bash等传统shell不这样做。POSIX指定退出状态为8位。
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