본문 바로가기

[Debugging] Tips

[리눅스][디버깅] GDB로 깨진 콜 스택 복원하기(공유 라이브러리 로딩하는 방법)

리눅스 프로젝트를 개발하면 코어덤프(coredump)를 열어서 크래시가 발생한 원인을 분석할 때가 많습니다.
많은 개발자 분들이 코어덤프를 열어서 크래시가 발생한 원인을 분석하죠.
 
코어덤프를 열어 gdb를 사용해 디버깅할 때 가장 짜증나는 것 중 하나는 공유 라이브러리를 제대로 로딩하지 못해
콜 스택이 보이지 않을 때 입니다.
 
이번에는 코어덤프를 로딩할 때 필요한 정보 중 하나인 공유 라이브러리의 정보(패스/이름)을 확인하는 방법을 소개합니다.
 
깨진 콜 스택 확인하기
 
먼저 콜 스택을 보겠습니다.
 
(gdb) bt
#0  0x0000007f7def1808 in __glibc_raise (sig=sig@entry=6) at /usr/glibc/raise.c:1354
#1  0x0000007f7def2d80 in __glibc_abort () at /usr/glibc/abort.c:489
#2  0x0000007f7deeadbc in __assert_fail_base()     at /usr/glibc/assert.c:912
#3  0x0000007f7deeae6c in __glibc___assert_fail () at  /usr/glibc/assert.c:1014
#4  0x0000007f7e4f13ec in power_railway_delay () at /usr/src/powerrail/powerrail.c:9489
#5  0x0000007f7efa5af4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
 
위 메시지를 보면 #5 줄에서 콜 스택이 깨진 걸 볼 수 있습니다.
 
#5  0x0000007f7efa5af4 in ?? ()
 
0x0000007f7efa5af4 주소가 뭔지 모르겠다는 소리죠.
 
공유 라이브러리 이름 확인하기
 
이번에는 'info share' 명령어를 입력해 공용 라이브러리의 경로를 확인합시다.
 
(gdb) info share
From                To                  Syms Read   Shared Object Library
                                        No          /usr/lib/libPowerRailwayManagement.so
0x0000007f7ee1cf30  0x0000007f7eec9c9c  Yes          /usr/lib/librsi.so.0.1
 
가장 첫 번째 줄을 보면 libPowerRailwayManagement.so 라이브러리가 제대로 로딩이 안 됐다고 확인됩니다.
 
No          /usr/lib/libPowerRailwayManagement.so
 
 
공유 라이브러리 오프셋 확인하기
 
리눅스 터미널을 하나 더 열고, './readelf -n coredump' 명령어를 입력해 코어덤프에 있는 NT_FILE 노트를 확인합시다. 참고로 NT_FILE 노트에는 코어덤프에서 공유 라이브러리 정보가 포함돼 있습니다.
 
baldcandy.kim# ./readelf -n coredump 
 
Displaying notes found at file offset 0x00005168 with length 0x00010c58:
  Owner                 Data size       Description
  CORE                 0x00000188       NT_PRSTATUS (prstatus structure)
  CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
  CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
  CORE                 0x00000130       NT_AUXV (auxiliary vector)
  CORE                 0x00002b9d       NT_FILE (mapped files)
    Page size: 4096
                 Start                 End         Page Offset
...
    0x0000007f7ef21000  0x0000007f7f18a000  0x0000000000000000
        /usr/lib/libPowerRailwayManagement.so
    0x0000007f7f18a000  0x0000007f7f19a000  0x0000000000000269
        /usr/lib/libPowerRailwayManagement.so
    0x0000007f7f19a000  0x0000007f7f1a3000  0x0000000000000269
        /usr/lib/libPowerRailwayManagement.so
    0x0000007f7f1a3000  0x0000007f7f1a5000  0x0000000000000272
        /usr/lib/libPowerRailwayManagement.so
 
libPowerRailwayManagement.so 라이브러리의 실행 오프셋은 0x0000007f7ef21000임을 알 수 있습니다.
 
이번에는 libPowerRailwayManagement.so 라이브러리 파일을 찾아서 헤더 정보를 확인합시다.
이를 위해 'readelf -S libPowerRailwayManagement.so' 명령어를 입력해야 합니다.
 
baldcandy.kim# readelf -S libPowerRailwayManagement.so
There are 27 section headers, starting at offset 0x273728:
 
Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.gnu.build-i NOTE             00000000000001c8  000001c8
       0000000000000024  0000000000000000   A       0     0     4
...
  [10] .plt              PROGBITS         0000000000074b60  00074b60
       000000000000a110  0000000000000010  AX       0     0     16
  [11] .text             PROGBITS         000000000007ec70  0007ec70
       0000000000160ebc  0000000000000000  AX       0     0     16
 
'[11]' 번째 줄을 보면 .text 섹션 헤더의 오프셋은 0x0007ec70으로 확인됩니다.
 
결국 libPowerRailwayManagement.so 라이브러리의 오프셋은 다음 계산식으로 0x7F7EF9FC70입니다.
 
0x7F7EF9FC70 = 0x0000007f7ef21000 + 0x0007ec70
 
공유 라이브러리 로딩하기
 
다시 GDB 프로그램 화면으로 되돌아와, 다음 명령어를 입력해 라이브러리 패스를 추가합시다.
 
'add-symbol-file ./libPowerRailwayManagement.so 0x7F7EF9FC70'
 
(gdb) add-symbol-file ./libPowerRailwayManagement.so 0x7F7EF9FC70
add symbol table from file "./libPowerRailwayManagement.so" at
        .text_addr = 0x7f7ef9fc70
(y or n) y
 
명령어를 입력하면 '(y or n)' 텍스트가 출력되면서 .text_addr를 0x7f7ef9fc70로 지정하도 되는지 묻습니다.
바로 'y'를 선택합니다. 그러면 다음과 같은 화면이 보일 것입니다.
 
(y or n) y
Reading symbols from ./libPowerRailwayManagement.so...(no debugging symbols found)...done.
 
공유 라이브러리를 로딩한 다음 콜 스택을 확인하니 깨진 콜 스택이 복구해 볼 수 있습니다.
 
(gdb) bt
#0  0x0000007f7def1808 in __glibc_raise (sig=sig@entry=6) at /usr/glibc/raise.c:1354
#1  0x0000007f7def2d80 in __glibc_abort () at /usr/glibc/abort.c:489
#2  0x0000007f7deeadbc in __assert_fail_base()     at /usr/glibc/assert.c:912
#3  0x0000007f7deeae6c in __glibc___assert_fail () at  /usr/glibc/assert.c:1014
#4  0x0000007f7e4f13ec in power_railway_delay () at /usr/src/powerrail/powerrail.c:9489
#5  0x0000007f7efa5af4 in PowerManager::set_powerrail_delay() 
#6  0x0000007f7efaf774 in PowerManager::set_powerrail_run() 
#7  0x0000007f7efb17e8 in PowerManager::powerrail_set_railway() 
#8  0x0000007f7efb3018 in PowerManager::powerrail_init(void*) ()
#9  0x0000007f7efb3c60 in ?? ()
#10 0x0000007f7e26b020 in start_thread (arg=0x147f7e28f010 <__pthread_keys+15752>) at /usr/src/pthread_create.c:11335
#11 0x0000007f7df88970 in thread_start () at ../sysdeps/unix/sysv/linux/aarch64/clone.S:89
 
아래는 공유 라이브러리를 로딩하기 전의 깨진 콜 스택 정보입니다.
 
(gdb) bt
#0  0x0000007f7def1808 in __glibc_raise (sig=sig@entry=6) at /usr/glibc/raise.c:1354
#1  0x0000007f7def2d80 in __glibc_abort () at /usr/glibc/abort.c:489
#2  0x0000007f7deeadbc in __assert_fail_base()     at /usr/glibc/assert.c:912
#3  0x0000007f7deeae6c in __glibc___assert_fail () at  /usr/glibc/assert.c:1014
#4  0x0000007f7e4f13ec in power_railway_delay () at /usr/src/powerrail/powerrail.c:9489
#5  0x0000007f7efa5af4 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
 
 
---
"이 포스팅이 유익하다고 생각되시면 공감 혹은 댓글로 응원해주시면 감사하겠습니다. 
"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!"
 
​Thanks,
Guillermo Austin Kim(austindh.kim@gmail.com)
---