如何在汇编中打印字符串的长度
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 $ - 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
.
中的returns错误码
要使用 sys_write
输出任何内容到控制台,你必须首先将它作为 ASCII 写入内存(我认为 Ubuntu shell 默认支持 UTF8,但懒得验证)编码的字符串,并给出该内存的 sys_write
地址,以及以字节为单位的长度(对于 UTF8 字符串,字节和字符之间的区别很重要,sys_write
不知道字符,它适用于二进制文件和字节,所以长度是字节数)。
我不打算编写代码来输出数字,因为那有几行长(简化的 printf
实现)并且因此有几个 Q+A,但我希望我的解释能帮助你了解发生了什么及其运作方式。
如果您刚刚学习 ASM,请考虑链接到 clib
以使 printf
可用,或者更好,使用调试器,并直接在调试器的寄存器中验证值,不要打扰字符串输出,这是比初始算法、基本流控制和操作堆栈更高级的主题。在您对基本指令的工作原理以及如何调试代码更加熟悉之后,尝试输出数字会更加容易。
我正在使用以下 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 $ - 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,但懒得验证)编码的字符串,并给出该内存的 sys_write
地址,以及以字节为单位的长度(对于 UTF8 字符串,字节和字符之间的区别很重要,sys_write
不知道字符,它适用于二进制文件和字节,所以长度是字节数)。
我不打算编写代码来输出数字,因为那有几行长(简化的 printf
实现)并且因此有几个 Q+A,但我希望我的解释能帮助你了解发生了什么及其运作方式。
如果您刚刚学习 ASM,请考虑链接到 clib
以使 printf
可用,或者更好,使用调试器,并直接在调试器的寄存器中验证值,不要打扰字符串输出,这是比初始算法、基本流控制和操作堆栈更高级的主题。在您对基本指令的工作原理以及如何调试代码更加熟悉之后,尝试输出数字会更加容易。