@Wibowit:
Tak, mniej więcej o to. Ogólnie chodzi o możliwości łączenia kodu napisanego w różnych językach źródłowych, by nie okazało się, że niby kompilujemy wszystko do WASMa, ale integracja jest tylko w jedną stronę albo w ogóle w żadną (tzn. cały kod i biblioteki używane w aplikacji muszą być napisane w jednym języku źródłowym).
Z uwagi na to że nie chce mi się reverse engineerować Blazora, to wygenerowałem WASMA z C:
emcc hello.c -s WASM=1 -o hello.html
#include <stdio.h>
int test_xiao() // to się niewexportowało bo zapomniałem dodać param -s EXPORT_ALL=1
{
return 123;
}
int main() {
return 1234;
}
dla którego WASM 2 WAT wygląda tak:
(module
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(type (;2;) (func (param i32) (result i32)))
(type (;3;) (func))
(type (;4;) (func (param i32 i32) (result i32)))
(type (;5;) (func (param i32 i32 i32) (result i32)))
(type (;6;) (func (param i32 i64 i32) (result i64)))
(func (;0;) (type 3)
call 6)
(func (;1;) (type 0) (result i32)
(local i32 i32 i32 i32 i32)
global.get 0
local.set 0
i32.const 16
local.set 1
local.get 0
local.get 1
i32.sub
local.set 2
i32.const 0
local.set 3
local.get 2
local.get 3
i32.store offset=12
i32.const 1234
local.set 4
local.get 4
return)
(func (;2;) (type 4) (param i32 i32) (result i32)
(local i32)
call 1
local.set 2
local.get 2
return)
(func (;3;) (type 0) (result i32)
global.get 0)
(func (;4;) (type 1) (param i32)
local.get 0
global.set 0)
(func (;5;) (type 2) (param i32) (result i32)
(local i32 i32)
global.get 0
local.get 0
i32.sub
i32.const -16
i32.and
local.tee 1
global.set 0
local.get 1)
(func (;6;) (type 3)
i32.const 5243936
global.set 2
i32.const 1044
i32.const 15
i32.add
i32.const -16
i32.and
global.set 1)
(func (;7;) (type 0) (result i32)
global.get 0
global.get 1
i32.sub)
(func (;8;) (type 0) (result i32)
global.get 1)
(func (;9;) (type 2) (param i32) (result i32)
i32.const 1)
(func (;10;) (type 1) (param i32))
(func (;11;) (type 1) (param i32))
(func (;12;) (type 1) (param i32))
(func (;13;) (type 0) (result i32)
i32.const 1024
call 11
i32.const 1032)
(func (;14;) (type 3)
i32.const 1024
call 12)
(func (;15;) (type 2) (param i32) (result i32)
(local i32 i32)
block ;; label = @1
block ;; label = @2
local.get 0
i32.eqz
br_if 0 (;@2;)
block ;; label = @3
local.get 0
i32.load offset=76
i32.const -1
i32.gt_s
br_if 0 (;@3;)
local.get 0
call 16
return
end
local.get 0
call 9
local.set 1
local.get 0
call 16
local.set 2
local.get 1
i32.eqz
br_if 1 (;@1;)
local.get 0
call 10
local.get 2
return
end
i32.const 0
local.set 2
block ;; label = @2
i32.const 0
i32.load offset=1036
i32.eqz
br_if 0 (;@2;)
i32.const 0
i32.load offset=1036
call 15
local.set 2
end
block ;; label = @2
call 13
i32.load
local.tee 0
i32.eqz
br_if 0 (;@2;)
loop ;; label = @3
i32.const 0
local.set 1
block ;; label = @4
local.get 0
i32.load offset=76
i32.const 0
i32.lt_s
br_if 0 (;@4;)
local.get 0
call 9
local.set 1
end
block ;; label = @4
local.get 0
i32.load offset=20
local.get 0
i32.load offset=28
i32.le_u
br_if 0 (;@4;)
local.get 0
call 16
local.get 2
i32.or
local.set 2
end
block ;; label = @4
local.get 1
i32.eqz
br_if 0 (;@4;)
local.get 0
call 10
end
local.get 0
i32.load offset=56
local.tee 0
br_if 0 (;@3;)
end
end
call 14
end
local.get 2)
(func (;16;) (type 2) (param i32) (result i32)
(local i32 i32)
block ;; label = @1
local.get 0
i32.load offset=20
local.get 0
i32.load offset=28
i32.le_u
br_if 0 (;@1;)
local.get 0
i32.const 0
i32.const 0
local.get 0
i32.load offset=36
call_indirect (type 5)
drop
local.get 0
i32.load offset=20
br_if 0 (;@1;)
i32.const -1
return
end
block ;; label = @1
local.get 0
i32.load offset=4
local.tee 1
local.get 0
i32.load offset=8
local.tee 2
i32.ge_u
br_if 0 (;@1;)
local.get 0
local.get 1
local.get 2
i32.sub
i64.extend_i32_s
i32.const 1
local.get 0
i32.load offset=40
call_indirect (type 6)
drop
end
local.get 0
i32.const 0
i32.store offset=28
local.get 0
i64.const 0
i64.store offset=16
local.get 0
i64.const 0
i64.store offset=4 align=4
i32.const 0)
(func (;17;) (type 0) (result i32)
i32.const 1040)
(table (;0;) 1 1 funcref)
(memory (;0;) 256 256)
(global (;0;) (mut i32) (i32.const 5243936))
(global (;1;) (mut i32) (i32.const 0))
(global (;2;) (mut i32) (i32.const 0))
(export "memory" (memory 0))
(export "__wasm_call_ctors" (func 0))
(export "main" (func 2))
(export "__indirect_function_table" (table 0))
(export "fflush" (func 15))
(export "__errno_location" (func 17))
(export "stackSave" (func 3))
(export "stackRestore" (func 4))
(export "stackAlloc" (func 5))
(export "emscripten_stack_init" (func 6))
(export "emscripten_stack_get_free" (func 7))
(export "emscripten_stack_get_end" (func 8)))
oraz drugi programik LLVM IR:
; ModuleID = 'main.ll'
source_filename = "main.ll"
; Function Attrs: nofree norecurse nosync nounwind readnone willreturn mustprogress
define dso_local i32 @Test(i32 %0, i32 %1) local_unnamed_addr #0 {
ret i32 5
}
; Function Attrs: nofree norecurse nosync nounwind readnone willreturn mustprogress
define dso_local i32 @Test2() local_unnamed_addr #0 {
ret i32 23
}
attributes #0 = { nofree norecurse nosync nounwind readnone willreturn mustprogress }
WASM 2 WAT:
(module
(type (;0;) (func))
(type (;1;) (func (param i32 i32) (result i32)))
(type (;2;) (func (result i32)))
(func $__wasm_call_ctors (type 0))
(func $Test (type 1) (param i32 i32) (result i32)
i32.const 5)
(func $Test2 (type 2) (result i32)
i32.const 23)
(memory (;0;) 2)
(global (;0;) (mut i32) (i32.const 66560))
(global (;1;) i32 (i32.const 1024))
(global (;2;) i32 (i32.const 1024))
(global (;3;) i32 (i32.const 1024))
(global (;4;) i32 (i32.const 66560))
(global (;5;) i32 (i32.const 0))
(global (;6;) i32 (i32.const 1))
(export "memory" (memory 0))
(export "__wasm_call_ctors" (func $__wasm_call_ctors))
(export "Test" (func $Test))
(export "Test2" (func $Test2))
(export "__dso_handle" (global 1))
(export "__data_end" (global 2))
(export "__global_base" (global 3))
(export "__heap_base" (global 4))
(export "__memory_base" (global 5))
(export "__table_base" (global 6)))
No to jak się przyjrzysz na sam format tego WebAssemblyTextFormat, no to widać że łatwo to można złączyć, co akurat przetestowałem z ręki:
(module
(type (;0;) (func (result i32)))
(type (;1;) (func (param i32)))
(type (;2;) (func (param i32) (result i32)))
(type (;3;) (func))
(type (;4;) (func (param i32 i32) (result i32)))
(type (;5;) (func (param i32 i32 i32) (result i32)))
(type (;6;) (func (param i32 i64 i32) (result i64)))
(type (;7;) (func))
(type (;8;) (func (param i32 i32) (result i32)))
(type (;9;) (func (result i32)))
(func (;0;) (type 3)
call 6)
(func (;1;) (type 0) (result i32)
(local i32 i32 i32 i32 i32)
global.get 0
local.set 0
i32.const 16
local.set 1
local.get 0
local.get 1
i32.sub
local.set 2
i32.const 0
local.set 3
local.get 2
local.get 3
i32.store offset=12
i32.const 1234
local.set 4
local.get 4
return)
(func (;2;) (type 4) (param i32 i32) (result i32)
(local i32)
call 1
local.set 2
local.get 2
return)
(func (;3;) (type 0) (result i32)
global.get 0)
(func (;4;) (type 1) (param i32)
local.get 0
global.set 0)
(func (;5;) (type 2) (param i32) (result i32)
(local i32 i32)
global.get 0
local.get 0
i32.sub
i32.const -16
i32.and
local.tee 1
global.set 0
local.get 1)
(func (;6;) (type 3)
i32.const 5243936
global.set 2
i32.const 1044
i32.const 15
i32.add
i32.const -16
i32.and
global.set 1)
(func (;7;) (type 0) (result i32)
global.get 0
global.get 1
i32.sub)
(func (;8;) (type 0) (result i32)
global.get 1)
(func (;9;) (type 2) (param i32) (result i32)
i32.const 1)
(func (;10;) (type 1) (param i32))
(func (;11;) (type 1) (param i32))
(func (;12;) (type 1) (param i32))
(func (;13;) (type 0) (result i32)
i32.const 1024
call 11
i32.const 1032)
(func (;14;) (type 3)
i32.const 1024
call 12)
(func (;15;) (type 2) (param i32) (result i32)
(local i32 i32)
block ;; label = @1
block ;; label = @2
local.get 0
i32.eqz
br_if 0 (;@2;)
block ;; label = @3
local.get 0
i32.load offset=76
i32.const -1
i32.gt_s
br_if 0 (;@3;)
local.get 0
call 16
return
end
local.get 0
call 9
local.set 1
local.get 0
call 16
local.set 2
local.get 1
i32.eqz
br_if 1 (;@1;)
local.get 0
call 10
local.get 2
return
end
i32.const 0
local.set 2
block ;; label = @2
i32.const 0
i32.load offset=1036
i32.eqz
br_if 0 (;@2;)
i32.const 0
i32.load offset=1036
call 15
local.set 2
end
block ;; label = @2
call 13
i32.load
local.tee 0
i32.eqz
br_if 0 (;@2;)
loop ;; label = @3
i32.const 0
local.set 1
block ;; label = @4
local.get 0
i32.load offset=76
i32.const 0
i32.lt_s
br_if 0 (;@4;)
local.get 0
call 9
local.set 1
end
block ;; label = @4
local.get 0
i32.load offset=20
local.get 0
i32.load offset=28
i32.le_u
br_if 0 (;@4;)
local.get 0
call 16
local.get 2
i32.or
local.set 2
end
block ;; label = @4
local.get 1
i32.eqz
br_if 0 (;@4;)
local.get 0
call 10
end
local.get 0
i32.load offset=56
local.tee 0
br_if 0 (;@3;)
end
end
call 14
end
local.get 2)
(func (;16;) (type 2) (param i32) (result i32)
(local i32 i32)
block ;; label = @1
local.get 0
i32.load offset=20
local.get 0
i32.load offset=28
i32.le_u
br_if 0 (;@1;)
local.get 0
i32.const 0
i32.const 0
local.get 0
i32.load offset=36
call_indirect (type 5)
drop
local.get 0
i32.load offset=20
br_if 0 (;@1;)
i32.const -1
return
end
block ;; label = @1
local.get 0
i32.load offset=4
local.tee 1
local.get 0
i32.load offset=8
local.tee 2
i32.ge_u
br_if 0 (;@1;)
local.get 0
local.get 1
local.get 2
i32.sub
i64.extend_i32_s
i32.const 1
local.get 0
i32.load offset=40
call_indirect (type 6)
drop
end
local.get 0
i32.const 0
i32.store offset=28
local.get 0
i64.const 0
i64.store offset=16
local.get 0
i64.const 0
i64.store offset=4 align=4
i32.const 0)
(func (;17;) (type 0) (result i32)
i32.const 1040)
(func $__wasm_call_ctors (type 7))
(func $Test (type 8) (param i32 i32) (result i32)
i32.const 5)
(func $Test2 (type 9) (result i32)
i32.const 23)
(table (;0;) 1 1 funcref)
(memory (;0;) 256 256)
(global (;0;) (mut i32) (i32.const 5243936))
(global (;1;) (mut i32) (i32.const 0))
(global (;2;) (mut i32) (i32.const 0))
(global (;3;) (mut i32) (i32.const 66560))
(global (;4;) i32 (i32.const 1024))
(global (;5;) i32 (i32.const 1024))
(global (;6;) i32 (i32.const 1024))
(global (;7;) i32 (i32.const 66560))
(global (;8;) i32 (i32.const 0))
(global (;9;) i32 (i32.const 1))
(export "memory" (memory 0))
(export "__wasm_call_ctors" (func 0))
(export "main" (func 2))
(export "__indirect_function_table" (table 0))
(export "fflush" (func 15))
(export "__errno_location" (func 17))
(export "stackSave" (func 3))
(export "stackRestore" (func 4))
(export "stackAlloc" (func 5))
(export "emscripten_stack_init" (func 6))
(export "emscripten_stack_get_free" (func 7))
(export "emscripten_stack_get_end" (func 8)))
(export "__wasm_call_ctors" (func $__wasm_call_ctors))
(export "Test" (func $Test))
(export "Test2" (func $Test2))
(export "__dso_handle" (global 3))
(export "__data_end" (global 4))
(export "__global_base" (global 5))
(export "__heap_base" (global 6))
(export "__memory_base" (global 7))
(export "__table_base" (global 8)))
I oto rezultat:
czyl idziała :)
No generalnie jeżeli ktoś/coś faktycznie generuje pure-wasm, a nie generuje wasm czegoś, co będzie mieliło kod (e.g Mono), to nie widzę problemu.