#assembly #gcc #arm #stack #callstack
#сборка #gcc #arm #стек #callstack
Вопрос:
Вот контекст для моего вопроса:
- Я использую архитектуру ARM7 (ARM720T TDMI …)
- Скомпилируйте с использованием GCC codesourcedy (arm-none-eabi версия 4.5.2)
- Я новичок в архитектуре gcc и ARM, но проработал в embedded 5 лет 😉
В моем проекте функция C вызывается из файла сборки до инициализации стека. Поскольку стек не инициализирован, функция не должна использовать стек.
Возможно ли с помощью какой-либо «подобной прагме» команды заставить gcc не использовать стек с этой конкретной функцией?
Дополнительная информация: Цель моей работы — преобразовать проект, ранее скомпилированный с помощью ARMASM, в gcc. Итак, в ARMASM вызов этой функции C до инициализации стека работал. Возможно, окончательный ответ будет заключаться в том, что это невозможно сделать в gcc…
Ниже приведена выдержка из списка ELF для функции C, вызываемой в коде сборки (как вы можете видеть, я пробовал always_inline, но при вызове из сборки этого, похоже, недостаточно):
000049d0 <CInit_Init>:
__attribute__((always_inline)) extern void CInit_Init(void) {
49d0: e52db004 push {fp} ; (str fp, [sp, #-4]!)
49d4: e28db000 add fp, sp, #0
__attribute__((always_inline)) void COM1_Init(void);
__attribute__((always_inline)) extern inline void COM1_Init_I(void) {
// Skip if already enabled
if (TEST_BIT_CLR(HwrSysControl1, HwSysControl1UartEnable)) {
49d8: e59f3038 ldr r3, [pc, #56] ; 4a18 <CInit_Init 0x48>
49dc: e5933000 ldr r3, [r3]
49e0: e2033c01 and r3, r3, #256 ; 0x100
49e4: e3530000 cmp r3, #0
49e8: 1a000007 bne 4a0c <CInit_Init 0x3c>
HwrUart1Control = (
49ec: e59f3028 ldr r3, [pc, #40] ; 4a1c <CInit_Init 0x4c>
49f0: e59f2028 ldr r2, [pc, #40] ; 4a20 <CInit_Init 0x50>
49f4: e5832000 str r2, [r3]
HwUartControlDataLength8|
HwUartControlFifoEnable|
HwUartControlRate115200);
BIT_SET(HwrSysControl1, HwSysControl1UartEnable);
49f8: e59f3018 ldr r3, [pc, #24] ; 4a18 <CInit_Init 0x48>
49fc: e59f2014 ldr r2, [pc, #20] ; 4a18 <CInit_Init 0x48>
4a00: e5922000 ldr r2, [r2]
4a04: e3822c01 orr r2, r2, #256 ; 0x100
4a08: e5832000 str r2, [r3]
COM1_Init_I();
}
4a0c: e28bd000 add sp, fp, #0
4a10: e49db004 pop {fp} ; (ldr fp, [sp], #4)
4a14: e12fff1e bx lr
4a18: 80000100 .word 0x80000100
4a1c: 800004c0 .word 0x800004c0
4a20: 00070001 .word 0x00070001
Комментарии:
1. Если вы выполняете вызов из сборки, вы можете легко настроить очень временный стек, скажем, 32-128 байт, указав регистр стека на некоторый статический символ [] или аналогичный.
Ответ №1:
просто настройте стек. В противном случае, если вы не хотите, чтобы в стеке не было локальных переменных и было достаточно мало кода, чтобы у вас не заканчивались регистры, Не вызывайте никаких функций из этой функции и т. Д. Если вам действительно нужны переменные, используйте глобальные переменные. Компилятор НЕ СМОЖЕТ сгенерировать ваш код без использования стека, если вы приведете к тому, что в коде закончатся регистры и глобальные переменные. Ни один переключатель компилятора не изобретет хранилище, и я бы не стал доверять компилятору, у которого есть какой-то нестандартный трюк, не относящийся к стеку, который он может попытаться использовать..
Простой, без локальных переменных, даже не нужен промежуточный регистр:
unsigned int fun ( unsigned int a, unsigned int b )
{
return(a b);
}
нет стека:
00000000 <fun>:
0: e0810000 add r0, r1, r0
4: e12fff1e bx lr
вы всегда можете изменить стек после вызова исходного кода на C, для установки стека перед вызовом C требуется от одной до нескольких инструкций, просто установите указатель стека. Вам не нужны все указатели стека, только один. либо установите указатель стека, либо напишите функцию на ассемблере, а не на C. В любом случае вы должны инициализировать стек перед com-портом. Это одна инструкция, два расположения слов, и вы вызываете C, поэтому стоимость незначительна.
.globl _start
_start:
b reset
reset:
ldr sp,=0x20008000
bl more_fun
b .
.globl fun
fun:
bx lr
.globl fun_out
fun_out:
bx lr
unsigned int fun ( unsigned int , unsigned int );
void fun_out ( unsigned int, unsigned int, unsigned int, unsigned int );
unsigned int more_fun ( unsigned int a, unsigned int b, unsigned int c )
{
unsigned int d;
d = fun(a,b);
fun_out(a,b c,b,a c);
}
по инструкции, два слова, и вы можете вызвать C
4: e59fd00c ldr sp, [pc, #12] ; 18 <fun_out 0x4>
8: eb000003 bl 1c <more_fun>
...
18: 20008000 andcs r8, r0, r0
0000001c <more_fun>:
1c: e92d4070 push {r4, r5, r6, lr}
20: e1a05002 mov r5, r2
24: e1a06000 mov r6, r0
28: e1a04001 mov r4, r1
2c: ebfffff7 bl 10 <fun>
30: e1a00006 mov r0, r6
34: e0853006 add r3, r5, r6
38: e0851004 add r1, r5, r4
3c: e1a02004 mov r2, r4
40: ebfffff3 bl 14 <fun_out>
44: e8bd8070 pop {r4, r5, r6, pc}
Комментарии:
1. Спасибо! Я изучу ваше решение и вернусь к вам через пару дней. Мне особенно нравится идея установки временного указателя стека…
2. Наконец, я настраиваю sp перед вызовом моей функции C. Но я все еще не понимаю, почему / как тот же код сборки с другим компилятором смог вызвать функцию C без использования указателя стека…
3. посмотрите на разборку, либо не использовали стек, либо, возможно, в системе, где стек был настроен для вас и т. Д., И, Вероятно, Он включал вызовы второго уровня и т. Д.
4. Обратите внимание, что даже ваш simple
fun
зависит от компиляции с включенной оптимизацией. В противном случае это приведет к переносу аргументов регистра в стек. Возможно__attribute__((optimize("O2")))
, этого можно избежать. Или просто посмотрите на это как на еще одну причину никогда не вызывать сгенерированный компилятором код без действительного стека.
Ответ №2:
У вас есть примерно два варианта:
- Настройте стек и забудьте об этом. Вы все равно должны установить указатель стека в сборке, поскольку вы не можете этого сделать в C (если только компилятор не поддерживает некоторые нестандартные расширения, позволяющие прямой доступ к регистрам).
- Закомментируйте исходную функцию C, дизассемблирование которой вы представили выше, скопируйте код дизассемблирования, замените push / pop / bx на str / ldr / b соответствующим образом (вам нужно будет выделить глобальную переменную для сохранения / восстановления этих энергонезависимых регистров (fp?)), исправьте другие вещипри необходимости и перекомпилируйте его в модуле сборки. РЕДАКТИРОВАТЬ: с другой стороны, вместо того, чтобы выполнять разборку, переведите эту функцию в сборку, используя, например, переключатель —
gcc -c cfile.c -S -o asmfile.s
S. Это, вероятно, сэкономит вам немного работы.
Я бы предпочел иметь действительный стек немедленно.
Комментарии:
1. Я понимаю, что я должен настроить стек раньше. Но я хотел бы сохранить исходный файл. Я попробую переключить -S с помощью curiosity!