Сборка - Как умножить / разделить константу на другую константу в сборке?
Итак, у меня есть функция сборки, которая вызывается на 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