1차 공개일 · 8월 20일1차 강의가 모두 공개됩니다
TECH 으로 돌아가기
TECH HACKER NEWS 오늘 8분 읽기 18 READS

뮤슬 정적 바이너리로 호스트 GPU 드라이버를 쓴다, SoLo의 접근법

뮤슬 정적 바이너리로 호스트 GPU 드라이버를 쓴다, SoLo의 접근법
SOURCE IMAGE · HACKER NEWS

리눅스에서 소프트웨어를 배포하는 가장 지루하면서도 안정적인 방법 중 하나가 정적 링크된 바이너리다. 파일 하나에 모든 의존성을 담아 두면 배포 대상 시스템의 라이브러리 버전이나 배포판 차이에 휘둘리지 않는다. 문제는 이 깔끔함이 GPU를 만나는 순간 무너진다는 점이다. Vulkan이나 OpenGL 드라이버는 호스트가 공유 오브젝트(.so) 형태로 제공하며, 대부분 glibc에 링크되어 있다. 반면 완전 정적 바이너리를 만들 때 자주 쓰이는 musl libc 기반 실행 파일은 원칙적으로 이런 드라이버를 dlopen()으로 불러올 수 없다. 정적 배포의 이점을 취하려다 하드웨어 가속을 포기해야 하는 딜레마가 생기는 셈이다.

pg83가 공개한 SoLo는 바로 이 경계를 넘기 위한 프로젝트다. 컨테이너도, AppImage도, 프로세스 안에 두 번째 libc를 끼워 넣는 방식도 아니다. musl로 링크된 정적 실행 파일 하나를 배포하되, 실행 시점에 사용자 시스템에 이미 설치된 glibc 링크 GPU 드라이버를 그대로 불러오는 구조다. 하드웨어 종속적인 코드는 호스트에 남겨 두고, 나머지 전부는 배포자가 하나의 파일로 들고 다닌다는 발상이다.

자체 ELF 로더와 glibc ABI 브리지

SoLo의 핵심은 dlfcn 스타일의 소스 API 뒤에 자체 ELF 로더(x86-64와 aarch64 지원)와 musl 위에 구현한 glibc ABI 브리지를 두는 것이다. elf_loader.cpp는 ELF 세그먼트를 매핑하고 DT_NEEDED를 따라가며 버전 지정 심볼을 해석하고, x86-64 재배치와 ELF TLS·TLSDESC, IFUNC 구체화, RELRO 적용, 초기화 루틴 실행까지 처리한다. 의존 대상이 또 다른 ELF DSO라면 재귀적으로 로드한다.

주목할 점은 glibc 자체는 의도적으로 로드하지 않는다는 것이다. malloc@GLIBC_2.2.5 같은 임포트는 glibc_shim.cpp가 프로세스가 이미 쓰고 있는 musl 런타임 위의 ABI 호환 어댑터로 연결한다. 지원되지 않는 glibc 함수는 각각 고유한 스텁으로 생성되어, 호출되는 순간 정확히 어떤 심볼과 버전이 문제인지를 드러내며 큰 소리로 실패한다. 조용히 프로세스를 망가뜨리는 대신 실패 지점을 특정하게 만든 설계다.

동기화 객체 처리도 세심하다. musl은 각 아키텍처의 glibc ABI에 맞춰 동기화 객체 크기를 잡기 때문에, 브리지는 이를 이중으로 감싸지 않는다. 드라이버가 만든 pthread_mutex_t는 그 자리에서 그대로 쓰이고, 하나의 락은 로드된 DSO와 이를 공유하는 정적 실행 파일 양쪽에서 동일한 하나의 락으로 동작한다. glibc의 재귀·오류검사 초기화 값은 첫 사용 시점에 채택된다. 이런 부분들이야말로 단순한 dlsym 셈이 아니라 외부 코드가 실제로 정상 동작하게 만드는 핵심이다.

검증 방식과 정적 의존성 대체

SoLo는 데모의 말만 믿으라고 하지 않는다. 저장소에는 완전 정적 실행 파일이 호스트의 수정되지 않은 Vulkan 드라이버를 불러와 컴퓨트 셰이더를 돌리고 결과를 PNG로 저장하는 엔드투엔드 예제가 들어 있다. AMD radv·radeonsi, 인텔, 엔비디아 GPU, 그리고 Asahi Linux 위의 애플 M1에서 테스트됐다고 밝힌다. 나아가 매 커밋마다 CI가 데비안에서 가장 많이 설치되는 1,000개 패키지의 공유 라이브러리, 즉 2,100개가 넘는 호스트 오브젝트를 SoLo를 통해 x86-64와 aarch64 양쪽에서 로드한다. 네이티브 빌드·테스트는 Alpine/musl(GCC), Fedora(GCC), Ubuntu(Clang)에서 수행되며 Vulkan 테스트는 각 배포판 자체의 Lavapipe 패키지를 쓴다.

또 하나 흥미로운 기능은 정적 제공자 레지스트리다. SoLo는 디스크에서 DSO를 로드하기 전 이 레지스트리를 확인해, 이를테면 Wayland 의존성을 실행 파일에 이미 링크된 함수로 충족시킬 수 있다. 덕분에 애플리케이션은 모든 지원 시스템 중 가장 오래된 libwayland에 맞추는 대신 최신 libwayland를 자체 임베드해 놓고, 시스템 DSO가 요구하는 의존성에 대해 "이 심볼은 내 실행 파일 안의 것을 쓰라"고 동적 로더에 지시할 수 있다. 표준 디렉터리 밖 라이브러리를 위해 LD_LIBRARY_PATH와 DL_ELF_LIBRARY_PATH도 존중된다.

한국 실무자에게 주는 의미와 한계

실무적으로 SoLo의 가치는 배포 단순성과 하드웨어 가속을 양립시킨다는 데 있다. 데스크톱 애플리케이션, 게임 관련 도구, GPU 컴퓨트 유틸리티를 여러 배포판에 뿌려야 하는 팀이라면, 컨테이너 런타임이나 AppImage 패키징 없이 단일 실행 파일로 최신 라이브러리를 담으면서도 각 시스템의 네이티브 드라이버 성능을 그대로 쓸 수 있다. 실제로 이 저장소는 자신들이 개발하는 터미널 에뮬레이터의 릴리스 바이너리를 이런 방식으로 빌드한다고 언급한다. 사용법도 무겁지 않아서, mesa-vulkan-drivers 정도만 설치된 리눅스라면 사전 빌드된 바이너리로 512×512 RGBA 이미지를 만드는 데모를 바로 실행할 수 있고, 파이썬 3와 C/C++ 컴파일러만 있으면 소스에서 빌드도 된다. 배포되는 libdlfcn.a 아카이브를 musl 정적 애플리케이션에 링크하고 lib/dlfcn.h를 포함하면 기존 dlopen()·dlsym() 호출이 SoLo로 리다이렉트되므로, 기존 정적 빌드 그래프에 복사해 넣기도 수월하다.

다만 한계와 전제도 분명히 인식할 필요가 있다. 이 프로젝트는 glibc 함수 전체를 지원하는 것이 아니라 필요한 부분을 유한하고 테스트 가능한 호환 계층으로 만드는 것을 목표로 한다. 지원되지 않는 glibc 함수는 호출 시 실패하도록 설계됐으므로, 드라이버가 브리지가 아직 다루지 않은 심볼을 요구하면 그 지점에서 막힌다. 로더 대상 아키텍처도 x86-64와 aarch64로 한정된다. 즉 SoLo는 임의의 glibc 바이너리를 마법처럼 돌려 주는 만능 도구라기보다, GPU 드라이버 로딩이라는 구체적 문제를 겨냥한 정밀한 호환 계층에 가깝다. 프로젝트 스스로 표현한 대로, Vulkan PNG는 '완전 정적'과 '시스템 GPU 사용' 사이의 단단한 벽에 문이 있음을 보여 주는 첫 증거일 뿐이다. 도입을 검토한다면 자신의 드라이버 스택과 필요한 심볼 집합이 CI에서 검증된 범위 안에 드는지부터 확인하는 것이 현실적이다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://github.com/pg83/solo
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기
처리 중...