본문 바로가기
임베디드 개발/임베디드 리눅스

Linux ] sysroot란?

by eteo 2026. 1. 10.
반응형

 

 

임베디드 리눅스 개발을 하다보면 호스트 PC에서 타겟 보드용 애플리케이션을 개발해야할 경우가 많이 있다.

 

이 때 제대로 된 크로스컴파일 환경을 구성하려면, 크로스 컴파일러 말고도 꼭 있어야하는 것이 sysroot다. 이번 글에서는 sysroot의 개념과 gcc의 --sysroot 옵션 사용 방법에 대해 정리하고자 한다.

 

 

 

gcc는 기본적으로 어떤 파일들을 어디에서 찾을까?

네이티브 컴파일 환경에서 gcc는 다음을 암묵적으로 전제한다.

 

  • 헤더 검색 위치
/usr/include
/usr/include/<arch>

 

  • 라이브러리 검색 위치
/lib
/usr/lib
/lib/<arch>
/usr/lib/<arch>

 

즉, gcc는 별도의 옵션이 지정되지 않은 경우, 이 코드는 지금 실행 중인 시스템에서 동작할 프로그램이라고 가정하고 현재 시스템의 루트(/) 아래에서 헤더와 라이브러리를 탐색한다.

 

예를 들어, 호스트 PC(x86-64)에서 타겟 보드(aarch64)를 대상으로 컴파일한다고 가정해보자. 이 경우, 헤더는 타겟용이어야하고 라이브러리와 glibc(GNU C 표준 라이브러리 구현체)도 타겟 ABI에 맞아야한다.

 

따라서 단순히 크로스 컴파일러(prefix가 붙은 gcc)를 사용하는 것만으로는 충분하지 않고, gcc가 파일을 탐색하는 기준 루트 자체를 타겟 용으로 바꿔줘야 한다.

 

이 역할을 하는 옵션이 --sysroot다.

 

 

 

 

 

 

 

 

gcc --sysroot=<path> 옵션의 의미

gcc의 --sysroot=<path> 옵션은 컴파일러가 헤더, 라이브러리, CRT(C Runtime) Object들을 탐색할 때 루트(/)로 간주하는 기준 경로를 변경하는 옵션이다. 보통 크로스 컴파일용 툴체인 안에 sysroot가 같이 배포되기 때문에 이 경로를 지정해주면 된다.

# Makefile 예시
CROSS   = aarch64-linux-gnu-
CC      = $(CROSS)gcc
SYSROOT = /<toolchain>/sysroot

CFLAGS  = -Wall -O2
LDFLAGS =
LIBS    = -lpthread

TARGET  = test
SRC     = $(wildcard *.c)

all:
	$(CC) --sysroot=$(SYSROOT) $(CFLAGS) \
	      -o $(TARGET) $(SRC) $(LDFLAGS) $(LIBS)

 

예를 들어 위와 같이 컴파일할 경우, 소스 코드에서 #include <stdio.h> 구문이 사용되면 크로스 컴파일러는 기본 경로인 /usr/include/stdio.h 대신 다음 경로에서 헤더 파일을 찾는다.

 

/<toolchain>/sysroot/usr/include/stdio.h

 

 

 

 

 

 

 

sysroot의 구조

크로스 컴파일용 툴체인은 보통 컴파일러와 함께 sysroot를 포함한 형태로 배포된다.

예를 들어 Jetson용 L4T GCC 툴체인의 경우, 다음과 같은 경로에 sysroot가 포함되어 있다.

 

l4t-gcc
└── aarch64--glibc--stable-2022.08-1
    └── aarch64-buildroot-linux-gnu
        └── sysroot

 

 

이 sysroot 디렉터리를 살펴보면, 다음과 같이 루트 파일시스템의 뼈대를 다 갖추고 있다.

 

$ tree . -L 1

 

 

하지만 sysroot는 실행을 위한 환경이 아니라 컴파일과 링킹을 위한 환경이기 때문에 bin, sbin과 같은 실행 파일 디렉터리는 비어 있거나 최소한만 포함되는 경우가 많고, 실제로 중요한 파일들은 usr/include, lib, usr/lib 경로에 집중되어 있다.

 

 

 

 

 

 

 

 

 

--sysroot에 타겟 root filesystem이 아닌 sysroot를 사용하는 이유

호스트에 타겟 rootfs가 있다면 이걸 --sysroot로 활용할 수 있지 않을까? 그런 생각이 들어 빌드를 시도해봤는데 필수 헤더를 찾지 못하는 오류가 발생했다. 이건 네이티브 gcc와 크로스 컴파일러의 경로 탐색 메커니즘 차이 때문이었다.

네이티브 gcc는 해당 배포판의 Multiarch 규칙(헤더와 라이브러리의 배치 레이아웃)을 이미 알고 있어, /usr/include/aarch64-linux-gnu/ 같은 아키텍처 specific 경로를 자동으로 탐색한다.
반면 크로스 컴파일러는 여러 환경에서 재사용되는 범용 도구이기 때문에, 별도의 설정이 없으면 /usr/include/ 같은 표준 경로에서만 파일을 탐색한다. 이 때문에 타겟 rootfs를 sysroot로 지정하면, 헤더가 아키텍처 specific 경로 안에 존재하더라도 탐색되지 않아 오류가 발생할 수 있다.

 

 

 

 

 

 

 

sysroot에 필요한 라이브러리가 없을 때

호스트에서 타겟용 애플리케이션을 개발하다보면 타겟에는 라이브러리가 설치되어있는데, sysroot에는 해당 라이브러리가 없어서 링크 에러가 발생하는 상황을 마주칠 수 있다.

 

만약 해당 라이브러리가 패키지로 배포 된다면 다음 절차로 해결할 수 있다.

 

 

1. 타겟에서 패키지를 다운로드해 개발용 .deb 파일을 확보한다.

 

apt download <package>

 

 

2. 호스트에서 패키지를 압축해제하고 필요한 헤더와 라이브러리, 심볼릭 링크 파일을 확인한다. 

 

dpkg-deb -x <package>-dev_arm64.deb extracted/
cd extracted
tree .

 

 

3. 필요한 파일을 sysroot의 /usr/include, /usr/lib 등에 경로를 유지한채 복사한다.

 

반응형