WebAssembly 대상에 대한 변경 사항 및 정의되지 않은 심볼 처리

발행: (2026년 4월 4일 오전 09:00 GMT+9)
7 분 소요
원문: Rust Blog

Source: Rust Blog

TL;DR

  • Rust의 모든 WebAssembly 타겟은 wasm‑ld--allow-undefined 플래그를 전달하여 링크되었습니다.
  • 해당 플래그가 제거됩니다.
  • 이 변경은 Rust 1.96(2026‑05‑28) 에 포함됩니다.

--allow-undefined란?

Rust에서 WebAssembly 바이너리는 wasm‑ld(WebAssembly 링커)와 함께 링크하여 생성됩니다.
WebAssembly 타깃이 처음 도입된 이후로, 다음 플래그가 wasm‑ld에 전달되어 왔습니다.

--allow-undefined

그 문서는 다음과 같이 설명합니다:

--allow-undefined       Allow undefined symbols in linked binary.
                         This option is equivalent to
                         --import-undefined and
                         --unresolved-symbols=ignore-all

이 문맥에서 “Undefined”란

  • 미정의 심볼(undefined symbol) 은 현재 객체 파일 집합 안에서 링커가 해석할 수 없는 심볼을 의미합니다.
  • Rust에서는 extern "C" 블록을 통해 이러한 심볼을 참조할 수 있습니다. 예:
unsafe extern "C" {
    fn mylibrary_init();
}

fn init() {
    unsafe {
        mylibrary_init();
    }
}
  • mylibrary_init 은 별도의 구성 요소(예: C 라이브러리)가 정의를 제공하기 전까지는 미정의 상태입니다.

--allow-undefined 를 사용하면 링커는 미정의 심볼을 import 로 취급하고 다음과 같은 모듈을 출력합니다:

(module
  (import "env" "mylibrary_init" (func $mylibrary_init))
  ;; …
)

즉, 미정의 심볼은 링커에 의해 무시되고 최종 WebAssembly 모듈에서는 import 로 변환됩니다.

왜 이 플래그가 추가되었나요?

wasm‑ld 통합 초기 단계에서는 --allow-undefined 가 사실상 아무 것이든 링크하기 위해 필수적이었습니다. 이 우회 방법이 지속되면서 기본값이 되었지만, 현재는 더 이상 필요하지 않습니다.

--allow-undefined에 문제가 있는 이유

모든 WebAssembly 대상에 --allow-undefined를 전달하면 기본적으로 정의되지 않은 심볼을 오류로 처리하는 네이티브 플랫폼과 다른 동작을 만들게 됩니다. 주요 문제는 다음과 같습니다:

  1. 조용한 실패 – 잘못된 설정으로 실제로는 잘못된 심볼을 가져오는 실행 가능한 모듈이 생성됩니다.

  2. 진단하기 어려운 오류 – 모듈을 사용하는 도구(e.g., wasm-bindgen, wasm-tools component new)가 기본 "env" 임포트를 이해하지 못하고 혼란스러운 메시지를 출력합니다.

  3. 런타임에서의 놀라움 – 브라우저에서 다음과 같은 오류가 나타날 수 있습니다

    Uncaught TypeError: Failed to resolve module specifier "env".
    Relative references must start with either "/", "./", or "../".

    실제 문제는 누락된 "env" 모듈이 아니라 모듈에 섞여 들어간 정의되지 않은 심볼입니다.

모든 네이티브 플랫폼은 정의되지 않은 심볼을 오류로 처리하므로, Rust의 현재 WebAssembly 동작은 놀라운 일입니다. 플래그를 제거하면 WebAssembly가 네이티브 플랫폼과 일치하고 개발자에게 더 빠르고 명확한 진단을 제공하게 됩니다.

무엇이 깨질 수 있으며, 어떻게 고칠 수 있나요?

예상 영향

대부분의 경우 깨지는 일은 없으며, 임베딩 환경이 제공하지 않는 심볼을 가져오면 바이너리는 단순히 실행에 실패합니다. 이는 실제로 좋은 일인데, 런타임 오류 대신 링커 오류를 받게 되기 때문입니다.

플래그에 대한 의도적인 의존

일부 프로젝트는 --allow-undefined 플래그에 의존해 임포트를 생성합니다. 예를 들어:

unsafe extern "C" {
    fn js_log(n: u32);
}

그리고 JavaScript에서 임포트를 제공합니다:

let instance = await WebAssembly.instantiate(module, {
  env: {
    js_log: n => console.log(n),
  },
});

해결 방법

  1. 명시적 임포트 모듈#[link] 속성을 사용해 링커에게 해당 심볼이 어느 모듈에 속하는지 알려줍니다:

    #[link(wasm_import_module = "env")]
    unsafe extern "C" {
        fn js_log(n: u32);
    }

    이렇게 하면 임포트가 명시적으로 지정되어, 심볼이 정의되지 않은 것으로 간주되지 않으며 변경 전후 모두 정상 동작합니다.

  2. 이전 동작의 임시 복원 – 빠른 해결책이 필요하면 플래그를 수동으로 지정해 컴파일합니다:

    cargo rustc -- -Clink-arg=--allow-undefined

    (또는 -Clink-arg=--allow-undefined.cargo/config.toml 에 추가합니다.)

이 변경은 언제 발생하나요?

--allow-undefined 제거는 rust-lang/rust#149868에서 추적됩니다.

  • Nightly: 변경이 곧 적용됩니다.
  • Stable: Rust 1.96와 함께 2026‑05‑28에 릴리스될 예정입니다.

변경 후 문제가 발생하면 Rust 저장소에 이슈를 제출해 주세요.


계속 지켜봐 주세요, 즐거운 코딩 되세요!

0 조회
Back to Blog

관련 글

더 보기 »