본문 바로가기

dis & asm

어셈 분석 (변수)


#include <stdio.h>

int main()
{
    char a[]="abc";
    char *b="def";
    
    a[0]='q';
    b[0]='q';
 
    return 0;
}

c언어 책에 한번쯤 나와본 예제입니다.
프로그램을 실행하면 리눅스에서 Segmentation fault 에러 납니다.

이말은 곧 메모리를 잘못 참조하였는 말인데
b[0]='q' <--- 이 부분이 잘못되었죠

보통 추상적인 메모리 구조를 보면 스택, 힙, 데이타 영역으로 크게 나누어집니다.

char *b="def";   <-- "def" 문자열은 상수입니다.
상수는 값을 바꿀수 없습니다.
c언어 타입으로 보면 const char *이죠

밑에서 b[0]='q'로 바꿀려고 하니 에러가 나는 겁니다.

이제 어셈으로 디스어셈 해보겠습니다.

int main()
{
 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
 804839f:   89 e5                   mov    %esp,%ebp
 80483a1:   51                      push   %ecx
 80483a2:   83 ec 10                sub    $0x10,%esp
    char a[]="abc";
 80483a5:   a1 94 84 04 08          mov    0x8048494,%eax
 80483aa:   89 45 f8                mov    %eax,-0x8(%ebp)
    char *b="def";
 80483ad:   c7 45 f4 90 84 04 08    movl   $0x8048490,-0xc(%ebp)

    a[0]='q';
 80483b4:   c6 45 f8 71             movb   $0x71,-0x8(%ebp)
    b[0]='q';
 80483b8:   8b 45 f4                mov    -0xc(%ebp),%eax
 80483bb:   c6 00 71                movb   $0x71,(%eax)

    return 0;
 80483be:   b8 00 00 00 00          mov    $0x0,%eax
}


    char a[]="abc";
 80483a5:   a1 94 84 04 08          mov    0x8048494,%eax
 80483aa:   89 45 f8                mov    %eax,-0x8(%ebp)

먼저 스택에 저장되는 a값을 보겠습니다.
"abc" <--- 문자열도 상수값입니다.

0x8048494 번지에 "abc"값이 들어있습니다.
(gdb) x 0x8048494
0x80484a4:    0x00636261

0x80484a4 번지안에 --> 0x00636261 값이 있는것입니다.

61 char로 'a'입니다.
62 char로 'b'입니다.
63 char로 'c'입니다.

그런데 순서가 뒤에서 부터 시작하죠?
이건 리틀엔디안 방식으로 보이는 겁니다.
잘 모르시겠으면 네이버에 리틀엔디안 검색해 보삼


80483a5:   a1 94 84 04 08          mov    0x8048494,%eax
eax레지스터에 0x8048494값이 들어가는게 아닙니다.
eax에 0x8048494안에 있는값 즉 0x00636261값이 들어갑니다.

즉 값을 복사한겁니다.

 80483aa:   89 45 f8                mov    %eax,-0x8(%ebp)
스택에 값을 넣고 ebp-0x8번지에 0x00636261값이 들어있습니다.


    a[0]='q';
 80483b4:   c6 45 f8 71             movb   $0x71,-0x8(%ebp)
 그러면 위에 문장에서 값을 바꿔도 아무 문제 없습니다.

하지만 *b는
    char *b="def";
 80483ad:   c7 45 f4 90 84 04 08    movl   $0x8048490,-0xc(%ebp)

상수주소번지를 스택에 넣습니다.
결과를 보면

(gdb) x $ebp-0xc
0xbf819cec:    0x08048490   <--- 스택에 실주소값이 들어가 있습니다.


    b[0]='q';
 80483b8:   8b 45 f4                mov    -0xc(%ebp),%eax
 80483bb:   c6 00 71                movb   $0x71,(%eax)

실 주소값을 eax에 넣으면 0x8048490 값이 들어가고

eax에 간접참조하여 0x71을 넣을려고 하니 에러가 나는겁니다.
(%eax) <-- 괄호의 의미는 eax안에 값을 바꾸는게 아니라 eax안에 주소안에 있는 값을 바꾸는 겁니다.

결론은 a[]는 실제로 상수값을 복사하여 스택에 넣었고
*b는 주소값을 그대로 스택에 가져온겁니다.