3

如果这个问题被认为太琐碎,我提前道歉,但我确实花了很多时间试图在网上找到一个直截了当的答案,但找不到。

我最近读了一个关于程序分段的介绍性编程课程,以及程序通常如何划分为“段”,这些“段”在需要时单独加载到内存中(或部分加载到内存中,通过分页......我认为)。我们的书提到程序通常根据逻辑划分被划分为段,例如堆栈段、堆段、全局常量段等。

我想知道究竟是什么决定了这种分割是如何工作的。它是由编译器在编译时完成的吗?或者操作系统是否以某种方式处理它?每个子程序通常都有自己的段吗,不管它有多小?

我知道与段描述符等分段相关的信息是在体系结构级别使用寄存器处理的,并且专门分配用于处理分段。但我只是很难想象程序的实际分段首先在哪里/如何发生,以及这些信息如何进入这些寄存器。地址如何转换为段 id 和偏移量?任何人都可以启发我吗?非常感谢您提供的任何帮助,如果我在这里扼杀了任何概念,我们深表歉意。

4

3 回答 3

1

这是指可执行文件格式;正如其他人所指出的,链接器将它们放在一起。

在我的 OS X 系统上,file /bin/ls报告/bin/ls: Mach-O universal binary with 2 architectures

接下来,您要查找有关该格式的详细信息以及阅读它们的工具。我认为,实际上查看这些细分将使您对其中的内容及其结构有一个很好的了解。

从后一个链接:

每个 Mach-O 文件由一个 Mach-O 头、一系列加载命令和一个或多个段组成,每个段包含 0 到 255 个段。Mach-O 使用 REL 重定位格式来处理对符号的引用。在查找符号时,Mach-O 使用两级命名空间,将每个符号编码为“对象/符号名称”对,然后首先线性搜索对象,然后是符号名称。

基本结构——一个可变长度的“加载命令”列表,引用文件中其他地方的数据页——也用于 Accent 的可执行文件格式。反过来,Accent 文件格式是基于 Spice Lisp 的一个想法。

为了完整起见,其他操作系统的其他工具:

于 2012-04-28T05:26:23.923 回答
1

这是一个很好的问题,我只能提供一些信息,可能会引导您朝着正确的方向前进。我相信程序分段是由可执行文件格式定义的,因此如果您需要特定信息,请查找您的本机格式的规范(例如,任何各种 ELF 变体)。如果只是为了透视和查看更简单的规范,阅读诸如 a.out 或旧的“MZ”DOS 二进制文件之类的旧格式可能会很有趣。[编辑:为清楚起见]

正如您似乎已经猜到的那样,分段是由工具链(主要是链接器,尽管编译器有一些影响:例如全局 C 变量与局部变量进入不同的段,后者进入堆栈)和操作系统共同处理. 举一个涉及操作系统的例子,好的操作系统利用硬件平台的内存保护特性来强制正确使用程序的段。

希望这将为您提供进一步研究的材料。

于 2012-04-28T05:44:08.710 回答
1

这里有一些基本的想法。

我们希望确保程序的代码和常量数据在运行时不会因为其中的错误或利用它们的恶意输入而被修改。如果检测到尝试,操作系统应终止程序。好处:捕捉错误,提高安全性。典型实现机制:页级内存保护。

我们通常也不希望程序中的任何数据区域都是可执行的。恶意输入可以利用程序错误并导致在这些区域执行任意(攻击者控制的)代码。相同的实现机制。

在无法读取/写入/执行的不同类型的程序部分(代码/常量、数据、堆栈)之间存在内存间隙(通常称为保护页)可以捕获一些缓冲区溢出错误,这又可以具有安全性影响。有时,这样的特殊间隙会放置在每个数据对象之前和之后。在生产代码中,它们的成本太高(因为内存浪费和需要执行额外的代码来管理它们),但它们在调试时会有很大的帮助。

代码和数据逻辑分离的另一个原因是共享库(例如 DLL)。您的操作系统可以在不同进程之间共享(同样,通过使用页面翻译)仅库的代码,从而节省内存,同时维护这些进程中的各个数据区域。

当您阅读并理解页面转换(使用所有这些页表和虚拟到物理地址转换)时,您将了解如何使所有这些成为可能。

最后,可能存在某些硬件限制,例如分段地址空间。x86 CPU 的 16 位模式就是这种情况。在这些模式下,即使您最多可以访问大约 1MB 的内存(在实寻址模式和虚拟 8086 模式下)和 16MB 的内存(在 16 位保护模式下),CPU 也会强制您使用地址分为 16 位部分、段选择器和偏移量。在每个这样的段中,您最多只能访问 65536 个字节。如果您需要更多,则必须使用多个段,并且为了在段之间切换,您需要重新加载段寄存器以指向感兴趣的段。这种限制使得许多 MSDOS 汇编器和编译器生成的对象(=部分编译)和可执行(=完全编译)代码在各个程序部分之间具有清晰的界限,

于 2012-04-28T05:55:22.967 回答