Assembly 汇编-如何将汇编中的一个常数乘以/除以另一个常数?
所以,我有一个汇编函数,它是在C中调用的。它编译并没有给我任何警告,但当我试图运行它时,它给了我一个分段错误。我认为这是因为我不能将常量移动到寄存器中,但要使用mul/div命令,它需要一个值位于EAX寄存器中。 如何在汇编中乘或除两个常量 这是到目前为止的代码Assembly 汇编-如何将汇编中的一个常数乘以/除以另一个常数?,assembly,x86,constants,gnu-assembler,att,Assembly,X86,Constants,Gnu Assembler,Att,所以,我有一个汇编函数,它是在C中调用的。它编译并没有给我任何警告,但当我试图运行它时,它给了我一个分段错误。我认为这是因为我不能将常量移动到寄存器中,但要使用mul/div命令,它需要一个值位于EAX寄存器中。 如何在汇编中乘或除两个常量 这是到目前为止的代码 .section .data .global n .equ A, 50 .equ B, 5 .section .text .global loop_function loop_function: # prologue
.section .data
.global n
.equ A, 50
.equ B, 5
.section .text
.global loop_function
loop_function:
# prologue
pushl %ebp # save previous stack frame pointer
movl %esp, %ebp # the stack frame pointer for sum function
# beginning
movl i, %ebx # place i (declared in c) in ebx
movl A, %eax # place A in eax
movl B, %ecx # place B in ecx
jmp loop
loop:
movl $0, %edx # clean edx register
cdq
idivl %ecx # A / B, result in eax
imull %ebx # i * A / B, result in eax
incl %ebx
cmpl %ebx, n # if i <= n
jle loop # then jumps to loop
jmp end # else jumps to end
end:
# epilogue
movl %ebp, %esp # restore the previous stack pointer ("clear" the stack)
popl %ebp # restore the previous stack frame pointer
ret
气体支持常数。例如,mov$5*50,%eax汇编成与mov$250,%eax完全相同的机器代码。其他操作符如+-/%和按位填充也可用。我将仅使用*作为示例,但您可以使用编译时常量构造任意表达式,只要它们的计算结果是链接器可以解析的单个数字或符号的偏移量
这也适用于汇编器常量,如.eq A、50或A=50
请注意,整个立即数常量只在每个符号名上使用一个$,而不是一个$。例如,mov$5+$A,%eax尝试将名为$A+5的符号的地址放入%eax,因此未定义的符号会出现链接时间错误
mov$$A*$B,%eax甚至没有组装:
错误:“*”的无效操作数*UND*和*UND*部分
这是因为您试图将两个未知符号$A和$B的地址相乘,而不是将汇编器常量A和B相乘
在气体中。当您使用.eq或=”定义符号时,它是一个绝对符号,而不是一个.data节或.text节符号,就像您从标签中得到的那样:
汇编器常量与用标签定义的符号并没有什么不同。但是,除了+和-,所有
您的代码似乎试图将常量放入寄存器,以便在运行时将它们相乘。如果你坚持这样做是一种锻炼
mov $A, %ecx # put symbol's value in ECX
imul $B, %ecx, %eax # EAX = A * B
mov A,%eax是来自符号值的负载。i、 e.从绝对地址50加载,这显然会导致故障。使用调试器执行单个步骤,并查看反汇编以了解发生了什么
AT&T语法使用$作为立即数常量,因此使用它来获取值。请记住,.eq符号的行为与标签相同,比如如何使用$my_字符串以立即数形式获取地址。谢谢大家的帮助,伙计们,我用以下代码完成了练习:
.section .data
.global n
.global i
.equ A, 50
.equ B, 5
.section .text
.global loop_function
loop_function:
# prologue
pushl %ebp # save previous stack frame pointer
movl %esp, %ebp # the stack frame pointer for sum function
# beginning
movl i, %ecx # place i (declared in c) in ecx
movl $A, %eax # place A in eax
movl $B, %ebx # place B in ebx
movl $0, %edx # clean edx register
cdq
idivl %ebx # (A / B), result goes to eax
loop:
incl %ecx # increment i, which is in ecx
cmpl n, %ecx # if n > i
jg loop # then jumps to loop
end:
incl %ecx
imull %ecx # multiply i by (A / B), result in eax
# epilogue
movl %ebp, %esp # restore the previous stack pointer ("clear" the stack)
popl %ebp # restore the previous stack frame pointer
ret
真正的答案在ABI中。如果您没有ABI,那么我建议将您使用的每个寄存器推到堆栈上,并在返回之前弹出它。错误的可能原因是您没有遵循标准调用约定。特别是,您破坏了ebx,它是被调用方保存的寄存器,因此您的调用方很可能希望它保持不变。a和B似乎是常量,我猜我是一个变量,但您对它们使用完全相同的语法。我认为它试图从内存地址50和地址5加载值,这是行不通的。我建议使用$A和$B。如果我在常量声明中放入$50和$5,编译失败,$A给出了常量的内存地址,我想。您将常量声明为内存地址,所以应该使用$A和$B。您尝试过吗?要能够使用,您需要类似以下内容:A:.int 50。您应该将i和n作为函数参数而不是全局参数传递。此外,干熄焦从eax设置edx,因此没有必要先将edx归零。而且,我看不到循环的意义。您可以用mov n,%ecx替换它。或者我猜使用extra inc,您正在执行%ecx=n+1操作,并且您在不保存它的情况下对%ebx进行了重击。使用%ecx为idiv除数保留$B。因此,您的整个函数可以是mov$A,%eax;干熄焦;mov$B,%ecx;idiv%ecx;imul n,%eax。或者将n加载到ecx或edx中,然后inc%ecx(如果实际需要n+1)。您不需要使用单操作数形式,除非您希望在edx:eax中返回完整的64位乘积。2操作数形式更快,只写入一个寄存器。
.section .data
.global n
.global i
.equ A, 50
.equ B, 5
.section .text
.global loop_function
loop_function:
# prologue
pushl %ebp # save previous stack frame pointer
movl %esp, %ebp # the stack frame pointer for sum function
# beginning
movl i, %ecx # place i (declared in c) in ecx
movl $A, %eax # place A in eax
movl $B, %ebx # place B in ebx
movl $0, %edx # clean edx register
cdq
idivl %ebx # (A / B), result goes to eax
loop:
incl %ecx # increment i, which is in ecx
cmpl n, %ecx # if n > i
jg loop # then jumps to loop
end:
incl %ecx
imull %ecx # multiply i by (A / B), result in eax
# epilogue
movl %ebp, %esp # restore the previous stack pointer ("clear" the stack)
popl %ebp # restore the previous stack frame pointer
ret