2017-04-23 1 views
1

재미를 위해 어셈블리를 약간 흘려 보면서 이제는 프로 시저를 호출하는 데 어려움이 있습니다.스택을 통한 어셈블리 전달 변수

... 
_start: 
    push dword len 
    push dword msg 

    call print 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    pop ecx 
    pop edx 
    int 80h 
    ret 

나는 어셈블리의이 조각을 실행하면

nasm -f elf program.asm && ld -m elf_i386 -o program program.o && ./program 

내가 인쇄 기능의 내용으로 "전화 인쇄"를 대체 할 경우 동시에 그것은 모든 후, 프로그램의 내용 독방 감금 오류를 출력합니다 , 그것은 잘 작동합니다.

+0

있습니다됩니다 호출이 스택에 값을 넣는다는 것을 알고 있습니까? – rcd

답변

1

첫 번째 POP은 반송 주소를 나타냅니다. 두 번째 POP 주소는 msg입니다. int 80h 호출을 엉망으로 만들지 않으면 함수가 반환하려고 할 때 세그먼트 화 오류가 발생합니다.

반송 주소 뒤에는 esp + 4 및 esp + 8과 관련된 값이 있습니다. ESP+xx으로 직접이 주소에 액세스 할 수 있습니다. 좀 더 복잡한 절차를 빌드 할 때 당신은 EBP을 회피하고 싶지만 순간 ESP 함께 할 수 있습니다 지시 팝 ECX 대신 RET 명령의`EAX의 SYS_exit 시스템`의 주소를 수신하기 때문에, 당신은

SYS_EXIT equ 1 
SYS_WRITE equ 4 
STDOUT equ 1 

segment .data 

    msg db `Hello world!\n`  ; Backspaces for C-like escape-sequences ("\n") 
    len equ $- msg 

section .text 

    global _start 

_start: 
    push dword len 
    push dword msg 

    call print 
    ; and the stack? 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    mov ecx, [esp+4] 
    mov edx, [esp+8] 
    int 80h 
    ret 
+0

코드를 수정할 때,'call print' 후에 스택을 정리하지 않은 이유는 무엇입니까? 거기에 의견이 있습니다. '; 그리고 스택',하지만 그게 무슨 뜻인지 모르겠지만, 나는 의심스럽지 않을 것으로 의심합니다. –

+0

@CodyGray : 다음 SYS_EXIT는 스택을 "정리"합니다. 따라서 청소 여부는 중요하지 않습니다. 왜 OP가이 시점에서 스택을 정리하지 않았는지 나는 알지 못합니다. 어쩌면 여기에 게시되지 않은 목적을 위해 필요할 것입니다. – rkhb

+0

반송 주소가 튀어 나와서 저장 한 다음 나머지를 꺼내면 반송 주소를 다시 누른 다음 되돌릴 수 있습니까? – Jackywathy

2

아래의 코드는 당신이 그것을 작성해야하는 방식입니다 :

_start: 
    push dword len 
    push dword msg 
    call print 
    add esp, 8 ; equivalent to 2 32-bit POP instructions 
       ; (effectively "undoes" the above PUSH instructions 
       ; to restore the stack to its original state) 

    mov eax, SYS_EXIT 
    mov ebx, 0 
    int 80h 

print: ; *char (message), int (len) -> push len, then message 
    mov eax, SYS_WRITE 
    mov ebx, STDOUT 
    mov ecx, [esp+4] ; load "msg" from stack using an offset from ESP 
    mov edx, [esp+8] ; load "length" from stack using an offset from ESP 
    int 80h 
    ret 

문제는 어디에가해야 스택이 가리 키지 않았다이었다. 마지막으로 스택의 성격을 기억하고, callret 명령어가 스택 포인터에 영향을 미친다는 것을 기억해야합니다. call 함수를 사용하면 반환 주소가 스택에 푸시됩니다. popprint 안에 넣으면 실제로 반환 값이 스택에서 벗어나 잘못된 값을 제공 할뿐만 아니라 엉망입니다. 너의 능력은 나중에 ret 항아리.

스택의 함수에 전달 된 매개 변수를 검색하는 올바른 방법은 스택 포인터 (ESP)의 오프셋을 사용하는 것입니다. 첫 번째 매개 변수는 ESP + 4 (스택에 푸시 된 4 바이트 반환 주소 뒤)에서 찾을 수 있습니다 (call). 자세한 내용은 C 코드에서 일반적으로 사용되는 STDCALL 및 CDECL 호출 규칙을 참조하십시오.

+0

편집을 위해 Cody Gray에게 감사드립니다. :). – rcd

관련 문제