这里的新人,我已经有了一个问题。
我正在调整Jeff Duntemann的汇编书中使用的示例代码,我想将存储在数据寄存器中的整数值打印到终端?
以下代码的作用是打印出好的字符串,在ECX中推送值好,但是当它达到以下代码时:
pop ecx
mov eax,4
mov ebx,1
mov edx, ecx
int 80h
它没有在终端上显示edx的内容,不过我想我用mov eax,4等告诉它。
任何人都能给我任何“指针”(双关语)?
参考编号(自2012年6月17日起修订):
SECTION .data
submessage: db "I am subtracting 5 from 10!", 10
msglen: equ $-submessage
ansmsg: db "Answer is:", 10
msglen2: equ $-ansmsg
EOL: db 10
SECTION .bss
msg: resd 2 ; reserve space for 2 dwords
SECTION .text
global _start
_start: nop
;Displays test on shell
mov eax,4 ;print to terminal
mov ebx,1
mov ecx, submessage
mov edx, msglen
int 80h ;"I am subtracting 5 from 10!"
mov eax,4 ;print to terminal
mov ebx,1
mov ecx, ansmsg
mov edx, msglen2
int 80h ;"Answer is..."
;Subtraction operation below:
mov edx, 10
sub edx, 5
mov [msg], edx ; store 5 in msg
; now we need to print msg to terminal
mov eax, 4 ;print to terminal
mov ebx, 1
mov dword [msg+1], 0xa ;helps prints something out!
;Encountered problem here= prints out 'Answe' instead of integer '5'
push dword 2 ; store size of msg
push dword [msg] ; push to stack contents of msg
int 80h
add esp, 3 ;clean stack (2 push calls *4)
int 80h
; I like labels :)
sys_exit: mov eax,1 ;exit status
mov ebx,0
int 80h
nop
PS-如果我的线路缩进糟透了,我想知道如何改进它;一旦你克服了最初的学习“驼峰”,恕我直言学习集会会变得更有吸引力:)
答案 0 :(得分:2)
首先,感谢这个问题 - 它促使我在80小时内学习,这是我以前不熟悉的。
您的计划目前的形式是什么?它打印什么?如果我在脑海中正确执行它,我希望它打印第一条消息然后崩溃。由于缓冲,它可能甚至不会在可疑的崩溃之前显示第一条消息。
int 80h / eax = 4映射到write()函数。运行'man 2 write'以获取完整的文档。函数原型是:
ssize_t write(int fd, const void *buf, size_t count);
因此,eax = 4,ebx = fd,ecx = buf,edx = count。如果你想对代码注释更迂腐,“mov eax,4”表示“将字符串写入文件”,而“mov ebx,1”表示“指定文件#1,映射到STDOUT(终端)”
我希望第一个int 80h调用打印一些东西。第二次内部80h呼叫是可疑的。此时eax和ebx没有变化。但是,edx也保持不变,并保存第一个字符串的字符串长度。更有问题的是你将值5放入ecx。 ecx将指针保存到要写入的字符串,而不是要写入的值。所以当你把5放在那里并调用中断时,int会尝试打印一个从地址0x00000005开始的字符串。这几乎肯定会导致段错误(崩溃)。
另外,如果我正确读取int 80h文档,则示例中的push / pop堆栈操作没有任何相关性。
在ASM中将数字作为字符串打印是一件苦差事。您需要将数字转换为字符串,然后您可以使用int 80h打印它。如果您只想打印一个数字,这是一种可以作弊的方式:
一旦你开始工作,转换多个数字的数字将留给你练习。
祝你好运!答案 1 :(得分:1)
我认为推送消息是相关的。这是“你好世界”计划的另一个例子。
section .text
global _start ;must be declared for linker (ld)
_syscall:
int 0x80 ;system call
ret
_start: ;tell linker entry point
push dword len ;message length
push dword msg ;message to write
push dword 1 ;file descriptor (stdout)
mov eax,0x4 ;system call number (sys_write)
call _syscall ;call kernel
;the alternate way to call kernel:
;push eax
;call 7:0
add esp,12 ;clean stack (3 arguments * 4)
push dword 0 ;exit code
mov eax,0x1 ;system call number (sys_exit)
call _syscall ;call kernel
;we do not return from sys_exit,
;there's no need to clean stack
section .data
msg db "Hello, world!",0xa ;our dear string
len equ $ - msg ;length of our dear string
现在,我没有写上面的代码,它来自这里:
http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html#helloworld
所以你可以看到这个作者能够将值移动到堆栈上并调用内核例程,该例程将从栈中删除params。这实际上对我来说更有意义,因为我认为params是如何传递给c函数的。通过特定寄存器传递没有任何意义。如果一个方法有30个参数,你将如何通过寄存器传递该怎么办?
无论如何,我编译了上面的代码,将它链接起来并在我的macbook pro上运行它并且运行正常。
无论如何,和上面的例子一样好,它并没有教你多少。我认为,这是一个更好的例子,说明如何从寄存器中打印出任意值。
所以我想要的不是预定义的字符串,而是真正的编程结构;变量。
很简单,您可以通过执行以下操作来定义32位变量:
section .bss
msg: resd 2
这给了我一个名为“msg”的变量(或内存位置),我可以将其存储到其中。通过使用resd声明它,我定义了我想要保留的双字数。双字是32位,所以我声明我想要保留2个双字。为什么2?我马上就告诉你。
到目前为止一切顺利。
现在我需要做的就是将一个值移动到该内存位置并按照hello,world的示例,对吧?所以我编码了这个:
section .text
global _start ;must be declared for linker (ld)
_syscall:
int 0x80 ;system call
ret
_start: ;tell linker entry point
mov dword [msg], 'h' ;move the letter h into my memory location
mov dword [msg+1], 0xa ;this is so important, but other author's gloss over it
; the line terminator is essential in order to
; print something out
push dword 2 ;message length
push dword msg ;message to write
push dword 1 ;file descriptor (stdout)
mov eax,0x4 ;system call number (sys_write)
call _syscall ;call kernel
;the alternate way to call kernel:
;push eax
;call 7:0
add esp,12 ;clean stack (3 arguments * 4)
push dword 0 ;exit code
mov eax,0x1 ;system call number (sys_exit)
call _syscall ;call kernel
;we do not return from sys_exit,
;there's no need to clean stack
section .bss
msg: resd 1
所以没有太多变化。我添加了.bss部分,用于未初始化的数据。比静态字符串更有用。我们将静态值(字母h)移动到我的msg内存位置,但你可以像这样轻松移动寄存器
mov [msg], eax
下一行是如此重要,但每个“你好世界”的作者都只是掩饰它。 0xa是std out需要的行终止符,或者WILL_NOT显示你的值。这让我疯狂了很久,试图找出为什么我的一切都不会打印出来。你需要那个行终止符。这也是我们需要定义变量来保存两个值而不是一个值的原因。我们需要存储该行终止符。
其余的很简单,将params放在堆栈上并调用内核。现在您有一个如何从实际可变数据位置打印出一些任意数据的真实示例。享受!