【问题标题】:Understanding assembly stack manipulation了解程序集堆栈操作
【发布时间】:2013-04-10 22:29:27
【问题描述】:

我正在裸机 ARM 上编写一个 sdram 测试程序。我用 C 编写了它,但现在我想修改生成的程序集以防止程序使用 sdram,这意味着除其他外,没有堆栈。

我最近开始学习ARM汇编,不明白编译器生成的汇编是如何使用下面代码中的堆栈的(我通过阅读ARM ARM :/也找不到答案)。 32位的变量值是放在栈上的,但是为什么push在函数开头保留3乘以32位呢?有人可以在这里解释堆栈操作吗?

C 代码:

/* ugly to have it as global but it reduces stack usage*/                                              
unsigned int const led_port[]= {0,0,1,1,2,2,3,3,4,4};
unsigned int const led_value_on[]={0x90,0x9,0x90,0x9,0x90,0x9,0x90,0x9,0x90,0x9};                      
unsigned int const masks[] = {0xf0,0xf,0xf0,0xf,0xf0,0xf,0xf0,0xf,0xf0,0xf};                           
unsigned int const led_value_off[]={0x80,0x8,0x80,0x8,0x80,0x8,0x80,0x8,0x80,0x8};                     

 void gbe_led_on(int i)
 {                        
         unsigned int value = 0;                                                                                
         phy_read(led_port[i], 0x10, &value);                                                                   
         value &= ~masks[i];
         value |= led_value_on[i];                                                                              
         phy_write(led_port[i], 0x10, value);
 }

生成的程序集(来自 gcc-arm-elf):

     <gbe_led_off>:
push    {r4, r5, r6, lr}        /* ;reserve space on the stack for 3 32 bits variables + return address */
ldr     r5, [pc, #84]   ; ffff1578 <gbe_led_off+0x60>  /*r5=led_port (array base address) */
sub     sp, sp, #8              /* sp = sp-8 (decimal 8) what does it point to??*/
ldr     r4, [r5, r0, lsl #2]    /* r4 = *(led_port+i)&0x00ff, (shift from 16 bits) */
add     r2, sp, #8              /* r2 = sp+8 (decimal 8) why???*/
mov     r6, r0                  /* r6 = i */
mov     r3, #0                  /* r3 = 0 */
mov     r0, r4                  /* r0 = led_port[i]*/
str     r3, [r2, #-4]!          /* r3 = *(sp+8-4); update r2, to which value???*/
add     r5, r5, r6, lsl #2      /* r5 = led_port[i] & 0x00ff */
mov     r1, #16                 /* r1 = 16 (decimal) */
bl      ffff13f8 <phy_read>     /* call phy_read with arguments on r0, r1, r2*/
ldr     r1, [r5, #40]   ; 0x28  /* r1 = masks[i] */
ldr     r3, [sp, #4]            /* r3 = *(sp+4) ????*/
ldr     r2, [r5, #120]  ; 0x78  /* r2 = led_value_on[i] */
bic     r3, r3, r1              /* value &= masks[i] */
orr     r3, r3, r2              /* value |= led_value_on[i] */
mov     r0, r4                  /* r0 = led_port[i] */
mov     r2, r3                  /* r2 = value  */
mov     r1, #16                 /* r1 = 16    */  
str     r3, [sp, #4]            /* *(sp+4) = value; why do we do that???*/             
bl      ffff13cc <phy_write>    /* branch to phy_write with arguments on r0,r1,r2*/
add     sp, sp, #8              /* sp = sp+8 restore stack pointer before pop? */
pop     {r4, r5, r6, pc}        /* remove 4 bytes from the stack and branch to return address */
.word   0xffff1a30

【问题讨论】:

    标签: assembly arm bare-metal


    【解决方案1】:

    push是保存r4r5r6这几个寄存器,根据ARM编程模型必须要保存。 lr 的推送是为了在您调用其他将修改它的函数时保留返回地址。堆栈中的 sub 8 保留另外 8 个字节的内存供其他变量使用(value 变量) - 稍后在 str r3, [2, #-4] 行中使用。此外,blphy_readphy_write 的分支链接也可能正在修改堆栈空间,因此您的堆栈内存问题可能比您想象的要大。此外,您最后关于 4 个字节的流行评论不正确 - 它释放了 16 个字节的空间。

    现在,您将有哪些可用的 RAM 资源可供使用?你需要一些东西,否则你的unsigned int value 将没有任何工作的地方,更不用说你的电话了。你必须有一些可用的东西。如果你这样做了,你可以通过链接脚本和section指令告诉你的C程序,省去汇编程序的麻烦。

    【讨论】:

    • phy_read 不使用堆栈(启用内联),并且是我程序中最深的函数。除了寄存器,我没有任何其他 RAM 方面的资源,这就是问题所在:/ 我计划在修改程序集后删除带有内联和声明裸函数的函数调用。
    • 这是一个支持浮点的ARM芯片吗?如果是这样,您可以根据需要将这些寄存器用作“工作 RAM”。此外,可以肯定的是,您是在 ARM 模式下编程,而不是 Thumb。最后,在用户模式和管理员模式之间切换时可以使用一堆寄存器。考虑到所有这些,您可能可以在没有 RAM 的情况下完成这项工作。
    • 没有浮点可用,但我在寄存器列表(配置寄存器,而不是“CPU寄存器”)中找到了4个32位“通用读/写寄存器”我可以使用。是的,我正在 ARM 模式下编程。
    • 因此内联您的 LED 功能并将您的 u_int 值标记为寄存器,然后编译。您将拥有输出所需的 90%。然后只需删除 r4-r11 推送/弹出并在其位置使用寄存器即可。祝你好运!
    猜你喜欢
    • 2014-10-03
    • 2011-01-26
    • 1970-01-01
    • 2013-03-25
    • 2018-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多