source

웹어셈블리로 컴파일된 러스트 라이브러리에서 C 라이브러리를 사용하려면 어떻게 해야 합니까?

nicesource 2023. 7. 28. 22:09
반응형

웹어셈블리로 컴파일된 러스트 라이브러리에서 C 라이브러리를 사용하려면 어떻게 해야 합니까?

저는 최종적으로 브라우저나 Node.js에서 Rust(정적 C 종속성 포함) 라이브러리를 사용하기 위해 Rust, WebAssembly 및 C 상호 운용성을 실험하고 있습니다.자바스크립트 글루 코드에 사용하고 있습니다.

#![feature(libc, use_extern_macros)]
extern crate wasm_bindgen;

use wasm_bindgen::prelude::*;
use std::os::raw::c_char;
use std::ffi::CStr;

extern "C" {
    fn hello() -> *const c_char; // returns "hello from C" 
}

#[wasm_bindgen]
pub fn greet() -> String {
    let c_msg = unsafe { CStr::from_ptr(hello()) };
    format!("{} and Rust!", c_msg.to_str().unwrap())
}

나의 첫 번째 순진한 접근법은build.rsC 코드에서 정적 라이브러리를 생성하기 위해 gcc crate를 사용하는 스크립트입니다.비트를 하여 WASM 트비전러프에스컴다로다니볼있습었수음을고파하일을을 볼 수 .hello from C에서 " , 터컴파로다음같과발다오니생합류가은러일"라는 오류가 나타납니다.

rust-lld: error: unknown file type: hello.o

build.rs

extern crate gcc;                                                                                         

fn main() {
    gcc::Build::new()
        .file("src/hello.c")
        .compile("libhello.a");
}

지금 생각해보면, 이건 말이 돼요, 그 이후로.hello.o파일이 WebAssembly가 아닌 내 노트북의 아키텍처용으로 컴파일되었습니다.

이상적으로 저는 이것이 즉시 작동하여 제 build.rs 에 마법을 추가하고 싶습니다. 예를 들어 C 라이브러리를 Rust가 사용할 수 있는 정적 웹 어셈블리 라이브러리로 컴파일할 수 있습니다.

제가 생각하기에 그것이 효과가 있을 수 있지만 더 문제가 있어 보이고 싶지 않은 것은 Emscripten을 사용하여 C 코드에 대한 WASM 라이브러리를 만든 다음 Rust 라이브러리를 별도로 컴파일하고 JavaScript에서 함께 접착하는 것입니다.

TL;DR: "C와 러스트에서 안녕!"을 받으려면 "새로운 주, 새로운 모험"으로 점프하십시오.

좋은 방법은 WASM 라이브러리를 만들어 링커에 전달하는 것입니다.rustc에 대한 옵션이 있습니다(소스 코드 지시어도 있는 것 같습니다).

rustc <yourcode.rs> --target wasm32-unknown-unknown --crate-type=cdylib -C link-arg=<library.wasm>

에 도서관에 도서관이 한다는 입니다.reloc )linking섹션을 선택합니다.엠스크리펜은 그것에 대한 상징을 가지고 있는 것 같습니다.RELOCATABLE:

emcc <something.c> -s WASM=1 -s SIDE_MODULE=1 -s RELOCATABLE=1 -s EMULATED_FUNCTION_POINTERS=1 -s ONLY_MY_CODE=1 -o <something.wasm>

(EMULATED_FUNCTION_POINTERS 포함되어 .RELOCATABLE그래서 그것은 정말로 필요하지 않습니다.ONLY_MY_CODE엑스트라를 하지 않습니다.)

한 것은, 한건요중,,emcc 가능한 이전가파생않지았다습니하성을일한능▁a를 생성하지 않았습니다.wasm적어도 이번 주에 다운로드한 버전이 아닌 Windows용 파일입니다(후회적으로 최선의 아이디어는 아니었을 수도 있는 어려운 문제로 재생했습니다). 그서섹누었고되락이션래,고▁so▁are▁sections었▁missing.rustc에 대해 계속 불평합니다.<something.wasm> is not a relocatable wasm file.

다음에 다에음그가 나옵니다.clang 가능한 재치가파생수있성다니습할일을 할 수 .wasm매우 간단한 원라이너가 있는 모듈:

clang -c <something.c> -o <something.wasm> --target=wasm32-unknown-unknown

그리고나서rustc하위 섹션 연결이 너무 일찍 종료되었습니다.아, 네. (그런데, 제 러스트 설정도 새로웠습니다.)그럼 두 개가 있다는 걸 읽었습니다.clang wasm 대상:wasm32-unknown-unknown-wasm그리고.wasm32-unknown-unknown-elf그리고 아마도 후자는 여기서 사용되어야 할 것입니다.나의 또한 새것으로.llvm+clang빌드에서 이 대상에 대한 내부 오류가 발생하여 개발자에게 오류 보고서를 보내달라고 요청합니다. *nix 또는 Mac 박스와 같이 쉬운 또는 중간에서 테스트해야 할 내용일 수 있습니다.

최소 성공 사례: 세 개의 숫자의 합계

이 시점에서 방금 추가했습니다.lldllvm비트코드 파일에서 테스트 코드를 수동으로 연결하는 데 성공했습니다.

clang cadd.c --target=wasm32-unknown-unknown -emit-llvm -c
rustc rsum.rs --target wasm32-unknown-unknown --crate-type=cdylib --emit llvm-bc
lld -flavor wasm rsum.bc cadd.bc -o msum.wasm --no-entry

아 네, 숫자를 합해서 2인치입니다.C의 경우 12: 녹의 경우 1+2:

캐드.c.

int cadd(int x,int y){
  return x+y;
}

msum.rs

extern "C" {
    fn cadd(x: i32, y: i32) -> i32;
}

#[no_mangle]
pub fn rsum(x: i32, y: i32, z: i32) -> i32 {
    x + unsafe { cadd(y, z) }
}

시험을 보다

<script>
  fetch('msum.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.compile(bytes))
    .then(module => {
      console.log(WebAssembly.Module.exports(module));
      console.log(WebAssembly.Module.imports(module));
      return WebAssembly.instantiate(module, {
        env:{
          _ZN4core9panicking5panic17hfbb77505dc622acdE:alert
        }
      });
    })
    .then(instance => {
      alert(instance.exports.rsum(13,14,15));
    });
</script>

ㅠㅠ_ZN4core9panicking5panic17hfbb77505dc622acdE매우 자연스럽게 느껴집니다(모듈은 내보내기 및 가져오기를 기록하기 위해 두 단계로 컴파일 및 인스턴스화됩니다. 이것이 바로 이러한 누락된 조각을 찾을 수 있는 방법입니다.). 런임라이리대한다참른없조작전다동니합작업이체때문타.으로 조롱할 수 .그리고 이 특정 방법은 수동으로 조롱/제공될 수 있습니다.

사이드 스토리: 문자열

as and itsLayout저는 약간 겁이 났습니다. 저는 여기나 Hello, Rust!와 같은 벡터 기반 접근법을 때때로 설명/사용했습니다.
여기 예가 있습니다. "안녕하세요...." 문자열을 가져오는 것입니다.

rhello.rs

use std::ffi::CStr;
use std::mem;
use std::os::raw::{c_char, c_void};
use std::ptr;

extern "C" {
    fn chello() -> *mut c_char;
}

#[no_mangle]
pub fn alloc(size: usize) -> *mut c_void {
    let mut buf = Vec::with_capacity(size);
    let p = buf.as_mut_ptr();
    mem::forget(buf);
    p as *mut c_void
}

#[no_mangle]
pub fn dealloc(p: *mut c_void, size: usize) {
    unsafe {
        let _ = Vec::from_raw_parts(p, 0, size);
    }
}

#[no_mangle]
pub fn hello() -> *mut c_char {
    let phello = unsafe { chello() };
    let c_msg = unsafe { CStr::from_ptr(phello) };
    let message = format!("{} and Rust!", c_msg.to_str().unwrap());
    dealloc(phello as *mut c_void, c_msg.to_bytes().len() + 1);
    let bytes = message.as_bytes();
    let len = message.len();
    let p = alloc(len + 1) as *mut u8;
    unsafe {
        for i in 0..len as isize {
            ptr::write(p.offset(i), bytes[i as usize]);
        }
        ptr::write(p.offset(len as isize), 0);
    }
    p as *mut c_char
}

다음으로 빌드됨rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib

그리고 실제로 함께 일하는 것.JavaScript:

안녕하세요.

<script>
  var e;
  fetch('rhello.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.compile(bytes))
    .then(module => {
      console.log(WebAssembly.Module.exports(module));
      console.log(WebAssembly.Module.imports(module));
      return WebAssembly.instantiate(module, {
        env:{
          chello:function(){
            var s="Hello from JavaScript";
            var p=e.alloc(s.length+1);
            var m=new Uint8Array(e.memory.buffer);
            for(var i=0;i<s.length;i++)
              m[p+i]=s.charCodeAt(i);
            m[s.length]=0;
            return p;
          }
        }
      });
    })
    .then(instance => {
      /*var*/ e=instance.exports;
      var ptr=e.hello();
      var optr=ptr;
      var m=new Uint8Array(e.memory.buffer);
      var s="";
      while(m[ptr]!=0)
        s+=String.fromCharCode(m[ptr++]);
      e.dealloc(optr,s.length+1);
      console.log(s);
    });
</script>

러스트에 ), 은 제가하는 것과 일을 .dealloc작동할 수 있습니다(최소한 두 번 이상 호출하면 패닉이 발생합니다).
. 할 때 될 수 . 모듈이 메모리를 관리할 때 모듈의 크기가 변경되어 백업이 무효화될 수 있습니다.ArrayBuffer개체 및 해당 보기.그래서 그런 이유야.memory.buffer여러 번 검사되고, 에 전화한 후 검사됩니다.wasm암호를

그리고 이것이 바로 제가 갇혀있는 곳입니다. 왜냐하면 이 코드는 런타임 라이브러리를 의미할 것이고,.rlib은 다음과 s. 수동 빌드에 가장 근접한 것은 다음과 같습니다.

rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib --emit obj
lld -flavor wasm rhello.o -o rhello.wasm --no-entry --allow-undefined
     liballoc-5235bf36189564a3.rlib liballoc_system-f0b9538845741d3e.rlib
     libcompiler_builtins-874d313336916306.rlib libcore-5725e7f9b84bd931.rlib
     libdlmalloc-fffd4efad67b62a4.rlib liblibc-453d825a151d7dec.rlib
     libpanic_abort-43290913ef2070ae.rlib libstd-dcc98be97614a8b6.rlib
     libunwind-8cd3b0417a81fb26.rlib

▁은▁i▁▁to던▁had를 사용해야 했던 곳.lld로서 러스트 툴체인의 깊은 곳에 앉아 있는 것..rlib-s는 해석된다고 하므로, 그것들은 다음과 같이 구속됩니다.Rust 공구체인

--crate-type=rlib,#[crate_type = "rlib"]"Rust 라이브러리" 파일이 생성됩니다.이것은 중간 아티팩트로 사용되며 "정적 러스트 라이브러리"라고 생각할 수 있습니다. 것들이.rlib 파, 리와 staticlibRust 컴파일러가 향후 링크에서 해석하는 파일입니다.이것은 본질적으로 다음을 의미합니다.rustc를 검색합니다.rlib동적 라이브러리에서 메타데이터를 찾는 것과 같은 파일.의 출력은 과 " " " 를 생성하는 데 됩니다.staticlib 출력

이 물론이은것은것▁of이.lld을 먹지 ..wasm/.oclang또는llc(" 일찍 종료되었습니다("" 결하섹너일종니다습었되료찍연다것할니입작다야해성따시라정부분의사")으로 다시 할 llvm.
또한 이 빌드에는 실제 할당자가 누락된 것 같습니다.chello이 더 입니다: 가오개테에 4의항더있습다니.__rust_alloc,__rust_alloc_zeroed,__rust_dealloc그리고.__rust_realloc될 수 메모리를 만 아니라 했습니다.rustc빌드...아, 네, 이번 주(2018년 8월 11일 21시 56분)에 포기한 곳입니다.

주, 새운주모험로, 운와함께리바너, 이로새와함,께▁new,wasm-dis/merge

아이디어는 이미 만들어진 러스트 코드(할당자 및 모든 것이 제자리에 있음)를 수정하는 것이었습니다.그리고 이것은 효과가 있습니다.C 코드에 데이터가 없는 한.

개념 증명 코드:

첼로

void *alloc(int len); // allocator comes from Rust

char *chello(){
  char *hell=alloc(13);
  hell[0]='H';
  hell[1]='e';
  hell[2]='l';
  hell[3]='l';
  hell[4]='o';
  hell[5]=' ';
  hell[6]='f';
  hell[7]='r';
  hell[8]='o';
  hell[9]='m';
  hell[10]=' ';
  hell[11]='C';
  hell[12]=0;
  return hell;
}

극히 일반적인 것은 아니지만, C 코드는 C 코드입니다.

rustc rhello.rs --target wasm32-unknown-unknown --crate-type=cdylib
wasm-dis rhello.wasm -o rhello.wast
clang chello.c --target=wasm32-unknown-unknown -nostdlib -Wl,--no-entry,--export=chello,--allow-undefined
wasm-dis a.out -o chello.wast
wasm-merge rhello.wast chello.wast -o mhello.wasm -O

(rhello.rs" story "side story: 스링에트것제동과일함시된사스토리이드에▁"")동▁is▁same▁▁in▁theside)")에서 제시된 과 동일한 것입니다.
그리고 그 결과는 다음과 같습니다.

안녕하세요.

<script>
  fetch('mhello.wasm')
    .then(response => response.arrayBuffer())
    .then(bytes => WebAssembly.compile(bytes))
    .then(module => {
      console.log(WebAssembly.Module.exports(module));
      console.log(WebAssembly.Module.imports(module));
      return WebAssembly.instantiate(module, {
        env:{
          memoryBase: 0,
          tableBase: 0
        }
      });
    })
    .then(instance => {
      var e=instance.exports;
      var ptr=e.hello();
      console.log(ptr);
      var optr=ptr;
      var m=new Uint8Array(e.memory.buffer);
      var s="";
      while(m[ptr]!=0)
        s+=String.fromCharCode(m[ptr++]);
      e.dealloc(optr,s.length+1);
      console.log(s);
    });
</script>

도 무언가를 것처럼 .ptr사용/사용하지 않고 반복된 블록의 판독치dealloc메모리 누수/누락이 발생하지 않는 방법을 보여줍니다.

물론 이것은 매우 깨지기 쉽고 신비로운 부분도 있습니다.

  • 을 최종병실경우할행으로 -S (switch 대신 사용).wasm).wasm-as는 몇 더 이러한 는 내보내기중인 ).
  • 병합 사항의 순서는 "녹원" 파일이 먼저 와야 합니다.wasm-merge chello.wast rhello.wast [...]재미있는 메시지와 함께 사망합니다.

    module] , on [segment offset wasm-computer error] false: 를 참조하십시오.
    [i32] (i32.const 1)
    유효성 중 오류 : 출력 유효성 검사 오류입니다.

  • 아마도 내 잘못이겠지만, 나는 완전한 것을 만들어야 했습니다.chello.wasm모듈(따라서 링크 있음)컴파일만(clang -c [...]는 이 이야기의 첫 에서 너무 , 그 중 으)로 분해했습니다..wast) 명명된 내보내기가 손실되었습니다(chello()):
    (export "chello" (func $chello)) 사라지다, 사라지다, 사라지다, 사라지다, 사라지다, 사라지다, 사라지다, 사라지다.
    (func $chello ... 되다(func $0 ...내부 함수(wasm-disreloc그리고.linking에 해당 ), 섹션, 섹션 및 섹션에 대한 설명만 삽입합니다.
  • 하여: 모듈의 이모듈 ) 는 " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "wasm-merge문자열 자체에 대한 참조를 잡을 수 있는 기회가 있는 동안(const char *HELLO="Hello from C";특히 오프셋 1024에서 상수가 되며, 나중에 다음과 같이 언급됩니다.(i32.const 1024)함수 내부의 국소 상수인 경우에는 발생하지 않습니다. 만약 상수라면, , , 그은 그고만그전상, 면그에가주, 장셋프도 1040저된숫고, 오자참같다것니 1040과고로 입니다.(i32.load offset=1040 [...]잡기가 어려워지기 시작합니다.

웃기려고, 이 코드는 컴파일되고 작동하기도 합니다.

void *alloc(int len);

int my_strlen(const char *ptr){
  int ret=0;
  while(*ptr++)ret++;
  return ret;
}

char *my_strcpy(char *dst,const char *src){
  char *ret=dst;
  while(*src)*dst++=*src++;
  *dst=0;
  return ret;
}

char *chello(){
  const char *HELLO="Hello from C";
  char *hell=alloc(my_strlen(HELLO)+1);
  return my_strcpy(hell,HELLO);
}

그냥 러스트의 메시지 풀 중간에 "Hello from C"라고 써서 출력이 됩니다.

Clt에서 안녕하세요:: "Err'an value and Rust!"에 대한 unwrap()입니다.

플래그 .)-O)
그리고 그것은 또한 그것을 찾는 것에 대한 질문을 제기합니다.libc▁(▁defining▁them않)my_,clang 급들strlen그리고.strcpy내장되어 있으며 정확한 특징을 알려주기 때문에 코드를 내보내지 않으며 결과 모듈에 대한 가져오기가 됩니다.)

언급URL : https://stackoverflow.com/questions/51666736/how-do-i-use-a-c-library-in-a-rust-library-compiled-to-webassembly

반응형