Assembly - How to multiply/divide a constant by an

2019-01-20 19:41发布

问题:

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

回答1:

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.)



回答2:

Thanks for all your help, guys, I managed to do the exercise with the following code:

.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