使用gcc编译时-ffreestanding和-nostlib之间的差异

使用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:

我正在使用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: 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

  • 指示默认情况下不应链接库和启动文件


据我所知,32位机器甚至没有RIP,不是吗?我做过一些汇编编程,我认为简单的函数调用通常使用指令指针相对地址来跳转/分支。为什么说32位模式没有相对寻址???@SuraajKS:EIP/RIP相对数据寻址模式是x86-64新增。它在32位模式下不可用。我想我可以说“无EIP相对寻址模式”因为如果它以32位模式存在,它就是这样。Yes call和jmp被编码为
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