2012-12-11 3 views
3

scanf 및 printf를 사용하는 일부 어셈블리 코드가 있는데 몇 가지 문제가 있습니다. 이 두 함수가 동일한 코드에서 사용될 때 레지스터의 값은 손실 된 것 같습니다. 프로그램은 기본적으로 숫자를로드하고 인쇄합니다.어셈블리에서의 호출 규칙 (64 비트) - scanf

extern printf 
extern scanf 
section .data 

    a db "set: ", 0 
    b db "not set: ", 0 
    reading db "Please enter a number: ", 0 
    message db "\n", 0 
    printsent db "%s", 10, 0 
    printint db "%d", 10, 0 
    printchar db "%c", 10, 0 

    readInt db "%d", 0 
    input db "%d", 0 

section .text 
    global main 

main: 

hatta: 
push rbp, 
mov rbp, rsp, 
push rbx, 
xor rax, rax, 
mov rdi, printsent, 
mov rsi, reading 
call printf, 
pop rbx, 

xor rax, rax, 
mov rdi, readInt, 
call scanf, 
mov rbx, rdi 

push rbx, 
xor rax, rax, 
mov rdi, printint, 

mov rsi, rbx, 
call printf, 
pop rbx, 

pop rbp, 
ret 

이상한 점은 라인 mov rdi, printint,이 제거되면, 우리는 정확한 값을 얻을 수 있다는 것입니다 : 우리는

여기에 우리의 코드의 리눅스에

nasm -f elf64 file.asm && gcc -o file file.o && ./file 

를 사용하여 실행합니다. 그러나 우리가 printsentence와 같은 일을한다면, 우리는 segmentation fault를 얻는다. 아무도 우리에게이 이유를 말할 수 있습니까?

감사합니다.

+1

호출 규칙이 다양하기 때문에 어떤 OS를 사용하고 있는지 말할 필요가 있습니다. 호출 규칙은 일반 함수와 가변 함수 (예 : printf)간에 차이가 있으므로 읽는 정보를 신중히 선택하십시오. – ams

+2

저는 이것이 어셈블러에서 이런 것들을 코딩하는 것이 요즘 의미가 있다고 생각하지 않기 때문에 이것이 운동이라고 생각합니다. 그것을 수행하는 방법을 찾는 가장 쉬운 방법은 해당 C 프로그램을 작성하고, 좋아하는 컴파일러를 가져 와서'-S' 또는 이와 동등한 것으로 컴파일하여 어셈블러에 넣는 것입니다. –

답변

1

당신이 여기에 C 플래그를 왜 이해가 안가, 반군 C 코드가 없지만, 귀하의 질문에 :

는 지금까지 내가 printf(format, argument)에 대한 리눅스의 glibc의 64에서 호출 규칙을 기억하는 format in rdi입니다, argument in rsi.

mov rdi, printsent,을 제거하면 printf(undefined,"Please enter a number: ")입니다. rdi에 형식 인수를 제공하지 않았지만 printf 은 모르며 그 순간에 rdi에있는 것을 사용합니다. 대부분 유효하지 않은 메모리 주소이므로 SIGSEGV이 호출됩니다.

기본적으로 x86의 함수 호출은 인수에 대해 비파괴 적이어야합니다 (필수 사항은 아니지만). 표준 라이브러리 기능은 일반적으로 있습니다. 인수를 스택에 푸시하고 완료되면 다시로드하여이 작업을 수행합니다. 이와 같이

당신이 그것을 반환 할 때 rdiprintint과 같은 내용을 가지고 readInt 포인터를 복원합니다 scanf(readInt, ...) 호출합니다. 따라서 rdi에는 필요한 형식에 대한 유효한 포인터가 들어 있기 때문에 mov rdi, printint, 줄을 제거해도 아무런 효과가 없습니다.

+0

"기본적으로 x86의 함수 호출은 인수에서 비파괴적인 것으로 간주됩니다"- Win64 나 UN * X x86_64에서 64 비트 x86에 대해서는 사실이 아닙니다.인수 전달에 사용되는 레지스터는 스크래치이며 실제로 인수를 취하지 않더라도 호출 된 함수가 임의로 수정할 수 있습니다. –

1

은 가능 하나 개 잘못된 가정을 기반으로 scanf 사용에 두 가지 오류가 있습니다 : 당신은 scanfrdi에 더 이상의 인수가 형식 "%d"으로 필요하지된다는로드 수를 반환한다는 것을 의미하는 것. 실제로 (성공적으로 스캔 된 경우) 번호는 두 번째 인수가 가리키는 메모리에 반환됩니다. 따라서 다음과 같이 변경하면 코드가 작동합니다. 라인 mov 인 RDI, printint이 제거되면 에 관한

pop rbx,        | ;delete 
             = 
xor rax, rax,       = xor rax, rax, 
mov rdi, readInt,      = mov rdi, readInt, 
             > mov rsi, rsp 
call scanf,       = call scanf, 
mov rbx, rdi       | pop rbx, 

, 우리는 올바른 값를 얻을 - 나는 그것을 의심한다.

관련 문제