mold 3.0의 Rust 전환, 명령이 같아도 빌드 조건은 달라진다
mold 3.0은 기존 명령과 링크 성능을 유지하면서 구현을 Rust로 바꿨다. 메모리 접근 검사의 의미와 Cargo 전환 비용을 구분하고, 바이너리 사용자·CI 관리자·패키지 관리자가 교체 전에 확인할 조건을 정리한다.
링커를 업데이트했는데 애플리케이션의 빌드 명령은 그대로이고, 링커를 만드는 CI부터 고쳐야 한다면 어떤 종류의 호환성이 유지된 것일까. C++에서 Rust로 재작성된 mold 3.0은 이 질문을 던진다. 실행 파일을 받아 쓰는 개발자와 빌드 도구를 직접 패키징하는 개발자가 같은 업데이트에서 확인할 것은 다르다.
링커는 컴파일된 목적 파일과 라이브러리를 연결해 실행 파일이나 공유 라이브러리를 만드는 도구다. mold의 3.0.0 공식 릴리스는 한국 시각 2026년 10월 5일 오후 7시 23분에 공개됐고, GeekNews에는 10월 6일 오전 2시 36분에 등록됐다. 10월 6일 확인한 최신 릴리스는 3.0.0이다.
재작성의 이유는 다음 수십 년에 있다
개발자 Rui Ueyama는 2.42.1 릴리스 노트에서 mold가 수십 년간 쓰일 도구라는 전제와, Rust가 시스템 소프트웨어에 사용할 만큼 성숙했다는 판단을 밝혔다. C++에 견줄 성능을 유지하면서 메모리 안전성을 얻는 것이 전환의 이유다. AI를 활용한 코딩이 큰 재작성을 더 현실적인 선택으로 만들었다고 설명하면서도, 기존 사용자의 신뢰를 잃을 위험이 사라지는 것은 아니라고 적었다.
여기서 언어 변경과 속도 향상을 바로 연결하면 발표를 잘못 읽게 된다. 3.0 발표는 링크 성능이 마지막 C++ 버전인 2.42.1과 동등한 수준이라고 설명한다. 같은 명령행 옵션과 대상 아키텍처를 지원하고, 열거한 버그 수정 외에는 같은 출력을 만드는 것을 목표로 한다. 이는 개발팀의 검증 결과이며 이 글에서 성능을 재측정하지 않았다. 3.0의 호환성 설명
내가 주목한 부분은 재작성의 성공 기준이 기존 동작을 보존하는 데 놓여 있다는 점이다. 빌드 도구를 관리하는 입장에서는 구현 언어보다 산출물과 실패 방식이 예측 가능한지가 먼저다. Rust 전환은 그 보존 작업을 앞으로 어떤 도구로 수행할지에 관한 선택으로 읽는 편이 정확하다.
안전하게 중단하는 것과 올바르게 링크하는 것은 별개다
구체적인 개선은 손상된 입력 파일을 읽는 경로에 있다. 공식 설명에 따르면 C++ 버전에서 범위를 벗어난 메모리 읽기로 종료될 수 있던 접근에 경계 검사가 적용됐다. 잘못된 위치를 읽으려 하면 Rust의 패닉으로 중단한다. 패닉은 정상 실행을 계속할 수 없는 상태를 알리는 메커니즘이다. 변경된 실패 방식
이것이 손상된 파일을 복구하거나 링크를 성공시킨다는 뜻은 아니다. 또한 메모리 접근을 검사한다고 함수 주소 계산이나 심볼 처리까지 자동으로 맞아지는 것도 아니다. 심볼은 다른 파일에서 참조할 함수·변수 등의 이름이다. 링커에는 이 이름을 올바른 정의와 연결하는 별도의 정확성이 필요하다.
실제로 3.0에는 공유 라이브러리에서 외부로 공개한 함수를 --icf=safe가 병합하던 문제, 부분 링크 결과에서 C++ 예외 처리가 깨지던 문제 등의 수정이 함께 들어 있다. 개발팀은 모든 지원 대상의 테스트, 다양한 실제 입력과 옵션 조합의 출력 비교, Gentoo 패키지 빌드에서 회귀를 찾지 못했다고 보고한다. 수정 목록과 검증 범위 나는 이 결과를 도입 검토의 근거로 삼되, 우리 서비스의 예외 처리나 공유 라이브러리 로딩 시험까지 생략할 근거로 삼지는 않겠다.
GNU ld와의 호환성도 진행 중인 목표다. 특히 메모리 배치 등을 지정하는 링커 스크립트의 지원을 넓혀 Linux 배포판의 기본 링커로 채택될 기반을 마련하려 한다. 커널과 펌웨어까지 링크하려는 계획을 이미 완성된 지원으로 읽어서는 안 된다. 개발 방향
바이너리 사용자와 패키지 관리자의 전환 비용
mold 자체는 MIT 라이선스로 제공된다. v3.0.0의 설치 안내에는 Linux용 사전 빌드 바이너리와 지원 CPU 목록이 있다. 제공된 바이너리를 쓰는 경로와 소스에서 만드는 경로를 구분하면 Rust 의존성이 누구에게 추가되는지 분명해진다.
| 사용하는 방식 | 먼저 확인할 변화 |
|---|---|
| 기존 mold 실행 파일을 교체한다 | 실제 사용 버전, 대상 CPU, 산출물과 실행 시험 |
| CI에서 mold 소스를 빌드한다 | Rust 1.95 이상, Cargo와 C 컴파일러 준비 |
| 배포판 패키지를 만든다 | 제거된 CMake 옵션, Cargo 기능, 라이브러리 설치 경로 |
| 초기 도구부터 순서대로 구축한다 | mold를 만들 시점에 Rust 도구체인이 이미 있는지 |
직접 빌드하는 경로에서는 CMake 대신 Cargo를 쓰고 테스트도 ctest에서 cargo test로 바뀐다. Rust로 옮겨도 C 컴파일러는 필요하다. 최소 Rust 버전 1.95는 릴리스 태그의 Cargo.toml에도 명시돼 있다. 기존 Docker 이미지에서 CMake 명령만 바꾸면 끝난다고 가정해서는 안 된다.
설치 경로에는 작은 함정도 있다. mold -run은 다른 빌드 명령이 호출하는 링커를 mold로 연결하는 기능이다. 동반 라이브러리를 /usr/lib64처럼 기본값과 다른 위치에 설치한다면, 빌드와 설치 양쪽에 MOLD_LIBDIR를 맞춰야 이 기능이 라이브러리를 찾는다. oneTBB 의존성은 제거됐고 압축 라이브러리의 시스템 버전 사용 설정도 확인할 대상이다. 빌드 변경 사항
최소한의 도구에서 시작해 컴파일러를 차례로 만드는 부트스트랩 환경이라면 비용이 더 크다고 본다. 예를 들어 Rust를 만들기 전에 mold를 먼저 준비하던 순서에는 새 의존성이 생긴다. 이는 새 버전의 빌드 전제에서 도출한 판단이다. 기존 GNU ld나 lld로 초기 단계를 통과한 뒤 mold를 만드는 방법, 제공되는 바이너리를 사용하는 방법을 환경에 맞춰 검토할 수 있다. 기존 도구로 요구를 충족한다면 전환 시점을 늦추는 선택도 가능하다.
교체 여부는 대표 산출물 하나에서 결정한다
나는 시스템 전체의 기본 링커를 바꾸기 전에 프로젝트 하나에서 비교하겠다. 먼저 현재 빌드 로그에서 링크 단계가 차지하는 시간을 분리한다. 컴파일이나 테스트가 대부분의 시간을 쓰는 작업이라면 링크 도구만 바꿨을 때 줄어들 시간을 과대평가하기 쉽다. 기존 mold 사용자라면 3.0에서 추가 속도 향상을 얻는다는 전제도 두지 않는다.
검증 순서는 다음처럼 잡을 수 있다.
- 컴파일러, 입력 파일, 최적화 옵션을 고정하고 두 버전으로 링크한다.
- 산출물을 실행해 주요 기능, 예외 처리, 공유 라이브러리 로딩을 확인한다.
- 디버그 정보와 외부 공개 심볼을 비교하고, 차이가 있다면 릴리스의 수정 목록과 대조한다.
- 같은 조건에서 링크 시간과 최대 메모리를 측정하고 이전 버전으로 되돌릴 경로를 남긴다.
설정 파일에 mold를 적었다는 사실만으로 실제 사용을 확인할 수는 없다. 공식 README는 산출물의 .comment 섹션에 남는 식별 문자열을 확인하는 방법을 안내한다. 아래는 Linux에서 mold 3.0.0으로 만든 ELF 실행 파일 ./app과 GNU Binutils의 readelf가 있다는 전제의 확인 명령이다. 여기서는 실행하지 않았다.
readelf -p .comment ./app
출력의 mold 식별자는 어떤 링커가 산출물을 만들었는지 확인하는 단서다. 실행 결과의 정확성을 보장하는 표시는 아니므로 앞의 시험과 함께 사용한다.
mold 3.0을 검토할 이유는 메모리 접근 검사와 호환성 수정, 앞으로의 유지보수 방향에 있다. 선택을 가르는 질문은 더 구체적이다. 필요한 도구체인을 준비할 수 있는가, 우리 옵션으로 만든 프로그램이 제대로 동작하는가, 전체 개발 과정에서 줄어드는 비용이 있는가. 이 세 가지에 답할 수 있을 때 교체 범위를 정하는 것이 합리적이라고 본다.
참고 자료
이 이야기가 도움이 되었나요?