汇编部分 .code 和 .text 的行为不同

lon*_*ack 0 assembly x86-64 nasm elf

我是汇编新手,从我了解到的情况.code与 相同.text,但下面的代码将使用.code.

segment .data
    msg db "hello, world", 0xa
    len equ $ - msg

section .text
    global _start

_start:
    mov edx, len
    mov ecx, msg

    mov ebx, 1
    mov eax, 4
    int 0x80

    mov ebx, 0
    mov eax, 1
    int 0x80

Run Code Online (Sandbox Code Playgroud)
nasm -f elf64 -o hello.o hello.s 
ld -s -o hello hello.o
hello, world

sed -i s/.text/.code/ ./hello.s
nasm -f elf64 -o hello.o hello.s 
ld -s -o hello hello.o
./stack.sh: line 8:  4621 Segmentation fault      (core dumped) ./hello
Run Code Online (Sandbox Code Playgroud)

事实上,我不认为这有什么不同。为什么会出现这种情况?

Pet*_*des 6

在具有标准工具链 (GNU Binutils ld) 的 Linux 上,.text是一个“特殊”节名称,它得到特殊处理(默认情况下具有执行权限),但.code事实并非如此。(其他特殊部分包括.data(writeable) 和.bss(writable nobits),并且所有部分的默认对齐方式 > 1。)

section .textNASM ELF/Linux 相当于 Windows MASM.code指令,但这并不意味着Linux 工具可以识别.code指令或节名称1

section .code与 没有什么不同section xyz123;它只是使用默认值noexec nowrite 请参阅NASM 文档中表格other底部的条目。

用于readelf -a hello查看节(链接)和段(程序加载器)属性,明显缺少任何X地方。

脚注1:事实上,我认为Windows可执行文件仍然使用实际的节名称.text。至少 GNUobjdump -d仍然说代码在该.text部分中。所以MASM.code指令是切换到该.text部分的快捷方式。


有趣的事实:如果您将其构建为 32 位代码(您应该这样做,因为它仅使用 32 位int 0x80系统调用),这确实会“意外”正确运行,就像在从 16 位错误移植时使用的情况section .code一样MASM 代码到 Linux NASM。
或者,如果您在较旧的内核上运行 64 位代码。

原因是,在没有明确指定PT_GNU_STACK注释的情况下,内核对 32 位可执行文件使用向后兼容假设,并使用READ_IMPLIES_EXEC影响每个页面的用法:可执行文件 .data 部分的 Linux 默认行为在 5.4 和 5.9 之间发生了变化?。较旧的内核甚至对于 64 位可执行文件也会这样做,在这种情况下,较新的内核仅使堆栈本身可执行。

添加section .note.GNU-stack noalloc noexec nowrite progbits到源代码会使其出现应有的段错误,即使构建为 32 位可执行文件时也是如此。( nasm -felf32/ ld -melf_i386 -o foo foo.o)。看到这个答案

另请参阅有关旧情况的项​​目中包含程序集文件时来自 mmap 的意外 exec 权限。