Linux 当您从db$-msg移动edx[msgLen]时,为什么写入系统调用会打印一堆垃圾,与地址相同?

Linux 当您从db$-msg移动edx[msgLen]时,为什么写入系统调用会打印一堆垃圾,与地址相同?,linux,assembly,x86,nasm,dereference,Linux,Assembly,X86,Nasm,Dereference,出于某种原因,[msgLen]和msgLen产生了相同的结果。我知道我应该使用equ或其他什么,但我想知道为什么这不起作用。NASM是否忽略了解引用?它打印了我的字符串和一堆垃圾,它认为字符串比它本身大,对吗?谢谢 请参阅下面的大会: _start: mov edx,[msgLen] mov ecx,msg mov ebx,1 mov eax,4 int 0x80 mov eax,1 int 0x80 section .data

出于某种原因,[msgLen]和msgLen产生了相同的结果。我知道我应该使用equ或其他什么,但我想知道为什么这不起作用。NASM是否忽略了解引用?它打印了我的字符串和一堆垃圾,它认为字符串比它本身大,对吗?谢谢

请参阅下面的大会:

_start:
    mov edx,[msgLen]
    mov ecx,msg
    mov ebx,1
    mov eax,4
    int 0x80
    mov eax,1
    int 0x80

section .data
    msg db 'Zoom',0xA
    msgLen db $-msg       

加载后使用调试器查看EDX,或使用
strace./a.out
查看实际传递给系统调用的长度。
它将不同(地址与加载的值)。这两种方法都有不同的缺陷,恰好产生了较大的值,因此效果恰好相同。不过,调试/跟踪工具会清楚地告诉您,NASM并没有“忽略取消引用”

mov edx,msgLen
是地址
,就在msg旁边。如果您使用
ld-melf\u i386-o foo foo.o
链接到标准的Linux非PIE静态可执行文件,它将类似于
0x804a005

mov-edx,[msgLen]
从该地址(edx的宽度)加载4个字节,在该地址中,您仅在数据部分将大小组合为1个字节。3个高字节来自映射到内存页的文件部分的下一个字节

当我仅使用
nasm-felf32 foo.asm
进行组装并链接到
ld-melf_i386
时,正好是3个字节的零,因此我不会从dword加载版本中打印任何垃圾。(x86是little endian)

但您的非零字节似乎是调试信息。我发现NASM的调试信息与有用信息相反,有时使GDB的反汇编变得混乱(
layout reg
/
layout asm
有时在标签后无法反汇编整个块),所以我不使用它

但是如果我使用
nasm-felf32-Fdwarf
,那么我确实从
.data
部分末尾的dword加载中获得
7173
。这是一个不同的大数,所以这是一个不同的错误,不是同一个问题
7173
0x1c05
,因此它对应于
DB5,0x1c,0,0
。i、 e.您计算的长度5是低字节,但它后面有一个
0x1c
yasm-gdwarf2
给了我
469762053
=
0x1c000005

如果使用了
db$-msg、0,0,0
dd$-msg
,则可以加载整个dword。(要将字节加载并零扩展到dword寄存器中,请使用
movzx-edx,byte[mem]


write()
长度较大的行为 如果给它一个很大的长度,
write
将一直到它到达一个不可读的页面,然后返回它实际写入的长度。(在开始从用户复制之前,它不会检查整个缓冲区的可读性。如果在遇到不可读页面之前写入的字节数不为零,它会返回写入的字节数。如果立即遇到不可读页面,则只能获得
-EFAULT
,而不是传递包含一些未映射的p的巨大长度(过了很久。)

e、 g.带有
mov edx,msgLen
(标签地址)

134520837
是您传递的长度,0x804a005(msgLen的地址)。系统调用在到达未映射的页面并提前停止之前写入4096字节,1整页。**您传递的数字高于该数字并不重要,因为映射结束之前只有1页。(而且
msg:
显然就在那一页的开头。)

在终端上(其中
\0
打印为空),您通常只看到可打印字符;如果您想更好地查看二进制数据,请将管道插入到hextump-C。它包含来自文件的元数据位,因为内核的ELF程序加载器通过将文件映射到内存中来工作(该部分使用MAP_私有读写no-exec映射)。使用
readelf-a
并查看ELF程序头:它们告诉内核文件的哪些部分要映射到内存中,在哪里,使用什么权限


有趣的事实:如果我重定向到/dev/null(
strace./foo>/dev/null
),内核的
write
处理程序甚至不会检查缓冲区的权限,因此
write()
实际上会返回
134520837

write(1,“缩放\n\5\0\0[…]\0\0\220\4\10”…,134520837)=134520837


正确使用eq 是的,您应该使用
eq
,这样您就可以使用实际长度作为立即数。让汇编程序计算它,然后将该字节汇编到数据段中不太方便。这样打印的大小正好合适,并且比使用绝对地址引用1个常量字节更有效

   mov   edx, msg.len         ; mov r32, imm32
  ...

section .rodata
  msg:  db 'Zoom',0xA
  .len  equ $-msg       
(使用NASM
本地标签与使用
eq
无关;我也展示了这一点,因为我喜欢它帮助组织全局名称空间的方式。)



还与
ld
如何为程序加载器将节布局为ELF段相关:或类似的内容,以避免在不需要的地方映射数据。尤其是在可执行页面之外。

在加载后使用调试器查看EDX,或者使用
strace./a.out
查看实际传递给系统调用的长度。
它将不同(地址与加载的值)。这两种方法都有不同的缺陷,恰好产生了较大的值,因此效果恰好相同。不过,调试/跟踪工具会清楚地告诉您,NASM并没有“忽略取消引用”

mov edx,msgLen
是地址
,就在msg旁边。如果您链接到一个标准的Linux非PIE s,它将类似于
0x804a005
   mov   edx, msg.len         ; mov r32, imm32
  ...

section .rodata
  msg:  db 'Zoom',0xA
  .len  equ $-msg