시험스포
ln
signal
until
컴파일 단계
gcc 다운로드
gcc --version
sudo apt install gcc


gcc 컴파일러
전처리 단계
전처리 지시문을 처리하는 단계
AI에서 전처리: 비정형 데이터를 정형화



nano hello.i 해보면 전처리 과정을 거쳐 자동으로 .i 파일이 작성된 것을 확인할 수 있다.
gcc -E hello.c -o hello.i ---> 전처리된 hello.i 파일 생성
컴파일 단계

전처리된 C 코드를 CPU가 이해할 수 있는 명령어에 더 가까운 어셈블리어로 바꾸는 단계
문법 검사, 중간코드 생성, 최적화, 어셈블리어 생성 수행
어셈블리어: CPU가 직접 실행할 수 있는 명령어(기계어)를 사람이 이해할 수 있도록 문자로 표현한 저수준 언어
어셈블 단계
어셈블리어 파일을 컴퓨터가 직접 실행가능한 기계어 명령어로 구성된 목적코드로 변환하는 단계
gcc -c hello.s -o hello.o

링크 단계
=링크 단계에서는 작성한 프로그램이 사용하는 다른 프로그램이나 라이브러리를 가져와서 연결한다.


대상파일을 지정하지 않으면 a.out으로 나오는데 실행 결과는 동일하다.
실행

각 단계에서 생성되는 중간파일들(.i, .s, .o)은 임시로 생성되며 자동으로 삭제된다.
gcc -save-temps hello.c -o hello --> 명령어를 하면 중간파일이 유지된다.

중간 파일이 유지되는 것을 확인할 수 있다.
실습 -2개 파일 작성



gcc main.c hello.c -o hello
- gcc 컴파일러로 main.c와 hello.c 둘 다 컴파일하고 링크하는 명령어
- -o hello는 최종 실행파일 이름을 hello로 만든다는 뜻
- 링크 과정에서 main()이 hello()를 찾을 수 있어야 하기 때문에, 둘을 같이 컴파일하고 링크해야함
gdb란
GNU Debugger의 줄임말
리눅스 환경에서 C, C++, 어셈블리어로 작성된 언어 디버깅하는 도구



2개 파일 생성 : gcc hello.c -o hello : 실행파일 hello가 생성되지만 디버깅 정보 포함 ㄴㄴ
gcc -g hello: 실행파일 a.out이 생성되며 디버깅 정보가 포함됨
gdb 실습






실습 - 코어덤프
p264
segmentation fault: 메모리에 접근하려고 할 때 허용되지 않은 메모리 접근



코어덤프 디버깅 순서

run: 프로그램을 GDB 안에서 실행. 죽는 지점에서 멈춤

bt: 죽은 이유를 확인할 수 있도록 backtrace(호출 스택) 출력
list: 죽은 부분 주변 코드를 보고 싶을때


gdb를 통해 16번째 줄이 문제인 것을 알 수 있음
*(volatile unsigned int*)(main+1) = 1;
- main은 함수 포인터 (즉, 함수가 시작하는 메모리 주소를 가리킴)
- main+1은 함수 주소에 1을 더한 것 → 말도 안 되는 메모리 주소
- (volatile unsigned int*)로 강제로 포인터 변환하고 그 주소에 1을 쓰기 하려고 함
- 프로그램의 코드 영역(text segment)에 쓰기를 시도한 거 Segmentation fault(세그멘테이션 오류) 발생!
int main()
{
puts("hello");
// *(volatile unsigned int*)(main+1) = 1; ← 삭제!
puts("world");
return 0;
}
수정하고 gcc -g demo_SIGSEGV.c -o demo_SIGSEGV 다시 컴파일하고 ./demo_SIGSEGV 실행하면 오류 안남
dwarf
debugging With Attributed Record Formats
GCC, Clang 등 컴파일러가 생성라는 디버깅 심볼 형식
-g 옵션을 쓰면 컴파일러는 이 DWARF 정보를 바이너리에 포함
gcc -g -gdwarf-2 buggy.c
objdump: 컴파일 결과물을 디버깅하거나 내부 구조를 들여다볼 때 사용
dwarf 실습


help
gdb안에서
(gdb) help status
(gdb) status
(gdb)help list
(gdb)list

스택


backtrace
Defensive Code
예상하지 못한 잘못된 입력이나 상황에서 프로그램이 안전하게 동작하도록 코드를 작성하는 기법

scores3? -30 입력했더니 코드에 있는 assert(n >= 0); 에 걸려서 SIGABRT (abort) 신호로 죽음
assert를 무시하고(=비활성화하고) 디버깅하기 위해 -DNDEBUG 사용
gcc -g -DNDEBUG buggy.c -o buggy
gdb ./buggy
(gdb) run

assert에 안 걸리고 통과하는 거 확인 가능
break point
프로그램이 실행 중 특정 위치에 도달하면 자동으로 '멈추게' 하는 기능



[Inferior 1 (process 41912) exited normally]
(gdb) b sum
Breakpoint 1 at 0x5555555551e2: file buggy.c, line 22.
(gdb) run
Starting program: /home/veda/20250428/buggy
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
input 5 scores
scores1? 3
scores2? 232
scores3? 3
scores4? 2
scores5? 1
Breakpoint 1, sum () at buggy.c:22
22 int sum=0 ; //TODO //=0
(gdb) run
The program being debugged has been started already.
Start it from the beginning? (y or n)
- (gdb) b sum → sum 함수 시작할 때 멈춰라
- (gdb) run → 프로그램 시작
- sum() 함수 도달 → 브레이크포인트 걸려서 멈춤
- 현재 멈춘 코드: int sum = 0; 초기화하는 줄
- 이제 next, step, continue 같은 걸 써서 계속 진행할 수 있음
문맥
현재 프로세스 또는 스레드가 실행되던 모든 정보의 스냅삿
툴 체인이란
소프트웨어 개발에 사용되는 프로그래밍 도구의 집합
종류: 네이티브, 크로스

cross 툴체인


QEMU 실습



QEMU는 가상 머신/에뮬레이터야
간단히 말하면, "다른 CPU를 흉내내서(에뮬레이션) 프로그램을 돌릴 수 있게 해주는 도구"
qemu가 할 수 있는 거
- 내 x86 PC에서 ARM 리눅스 실행하기
- 내 PC에서 MIPS, RISC-V, PowerPC 같은 다른 CPU용 코드 실행
- 임베디드 보드(라즈베리파이 등) OS 부팅 테스트
- 리눅스 커널 부팅 실험
- 크로스 컴파일한 프로그램 테스트 (ARM용 바이너리 실행 등)
원격 디버깅
디버깅 대상 프로그램이 로컬이 아닌 다른 시스템에서 실행 중일 때 로컬 개발 PC의 디버깅 도구를 통해 원격에서 디버깅을 수행하는 방식

sudo apt install gdbserver


왼쪽이 서버 오른쪽이 클라이언트

리눅스 라이브러리
| 정적 라이브러리 | Static Library | .a | 컴파일할 때 프로그램 안에 코드를 복사해서 넣음. 실행 파일이 커짐. 실행 시 별도 파일 필요 없음. |
| 공유 라이브러리 | Shared Library | .so | 프로그램 실행할 때 링크해서 사용하는 라이브러리. 실행 파일은 작고, 여러 프로그램이 동시에 공유 가능. |
| 표준 C 라이브러리 | glibc (libc.so) | .so | C언어의 표준 함수 제공 (printf, malloc, open 등). 리눅스 시스템 필수 라이브러리. |
| 수학 라이브러리 | libm (libm.so) | .so | sin, cos, sqrt 같은 수학 함수 제공. (-lm 옵션으로 링크) |
| POSIX 스레드 라이브러리 | libpthread (libpthread.so) | .so | 멀티스레드 프로그래밍 지원 (pthread_create, pthread_join 등). |
| 동적 로딩 라이브러리 | libdl (libdl.so) | .so | 프로그램 실행 중 동적으로 다른 라이브러리 로딩 (dlopen, dlsym 등). |
| 실행 지원 라이브러리 | ld-linux.so | .so | 프로그램 시작할 때 공유 라이브러리를 메모리에 로딩하고 연결하는 역할. (dynamic linker) |
| C++ 표준 라이브러리 | libstdc++ (libstdc++.so) | .so | C++의 표준 기능 (iostream, vector, string 등)을 제공. gcc에 포함. |
| 특수 목적 라이브러리 | (ex: SSL, Zlib 등) | .so, .a | 암호화(OpenSSL), 압축(Zlib), 그래픽(OpenGL) 같은 특정 기능 지원 라이브러리. |
소스코드
↓ (컴파일)
정적 라이브러리(.a) 포함
or
공유 라이브러리(.so) 링크
↓
실행 파일
공유 라이브러리 장점
| 1. 실행 파일 크기가 작아진다 | 라이브러리 코드를 복사하지 않고 연결만 하기 때문에, 실행파일 크기가 줄어든다. |
| 2. 메모리를 절약할 수 있다 | 여러 프로그램이 같은 라이브러리를 메모리에서 "공유"해서, 메모리 사용량이 크게 줄어든다. |
| 3. 유지보수가 쉽다 | 라이브러리만 업데이트하면, 그걸 사용하는 모든 프로그램이 자동으로 최신 버전을 쓸 수 있다. (프로그램 재컴파일 필요 없음) |
| 4. 프로그램 배포가 가벼워진다 | 프로그램 자체에는 핵심 로직만 포함하고, 시스템에 설치된 공유 라이브러리를 이용할 수 있어서 배포 크기가 줄어든다. |
| 5. 런타임 기능 확장이 가능하다 | dlopen() 같은 함수를 이용하면 실행 중에 동적으로 라이브러리를 로딩할 수 있다. (플러그인 시스템 구현 가능) |
정적 라이브러리 작성하기

ar은 목적파일들을 병합하여 라이브러리를 만들 때 사용하는 아카이브 유틸리티
.a라는 정적 라이브러리 파일을 생성
r 옵션은 아카이브에 파일을 추가하거나 교체
libmylib.a라는 이름으로 정적 라이브러리를 만들고, 그 안에 mylib.o를 포함

-o output: 생성될 실행파일 이름을 지정
-Lsearchdir 라이브러리를 찾을 디렉터리 경로를 추가
-lnamespec : libnamespec.a또는 libnamespec.so를 사용한다는 의미
lib 접두사와 .a또는 .so 확장자는 생략



경로 설정완
동적 라이브러리 사용하기

'임베디드' 카테고리의 다른 글
| 커널1 (0) | 2025.05.26 |
|---|---|
| 임베디드 엔지니어 교과서 1-3정리 (0) | 2025.05.01 |
| 라즈베리파이 (0) | 2025.04.17 |