2010-11-19 6 views
8

일부 어셈블리 코드를 이해하려고하는데 몇 줄을 제외하고 대부분을 완료 할 수있었습니다. 내부에서 일어나는 일의 대부분을 이해할 수 있지만 코드의 시작과 끝에서 무엇이 (그리고 왜) 일어나고 있는지 완전히 이해할 수는 없습니다. 누군가가 이것에 대해 밝힐 수 있습니까?일부 어셈블리 문의 목적 이해

int main() { 
    int a, b; 
    a = 12; 
    b = 20; 
    b = a + 123; 
    return 0; 
} 

분해 된 버전 :

8048394:8d 4c 24 04   lea 0x4(%esp),%ecx    ; ?? 
8048398:83 e4 f0    and $0xfffffff0,%esp   ; ?? 
804839b:ff 71 fc    pushl -0x4(%ecx)     ; ?? 
804839e:55     push %ebp      ; Store the Base pointer 
804839f:89 e5    mov %esp,%ebp     ; Initialize the Base pointer with the stack pointer 
80483a1:51     push %ecx      ; ?? 
80483a2:83 ec 4c    sub $0x4c,%esp     ; ?? 
80483a5:c7 45 f8 0c 00 00 00 movl $0xc,-0x8(%ebp)    ; Move 12 into -0x8(%ebp) 
80483ac:c7 45 f4 14 00 00 00 movl $0x14,-0xc(%ebp)   ; Move 20 into -0xc(%ebp) 
80483b3:8b 45 f8    mov -0x8(%ebp),%eax    ; Move [email protected](%ebp) into eax 
80483b6:83 c0 7b    add $0x7b,%eax     ; Add 123 to [email protected] 
80483b9:89 45 f4    mov %eax,-0xc(%ebp)    ; Store the result into [email protected]c(%ebp) 
80483bc:b8 00 00 00 00  mov $0x0,%eax     ; Move 0 into eax 
80483c1:83 c4 10    add $0x10,%esp     ; ?? 
80483c4:59     pop %ecx      ; ?? 
80483c5:5d     pop %ebp      ; ?? 
80483c6:8d 61 fc    lea -0x4(%ecx),%esp    ; ?? 
+0

스택의 위치를 ​​그림으로 그리는 것이 도움이됩니다. –

+0

댓글에서 opcodes에 대한 영어 이름 만 쓰면 도움이되지 않습니다. 더 높은 수준으로 만들어보십시오. 그리고 만약 당신이 정말로 dissasembly로부터 뭔가를 배우고 싶다면, 컴파일러가 아닌 인간의 코드를 사용해보십시오. – ruslik

+1

일반적으로 시작과 끝 부분은 일반적인 스택 설정 코드입니다. 할당 및 정렬 등. – zdav

답변

26

스택은 아래 성장. push은 스택 포인터 (esp)에서 빼고 pop은 esp에 더합니다. 이 점을 많이 이해하려면이 점을 명심해야합니다.

8048394:8d 4c 24 04   lea 0x4(%esp),%ecx    ; ?? 

레아 = 부하 효과적인 주소

이 스택에 4 바이트를 거짓말 물건의 주소를 저장합니다. 이것은 스택의 두 번째 항목을 의미하는 32 비트 (4 바이트 워드) x86 코드이기 때문에. 이것은 함수 코드 (이 경우에는 main)이므로 스택 맨 위에있는 4 바이트는 리턴 주소입니다.

8048398:83 e4 f0    and $0xfffffff0,%esp   ; ?? 

이 코드는 스택을 16 바이트로 정렬되어 있는지 확인합니다. 이 작업 후에 esp는이 작업 이전의 값보다 작거나 같아서 스택이 커질 수 있으므로 이미 스택에있는 모든 것을 보호 할 수 있습니다. 이것은 때로 main에서 수행됩니다. 함수가 정렬되지 않은 스택과 함께 호출되어 상황이 실제로 느려질 수 있습니다 (16 바이트는 x86에서 캐시 라인 너비입니다. 4 바이트 정렬이 실제로 중요합니다.). main에 정렬되지 않은 스택이 있으면 나머지 프로그램 역시 스택됩니다. 이 -4 인덱스를 가지고 있기 때문에이 반환 주소로 백업하는 의미하므로 ECX는, 스택의 이전 상단의 반환 주소의 다른 측면에있는 것에 대한 포인터로 전에로드 된 이후

804839b:ff 71 fc    pushl -0x4(%ecx)     ; ?? 

현재 함수가 스택의 맨 위로 되돌아 가게되어 메인이 정상적으로 리턴 될 수 있습니다. (푸시는 마술이며 동일한 명령에서 RAM의 다른 위치로로드하고 저장할 수있는 것처럼 보입니다.)

804839e:55     push %ebp      ; Store the Base pointer 
804839f:89 e5    mov %esp,%ebp     ; Initialize the Base pointer with the stack pointer 
80483a1:51     push %ecx      ; ?? 
80483a2:83 ec 4c    sub $0x4c,%esp     ; ?? 

이것은 표준 함수 프롤로그 (이전의 물건 주요 특별했다) 대부분이다. 이것은 지역 변수가 살 수있는 스택 프레임 (ebp와 esp 사이의 영역)을 만들고 있습니다. ebp가 푸시되어 이전 스택 프레임을 에밀그로 복원 할 수 있습니다 (현재 함수의 끝에서).

80483a5:c7 45 f8 0c 00 00 00 movl $0xc,-0x8(%ebp)    ; Move 12 into -0x8(%ebp) 
80483ac:c7 45 f4 14 00 00 00 movl $0x14,-0xc(%ebp)   ; Move 20 into -0xc(%ebp) 
80483b3:8b 45 f8    mov -0x8(%ebp),%eax    ; Move [email protected](%ebp) into eax 
80483b6:83 c0 7b    add $0x7b,%eax     ; Add 123 to [email protected] 
80483b9:89 45 f4    mov %eax,-0xc(%ebp)    ; Store the result into [email protected](%ebp) 

80483bc:b8 00 00 00 00  mov $0x0,%eax     ; Move 0 into eax 

eax는 정수 함수 반환 값이 저장되는 곳입니다. 이것은 main에서 0을 반환하도록 설정합니다.

80483c1:83 c4 10    add $0x10,%esp     ; ?? 
80483c4:59     pop %ecx      ; ?? 
80483c5:5d     pop %ebp      ; ?? 
80483c6:8d 61 fc    lea -0x4(%ecx),%esp    ; ?? 

함수 에필로그이다. 처음에는 이상한 스택 정렬 코드 때문에 이해하기가 더 어렵습니다. 스택이 왜 프롤로그보다 더 적은 양으로 조정되는지 알아내는 데 약간의 어려움이 있습니다.

이 특정 코드가 최적화 된 상태로 컴파일되지 않았다는 것이 확실하다면.컴파일러는 프로그램의 최종 결과가 동일하다는 것을 main에 나열된 수학을 수행하지 않더라도 볼 수 있기 때문에 아마도 거기에 없었을 것입니다. 실제로 뭔가를하는 프로그램 (부작용이나 결과가 있음)을 사용하면 가볍게 최적화 된 코드 (gcc에 대한 -O1 또는 -0s 인수)를 더 쉽게 읽을 수 있습니다.

컴파일러에서 생성 된 읽기 어셈블리는 종종 main이 아닌 함수에서 훨씬 쉽습니다. 코드를 이해하기 위해 읽고 싶다면 결과를 산출하기 위해 몇 가지 인수를 취하거나 전역 변수에서 작동하는 함수를 직접 작성하면 더 잘 이해할 수 있습니다.

아마도 도움이 될 또 다른 사항은 gcc가 어셈블리 파일을 디스 어셈블하는 대신 생성하는 것입니다. -S 플래그는 생성하도록 지시하지만 (다른 파일을 생성하지는 않음) 어셈블리 파일 이름을 .s으로 끝냅니다. 디스 어셈블 된 버전보다 읽기 쉬워야합니다.

+0

시간 내 주셔서 감사합니다. 매우 자세한 설명. 내가 뭘 찾고 있었는지. – Legend

+0

Q1 : 왜 처음으로 4 바이트를 더한 다음 빼기를하는 이유. 왜'lea (% esp), % ecx' 그리고'pushl (% ecx)'가 아닌가? 질문 2 : 이런 종류의 정렬을 비활성화하는 플래그가 있습니까 ('-fno-align-functions'가 작동하지 않습니다). – Tony

6

확실하지 왜 컴파일러는이 모든 물건을 수행하지만, 여기에 내가 해독 할 수있는 작업은 다음과 같습니다

8048394:8d 4c 24 04   lea 0x4(%esp),%ecx    ; ecx := esp+4 
8048398:83 e4 f0    and $0xfffffff0,%esp   ; align the stack to 16 bytes 
804839b:ff 71 fc    pushl -0x4(%ecx)     ; push [ecx-4] ([esp]) 
80483a1:51     push %ecx      ; push ecx 
80483a2:83 ec 4c    sub $0x4c,%esp     ; allocate 19 dwords on stack 
80483c1:83 c4 10    add $0x10,%esp     ; deallocate 4 dwords from stack 
80483c4:59     pop %ecx      ; restore ecx 
80483c5:5d     pop %ebp      ; and ebp 
80483c6:8d 61 fc    lea -0x4(%ecx),%esp    ; esp := [ecx-4] 
+1

설명 주셔서 감사. – Legend