링커를 바꿨는데 빌드 명령은 그대로 두려는 릴리스

C나 C++, Rust처럼 컴파일 결과를 실행 파일로 묶는 언어를 쓰다 보면 소스 코드를 오브젝트 파일로 만드는 시간만큼 마지막 링크 단계가 발목을 잡는 경우가 있다. 특히 Chromium, LLVM, ClickHouse처럼 출력 파일이 크고 오브젝트 파일 수가 많은 프로젝트에서는 코드 한 줄을 고친 뒤 다시 실행하기까지 링크가 몇 초씩 이어질 수 있다. mold는 이 시간을 줄이기 위해 만들어진 병렬 링커이고, 3.0은 그 핵심 구현을 C++에서 Rust로 통째로 바꾼 첫 정식 릴리스다.

보통 이런 규모의 재작성은 사용자가 마이그레이션 비용부터 걱정하게 만든다. 하지만 3.0의 릴리스 노트가 가장 앞에서 강조하는 목표는 새 인터페이스가 아니라 2.42.1과의 호환성이다. 같은 명령행 옵션을 받고, 같은 대상 아키텍처를 지원하며, 이번 릴리스에 포함된 버그 수정 차이를 제외하면 같은 출력을 만들도록 설계했다고 프로젝트가 설명한다.

프로젝트는 지원하는 모든 대상에서 테스트 스위트를 돌리고, 다양한 실제 워크로드와 옵션 조합에서 2.42.1과 출력을 비교했으며, Gentoo 패키지를 전부 빌드해 회귀를 찾았다고 밝힌다. 이 검증은 mold 프로젝트가 수행한 자체 검증이므로 제3자 인증으로 볼 수는 없지만, 단순히 언어만 바꾼 실험 브랜치가 아니라 기존 사용자 교체를 전제로 출시했다는 점은 분명하다.

근거 [1] [4]

설치해서 쓰는 사람보다 직접 빌드하는 사람이 더 많이 바뀐다

배포판 패키지나 릴리스 바이너리를 받아서 쓰는 경우라면 평소처럼 컴파일러에 mold를 지정하면 된다. Clang과 GCC 12.1 이상에서는 기존처럼 `-fuse-ld=mold`를 전달할 수 있고, 프로젝트가 제공하는 Linux 사전 빌드 바이너리도 x86-64, ARM, RISC-V, PPC64LE, s390x, LoongArch 등 여러 아키텍처에 맞춰 릴리스 자산으로 올라와 있다. 즉 실행 파일을 가져다 쓰는 흐름은 2.x와 크게 다르지 않다.

변화가 큰 쪽은 소스에서 직접 빌드하는 개발자와 배포판 유지 관리자다. 2.x에서 쓰던 CMake 기반 빌드가 사라지고 3.0은 Cargo로 빌드한다. 공식 README와 Cargo.toml은 Rust 1.95 이상을 요구하며, 기본 빌드는 `cargo build --release`, 설치는 `./install-mold.sh`를 사용한다. 기존 CMake 옵션은 제거됐다.

라이브러리 설치 경로도 확인해야 한다. `mold -run`은 동반 라이브러리인 `mold-wrapper.so`를 사용하므로, 배포판이 `$PREFIX/lib`가 아닌 `/usr/lib64` 같은 경로에 라이브러리를 넣는다면 빌드와 설치 양쪽에서 `MOLD_LIBDIR`를 맞춰야 한다. 패키징 스크립트가 예전 CMake 변수와 설치 경로를 가정하고 있다면 3.0 업그레이드에서 가장 먼저 깨질 가능성이 높은 부분이다.

shell · 공식 README의 빌드·사용 흐름을 한데 정리한 예시. 소스 빌드는 Rust 1.95 이상이 필요하다.
git clone https://github.com/rui314/mold.git
cd mold
cargo build --release
sudo ./install-mold.sh

# Clang/GCC 12.1+에서 사용
clang -fuse-ld=mold main.o -o app

근거 [1] [2] [3]

Rust 전환이 성능 개선 릴리스라는 뜻은 아니다

3.0이라는 큰 버전 번호만 보면 Rust로 바꾸면서 더 빨라졌다고 생각하기 쉽지만, 프로젝트는 오히려 성능을 2.42.1과 같은 수준으로 유지했다고 설명한다. 이번 재작성에서 우선순위는 기존 고속 링커의 동작과 성능을 보존하면서 구현 언어를 바꾸는 것이었다. 따라서 3.0 자체의 가치를 ‘몇 배 더 빨라졌다’고 표현하는 것은 릴리스 노트와 맞지 않는다.

현재 README에 실린 성능 수치는 2026년 8월 28일 기준으로 lld, wild, mold를 같은 조건에서 빌드해 비교한 프로젝트 자체 벤치마크다. AMD Ryzen Threadripper 7980X와 Apple M1 Ultra 두 환경에서 여러 대형 프로젝트를 링크했고, README는 전체 중앙값 기준으로 mold가 LLVM lld보다 4.9배, wild보다 1.9배 빠르다고 요약한다. 이 수치는 3.0만의 성능 향상을 뜻하는 것이 아니라 mold라는 링커의 기존 성능 특성을 설명하는 자료다.

예를 들어 Threadripper 환경의 디버그 빌드에서 README는 Chromium 145를 lld가 16.64초, mold가 1.65초에 링크했다고 기록한다. ClickHouse 26.1은 6.62초 대 0.98초, TensorFlow 2.21은 50.73초 대 3.15초였다. 반대로 Apple M1 Ultra의 일부 항목에서는 wild와 차이가 크지 않거나, Clang 디버그 빌드처럼 wild가 mold보다 조금 빠른 경우도 있다. 어떤 프로젝트에서나 같은 배수로 빨라진다고 일반화할 수 없는 이유다.

Threadripper 디버그 빌드LLVM lldmoldlld/mold
Chromium 14516.64초1.65초10.1배
ClickHouse 26.16.62초0.98초6.7배
TensorFlow 2.2150.73초3.15초16.1배

mold README의 2026-08-28 프로젝트 자체 벤치마크 중 일부. 같은 하드웨어·입력에서 비교한 결과지만 독립 기관 측정값은 아니다.

근거 [1] [2]

메모리 안전성은 좋아졌지만 잘못된 입력이 자동으로 복구되는 건 아니다

프로젝트가 Rust 전환의 실질적인 이점으로 직접 언급한 부분은 손상된 입력을 다루는 방식이다. C++ 구현에서는 깨진 오브젝트 파일을 읽다가 범위를 벗어난 메모리에 접근해 세그멘테이션 폴트로 끝날 수 있었지만, Rust 구현에서는 같은 범위 밖 접근이 검사되어 해당 지점에서 panic으로 중단된다고 설명한다. 메모리 밖 데이터를 계속 읽는 형태의 실패를 줄이는 방향이다.

그렇다고 Rust로 다시 썼다는 사실만으로 링크 결과의 논리적 버그까지 사라지는 것은 아니다. 3.0 릴리스 노트에도 정적 실행 파일과 버전 스크립트를 함께 쓸 때의 충돌, `--gc-sections`가 초기화 함수를 제거하던 문제, 여러 재배치 처리 오류, `-r` 관련 문제처럼 링크 의미 자체에 영향을 주는 버그 수정이 길게 포함돼 있다. 메모리 안전성과 링커 의미의 정확성은 서로 다른 문제다.

업그레이드 검증에서도 이 둘을 나눠 보는 편이 좋다. CI에서 기존 2.42.1과 3.0을 모두 돌릴 수 있다면 대표적인 실행 파일과 공유 라이브러리를 두 버전으로 만들어 테스트하고, 버전 스크립트·LTO·부분 링크(`-r`)·ICF·GC sections처럼 프로젝트가 실제로 쓰는 옵션을 따로 확인하는 것이 낫다. 릴리스 노트가 드롭인 교체를 목표로 한다고 해서 모든 특수 빌드가 자동으로 안전하다는 뜻은 아니다.

근거 [1] [3]

oneTBB가 빠지고 병렬 처리는 Rust 생태계로 옮겨갔다

빌드 의존성도 꽤 달라졌다. 3.0은 oneTBB 의존성을 제거했고 Cargo.toml에는 Rayon이 들어가 있다. 프로젝트는 기존처럼 mimalloc 3.5.3을 정적으로 링크하며, 원하면 `--features system-allocator`로 시스템 malloc을 사용할 수 있다고 안내한다. zlib은 시스템 라이브러리를 사용할 수 있고 zstd와 BLAKE3도 의존성에 포함돼 있다.

지원 대상을 선택하는 방식도 CMake의 `MOLD_TARGETS` 변수에서 Cargo feature로 이동했다. 배포판은 이전과 마찬가지로 모든 대상을 포함해 빌드하는 것을 프로젝트가 권장한다. 테스트 명령도 `ctest`에서 `cargo test`로 바뀌었고, 테스트용 의존성 설치 스크립트도 `install-test-deps.sh`로 정리됐다.

이 변화 때문에 배포 자동화는 소스 코드보다 먼저 손볼 부분이 생긴다. Dockerfile이나 패키지 레시피에서 CMake, Ninja, oneTBB를 설치하고 `cmake -DMOLD_TARGETS=...`를 실행하던 흐름이라면 3.0에서는 그 단계가 그대로 통하지 않는다. 반대로 단순히 공식 바이너리를 받아 `-fuse-ld=mold`만 지정해 쓰던 팀은 내부 구현 언어가 바뀌어도 변경량이 훨씬 작다.

근거 [1] [3]

3.x의 다음 목표는 더 빠른 링크보다 GNU ld 호환성이다

3.0의 의미를 장기적으로 보면 Rust 전환보다 다음 목표가 더 크다. Rui Ueyama는 2.42.1 릴리스에서 mold 3.x를 Linux 배포판이 `/usr/bin/ld` 기본 링커로 채택할 수 있는 수준으로 만드는 것을 높은 우선순위로 제시했다. 지금까지 mold가 속도에 집중했다면 앞으로는 기존 GNU ld가 받아들이는 linker script와 특수 워크로드까지 얼마나 넓게 호환하는지가 중요한 과제가 된다.

커널과 펌웨어처럼 일반 사용자 공간 프로그램보다 까다로운 빌드는 linker script 기능에 많이 의존한다. 프로젝트는 이런 영역까지 연결하려면 아직 빠진 스크립트 기능을 구현하고 배포판 개발자와 광범위한 호환성 테스트를 해야 한다고 설명했다. 그래서 3.0을 ‘이제 GNU ld를 바로 대체해도 된다’는 선언으로 읽으면 너무 앞서간다. 3.0은 그 목표를 향한 3.x 계열의 출발점에 가깝다.

실제 도입 판단은 단순하다. 이미 mold 2.42.1을 쓰고 공식 바이너리나 일반적인 `-fuse-ld=mold` 흐름에 의존한다면 3.0은 비교적 낮은 변경 비용으로 시험할 수 있다. 직접 패키징하거나 특수한 linker script, 부분 링크, LTO 조합을 많이 쓴다면 새 빌드 체계와 이번에 수정된 호환성 영역을 CI에서 먼저 확인하는 편이 맞다. Rust 재작성이라는 제목보다도 이 호환성 검증이 업그레이드의 핵심이다.

패키지 관리자가 아니라 애플리케이션 개발자라면 업그레이드 절차를 더 단순하게 잡을 수 있다. 먼저 기존 빌드에서 링크 시간을 기록하고 3.0으로 교체한 뒤 동일한 입력을 다시 빌드한다. 실행 테스트와 바이너리 크기, 디버그 심볼 처리까지 문제가 없는지 확인하면 된다. 링크 시간이 프로젝트 전체 빌드의 병목이 아니었다면 mold 버전 교체만으로 체감 속도가 크게 달라지지 않을 수도 있다.

반대로 대규모 C++ 프로젝트처럼 링크가 반복 개발 주기의 큰 비중을 차지한다면 이번 릴리스의 핵심은 새로운 최고 속도보다 유지보수 기반이 바뀌었다는 데 있다. 프로젝트는 3.0에서 기존 성능 수준을 유지했다고 밝히고 있으므로, 팀 입장에서는 성능을 다시 얻는 릴리스라기보다 앞으로의 GNU ld 호환성 확대와 장기 유지보수를 위한 기반 교체로 보는 편이 정확하다.

근거 [1] [4]