2016-09-25 5 views
0

код C++ ниже Я пытаюсь реализовать в 32-битной сборки записывается так:Бесконечный цикл с использованием тройной вложенной для цикла (32-разрядную сборку)

for(int ebx = 3; ebx < 10; ebx++){ 
     print("LO"); 
     for(int esi = 2; esi < ebx; esi++){ 
      PRINT("L1"); 
      for(int ebp = 0; ebp < esi; ebp++){ 
       PRINT("L2"); 
      } 
     } 
} 

Это мой код сборки:

SECTION .data     ; Section containing initialized data 
helloWorld0: dw "L1",10,0 
helloWorld1: dw "L2",10,0 
helloWorld2: dw "L3",10,0 

SECTION .bss      ; Section containing uninitialized data 
SECTION .text     ; Section containing code 
extern printf     ; Print function from glibc 
global main      ; Linker needs this to find the entry point 
main: 
nop        ; This no-op keeps gdb happy 
push  ebp      ; Set up stack frame for debugger 
mov  ebp,esp 
push  ebx      ; Must preserve EBP, EBX, ESI & EDI 
push  esi 
push  edi 
; Everything before this is boilerplate; use it for all apps 

mov ebx, 3      ;L1 
mov esi, 2      ;L2 
mov ebp, 0      ;L3 
L1: 
push helloWorld0 
call printf 

L2: 
push helloWorld1 
call printf 

L3: 
push helloWorld2 
call printf 
inc  ebp 
cmp  ebp, esi 
jne  L3 

inc  esi 
cmp  esi, ebx 
jne  L2 

mov  esi, 2 
inc  ebx 
cmp  ebx, 10 
jne  L1 
; Everything after this is boilerplate; use it for all apps 
pop  edi      ; Restore saved registers 
pop  esi 
pop  ebx 
mov  esp,ebp     ; Destroy stack frame before returning 
pop  ebp 
ret        ; Return control to Linux 

Похоже, что код никогда не определяет, когда ebp = esi. Я новичок в ассемблере, поэтому мой профессор предоставил шаблоны. Я использовал EBP, ESI и EBX, поскольку они сохранены для использования. Любые идеи о том, что вызывает бесконечный цикл в третьем вложенном цикле?

+0

Что вы узнали при отладке строки за строкой внутри цикла? –

+0

Я использую терминал linux для кодирования, какое программное обеспечение необходимо отлаживать? –

+0

Кажется, у вас есть аналогичное назначение для этого: http://stackoverflow.com/questions/39680843/infinite-loop-on-simple-assembly-loop. Некоторые из моих комментариев там будут применяться здесь. –

ответ

2

В вашем C, int ebp = 0 находится внутри обеих внешних контуров и работает перед входом в внутренний контур каждые раз. Это не то место, где оно находится в вашем asm.

Кроме того, не изменяйте EBP внутри своей функции. Оболочка вашего профессора создает «стек стека» (google it), используя EBP в качестве указателя кадра. В конце функции для очистки используется mov esp,ebp. (Хорошая вещь, тоже, так как ваш код толкает арг для Printf, но не всегда регулировать ESP после PRINTF возвращается!)

Предположительно будущая лекция будет говорить о доступе к функции арг и местных жителей с помощью [ebp + 8] или [ebp - 4] или любой другой. На данный момент, просто знайте, что этот фрейм-указатель ерунда тратит впустую еще один из ваших драгоценных регистров, поэтому вы не можете его использовать. (То есть только 3 регистров целых регуляр 8 x86, которые можно использовать и которые не изменяются вызовами функций EDI, ESI и EBX.)


Re: точка Седрика о состоянии отрасли:

Ваши петли в порядке, если я не пропустил что-то.

Все ваши петли имеют удобное свойство, чтобы они всегда запускались один раз, поэтому вы можете записать их как петли do{}while() без дополнительной проверки вверху. Это означает, что они могут быть прекрасно реализованы в asm, с проверкой внизу, как вы делаете.

Так, например, printf (т. Е. Тело цикла) должен запускаться дважды в первом внутреннем цикле: один раз с EBP = 0 и один раз с EBP = 1. После этого, приращение сделает EBP = 2, поэтому CMP/JNE провалится, потому что EBP == ESI == 2.

Для внешних циклов логика такая же. Весь внутренний цикл - это «тело цикла», и он запускается до того, как вы увеличите счетчик. Так что ты в порядке.

Вы можете эквивалентно использовать CMP/JL для выполнения подписанного сравнения и повторного запуска тела цикла, в то время как счетчик меньше предела. Это соответствовало бы C. Оно также изменило бы режим отказа с ок. 2^32 проезжает через внутренний контур (до inc ebp обертывается и достигает 2). С JL, режим отказа от вашей текущей ошибки будет состоять только в том, чтобы совершить одно прохождение через внутренний цикл, если счетчик циклов был настолько высок, что мы не должны были входить в цикл в первую очередь.


Обычно в asm легче подсчитывать до нуля, например. dec ebp/jnz не нуждается в CMP. Преобразование ваших циклов для этого нетривиально, потому что ваши внешние счетчики цикла действуют как верхние границы для внутренних циклов. Это должно быть возможно. Вы не печатаете значения счетчика внутри цикла, поэтому на самом деле не имеет значения, что они собой представляют, только подсчет циклов.

+0

Это определенно ответ, хотя цикл 'for' имеет определенные свойства, которые не сохраняются в вашем ответе. Я знаю, что настройка оптимизации на высокие настройки в вашем компиляторе может переписать ее, как вы сказали, для перевода C в asm напрямую требуется, чтобы цикл 'for' мог вообще не выполняться, если сравнение возвращает false. Более того, приращение выполняется после тела цикла. Наконец, использование 'jne' вместо' jl' является сбивающим с толку, потому что, насколько мне известно, в этом нет никакой пользы, кроме неожиданного поведения, если кто-то без знания в asm пытается использовать ваш код для другого проекта. – Cedric

+0

@Cedric: Приращения здесь * * выполняются после тел петли, как я указал в своем ответе. Значения счетчиков в регистре во время выполнения цикла в коде OP * do * соответствуют C. –

+0

@Cedric: нормальный способ реализации цикла 'for()' в asm - с тегом & ветвью вне цикла, чтобы проверить, нужно ли ему выполняйте 0 раз, затем впадайте в структуру 'do() {} while' с тегом test & branch внизу, как у OP. Как я указал в ответе, исключение начального теста вне цикла возможно из-за специального случая кода OP. Насколько я знаю, JNE против JL, я согласен, как правило, лучше сузить диапазон истинных сравнений, когда это удобно и не мешает макро-fusion (из cmp/jcc в один uop) на процессорах Intel. –