summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-02-12 10:09:12 +0800
committerGitHub <[email protected]>2026-02-12 10:09:12 +0800
commit9d084db557d256923fc7f8a5daf66b21709f4bc4 (patch)
tree80be799cf286994b09bc0bc00bb6cdf766029ec6
parent2f944ae3380afc0129baff3f944ce31ed1faf72e (diff)
parent37b506c86c3390d6a175ce37828e8defd44a2407 (diff)
Merge pull request #7312 from xmake-io/binutils
Improve binutils to support wasm
-rw-r--r--core/src/xmake/binutils/ar/prefix.h14
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c28
-rw-r--r--core/src/xmake/binutils/deplibs.c22
-rw-r--r--core/src/xmake/binutils/elf/prefix.h5
-rw-r--r--core/src/xmake/binutils/extractlib.c6
-rw-r--r--core/src/xmake/binutils/format.c10
-rw-r--r--core/src/xmake/binutils/prefix.h1
-rw-r--r--core/src/xmake/binutils/readsyms.c15
-rw-r--r--core/src/xmake/binutils/wasm/prefix.h212
-rw-r--r--core/src/xmake/binutils/wasm/readsyms.c350
-rwxr-xr-xcore/src/xmake/xmake.sh2
-rw-r--r--tests/modules/binutils/test.lua105
-rw-r--r--xmake/core/sandbox/modules/import/core/base/binutils.lua2
13 files changed, 748 insertions, 24 deletions
diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h
index 53df74e5d..c474eefa6 100644
--- a/core/src/xmake/binutils/ar/prefix.h
+++ b/core/src/xmake/binutils/ar/prefix.h
@@ -28,6 +28,7 @@
#include "../coff/prefix.h"
#include "../elf/prefix.h"
#include "../macho/prefix.h"
+#include "../wasm/prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* forward declarations
@@ -35,6 +36,7 @@
extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_wasm_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* types
@@ -157,6 +159,18 @@ static __tb_inline__ tb_bool_t xm_binutils_ar_get_member_name(tb_stream_ref_t is
}
}
+ if (header->name[0] == '/') {
+ if (header->name[1] == '/') {
+ tb_strlcpy(name, "//", name_size);
+ *name_len = 2;
+ } else {
+ tb_strlcpy(name, "/", name_size);
+ *name_len = 1;
+ }
+ *bytes_read = 0;
+ return tb_true;
+ }
+
// regular name (null-terminated or space-padded)
tb_size_t i = 0;
for (i = 0; i < 16 && i < name_size - 1; i++) {
diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c
index 1a0d3b540..9249d0a0a 100644
--- a/core/src/xmake/binutils/ar/readsyms.c
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -30,6 +30,30 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
*/
+static tb_int_t xm_binutils_ar_detect_member_format(tb_stream_ref_t istream, tb_hize_t base_offset) {
+ tb_uint8_t magic[8] = {0};
+ if (!tb_stream_seek(istream, base_offset)) {
+ return -1;
+ }
+ if (!tb_stream_bread(istream, magic, 8)) {
+ tb_stream_seek(istream, base_offset);
+ return -1;
+ }
+ tb_stream_seek(istream, base_offset);
+
+ if (magic[0] == XM_WASM_MAGIC0 && magic[1] == XM_WASM_MAGIC1 && magic[2] == XM_WASM_MAGIC2 && magic[3] == XM_WASM_MAGIC3) {
+ return XM_BINUTILS_FORMAT_WASM;
+ }
+ if (magic[0] == XM_ELF_MAGIC0 && magic[1] == XM_ELF_MAGIC1 && magic[2] == XM_ELF_MAGIC2 && magic[3] == XM_ELF_MAGIC3) {
+ return XM_BINUTILS_FORMAT_ELF;
+ }
+ tb_uint32_t macho_magic = tb_bits_get_u32_be(magic);
+ if (macho_magic == XM_MACHO_MAGIC_32 || macho_magic == XM_MACHO_MAGIC_64 ||
+ macho_magic == XM_MACHO_MAGIC_32_BE || macho_magic == XM_MACHO_MAGIC_64_BE) {
+ return XM_BINUTILS_FORMAT_MACHO;
+ }
+ return XM_BINUTILS_FORMAT_UNKNOWN;
+}
/* parse BSD symbol table (__.SYMDEF or __.SYMDEF SORTED)
*
@@ -346,7 +370,7 @@ tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_of
tb_hize_t current_pos = tb_stream_offset(istream);
// detect format
- tb_int_t format = xm_binutils_format_detect(istream);
+ tb_int_t format = xm_binutils_ar_detect_member_format(istream, current_pos);
if (format != XM_BINUTILS_FORMAT_AR) {
// create entry table
lua_newtable(lua);
@@ -365,6 +389,8 @@ tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_of
read_ok = xm_binutils_elf_read_symbols(istream, current_pos, lua);
} else if (format == XM_BINUTILS_FORMAT_MACHO) {
read_ok = xm_binutils_macho_read_symbols(istream, current_pos, lua);
+ } else if (format == XM_BINUTILS_FORMAT_WASM) {
+ read_ok = xm_binutils_wasm_read_symbols(istream, current_pos, lua);
}
if (!read_ok) {
diff --git a/core/src/xmake/binutils/deplibs.c b/core/src/xmake/binutils/deplibs.c
index 48a7cd200..1ce0d5c29 100644
--- a/core/src/xmake/binutils/deplibs.c
+++ b/core/src/xmake/binutils/deplibs.c
@@ -60,7 +60,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO);
if (!istream) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: open %s failed", binaryfile);
+ lua_pushfstring(lua, "open %s failed", binaryfile);
return 2;
}
@@ -68,7 +68,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
do {
if (!tb_stream_open(istream)) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: open %s failed", binaryfile);
+ lua_pushfstring(lua, "open %s failed", binaryfile);
break;
}
@@ -76,7 +76,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: cannot detect file format");
+ lua_pushfstring(lua, "cannot detect file format");
break;
}
@@ -88,7 +88,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
if (!xm_binutils_coff_deplibs(istream, 0, lua)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to parse COFF");
+ lua_pushfstring(lua, "failed to parse COFF");
break;
}
} else if (format == XM_BINUTILS_FORMAT_PE) {
@@ -96,7 +96,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
if (!tb_stream_seek(istream, 0x3c)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to seek to e_lfanew");
+ lua_pushfstring(lua, "failed to seek to e_lfanew");
break;
}
@@ -105,7 +105,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
if (!tb_stream_bread(istream, (tb_byte_t*)&e_lfanew, 4)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to read e_lfanew");
+ lua_pushfstring(lua, "failed to read e_lfanew");
break;
}
@@ -116,31 +116,31 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
if (!xm_binutils_coff_deplibs(istream, e_lfanew + 4, lua)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to parse PE/COFF");
+ lua_pushfstring(lua, "failed to parse PE/COFF");
break;
}
} else if (format == XM_BINUTILS_FORMAT_MACHO) {
if (!xm_binutils_macho_deplibs(istream, 0, lua)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to parse Mach-O");
+ lua_pushfstring(lua, "failed to parse Mach-O");
break;
}
} else if (format == XM_BINUTILS_FORMAT_ELF) {
if (!xm_binutils_elf_deplibs(istream, 0, lua)) {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: failed to parse ELF");
+ lua_pushfstring(lua, "failed to parse ELF");
break;
}
+ } else if (format == XM_BINUTILS_FORMAT_WASM) {
} else {
lua_pop(lua, 1); // pop table
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "deplibs: unsupported format %d", format);
+ lua_pushfstring(lua, "unsupported format %d", format);
break;
}
-
ok = tb_true;
} while (0);
diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h
index 2561b41cb..2072ee879 100644
--- a/core/src/xmake/binutils/elf/prefix.h
+++ b/core/src/xmake/binutils/elf/prefix.h
@@ -29,6 +29,11 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* macros
*/
+#define XM_ELF_MAGIC0 0x7f
+#define XM_ELF_MAGIC1 'E'
+#define XM_ELF_MAGIC2 'L'
+#define XM_ELF_MAGIC3 'F'
+
// ELF class
#define XM_ELF_EI_CLASS 4
#define XM_ELF_CLASS32 1
diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c
index 5e6559082..fbfbacfef 100644
--- a/core/src/xmake/binutils/extractlib.c
+++ b/core/src/xmake/binutils/extractlib.c
@@ -74,7 +74,7 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) {
tb_stream_ref_t istream = tb_stream_init_from_file(libraryfile, TB_FILE_MODE_RO);
if (!istream) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "extractlib: open %s failed", libraryfile);
+ lua_pushfstring(lua, "open %s failed", libraryfile);
return 2;
}
@@ -139,9 +139,9 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) {
} else {
lua_pushboolean(lua, tb_false);
if (error_msg) {
- lua_pushfstring(lua, "extractlib: %s %s", error_msg, libraryfile);
+ lua_pushfstring(lua, "%s %s", error_msg, libraryfile);
} else {
- lua_pushfstring(lua, "extractlib: unknown error for %s", libraryfile);
+ lua_pushfstring(lua, "unknown error for %s", libraryfile);
}
return 2;
}
diff --git a/core/src/xmake/binutils/format.c b/core/src/xmake/binutils/format.c
index dd7e836f3..956340b0a 100644
--- a/core/src/xmake/binutils/format.c
+++ b/core/src/xmake/binutils/format.c
@@ -91,6 +91,10 @@ static __tb_inline__ tb_bool_t xm_binutils_format_is_ape(tb_byte_t const* first8
first8[4] == 'p' && first8[5] == 'D';
}
+static __tb_inline__ tb_bool_t xm_binutils_format_is_wasm(tb_byte_t const* first8) {
+ return first8[0] == 0x00 && first8[1] == 0x61 && first8[2] == 0x73 && first8[3] == 0x6d;
+}
+
static __tb_inline__ tb_bool_t xm_binutils_format_is_elf(tb_byte_t const* first8) {
return first8[0] == 0x7f && first8[1] == 'E' && first8[2] == 'L' && first8[3] == 'F';
}
@@ -164,6 +168,11 @@ tb_int_t xm_binutils_format_detect(tb_stream_ref_t istream) {
break;
}
+ if (xm_binutils_format_is_wasm(p)) {
+ format = XM_BINUTILS_FORMAT_WASM;
+ break;
+ }
+
if (xm_binutils_format_is_elf(p)) {
format = XM_BINUTILS_FORMAT_ELF;
break;
@@ -237,6 +246,7 @@ tb_int_t xm_binutils_format(lua_State *lua) {
case XM_BINUTILS_FORMAT_PE: lua_pushliteral(lua, "pe"); break;
case XM_BINUTILS_FORMAT_SHEBANG: lua_pushliteral(lua, "shebang"); break;
case XM_BINUTILS_FORMAT_APE: lua_pushliteral(lua, "ape"); break;
+ case XM_BINUTILS_FORMAT_WASM: lua_pushliteral(lua, "wasm"); break;
default: lua_pushliteral(lua, "unknown"); break;
}
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index b3ffd8c27..608424958 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -36,6 +36,7 @@
#define XM_BINUTILS_FORMAT_PE 5
#define XM_BINUTILS_FORMAT_SHEBANG 6
#define XM_BINUTILS_FORMAT_APE 7
+#define XM_BINUTILS_FORMAT_WASM 8
#define XM_BINUTILS_FORMAT_UNKNOWN 0
// COFF machine types (for format detection)
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
index e29010314..2c119a538 100644
--- a/core/src/xmake/binutils/readsyms.c
+++ b/core/src/xmake/binutils/readsyms.c
@@ -41,6 +41,7 @@ extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t
extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_wasm_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
@@ -62,7 +63,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
tb_stream_ref_t istream = tb_stream_init_from_file(objectfile, TB_FILE_MODE_RO);
if (!istream) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: open %s failed", objectfile);
+ lua_pushfstring(lua, "open %s failed", objectfile);
return 2;
}
@@ -70,7 +71,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
do {
if (!tb_stream_open(istream)) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: open %s failed", objectfile);
+ lua_pushfstring(lua, "open %s failed", objectfile);
break;
}
@@ -78,7 +79,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: cannot detect file format");
+ lua_pushfstring(lua, "cannot detect file format");
break;
}
@@ -101,14 +102,14 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
// fallback to ar
if (!xm_binutils_ar_read_symbols(istream, 0, lua)) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: read AR/MSLIB archive symbols failed");
+ lua_pushfstring(lua, "read AR/MSLIB archive symbols failed");
break;
}
}
} else {
if (!xm_binutils_ar_read_symbols(istream, 0, lua)) {
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: read AR archive symbols failed");
+ lua_pushfstring(lua, "read AR archive symbols failed");
break;
}
}
@@ -140,6 +141,8 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
read_ok = xm_binutils_elf_read_symbols(istream, 0, lua);
} else if (format == XM_BINUTILS_FORMAT_MACHO) {
read_ok = xm_binutils_macho_read_symbols(istream, 0, lua);
+ } else if (format == XM_BINUTILS_FORMAT_WASM) {
+ read_ok = xm_binutils_wasm_read_symbols(istream, 0, lua);
}
if (read_ok) {
@@ -148,7 +151,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
} else {
lua_pop(lua, 2); // pop entry table and result list
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: read symbols failed");
+ lua_pushfstring(lua, "read symbols failed");
break;
}
}
diff --git a/core/src/xmake/binutils/wasm/prefix.h b/core/src/xmake/binutils/wasm/prefix.h
new file mode 100644
index 000000000..9ec011a50
--- /dev/null
+++ b/core/src/xmake/binutils/wasm/prefix.h
@@ -0,0 +1,212 @@
+/*!A cross-platform build utility based on Lua
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2015-present, Xmake Open Source Community.
+ *
+ * @author ruki
+ * @file prefix.h
+ *
+ */
+#ifndef XM_BINUTILS_WASM_PREFIX_H
+#define XM_BINUTILS_WASM_PREFIX_H
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "../prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * macros
+ */
+
+// wasm file magic: \0asm
+#define XM_WASM_MAGIC0 0x00
+#define XM_WASM_MAGIC1 0x61
+#define XM_WASM_MAGIC2 0x73
+#define XM_WASM_MAGIC3 0x6d
+
+// wasm header size: magic(4) + version(4)
+#define XM_WASM_HEADER_SIZE 8
+
+// wasm section ids
+#define XM_WASM_SECTION_IMPORT 2
+#define XM_WASM_SECTION_EXPORT 7
+
+// wasm custom section names
+#define XM_WASM_CUSTOM_LINKING "linking"
+#define XM_WASM_CUSTOM_NAME "name"
+
+// wasm import/export kinds
+#define XM_WASM_KIND_FUNC 0
+#define XM_WASM_KIND_TABLE 1
+#define XM_WASM_KIND_MEMORY 2
+#define XM_WASM_KIND_GLOBAL 3
+#define XM_WASM_KIND_TAG 4
+
+// wasm limits flags
+#define XM_WASM_LIMITS_HAS_MAX 0x01
+#define XM_WASM_LIMITS_MEM64 0x04
+
+// leb128 max bytes
+#define XM_WASM_U32_LEB_MAX 5
+#define XM_WASM_U64_LEB_MAX 10
+
+// linking custom section
+#define XM_WASM_LINKING_VERSION_2 2
+#define XM_WASM_LINKING_SUBSEC_SYMTAB 8
+
+// symbol table kinds (linking section)
+#define XM_WASM_SYMTAB_KIND_FUNCTION 0
+#define XM_WASM_SYMTAB_KIND_DATA 1
+#define XM_WASM_SYMTAB_KIND_GLOBAL 2
+#define XM_WASM_SYMTAB_KIND_SECTION 3
+#define XM_WASM_SYMTAB_KIND_EVENT 4
+#define XM_WASM_SYMTAB_KIND_TABLE 5
+#define XM_WASM_SYMTAB_KIND_TAG 6
+
+// symbol table flags (linking section)
+#define XM_WASM_SYMTAB_FLAG_UNDEFINED 0x10
+
+// symbol types for binutils.readsyms
+#define XM_WASM_SYM_UNDEF "U"
+#define XM_WASM_SYM_TEXT "T"
+#define XM_WASM_SYM_DATA "D"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * inline implementation
+ */
+
+// decode unsigned leb128 (u32) from current stream offset
+static __tb_inline__ tb_bool_t xm_binutils_wasm_read_u32_leb(tb_stream_ref_t istream, tb_uint32_t* out) {
+ tb_uint32_t result = 0;
+ tb_uint32_t shift = 0;
+ tb_byte_t byte = 0;
+ for (tb_size_t i = 0; i < XM_WASM_U32_LEB_MAX; i++) {
+ if (!tb_stream_bread(istream, &byte, 1)) {
+ return tb_false;
+ }
+ result |= ((tb_uint32_t)(byte & 0x7f)) << shift;
+ if (!(byte & 0x80)) {
+ *out = result;
+ return tb_true;
+ }
+ shift += 7;
+ }
+ return tb_false;
+}
+
+// decode unsigned leb128 (u64) from current stream offset
+static __tb_inline__ tb_bool_t xm_binutils_wasm_read_u64_leb(tb_stream_ref_t istream, tb_uint64_t* out) {
+ tb_uint64_t result = 0;
+ tb_uint32_t shift = 0;
+ tb_byte_t byte = 0;
+ for (tb_size_t i = 0; i < XM_WASM_U64_LEB_MAX; i++) {
+ if (!tb_stream_bread(istream, &byte, 1)) {
+ return tb_false;
+ }
+ result |= ((tb_uint64_t)(byte & 0x7f)) << shift;
+ if (!(byte & 0x80)) {
+ *out = result;
+ return tb_true;
+ }
+ shift += 7;
+ }
+ return tb_false;
+}
+
+// read wasm "name" (u32 leb length + bytes), truncate to fit buffer and skip remaining bytes
+static __tb_inline__ tb_bool_t xm_binutils_wasm_read_name(tb_stream_ref_t istream, tb_char_t* name, tb_size_t name_size) {
+ tb_uint32_t len = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &len)) {
+ return tb_false;
+ }
+ tb_size_t readn = (tb_size_t)tb_min((tb_uint32_t)(name_size > 0 ? name_size - 1 : 0), len);
+ if (readn && !tb_stream_bread(istream, (tb_byte_t*)name, readn)) {
+ return tb_false;
+ }
+ if (name_size) {
+ name[readn] = '\0';
+ }
+ if (len > readn) {
+ if (!tb_stream_skip(istream, (tb_hize_t)(len - readn))) {
+ return tb_false;
+ }
+ }
+ return tb_true;
+}
+
+// skip wasm limits used by table/memory types, supports wasm32 and wasm64(memory64)
+static __tb_inline__ tb_bool_t xm_binutils_wasm_skip_limits(tb_stream_ref_t istream) {
+ tb_uint32_t flags = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &flags)) {
+ return tb_false;
+ }
+ if (flags & XM_WASM_LIMITS_MEM64) {
+ tb_uint64_t tmp64 = 0;
+ if (!xm_binutils_wasm_read_u64_leb(istream, &tmp64)) {
+ return tb_false;
+ }
+ if (flags & XM_WASM_LIMITS_HAS_MAX) {
+ if (!xm_binutils_wasm_read_u64_leb(istream, &tmp64)) {
+ return tb_false;
+ }
+ }
+ } else {
+ tb_uint32_t tmp32 = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp32)) {
+ return tb_false;
+ }
+ if (flags & XM_WASM_LIMITS_HAS_MAX) {
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp32)) {
+ return tb_false;
+ }
+ }
+ }
+ return tb_true;
+}
+
+// add one symbol table entry: {name=..., type=...}
+static __tb_inline__ tb_void_t xm_binutils_wasm_add_symbol(lua_State* lua, tb_size_t* result_count, tb_char_t const* name, tb_char_t const* type) {
+ lua_pushinteger(lua, (lua_Integer)(*result_count + 1));
+ lua_newtable(lua);
+
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, type);
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ (*result_count)++;
+}
+
+// seek and validate wasm header at base_offset, leave stream offset after header
+static __tb_inline__ tb_bool_t xm_binutils_wasm_check_header(tb_stream_ref_t istream, tb_hize_t base_offset) {
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+
+ tb_uint8_t header[XM_WASM_HEADER_SIZE];
+ if (!tb_stream_bread(istream, header, XM_WASM_HEADER_SIZE)) {
+ return tb_false;
+ }
+ if (header[0] != XM_WASM_MAGIC0 || header[1] != XM_WASM_MAGIC1 || header[2] != XM_WASM_MAGIC2 || header[3] != XM_WASM_MAGIC3) {
+ return tb_false;
+ }
+ return tb_true;
+}
+
+#endif
diff --git a/core/src/xmake/binutils/wasm/readsyms.c b/core/src/xmake/binutils/wasm/readsyms.c
new file mode 100644
index 000000000..b57f27433
--- /dev/null
+++ b/core/src/xmake/binutils/wasm/readsyms.c
@@ -0,0 +1,350 @@
+/*!A cross-platform build utility based on Lua
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2015-present, Xmake Open Source Community.
+ *
+ * @author ruki
+ * @file readsyms.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "wasm_readsyms"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+static tb_bool_t xm_binutils_wasm_parse_linking_symtab(tb_stream_ref_t istream, tb_hize_t payload_end, lua_State* lua, tb_size_t* result_count) {
+ tb_uint32_t symcount = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &symcount)) {
+ return tb_false;
+ }
+
+ for (tb_uint32_t i = 0; i < symcount; i++) {
+ tb_byte_t kind = 0;
+ if (!tb_stream_bread(istream, &kind, 1)) {
+ return tb_false;
+ }
+
+ tb_uint32_t flags = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &flags)) {
+ return tb_false;
+ }
+
+ tb_bool_t is_undef = (flags & XM_WASM_SYMTAB_FLAG_UNDEFINED) != 0;
+ tb_char_t name[256] = {0};
+
+ if (kind == XM_WASM_SYMTAB_KIND_FUNCTION || kind == XM_WASM_SYMTAB_KIND_GLOBAL ||
+ kind == XM_WASM_SYMTAB_KIND_EVENT || kind == XM_WASM_SYMTAB_KIND_TABLE ||
+ kind == XM_WASM_SYMTAB_KIND_TAG) {
+
+ if (!is_undef) {
+ tb_uint32_t index = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &index)) {
+ return tb_false;
+ }
+ }
+ if (!xm_binutils_wasm_read_name(istream, name, sizeof(name))) {
+ return tb_false;
+ }
+ } else if (kind == XM_WASM_SYMTAB_KIND_DATA) {
+ if (!xm_binutils_wasm_read_name(istream, name, sizeof(name))) {
+ return tb_false;
+ }
+ if (!is_undef) {
+ tb_uint32_t tmp = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp)) { // segment
+ return tb_false;
+ }
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp)) { // offset
+ return tb_false;
+ }
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp)) { // size
+ return tb_false;
+ }
+ }
+ } else if (kind == XM_WASM_SYMTAB_KIND_SECTION) {
+ tb_uint32_t section_index = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &section_index)) {
+ return tb_false;
+ }
+ if (!xm_binutils_wasm_read_name(istream, name, sizeof(name))) {
+ return tb_false;
+ }
+ } else {
+ return tb_false;
+ }
+
+ if (name[0]) {
+ tb_char_t const* type = XM_WASM_SYM_DATA;
+ if (is_undef) {
+ type = XM_WASM_SYM_UNDEF;
+ } else if (kind == XM_WASM_SYMTAB_KIND_FUNCTION) {
+ type = XM_WASM_SYM_TEXT;
+ }
+ xm_binutils_wasm_add_symbol(lua, result_count, name, type);
+ }
+
+ if (tb_stream_offset(istream) > payload_end) {
+ return tb_false;
+ }
+ }
+ return tb_true;
+}
+
+static tb_bool_t xm_binutils_wasm_parse_custom_linking(tb_stream_ref_t istream, tb_hize_t payload_end, lua_State* lua, tb_size_t* result_count) {
+ tb_uint32_t version = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &version)) {
+ return tb_false;
+ }
+
+ while (tb_stream_offset(istream) < payload_end) {
+ tb_byte_t subsec_type = 0;
+ if (!tb_stream_bread(istream, &subsec_type, 1)) {
+ return tb_false;
+ }
+
+ tb_uint32_t subsec_size = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &subsec_size)) {
+ return tb_false;
+ }
+
+ tb_hize_t subsec_end = tb_stream_offset(istream) + (tb_hize_t)subsec_size;
+ if (subsec_end > payload_end) {
+ return tb_false;
+ }
+
+ if (subsec_type == XM_WASM_LINKING_SUBSEC_SYMTAB) {
+ if (!xm_binutils_wasm_parse_linking_symtab(istream, subsec_end, lua, result_count)) {
+ return tb_false;
+ }
+ }
+
+ if (tb_stream_offset(istream) < subsec_end) {
+ if (!tb_stream_seek(istream, subsec_end)) {
+ return tb_false;
+ }
+ }
+ }
+
+ return version > 0 ? tb_true : tb_false;
+}
+
+static tb_bool_t xm_binutils_wasm_parse_custom_name(tb_stream_ref_t istream, tb_hize_t payload_end, lua_State* lua, tb_size_t* result_count) {
+ // only use the name section as a fallback when we have no symbols yet
+ if (*result_count != 0) {
+ return tb_true;
+ }
+
+ while (tb_stream_offset(istream) < payload_end) {
+ tb_byte_t subsec_type = 0;
+ if (!tb_stream_bread(istream, &subsec_type, 1)) {
+ return tb_false;
+ }
+ tb_uint32_t subsec_size = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &subsec_size)) {
+ return tb_false;
+ }
+ tb_hize_t subsec_end = tb_stream_offset(istream) + (tb_hize_t)subsec_size;
+ if (subsec_end > payload_end) {
+ return tb_false;
+ }
+
+ if (subsec_type == 1) {
+ tb_uint32_t count = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &count)) {
+ return tb_false;
+ }
+ for (tb_uint32_t i = 0; i < count; i++) {
+ tb_uint32_t index = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &index)) {
+ return tb_false;
+ }
+ tb_char_t name[256] = {0};
+ if (!xm_binutils_wasm_read_name(istream, name, sizeof(name))) {
+ return tb_false;
+ }
+ if (name[0]) {
+ xm_binutils_wasm_add_symbol(lua, result_count, name, XM_WASM_SYM_TEXT);
+ }
+ if (tb_stream_offset(istream) > subsec_end) {
+ return tb_false;
+ }
+ }
+ }
+
+ if (tb_stream_offset(istream) < subsec_end) {
+ if (!tb_stream_seek(istream, subsec_end)) {
+ return tb_false;
+ }
+ }
+ }
+ return tb_true;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * interfaces
+ */
+tb_bool_t xm_binutils_wasm_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+ if (!xm_binutils_wasm_check_header(istream, base_offset)) {
+ return tb_false;
+ }
+
+ lua_newtable(lua);
+ tb_size_t result_count = 0;
+
+ // parse wasm sections until EOF, collect imports/exports as symbols
+ for (;;) {
+ tb_byte_t section_id = 0;
+ if (!tb_stream_bread(istream, &section_id, 1)) {
+ break;
+ }
+
+ tb_uint32_t payload_len = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &payload_len)) {
+ break;
+ }
+
+ tb_hize_t payload_start = tb_stream_offset(istream);
+ tb_hize_t payload_end = payload_start + (tb_hize_t)payload_len;
+ if (payload_len == 0) {
+ continue;
+ }
+
+ if (section_id == 0) {
+ // custom section: name + custom payload
+ tb_char_t custom_name[64] = {0};
+ if (!xm_binutils_wasm_read_name(istream, custom_name, sizeof(custom_name))) {
+ break;
+ }
+
+ if (!tb_strcmp(custom_name, XM_WASM_CUSTOM_LINKING)) {
+ if (!xm_binutils_wasm_parse_custom_linking(istream, payload_end, lua, &result_count)) {
+ break;
+ }
+ } else if (!tb_strcmp(custom_name, XM_WASM_CUSTOM_NAME)) {
+ if (!xm_binutils_wasm_parse_custom_name(istream, payload_end, lua, &result_count)) {
+ break;
+ }
+ }
+ } else if (section_id == XM_WASM_SECTION_IMPORT) {
+ // import section: (vec import), each import => undefined symbol
+ tb_uint32_t count = 0;
+ if (xm_binutils_wasm_read_u32_leb(istream, &count)) {
+ for (tb_uint32_t i = 0; i < count; i++) {
+ tb_char_t module[256] = {0};
+ tb_char_t field[256] = {0};
+ if (!xm_binutils_wasm_read_name(istream, module, sizeof(module))) {
+ break;
+ }
+ if (!xm_binutils_wasm_read_name(istream, field, sizeof(field))) {
+ break;
+ }
+ tb_byte_t kind = 0;
+ if (!tb_stream_bread(istream, &kind, 1)) {
+ break;
+ }
+ tb_uint32_t tmp = 0;
+ if (kind == XM_WASM_KIND_FUNC) {
+ // type index (u32)
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp)) {
+ break;
+ }
+ } else if (kind == XM_WASM_KIND_TABLE) {
+ // table type: elemtype + limits
+ if (!tb_stream_bread(istream, (tb_byte_t*)&kind, 1)) {
+ break;
+ }
+ if (!xm_binutils_wasm_skip_limits(istream)) {
+ break;
+ }
+ } else if (kind == XM_WASM_KIND_MEMORY) {
+ // memory type: limits
+ if (!xm_binutils_wasm_skip_limits(istream)) {
+ break;
+ }
+ } else if (kind == XM_WASM_KIND_GLOBAL) {
+ // global type: valtype + mut
+ tb_byte_t b = 0;
+ if (!tb_stream_bread(istream, &b, 1)) {
+ break;
+ }
+ if (!tb_stream_bread(istream, &b, 1)) {
+ break;
+ }
+ } else if (kind == XM_WASM_KIND_TAG) {
+ // tag: attribute + type index
+ if (!xm_binutils_wasm_read_u32_leb(istream, &tmp)) {
+ break;
+ }
+ } else {
+ break;
+ }
+
+ // keep consistent with nm-style output: use the imported field name as symbol name
+ if (field[0]) {
+ xm_binutils_wasm_add_symbol(lua, &result_count, field, XM_WASM_SYM_UNDEF);
+ } else if (module[0]) {
+ xm_binutils_wasm_add_symbol(lua, &result_count, module, XM_WASM_SYM_UNDEF);
+ }
+ }
+ }
+ } else if (section_id == XM_WASM_SECTION_EXPORT) {
+ // export section: (vec export), each export => defined symbol
+ tb_uint32_t count = 0;
+ if (xm_binutils_wasm_read_u32_leb(istream, &count)) {
+ for (tb_uint32_t i = 0; i < count; i++) {
+ tb_char_t name[256] = {0};
+ if (!xm_binutils_wasm_read_name(istream, name, sizeof(name))) {
+ break;
+ }
+ tb_byte_t kind = 0;
+ if (!tb_stream_bread(istream, &kind, 1)) {
+ break;
+ }
+ tb_uint32_t index = 0;
+ if (!xm_binutils_wasm_read_u32_leb(istream, &index)) {
+ break;
+ }
+ if (name[0]) {
+ // keep consistent with other formats: function => "T", others => "D"
+ tb_char_t const* type = XM_WASM_SYM_DATA;
+ if (kind == XM_WASM_KIND_FUNC) {
+ type = XM_WASM_SYM_TEXT;
+ }
+ xm_binutils_wasm_add_symbol(lua, &result_count, name, type);
+ }
+ }
+ }
+ }
+
+ // always seek to end of section payload to continue scanning
+ if (tb_stream_offset(istream) < payload_end) {
+ if (!tb_stream_seek(istream, payload_end)) {
+ break;
+ }
+ }
+ }
+
+ return tb_true;
+}
diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh
index dfe533aa3..06a3a82fc 100755
--- a/core/src/xmake/xmake.sh
+++ b/core/src/xmake/xmake.sh
@@ -77,10 +77,10 @@ target "xmake"
add_files "binutils/coff/*.c"
add_files "binutils/macho/*.c"
add_files "binutils/elf/*.c"
+ add_files "binutils/wasm/*.c"
add_files "binutils/ar/*.c"
add_files "binutils/mslib/*.c"
add_files "thread/*.c"
if is_plat "mingw"; then
add_files "winos/*.c"
fi
-
diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua
index bbb466144..ce68bcda8 100644
--- a/tests/modules/binutils/test.lua
+++ b/tests/modules/binutils/test.lua
@@ -44,6 +44,10 @@ function test_format(t)
io.writefile(ar, "!<arch>\n")
t:are_equal(binutils.format(ar), "ar")
+ local wasmso = path.join(tempdir, "libfoo.so")
+ io.writefile(wasmso, string.char(0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00))
+ t:are_equal(binutils.format(wasmso), "wasm")
+
local elf = path.join(tempdir, "a.elf")
io.writefile(elf, string.char(0x7f, string.byte("E"), string.byte("L"), string.byte("F"), 0, 0, 0, 0))
t:are_equal(binutils.format(elf), "elf")
@@ -76,3 +80,104 @@ function test_format(t)
os.tryrm(tempdir)
end
+
+function test_deplibs(t)
+ local tempdir = "temp/binutils_deplibs"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ local wasmso = path.join(tempdir, "libfoo.so")
+ io.writefile(wasmso, string.char(0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00))
+ local libs = binutils.deplibs(wasmso)
+ t:are_equal(#libs, 0)
+
+ os.tryrm(tempdir)
+end
+
+function test_readsyms(t)
+ local tempdir = "temp/binutils_readsyms"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ local function _writebin(filepath, data)
+ io.writefile(filepath, data, {encoding = "binary"})
+ end
+
+ local wasmso = path.join(tempdir, "libfoo.so")
+ _writebin(wasmso, string.char(
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x0b, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x03, 0x62, 0x61, 0x72, 0x00, 0x00,
+ 0x07, 0x07, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x00, 0x00))
+ local results = binutils.readsyms(wasmso)
+ t:are_equal(#results, 1)
+ t:are_equal(results[1].objectfile, "libfoo.so")
+ t:are_equal(#results[1].symbols, 2)
+ t:are_equal(results[1].symbols[1].name, "bar")
+ t:are_equal(results[1].symbols[1].type, "U")
+ t:are_equal(results[1].symbols[2].name, "foo")
+ t:are_equal(results[1].symbols[2].type, "T")
+
+ local wasmso64 = path.join(tempdir, "libfoo64.so")
+ _writebin(wasmso64, string.char(
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x0c, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x04, 0x01,
+ 0x07, 0x07, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x00, 0x00))
+ local results64 = binutils.readsyms(wasmso64)
+ t:are_equal(#results64, 1)
+ t:are_equal(results64[1].objectfile, "libfoo64.so")
+ t:are_equal(#results64[1].symbols, 2)
+ t:are_equal(results64[1].symbols[1].name, "mem")
+ t:are_equal(results64[1].symbols[1].type, "U")
+ t:are_equal(results64[1].symbols[2].name, "foo")
+ t:are_equal(results64[1].symbols[2].type, "T")
+
+ local wasmlink = path.join(tempdir, "liblink.so")
+ _writebin(wasmlink, string.char(
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00,
+ 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
+ 0x00, 0x13, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x02, 0x08, 0x08, 0x01, 0x00, 0x00, 0x00, 0x03, 0x61, 0x64, 0x64))
+ local resultslink = binutils.readsyms(wasmlink)
+ t:are_equal(#resultslink, 1)
+ t:are_equal(resultslink[1].objectfile, "liblink.so")
+ t:are_equal(#resultslink[1].symbols, 1)
+ t:are_equal(resultslink[1].symbols[1].name, "add")
+ t:are_equal(resultslink[1].symbols[1].type, "T")
+
+ local wasmar = path.join(tempdir, "libbar.a")
+ local function _pad(str, n)
+ if #str < n then
+ return str .. string.rep(" ", n - #str)
+ end
+ return str:sub(1, n)
+ end
+ local function _ar_header(name, size)
+ return _pad(name, 16) ..
+ _pad("0", 12) ..
+ _pad("0", 6) ..
+ _pad("0", 6) ..
+ _pad("644", 8) ..
+ _pad(tostring(size), 10) ..
+ "`\n"
+ end
+ local wasmobj = string.char(
+ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
+ 0x03, 0x02, 0x01, 0x00,
+ 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
+ 0x00, 0x13, 0x07, 0x6c, 0x69, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x02, 0x08, 0x08, 0x01, 0x00, 0x00, 0x00, 0x03, 0x61, 0x64, 0x64)
+ local symtab = string.char(0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50) .. "add\0"
+ local ardata = "!<arch>\n" ..
+ _ar_header("/", #symtab) .. symtab .. ((#symtab % 2 == 1) and "\n" or "") ..
+ _ar_header("foo.cpp.o/", #wasmobj) .. wasmobj .. ((#wasmobj % 2 == 1) and "\n" or "")
+ _writebin(wasmar, ardata)
+ local resultsar = binutils.readsyms(wasmar)
+ t:are_equal(#resultsar, 1)
+ t:are_equal(resultsar[1].objectfile, "foo.cpp.o")
+ t:are_equal(#resultsar[1].symbols, 1)
+ t:are_equal(resultsar[1].symbols[1].name, "add")
+ t:are_equal(resultsar[1].symbols[1].type, "T")
+
+ os.tryrm(tempdir)
+end
diff --git a/xmake/core/sandbox/modules/import/core/base/binutils.lua b/xmake/core/sandbox/modules/import/core/base/binutils.lua
index 0fc2b6796..a3b9428b6 100644
--- a/xmake/core/sandbox/modules/import/core/base/binutils.lua
+++ b/xmake/core/sandbox/modules/import/core/base/binutils.lua
@@ -82,8 +82,6 @@ function sandbox_core_base_binutils.rpath_list(binaryfile)
end
end
--- insert rpath to binary file (auto-detect format: ELF or Mach-O)
-
-- clean rpaths from binary file (auto-detect format: ELF or Mach-O)
function sandbox_core_base_binutils.rpath_clean(binaryfile)
local ok, errors = binutils.rpath_clean(binaryfile)