Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.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
Linux 节数据中带有变量的分段错误_Linux_Assembly_X86_Segmentation Fault_Nasm - Fatal编程技术网

Linux 节数据中带有变量的分段错误

Linux 节数据中带有变量的分段错误,linux,assembly,x86,segmentation-fault,nasm,Linux,Assembly,X86,Segmentation Fault,Nasm,我正在努力学习nasm。我想制作一个打印“Hello,world.”n次的程序(在本例中为10次)。我试图将循环寄存器值保存在一个常量中,以便在执行循环体时不会更改它。当我尝试这样做时,我收到一个分段错误。我不知道为什么会这样 我的代码: SECTION .DATA print_str: db 'Hello, world.', 10 print_str_len: equ $-print_str limit: equ

我正在努力学习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)
cmd:

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

部分。数据是撞车的直接原因。小写
部分。数据是特殊的,作为可执行文件的读写(私有)映射链接

大写
.DATA
不是链接器专用的,它最终作为文本段的一部分,映射为read+exec,没有写入权限。
()

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


在GDB(
GDB./foo
starti
)下启动程序后,我从另一个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提示,请参阅的底部。)

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以使用
部分.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段是R+E,没有W,并且
.text
部分就在那里。段01是RW(读+写),没有exec,并且
.data
部分在那里

LOAD
标记表示它们被映射到进程的虚拟地址空间。有些部分(如调试信息)不是,只是其他工具的元数据。但是NASM将未知的节名标记为progbits,即loaded,这就是为什么它能够链接并使load not segfault


将其修复为使用
section.data
后,您的程序运行时不会出现分段故障

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

dw
表示“数据字”或“定义字”对数据dword使用
dd


顺便说一句,无需将循环计数器保留在内存中。您没有使用RDI、RSI、RBP或R8..R15进行任何操作,因此您可以将其保存在注册表中。与
mov-edi一样,在循环之前限制
,在底部限制
dec-edi/jnz

但实际上,如果要构建64位代码,应该使用64位
syscall
ABI,而不是32位
int0x80
ABI。或者,如果您正在遵循为此编写的指南或教程,则可以构建32位可执行文件


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

您将step定义为一个字(16位),但使用
mov[step],eax
将32位数据移动到它。以及节名
.DATA
.TEXT
应该是小写,而不是大写。您应该使用
dd
而不是
dw
。您修复了节名吗?请参阅我上面的评论。它提到的名称需要小写
.text
.data
是特殊的,但
.data
.text
不是特殊的。另请参见以获取后续参考。