使用gcc编译时-ffreestanding和-nostlib之间的差异
我正在使用64位linux机器和x84-elf64-gcc编译器。我刚刚开始了低级编程,希望了解C代码是如何实际翻译成二进制的。这主要用于操作系统开发,因为我知道处理器不理解ELF或任何其他格式,只理解二进制 例如,以下c文件:使用gcc编译时-ffreestanding和-nostlib之间的差异,c,gcc,compilation,x86,linker,C,Gcc,Compilation,X86,Linker,我正在使用64位linux机器和x84-elf64-gcc编译器。我刚刚开始了低级编程,希望了解C代码是如何实际翻译成二进制的。这主要用于操作系统开发,因为我知道处理器不理解ELF或任何其他格式,只理解二进制 例如,以下c文件: //test.c int func() { return 0x12345678; } 当我使用gcc编译时: gcc test.c 我得到以下错误: (.text+0x20): undefined reference to `main' collect2:
//test.c
int func()
{
return 0x12345678;
}
当我使用gcc编译时:
gcc test.c
我得到以下错误:
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
所以我猜链接器有问题。
我有:
我得到一个ELF对象文件,然后执行一个objdump并得到预期的:
0000000000000000 <func>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: b8 78 56 34 12 mov $0x12345678,%eax
9: 5d pop %rbp
a: c3 retq
0000000000000000:
0:55推送%rbp
1:48 89 e5 mov%rsp,%rbp
4:b8 78 56 34 12 mov$0x12345678,%eax
9:5d流行音乐%rbp
a:c3-retq
但当我使用-m32选项和objdump“交叉编译”32位版本时,我得到:
hello.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <func+0x4>
8: 05 01 00 00 00 add $0x1,%eax
d: b8 78 56 34 12 mov $0x12345678,%eax
12: 5d pop %ebp
13: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret
hello.o:文件格式elf32-i386
第节的分解。正文:
00000000 :
0:55推力%ebp
1:89 e5 mov%esp,%ebp
3:e8 fc ff呼叫4
8:05 01 00添加$0x1,%eax
d:b8 78 56 34 12 mov$0x12345678,%eax
12:5d流行百分比ebp
13:c3 ret
.text.\uuux86.get\u pc\u thunk.ax节的反汇编:
00000000 :
0:8b 04 24 mov(%esp),%eax
3:c3 ret
我在前面的回答中读到,这与位置无关代码有关:
为什么在使用-m32选项编译时会有这样的更改?
此外,有人建议我在编译时使用-ffreestanding选项,但它在这里似乎没有效果。我已经读到-ffreestanding告诉编译器没有标准库,那么-nostlib是什么呢
注意:我对这种硬核c编程比较陌生,我认为这里的主要问题是我不太了解链接器/编译器是如何工作的(我不知道
-f重建
到底做了什么,这部分是个好问题
但不幸的是,您的问题在32位饼图代码中有一个很大的旁道:
为什么在使用-m32选项编译时会有这样的更改
因为您省略了任何-O
优化选项,并且32位模式没有用于数据的EIP相对寻址模式(仅相对跳转/调用)。因此显然,调试模式总是将寄存器设置为GET指针,作为寻址静态数据的基础,即使在不使用它的函数中也是如此
始终使用-fno pie
关闭默认设置,除非您特别希望使pie可执行
您可能还需要
-mcmodel=kernel
-如果您正在编译64位高半内核,这是一个好主意(静态地址可以用于32位符号扩展立即,但不能用于32位零扩展).但是IDK如果对32位代码有任何作用的话。我不知道-f重建
到底做了什么,这是一个好问题
但不幸的是,您的问题在32位饼图代码中有一个很大的旁道:
为什么在使用-m32选项编译时会有这样的更改
因为您省略了任何-O
优化选项,并且32位模式没有用于数据的EIP相对寻址模式(仅相对跳转/调用)。因此显然,调试模式总是将寄存器设置为GET指针,作为寻址静态数据的基础,即使在不使用它的函数中也是如此
始终使用-fno pie
关闭默认设置,除非您特别希望使pie可执行
您可能还需要
-mcmodel=kernel
-如果您正在编译64位高半内核,这是一个好主意(静态地址可以直接用于32位符号扩展,但不能用于32位零扩展)。但是IDK如果对32位代码有任何作用,则它可以控制该过程的两部分:
- 向编译器指示它应该是独立的,唯一的效果是禁用一些内置函数,如memcpy
- 指示默认情况下不应链接库和启动文件
- 向编译器指示它应该是独立的,唯一的效果是禁用一些内置函数,如memcpy
- 指示默认情况下不应链接库和启动文件
rel32
或rel8
,但这与ModRM中的数据寻址模式是分开的。所以换句话说,像jmp这样的指令在访问内存时使用(可以使用)相对地址(例如mov eax,[位置])使用绝对值?@SuraajKS:在32位模式下,是的。据我所知,32位机器甚至没有RIP,不是EIP吗?我做过一些汇编编程,我认为简单的函数调用通常使用指令指针相对地址来跳转/分支。为什么说32位模式没有相对地址??@SuraajKS:EIP/RIP相对数据寻址模式在x86-64中是新的。它在32位模式下不可用。我想我可以说“没有EIP相对寻址模式”,因为如果它在32位模式下存在,那是
hello.o: file format elf32-i386
Disassembly of section .text:
00000000 <func>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <func+0x4>
8: 05 01 00 00 00 add $0x1,%eax
d: b8 78 56 34 12 mov $0x12345678,%eax
12: 5d pop %ebp
13: c3 ret
Disassembly of section .text.__x86.get_pc_thunk.ax:
00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret