[강의용 문서라긴 보다 저도 미흡하기에 제가 직접 해본것을 정리하고 수정해가면서 더 나은 글이 되도록 해보겠습니다 .]

    

lob4번을 계속 풀어보다가 분명히 맞는거 같은데..같은데.. 하면서 코어덤프를 시도하게 되면서 방법을 대충 아주 짧게 정리해서 적겠습니다 
(결국 bash2가 원인이였습니다..)

코어 파일을 생성하기 위해서는 먼저 코어 파일의 사이즈를 설정을 해야합니다

ulimit 명령으로 제한 설정을 해야하는데
제가 사용한 명령어는 ulimit -c unlimited 입니다 .이렇게 설정되면 core파일이 설정됩니다 

gdb로 여는 명령어는 gdb -q [분석하려는 파일명] core 입니다 


이런식으로 어디 부분에서 오류가 낫는지 알 수 있습니다.


41414149 메모리에 접근할 수 없다는 뜻이며 레지스터나 메모리값을 변조해서 생긴 에러입니다

이제 오류가 난 지점에 접근해서 어디 부분에서 문제가 생겼는지 찾아보시면 됩니다.

제가 문제를 풀면서 발견한 에러들을 하나하나 모아두도록 하겠습니다..

'시스템' 카테고리의 다른 글

/bin/sh의 의미  (0) 2016.05.22
dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
리눅스 gdb 명령어 (계속 추가)  (0) 2016.02.28
공유메모리  (0) 2016.02.18
시스템 $0x80  (0) 2016.02.11


1. 프로그램 수행 관련 명령


  $ gdb -q [파일명]

    => gdb를 조용히(?) 켠다


  $ run [args] 

    => 디버깅할 프로그램의 수행 시작


  $ set args [args]

    => 프로그램이 시작 될 때, 전달할 인자 설정


  $ next [n]

    => breakpoint를 걸은 프로그램에서 다음 n(default=1)개의 문장을 수행하고 멈춘다.

    => 함수인 경우 함수 전체를 수행합니다.


  $ step [n] 

    => next [n]과 비슷합니다.

    => 차이점으로, 함수의 경우 함수에 들어가 한 문장씩 수행합니다.


  $ continue [n]

    => Ctrl+C이나 breakpoint에 멈추어진 프로그램의 수행을 계속합니다.

    => 여기서 n이 지정될 경우 n번째 breakpoint에서 수행을 멈추게 됩니다.



2. 브레이크 포인트 


  $ watch [EXP]

    => expression EXP 에 대한 watchpoint를 설정합니다.

    => EXP의 값이 변할 때마다 프로그램의 수행을 멈춥니다.


  $ delete [N]

    => breakpoint를 제거합니다. 인자값이 없으면 모든 bp를 제거합니다.

    => delete breakpoint 1 ( 예시 )


  $ enable [N .. ]

    => breakpoint를 enable 시킵니다. N은 breakpoint 숫자입니다.



3. 상태 확인

 


  $ info breakpoints [N]

    => 설정된 breakpoint 상태를 보여줍니다.


  $ info args

    => 현재 스택 프레임의 인자 변수를 보여줍니다.


  $ info display

    => 프로그램이 멈출 때 출력할 expression을 보여줍니다.




4. 이 외의 명령어


  $ quit

    => gdb를 종료합니다.


  $ make [args]

    => make를 실행합니다.


  $ set disassembly-flavor intel

    => intel 형식으로 어셈블리를 봅니다. ( set dis int 줄임 가능 )


  $ disassembly [func_name]

    => 함수의 어셈구문 코드를 확인합니다.


  $ print [$register or func_name]

    => 스택에 저장된 값을 보여줍니다.


  $ x/x [$register]

    => 해당 레지스터에 들어있는 값을 Hex로 표현


  $ x/5s [$register]

    => 해당 레지스터에 들어있는 값을 String으로 5개까지 표현


  $ x/d [$register]

    => Decimal


  $ x/i [$register]

    => Instructions


  $ list

    => Source File이 어딨는지 보여줌


  $ where

    => 어디에서 실행되었는지 보여줌


  $ main info sec

    => 실행가능한 섹션들을 보여줍니다.



'시스템' 카테고리의 다른 글

dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
코어덤프  (0) 2016.05.01
공유메모리  (0) 2016.02.18
시스템 $0x80  (0) 2016.02.11
프로시저와 스택 구조  (0) 2016.02.10

1 공유메모리 (shared memory)

보통 프로세스에서 사용되는 메모리영역은 해당 프로세스만이사용할수 있다. 하지만 때때로 여러개의 프로세스가 특정 메모리영역을 사용했으면 하는때가 있을것이다.

System V IPC 설비중의 하나인 "공유메모리"를 통해서 이러한일을 할수있다.

1.1 개요

모든 프로세스(:12)는 자신의 업무를 수행하기 위해서 필요한 자료를 저장하기 위한 메모리 공간을 가지게 된다. 이러한 메모리공간에는 CPU에 의해 수행되는 명령어들, 프로그램 시작시 정의되고 초기화된 데이타, 프로그램 시작시 정의되었지만 초기화 되지 않은 데이타, 함수호출에 필요한 정보, 동적할당이 이루어지는 데이타등 이 들어가게 된다. hjh 프로세스는 시작시 혹은 실행중에 이러한 데이타를 저장하고 사용하기 위한 메모리 공간을 커널에 요구하여서 할당받아 사용하게 되는데, 이러한 메모리공간은 기본적으로 메모리를 요청한 프로세스만이 접근가능하도록 되어있다. 하지만 가끔은 여러개의 프로세스가 특정 메모리 공간을 동시에 접근해야할 필요성을 가질때가 있을것이다.

공유메모리는 이러한 작업을 위한 효율적인 방법을 제공한다.

공유메모리는 여러 IPC 중에서 가장 빠른 수행속도를 보여준다.

그이유는 하나의 메모리를 공유해서 접근하게 되므로, 데이타 복사와 같은 불필요한 오버헤드가 발생하지 않기 때문으로, 빠른 데이타의 이용이 가능하다. 그러나 하나의 프로세스가 메모리에 접근중에 있을때, 또다른 프로세스가 메모리에 접근하는 일이 발생하면 자칫 데이타가 홰손될수 있을것이므로, 한번에 하나의 프로세스가 메모리에 접근하고 있다는걸 보증해줄수 있어야 할것이다.

이러한 작업을 위해서 Unix(:12) 에서는 Semaphore(:12) 라는 또다른 공유자원을 제어할수 있도록 해주는 도구를 제공해준다. 이번 문서에서는 Semaphore 를 다루지는 않을것이다. 이것은 다른 문서에서 다루도록 하고 여기에서는 단지 공유메모리에 대해서만 다루도록 할것이다.

다음은 공유메모리에 관련된 함수들의 모음이다.
#include <sys/types.h>
#include <sys/shm.h>

int shmget(key_t key, int size, int shmflg)
void *shmat( int shmid, const void *shmaddr, int shmflg )
int shmdt( const void *shmaddr)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)


=== 공유메모리는 어떻게 할당되는가 == 위의 함수들을 설명하기 전에 우선 공유메모리가 어떻게 할당되고, 어떤 과정을 통해서 접근가능한지에 대해서 우선 알아보도록 하겠다.

공유메모리의 생성요청은 최초 공유메모리 영역을 만드는 프로세스가 커널에 공유메모리 공간의 할당을 요청함으로써 이루어지며, 만들어진 공유메모리는 커널에 의해서 관리 되게 된다.

이런 이유로 한번만들어진 공유메모리는 운영체제를 리부팅하거나, 직접 공유메모리 공간을 삭제시켜주지 않은한, 공유메모리를 사용하는 모든 프로세스가 없어졌다고 하더라도, 계속적으로 유지되게 된다.

프로세스가 커널에게 공유메모리 공간을 요청하게 되면, 커널은 공유메모리 공간을 할당시켜주고 이들 공유메모리공간을 관리하기 위한 내부자료구조를 통하여, 이들 공유메모리를 관리하게 된다. 이 자료는 shmid_ds 라는 구조체에 의해서 관리되며 shm.h 에 정의되어 있다.
struct shmid_ds
{
    struct         ipc_perm shm_perm;    // 퍼미션
    int            shm_segsz;            // 메모리 공간의 크기
    time_t         shm_dtime;            // 마지막 attach 시간 
    time_t         shm_dtime;            // 마지막 detach 시간 
    time_t         shm_ctime;            // 마지막 변경 시간
    unsigned short shm_cpid;             // 생성프로세스의 pid
    unsigned short shm_lpid;             // 마지막으로 작동한 프로세스의 pid
    short          shm_nattch;           // 현재 접근한 프로세스의 수
};
Unix 버젼에 따라서 멤버변수들이 약간씩 차이를 보일수 있다.
  • shm_perm 공유메모리는 여러개의 프로세스가 동시에 접근 가능하므로, 파일과 같이 그 접근권한을 분명히 명시해줘야 한다.
  • shm_segsz 할당된 메모리의 byte 크기이다
  • shm_atime 가장최근의 프로세스가 세그먼트를 attach한 시간
  • shm_dtime 가장최근의 프로세스가 세그먼트를 detach한 시간
  • shm_ctime 마지막으로 이 구조체가 변경된 시간
  • shm_cpid 이 구조체를 생성한 프로세스의 pid
  • shm_lpid 마지막으로 작동을 수행한 프로세스의 pid
  • shm_nattch 현재 접근중인 프로세스의 수
이러한 공유메모리에 접근을 하기 위해서는 고유의 공유메모리 key 를 통해서 접근가능해지며, 이 key값을 통해서 다른 여러개의 공유메모리들과 구분되어 질수 있다.


1.2 shmget

shmget 은 커널에 공유메모리 공간을 요청하기 위해 호출하는 시스템 호출 함수이다. key 는 바로 위에서 설명했듯이 고유의 공유메모리임을 알려주기 위해서 사용된다. shmget 을 이용해서 새로운 공유메모리 영역을 생성하거나 기존에 만들어져있던 공유메모리 영역을 참조할수 있다 




  첫번째 아규먼트는 여러개의 공유메모리중 원하는 공유메모리에 접근하기 위한 Key 값이다. 이 Key 값은 커널에 의해서 관리되며, Key 값을 통해서 선택적인 공유메모리에의 접근이 가능하다.    두번째 아규먼트는 공유메모리 의 최소크기 이다. 새로운 공유메모리를 생성하고자 한다면 크기를 명시해주어야 한다.  존재하는 메모리를 참조한다면 크기는 0으로 명시한다.   3번째 아규먼트는 공유메모리의 접근권한과, 생성방식을 명시하기 위해서 사용한다. 아규먼트의 생성방식을 지정하기 위해서 IPC_CREAT 와 IPC_EXCL 을 사용할수 있다. 아래 이들에 대해서 설명을 해두었다. 




  * IPC_CREAT
key 를 이용 새로운 공유메모리 공간을 만든다.
  • IPC_EXCL
    IPC_CREAT와 같이 사용되며, 공유메모리 공간이 이미 존재할경우 error 를 되돌려준다.

    만약 IPC_CREAT 만 사용된다면 shmget()은 새로 생성되는 공유메모리공간을 지시하는 공유메모리공간 "식별자" 되돌려준다. 만약 입력된 key 값이 지시하는 공유메모리 공간이 이미 존재하고 있다면 존재하는 공유메모리 공간의 "식별자"를 되돌려준다. IPC_EXCL 과 IPC_CREAT 를 같이 사용할경우, 공유메모리 공간이 존재하지 않으면 새로 생성시켜주며, 존재할경우에 error 를 되돌려준다.

  • 3번째 아규먼트는 이외에도 권한을 지정해줄수도 있다. 권한은 파일권한과 동일하게, 유저, 그룹, Other 에 대한 읽기/쓰기 권한을 지정할수 있다. 단 실행권한은 줄수 없도록 되어 있다. 아래와 같이 사용가능하다.
    int shmid;
    key_t keyval;
    
    keyval = 1234;
    shmid = shmget(keyval, 1024, IPC_CREAT | 0666)); 
    if (shmid == -1)
    {
        return -1;
    }
    

    1.3 shmat

    일단 공유메모리 공간을 생성했으면, 우리는 공유메모리에 접근할수 있는 int 형의 "식별자" 를 얻게 된다. 우리는 이 식별자를 shmat 를 이용해서 지금의 프로세스가 공유메모리를 사용가능하도록 "덧붙임" 작업을 해주어야 한다.

    첫번째 아규먼트는 shmget을 이용해서 얻어낸 식별자 번호이며, 두번째 아규먼트는 메모리가 붙을 주소를 명시하기 위해 사용하는데, 0을 사용할경우 커널이 메모리가 붙을 주소를 명시하게 된다. 특별한 사항이 없다면 0을 사용하도록 한다. 세번째 아규먼트를 이용해서, 우리는 해당 공유메모리에 대한 "읽기전용", "읽기/쓰기가능" 모드로 열수 있는데, SHM_RDONLY를 지정할경우 읽기 전용으로, 아무값도 지정하지 않을경우 "읽기/쓰기 가능" 모드로 열리게 된다.

    1.4 shmdt

    프로세스가 더이상 공유메모리를 사용할필요가 없을경우 프로세스와 공유메모리를 분리 하기 위해서 사용한다. 이 함수를 호출할 경우 단지 현재 프로세스와 공유메모리를 분리시킬뿐이지, 공유메모리 내용을 삭제하지는 않는다는 점을 기억해야 한다. 공유메모리를 커널상에서 삭제 시키길 원한다면 shmctl 같은 함수를 이용해야 한다.

    shmdt 가 성공적으로 수행되면 커널은 shmid_ds 의 내용을 갱신한다. 즉 shm_dtime, shm_lpid, shm_nattch 등의 내용을 갱신하는데, shm_dtime 는 가장 최근에 dettach (즉 shmdt 를 사용한)된 시간, shm_lpid 는 호출한 프로세세의 PID, shm_nattch 는 현재 공유메모리를 사용하는 (shmat 를 이용해서 공유메모리에 붙어있는) 프로세스의 수를 돌려준다. shmdt 를 사용하게 되면 shm_nattch 는 1 감소하게 될것이며, shm_nattch 가 0 즉 더이상 붙어있는 프로세스가 없다라는 뜻이 될것이다. shm_nattch 가 0이 되어있을때 만약 이 공유메모리가 shm_ctl 등에 의해 삭제표시 가 되어 있다면, 이 공유메모리는 삭제되게 된다.

    1.5 shmctl

    이것은 공유메모리를 제어하기 위해서 사용한다. 즉 shmid_ds 를 직접 제어함으로써, 해당 공유메모리에 대한 소유자, 그룹 등의 허가권을 변경하거나, 공유메모리를 삭제혹은, 공유메모리의 잠금을 설정하거나 해제하는 등의 작업을 한다.

    2번째 아규먼트를 이용해서 shmid 가 가르키는 공유메모리를 제어하며, cmd 를 이용해서 원하는 제어를 할수 있다. cmd 를 이용해 내릴수 있는 명령에는 다음과 같은 것들이 있다.
    • IPC_STAT 공유메모리 공간에 관한 정보를 가져오기 위해서 사용된다. 정보는 buf 에 저장된다.
    • IPC_SET 공유메모리 공간에 대한 사용자권한 변경을 위해서 사용된다. 사용자 권한 변경을 위해서는 슈퍼유저 혹은 사용자권한을 가지고 있어야 한다.
    • IPC_RMID 공유메모리 공간을 삭제하기 위해서 사용된다. 이 명령을 사용한다고 해서 곧바로 사용되는건 아니며, 더이상 공유메모리 공간을 사용하는 프로세스가 없을때, 즉 shm_nattch 가 0일때 까지 기다렸다가 삭제된다. 즉 해당 공유메모리 공간에 대해서 삭제표시를 하는거라고 생각하면 된다.

    다음은 실제로 공유메모리를 사용하는 방법에 대한 가장간단한 예제이다. 자식과 부모프로세스간에 어떻게 메모리가 공유되는지 보여준다.

    예제 : shm.c
    #include <sys/ipc.h> 
    #include <sys/shm.h> 
    #include <string.h> 
    #include <unistd.h> 
    
    
    int main()
    {
        int shmid;
        int pid;
    
        int *cal_num;
        void *shared_memory = (void *)0;
    
    
        // 공유메모리 공간을 만든다.
        shmid = shmget((key_t)1234, sizeof(int), 0666|IPC_CREAT);
    
        if (shmid == -1)
        {
            perror("shmget failed : ");
            exit(0);
        }
    
        // 공유메모리를 사용하기 위해 프로세스메모리에 붙인다. 
        shared_memory = shmat(shmid, (void *)0, 0);
        if (shared_memory == (void *)-1)
        {
            perror("shmat failed : ");
            exit(0);
        }
    
        cal_num = (int *)shared_memory;
        pid = fork();
        if (pid == 0)
        {
            shmid = shmget((key_t)1234, sizeof(int), 0);
            if (shmid == -1)
            {
                perror("shmget failed : ");
                exit(0);
            }
            shared_memory = shmat(shmid, (void *)0, 0666|IPC_CREAT);
            if (shared_memory == (void *)-1)
            {
                perror("shmat failed : ");
                exit(0);
            }
            cal_num = (int *)shared_memory;
            *cal_num = 1;
    
            while(1)
            {
                *cal_num = *cal_num + 1;
                printf("child %d\n", *cal_num); 
                sleep(1);
            }
        }
    
        // 부모 프로세스로 공유메모리의 내용을 보여준다. 
        else if(pid > 0)
        {
            while(1)
            {
                sleep(1);
                printf("%d\n", *cal_num);
            }
        }
    }
    
    예제 프로그램이 하는 일은 간단하다. int 형의 공유메모리 공간을 할당한다음. 자식프로세스에서 여기에 1씩을 더하고 부모프로세스에서는 공유메모리 내용을 출력하는 일을한다.

    2 공유메모리 제어하기

    쉘에서 공유메모리의 상황을 보여주기 위해서 ipcs(1)란 도구를 제공한다. ipcs 를 사용하면 공유메모리 뿐만 아닌, Semaphore, Message Queue 등 소위 sytem V IPC 설비에 대한 내용을 보여준다. 그리고 ipcrm 도구를 이용해서 필요없는 공유메모리, Message Queue, Semaphore 등을 지워줄수 있다.

    위의 예제코드를 컴파일 시켜서 실행시킨다음 ipcs 를 이용해서 확인을 해보면 공유메모리 자원이 어떤식으로 관리되는지 좀더 이해를 쉽게 할수 있을것이다.

    2.1 공유 메모리 정보 확인

    -l 옵션과 함께 ipcs를 실행하면 ipc(:12)자원 제한 정보를 확인할 수 있다.
    $ ipcs -l
    ------ Shared Memory Limits --------
    max number of segments = 4096
    max seg size (kbytes) = 32768
    max total shared memory (kbytes) = 8388608
    min seg size (bytes) = 1
    
    ------ Semaphore Limits --------
    max number of arrays = 128
    max semaphores per array = 250
    max semaphores system wide = 32000
    max ops per semop call = 32
    semaphore max value = 32767
    
    ------ Messages: Limits --------
    max queues system wide = 1706
    max size of message (bytes) = 8192
    default max size of queue (bytes) = 16384
    
    -m 옵션으로 실행하면 현재 사용중인 ipc 자원 정보를 확인할 수 있다.
    $ ipcs -m
    
    ------ Shared Memory Segments --------
    key        shmid      owner      perms      bytes      nattch     status      
    0x00000000 0          root       777        135168     2                       
    0x00000000 819201     yundream   600        393216     2          dest         
    0x00000000 950274     yundream   600        393216     2          dest         
    0x00000000 983043     yundream   600        393216     2          dest         
    0x00000000 917508     yundream   600        393216     2          dest         
    0x00000000 1015813    yundream   600        393216     2          dest         
    0x00000000 1048582    yundream   600        393216     2          dest         
    0x00000000 27590663   yundream   600        393216     2          dest         
    0x00000000 35684360   yundream   666        4343780    2          dest         
    0x00000000 35717129   yundream   666        282808     2          dest  
    

    2.2 /proc 파일 시스템으로 제어하기

    리눅스 운영체제(:12)는 /proc 파일 시스템으로 공유 메모리 자원 값을 변경할 수 있다.
    • /proc/sys/kernel/shmmax : 프로세스가 생성할 수 있는 공유 메모리의 최대 크기
    • /proc/sys/kernel/shmall : 현재 사용중인 공유 메모리 크기
    프로세스가 생성할 수 있는 공유 메모리 크기는 다음과 같이 변경하면 된다.
    // 500M로
    # echo 536870912 > /proc/sys/kernel/shmmax
    

    출처  : :http://www.joinc.co.kr/modules/moniwiki/wiki.php/Site/system_programing/IPC/SharedMemory

    '시스템' 카테고리의 다른 글

    dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
    코어덤프  (0) 2016.05.01
    리눅스 gdb 명령어 (계속 추가)  (0) 2016.02.28
    시스템 $0x80  (0) 2016.02.11
    프로시저와 스택 구조  (0) 2016.02.10

    int $0x80은 운영체제에 할당된 인터럽트 영역으로 system call을 하라는 뜻이다

    '시스템' 카테고리의 다른 글

    dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
    코어덤프  (0) 2016.05.01
    리눅스 gdb 명령어 (계속 추가)  (0) 2016.02.28
    공유메모리  (0) 2016.02.18
    프로시저와 스택 구조  (0) 2016.02.10

    - 복귀 주소 (Return Address)

     프로시저가 해당 기능을 끝마친 후에 이 프로시저를 호출한 바로 다음 코드의 내용이 실행되어질 수 있도록 해당 정보를 가지고 있음

     프로시저가 호출되기 바로 직전에 저장해 놓고 호출한 프로시저가 끝마치게 되면 이 내용을 사용하고, 해당 프로시저가 끝나게 되면 복귀 주소 내용 역시 필요 없어짐

     프로시저 안에서 또 다른 프로시저의 호출 또는 재귀 호출과 같은 형태의 흐름이 가능함 (복귀 주소로 돌아오기 전에 또 다른 복귀 주소가 중첩하여 여러 개 존재할 수 있음)

     => 이러한 복귀 주소의 특성을 고려하여 컴퓨터 시스템에서는 스택이라는 메모리 공간을 정의해놓고, 복귀 주소 정보를 저장하고 있다.

     

    - 스택 로컬 변수 저장

     프로시저 내에서 사용하는 로컬 변수는 그 프로시저가 호출되어 실행될 때까지만 유효함

     즉, 프로시저가 호출될 때에만 그 프로시저에서 사용할 로컬 변수의 메모리 공간이 필요하고, 프로시저가 복귀하게 되면 이 영역에서 사용한 로컬 변수들 역시 쓸모없는 데이터가 되어버리기 때문에, 복귀 주소처럼 스택 구조에 저장

     스택 구조에서 쓰레기 값 영역에 대해 다음 번 프로세서가 덮어쓰기 형태로 사용하기 때문에 효율적으로 스택 메모리를 사용할 수 있음

     

    - 스택 파라미터 전달

     프로그램에서 어떠한 데이터를 프로시저에게 전달하고자 할 때 전달하고자 하는 데이터들을 어디엔가 저장하여 놓거나, 데이터가 저장된 위치를 발견하여 프로시저 내에서 사용하는 단계, 혹은 프로시저가 복귀 되어질 때 전달받은 데이터를 다시 해제하는 방법이 있음

     프로시저로의 데이터 전달 역시 전달하고자 하는 프로시저 내에서만 사용하면 되기 때문에 스택 영역에 저장

     파라미터가 있는 프로시저의 경우 프로시저가 호출되기 전에 전달하고자 하는 파라미터들을 스택에 저장하여 놓은 후 프로시저로 제어 흐름을 바꿈

     프로시저 내에서 중간에 스택의 내용을 변경하는 코드가 있는 경우를 해결하기 위해 스택이 변경되는 경우까지 고려하여 그 이후의 코드에 대해 스택의 위치를 새로 계산하여 올바른 파라미터를 가리킬 수 있도록 하고 있으며, 스택 프레임이라는 것을 구성하여 해결하고 있음

     * 스택 프레임

       호출된 프로시저의 복귀 주소를 기준으로 고정된 변위 값을 통하여 파라미터를 다루는 것

       파라미터 참조 기준을 push나 pop 등에 의해 스택 포인터의 값이 변경될 수 있는 스택 포인터(ESP) 기준이 아니라, 명령어에 의하여 영향을 받지 않는 EBP 레지스터를 기준으로 하여 문제를 해결함

       스택 프레임 구성은 로컬 변수 참조에도 편리함을 제공

     ESP, EBP 어셈블리 예제)

    push ebp // 이전 스택의 베이스 주소를 저장

    mov ebp, esp // 현재 스택의 꼭대기를 새로운 스택의 베이스로 설정 (새로운 스택 시작)

    보통 EBP는 데이터 접근을 위한 기준 주소를 저장, ESP는 최상위 주소를 유지하는  용도로 사용

    - 파라미터 전달 방법 (Calling Conventions)

     콜링 컨벤션 : 스택을 이용하여 파라미터를 전달할 때 스택에 파라미터를 어떤 순서로 넣을 것인지, 전달된 파라미터를 어느 곳에서 해제할 것인가에 따른 여러가지 방법

     * __cdecl

       C 또는 C++ 프로그램에서 파라미터 전달 시 디폴트로 사용하는 방식으로, 파라미터 전달은 오른쪽에서 왼쪽 방향으로 이루어지며 프로시저를 호출한 쪽에서 파라미터에 대한 해제까지 책임짐

     * __stdcall

       Windows API의 프로시저에서 사용하는 콜링 컨벤션 방식으로, 오픈쪽에서 왼쪽 방향으로 스택에 저장하며 파라미터의 해제는 프로시저가 복귀 되기 전에 이루어짐

       함수의 독립성이 뛰어나다는 장점이 있음 (프로시저를 부르기 전에 스택에 파라미터를 쌓아놓고 그 프로시저를 부르기만 하면 그 함수가 리턴되어진 후 호출한 프로시저에 대해 신경쓰지 않아도 되며, 스택을 해제하는 코드가 호출한 프로시저 안에 있으므로 여러 곳에서 호출되더라도 스택을 해제하는 코드는 하나만 존재하므로 코드의 크기가 줄어듬)

     * __fastcall

       처음 두 개의 파라미터는 스택을 사용하지 않고 ecx와 edx 레지스터를 사용하며, 그 이상의 파라미터에 대해서만 오른쪽에서 왼쪽 방향으로 스택에 저장하며, 스택 제거는 __stdcall과 동일함

     

    - Windows에서의 스택 구조

     어떤 프로그램이 함수를 부르거나 로컬 변수를 저장하기 위해서는 스택 공간이 필요하며, 윈도우즈에서는 이러한 공간을 스레드가 생성되는 단계에서 함께 만들며, 스레드 생성시 디폴터로 1MB의 메모리를 예약하여 놓고 이 중 1개의 페이지인 4KB만을 실제 메모리와 매핑시켜 놓음으로써 사용 가능하게 하고 또 하나의 메모리는 가드 페이지라는 형태로 만들어 둠

     기본적인 1MB 메모리에서 상위 첫 페이지의 4KB 영역은 Committed 영역으로 프로그램에서 바로 스택 영역으로 사용되어질 수 있는 영역이며, 그 아래 한 페이지인 4KB 영역은 가드 페이지로 윈도우즈에 의해 페이지 보호가 이루어지고 있으며, 그 외의 영역은 단지 예약만 해놓은 상태로 실제적인 물리적 메모리가 할당되지 않은 상태임 (예약 해놓음으로써 이 프로세스 공간에 대한 메모리 할당을 스택 용도 외로 사용하지 못하게 함)

     

    - 함수에서의 레지스터 사용

     컴퓨터에서 수행되는 대부분의 연산은 레지스터를 이용해 이루어지는데, 레지스터의 개수는 제한적이기 때문에 함수 내에서의 또 다른 함수 호출 시 레지스터간의 충돌을 고려해야 함

     Windows의 컴파일러들은 이러한 문제를 피하기 위해 Caller-save/Callee-save 레지스터와 같은 규칙을 사용하여 함수들간의 레지스터 사용의 충돌을 피함

     * Caller-save 레지스터 (scratch 레지스터 혹은 volatile 레지스터)

       함수 내에서 어떠한 임시적 저장 없이 마음대로 쓸 수 있는 레지스터로, 호출한 이전 함수에 어떠한 영향도 미치지 않는 레지스터들로 정의

       ex) RAX, RCS, RDX, R8~R11, ST(0)~ST(7), XMM0~XMM5, High half of XMM6~XMM15

     * Callee-save 레지스터

       호출한 함수에 영향을 미칠 수 있기 때문에 해당 레지스터를 사용하기 전에 반드시 저장을 해놓고 사용해야 하는 레지스터로, 해당 함수가 끝났을 때 반드시 이전의 값으로 돌려주어 이 함수를 호출한 함수에 어떠한 영향을 미쳐선 안 되는 레지스터들로 정의

       ex) RBX, RSI, RDI, RBP, R12~R15, XMM6~XMM15

     

    - Name Mangling (Name Decoration)

     함수를 선언하거나 전역 변수 등을 선언하였을 때, 사용되어진 이름들은 실제 컴파일 단계에서 일정한 규칙을 가지고 그 이름을 변경하는데 이를 네임 맹글링 혹은 네임 데코레이션이라 부름

     링커(Linker)가 다른 범위에 있는 같은 이름의 함수와 변수들에 대하여 구별할 수 있도록 함으로써 해당 함수 전체의 모듈에서 동일한 방법으로 사용되어지는지 체크할 수 있기 때문에 컴파일러 입장에서 중요한 작업이며, Namespace 안에 있는 변수의 이름에 대해서도 그 Namespace와 합해진 형태의 네임 맹글링을 만들게 됨

     

     

    '시스템' 카테고리의 다른 글

    dummy 크기 정확히 파악하기 - 출처 http://beist.org  (0) 2016.05.01
    코어덤프  (0) 2016.05.01
    리눅스 gdb 명령어 (계속 추가)  (0) 2016.02.28
    공유메모리  (0) 2016.02.18
    시스템 $0x80  (0) 2016.02.11

    + Recent posts