Понимание манипулирования стеком сборки
Я пишу тестовую программу SDRAM на голой металлической ARM. Я написал это на 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
1 ответ
Толчок сохранить регистры r4
, r5
, а также r6
все из которых должны быть сохранены в соответствии с моделью программирования ARM. Толчок lr
сохранить адрес возврата при вызове других функций, которые будут его изменять. sub 8
из стека резервирует еще 8 байтов памяти для использования другой переменной (value
переменная) - используется позже str r3, [2, #-4]
линия. Кроме того, ссылка филиала bl
в phy_read
а также phy_write
также может изменять пространство стека, поэтому ваша проблема со стековой памятью может оказаться больше, чем вы думаете. Кроме того, ваш последний всплывающий комментарий о 4 байтах неверен - это 16 байт свободного места.
Теперь, какие ресурсы у вас будут доступны в оперативной памяти для использования? Вам нужно что-то или ваше unsigned int value
не будет места для работы, не говоря уже о ваших звонках. Вы должны иметь что-то доступное. Если вы это сделаете, вы можете рассказать об этом вашей программе на C через скрипты компоновщика и section
директивы, избавляя вас от хлопот ассемблера.