summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShravan Narayan <shravanrn@gmail.com>2024-07-15 17:06:24 -0500
committerShravan Narayan <shravanrn@gmail.com>2024-09-06 16:48:31 -0500
commitc6853eb4857908072fd1d7c28dffb1d2613a0ee6 (patch)
tree78ef4f5369ee821c94aa643f7b82ebca726d6ad4
parente3b16dae4166b138bbd04bbe67281f4fa47fd4de (diff)
downloadwabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.tar.gz
wabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.tar.bz2
wabt-c6853eb4857908072fd1d7c28dffb1d2613a0ee6.zip
wasm2c: Segue support for CPUs without wrgsbase instructions
-rw-r--r--src/c-writer.cc6
-rw-r--r--src/prebuilt/wasm2c_source_declarations.cc44
-rw-r--r--src/template/wasm2c.declarations.c28
-rw-r--r--test/wasm2c/add.txt28
-rw-r--r--test/wasm2c/check-imports.txt34
-rw-r--r--test/wasm2c/export-names.txt64
-rw-r--r--test/wasm2c/hello.txt40
-rw-r--r--test/wasm2c/minimal.txt28
-rw-r--r--test/wasm2c/tail-calls.txt28
-rw-r--r--wasm2c/examples/fac/fac.c76
-rw-r--r--wasm2c/wasm-rt-impl.c39
-rw-r--r--wasm2c/wasm-rt.h21
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