안녕하세요.
리눅스 시스템 기반에서 시스템최적화와 프로파일링에 관심이 많은 소프트웨어 개발자입니다.
이번 글에서는 현업에서 개발 중 발생한 문제에 대해 공유하고 ELF 구조에서 설명했던 Weak / Strong Symbol에 대해 한번 더 정리하고자 합니다.
오래 전에 ELF에 대해 정리하던 글 중 Symbol에 관한 글을 작성한 적이 있습니다. ELF 내에서 Symbol이 가지는 의미와 Symbol Resolution에 대해서 쉽게 다룬 적이 있었습니다. Strong symbol와 Weak symbol에 대한 기본적인 개념을 이해하고 어떻게 서로 작용하여 동작하는지 이해하셨으면 이 글을 읽기 더욱 쉬울 것 같습니다.
지금부터는 제가 현업에서 개발하던 중 발생한 문제로 Symbol이 어떻게 동작하는지에 대해 이해하고 문제에 접근하여 해결한 경우입니다. 그 경험을 간략히 정리해보고자 합니다.
개발 환경
- 임베디드 시스템
- Open source JSON library (version 1.x.x)
- Json format의 데이터를 관리하는 라이브러리 개발을 요청받음 (특정 정보를 관리)
특정 기능을 수행하는 모듈 개발을 요청받다.
시스템의 정보를 알림 및 업데이트하는 모듈 개발을 요청받았습니다. 시스템의 정보는 Json 포맷으로 관리를 하며 모듈에 접근 및 업데이트를 위한 공유 라이브러리를 개발하게 되었습니다. 해당 API는 다양한 기능을 하고 있고, 내부에서는 json 파일을 다루는 기능을 구현하였습니다. TDD 기반으로 Testing code까지 모두 작성하면서 개발을 하였고, 시스템 정보를 다루기에 모든 코너케이스에 대해 테스트하려고 노력하였습니다.
라이브러리를 배포한 후 문제가 발생하다.
라이브러리 단독으로 테스트를 완료 후 데몬에서 제가 개발한 라이브러리를 링크하고 동작을 수행하게 되었습니다. 하지만 비정상적인 동작을 수행하며 프로세스가 비정상적으로 종료하는 문제가 발생하였습니다. 해당 팀에서는 분석이 쉽지 않고 저희 라이브러리를 링크하지 않으면 문제가 발생하지 않는다는 이유로 문제를 리포팅하였습니다.
코어덤프로 분석 시작하기.
일단 문제는 리포팅 받았는데 도저히 리포팅 문제 현상만 봐서는 이해가 되지 않았습니다. 문제 재현방법이 매우 쉬웠기에(라이브러리 링크하고 프로세스 실행만 시키면 문제 재현이 가능..) 먼저 문제 재현부터 시작하였습니다. 코어덤프에서 Crash 발생 포인트를 확인하니 제 라이브러리 내부에서 json library 동작을 수행 시 문제가 발생하였습니다. 문제가 발생한 함수를 json_xxx() 라고 하시죠. 코어덤프를 분석하면서 json_xxx() 해당 함수에 break point를 삽입하고 실행을 해보아도 정상적으로 해당 함수에서 프로시져가 정지하지 않는 상황이 발생하였습니다. 이상하더군요, 해당 함수 내부에서 어떠한 동작을 발생하는지 확인하고자 했는데 그것이 쉽지 않았습니다.
일단 해당 프로세스와 개발한 라이브러리, 그리고 라이브러리가 의존성을 가지는 모든 라이브러리의 디버그 정보를 포함하여 분석을 시작하였습니다. debug-source, debug-info를 모두 설치하여 더욱 분석이 쉽게 마무리 될 거라고 생각했으나.. json_xxx() 에 대해 동일하게 break point에서 정상적으로 프로세스가 멈추지 않았습니다. 또한 step으로 분석하여도, json_xxx() 함수만 만나면 내부로 진입하지 않고 지나쳐가는 상황이 발생하였습니다.
문제 해결을 위한 가설 세우기
다른 프로세스가 아닌 특정 프로세스에서만 라이브러리에 정의된 함수가 호출되지 않는다면 여러 가설을 세워볼 수 있습니다. 각 상황마다 다른 케이스가 존재하겠지만 링킹 타임에 다른 동작으로 Undefined으로 명시된 함수를 찾지 못하거나 다른 함수로 정의된 경우가 발생할 수 있습니다.
저는 다른 함수가 호출된다고 가정을 하였습니다. 만약 그렇다면 링킹 타임에 함수를 덮어쓰는 경우는 어떤 부분이 있을까요? 동일한 함수명이라면 분명 중복 정의로 인해 컴파일 에러가 발생했을텐데 에러가 발생하지 않고 정상적으로 빌드가 완료되었습니다. 그렇다면 오픈소스 라이브러리의 함수가 Weak symbol로 정의되어 있다면 이 가정이 성립할 수 있는 조건이 절반은 만족한다고 판단했습니다. 직접 코드를 확인하니 예상대로 weak symbol로 정의되어있었습니다. 그렇다면 동일한 함수명의 strong symbol이 존재한다면 오픈소스의 함수를 링킹하지 않고 strong symbol을 링크하게 되기에 해당 문제가 동일하게 발생할 수 있습니다.
가설이 성립함을 확인해보자.
하지만 해당 프로세스와 프로세스가 의존하는 모든 라이브러리의 Symbol 정보를 출력하여 분석하여도 해당 함수와 동일한 함수명은 찾을 수가 없었습니다. ldd로 의존성이 있는 라이브러리를 확인하던 도중 조금 특이한 라이브러리를 확인하였습니다. 해당 부서에서 자체적으로 개발한듯한 라이브러리였고, 코드를 직접 확인하게 되었습니다.
해당 라이브러리의 빌드 구조를 파악해보니 특정 정적 라이브러리 (.a) 아카이브를 참조하여 빌드하는 것을 발견하였습니다. 그리고 해당 아카이브를 해제하니 json library에 관한 object가 발견되었습니다. 히스토리를 문의하니 오픈소스 예전 버전을 빌드하여 정적 라이브러리 형태로 만들어 지속적으로 사용하고 있었습니다.
정리하기
버전 관리가 제대로 되지 않은채 오래된 버전의 오픈소스 코드를 정적 라이브러리 형태로 만들어 특정 라이브러리 내부에 포함해서 사용하는 것은 매우 위험합니다. 해결된 이슈에 대해서도 대응이 어려우며 플랫폼 상에서 관리, 유지보수하는 오픈소스의 버전과도 맞지 않는 상태가 발생합니다. 그리고 이 문제와 같이 정적 라이브러리 형태로 빌드하며 Strong symbol이 된 함수가 오픈소스 라이브러리의 Weak symbol 함수를 덮어씀으로서 의도치 않은 문제를 발생시킵니다. 해당 함수의 이름을 그대로지만 내부는 버전업이 될수록 변경될 확률이 있기 떄문입니다.
지금까지 Strong symbol과 Weak symbol의 이론을 이용하여 문제를 해결하기 위한 가설을 세우고 해결해 나간 방법을 정리하였습니다.
읽어주셔서 감사합니다.
'Linux > Profiling' 카테고리의 다른 글
[glibc] malloc, free mechanism (0) | 2021.02.04 |
---|---|
Process Profiling (0) | 2021.02.04 |
Performance Methodologies (0) | 2021.02.04 |
Memory Profiling (0) | 2021.02.04 |
[Profiling Tool] LTTng (0) | 2021.02.04 |