Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 内存中的局部变量位置_C_Arm_Objdump - Fatal编程技术网

C 内存中的局部变量位置

C 内存中的局部变量位置,c,arm,objdump,C,Arm,Objdump,作为家庭作业,我得到了一些c文件,并使用arm linux gcc编译了它们(我们最终将针对gumstix板,但对于这些练习,我们一直在使用qemu和ema) 其中一个问题让我有点困惑——我们被告知: 使用arm linux objdump在可执行二进制文件中查找main()中声明的变量的位置 然而,这些变量是本地的,因此在运行时之前不应该有地址,对吗 我在想,也许我需要找到的是堆栈帧中的偏移量,实际上可以使用objdump(我不知道如何找到) 无论如何,对此事的任何见解都将不胜感激,如有必要,

作为家庭作业,我得到了一些c文件,并使用arm linux gcc编译了它们(我们最终将针对gumstix板,但对于这些练习,我们一直在使用qemu和ema)

其中一个问题让我有点困惑——我们被告知:

使用arm linux objdump在可执行二进制文件中查找main()中声明的变量的位置

然而,这些变量是本地的,因此在运行时之前不应该有地址,对吗


我在想,也许我需要找到的是堆栈帧中的偏移量,实际上可以使用objdump(我不知道如何找到)


无论如何,对此事的任何见解都将不胜感激,如有必要,我将很乐意发布源代码。

这将取决于程序以及他们希望变量位置的准确程度。问题是否需要它们存储在哪个代码段中。康斯特bss等?它需要特定的地址吗?无论哪种方式,一个好的开始都是使用objdump-S标志

objdump -S myprogram > dump.txt
这很好,因为它将打印出源代码和带有地址的程序集的混合。从这里开始,只需搜索
intmain
,就可以开始了

unsigned int one ( unsigned int, unsigned int );
unsigned int two ( unsigned int, unsigned int );
unsigned int myfun ( unsigned int x, unsigned int y, unsigned int z )
{
    unsigned int a,b;
    a=one(x,y);
    b=two(a,z);
    return(a+b);
}
编译和反汇编

arm-none-eabi-gcc -c fun.c -o fun.o
arm-none-eabi-objdump -D fun.o
由编译器创建的代码

00000000 <myfun>:
   0:   e92d4800    push    {fp, lr}
   4:   e28db004    add fp, sp, #4
   8:   e24dd018    sub sp, sp, #24
   c:   e50b0010    str r0, [fp, #-16]
  10:   e50b1014    str r1, [fp, #-20]
  14:   e50b2018    str r2, [fp, #-24]
  18:   e51b0010    ldr r0, [fp, #-16]
  1c:   e51b1014    ldr r1, [fp, #-20]
  20:   ebfffffe    bl  0 <one>
  24:   e50b0008    str r0, [fp, #-8]
  28:   e51b0008    ldr r0, [fp, #-8]
  2c:   e51b1018    ldr r1, [fp, #-24]
  30:   ebfffffe    bl  0 <two>
  34:   e50b000c    str r0, [fp, #-12]
  38:   e51b2008    ldr r2, [fp, #-8]
  3c:   e51b300c    ldr r3, [fp, #-12]
  40:   e0823003    add r3, r2, r3
  44:   e1a00003    mov r0, r3
  48:   e24bd004    sub sp, fp, #4
  4c:   e8bd4800    pop {fp, lr}
  50:   e12fff1e    bx  lr
堆栈帧消失,局部变量保留在寄存器中

00000000: 0:e92d4038推送{r3,r4,r5,lr} 4:e1a05002 mov r5,r2 8:ebfffffe bl 0 c:e1a04000 mov r4,r0 10:e1a01005 mov r1,r5 14:ebfffffe bl 0 18:e0800004添加r0、r0、r4 1c:e8bd4038 pop{r3,r4,r5,lr} 20:E12FF1E bx lr

编译器决定做的是通过将寄存器保存在堆栈上,为自己提供更多的寄存器。为什么它救了r3是个谜,但这是另一个话题

按照调用约定输入函数r0=x,r1=y和r2=z,我们可以不使用r0和r1(使用一个函数(y,x)再试一次,看看会发生什么),因为它们正好落在一个函数()中,不再使用。调用约定说r0-r3可以被一个函数破坏,所以我们需要为以后保留z,所以我们将它保存在r5中。根据调用约定,one()的结果是r0,因为two()会破坏r0-r3,我们需要保存一个以备以后使用,在调用two()之后,我们仍然需要r0来调用two,因此r4现在保存一个。在调用one之前,我们在r5中保存了z(在r2中移动到r5),我们需要one()的结果作为two()的第一个参数,它已经存在,我们需要z作为第二个参数,所以我们将r5移动到保存z到r1的位置,然后我们调用two()。每个调用约定的two()的结果。由于基本数学属性中的b+a=a+b,返回前的最终加法是r0+r4,即b+a,结果进入r0,根据约定,r0是用于从函数返回内容的寄存器。清理堆栈并恢复修改后的寄存器,完成

由于myfun()使用bl调用其他函数,bl修改链接寄存器(r14),为了能够从myfun()返回,我们需要保留链接寄存器中的值,从函数的入口到最终返回(bx lr),因此lr被推到堆栈上。约定规定我们可以在函数中销毁r0-r3,但不能销毁其他寄存器,因此r4和r5被推到堆栈上,因为我们使用了它们。为什么r3被推到堆栈上从调用约定的角度来看是不必要的,我想知道它是否是在预期64位内存系统的情况下完成的,进行两次完整的64位写入比一次64位写入和一次32位写入便宜。但是您需要知道堆栈的对齐方式,所以这只是一个理论。没有理由在这段代码中保留r3

现在掌握这些知识,反汇编分配的代码(arm-…-objdump-D something.something),并进行同样的分析。特别是对于名为main()的函数和未名为main()的函数(我没有特意使用main()),堆栈帧的大小可能没有意义,或者比其他函数的意义小。在上面的非优化情况下,我们需要总共存储6个东西,x,y,z,a,b和链接寄存器6*4=24字节,这导致了sub sp,sp,#24,我需要考虑堆栈指针和帧指针 有一点事。我认为有一个命令行参数告诉编译器不要使用帧指针-fomit帧指针,它保存了两条指令

00000000 <myfun>:
   0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   4:   e24dd01c    sub sp, sp, #28
   8:   e58d000c    str r0, [sp, #12]
   c:   e58d1008    str r1, [sp, #8]
  10:   e58d2004    str r2, [sp, #4]
  14:   e59d000c    ldr r0, [sp, #12]
  18:   e59d1008    ldr r1, [sp, #8]
  1c:   ebfffffe    bl  0 <one>
  20:   e58d0014    str r0, [sp, #20]
  24:   e59d0014    ldr r0, [sp, #20]
  28:   e59d1004    ldr r1, [sp, #4]
  2c:   ebfffffe    bl  0 <two>
  30:   e58d0010    str r0, [sp, #16]
  34:   e59d2014    ldr r2, [sp, #20]
  38:   e59d3010    ldr r3, [sp, #16]
  3c:   e0823003    add r3, r2, r3
  40:   e1a00003    mov r0, r3
  44:   e28dd01c    add sp, sp, #28
  48:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
  4c:   e12fff1e    bx  lr
00000000:
0:e52de004推送{lr};(str lr,[sp,#-4]!)
4:e24dd01c子sp,sp,#28
8:e58d000c str r0[sp,#12]
c:e58d1008 str r1[sp,#8]
10:e58d2004 str r2[sp,#4]
14:e59d000c ldr r0,[sp,#12]
18:e59d1008 ldr r1[sp,#8]
1c:ebfffffe bl 0
20:e58d0014 str r0[sp,#20]
24:e59d0014 ldr r0,[sp,#20]
28:e59d1004 ldr r1[sp,#4]
2c:ebfffffe bl 0
30:e58d0010 str r0[sp,#16]
34:e59d2014 ldr r2[sp,#20]
38:e59d3010 ldr r3[sp,#16]
3c:e0823003添加r3、r2、r3
40:e1a00003 mov r0,r3
44:e28dd01c添加sp,sp,#28
48:e49de004 pop{lr};(ldr lr,[sp],#4)
4c:E12FF1E bx lr

不过,优化节省了很多…

我认为单靠源代码在这里不会有太多用处。如果你能把反汇编的二进制代码和它一起发布,那就更好了。本质上,您应该尝试扫描生成的代码以查找您所做的分配。然而,如果你运气不好,根本就不会有任何存储位置,因为编译器可以自由地将局部变量保存在寄存器中,而无需将它们存储到主存中。但是你至少可以尝试一下。哦,这一切都适用于二进制文件没有用调试符号构建的情况,否则工作应该会容易得多。“我需要找到堆栈帧中的偏移量”——这就是
00000000 <myfun>:
   0:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   4:   e24dd01c    sub sp, sp, #28
   8:   e58d000c    str r0, [sp, #12]
   c:   e58d1008    str r1, [sp, #8]
  10:   e58d2004    str r2, [sp, #4]
  14:   e59d000c    ldr r0, [sp, #12]
  18:   e59d1008    ldr r1, [sp, #8]
  1c:   ebfffffe    bl  0 <one>
  20:   e58d0014    str r0, [sp, #20]
  24:   e59d0014    ldr r0, [sp, #20]
  28:   e59d1004    ldr r1, [sp, #4]
  2c:   ebfffffe    bl  0 <two>
  30:   e58d0010    str r0, [sp, #16]
  34:   e59d2014    ldr r2, [sp, #20]
  38:   e59d3010    ldr r3, [sp, #16]
  3c:   e0823003    add r3, r2, r3
  40:   e1a00003    mov r0, r3
  44:   e28dd01c    add sp, sp, #28
  48:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
  4c:   e12fff1e    bx  lr