2014-06-14 1 views
1

나는 최근에 x86-64 어셈블리를 사용하기로 결정했습니다. argv를 표시 할 때 문제가 발생했습니다x86-64 linux assembly. EFAULT 때문에 argv에서 쓰기를 사용하면 작동하지 않습니까? "잘못된 주소"

예 제가 작성한 코드는 좋지 않습니다. 가정을 만들고 오류를 확인하지는 않지만 실제로 그 원인이라고 생각하지 않습니다. 이 문제.

여기 내 프로그램이 핵심입니다.

 .globl _start 
     .text 
main: 
_start: 
     movl $10,%edx   # No. of chars to write 
     movq 16(%rsp),%rcx  # argv[1] 
     movl $1,%ebx   # stdout 
     movl $4,%eax   # write 
     int  $0x80 

     movl $0,%ebx 
     movl $1,%eax 
     int  $0x80 

나는

을 helloworldddddddddd ./myprog 내 프로그램을 실행 그래서 변수는 argv [0] =./MYPROG 와는 argv [1] = helloworlddddddddddd 아니면

를 입력 어떤 내 프로그램은 첫 번째를 작성해야

argv [1]의 10 문자는 stdout에 저장됩니다.

예외 : 작동하지 않는 경우. 쓰기가 -14로 반환됩니다. 오류 EFAULT입니까? 나쁜 주소를 의미합니다.

그래서 나는이 대신

 .globl _start 
     .text 
main: 
_start: 
     movq 16(%rsp),%rax 
     movq $bob,%rbx 
     subq %rcx,%rcx 
.Lloop: 
     movb (%rax,%rcx),%dl 
     movb %dl,(%rbx,%rcx) 
     cmpb $0,%dl 
     je  .Ldone 
     addq $1,%rcx 
     jmp  .Lloop 

.Ldone: movl $10,%edx 
     movq $bob,%rcx 
     movl $1,%ebx 
     movl $4,%eax 
     int  $0x80 

     movl $0,%ebx 
     movl $1,%eax 
     int  $0x80 

     .comm bob,50 

그것은 복사 [1] 메모리 영역에 내가 전화 한 밥을 argv를 한 다음 표준 출력이 복사본을 쓰려고 썼다. 나는 심한 x86이라고 확신하지만, 제대로 작동합니다. 나는이 프로그램을 컴파일하고 실행하면 [1]

마지막으로 좀 더 내 초 이상 내 첫 번째 프로그램처럼
#include <unistd.h> 

int main(int argc, char **argv) { 
     write(1,argv[1],10); 

     return 0; 
} 

C

이 썼다는 argv에 있던 어떤 출력뿐만 아니라 작동합니다. 이 단계까지 나는 완전히 당혹 스럽다.

그래서 "쓰기"시스템 호출은 내 프로그램의 argv 배열을 읽을 수 없지만 사본을 읽을 수 있습니다. 아, 그리고 C로 쓰면 작동합니다. 이것은 완전히 기괴한 것 같습니다. 아무도 나에게 무슨 일이 일어나고 있는지, 왜 말해 줄 수 있습니까?


편집 :

그것은 내가 혼합 된 32 비트 및 64 비트 코드를 사용하고 있음을 지적하고있다. 그래서 나는 100 % 64bit로 바꿨다.

첫 번째 프로그램 :

.globl _start 
    .text 
main: 
_start: 
    movl $10,%edx    # No. of chars to write 
    movl 16(%rsp),%esi   # argv[1] 
    movl $1,%edi     # stdout 
    movl $1,%eax     # write 
    syscall 

    movl $0,%edi 
    movl $60,%eax 
    syscall 

번째 프로그램 : 심지어 지금 64 비트, 그리고 두 번째 프로그램이 여전히 작동하지만 여전히

.globl _start 
    .text 
main: 
_start: 
    movq 16(%rsp),%rax 
    movq $bob,%rbx 
    subq %rcx,%rcx 
.Lloop: 
    movb (%rax,%rcx),%dl 
    movb %dl,(%rbx,%rcx) 
    cmpb $0,%dl 
    je  .Ldone 
    addq $1,%rcx 
    jmp  .Lloop 

.Ldone: movl $10,%edx    # No. of chars to write 
    movq $bob,%rsi    # buffer 
    movl $1,%edi     # stdout 
    movl $1,%eax     # write 
    syscall 

    movl $0,%edi     # return 0 
    movl $60,%eax    # exit 
    syscall 

    .comm bob,50 

같은 오류가 첫 번째 프로그램은 여전히도 작동하지 그것도 지금은 64bit

첫번째 프로그램의 64 비트 버전을위한 Strace. (작동하지 않는 한) :

execve("./Myprog", ["./Myprog", "bananablahblah"], [/* 46 vars */]) = 0 
write(1, 0x1491f543, 10)    = -1 EFAULT (Bad address) 
_exit(0)        = ? 
+++ exited with 0 +++ 

답변

2

현재 세 가지 주요 오류를하고있다 :

  1. 당신은 _start으로 main을 혼동하고 있습니다.그들은 이 아닙니다 동의어 - _start의 표준 C 라이브러리 구현은 main을 호출하기 전에 중요한 초기화를 수행합니다. _start을 다시 구현하려고하지 않으려 고합니다. 실행 파일에 정의하지 마십시오. 그것을 얻기 위해 libc에 링크하십시오.

  2. 64 비트 실행 파일에서 32 비트 시스템 호출 (int $0x80)을 사용하려고합니다. 이것은 올바르게 작동하지 않습니다. 특히, 스택을 포함하여 4GB 경계 위에 존재하는 메모리를 읽을 수 없습니다! 64 비트 시스템 호출을 수행하려면 syscall 명령어를 사용해야합니다. 그러나 약간 다른 호출 규칙을 사용하고 다른 호출 번호를 사용한다는 점을 명심하십시오!

  3. 64 비트 명령어가 필요하며 64 비트 명령어가 필요한 일부 위치에 레지스터를 사용하고 있습니다. 이것은 때때로 낮은 포인터와 값에 대해서는 작동하지만 다른 값은 잘립니다. 특별히 작은 것을 필요로하지 않는 한 (예 : char의 경우 8 비트 레지스터) 64 비트 명령어를 사용하고 64 비트 명령어를 사용하여 의 습관을 항상으로 설정하십시오.

+0

나는 gdb가 불평을 멈추기 위해 주에 넣었습니다. 그것은 그것없이 잘 동작합니다. 나는 32 비트 64 비트 int 대 시스템 콜에 대해 몰랐다. 고마워, 내가 살펴 보겠다.하지만 여전히 내 문제의 주요 부분이라고 생각하지 않는다. – user3429500

+0

그가 말하는 것은 당신이받는 오류와 일치한다. "잘못된 주소"라는 오류는 쓰기를 시도한 메모리의 주소가 잘못되어 액세스 할 수 없거나 다른 주소로 액세스 할 수 없음을 의미합니다. 다른 것 같지 않으므로 제거 프로세스에 따라 duskwuff의 'int 80'대답이 문제가됩니다. – Tyler

+0

그렇지 않은 경우. 방금 두 프로그램 모두 64 비트 시스템 호출을 사용하도록 다시 작성했는데 첫 번째 프로그램은 여전히 ​​작동하지 않고 두 번째 프로그램은 여전히 ​​작동합니다. – user3429500