Egloos | Log-in
F/OSS study
F/OSS study
core 파일의 구조 (1)
OS: ubuntu-9.10
linux: 2.6.31
arch: x86


SIGSEGV 등의 몇몇 signal들은 기본 동작으로 프로세스를 종료시키면서
가능한 경우 해당 프로세스의 메모리 상태를 dump하는 core 파일을 생성한다.
core 파일의 크기에는 제한이 걸려있는데 보통 기본값은 0으로
이 경우 core 파일을 생성하지 않는다.

이를 확인하기 위해서는 ulimit 명령을 이용하면 된다.
core 파일의 크기 제한을 변경하려면 -c 옵션을 주면 된다.
한 가지 주의할 점은 파일 크기의 단위는 byte가 아니라 block 크기라는 점이다.
block 크기는 시스템에 따라 달라질 것 같은데 (512 or 1024)
내 환경에서는 1024 byte였다. (ubuntu-9.10-desktop-i386, bash-4.0)

core 파일을 생성하기 위해 다음과 같은 프로그램을 사용할 것이다.

segfault.c:
int main(void)
{
  *(int *) 0 = 1;
  return 0;
}

코드는 다음과 같이 생성된다.

$ gcc segfault.c
$ objdump -d | grep -A8 '<main>'
080483b4 <main>:
 80483b4:    55                       push   %ebp
 80483b5:    89 e5                    mov    %esp,%ebp
 80483b7:    b8 00 00 00 00           mov    $0x0,%eax
 80483bc:    c7 00 01 00 00 00        movl   $0x1,(%eax)
 80483c2:    b8 00 00 00 00           mov    $0x0,%eax
 80483c7:    5d                       pop    %ebp
 80483c8:    c3                       ret    
 80483c9:    90                       nop

즉, 0x80483bc 위치에서 signal이 발생할 것이라는 것을 알 수 있다.
이 값은 segmentation fault 발생 시 cr2 레지스터에 저장된다.
아마도 core 파일에 어딘가에 eip = 0x080483bc, eax = 0 이라는 정보가 담겨있을 것이다.

이제 core 파일 크기 제한을 풀고 프로그램을 실행해보자.

$ ulimit -c unlimited
$ ./a.out
Segmentation fault (core dumped)
$ ls -hl core
-rw------- 1 namhyung namhyung 144K 2009-11-28 15:48 core

참고로 ulimit에 대해서 한 마디 추가하자면
위와 같이 ulimit 명령을 실행하는 경우 hard limit 값과 soft limit 값이 모두 변경된다.
이 경우 값을 현재보다 낮추는 것은 가능하지만 다시 올리는 것을 불가능하다.
(단 최초에 0일 때 늘리는 것만 가능한 것 같다!)

따라서 limit 값을 계속 바꿔가면서 작업해야 할 때는
soft limit 값만 변경하도록 -S 옵션을 앞에!! 붙여서 호출해야 (ex. ulimit -Sc 100)
쓸데없이 터미널을 죽였다 다시 실행하는 수고를 줄일 수 있다..

core 파일은 ELF 형식으로 구성되므로 readelf 명령을 통해 구조를 볼 수 있는데
아래와 같이 실행했을 때 LOAD 타입의 segment들이 바로
커널에서 vm_area_struct (vma) 구조체로 표현되는 메모리 구역이다.

$ readelf -l core

Elf file type is CORE (Core file)
Entry point 0x0
There are 15 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  NOTE           0x000214 0x00000000 0x00000000 0x0022c 0x00000     0
  LOAD           0x001000 0x00110000 0x00000000 0x00000 0x13e000 R E 0x1000
  LOAD           0x001000 0x0024e000 0x00000000 0x02000 0x02000 R   0x1000
  LOAD           0x003000 0x00250000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x004000 0x00251000 0x00000000 0x03000 0x03000 RW  0x1000
  LOAD           0x007000 0x0042b000 0x00000000 0x01000 0x01000 R E 0x1000
  LOAD           0x008000 0x009ea000 0x00000000 0x00000 0x1b000 R E 0x1000
  LOAD           0x008000 0x00a05000 0x00000000 0x01000 0x01000 R   0x1000
  LOAD           0x009000 0x00a06000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x00a000 0x08048000 0x00000000 0x00000 0x01000 R E 0x1000
  LOAD           0x00a000 0x08049000 0x00000000 0x01000 0x01000 R   0x1000
  LOAD           0x00b000 0x0804a000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x00c000 0xb7896000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x00d000 0xb78af000 0x00000000 0x02000 0x02000 RW  0x1000
  LOAD           0x00f000 0xbff59000 0x00000000 0x15000 0x15000 RW  0x1000

이러한 각각의 영역은 /proc/self/maps 파일을 통해 볼 수 있는 각 영역과 정확히 일치한다.
여기서 5번째 column인 FileSiz는 dump된 크기를 나타내는 것인데
자세히 보면 1, 6, 9번째 segment의 값이 0으로 되어있는 것을 볼 수 있다.
즉 해당 영역은 dump되지 않은 것이다.

커널 빌드 시 설정 값 (CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS)에 따라
ELF 헤더 정보가 있는 각 공유 라이브러리들의 text (code) 영역은 dump 되지 않도록 설정해 둔 것이다.
2.6.23 이후의 커널은 /proc/self/coredump_filter 파일을 수정하여 이를 변경할 수 있다.
이 경우 부모 프로세스인 bash의 설정을 변경하는 것이 좋다.
($$ 변수는 (bash에 의해!) bash의 pid로 자동 치환된다.)

$ echo "0x33" > /proc/$$/coredump_filter

이제 다시 프로그램을 실행하여 core 파일을 재생성하고
readelf 명령으로 읽어보면 1, 6, 9번 영역의 FileSiz가 바뀐 것을 볼 수 있다.
(단 해당 영역을 모두 dump하는 것이 아니라 ELF 헤더가 있는 제일 처음 페이지만 dump하므로
FileSiz는 0x1000 (= 4096)으로 설정된다.
(이 영역들은 실행 파일 혹은 라이브러리의 text 영역을 그대로 매핑한 공유 영역이므로
ELF 헤더 정보만 있으면 언제든 다시 매핑시킬 수 있을 것이다.)

$ readelf -l core

Elf file type is CORE (Core file)
Entry point 0x0
There are 15 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  NOTE           0x000214 0x00000000 0x00000000 0x0022c 0x00000     0
  LOAD           0x001000 0x00110000 0x00000000 0x01000 0x13e000 R E 0x1000
  LOAD           0x002000 0x0024e000 0x00000000 0x02000 0x02000 R   0x1000
  LOAD           0x004000 0x00250000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x005000 0x00251000 0x00000000 0x03000 0x03000 RW  0x1000
  LOAD           0x008000 0x0042b000 0x00000000 0x01000 0x01000 R E 0x1000
  LOAD           0x009000 0x009ea000 0x00000000 0x01000 0x1b000 R E 0x1000
  LOAD           0x00a000 0x00a05000 0x00000000 0x01000 0x01000 R   0x1000
  LOAD           0x00b000 0x00a06000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x00c000 0x08048000 0x00000000 0x01000 0x01000 R E 0x1000
  LOAD           0x00d000 0x08049000 0x00000000 0x01000 0x01000 R   0x1000
  LOAD           0x00e000 0x0804a000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x00f000 0xb7896000 0x00000000 0x01000 0x01000 RW  0x1000
  LOAD           0x010000 0xb78af000 0x00000000 0x02000 0x02000 RW  0x1000
  LOAD           0x012000 0xbff59000 0x00000000 0x15000 0x15000 RW  0x1000

아래는 가장 기본적인 메모리 맵 구성을 살펴보기 위해
/proc/self/maps 파일을 읽어서 출력하도록 프로그램을 작성하여 실행한 결과이다.
(단 각 영역이 메모리에 매핑되는 위치는 random하게 결정되므로 매 실행 시 마다 달라지는데
여기서는 결과를 알아보기 쉽도록 위의 core 파일과 일치하게 수정해 두었다.)

$ ./a.out
00110000-0024e000 r-xp 00000000 08:01 2466800    /lib/tls/i686/cmov/libc-2.10.1.so
0024e000-00250000 r--p 0013e000 08:01 2466800    /lib/tls/i686/cmov/libc-2.10.1.so
00250000-00251000 rw-p 00140000 08:01 2466800    /lib/tls/i686/cmov/libc-2.10.1.so
00251000-00254000 rw-p 00000000 00:00 0
0042b000-0042c000 r-xp 00000000 00:00 0          [vdso]
009ea000-00a05000 r-xp 00000000 08:01 2450914    /lib/ld-2.10.1.so
00a05000-00a06000 r--p 0001a000 08:01 2450914    /lib/ld-2.10.1.so
00a06000-00a07000 rw-p 0001b000 08:01 2450914    /lib/ld-2.10.1.so
08048000-08049000 r-xp 00000000 08:01 2105354    /home/namhyung/temp/a.out
08049000-0804a000 r--p 00000000 08:01 2105354    /home/namhyung/temp/a.out
0804a000-0804b000 rw-p 00001000 08:01 2105354    /home/namhyung/temp/a.out
b7876000-b7877000 rw-p 00000000 00:00 0
b788f000-b7891000 rw-p 00000000 00:00 0
bff59000-bff6e000 rw-p 00000000 00:00 0          [stack]

위의 readelf 명령의 출력이 보여준 14개의 LOAD 세그먼트에 해당하는 영역이 무엇인지 볼 수 있다.
한 가지 이상한 것은 모든 영역이 private이라는 것이다. (3번째 column의 마지막 'p')
최소한 libc나 ld와 같은 공유! 라이브러리들의 코드는 (말 그대로) 공유될 터인데 왜 그럴까?

UTLK에 의하면 이는 성능 상의 이유로
쓰기 금지된 공유 영역을 비공개(private) 영역과 동일하게 처리하기 때문이란다.
(아직 정확하게 어느 부분이 영향을 받는지는 알아보지 못했다.. 일단은 그냥 넘어가자.)
메모리 매핑에 대한 오해가 있었다. 매핑이 private인지 shared인지는 단지 write하는 경우에만 영향이 있다.
read의 경우에는 차이가 없으며 private이든 shared이든 모두 동일한 내용을 읽을 수 있다.
private mapping은 write 시 별도의 페이지를 생성하여 해당 프로세스에서만 따로 관리하므로
다른 프로세스에게 변경 내역이 알려지지 않는다. 따라서 라이브러리의 데이터 영역은 private mapping을 이용해야 한다.
shared mapping의 경우 write 시 해당 파일의 내용을 직접 변경하므로 다른 프로세스들도 변경된 내용을 알 수 있다.

다시 maps 파일을 살펴보면 한 파일 당 3개의 영역으로 구성되는 것을 알 수 있다.
각각은 코드 (r-x), 읽기 전용 데이터 (r--), 데이터 (rw-) 영역에 해당하며
커널에서 제공하는 [vdso] 페이지와 프로세스의 [stack] 영역이 존재하고
(프로그램 내에서 동적 메모리 할당을 하지 않아서 [heap] 영역은 생성되지 않았다.)
그리고 나머지 3개의 이름없는(anonymous) 영역이 존재한다.
접근 권한을 봐서는 (rw-) 데이터 영역에 해당하는 것으로 판단할 수 있는데
(아직 정확히 모르지만) TLS 영역으로 사용되는 듯 하다.

프로세스 상태에 대한 나머지 정보는 가장 처음에 있는 NOTE 세그먼트에 존재하는데
글이 길어질 것 같으니 이에 대한 내용은 나중으로 미루기로 한다.

by namhyung | 2009/11/29 10:28 | System | 트랙백(3) | 덧글(6)
트랙백 주소 : http://studyfoss.egloos.com/tb/5183167
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Tracked from F/OSS study at 2009/11/29 19:05

제목 : core 파일의 구조 (2)
OS: ubuntu 9.10 Linux: 2.6.31 arch: x86 이전 글 보기: core 파일의 구조 (1) 앞서 말했듯이 coredump를 일으킨 시점의 프로세스 정보는 NOTE 영역에 저장된다. 다음 명령을 실행하여 이를 확인할 수 있다. $ readelf -n core Notes at offset 0x00000214 with length 0x0000022c: Owner &......more

Tracked from tomato_house at 2010/02/09 17:16

제목 : core 파일의 구조 (1)
core 파일의 구조 (1)...more

Tracked from at 2014/03/11 00:37

제목 : http://helenmccrory.org/
line6...more

Commented by onegun at 2010/04/21 17:39
왜 이런 멋진글에 댓글이 없을까요?
소중한 내용 공유해 주셔서 감사합니다.
Commented by namhyung at 2010/04/21 20:13
부족한 글을 좋게 봐 주셔서 감사합니다.
그다지 알려지지 않은 블로그라 다녀가시는 분들이 많지 않습니다... ;;
Commented by dhyi123 at 2010/07/22 22:43
고수가 여기 잉네... 좋은 정보 잘 읽고 갑니다. 늘 좋은 하루 되세요.
Commented by namhyung at 2010/07/23 12:05
감사합니다.. ^^
Commented by 달리나음 at 2011/01/12 13:57
cat 0x33 > /proc/$$/coredump_filter 이부분이 echo "0x33" > proc/$$/coredump_filter가 아닌가 싶습니다. :)
Commented by namhyung at 2011/01/12 18:12
그렇군요.. 지적 감사합니다. 수정해 두었습니다.

:         :

:

비공개 덧글

◀ 이전 페이지 다음 페이지 ▶

카테고리
General
Application
System
Kernel
Book
Tips
태그
linux emacs memory kernel x86 C documentation block-layer gcc glibc vcs scm computer-architecture awk script git build synchronization CAaQA3 elf bash CARM sed blktrace compiler patch algorithm binutils SMP perf
전체보기
이글루 파인더

최근 등록된 덧글
http://serbaserbiinfoterkini56..
by cakra at 09/22
informsi yang bagus dan b..
by cakra at 09/16
informsi yang bagus dan be..
by pordanaia at 08/05
최근 등록된 트랙백
Tod's Ferrari Homme
by Tods Pas Cher,Kodak did ..
Mocassin Femme
by Mocassins Homme, I got so..
natural garcinia cambogia
by
rss

skin by jiinny


X