RP2350 · ELF · Ghidra · SWD · GDB

Pico 2: 소스·ELF·실행 결과를 맞춰 보는 디버깅

디컴파일 화면, 디버거의 값, 실제 핀 동작은 서로 다른 증거입니다. 같은 빌드와 같은 실행 시점을 기준으로 연결해야 원인을 좁힐 수 있습니다.

소스와 보드가 같은 빌드를 가리키는지 확인합니다

Kevin Thomas의 『Embedded Hacking』 초판 1.0001은 Raspberry Pi Pico 2를 대상으로 C 예제와 디버깅을 연결하는 교재입니다. 217쪽, 40개 장으로 구성된 이 판본에서는 예제 코드를 읽고, 빌드 결과를 살펴보고, 개발 보드에서 동작을 바꿔 비교합니다. 여기서는 교재의 코드를 옮기기보다 각 단계에서 무엇을 대조해야 하는지 정리합니다.

먼저 보드에 올린 펌웨어와 분석할 ELF가 같은 빌드인지 맞춥니다. 소스가 같아도 SDK, 컴파일 옵션, 링크 배치가 달라지면 함수 주소와 명령이 달라질 수 있습니다. 소스 커밋, SDK 버전, 보드 설정, 컴파일 옵션, ELF와 실제 업로드 파일의 해시를 함께 기록합니다. 파일 형식이 다른 ELF와 UF2의 해시가 서로 같아야 한다는 뜻은 아닙니다. 같은 빌드에서 어떤 파일이 만들어지고 어떤 파일을 업로드했는지를 추적하는 기록입니다.

같은 소스와 빌드를 기준으로 ELF는 Ghidra에서 정적으로 읽고, 보드는 SWD와 GDB로 관찰한 뒤 UART 로그와 실제 GPIO 출력을 비교하는 흐름
ELF 분석과 보드 관찰을 같은 빌드에 연결합니다. UART 출력이 예상과 맞아도 실제 GPIO가 같은 상태인지는 별도로 확인해야 합니다. DUJINLABS 자체 작성 도식입니다.

실행 주소를 소스 줄로 바꾸는 일반적인 절차는 주소와 소스 위치 맞추기에서 이어 볼 수 있습니다. 교재에 나온 특정 주소를 다른 펌웨어에 그대로 넣는 것은 이 확인을 대신하지 못합니다.

디컴파일된 C는 원래 소스의 복사본이 아닙니다

교재 11~16쪽은 Ghidra에 ELF를 불러와 어셈블리와 디컴파일 결과를 비교합니다. 16쪽에서는 원래의 while 형태와 다른 루프 표현이 나타나고, 간단한 출력의 printf 호출이 빌드 결과에서는 puts로 바뀐 예를 설명합니다. 이 한 예가 모든 빌드에서 같은 최적화가 일어난다는 뜻은 아닙니다.

함수 이름이나 의사 코드 한 줄만으로 동작을 확정하지 않습니다. 해당 명령의 입력 레지스터, 분기 조건, 참조 데이터와 호출 대상을 확인하고, 필요한 가설을 실행 중 관찰로 검증합니다. 디버그 정보와 심볼 유무도 함께 기록합니다. ELF 형식이라는 사실만으로 모든 소스 이름과 줄 정보가 들어 있다고 볼 수는 없습니다.

디버거의 값과 실제 출력을 함께 확인합니다

Raspberry Pi Debug Probe는 USB와 SWD를 연결하는 기능, UART 통신을 연결하는 기능을 제공합니다. SWD와 OpenOCD·GDB로 실행을 멈추고 레지스터·메모리를 살펴보는 일과, UART에서 출력 문자열을 읽는 일은 구분해야 합니다. 교재 16·26~29쪽의 예는 디버그가 가능한 Pico 2에서 이 두 관찰을 연결합니다.

관찰 대상확인할 수 있는 것추가로 필요한 확인
ELF·어셈블리해당 빌드의 명령, 심볼, 배치보드가 실제로 같은 빌드를 실행하는지
GDB의 레지스터·메모리멈춘 시점의 값과 실행 위치중단·단계 실행이 타이밍에 준 영향
UART 로그프로그램이 출력한 문자열그 문장이 현재 핀 동작과 일치하는지
LED·핀 측정관찰한 출력과 변화 시점측정한 핀, 입력 조건, 펌웨어와의 대응

교재 215~217쪽에서는 예제의 LED 동작을 바꿔도 직렬 로그가 이전 GPIO 설명을 계속 출력하는 사례를 보여 줍니다. 이는 로그 문구와 실제 동작을 따로 확인해야 한다는 구체적인 예입니다. 이 교재의 시연을 다른 제품에서도 같은 변경이 가능하다는 증거로 확대해서는 안 됩니다.

문제를 재현할 때에는 입력, 중단점, 관찰 시점, UART 출력, 외부 측정을 함께 남깁니다. 프로그램을 멈춘 상태에서 읽은 값과 정상 속도로 실행할 때의 동작을 구분합니다. 이 글을 작성하며 보드에 펌웨어를 올리거나 교재의 실습을 실행해 재현한 것은 아닙니다.

부팅 주소와 초기화 코드는 사용한 빌드에서 찾습니다

교재의 부팅 설명은 boot ROM, XIP, 벡터 테이블과 C 런타임 초기화를 다룹니다. 예제의 역어셈블 주소를 RP2350의 모든 프로그램에 적용하지 않습니다. 사용한 SDK의 시작 코드와 링커 스크립트, ELF의 실제 배치를 먼저 확인합니다. 칩의 기능과 예제 프로그램의 배치를 구별할 때에는 RP2350 공식 데이터시트와 공식 boot ROM 소스가 기준 자료입니다.

열린 디버그 포트에서의 실습은 보안 잠금 우회가 아닙니다

RP2350 데이터시트 §3.5.9는 기본 SWD 접근과 OTP 설정을 통한 디버그 접근 제한을 구분합니다. 전체 디버그를 제한하는 설정과 Secure 상태의 디버그를 제한하는 설정도 다릅니다. 설정과 소프트웨어 정책에 따라 허용되는 접근이 달라지므로, 개발 보드에서 값을 바꿨다는 사실만으로 잠긴 제품에서도 같은 접근이 가능하다고 결론 내릴 수 없습니다.

보안 부트는 부팅할 이미지의 검증 문제이고, 디버그 포트 접근은 실행 중 상태를 관찰하거나 바꾸는 권한 문제입니다. 제품의 보안을 논하려면 실제 OTP 설정, 디버그 허용 상태와 부팅 정책을 따로 확인해야 합니다. 교재의 공개 예제 실습은 보안 부트나 디버그 잠금을 우회했다는 검증 보고서가 아닙니다.

자료와 검토 범위

이 글은 Héctor Joaquín의 소개 게시물과 저자 저장소의 자료를 계기로 작성했습니다. 대조한 참고본은 Kevin Thomas, 『Embedded Hacking』, First Edition 1.0001, © 2025, 217쪽입니다. 핵심 예제의 해당 쪽과 공식 디버그 문서를 대조했으며, 모든 실습의 실행 결과나 모든 주소·코드를 독립 검증한 것은 아닙니다. 2026년 10월 18일 확보한 LinkedIn 첨부와 저자 저장소의 참고 PDF는 파일 크기와 SHA-256이 일치함을 확인했습니다. 이후 저장소에서 바뀐 파일까지 같다는 뜻은 아닙니다.

교재의 PDF·그림·코드는 이 사이트에 복제하지 않았습니다. 본문 도식은 증거의 관계를 설명하기 위해 새로 작성했습니다.