我正在编写一个将brainfuck代码转换为x86_64程序集的简单程序。部分原因是在程序开始时创建一个大的零初始化数组。因此,每个编译的程序都以以下汇编代码开头:
.data
ARR:
.space 32430
.text
.globl _start
.type _start, @function
_start:
... #code as compiled from the brainfuck program
...
从那里编译的程序应该能够访问该数组的任何部分,但如果它试图在它之前或之后访问内存它应该是段错误的。
因为数组直接跟着一个.text部分,我的理解是只读的,并且因为它是程序的第一部分,所以我期望我想要的行为会自然而然地跟随。不幸的是,情况并非如此:已编译的程序能够访问数组开头左边(即地址低于)的非零初始化数据。
为什么会出现这种情况,是否有任何我可以在汇编代码中包含哪些内容来阻止它呢?
答案 0 :(得分:0)
这当然是高度依赖系统的,但由于您的观察结果适合典型的Linux / GNU系统,我将参考这样一个系统。
我认为链接器并不是将我的片段放在我认为的位置。
是的,链接器不会按照它们在您的代码段中显示的顺序放置段,而是首先.text
,.data
秒。我们可以看到这个e。 G。与
> objdump -h ARR ARR: file format elf32-i386 Sections: Idx Name Size VMA LMA File off Algn 0 .text 00000042 08048074 08048074 00000074 2**2 CONTENTS, ALLOC, LOAD, READONLY, CODE 1 .data 00007eae 080490b8 080490b8 000000b8 2**2 CONTENTS, ALLOC, LOAD, DATA
编译的程序能够访问数组开头左边(即地址低于)的非零初始化数据。
为什么会这样呢......
正如我们在上面的示例中所看到的,.data
部分在内存地址080490b8
处链接。虽然内存页面的长度为PAGE_SIZE(此处getconf PAGE_SIZE
产生4096
,即1000 16 )并从该大小的倍数开始,但数据的起始地址偏移量等于文件偏移 000000b8
(数据存储在磁盘文件中),因为包含.data
部分的文件页面作为copy-on-write页面映射到内存中。 .data
部分下面的非零初始化数据恰好出现在第一个文件页面0到b7 16 的位置,包括{{1 }}
...我可以在汇编代码中包含任何可以阻止它的内容吗?
我更喜欢使用一种解决方案来放置我的网段,以防错误的数组访问导致段错误。
正如Margaret Bloom和Ped7g暗示的那样,你可以在.text
以下分配额外的数据并创建一个无法访问的防护页面。通过将ARR
与下一个页面地址对齐,可以轻松完成此操作。下面的示例程序实现了这一点,并允许通过接受访问ARR
数据的索引参数(可选地为负)来测试它;如果在边界内,它应该退出状态0,否则段错误。注意:此方法仅在ARR
部分未在页面边界处结束时有效,因为如果确实如此,则.text
无效;但由于汇编代码是使用转换器程序创建的,因此该程序应该能够检查并在需要时添加一些额外的.align 4096
字节。
.text