Сборка - Как умножить / разделить константу на другую константу в сборке?

Итак, у меня есть функция сборки, которая вызывается на C. Она компилируется и не выдает мне никаких предупреждений, но когда я пытаюсь ее запустить, она вызывает ошибку сегментации. Я думаю, это потому, что я не могу переместить константу в регистр, но для использования команды mul/div требуется, чтобы значение было в регистре EAX. Как я могу умножить или разделить две константы в сборке?

Вот код до сих пор...

.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

2 ответа

ГАЗ поддерживает * операторы для умножения констант во время сборки. Например, mov $(5 * 50), %eax собирает точно такой же машинный код, что и mov $250, %eax, Другие операторы, такие как + - / %, и побитовые вещи также доступны. Я просто буду использовать * для примеров, но вы можете создавать произвольные выражения из констант времени компиляции, если они вычисляются до одного числа (или смещения от символа, который может разрешить компоновщик).

Это работает с константами ассемблера, такими как .equ A, 50 или же A = 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), а не ваши константы ассемблера A а также B,

В ГАЗЕ все символы имеют связанный раздел. Когда вы определяете символ с .equ или же = это "абсолютный" символ (а не .data раздел или .text символ раздела, как вы могли бы получить от ярлыка, как A:).

Константы ассемблера на самом деле не отличаются от символов, определенных с помощью меток. Однако кроме + а также - все математические операторы во время сборки требуют, чтобы оба аргумента были абсолютными, а результат - абсолютным.


Ваш код пытается поместить константы в регистры, чтобы умножить их во время выполнения. Если вы настаиваете на том, чтобы сделать это в качестве упражнения,

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

mov A, %eax является нагрузкой от значения символа. т.е. нагрузка с абсолютного адреса 50, который, очевидно, segfaults. Один шаг с отладчиком и посмотрите на разборки, чтобы понять, что произошло.

AT & T использует синтаксис $ для непосредственных констант, поэтому используйте это, чтобы получить значение. (Помните, .equ символы ведут себя так же, как метки, например, как вы используете $my_string чтобы получить адрес как можно скорее.)

Спасибо за вашу помощь, ребята, мне удалось выполнить упражнение со следующим кодом:

.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
Другие вопросы по тегам