Недавно я обнаружил проблему сбоя. Дизассемблирование просматривало стек вызовов слой за слоем. После достижения определенной функции я не мог найти соответствующую строку кода. Затем я ввел определенную функцию, которую она вызывала, а затем связал последовательность вызовов. . Поэтому я провел небольшой эксперимент, чтобы посмотреть, как компилятор справится с этим на уровне сборки.
Код С:
#include <stdio.h>
static void myfunc(void)
{
printf("myfunc in\n");
printf("myfunc out\n");
}
int main(int argc, char *argv[])
{
printf("main in\n");
myfunc();
printf("main out\n");
return 0;
}
Скомпилируйте с помощью Arm-none-linux-gnueabi-gcc main.c, а затем используйте Arm-none-linux-gnueabi-objdump -d a.out, чтобы дизассемблировать выходные данные. Из фрагмента сборки ниже вы можете увидеть вызов основной функции. в myfunc: bl 842c <myfunc>
0000842c <myfunc>:
842c: e92d4800 push {fp, lr}
8430: e28db004 add fp, sp, #4
8434: e59f000c ldr r0, [pc, #12] ; 8448 <myfunc+0x1c>
8438: ebffffcd bl 8374 <_init+0x44>
843c: e59f0008 ldr r0, [pc, #8] ; 844c <myfunc+0x20>
8440: ebffffcb bl 8374 <_init+0x44>
8444: e8bd8800 pop {fp, pc}
8448: 0000856c .word 0x0000856c
844c: 00008578 .word 0x00008578
00008450 <main>:
8450: e92d4800 push {fp, lr}
8454: e28db004 add fp, sp, #4
8458: e24dd008 sub sp, sp, #8
845c: e50b0008 str r0, [fp, #-8]
8460: e50b100c str r1, [fp, #-12]
8464: e59f001c ldr r0, [pc, #28] ; 8488 <main+0x38>
8468: ebffffc1 bl 8374 <_init+0x44>
846c: ebffffee bl 842c <myfunc>
8470: e59f0014 ldr r0, [pc, #20] ; 848c <main+0x3c>
8474: ebffffbe bl 8374 <_init+0x44>
8478: e3a03000 mov r3, #0
847c: e1a00003 mov r0, r3
8480: e24bd004 sub sp, fp, #4
8484: e8bd8800 pop {fp, pc}
8488: 00008584 .word 0x00008584
848c: 0000858c .word 0x0000858c
Затем используйте Arm-none-linux-gnueabi-gcc -O1 main.c, чтобы выполнить оптимизационную компиляцию уровня 1, и используйте Arm-none-linux-gnueabi-objdump -d a.out, чтобы дизассемблировать выходные данные. Myfunc не может быть найден в Сборка. В основной функции к ней нет вызова. На самом деле, myfunc меньше, и myfunc встроен в основную функцию. 8438-8444 — это два вызова printf в myfunc.
0000842c <main>:
842c: e92d4008 push {r3, lr}
8430: e59f0020 ldr r0, [pc, #32] ; 8458 <main+0x2c>
8434: ebffffce bl 8374 <_init+0x44>
8438: e59f001c ldr r0, [pc, #28] ; 845c <main+0x30>
843c: ebffffcc bl 8374 <_init+0x44>
8440: e59f0018 ldr r0, [pc, #24] ; 8460 <main+0x34>
8444: ebffffca bl 8374 <_init+0x44>
8448: e59f0014 ldr r0, [pc, #20] ; 8464 <main+0x38>
844c: ebffffc8 bl 8374 <_init+0x44>
8450: e3a00000 mov r0, #0
8454: e8bd8008 pop {r3, pc}
8458: 00008544 .word 0x00008544
845c: 0000854c .word 0x0000854c
8460: 00008558 .word 0x00008558
8464: 00008564 .word 0x00008564
---------------------------------------
8438: e59f001c ldr r0, [pc, #28]; 845c <main+0x30>
843c: ebffffcc bl 8374 <_init+0x44>
pc, #28 — это 845c, где хранится адрес памяти 854c, который будет (указатель памяти ) Данные, на которые указывает 854c, извлекаются и помещаются в r0, который является первым адресом памяти «myfunc in\n», а затем вызывается функция printf. Согласно соглашению о вызове функций, первым параметром является r0, за ним следуют r1 и r2, а все остальные параметры следует помещать в стек.
printf — это функция динамической библиотеки C. Здесь нет такого оператора, как bl printf. На самом деле это bl 8374 <_init+0x44>, а затем происходит переход к реальной функции печати.