编写 x86 程序集时链接描述文件的作用

Role of linker scripts when writing x86 assembly

我正在学习 x86 汇编是出于我自己的好奇心,以了解底层的东西,并且遇到了这个很棒的存储库 here,其中包含很多示例,这些示例可以 运行 来自 EFI shell.

当我检查 this hello world example 时,有一个包含以下内容的链接描述文件:

ENTRY(mystart)
SECTIONS
{
  . = 0x7c00;
  .text : {
    entry.o(.text)
    *(.text)
    *(.data)
    *(.rodata)
    __bss_start = .;
    /* COMMON vs BSS:  */
    *(.bss)
    *(COMMON)
    __bss_end = .;
  }
  /*  */
  .sig : AT(ADDR(.text) + 512 - 2)
  {
      SHORT(0xaa55);
  }
  /DISCARD/ : {
    *(.eh_frame)
  }
  __stack_bottom = .;
  . = . + 0x1000;
  __stack_top = .;
}

我不明白为什么需要它?只是为了指定加载地址?我对链接器脚本的一般理解是,当有多个目标文件时它们更有用,并且链接器脚本可用于定义如何将多个目标文件的部分组合到单个可执行文件中。

如果我在这个例子中没有指定链接描述文件怎么办? (肯定至少有 2 个目标文件 - 一个来自 .s,一个来自 .c

请注意,这是一个裸机示例,意味着没有操作系统。

安装在您计算机上的 gnu 工具链可能是为该计算机构建的,包括操作系统。

因此,当您 apt-get install build-essential 然后 gcc hello.c -o hello 时,使用的 linker 脚本是已安装工具链的一部分,并且特定于 Linux,您的发行版。 (即使您从源代码构建工具链和 libc,它也会检测到主机,如果不是作为交叉编译器构建,将使该主机的库存 bootstrap 和 linker 脚本成为默认脚本)

当您为 windows 找到并安装 gnu 工具链时,埋在该安装中的 linker 脚本特定于 windows。

但是当你想在这种情况下将工具链用作裸机的交叉编译器时,你需要 link 用于目标环境,这通常意味着带上你自己的 linker脚本,这个和往常一样过于复杂,但至少他们提供了一个。

作为 x86 裸机并使用 x86 主机进行开发,您可以(有时)将本机编译器用作交叉编译器。同样适用于在 arm 主机上构建 arm(例如 raspberry pi)等

如果没有 linker 脚本,当构建用于交叉编译的东西时,将使用默认脚本,如果您没有为您的目标自定义默认脚本,那么您可能会得到一个无法工作的构建。

linker 脚本的工作主要是为linker 定义地址space。我想要这个地址的 .text 我想要这个地址的 .data 等等。您可以使用命令行而不使用 linker 脚本来执行此操作,但它变得越简单,您想要获得的越复杂,gnu ld 在命令行与 linker 脚本方面存在一些问题(错误)。然后第二个原因是对于特定的语言你有一个 bootstrap,并且需要在 bootstrap 中满足一些语言假设,但是为了方便你需要 space 的地址部分 linker 的工作,以便于 linker 脚本。您正在让 linker/tools 为您完成工作。

因此,对于 C,假定 .bss 已归零并且 .data 填充了您在代码入口点之前要求的项目(通常是 main(),但在裸机中您可以做任何想做的事情并且通常不想使用那个函数名)被调用。作为一种省力的设备,您使用 linker 将所有项目放在您要求的位置,因此所有文本、所有 bss、数据和 rodata 等。它修补了函数之间的外部连接。但是现在 linker 知道 .bss 的位置和大小,例如,您如何将其传达给 bootstrap 代码?嗯,gnu 和其他工具链提供了一种机制(gnus 解决方案预计不会移植到任何其他工具,假设所有 linker 脚本语言都是自定义工具链并且不可移植,所以你必须编写新的和新的 bootstrap 对于每个工具链)为此。您可以在 linker 脚本中创建变量,linker 可以随意填写,.bss 的起始地址和结束地址,或者您可以在 linker 脚本中进行更多数学运算并获取 .bss 的起始地址和大小,然后将该变量导入 bootstrap 汇编语言代码(不能使用 C,这是先有鸡还是先有蛋的问题),现在 bootstrap 可以将 .bss 归零。

所以我称这是 bootstrap 代码和 linker 脚本之间的结合,出于多种原因,它们都是特定于工具链的,汇编语言是由汇编程序而不是目标定义的因此没有理由假设一个工具链的 x86 汇编语言(这与 Intel vs AT&T 无关)与另一个工具链汇编程序兼容,其次 linker 脚本语言也不被假设为跨工具链和特定的可移植到那个工具链。因此,您正在使用特定于工具链的语言,以 C 为例,您必须在调用任何已编译代码之前执行一些任务。组成 linking 和 bootstrap 的两个或多个文件是密切相关的。

请注意,此示例还包含一些 bootstrap 代码。我会寻找一个更清晰的例子,真正的汇编与内联,特别是因为项目中有一个汇编语言文件,C 部分可能一直在演示 C 而不是脚本内联汇编语言的东西。 link 教程似乎确实解释了正在发生的事情,所以也许所有这些都得到了解释。

裸机的美妙之处在于你可以做任何你想做的事,你可以遵循的规则更少,所以作者做到了。我个人不希望 .bss 归零并且不使用 .data 所以我的非便携式部分,linker 脚本和 bootstrap 不那么复杂。欢迎您有自己的风格和喜好,裸机编程之美。