分段故障,在.data节中有变量
我正在尝试学习nasm。我想制作一个打印“你好,世界”的程序。 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)
CMD:
nasm -f elf64 file.asm -o file.o
ld file.o -o file
./file
I am trying to learn nasm. I want to make a program that prints "Hello, world." n times (in this case 10). I am trying to save the loop register value in a constant so that it is not changed when the body of the loop is executed. When I try to do this I receive a segmentation fault error. I am not sure why this is happening.
My code:
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
The result:
Hello, world.
Segmentation fault (core dumped)
The cmd:
nasm -f elf64 file.asm -o file.o
ld file.o -o file
./file
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
章节.data
是崩溃的直接原因。较低案例部分.data
是特殊的,并链接为可执行文件的读写(私有)映射。部分名称对大小写。上盘
.data
对于NASM或Linker不是特别的,并且最终以文本段无需写入许可的映射读取+exec。代码>也很奇怪:默认情况下
objdump -drwc -mintel
仅拆卸.text
procepter(避免拆卸数据,就好像是代码一样),因此显示为空输出对于您的可执行文件。在较新的系统上权限,因此
.text
中的代码将segfault。与 indeblysbly。 >在GDB(
gdb ./foo
,starti
)下启动程序后,我从另一个外壳中查看了该过程的内存映射。As you can see, other than the special VDSO mappings and the stack, there's only the one file-backed mapping, and it has read+exec permission only.
Single-stepping inside GDB, the
mov eax,DWORD PTR ds:0x400086
load succeeds, but themov DWORD PTR ds:0x400086,eax
store faults。 (请参阅 x86 tag wiki for GDB ASM提示。) ,我们可以看到告诉OS的程序加载程序如何将其映射到内存中:
注意
.data
和.text 在同一段中。这就是您想要的
章节.rodata
(一个标准的部分名称,您应该在其中添加读取的常数数据(例如字符串),但它对可变的全局变量不起作用。修复您的ASM以使用
pection .Data
和.text
,readelf显示了我们:请注意,请注意,00 s segment 00是无w的r + e,
.text
部分在其中。段01是无exec的RW(read + Write),.Data
部分在那里。加载
标签意味着它们将其映射到该过程的虚拟地址空间中。某些部分(例如调试信息)不是,只是其他工具的元数据。但是NASM标志未知的部分名称为Progbits,即加载,这就是为什么它能够链接并具有负载而不是segfault的原因。将其修复到使用
e节.DATA
后,您的程序将在无segfaulting 的情况下运行。循环运行一次迭代,因为以下2个字节
步骤:DW 1
不是零。 DWORD加载后,我的系统上的rax = 0x2c0001
。 cmp 在0x002c0002和之间
(
”。 使用
DD
用于数据dword 。顺便说一句,无需将循环计数器保持在内存中。您不使用RDI,RSI,RBP或R8..R15用于任何东西,因此您可以将其保存在寄存器中。像
MOV EDI一样,限制
在循环之前,dec edi/jnz
在底部。但是实际上,如果要构建64位代码,则应使用64位
syscall
abi,而不是32位int 0x80
abi。 如果您在64位代码中使用32-1位INT 0x80 Linux ABI会发生什么?。或构建32位可执行文件,如果您遵循为此编写的指南或教程。无论如何,在这种情况下,您可以将
ebx
用作循环计数器,因为syscall abi使用不同的args用于寄存器。section .DATA
is the direct cause of the crash. Lower-casesection .data
is special, and linked as a read-write (private) mapping of the executable. Section names are case-sensitive.Upper-case
.DATA
is not special for nasm or the linker, and it ends up as part of the text segment mapped read+exec without write permission.Upper-case
.TEXT
is also weird: by defaultobjdump -drwC -Mintel
only disassembles the.text
section (to avoid disassembling data as if it were code), so it shows empty output for your executable.On newer systems, the default for a section name NASM doesn't recognize doesn't include exec permission, so code in
.TEXT
will segfault. Same as Assembly section .code and .text behave differentlyAfter starting the program under GDB (
gdb ./foo
,starti
), I looked at the process's memory map from another shell.As you can see, other than the special VDSO mappings and the stack, there's only the one file-backed mapping, and it has read+exec permission only.
Single-stepping inside GDB, the
mov eax,DWORD PTR ds:0x400086
load succeeds, but themov DWORD PTR ds:0x400086,eax
store faults. (See the bottom of the x86 tag wiki for GDB asm tips.)From
readelf -a foo
, we can see the ELF program headers that tell the OS's program loader how to map it into memory:Notice how both
.DATA
and.TEXT
are in the same segment. This is what you'd want forsection .rodata
(a standard section name where you should put read-only constant data like your string), but it won't work for mutable global variables.After fixing your asm to use
section .data
and.text
, readelf shows us:Notice how segment 00 is R + E without W, and the
.text
section is in there. Segment 01 is RW (read + write) without exec, and the.data
section is there.The
LOAD
tag means they're mapped into the process's virtual address space. Some section (like debug info) aren't, and are just metadata for other tools. But NASM flags unknown section names as progbits, i.e. loaded, which is why it was able to link and have the load not segfault.After fixing it to use
section .data
, your program runs without segfaulting.The loop runs for one iteration, because the 2 bytes following
step: dw 1
are not zero. After the dword load,RAX = 0x2c0001
on my system. (cmp
between 0x002c0002 and0xa
makes the LE condition false because it's not less or equal.)dw
means "data word" or "define word". Usedd
for a data dword.BTW, there's no need to keep your loop counter in memory. You're not using RDI, RSI, RBP, or R8..R15 for anything so you could just keep it in a register. Like
mov edi, limit
before the loop, anddec edi / jnz
at the bottom.But actually you should use the 64-bit
syscall
ABI if you want to build 64-bit code, not the 32-bitint 0x80
ABI. What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?. Or build 32-bit executables if you're following a guide or tutorial written for that.Anyway, in that case you'd be able to use
ebx
as your loop counter, because the syscall ABI uses different args for registers.