저번 글에서는 qemu와 gdb를 통해 발생한 원인을 추적하는 방법에 대해 배웠습니다. 하지만 정확히 어느 부분이 문제인지 확인하는 것은 수고스러운 일이였습니다. 따라서 손쉬운 디버깅을 위해 WAIOS만의 dump_stack 함수를 구현해보도록 하겠습니다. 우선 구현 결과물은 아래와 같습니다.
위 정보를 통해 b 함수에서 dump_stack 함수가 호출된 것을 확인할 수 있습니다. 실제 문제가 발생한 상황이라면 b 함수를 조사함으로써 조금 더 편리한 디버깅이 될 것입니다.
이제 본격적으로 dump_stack 함수 구현을 해보도록 하겠습니다. 구현은 핵심 컨셉은 Linux의 kallsyms에서 가져왔습니다. 따라서 kallsyms에 대해 알고 있다면 글을 읽는데에 많은 도움이 될 것입니다. kallsysm는 다른 글을 통해 소개한 적이 있으므로 관심이 있으신 분은 아래의 링크를 참고하시면 되겠습니다.
implementation
Linux kallsyms와 동일하게 앞으로 구현할 kallsysms는 3가지 파트로 구성되어 있습니다.
scripts/kallsyms.c: vmlinux에서 심볼 정보를 추출하고, 소스 코드로 변환하는 유틸리티
kallsyms.c: 커널이 추출한 심볼 정보 자료 구조에 접근을 제공하는 함수 구현
build.sh: 빌드할 때, kallsyms 정보들을 vmlinux에 포함하도록 별도의 처리 구현
그렇다면 각각의 구현의 목표를 구체화 해보도록 하겠습니다.
1. scripts/kallsyms.c
Linux의 scripts/kallsyms.c는 nm 유틸리티의 출력을 파싱합니다. nm 유틸리티는 오브젝트 파일에 사용되는 심볼 정보들을 테이블 형태로 출력합니다. nm 유틸리티에 WAIOS의 vmlinux를 입력으로 넣으면 다음과 같이 출력이 됩니다.
위와 같이 출력된 심볼 정보를 파싱하는 함수를 구현해야 합니다. 이를 위해서는 파싱한 정보를 저장하기 위한 자료 구조도 구성해야 합니다. 추가적으로 관심이 있는 심볼(함수의 심볼)만 가져오도록 필터링도 필요합니다. 정리를 하면 구현의 세부 목표는 다음과 같습니다.
심볼 정보를 저장할 자료 구조 선언
함수의 심볼인지 확인
파싱한 데이터를 자료 구조에 저장
제가 구현한 파싱 부분은 다음과 같습니다. Linux의 코드를 단순하게 만든 것이라 어색한 부분이 조금 있습니다.
나중에 빠른 검색을 위해 파싱한 다음 심볼 자료 구조들을 정렬할 필요가 있습니다. 정렬한 다음, 심볼 정보를 출력하면 됩니다. 제가 구현한 코드는 아래와 같습니다.
표준 라이브러리에 qsort를 이용하여 table을 정렬하였습니다. 또한 C 스타일로 심볼 정보들을 출력하였습니다. 해당 유틸리티를 실행하면, 아래와 같은 C 소스 코드가 출력됩니다.
2. kallsyms.c
이제 위처럼 구성된 자료 구조에 접근하는 함수들을 구현하겠습니다. 구현해야 할 함수는 주소를 받고 그 주소에 대응하는 문자열을 전달하는 addr_to_symbol 입니다. 실제로 Linux에서 하는 것처럼 이진 탐색으로 주소에 대응하는 심볼을 찾도록 구현하겠습니다.
제가 구현한 코드는 아래와 같습니다.
<kallsyms.h>
<kallsyms.c>
이제 addr_to_symbol을 이용해서 dump_stack 함수를 구현해보도록 하겠습니다. 해당 함수는 stack frame을 타고 올라가며 리턴 주소와 그것에 대응하는 심볼도 동시에 출력하면 됩니다. 설명과 같이 실제 구현도 단순하게 되었습니다.
<dump_stack.h>
<dump_stack.c>
3. build.sh
이제 kallsyms가 잘 동작하도록 빌드 스크립트를 수정하면 됩니다. Linux의 link-vmlinux.sh와 같이 임시 vmlinux를 생성하고 최종 vmlinux를 생성하는 2단계 빌드를 사용하겠습니다. 덕분에 꽤나 지저분하게 변하였습니다. 따라서 훗날 Makefile과 같은 방식으로 변경하도록 하겠습니다.
Test
이제 구성한 dump_stack 함수가 잘 동작하는지 살펴보도록 하겠습니다. 단순하게 main 함수에 몇 가지 dummy 함수를 추가하고 dump_stack 함수를 호출해보도록 하겠습니다. 변경한 main.c는 아래와 같습니다.
WAIOS는 start_kernel, a, b 함수들을 각각 호출하고 dump_stack 함수를 호출합니다. 그 결과물은 글 처음에 본 사진과 동일합니다.
Conclusion
해당 글을 통해 Linux kallsyms와 비슷한 스타일의 WAIOS kallsyms를 구현해보았습니다. 또한 이를 기반으로 단순한 dump_stack 함수를 구현해보았습니다. 지금 당장 dump_stack 함수만으로는 할 것이 별로 없지만, 앞으로 예외 핸들러를 다룰 때 dump_stack도 사용하면 굉장히 유용할 것입니다. 제가 구현한 코드의 원본은 아래의 링크를 달아두었습니다.
Comments