2017-10-21 204 views
0

所以,我有一个汇编函数,它在C中调用。它编译并且没有给出警告,但是当我尝试运行它时,它给了我一个分段错误。我认为这是因为我不能将一个常量移动到一个寄存器中,但是使用mul/div命令它需要一个值在EAX寄存器中。 我如何在Assembly中乘两个常量?装配 - 如何在装配中用常数乘以一个常数?

下面的代码到目前为止...

.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 
+1

真正的答案是在[Application_binary_interface](https://en.wikipedia.org/ wiki/Application_binary_interface)(ABI)。如果您没有ABI,那么我建议您将每个使用的寄存器推入堆栈,并在返回之前将其弹出。 – user3386109

+2

错误的可能原因是您没有遵循标准调用约定。特别是,你销毁了'ebx'这是一个被保存的被保存的寄存器,所以你的调用者可能会希望它没有改变。 – Jester

+2

A和B似乎是常量,我猜测我是一个变量,但是对所有变量使用完全相同的语法。我认为它试图从内存地址50和地址5加载值,这不会起作用。我建议使用$ A和$ B。 – prl

回答

1

GAS支持常量the * operators for assemble-time multiplication。例如,mov $(5 * 50), %eax汇编到与mov $250, %eax完全相同的机器代码。其他运算符(如+ - /%)和位运算符也可用。我只是使用*作为示例,但只要它们计算为单个数字(或链接器可以解析的符号的偏移量),就可以从编译时常量中构造任意表达式。

这适用于汇编常量,如.equ A, 50A = 50

.equ A, 50 
.equ B, 5 

aa = 3 
bb = 7 

.globl _start 
_start:     # machine code   .intel_syntax disassembly 
    mov $(5 * 50), %eax # b8 fa 00 00 00 mov eax,0xfa # 250 

    mov $(aa * B), %ecx # b9 0f 00 00 00 mov ecx,0xf # 3*5 = 15 
    mov $A * B, %edx # ba fa 00 00 00 mov edx,0xfa # 250 

注意,整个立即数只使用一个$,而不是在每一个符号名称$。例如,mov $(5 + $A), %eax会尝试将名为$A(加5)的符号地址放入%eax中,以便为未定义的符号获取链接时错误。

mov $($A * $B), %eax甚至没有组装:
Error: invalid operands (*UND* and *UND* sections) for '*'
这是因为你想乘两个未知符号($A$B)的地址,而不是你的汇编常数AB

在GAS中,all symbols have an associated section。当您使用.equ=定义符号时,它是一个“绝对”符号(而不是.data节或.text节符号,就像您从标签A:中获得的那样)。

汇编程序常量与用标号定义的符号并无太大区别。但是,除+-之外,全部为assemble-time math operators require both args to be absolute, and the result is absolute


您的代码似乎试图将常量放入寄存器中以在运行时将它们相乘。如果你坚持这样做,作为一个练习,

mov $A, %ecx   # put symbol's value in ECX 
imul $B, %ecx, %eax  # EAX = A * B 

mov A, %eax是从符号的值的负载。即来自绝对地址50的负载,这显然是段错误。使用调试器进行单步调试,并查看反汇编以了解发生了什么。

AT &对于即时常量,T语法使用$,所以使用它来获取值。 (请记住,.equ符号的行为与标签相同,例如您将如何使用$my_string来获取地址作为即时贴。)

0

感谢您的帮助,伙计们,我能够做到用下面的代码练习:

.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 
+0

您应该将'i'和'n'作为函数参数而不是全局变量。另外,'cdq'将'edx'设置为'eax',所以首先将'edx'归零是没有意义的。另外,我没有看到循环的重点。你可以用'mov n,%ecx'替换它。 (或者我想用额外的'inc',你正在做'%ecx =(n)+ 1') –

+0

另外,你在不保存它的情况下'%ebx'。使用'%ecx'来保存idiv除数的'$ B'。 –

+0

所以你的整个功能可能是'mov $ A,%eax'; 'cdq'; 'mov $ B,%ecx'; 'idiv%ecx'; 'imul n,%eax'。 (或者,如果它实际上是你需要的n + 1,则将'n'加载到'ecx'或'edx'中,然后加载'inc%ecx')。除非您想在'edx:eax'中返回完整的64位产品,否则不需要使用单操作数格式。 2操作数格式更快,只写入一个寄存器。) –