개발 환경 구성
본 문서에서는 ARM64(64비트 ARM) 보드에서 동작하는 메모리 분석 도구인 Valgrind를 x86_64 호스트에서 크로스 컴파일하는 방법을 설명한다.
빌드 환경
- 호스트 PC: Ubuntu Server 22 LTS
- 타겟 보드: ARM64(AArch64) 아키텍처
크로스 컴파일 도구체인
Linaro에서 제공하는 GCC 크로스 컴파일러를 사용한다:
https://releases.linaro.org/components/toolchain/binaries/7.5-2019.12/aarch64-linux-gnu/
참고사항: GCC 7.5 이상을 사용하려면 ARM 공식 사이트(https://developer.arm.com/downloads/-/gnu-a)에서 도구체인을 다운로드해야 한다.
도구체인을 선택할 때 다음 사항을 확인해야 한다:
- GCC 버전이 타겟 보드의 glibc 버전과 호환되어야 함
- 아키텍처(aarch64-linux-gnu)가 타겟 운영체제 및 SOC 코어와 일치해야 함
소스코드 준비
Valgrind 소스코드는 공식 웹사이트에서 다운로드한다:
https://valgrind.org/downloads/current.html
빌드 전에 필요한 의존 패키지를 설치한다:
sudo apt install automake
sudo apt install libc6-dbg
컴파일 스크립트 작성
Valgrind 3.21.0 소스코드의 README.aarch64 파일에 상세한 빌드 안내가 포함되어 있다. 이를 참고하여 스크립트를 작성한다.
#!/bin/sh
export CROSS_PREFIX=/your_path_to_linaro/bin/aarch64-linux-gnu-
export CC=${CROSS_PREFIX}gcc
export LD=${CROSS_PREFIX}ld
export AR=${CROSS_PREFIX}ar
./autogen.sh
./configure --prefix=`pwd`/Install --host=aarch64-unknown-linux \
--enable-only64bit
make -j4
make -j4 install
tar -czf ../valgrind-3.21.0-aarch64.tar.gz Install/
echo "빌드 완료: ../valgrind-3.21.0-aarch64.tar.gz"
위 스크립트에서 CROSS_PREFIX를 실제 크로스 컴파일 도구체인 경로로 수정해야 한다. 즉, aarch64-linux-gnu-gcc가 위치한 디렉토리의 경로를 지정하면 된다.
빌드가 완료되면 상위 디렉토리에 valgrind-3.21.0-aarch64.tar.gz 파일이 생성된다.
타겟 보드への部署
빌드된 패키지를 ARM64 보드로 전송하고 압축을 해제한다:
tar -xvf valgrind-3.21.0-aarch64.tar.gz
chmod -R +x Install/ && cp Install/* / -rfv
export VALGRIND_LIB=/libexec/valgrind/
메모리 분석 기능을 완전히 활용하려면 디버그 정보가 포함된 동적 링크러(libc-debug 버전)를 보드에 복사해야 한다. 이 과정은 아래의 오류 해결 섹션에서 다룬다.
使用법
기본 실행 명령어
valgrind --tool=memcheck --leak-check=full <분석할 프로그램> [인수]
valgrind --tool=memcheck --leak-check=full ./myapp
분석 대상 프로그램이 디버그 정보(-g 옵션)로 컴파일되어 있다면, 메모리 누수 및 오류를 상세히 확인할 수 있다.
常见 오류 및 해결 방법
1. memcheck 도구 로드 실패
오류 메시지:
valgrind: failed to start tool 'memcheck' for platform 'arm64-linux': No such file or directory
해결 방법: VALGRIND_LIB 환경변수를 설정한다.
export VALGRIND_LIB=/libexec/valgrind/
~/.bashrc에 위 줄을 추가하면 영구적으로 적용된다.
2. 함수 리다이렉션 설정 오류
이 오류는 타겟 보드에 디버그 정보가 없는 동적 링크러가 있을 때 발생한다. 프로그램이 완전히 디버그 정보로 컴파일되지 않았거나, 디버그 심볼이 포함된 libc가 보드에 없으면 이 문제가 발생한다.
오류 메시지:
root@plnx:/tmp# export VALGRIND_LIB=/libexec/valgrind/ && valgrind --tool=memcheck --leak-check=full /run/my_exec
==7643== Memcheck, a memory error detector
==7643== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==7643== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==7643== Command: /run/my_exec
==7643==
valgrind: Fatal error at startup: a function redirection
valgrind: which is mandatory for this platform-tool combination
valgrind: cannot be set up. Details of the redirection are:
valgrind:
valgrind: A must-be-redirected function
valgrind: whose name matches the pattern: strlen
valgrind: in an object with soname matching: ld-linux-aarch64.so.1
valgrind: was not found whilst processing
valgrind: symbols from the object with soname: ld-linux-aarch64.so.1
valgrind:
valgrind: Possible fixes: (1, short term): install glibc's debuginfo
valgrind: package on this machine. (2, longer term): ask the packagers
valgrind: for your Linux distribution to please in future ship a non-
valgrind: stripped ld.so (or whatever the dynamic linker .so is called)
valgrind: that exports the above-named function using the standard
valgrind: calling conventions for this platform. The package you need
valgrind: to install for fix (1) is called
valgrind:
valgrind: On Debian, Ubuntu: libc6-dbg
valgrind: On SuSE, openSuSE, Fedora, RHEL: glibc-debuginfo
valgrind:
valgrind: Note that if you are debugging a 32 bit process on a
valgrind: 64 bit system, you will need a corresponding 32 bit debuginfo
valgrind: package (e.g. libc6-dbg:i386).
valgrind:
valgrind: Cannot continue -- exiting now. Sorry.
해결 절차
먼저 호스트 PC의 크로스 컴파일 도구체인에서 디버그 심볼이 포함된 동적 링크러를 찾는다:
$ find ./aarch64-linux-gnu -name "*ld*.so"
./aarch64-linux-gnu/libc/lib/ld-2.25.so
$ file ./aarch64-linux-gnu/libc/lib/ld-2.25.so
./aarch64-linux-gnu/libc/lib/ld-2.25.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=73b23c16ff9fe3eab046636cd9dd6db9d3309f27, with debug_info, not stripped
검색된 ld-2.25.so 파일을 타겟 보드로 전송하고 실행 권한을 부여한 후, Valgrind와 함께 사용한다:
chmod +x /tmp/ld-2.25.so
export VALGRIND_LIB=/libexec/valgrind/ && valgrind --tool=memcheck --leak-check=full /tmp/ld-2.25.so /run/my_exec
실행 결과 예시
root@plnx:/tmp# export VALGRIND_LIB=/libexec/valgrind && valgrind --tool=memcheck --leak-check=full /tmp/ld-2.25.so /run/my_exec
==27913== Memcheck, a memory error detector
==27913== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
==27913== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==27913== Command: /tmp/ld-2.25.so /run/my_exec
==27913==
Hello World!
***************************************************************************
==27913==
==27913== HEAP SUMMARY:
==27913== in use at exit: 0 bytes in 0 blocks
==27913== total heap usage: 33 allocs, 33 frees, 90,118 bytes allocated
==27913==
==27913== All heap blocks were freed -- no leaks are possible
==27913==
==27913== For lists of detected and suppressed errors, rerun with: -s
==27913== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
위 결과에서 "All heap blocks were freed -- no leaks are possible"를 확인함으로써 메모리 누수가 없음을 검증할 수 있다.
편의 스크립트 작성
길어진 명령어를 간단하게 만들기 위해 래퍼 스크립트를 만든다:
#!/bin/sh
# tar -xvf valgrind-3.21.0-aarch64.tar.gz && chmod -R +x Install/ && cp Install/* / -rfv
# chmod +x /tmp/ld-*.so
export VALGRIND_LIB=/libexec/valgrind && valgrind --tool=memcheck --leak-check=full /tmp/ld-2.25.so $@
이 스크립트를 run_memcheck.sh로 저장하고 실행 권한을 부여한다.
사용 예시
# 일반 실행
./myapp 1 0
# 메모리 분석 실행
./run_memcheck.sh ./myapp 1 0
디버그 심볼이 포함된 동적 링크러를 /tmp 디렉토리에 두는 이유는 디버깅 시에만 임시로 사용하기 때문이다. 제품 환경에서는 디버그 정보가 포함된 바이너리를 사용하지 않는 것이 일반적이다.
부록: 특수 변수 설명
$@ 와 $* 의 차이점:
$*: 모든 인수를 하나의 문자열로 결합 ("$1 $2 ... $n")$@: 각 인수를 개별적인 문자열로 유지 ("$1" "$2" ... "$n")