TECH 으로 돌아가기
TECH HACKER NEWS 오늘 6분 읽기 41 READS

C 언어를 구하려면 ABI부터 구해야 한다 — 30년 묵은 바이너리 호환성의 족쇄

C 언어를 구하려면 ABI부터 구해야 한다 — 30년 묵은 바이너리 호환성의 족쇄
SOURCE IMAGE · HACKER NEWS
C 언어를 구하려면 ABI부터 구해야 한다 — 30년 묵은 바이너리 호환성의 족쇄

C 언어는 올해로 50년이 넘은 언어인데요. 운영체제, 데이터베이스, 임베디드 기기까지 세상의 기반 시설 대부분이 여전히 C 위에서 돌아가고 있어요. 그런데 이상하죠. 이렇게 중요한 언어가 왜 수십 년째 거의 변하지 못하는 걸까요? C 표준위원회(WG14)에서 활동해 온 JeanHeyd Meneide(필명 ThePhD)가 그 범인으로 'ABI'를 지목하는 글을 올렸어요. 제목부터 비장하죠. 'C를 구하려면, ABI를 구해야 한다.'

ABI가 뭐냐면요

API는 다들 아실 텐데요, ABI(Application Binary Interface)는 그 바이너리 버전이라고 보시면 돼요. API가 '이 함수는 이런 인자를 받아요'라는 소스코드 수준의 약속이라면, ABI는 컴파일이 끝난 기계어 수준의 약속이거든요. 함수 인자를 어떤 레지스터에 실어 보낼지, 구조체의 각 필드가 메모리 몇 번째 바이트에 놓일지, 함수 이름이 바이너리 안에서 어떤 심볼 이름으로 저장될지 같은 것들이요.

택배에 비유하면 이래요. API가 '주문서 양식'이라면 ABI는 '상자 규격과 송장 붙이는 위치'예요. 양식이 같아도 상자 크기가 몰래 바뀌면 물류 시스템 전체가 멈추는 거죠.

intmax_t라는 화석

글에서 대표 사례로 드는 게 intmax_t예요. 이름 그대로 '이 시스템에서 가장 큰 정수 타입'이어야 하는데, 현실에서는 64비트(long long)에 영원히 묶여버렸거든요. 요즘 컴파일러들이 128비트 정수(__int128)를 멀쩡히 지원하는데도요.

왜 못 바꾸냐면, 공유 라이브러리 때문이에요. 리눅스의 libc.so 같은 라이브러리는 시스템에 하나만 깔려 있고, 수천 개의 프로그램이 그걸 같이 써요. 10년 전에 컴파일된 프로그램은 imaxabs() 같은 함수에 8바이트 인자를 넘기도록 기계어가 박제되어 있는데, 라이브러리 쪽만 16바이트를 받도록 바뀌면 어떻게 될까요? 컴파일 에러 같은 친절한 경고는 없어요. 인자가 어긋난 채로 그냥 실행되고, 조용히 메모리가 깨지는 거예요. 그래서 표준위원회는 '기존 바이너리를 깨는 변경'을 사실상 전부 거부해 왔고, 그 결과 C는 발전이 멈춘 것처럼 보이게 됐어요.

해법: 이름과 심볼을 분리하자

저자가 미는 해법은 '투명한 별칭(transparent alias)'이에요. 사실 glibc는 이미 비슷한 걸 비표준 트릭으로 하고 있거든요. 소스코드에서는 같은 함수 이름을 쓰지만, 컴파일할 때 헤더가 그 이름을 버전이 붙은 심볼로 몰래 바꿔치기하는 거예요. 옛날에 빌드된 바이너리는 옛 심볼을 계속 쓰고, 새로 컴파일하는 코드는 새 심볼에 연결되니까 둘이 충돌하지 않아요.

문제는 이게 컴파일러마다 다른 마법(asm 라벨 같은)이라는 거예요. 그래서 이 메커니즘 자체를 C 표준에 넣자는 게 핵심 주장이에요. 언어 차원에서 '이 함수 이름은 실제로는 저 심볼을 가리킨다'라고 선언할 수 있게 되면, intmax_t 같은 타입도 드디어 키울 수 있고, 라이브러리 작성자들도 기존 사용자를 깨뜨리지 않으면서 함수를 개선할 수 있게 되죠.

C만의 문제가 아니에요

이 고민, C++는 더 심하게 겪고 있어요. GCC 5가 std::string 내부 구현을 바꿨을 때 리눅스 생태계 전체가 몇 년간 홍역을 치렀고, C++ 표준위원회는 2020년에 'ABI를 깰 것인가, 영원히 안 깰 것인가'라는 정면 투표까지 갔다가 결국 못 깨는 쪽으로 기울었어요. 그 대가로 성능을 크게 올릴 수 있었던 제안들이 줄줄이 좌초됐고요. 반대로 Rust는 아예 안정적인 ABI를 약속하지 않는 전략을 택했어요. 대신 모든 걸 소스에서 다시 컴파일하는 문화죠. Swift는 애플이 몇 년을 갈아 넣어서야 ABI 안정성을 달성했고요. 어느 쪽도 공짜가 없다는 거예요.

우리에게 주는 시사점

'나는 C 안 쓰는데?' 싶으실 수도 있는데요, ABI는 사실 모든 개발자의 문제예요. 파이썬 네이티브 확장이 파이썬 버전을 올릴 때마다 다시 빌드돼야 하는 것도, 안드로이드 NDK로 만든 .so 파일이 특정 구조체 레이아웃에 의존하는 것도 전부 같은 원리거든요. 공유 라이브러리나 SDK를 배포하는 분이라면 '공개 구조체에 필드 하나 추가하는 게 왜 위험한 변경인지'를 이 글 하나로 체감하실 수 있을 거예요.

한 줄로 정리하면 이래요. C가 늙어서 못 변하는 게 아니라, 바이너리 호환성이라는 족쇄를 풀 공식적인 열쇠가 없어서 못 변하는 거고, 그 열쇠(심볼 버저닝의 표준화)를 이제라도 만들자는 이야기예요.

여러분은 어떻게 생각하세요? 호환성을 위해 언어의 진화를 멈추는 게 맞을까요, 아니면 Rust처럼 '전부 다시 컴파일하세요'가 미래일까요?


🔗 출처: Hacker News

SOURCE · HACKER NEWS
원문 전체 보기 → https://thephd.dev/to-save-c-we-must-save-abi-fixing-c-funct...
SHARE
처리 중...