编写x86程序集时链接器脚本的角色



我学习x86汇编是出于对理解底层内容的好奇心,我发现了这个很棒的存储库,其中包含了许多可以从EFI shell运行的示例。

当我查看这个helloworld示例时,有一个链接器脚本,其中包含以下内容:

ENTRY(mystart)
SECTIONS
{
. = 0x7c00;
.text : {
entry.o(.text)
*(.text)
*(.data)
*(.rodata)
__bss_start = .;
/* COMMON vs BSS: https://stackoverflow.com/questions/16835716/bss-vs-common-what-goes-where */
*(.bss)
*(COMMON)
__bss_end = .;
}
/* https://stackoverflow.com/questions/53584666/why-does-gnu-ld-include-a-section-that-does-not-appear-in-the-linker-script */
.sig : AT(ADDR(.text) + 512 - 2)
{
SHORT(0xaa55);
}
/DISCARD/ : {
*(.eh_frame)
}
__stack_bottom = .;
. = . + 0x1000;
__stack_top = .;
}

我不明白为什么它是必需的?只是为了指定加载地址?我对链接器脚本的总体理解是,当有多个对象文件时,它们更有用,并且链接器脚本可以用来定义如何将多个对象档案中的部分组合成一个可执行文件。

如果我在这个例子中没有指定链接器脚本怎么办?(肯定至少有两个对象文件,一个来自.s,一个源自.c)

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

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

因此,当您倾向于获得install-build-ential然后gcc hello.c -o hello时,所使用的链接器脚本是已安装工具链的一部分,并且特定于您的发行版Linux。(即使您从源代码构建工具链和libc,它也会检测到主机,如果不是作为交叉编译器构建,则会使该主机的股票引导程序和链接器脚本成为默认脚本)

当您为windows找到并安装gnu工具链时,该安装中隐藏的链接器脚本是特定于windows的。

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

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

如果没有链接器脚本,在构建用于交叉编译的东西时,将使用默认脚本,如果您没有为目标自定义默认脚本,那么您可能会得到一个不起作用的构建。

链接器脚本的工作主要是定义链接器的地址空间。我想要这个地址的.text。这个地址的data等等。你可以用命令行和不带链接器脚本来做这件事,但你想得到的越复杂,它就越简单,gnu ld在命令行和链接器脚本之间有一些问题(bug)。第二个原因是,对于特定的语言,您有一个引导程序,并且在引导程序中需要满足一些语言假设,但为了方便起见,您需要链接器作业的地址空间部分来方便链接器脚本。您让链接器/工具为您完成工作。

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

所以我称之为引导程序代码和链接器脚本之间的结合,它们都是特定于工具链的,原因不止一个,汇编语言是由汇编程序定义的,而不是目标,所以没有理由假设一个工具链的x86汇编语言(这与英特尔与AT&T无关)与另一个工具串汇编程序兼容,其次,链接器脚本语言也不被认为是跨工具链可移植的,并且是特定于该工具链的。因此,您使用的是特定于工具链的语言,以C为例,在调用任何编译的代码之前,您必须执行一些任务。构成链接和引导程序的两个或多个文件是紧密连接的。

请注意,此示例还包含一些引导程序代码。我想找一个更干净的例子,真正的汇编与内联,特别是因为项目中有一个汇编语言文件,C部分本可以演示C,而不是脚本式的内联汇编语言。它似乎链接到了一个教程,该教程解释了正在发生的事情,所以也许所有这些都得到了解释。

裸金属的美妙之处在于,你可以做任何你想做的事,你没有那么多的生活规则,所以这位作者做到了。我个人不希望.bss为零,也不使用.data,所以我的非可移植部分、链接器脚本和引导程序就不那么复杂了。欢迎您选择自己的风格和偏好,体验裸机编程的美妙之处。

相关内容

最新更新