diff options
author | Shravan Narayan <shravanrn@gmail.com> | 2024-07-15 17:06:24 -0500 |
---|---|---|
committer | Shravan Narayan <shravanrn@gmail.com> | 2024-09-06 16:48:31 -0500 |
commit | c6853eb4857908072fd1d7c28dffb1d2613a0ee6 (patch) | |
tree | 78ef4f5369ee821c94aa643f7b82ebca726d6ad4 | |
parent | e3b16dae4166b138bbd04bbe67281f4fa47fd4de (diff) | |
download | wabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.tar.gz wabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.tar.bz2 wabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.zip |
wasm2c: Segue support for CPUs without wrgsbase instructions
-rw-r--r-- | src/c-writer.cc | 6 | ||||
-rw-r--r-- | src/prebuilt/wasm2c_source_declarations.cc | 44 | ||||
-rw-r--r-- | src/template/wasm2c.declarations.c | 28 | ||||
-rw-r--r-- | test/wasm2c/add.txt | 28 | ||||
-rw-r--r-- | test/wasm2c/check-imports.txt | 34 | ||||
-rw-r--r-- | test/wasm2c/export-names.txt | 64 | ||||
-rw-r--r-- | test/wasm2c/hello.txt | 40 | ||||
-rw-r--r-- | test/wasm2c/minimal.txt | 28 | ||||
-rw-r--r-- | test/wasm2c/tail-calls.txt | 28 | ||||
-rw-r--r-- | wasm2c/examples/fac/fac.c | 76 | ||||
-rw-r--r-- | wasm2c/wasm-rt-impl.c | 39 | ||||
-rw-r--r-- | wasm2c/wasm-rt.h | 21 |
12 files changed, 334 insertions, 102 deletions
diff --git a/src/c-writer.cc b/src/c-writer.cc index 1a091957..986ace55 100644 --- a/src/c-writer.cc +++ b/src/c-writer.cc @@ -2449,17 +2449,17 @@ bool CWriter::IsSingleUnsharedMemory() { void CWriter::InstallSegueBase(Memory* memory, bool save_old_value) { NonIndented([&] { Write("#if WASM_RT_USE_SEGUE", Newline()); }); if (save_old_value) { - Write("uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE();", Newline()); + Write("void* segue_saved_base = wasm_rt_segue_read_base();", Newline()); } auto primary_memory = ExternalInstanceRef(ModuleFieldType::Memory, memory->name); - Write("WASM_RT_SEGUE_WRITE_BASE(", primary_memory, ".data);", Newline()); + Write("wasm_rt_segue_write_base(", primary_memory, ".data);", Newline()); NonIndented([&] { Write("#endif", Newline()); }); } void CWriter::RestoreSegueBase() { NonIndented([&] { Write("#if WASM_RT_USE_SEGUE", Newline()); }); - Write("WASM_RT_SEGUE_WRITE_BASE(segue_saved_base);", Newline()); + Write("wasm_rt_segue_write_base(segue_saved_base);", Newline()); NonIndented([&] { Write("#endif", Newline()); }); } diff --git a/src/prebuilt/wasm2c_source_declarations.cc b/src/prebuilt/wasm2c_source_declarations.cc index 11aac524..000b25c8 100644 --- a/src/prebuilt/wasm2c_source_declarations.cc +++ b/src/prebuilt/wasm2c_source_declarations.cc @@ -64,15 +64,25 @@ R"w2c_template(// "address namespaces". GCC does not support the memcpy requ )w2c_template" R"w2c_template(// this leaves only clang for now. )w2c_template" -R"w2c_template(// (5) The OS doesn't replace the segment register on context switch which +R"w2c_template(// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +)w2c_template" +R"w2c_template(// or the implementation has to use a syscall for this. +)w2c_template" +R"w2c_template(// (6) The OS doesn't replace the segment register on context switch which )w2c_template" R"w2c_template(// eliminates windows for now )w2c_template" +R"w2c_template(// +)w2c_template" +R"w2c_template(// While more OS can be supported in the future, we only support linux for now +)w2c_template" R"w2c_template(#if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ )w2c_template" R"w2c_template( (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ )w2c_template" -R"w2c_template( __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) +R"w2c_template( __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ +)w2c_template" +R"w2c_template( !defined(_WIN32) && defined(__linux__) )w2c_template" R"w2c_template(#define WASM_RT_USE_SEGUE 1 )w2c_template" @@ -89,11 +99,33 @@ R"w2c_template( )w2c_template" R"w2c_template(// POSIX uses FS for TLS, GS is free )w2c_template" -R"w2c_template(#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() +R"w2c_template(static inline void* wasm_rt_segue_read_base() { )w2c_template" -R"w2c_template(#define WASM_RT_SEGUE_WRITE_BASE(base) \ +R"w2c_template( if (wasm_rt_fsgsbase_inst_supported) { +)w2c_template" +R"w2c_template( return (void*)__builtin_ia32_rdgsbase64(); +)w2c_template" +R"w2c_template( } else { +)w2c_template" +R"w2c_template( return wasm_rt_syscall_get_segue_base(); +)w2c_template" +R"w2c_template( } )w2c_template" -R"w2c_template( __builtin_ia32_wrgsbase64((uintptr_t)base) +R"w2c_template(} +)w2c_template" +R"w2c_template(static inline void wasm_rt_segue_write_base(void* base) { +)w2c_template" +R"w2c_template( if (wasm_rt_fsgsbase_inst_supported) { +)w2c_template" +R"w2c_template( __builtin_ia32_wrgsbase64((uintptr_t)base); +)w2c_template" +R"w2c_template( } else { +)w2c_template" +R"w2c_template( wasm_rt_syscall_set_segue_base(base); +)w2c_template" +R"w2c_template( } +)w2c_template" +R"w2c_template(} )w2c_template" R"w2c_template(#define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) )w2c_template" @@ -194,7 +226,7 @@ R"w2c_template(#include <stdio.h> )w2c_template" R"w2c_template(#define WASM_RT_CHECK_BASE(mem) \ )w2c_template" -R"w2c_template( if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ +R"w2c_template( if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ )w2c_template" R"w2c_template( puts("Segment register mismatch\n"); \ )w2c_template" diff --git a/src/template/wasm2c.declarations.c b/src/template/wasm2c.declarations.c index 5261a25b..efe9dbbe 100644 --- a/src/template/wasm2c.declarations.c +++ b/src/template/wasm2c.declarations.c @@ -32,11 +32,16 @@ // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -45,9 +50,20 @@ #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -103,7 +119,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } diff --git a/test/wasm2c/add.txt b/test/wasm2c/add.txt index ffb27f59..7a576d9c 100644 --- a/test/wasm2c/add.txt +++ b/test/wasm2c/add.txt @@ -99,11 +99,16 @@ u32 w2c_test_add(w2c_test*, u32, u32); // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -112,9 +117,20 @@ u32 w2c_test_add(w2c_test*, u32, u32); #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -170,7 +186,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } diff --git a/test/wasm2c/check-imports.txt b/test/wasm2c/check-imports.txt index caa5dc24..a5d3ae61 100644 --- a/test/wasm2c/check-imports.txt +++ b/test/wasm2c/check-imports.txt @@ -123,11 +123,16 @@ extern const u8 wasm2c_test_is64_env_0x5F_linear_memory; // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -136,9 +141,20 @@ extern const u8 wasm2c_test_is64_env_0x5F_linear_memory; #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -194,7 +210,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } @@ -809,12 +825,12 @@ void wasm2c_test_instantiate(w2c_test* instance, struct w2c_env* w2c_env_instanc init_tables(instance); init_memories(instance); #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_env_0x5F_linear_memory).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_env_0x5F_linear_memory).data); #endif init_elem_instances(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } diff --git a/test/wasm2c/export-names.txt b/test/wasm2c/export-names.txt index 7c4e6eda..f5762a19 100644 --- a/test/wasm2c/export-names.txt +++ b/test/wasm2c/export-names.txt @@ -123,11 +123,16 @@ void w2c_test_0xE20x9D0xA40xEF0xB80x8F(w2c_test*); // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -136,9 +141,20 @@ void w2c_test_0xE20x9D0xA40xEF0xB80x8F(w2c_test*); #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -194,7 +210,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } @@ -780,60 +796,60 @@ static void init_memories(w2c_test* instance) { /* export: '' */ void w2c_test_(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif w2c_test__0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } /* export: '*\2F' */ void w2c_test_0x2A0x2F(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif w2c_test__0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } /* export: '\3F\3F\2F' */ void w2c_test_0x3F0x3F0x2F(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif w2c_test__0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } /* export: '\0A' */ void w2c_test_0x0A(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif w2c_test__0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } /* export: '\E2\9D\A4\EF\B8\8F' */ void w2c_test_0xE20x9D0xA40xEF0xB80x8F(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif w2c_test__0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } @@ -850,11 +866,11 @@ void wasm2c_test_instantiate(w2c_test* instance, struct w2c_0x5Cmodule* w2c_0x5C init_instance_import(instance, w2c_0x5Cmodule_instance); init_memories(instance); #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base((*instance->w2c_0x5Cmodule_import0x200x2A0x2F).data); #endif #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } diff --git a/test/wasm2c/hello.txt b/test/wasm2c/hello.txt index 77608b6c..a9beeede 100644 --- a/test/wasm2c/hello.txt +++ b/test/wasm2c/hello.txt @@ -131,11 +131,16 @@ void w2c_test_0x5Fstart(w2c_test*); // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -144,9 +149,20 @@ void w2c_test_0x5Fstart(w2c_test*); #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -202,7 +218,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } @@ -817,12 +833,12 @@ wasm_rt_memory_t* w2c_test_memory(w2c_test* instance) { /* export: '_start' */ void w2c_test_0x5Fstart(w2c_test* instance) { #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE(instance->w2c_memory.data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base(instance->w2c_memory.data); #endif w2c_test_0x5Fstart_0(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } @@ -836,13 +852,13 @@ void wasm2c_test_instantiate(w2c_test* instance, struct w2c_wasi__snapshot__prev init_tables(instance); init_memories(instance); #if WASM_RT_USE_SEGUE - uintptr_t segue_saved_base = WASM_RT_SEGUE_READ_BASE(); - WASM_RT_SEGUE_WRITE_BASE(instance->w2c_memory.data); + void* segue_saved_base = wasm_rt_segue_read_base(); + wasm_rt_segue_write_base(instance->w2c_memory.data); #endif init_elem_instances(instance); init_data_instances(instance); #if WASM_RT_USE_SEGUE - WASM_RT_SEGUE_WRITE_BASE(segue_saved_base); + wasm_rt_segue_write_base(segue_saved_base); #endif } diff --git a/test/wasm2c/minimal.txt b/test/wasm2c/minimal.txt index e22e3662..3fb8ad8b 100644 --- a/test/wasm2c/minimal.txt +++ b/test/wasm2c/minimal.txt @@ -93,11 +93,16 @@ wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t res // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -106,9 +111,20 @@ wasm_rt_func_type_t wasm2c_test_get_func_type(uint32_t param_count, uint32_t res #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -164,7 +180,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } diff --git a/test/wasm2c/tail-calls.txt b/test/wasm2c/tail-calls.txt index dd97badf..c0e940f4 100644 --- a/test/wasm2c/tail-calls.txt +++ b/test/wasm2c/tail-calls.txt @@ -123,11 +123,16 @@ void wasm_tailcall_w2c_test_tailcaller(void **instance_ptr, void *tail_call_stac // for accessing pointers, and supports memcpy on pointers with custom // "address namespaces". GCC does not support the memcpy requirement, so // this leaves only clang for now. -// (5) The OS doesn't replace the segment register on context switch which +// (5) The OS provides a way to query if (rd|wr)gsbase is allowed by the kernel +// or the implementation has to use a syscall for this. +// (6) The OS doesn't replace the segment register on context switch which // eliminates windows for now +// +// While more OS can be supported in the future, we only support linux for now #if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ - __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && \ + !defined(_WIN32) && defined(__linux__) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -136,9 +141,20 @@ void wasm_tailcall_w2c_test_tailcaller(void **instance_ptr, void *tail_call_stac #if WASM_RT_USE_SEGUE // POSIX uses FS for TLS, GS is free -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + return wasm_rt_syscall_get_segue_base(); + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + wasm_rt_syscall_set_segue_base(base); + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) @@ -194,7 +210,7 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, #if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS #include <stdio.h> #define WASM_RT_CHECK_BASE(mem) \ - if (((uintptr_t)((mem)->data)) != ((uintptr_t)WASM_RT_SEGUE_READ_BASE())) { \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ puts("Segment register mismatch\n"); \ abort(); \ } diff --git a/wasm2c/examples/fac/fac.c b/wasm2c/examples/fac/fac.c index d9f821df..e253cc2a 100644 --- a/wasm2c/examples/fac/fac.c +++ b/wasm2c/examples/fac/fac.c @@ -47,14 +47,15 @@ // (1) Segue is allowed using WASM_RT_ALLOW_SEGUE // (2) on x86_64 without WABT_BIG_ENDIAN enabled // (3) the Wasm module uses a single unshared imported or exported memory -// (4) the compiler supports: intrinsics for (rd|wr)(fs|gs)base, "address -// namespaces" for accessing pointers, and supports memcpy on pointers with -// custom "address namespaces". GCC does not support the memcpy requirement, -// so this leaves only clang for now. -#if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ - (defined(__x86_64__) || defined(_M_X64)) && \ - WASM_RT_MODULE_IS_SINGLE_UNSHARED_MEMORY && __clang__ && \ - __has_builtin(__builtin_ia32_wrgsbase64) +// (4) the compiler supports: intrinsics for (rd|wr)gsbase, "address namespaces" +// for accessing pointers, and supports memcpy on pointers with custom +// "address namespaces". GCC does not support the memcpy requirement, so +// this leaves only clang for now. +// (5) The OS doesn't replace the segment register on context switch which +// eliminates windows for now +#if WASM_RT_ALLOW_SEGUE && !WABT_BIG_ENDIAN && \ + (defined(__x86_64__) || defined(_M_X64)) && IS_SINGLE_UNSHARED_MEMORY && \ + __clang__ && __has_builtin(__builtin_ia32_wrgsbase64) && !defined(_WIN32) #define WASM_RT_USE_SEGUE 1 #else #define WASM_RT_USE_SEGUE 0 @@ -62,21 +63,35 @@ #endif #if WASM_RT_USE_SEGUE -// Different segments are free on different platforms -// Windows uses GS for TLS, FS is free -// Linux uses FS for TLS, GS is free -#if defined(__WIN32) -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdfsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrfsbase64((uintptr_t)base) -#define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_fs*)(uintptr_t)addr) -#else -// POSIX style OS -#define WASM_RT_SEGUE_READ_BASE() __builtin_ia32_rdgsbase64() -#define WASM_RT_SEGUE_WRITE_BASE(base) \ - __builtin_ia32_wrgsbase64((uintptr_t)base) +// POSIX uses FS for TLS, GS is free +#include <asm/prctl.h> +#include <stdio.h> +#include <sys/syscall.h> +#include <unistd.h> + +static inline void* wasm_rt_segue_read_base() { + if (wasm_rt_fsgsbase_inst_supported) { + return (void*)__builtin_ia32_rdgsbase64(); + } else { + void* base; + if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) { + perror("Syscall SYS_arch_prctl error"); + abort(); + } + return base; + } +} +static inline void wasm_rt_segue_write_base(void* base) { + if (wasm_rt_fsgsbase_inst_supported) { + __builtin_ia32_wrgsbase64((uintptr_t)base); + } else { + if (syscall(SYS_arch_prctl, ARCH_SET_GS, (uintptr_t)base) != 0) { + perror("Syscall SYS_arch_prctl error"); + abort(); + } + } +} #define MEM_ADDR_MEMOP(mem, addr, n) ((uint8_t __seg_gs*)(uintptr_t)addr) -#endif #else #define MEM_ADDR_MEMOP(mem, addr, n) MEM_ADDR(mem, addr, n) #endif @@ -128,10 +143,23 @@ static inline bool func_types_eq(const wasm_rt_func_type_t a, TRAP(OOB); #endif +#if WASM_RT_USE_SEGUE && WASM_RT_SANITY_CHECKS +#include <stdio.h> +#define WASM_RT_CHECK_BASE(mem) \ + if (((uintptr_t)((mem)->data)) != ((uintptr_t)wasm_rt_segue_read_base())) { \ + puts("Segment register mismatch\n"); \ + abort(); \ + } +#else +#define WASM_RT_CHECK_BASE(mem) +#endif + #if WASM_RT_MEMCHECK_GUARD_PAGES -#define MEMCHECK(mem, a, t) +#define MEMCHECK(mem, a, t) WASM_RT_CHECK_BASE(mem); #else -#define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, sizeof(t)) +#define MEMCHECK(mem, a, t) \ + WASM_RT_CHECK_BASE(mem); \ + RANGE_CHECK(mem, a, sizeof(t)) #endif #ifdef __GNUC__ diff --git a/wasm2c/wasm-rt-impl.c b/wasm2c/wasm-rt-impl.c index 52a42e85..851a4c5a 100644 --- a/wasm2c/wasm-rt-impl.c +++ b/wasm2c/wasm-rt-impl.c @@ -49,6 +49,19 @@ static void* g_sig_handler_handle = 0; #endif #endif +#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +// Currently Segue is used only for linux +#include <sys/auxv.h> +#ifdef __GLIBC__ +#include <gnu/libc-version.h> +#endif +bool wasm_rt_fsgsbase_inst_supported = false; + +#include <asm/prctl.h> // For ARCH_SET_GS +#include <sys/syscall.h> // For SYS_arch_prctl +#include <unistd.h> // For syscall +#endif + #if WASM_RT_STACK_DEPTH_COUNT WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; WASM_RT_THREAD_LOCAL uint32_t wasm_rt_saved_call_stack_depth; @@ -220,6 +233,15 @@ void wasm_rt_init(void) { os_install_signal_handler(); } #endif + +#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +#if defined(__GLIBC__) && __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 18 + // Check for support for userspace wrgsbase instructions + unsigned long val = getauxval(AT_HWCAP2); + wasm_rt_fsgsbase_inst_supported = val & (1 << 1); +#endif +#endif + assert(wasm_rt_is_initialized()); } @@ -257,6 +279,23 @@ void wasm_rt_free_thread(void) { #endif } +#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +void wasm_rt_syscall_set_segue_base(void* base) { + if (syscall(SYS_arch_prctl, ARCH_SET_GS, base) != 0) { + perror("wasm_rt_syscall_set_segue_base error"); + abort(); + } +} +void* wasm_rt_syscall_get_segue_base() { + void* base; + if (syscall(SYS_arch_prctl, ARCH_GET_GS, &base) != 0) { + perror("wasm_rt_syscall_get_segue_base error"); + abort(); + } + return base; +} +#endif + // Include table operations for funcref #define WASM_RT_TABLE_OPS_FUNCREF #include "wasm-rt-impl-tableops.inc" diff --git a/wasm2c/wasm-rt.h b/wasm2c/wasm-rt.h index 5c07ff44..c489c850 100644 --- a/wasm2c/wasm-rt.h +++ b/wasm2c/wasm-rt.h @@ -294,6 +294,27 @@ extern WASM_RT_THREAD_LOCAL uint32_t wasm_rt_call_stack_depth; #endif +#if WASM_RT_USE_SEGUE || WASM_RT_ALLOW_SEGUE +/** + * The segue optimization uses x86 segments to point to a linear memory. If + * used, the runtime must query whether it can use the fast userspace wrgsbase + * instructions or whether it must invoke syscalls to set the segment base, + * depending on the supported CPU features. The result of this query is saved in + * this variable. + */ +extern bool wasm_rt_fsgsbase_inst_supported; +/** + * If fast userspace wrgsbase instructions don't exist, the runtime most provide + * a function that invokes the OS' underlying syscall to set the segment base. + */ +void wasm_rt_syscall_set_segue_base(void* base); +/** + * If fast userspace rdgsbase instructions don't exist, the runtime most provide + * a function that invokes the OS' underlying syscall to get the segment base. + */ +void* wasm_rt_syscall_get_segue_base(); +#endif + #if defined(_MSC_VER) #define WASM_RT_NO_RETURN __declspec(noreturn) #else |