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

时间:2017-07-28 07:47:53

标签: assembly x86 nasm

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

1 个答案:

答案 0 :(得分:2)

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

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

len2 equ $ - len之后的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,但是太懒而无法验证)编码字符串,并给{{ 1}}该内存的地址,以及字节长度(使用UTF8字符串,字节和字符之间的差异很重要,sys_write不知道字符,它适用于二进制文件和字节,所以长度是个字节)。

我不打算编写代码来输出数字,因为它有几行(简化sys_write实现)并且SO有几个Q + A,但我希望我的解释将帮助您了解发生了什么以及它是如何工作的。

如果您只是学习ASM,请考虑链接printf以使clib可用,甚至更好,使用调试器,并在调试器中直接验证寄存器中的值,不要打扰字符串输出,这是一个更高级的主题,然后是初始算术,基本流程控制和操作堆栈。在您更熟悉基本指令的工作方式以及如何调试代码之后,尝试输出数字会更容易。