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