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

How to print the length of a string in assembly

我正在使用以下 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 表示的数字?

   ...
   mov  edx,len     ;message length

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

   mov  ecx,msg     ;message to write

这会加载 ecx 第一个字符的地址(msg 是标签,指向内存)。

   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,这里的$相当于"current address",下一个定义的机器码字节会在这里编译,所以在这个地方等于msg + 14(我希望我没数错字符数 :))。并且 ((msg+14) - msg) = 14 = len 的定义和标签 msg.

之间内存中定义的字节数

请注意我是如何避免将单词作为变量或字符的,ASM 级别更低,因此内存中的标签和字节是更准确的措辞,希望它能帮助您识别细微差别。

您的 len2 equ $ - lenlen 之后确实将值 len2 定义为 (msg+14)(仍然存在于内存中,len 没有添加新字节定义) 减去 len14,因此您实际上定义了 len2 等于 msg.

然后:

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

是否调用 sys_write 并使用指向字符串的指针等于 14(无效的内存引用,普通用户代码不能访问该内存区域),并且长度等于地址 msg,这将在 32b linux 上很可能是一些像 0x80004000 这样的值,即要输出超过 2G 的字符。

sys_write自然不喜欢,失败,eax.

中的returns错误码

要使用 sys_write 输出任何内容到控制台,你必须首先将它作为 ASCII 写入内存(我认为 Ubuntu shell 默认支持 UTF8,但懒得验证)编码的字符串,并给出该内存的 sys_write 地址,以及以字节为单位的长度(对于 UTF8 字符串,字节和字符之间的区别很重要,sys_write 不知道字符,它适用于二进制文件和字节,所以长度是字节数)。

我不打算编写代码来输出数字,因为那有几行长(简化的 printf 实现)并且因此有几个 Q+A,但我希望我的解释能帮助你了解发生了什么及其运作方式。

如果您刚刚学习 ASM,请考虑链接到 clib 以使 printf 可用,或者更好,使用调试器,并直接在调试器的寄存器中验证值,不要打扰字符串输出,这是比初始算法、基本流控制和操作堆栈更高级的主题。在您对基本指令的工作原理以及如何调试代码更加熟悉之后,尝试输出数字会更加容易。