gpt4 book ai didi

linux - SECTION .DATA 中变量的段错误

转载 作者:太空宇宙 更新时间:2023-11-04 10:03:43 25 4
gpt4 key购买 nike

我正在努力学习 nasm。我想制作一个打印“Hello, world”的程序。 n 次(在本例中为 10 次)。我试图将循环寄存器值保存在一个常量中,以便在执行循环体时它不会改变。当我尝试这样做时,我收到了段错误。我不确定为什么会这样。

我的代码:

SECTION .DATA
print_str: db 'Hello, world.', 10
print_str_len: equ $-print_str

limit: equ 10
step: dw 1

SECTION .TEXT
GLOBAL _start

_start:
mov eax, 4 ; 'write' system call = 4
mov ebx, 1 ; file descriptor 1 = STDOUT
mov ecx, print_str ; string to write
mov edx, print_str_len ; length of string to write
int 80h ; call the kernel

mov eax, [step] ; moves the step value to eax
inc eax ; Increment
mov [step], eax ; moves the eax value to step
cmp eax, limit ; Compare sil to the limit
jle _start ; Loop while less or equal

exit:
mov eax, 1 ; 'exit' system call
mov ebx, 0 ; exit with error code 0
int 80h ; call the kernel

结果:

Hello, world.
Segmentation fault (core dumped)

命令:

nasm -f elf64 file.asm -o file.o
ld file.o -o file
./file

最佳答案

section .DATA是坠机的直接原因。小写 section .data是特殊的,并链接为可执行文件的读写(私有(private))映射。部分名称区分大小写。

大写 .DATA对于 nasm 或链接器来说不是特殊的,它最终作为 text segment 的一部分没有写入权限的映射读取+执行。

大写 .TEXT也很奇怪:默认情况下 objdump -drwC -Mintel只反汇编 .text部分(以避免将数据像代码一样反汇编),因此它显示可执行文件的空输出。

On newer systems , NASM 无法识别的部分名称的默认值不包括执行权限,因此在 .TEXT 中编写代码会出现段错误。与 Assembly section .code and .text behave differently 相同


在 GDB(gdb ./foostarti)下启动程序后,我从另一个 shell 查看进程的内存映射。

$ cat /proc/11343/maps
00400000-00401000 r-xp 00000000 00:31 110651257 /tmp/foo
7ffff7ffa000-7ffff7ffd000 r--p 00000000 00:00 0 [vvar]
7ffff7ffd000-7ffff7fff000 r-xp 00000000 00:00 0 [vdso]
7ffffffde000-7ffffffff000 rwxp 00000000 00:00 0 [stack]

如您所见,除了特殊的 VDSO 映射和堆栈之外,只有一个文件支持的映射,并且它只有 read+exec 权限。

GDB 中的单步执行,mov eax,DWORD PTR ds:0x400086加载成功,但是 mov DWORD PTR ds:0x400086,eax存储故障。 (有关 GDB asm 提示,请参阅 x86 tag wiki 的底部。)

来自 readelf -a foo ,我们可以看到 ELF 程序头告诉操作系统的程序加载器如何将其映射到内存中:

$ readelf -a foo   # broken version
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000bf 0x00000000000000bf R 0x200000

Section to Segment mapping:
Segment Sections...
00 .DATA .TEXT

注意 .DATA.TEXT在同一段。这就是您想要的 section .rodata (一个标准的部分名称,您应该在其中放置只读常量数据,例如您的字符串),但它不适用于可变全局变量。

在修复你的 asm 之后使用 section .data.text , readelf 向我们展示:

$ readelf -a foo    # fixed version
...
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000e7 0x00000000000000e7 R E 0x200000
LOAD 0x00000000000000e8 0x00000000006000e8 0x00000000006000e8
0x0000000000000010 0x0000000000000010 RW 0x200000

Section to Segment mapping:
Segment Sections...
00 .text
01 .data

注意段 00 是如何没有 W 的 R + E,以及 .text部分在那里。段 01 是没有 exec 的 RW(读 + 写),.data部分在那里。

LOAD标记意味着它们被映射到进程的虚拟地址空间。有些部分(如调试信息)不是,只是其他工具的元数据。但是 NASM 将未知的部分名称标记为 progbits,即已加载,这就是为什么它能够链接并且加载不是段错误的原因。


修复后使用section .data ,您的程序运行时不会出现段错误

循环运行一次迭代,因为 step: dw 1 后面的 2 个字节不为零。双字加载后,RAX = 0x2c0001在我的系统上。 (0x002c0002 和 cmp 之间的 0xa 使 LE 条件为假,因为它不小于或等于。)

dw表示“数据字”或“定义字”。 使用dd对于数据双字


顺便说一句,无需将循环计数器保存在内存中。您没有将 RDI、RSI、RBP 或 R8..R15 用于任何用途,因此您可以将其保存在寄存器中。喜欢mov edi, limit在循环之前,和 dec edi / jnz在底部。

但实际上你应该使用 64 位 syscall ABI 如果您想构建 64 位代码,而不是 32 位代码 int 0x80阿比。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? .或者构建 32 位可执行文件(如果您遵循为此编写的指南或教程)。

无论如何,在那种情况下你可以使用 ebx作为循环计数器,因为系统调用 ABI 使用不同的寄存器参数。

关于linux - SECTION .DATA 中变量的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54134394/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com