2016-09-25 3 views
0

내가 32 비트 어셈블리에서 구현하기 위해 노력하고 아래의 C++ 코드는 너무로 기록됩니다무한 루프는

:

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

리눅스 터미널을 사용하여 코드를 디버깅하려면 어떤 소프트웨어가 필요합니까? –

+0

당신과 비슷한 임무가 있다고 생각됩니다 : http://stackoverflow.com/questions/39680843/infinite-loop-on-simple-assembly-loop. 거기에 내 의견 중 일부는 여기에 적용됩니다. –

답변

2

C에서 int ebp = 0은 두 외부 루프 내부에 있으며 시간마다 내부 루프를 입력하기 전에 실행됩니다. 그것은 당신의 자리에있는 것이 아닙니다.

또한 함수 내에서 EBP를 수정하지 마십시오. 교수님의 상용구는 EBP를 프레임 포인터로 사용하여 "스택 프레임"(google it)을 만듭니다. 기능이 끝나면 mov esp,ebp을 사용하여 정리합니다. (당신의 코드의 printf에 대한 인수를 못살게 굴지하지만 지금의 printf의 복귀 후 ESP를 조정하지 않습니다! 때문에 좋은 점은,도)

아마도 미래의 강의 [ebp + 8] 또는 [ebp - 4] 또는 무엇이든을 사용하여 함수 인수 및 지역 주민 접근에 대해 이야기합니다. 지금 당장은이 프레임 포인터의 말도 안되는 점이 소중한 레지스터 중 하나를 낭비하고 있다는 것을 알고 있으므로 사용할 수 없습니다. (당신이 사용할 수있는 8 개의 86 정수 REGS의 유일한 3 개 레지스터가 함수 호출에 의해 수정되지 않습니다 즉, EDI, ESI 및 EBX 있습니다.)


재 : 분기 상태에 대한 세드릭의 점 :

내가 놓친 것이 아니라면 루프가 정상적으로 작동합니다.

루프에는 항상 한 번만 실행해야하는 편리한 속성이 있으므로 맨 위에 추가 체크 표시없이 do{}while() 루프로 쓸 수 있습니다. 이것은 당신이하고있는 것처럼 맨 밑에있는 체크와 함께 그들이 asm에서 멋지게 구현 될 수 있음을 의미합니다.

예를 들어, printf (즉, 본문)는 첫 번째 내부 루프에서 두 번 실행해야합니다. 한 번은 EBP = 0이고 다른 한 번은 EBP = 1입니다. 그 후에 증가분은 EBP = 2가 될 것이므로 EBP == ESI == 2이기 때문에 CMP/JNE는 빠질 것이다.

외부 루프의 경우 논리는 동일합니다. 전체 내부 루프는 "루프 본문"이며 카운터를 증가시키기 전에 실행됩니다. 너 괜찮아.

등가 적으로 CMP/JL을 사용하여 부호가있는 비교를 수행하고 카운터가 한계보다 작 으면 루프 본문을 다시 실행할 수 있습니다. 그것은 C와 일치 할 것입니다. 내부 루프를 통해 2^32 번 이동합니다 (inc ebp이 감싸고 2에 도달 할 때까지). JL을 사용하면 루프 카운터가 너무 커서 우리가 처음에 루프에 들어 가지 않아야한다면 현재 버그의 실패 모드가 내부 루프를 통해 한 번만 걸리는 것입니다.


일반적으로 asm에서는 0으로 카운트하는 것이 더 쉽습니다. dec ebp/jnz에는 CMP가 필요하지 않습니다. 외부 루프 카운터가 내부 루프에 대한 상한선의 역할을하기 때문에 루프를 변환하는 것은 중요하지 않습니다. 그것은 가능해야합니다. 루프 안의 카운터 값을 인쇄하지 않으므로 실제로는 루프 트립 카운트와 상관 없습니다.

+0

'for' 루프가 당신의 대답에서 유지되지 않는 어떤 속성들을 가지고 있지만 이것은 확실히 대답입니다. 필자는 컴파일러의 높은 설정을 최적화하는 것이 C에서 asm으로 직접 변환 할 때 비교가 false를 반환하면 'for' 루프가 전혀 실행되지 않아야한다고 말한 것처럼 다시 작성할 수 있음을 알고 있습니다. 또한 증가는 루프 본문 뒤에 수행됩니다. 마지막으로'jl' 대신'jl'을 사용하는 것이 혼란 스럽습니다. asm에 대한 지식이없는 사람이 다른 projet에 코드를 사용하려고 시도하면 예기치 않은 행동은 따로하고, 제가 아는 한 그렇게하는 것이 도움이되지 않기 때문입니다. – Cedric

+0

@Cedric : 여기에있는 증가분은 루프 본문 뒤에 있습니다. 제가 답을 지적했습니다. OP의 코드에서 루프 실행 중 레지스터의 카운터 값은 C와 일치합니다. –

+0

@Cedric : asm에서'for()'루프를 구현하는 일반적인 방법은 루프 외부의 테스트 및 분기를 사용하여 0 번 실행 한 다음 OP와 마찬가지로 바닥에 테스트 및 분기가있는'do() {while while '구조체에 포함됩니다. 대답에서 지적했듯이 OP 코드의 특수한 경우 때문에 루프 밖에서 초기 테스트를 생략 할 수 있습니다. JNE 대 JL까지는 그렇습니다. Intel CPU에서 매크로 퓨전 (cmp/jcc를 단일 uop로 사용)을 방해하지 않고 편리한 비교 범위를 좁히는 것이 일반적으로 더 낫습니다. –