我正在使用以下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表示的数字?
答案 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
可用,甚至更好,使用调试器,并在调试器中直接验证寄存器中的值,不要打扰字符串输出,这是一个更高级的主题,然后是初始算术,基本流程控制和操作堆栈。在您更熟悉基本指令的工作方式以及如何调试代码之后,尝试输出数字会更容易。