汇编 - 如何在汇编中将一个常数乘/除以另一个常数?

Posted

技术标签:

【中文标题】汇编 - 如何在汇编中将一个常数乘/除以另一个常数?【英文标题】:Assembly - How to multiply/divide a constant by another constant in assembly? 【发布时间】:2018-04-02 19:20:48 【问题描述】:

所以,我有一个汇编函数,它在 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

【问题讨论】:

真正的答案在Application_binary_interface (ABI) 中。如果您没有 ABI,那么我建议将您使用的每个寄存器都推入堆栈,并在返回之前将其弹出。 错误的可能原因是您没有遵循标准调用约定。特别是,您销毁了ebx,它是一个被调用者保存的寄存器,因此您的调用者很可能希望它保持不变。 A 和 B 似乎是常量,我猜 i 是一个变量,但您对它们都使用完全相同的语法。我认为它正在尝试从内存地址 50 和地址 5 加载值,这是行不通的。我建议使用 $A 和 $B。 如果我把 $50 和 $5 放在常量的声明中,编译失败,我认为 $A 给出了常量的内存地址。 您将常量声明为内存地址,因此您应该使用$A$B。你试过了吗?为了能够使用A,您需要类似:A: .int 50 【参考方案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 将地址作为立即数一样。)

【讨论】:

【参考方案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

【讨论】:

您应该将in 作为函数参数而不是全局参数传递。此外,cdqedx 设置为eax,因此首先将edx 归零是没有意义的。另外,我没有看到循环的意义。您可以将其替换为mov n, %ecx。 (或者我猜加上额外的inc,你正在做%ecx = (n) + 1 另外,你在不保存的情况下破坏了%ebx。使用%ecx 保存$B 作为idiv 除数。 所以你的整个函数可以是 mov $A, %eax ; cdq ; mov $B, %ecx ; idiv %ecx ; imul n, %eax。 (或者将n 加载到ecxedx 然后inc %ecx 如果它实际上是您需要的n+1)。您不需要使用单操作数形式,除非您想在 edx:eax 中返回完整的 64 位产品。 2 操作数形式更快,并且只写入一个寄存器。)

以上是关于汇编 - 如何在汇编中将一个常数乘/除以另一个常数?的主要内容,如果未能解决你的问题,请参考以下文章

汇编语言

汇编语言ARM扩展资料数据表示

汇编学习笔记-伪指令

一个函数乘以一个常数对原函数图像有没有影响?初中我只学过上加下减,左加右减。

LINUX下GDB反汇编和调试

JVM---汇编指令集