assembly 如何在汇编中打印字符串的长度

nfs0ujit  于 2023-06-23  发布在  其他
关注(0)|答案(1)|浏览(117)

我正在使用下面的hello world程序学习汇编

section .text
    global _start     ;must be declared for linker (ld)

_start:             ;tells linker entry point
   mov  edx,len     ;message length
   mov  ecx,msg     ;message to write
   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel

   mov  eax,1       ;system call number (sys_exit)
   int  0x80        ;call kernel

section .data
    msg db 'Hello, world!', 0xa  ;our string
    len equ $ - msg              ;length of our string

我最初的问题是字符串的长度是什么意思。它是指字符数还是内存中的长度(字节数)?为了检查这一点,我想打印变量len。我该怎么做?我天真地试图定义新变量

len2 equ $ - len

然后跑了

mov  edx,len2     ;message length
   mov  ecx,len     ;message to write
   mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel

尝试打印len,但这个什么也没打印出来。如何打印由len表示的数字?

nkkqxpd9

nkkqxpd91#

...
   mov  edx,len     ;message length

这将为edx加载某种数值,例如本例中的14。len是“equ”常量符号,类似于C中的#define

mov  ecx,msg     ;message to write

这将用第一个字符的地址加载ecxmsg是标签,指向内存)。

mov  ebx,1       ;file descriptor (stdout)
   mov  eax,4       ;system call number (sys_write)
   int  0x80        ;call kernel
   ...

    msg db 'Hello, world!', 0xa  ;our string

这定义了14个字节的存储器,具有值72('H ')、101('e')、…第一字节由msg标签(其存储器地址)指向。

len equ $ - msg              ;length of our string

这定义了在编译时可见的常量len。它没有定义任何内存内容,所以你不能在可执行文件或运行时找到它(除非使用,比如mov edx,len,然后它被编译成特定的指令)。
定义是$ - msg,在这个上下文中,$作为“当前地址”,下一个定义的机器码字节将被编译,所以在这个地方它等于msg + 14(我希望我正确地计算了字符数:)。((msg+14) - msg) = 14 =在len的定义和标签msg之间的存储器中定义的字节数。
注意我是如何避免将单词作为变量或字符的,ASM是更低级别的,因此将标签放入内存和字节是更准确的措辞,我希望这将有助于您识别细微的差异。
len之后的len2 equ $ - len确实将值len2定义为(msg+14)(仍然在内存中,len定义没有添加新字节)减去len,即14,因此实际上将len2定义为msg
然后:

mov  edx,len2     ;message length
   mov  ecx,len     ;message to write
   ...

调用sys_write时,指针指向的字符串等于14(无效的内存引用,该内存区域对普通用户代码来说是禁区),长度等于地址msg,在32b linux上很可能是像0x80004000这样的值,即超过2G的字符输出。
sys_write自然不喜欢这样,失败,并在eax中返回错误代码。
要使用sys_write将任何内容输出到控制台,必须首先将其作为ASCII写入内存(我认为Ubuntu shell默认支持UTF8,但懒得验证)编码字符串,并给出该内存的sys_write地址,以及字节长度(对于UTF8字符串,字节和字符之间的差异很重要,sys_write不知道字符,它适用于二进制文件和字节,因此长度是字节数)。
我不打算写代码来输出数字,因为那有几行长(简化的printf实现),SO对此有几个Q + A,但我希望我的解释能帮助你理解发生了什么以及它是如何工作的。
如果你刚开始学习ASM,可以考虑链接clib以获得printf,或者更好的是,使用调试器,直接在调试器的寄存器中验证值,不要为字符串输出而烦恼,这是一个比初始算法、基本流控制和操作堆栈更高级的主题。当你对基本指令的工作方式以及如何调试代码更加熟悉之后,尝试输出数字将更加容易。

相关问题