汇编中三个数字的平均值
Posted
技术标签:
【中文标题】汇编中三个数字的平均值【英文标题】:Average of three numbers in assembly 【发布时间】:2018-10-03 20:13:16 【问题描述】:谁能帮我写一个程序来计算汇编中 3 个数字的平均值(NASM 64 位)?
我尝试了什么:
section .data
num1 db 3
num2 db 4
num3 db 5
divisor db 3
digit db 0, 10
section .text
global _start
_start:
mov rax, num1
mov rax, num2
div rax, num3
mov rbx, divisor
div rbx
mov rax, 60
mov rdi, 0
syscall
_printRAX:
add rax, 48
mov [digit], al
mov rax, 1
mov rdi, 1
mov rsi, digit
mov rdx, 2
syscall
ret
【问题讨论】:
问:操作系统是什么?问:你对那个操作系统做了什么“系统调用”?问:您的具体问题/遇到了什么问题? 我很困惑,你认为在汇编中找到平均值看起来像什么?好像你把一堆东西移到了 rax 中。mov rax, num1
将rax
设置为num1
的地址,采用NASM 语法。 mov rax, [num1]
将是一个设置rax = 0x????0a0003050403
的 64 位负载(x86 是小端,db
只有 1 个字节宽)。除此之外,您还没有设置 RDX。对于div
,它需要归零。在静态链接的二进制文件中,Linux 会在 _start
之前将其归零,但在其他任何地方,此代码都可能因 SIGFPE 而因#DE 除法异常而崩溃。
不管怎样,你要么想movzx eax, byte [num1]
等到不同的寄存器中为add
设置,要么你想add al, [num2]
等等为后面的数字。
@paulsm4 操作系统是 Linux 64bit (mint)。
【参考方案1】:
好的,这里有两个可能有帮助的例子:
示例 1:
;
; Standalone NASM "Hello world"
;
; BUILD:
; nasm -f elf64 hello.asm
; ld -s -o hello hello.o
;
; EXAMPLE OUTPUT:
; Hello, world!
;
section .text ;code section (shareable between processes)
global _start ;loader entry point
_start:
mov edx,len ;arg3: msg len
mov ecx,msg ;arg2: *msg
mov ebx,1 ;arg1: 1 (stdout)
mov eax,4 ;syscall@4 (sys_write)
int 0x80
mov ebx,0 ;arg1: exit code (0)
mov eax,1 ;sycall@1 (sys_exit)
int 0x80
section .data ;data section (per process)
msg db "Hello, world!",0xa ;our dear string
len equ $ - msg ;length of our dear string
示例 2:
;
; "Hello world" using standard C library
;
; BUILD:
; nasm -f elf64 avg3.asm
; gcc -m64 -o avg avg.o
;
; EXAMPLE OUTPUT:
; sum=12
; avg=4
;
extern printf ;stdlib C function
section .text ;code section
global main ;standard GCC entry point
main:
push rbp ;set up stack frame: must be aligned
; Add 3+4+5
mov rax,3
add ax,4
add ax,5
; Save and print sum
push rax
mov rdi,fmt1 ;printf format string
mov rsi,rax ;1st parameter
mov rdx,0 ;No 2nd parameter
mov rax,0 ;No xmm registers
call printf
; Compute and print average
mov dx,0 ;Clear dividend, high
pop rax ;dividend, low <= sum
mov cx,3 ;divisor
div cx ;ax= high, dx= low
; Print average
mov rdi,fmt2 ;printf format string
mov rsi,rax ;1st parameter
mov rdx,0 ;No 2nd parameter
mov rax,0 ;No xmm registers
call printf
; Exit program
pop rbp
mov rax,0
ret
section .data ;data section
fmt1:
db "sum=%d",0xa,0
fmt2:
db "avg=%d",0xa,0
注意事项:
就个人而言,我更喜欢“Gnu Assembler”(gas)。它使在不同架构之间以及在 C 和内联汇编程序之间切换变得容易,而不会出现处理英特尔语法的“认知失调”;)
我强烈建议您尽可能多地利用标准 C 库。实际上,这意味着使用gcc 而不是ld 链接您的可执行文件。
您的“计算平均值”程序就是一个很好的例子,原因是:让 printf 格式化程序计算出正确的输出要容易得多,而不是将二进制值转换为 ASCII 数字,然后弄清楚如何将它们格式化为字符串手动。
'希望有帮助!
【讨论】:
在您的第一个示例中,int 0x80
仅在 64 位代码中工作,因为您在静态缓冲区上使用 sys_write
,并且您创建了一个位置相关(非 PIE)可执行文件因此静态代码/数据位于虚拟地址空间的低 32 位。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?.
点得好 - 谢谢。我只是想创建“最简单的示例”。如果您提出“更好的建议”,我很乐意更新示例。
在您的第二个示例中,您通过调用 printf
而堆栈未按 16 对齐(函数进入后的 2x push
)违反了 ABI。它恰好适用于当前的 glibc(AL=0),但允许出现段错误。不要使用 RBP 作为帧指针,而是使用它来保存总和。例如mov ebp, 3
/ add ebp, 4+5
/ mov esi, ebp
/ ... printf / mov eax, ebp
/ xor edx,edx
/ div
/ mov esi, eax
。 (或者只使用 3 个 args 进行一次 printf 调用,因此您不需要保存/恢复它的总和。)在这里使用 16 位寄存器也没有任何好处。
修复您的第一个示例:OP 已经使用 64 位 sys_write
和 sys_exit
,而我对 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? 的回答有一个 64 位 Hello World(包括一些额外的东西要制作该答案的一个例子,但它可以被剥离。)
问:push rax
的哪一部分没有使堆栈以 16 的倍数对齐?我不是在“争论”——我只是好奇在这个例子中什么是“有效的推动”。以上是关于汇编中三个数字的平均值的主要内容,如果未能解决你的问题,请参考以下文章