From 120bb0b0cf4a046638dca0d408a5bff604d91c9f Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 12 Feb 2026 00:41:49 +0800 Subject: improve binutils.format to support wasm --- tests/modules/binutils/test.lua | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'tests/modules/binutils/test.lua') diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua index bbb466144..b26e2b337 100644 --- a/tests/modules/binutils/test.lua +++ b/tests/modules/binutils/test.lua @@ -44,6 +44,10 @@ function test_format(t) io.writefile(ar, "!\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") -- cgit v1.3.1 From e0e3e4dc010c24c555499962f740f4d76eb25351 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 12 Feb 2026 00:42:34 +0800 Subject: improve deplibs to support wasm --- core/src/xmake/binutils/deplibs.c | 1 + tests/modules/binutils/test.lua | 13 +++++++++++++ 2 files changed, 14 insertions(+) (limited to 'tests/modules/binutils/test.lua') diff --git a/core/src/xmake/binutils/deplibs.c b/core/src/xmake/binutils/deplibs.c index cd44c2630..1ce0d5c29 100644 --- a/core/src/xmake/binutils/deplibs.c +++ b/core/src/xmake/binutils/deplibs.c @@ -133,6 +133,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) { 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); diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua index b26e2b337..a2801af6e 100644 --- a/tests/modules/binutils/test.lua +++ b/tests/modules/binutils/test.lua @@ -80,3 +80,16 @@ 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 -- cgit v1.3.1 From af7720f0b49e91bae20648f49133c6eef8f9e003 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 12 Feb 2026 00:53:43 +0800 Subject: add readsyms support for wasm --- core/src/xmake/binutils/readsyms.c | 3 + core/src/xmake/binutils/wasm/prefix.h | 212 +++++++++++++++++++ core/src/xmake/binutils/wasm/readsyms.c | 350 ++++++++++++++++++++++++++++++++ core/src/xmake/xmake.sh | 2 +- tests/modules/binutils/test.lua | 50 +++++ 5 files changed, 616 insertions(+), 1 deletion(-) create mode 100644 core/src/xmake/binutils/wasm/prefix.h create mode 100644 core/src/xmake/binutils/wasm/readsyms.c (limited to 'tests/modules/binutils/test.lua') diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c index e29010314..8cc40bf3c 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 @@ -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) { 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, §ion_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, §ion_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 a2801af6e..319a197e6 100644 --- a/tests/modules/binutils/test.lua +++ b/tests/modules/binutils/test.lua @@ -93,3 +93,53 @@ function test_deplibs(t) os.tryrm(tempdir) end + +function test_readsyms(t) + local tempdir = "temp/binutils_readsyms" + 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, + 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") + io.writefile(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") + io.writefile(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") + + os.tryrm(tempdir) +end -- cgit v1.3.1 From b445dc4e25b5acceacaabcea627fba3703050e15 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 12 Feb 2026 00:55:56 +0800 Subject: improve readsyms for wasm/.a --- core/src/xmake/binutils/ar/prefix.h | 14 ++++++++++++++ core/src/xmake/binutils/ar/readsyms.c | 28 +++++++++++++++++++++++++++- tests/modules/binutils/test.lua | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) (limited to 'tests/modules/binutils/test.lua') 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..3af3f8a62 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] == 0x00 && magic[1] == 0x61 && magic[2] == 0x73 && magic[3] == 0x6d) { + return XM_BINUTILS_FORMAT_WASM; + } + if (magic[0] == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F') { + return XM_BINUTILS_FORMAT_ELF; + } + if ((magic[0] == 0xfe && magic[1] == 0xed && magic[2] == 0xfa && (magic[3] == 0xce || magic[3] == 0xcf)) || + (magic[0] == 0xce && magic[1] == 0xfa && magic[2] == 0xed && magic[3] == 0xfe) || + (magic[0] == 0xcf && magic[1] == 0xfa && magic[2] == 0xed && magic[3] == 0xfe)) { + 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/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua index 319a197e6..964e84ed2 100644 --- a/tests/modules/binutils/test.lua +++ b/tests/modules/binutils/test.lua @@ -141,5 +141,39 @@ function test_readsyms(t) 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 = "!\n" .. + _ar_header("/", #symtab) .. symtab .. ((#symtab % 2 == 1) and "\n" or "") .. + _ar_header("foo.cpp.o/", #wasmobj) .. wasmobj .. ((#wasmobj % 2 == 1) and "\n" or "") + io.writefile(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 -- cgit v1.3.1 From 37b506c86c3390d6a175ce37828e8defd44a2407 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 12 Feb 2026 22:32:05 +0800 Subject: improve test --- core/src/xmake/binutils/elf/prefix.h | 1 - tests/modules/binutils/test.lua | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'tests/modules/binutils/test.lua') diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index 6bf64b2ed..2072ee879 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -29,7 +29,6 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * macros */ -// ELF file magic: 0x7f 'E' 'L' 'F' #define XM_ELF_MAGIC0 0x7f #define XM_ELF_MAGIC1 'E' #define XM_ELF_MAGIC2 'L' diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua index 964e84ed2..ce68bcda8 100644 --- a/tests/modules/binutils/test.lua +++ b/tests/modules/binutils/test.lua @@ -99,8 +99,12 @@ function test_readsyms(t) 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") - io.writefile(wasmso, string.char( + _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)) @@ -114,7 +118,7 @@ function test_readsyms(t) t:are_equal(results[1].symbols[2].type, "T") local wasmso64 = path.join(tempdir, "libfoo64.so") - io.writefile(wasmso64, string.char( + _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)) @@ -128,7 +132,7 @@ function test_readsyms(t) t:are_equal(results64[1].symbols[2].type, "T") local wasmlink = path.join(tempdir, "liblink.so") - io.writefile(wasmlink, string.char( + _writebin(wasmlink, string.char( 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, @@ -167,7 +171,7 @@ function test_readsyms(t) local ardata = "!\n" .. _ar_header("/", #symtab) .. symtab .. ((#symtab % 2 == 1) and "\n" or "") .. _ar_header("foo.cpp.o/", #wasmobj) .. wasmobj .. ((#wasmobj % 2 == 1) and "\n" or "") - io.writefile(wasmar, ardata) + _writebin(wasmar, ardata) local resultsar = binutils.readsyms(wasmar) t:are_equal(#resultsar, 1) t:are_equal(resultsar[1].objectfile, "foo.cpp.o") -- cgit v1.3.1