装配-如何在装配中将一个常数乘以/除以另一个常数? [英] Assembly - How to multiply/divide a constant by another constant in assembly?

查看:92
本文介绍了装配-如何在装配中将一个常数乘以/除以另一个常数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我有一个在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

解决方案

GAS支持 *常数的汇编时间乘法运算符.例如,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中,所有符号都具有关联的部分 .用.equ=定义符号时,它是绝对"符号(而不是像从A:这样的标签中获得的.data部分或.text部分符号).

汇编程序常量与使用标签定义的符号并没有什么不同.但是,除了+-之外,所有汇编时间数学运算符要求两个args都是绝对的,结果是绝对的.


您的代码似乎正在尝试将常量放入寄存器中,以便在运行时将它们相乘.如果您坚持要这样做,那么

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获取立即数的地址一样.)

So, I have an assembly function, which is called in C. It compiles and gives me no warnings, but when I try to run it, it gives me a segmentation fault. I think it's because I can't move a constant into a register, but to use the mul/div command it requires a value to be in EAX register. How can I multiply or divide two constants in Assembly?

Here's the code so far...

.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

解决方案

GAS supports the * operators for assemble-time multiplication of constants. For example, mov $(5 * 50), %eax assembles to exactly the same machine code as mov $250, %eax. Other operators like + - / %, and bitwise stuff are also available. I'll just use * for the examples, but you can construct arbitrary expressions out of compile-time constants as long as they evaluate to a single number (or offset from a symbol that the linker can resolve).

This works with assembler constants like .equ A, 50 or A = 50 as well.

.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

Note that the whole immediate constant only uses one $, rather than a $ on every symbol name. For example, mov $(5 + $A), %eax tries to put the address of a symbol called $A (plus 5) into %eax, so you get a link-time error for an undefined symbol.

mov $( $A * $B ), %eax doesn't even assemble:
Error: invalid operands (*UND* and *UND* sections) for '*'
This is because you're trying to multiply the address of two unknown symbols ($A and $B), rather than your assembler constants A and B.

In GAS, all symbols have an associated section. When you define a symbol with .equ or =, it's an "absolute" symbol (rather than a .data section or .text section symbol like you'd get from a label like A:).

Assembler constants aren't really different from symbols defined with labels. However, other than + and -, all assemble-time math operators require both args to be absolute, and the result is absolute.


Your code appears to be trying to put the constants into registers to multiply them at runtime. If you insist on doing that as an exercise,

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

mov A, %eax is a load from the symbol's value. i.e. a load from absolute address 50, which obviously segfaults. Single-step with a debugger and look at the disassembly to understand what happened.

AT&T syntax uses $ for immediate constants, so use that to get the value. (Remember, .equ symbols behave the same as labels, like how you'd use $my_string to get the address as an immediate.)

这篇关于装配-如何在装配中将一个常数乘以/除以另一个常数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆