1

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    The real answer is in the [Application_binary_interface](https://en.wikipedia.org/wiki/Application_binary_interface) (ABI). If you don't have the ABI, then I suggest pushing every register you use onto the stack, and popping it before returning. – user3386109 Oct 21 '17 at 23:38
  • 2
    The probable cause of error is that you are not following standard calling convention. In particular, you destroy `ebx` which is a callee-saved register and so your caller may very well expect it to be unchanged. – Jester Oct 21 '17 at 23:39
  • 2
    A and B seem to be constants and I'm guessing i is a variable, but you use the exact same syntax for all of them. I think it's trying to load values from memory address 50 and address 5, which isn't going to work. I suggest using $A and $B. – prl Oct 22 '17 at 00:04
  • If I put $50 and $5 in the declaration of the constants, it fails the compilation, and $A gives the memory address of the constant, I think. – thegoncalomartins Oct 22 '17 at 00:16
  • If I declare a global variable "xpto" and try to make an operation with it, "xpto" is the value stored in the variable, $xpto gives the memory address of that variable. I think it happens the same thing with constants. – thegoncalomartins Oct 22 '17 at 00:20
  • 1
    You declared the constant as memory addresses, so you should use `$A` and `$B`. Have you tried it? To be able to use `A`, you need something like: `A: .int 50`. – Jester Oct 22 '17 at 00:28
  • Yes, that is a declaration of a global variable. I'm going to try $A and $B then. – thegoncalomartins Oct 22 '17 at 00:31
  • @Jester: The syntax for assemble-time multiplication is less obvious than what I thought. Turns out you need to leave out all but the first `$`, so it's `mov $(A * B), %eax`, otherwise you end up trying to reference a symbol called `$A` if you write `mov $($A * $B), %eax` – Peter Cordes Oct 22 '17 at 03:29

2 Answers2

2

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

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
0

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
  • You should pass `i` and `n` as function args instead of globals. Also, `cdq` sets `edx` from `eax`, so there's no point in zeroing `edx` first. Also, I don't see the point of the loop. You could just replace it with `mov n, %ecx`. (Or I guess with the extra `inc`, you're doing `%ecx = (n) + 1`) – Peter Cordes Oct 22 '17 at 17:43
  • Also, you clobber `%ebx` without saving it. Use `%ecx` to hold `$B` for the idiv divisor. – Peter Cordes Oct 22 '17 at 17:44
  • So your whole function could be `mov $A, %eax` ; `cdq` ; `mov $B, %ecx` ; `idiv %ecx` ; `imul n, %eax`. (Or load `n` into `ecx` or `edx` and then `inc %ecx` if it's actually `n+1` that you need). You don't need to use the one-operand form, unless you want to return the full 64-bit product in `edx:eax`. The 2-operand form is faster and only writes one register.) – Peter Cordes Oct 22 '17 at 17:49