section .data
array dw 1,2,3,4,5,6,7,8,9,10 ; array of integers
msg db " numbers are : %d %d ",10,0
section .text
global main
extern printf ; for c printf
main:
push ebp
mov ebp,esp ;intialise stack
mov ax,11
push ax ;push ax with value 11
mov ax,22
push ax ;push ax with value 12
push msg
call printf ; calling printf function
add esp ,12
mov esp,ebp ;restore stack
pop ebp
当我推送即时值而不是通过AX推送时,它工作得很好。这是为什么?
最佳答案
正如 Jester 指出的那样:不要使用 32 位代码将 16 位值压入堆栈,除非您知道自己在做什么。 AX 是一个 16 位寄存器(32 位 EAX 寄存器的下半部分)。当你这样做时:
push ax
16 位被压入堆栈,因为 AX 是一个 16 位寄存器。这将阻止printf
由于数据不是 32 位宽,因此无法正确访问该值。如果你要这样做:
push 11
你会发现这很有效。当NASM生成32位代码时,它假设立即值被压入堆栈时是32位宽。这就是为什么这个场景对你有用。
如果您要PUSH一个32位寄存器,那么完整的32位数据将被放置在堆栈顶部。举个例子:
push eax
看来您的意图可能是访问或遍历 WORD 数组(WORD = 16 位值)并使用 %d
打印它们。 printf
使用的转换说明符。 %d
将 32 位 DWORDS 打印为有符号值。在将它们压入堆栈之前,您必须将它们作为 WORD 加载到内存中,并将它们转换为 DWORDS。
汇编语言没有高级编程语言传统意义上的变量概念。您可以为保存WORD(16 位值)的内存位置赋予含义。它是否已签名或未签名取决于您用于与该数据交互的代码。
386 有两条指令可以提供帮助。 MOVSX用于符号扩展较小的操作数到较大的操作数。当您希望保留SIGN(正数或负数)时使用此选项。 MOVZX用于将较小的操作数零扩展为较大的操作数。该指令适用于无符号值,在转换过程中只是将目标操作数的所有高位设置为零。
作为一个例子,我编写了一些围绕单词数组的代码:
section .data
array dw -1,0,1,2,3,4,5,6,7,8,9,10,-32768,32767,32768
; array of integers
arraylen equ ($-array)/2 ; number of word elements in array
msg db " numbers are : %d %d ",10,0
section .text
global main
extern printf ; for c printf
main:
push ebp
mov ebp,esp ; intialise stack
push ebx ; ebx is caller saved register. We destroy it so
; we must restore it before our function exits
xor ebx, ebx ; index = 0
; Make the equivalent of a for loop to traverse array
.loop1:
cmp ebx, arraylen ; We'll process all the elements of the array
je .endloop ; End when our index = arraylen
movzx eax, word [array + ebx * 2] ; Use EBX as index into WORD array
; zero extend 16-bit array value into 32-bit register
push eax ; parameter 3 = unsigned DWORD
movsx eax, word [array + ebx * 2] ; Use EBX as index into WORD array
; sign extend 16-bit array value into 32-bit register
; movsx eax, ax ; The line above would have also worked this way
push eax ; parameter 2 = signed DWORD onto stack
push msg ; parameter 1 = pointer to format string
call printf ; calling printf function
add esp, 12
inc ebx ; index += 1
jmp .loop1 ; continue for loop
.endloop:
pop ebx ; Restore ebx
mov esp,ebp ; restore stack
pop ebp
该代码确保根据CDECL calling convention被调用者保存的任何寄存器(上面代码中的EBX)在函数开始和结束时保存和恢复。更多关于这方面的解释可以在最近的一篇StackOverflow answer中找到。我写的。
我编写了等效的 for 循环(您可以将其编码为 do-while)或任何其他循环构造来遍历数组。我同时使用 MOVZX 和 MOVSX 并使用您的 printf
显示结果格式字符串。
注意::MOVZX也可以通过将目标操作数清零并随后将源操作数移至目标来完成。举个例子:
movzx eax, word [array + ebx * 2]
可以编码为:
xor eax, eax ; eax = 0
mov ax, word [array + ebx * 2]
人们应该能够组装和链接:
nasm -f elf32 testmov.asm
gcc -m32 -o testmov testmov.o
当运行为./testmov
时结果应如下所示:
numbers are : -1 65535 numbers are : 0 0 numbers are : 1 1 numbers are : 2 2 numbers are : 3 3 numbers are : 4 4 numbers are : 5 5 numbers are : 6 6 numbers are : 7 7 numbers are : 8 8 numbers are : 9 9 numbers are : 10 10 numbers are : -32768 32768 numbers are : 32767 32767 numbers are : -32768 32768
如果您想使用 printf 打印无符号 WORD(16 位值)您可以使用%hu
(无符号短),对于有符号的WORD,您可以使用 %hd
(缩写)。尽管您仍然必须为参数传递DWORD,但您无需担心零扩展(或符号扩展),因为 printf
只会查看作为参数传递的 DWORD 的低 2 个字节。
关于assembly - Printf 显示垃圾值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36132676/