Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 数据段之前的未初始化、可写数据_C_Assembly_Compilation_Brainfuck - Fatal编程技术网

C 数据段之前的未初始化、可写数据

C 数据段之前的未初始化、可写数据,c,assembly,compilation,brainfuck,C,Assembly,Compilation,Brainfuck,我正在编写一个简单的程序,将代码转换为x86_64汇编。其中一部分涉及在程序开始时创建一个大型零初始化数组。因此,每个编译的程序都以以下汇编代码开始: .data ARR: .space 32430 .text .globl _start .type _start, @function _start: ... #code as compiled from the brainfuck program ... 从那里编译的程序应该能够访问该数组的任何部分,但是如果它

我正在编写一个简单的程序,将代码转换为x86_64汇编。其中一部分涉及在程序开始时创建一个大型零初始化数组。因此,每个编译的程序都以以下汇编代码开始:

.data
ARR:
    .space 32430
.text
.globl _start
.type _start, @function
_start:
    ...     #code as compiled from the brainfuck program
    ...
从那里编译的程序应该能够访问该数组的任何部分,但是如果它试图在它之前或之后访问内存,它应该会出错

因为数组后面紧跟着一个.text部分,据我所知,该部分是只读的,而且因为它是程序的第一部分,所以我希望我所期望的行为会自然发生。不幸的是,情况并非如此:编译程序能够访问数组开头左侧(即地址低于)的非零初始化数据


为什么会出现这种情况?在汇编代码中是否有任何东西可以阻止这种情况?

这当然是高度依赖于系统的,但由于您的观察结果适合典型的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
解决此问题的一种方法是简单地计算
+
-
符号,以查看程序是否超出指定的大小-只需在超出允许的大小时引发分段错误即可
.text
应为只读,是否尝试使用
gdb
或类似工具对结果进行迭代?它可能不会覆盖
.text
您是对的,只有左边界被违反。事实上,最初的数组大小是30000,但程序能够写入超过数组开始的32430字节的位置;因此,我的带助解决方案简单地增加了初始化数组大小。对方的问题更难解决,;我假设链接器没有把我的片段放在我想的地方。我可以检查数据指针的每次移动是否有效,但这比我希望的开销要大;我更喜欢这样一种解决方案,即放置我的段时,坏的数组访问会导致segfault。在x86上,您总是被授予访问页面最后一个字节的权限(至少在现代操作系统中是这样)。查看生成的二进制文件(因为32430不是页面大小的倍数)并考虑分配粒度将非常有用。特别是,防护页面可能会有所帮助。@BrandonSides可能是ASLR导致的,试试看。你有没有尝试过写作,也远高于32340?不过,我认为我的解决方案可能更好——期望操作系统警告用户可能是一个非常糟糕的主意。同样,如果您只是转换为汇编,因此没有理由关心程序本身的行为,那么x86中的内存保护不是以字节粒度工作,而是按页工作。默认的
.data
段不太可能位于两个保护页之间(无效访问),即使如此,该段的大小也将是mem页面大小的倍数。我想我看到了两个比较优雅的选择。1) 检查OS API,如果您可以详细设置内存,在32+kiB缓冲区周围指定两个保护页,然后将缓冲区用作ARR->write/read+-4kiB outside=>crash。2) 创建64kiB ARR,并将所有ARR索引计算写入无符号16位,然后ARR[zero_extended_16b_index]将环绕。您应该使用
.bss
,而不是
.data
。还有,那个奇怪的asm是从哪里来的?为什么会有这么多16位的东西?您需要根据CX的高位字节进行调零,以便
movb
而不是
movzbl
工作,因此为什么不依赖于整个ECX和
jecxz
等等。(或者更好,使用
movzbl
删除循环中的假依赖项)。或者最好避免使用
jecxz
,因为它效率不高。此外,您可以乘以10并使用2条LEA指令进行累加,或者您可以使用
imul$10、%eax、%eax
而不是
dx
的笨重东西。此外,每个人都写
int$0x80
,而不是
$128
。此外,每个人都写int$0x80,而不是$128。-我已经驳斥了那个说法。嘿,说得对。其他人都像正常人一样写
int$0x80
。这只是风格问题;其他点导致不同(效率较低)的机器代码。
.data
    .align 4096
ARR:
    .space 30000        # we'll actually get 32768
.text
.globl _start
.type _start, @function
_start:
    mov (%esp),%ebx     # argc
    cmp $1,%ebx
    jbe 9f
    mov $0,%ax
    mov $1,%ebx         # sign 1
    mov 8(%esp),%esi    # argv[1]
0:  movb (%esi),%cl     # convert argument string to integer
    jcxz 1f
    sub $'0',%cl
    js  2f
    mov $10,%dx
    mul %dx
    add %cx,%ax
    jmp 3f
2:  neg %ebx            # change sign
3:  add $1,%esi
    jmp 0b
1:  mul %ebx            # multiply with sign 1 or -1
    movzx ARR(%eax),%ebx# load ARR[atoi(argv[1])]
9:  mov $1,%eax
    int $128            # _exit(ebx);