【发布时间】:2017-09-21 14:46:00
【问题描述】:
我打算用 C 写一个小内核,我真的不希望它因不必要的指令而膨胀。
我有两个 C 文件,分别称为 main.c 和 hello.c。我使用以下 GCC 命令编译并链接它们:
gcc -Wall -T lscript.ld -m16 -nostdlib main.c hello.c -o main.o
我正在使用以下 OBJDUMP 命令转储 .text 部分:
objdump -w -j .text -D -mi386 -Maddr16,data16,intel main.o
并获得以下转储:
00001000 <main>:
1000: 67 66 8d 4c 24 04 lea ecx,[esp+0x4]
1006: 66 83 e4 f0 and esp,0xfffffff0
100a: 67 66 ff 71 fc push DWORD PTR [ecx-0x4]
100f: 66 55 push ebp
1011: 66 89 e5 mov ebp,esp
1014: 66 51 push ecx
1016: 66 83 ec 04 sub esp,0x4
101a: 66 e8 10 00 00 00 call 1030 <hello>
1020: 90 nop
1021: 66 83 c4 04 add esp,0x4
1025: 66 59 pop ecx
1027: 66 5d pop ebp
1029: 67 66 8d 61 fc lea esp,[ecx-0x4]
102e: 66 c3 ret
00001030 <hello>:
1030: 66 55 push ebp
1032: 66 89 e5 mov ebp,esp
1035: 90 nop
1036: 66 5d pop ebp
1038: 66 c3 ret
我的问题是:为什么会生成以下行的机器代码? 可以看到减法和加法是互相补全的,但是为什么会生成呢?我没有要在堆栈上分配的任何变量。我会很感激有一个资源来了解 ECX 的用法。
1016: 66 83 ec 04 sub esp,0x4
1021: 66 83 c4 04 add esp,0x4
main.c
extern void hello();
void main(){
hello();
}
你好.c
void hello(){}
lscript.ld
SECTIONS{
.text 0x1000 : {*(.text)}
}
【问题讨论】:
-
-O0产生的无关操作很难说。它肯定比其他任何东西都更能反映编译器内部。尝试(至少)-Og。 -
因为它确实如此(gcc 源代码是数千行......其中一些确实产生了这个,可能是一些关于堆栈框架或调试的潜在脚手架,最终没有被使用)。你为什么在乎?
-
然后命令它删除不必要的指令,开启优化。但是你必须正确定义所有“外部影响”部分,所以没有什么重要的东西被优化为多余的,我认为这可能比编写普通应用程序 C 更难(有明显的 I/O 操作告诉编译器传播哪些结果“外部”),虽然内核的“输出”对优化器来说可能不太明显,但可能需要一些
volatile在这里和那里,或者其他在机器上获得正确效果的方法(你也可能想要调整一些内部选项朝着更小的代码大小)。 -
前几行(加上
push ecx)是为了确保堆栈在 Linux i386 ABI 所需的 16 字节边界上对齐。ret之前的pop ecx和lea在 main 中用于撤消该对齐工作。如果您要创建一个名为main以外的入口点,您将看不到它。您还应该使用优化进行编译(如-O3)。我强烈建议您使用 GCC 交叉编译器(例如i686-elf-gcc),而不是主机环境的 GCC 工具链。\