Понимание манипулирования стеком сборки

Я пишу тестовую программу sdram на чистом ARM. Я написал это на C, но теперь я хочу изменить сгенерированную сборку, чтобы программа не использовала sdram, что подразумевает, помимо прочего, отсутствие стека.

Недавно я начал изучать сборку ARM и не понимаю, как созданная компилятором сборка использует стек в следующем коде (и я не нахожу ответа, читая ARM ARM :/). 32-битное значение переменной помещается в стек, но почему push резервирует 3 раза по 32 бита в начале функции? Может кто-нибудь объяснить здесь манипулирование стеком?

Код С:

/* 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

person Étienne    schedule 10.04.2013    source источник


Ответы (1)


Нажатие состоит в том, чтобы сохранить регистры r4, r5 и r6, все из которых должны быть сохранены в соответствии с моделью программирования ARM. Нажатие lr состоит в том, чтобы сохранить адрес возврата, когда вы вызываете другие функции, которые будут его изменять. sub 8 из стека резервирует еще 8 байтов памяти для использования другой переменной (переменная value), которая используется позже в строке str r3, [2, #-4]. Кроме того, Branch Link с bl по phy_read и phy_write также может изменять пространство стека, поэтому проблема с памятью стека может быть больше, чем вы думаете. Кроме того, ваш последний поп-комментарий о 4 байтах неверен - освобождается 16 байтов пространства.

Теперь, какие ресурсы у вас будут доступны в оперативной памяти для использования? Вам что-то нужно, иначе вашему unsigned int value негде будет работать, не говоря уже о ваших звонках. У вас должно быть что-то в наличии. Если вы это сделаете, вы можете сообщить об этом своей программе на C через скрипты компоновщика и директивы section, что избавит вас от проблем с ассемблером.

person Michael Dorgan    schedule 10.04.2013
comment
phy_read не использует стек (с включенным inline) и является самой глубокой функцией в моей программе. У меня нет другого ресурса оперативной памяти, кроме регистров, вот в чем проблема :/ Я планирую удалить вызовы функций с встраиванием и объявлением голых функций после того, как я модифицировал сборку. - person Étienne; 11.04.2013
comment
Это чип ARM с плавающей запятой? Если это так, вы можете использовать эти регистры в качестве рабочей оперативной памяти, если это необходимо. Кроме того, чтобы быть уверенным, вы программируете в режиме ARM, а не в режиме Thumb. Наконец, есть набор регистров, которые можно использовать при переключении между режимами пользователя и супервизора. Учитывая все это, вы, вероятно, можете заставить это работать без оперативной памяти. - person Michael Dorgan; 11.04.2013
comment
Плавающая точка недоступна, но я нашел 4 32-битных регистра чтения/записи общего назначения в списке регистров (регистры конфигурации, а не регистры ЦП), которые я могу использовать. И да, я программирую в режиме ARM. - person Étienne; 12.04.2013
comment
Поэтому встройте свои светодиодные функции и отметьте значение u_int как регистр, а затем скомпилируйте. У вас будет 90% того, что вам нужно для вывода. Тогда просто нужно удалить push/pop r4-r11 и использовать вместо них регистры. Удачи! - person Michael Dorgan; 12.04.2013