如何在程序集中打印字符串的长度



>我正在使用以下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

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

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是"等值"常数符号,类似于 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,此上下文中的$用作"当前地址",其中将编译下一个定义的机器代码字节,因此在这个地方它等于msg + 14(我希望我确实正确计算了字符数:)(。而((msg+14) - msg) = 14=len定义和标签msg之间的内存中定义的字节数。

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

因此,您在len之后的len2 equ $ - len确实将值len2定义为(msg+14)(仍然存在于内存中,len定义没有添加新字节(减去14len,因此您有效地定义了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,或者更好的是,使用调试器,并直接在调试器中的寄存器中验证值,不要为字符串输出而烦恼,这是一个比初始算术、基本流控制和操作堆栈更高级的主题。在你对基本指令的工作原理以及如何调试代码更加熟悉之后,尝试输出数字会更容易。

最新更新