Почему для просмотра функций Rust в библиотеке WASM требуется `#[no_mangle]`?

Я создал новую пустую библиотеку Rust. Я установил crate-type на cdylib, чтобы генерировались файлы .wasm. Это мой lib.rs:

#[no_mangle]
pub extern fn fibonacci(n: usize) -> usize {
  if n < 2 {
     return n;
  }

  return fibonacci(n - 1) + fibonacci(n - 2);
}

Если я удалю #[no_mangle], Cargo создаст двоичный файл WASM, который даже не содержит никакого кода:

$ wasm-objdump -h -x target/wasm32-unknown-unknown/release/fibonacci.wasm

fibonacci.wasm: file format wasm 0x1

Sections:

    Table start=0x0000000a end=0x0000000f (size=0x00000005) count: 1
   Memory start=0x00000011 end=0x00000014 (size=0x00000003) count: 1
   Global start=0x00000016 end=0x0000002f (size=0x00000019) count: 3
   Export start=0x00000031 end=0x00000056 (size=0x00000025) count: 3
   Custom start=0x00000059 end=0x00000f5a (size=0x00000f01) ".debug_abbrev"
   Custom start=0x00000f5e end=0x0005c6df (size=0x0005b781) ".debug_info"
   Custom start=0x0005c6e3 end=0x00084461 (size=0x00027d7e) ".debug_ranges"
   Custom start=0x00084465 end=0x00118f6a (size=0x00094b05) ".debug_str"
   Custom start=0x00118f6e end=0x00152a48 (size=0x00039ada) ".debug_line"
   Custom start=0x00152a4a end=0x00152a63 (size=0x00000019) "name"
   Custom start=0x00152a65 end=0x00152ab2 (size=0x0000004d) "producers"
   Custom start=0x00152ab4 end=0x00152ae0 (size=0x0000002c) "target_features"

Section Details:

Table[1]:
 - table[0] type=funcref initial=1 max=1
Memory[1]:
 - memory[0] pages: initial=16
Global[3]:
 - global[0] i32 mutable=1 <__stack_pointer> - init i32=1048576
 - global[1] i32 mutable=0 <__data_end> - init i32=1048576
 - global[2] i32 mutable=0 <__heap_base> - init i32=1048576
Export[3]:
 - memory[0] -> "memory"
 - global[1] -> "__data_end"
 - global[2] -> "__heap_base"
Custom:
 - name: ".debug_abbrev"
Custom:
 - name: ".debug_info"
Custom:
 - name: ".debug_ranges"
Custom:
 - name: ".debug_str"
Custom:
 - name: ".debug_line"
Custom:
 - name: "name"
 - global[0] <__stack_pointer>
Custom:
 - name: "producers"
Custom:
 - name: "target_features"
  - [+] mutable-globals
  - [+] sign-ext

Есть ли способ принудительно включить функцию в двоичный файл без использования #[no_mangle]? Использование extern само по себе не работает.

Вам, вероятно, все равно понадобится #[no_mangle], чтобы иметь возможность назвать функцию, но странно, что без нее она полностью опускается.

Chayim Friedman 25.03.2024 21:16
extern просто влияет на ЛПИ. Фактически, отсутствие указания ABI просто означает ABI по умолчанию, а это означает, что он не имеет никаких эффектов.
jthulhu 25.03.2024 21:16
Вот, это задокументировано. Видимо, это также подразумевает используется.
prog-fh 25.03.2024 21:18

Означает ли это, что вы можете просто заменить его на used, и это может/должно работать?

TachyonicBytes 25.03.2024 21:20

@TachyonicBytes У него будет другое (искаженное) имя, но да, оно будет экспортировано.

Chayim Friedman 25.03.2024 22:16

@ChayimFriedman Я так не думаю; Я попробовал, но он отклонен (см. ответ ниже).

prog-fh 25.03.2024 22:24

@prog-fh Ах да.

Chayim Friedman 25.03.2024 22:27
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
2
8
118
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

extern эквивалентно extern "C", что означает, что ABI этой функции будет использовать общий ABI платформы (да, я отказываюсь называть его C ABI).

no_mangle преследует две цели: сначала отключить mangle, то есть преобразовать имя функции. И он пометит эту функцию как «экспортировать». Да, возможно, это сбивает с толку. Но no_mangle можно прочитать как export.

Узнать больше:

Точнее, публичные функции всегда экспортируются, но #[no_mangle] помечает функцию как «используемую», поэтому компилятор не удалит ее только потому, что на нее нет ссылки в коде.

Chayim Friedman 25.03.2024 21:19
Ответ принят как подходящий

В ссылке говорится о no_mangle , что он явно отключает искажение имен, но, кроме того, он аналогичен атрибуту Used.
Однако used применяется только к статическим элементам, а не к функциям, как показано в примере ниже.

Тогда единственный способ экспортировать вашу функцию — отключить искажение имен (что, на мой взгляд, разумно).

fn this_is_invisible(n: i32) -> i32 {
    n * 2 + 1
}

pub fn this_is_also_invisible(n: i32) -> i32 {
    n * 2 + 1
}

#[no_mangle]
fn this_is_accepted(n: i32) -> i32 {
    n * 2 + 1
}

/*
// ERROR: attribute must be applied to a `static` variable
#[used]
fn this_is_rejected(n: i32) -> i32 {
    n * 2 + 1
}
*/
$ nm target/debug/libmy_project.so | grep this_is
0000000000006e00 T this_is_accepted

Другие вопросы по теме