summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-09 00:29:51 +0800
committerruki <[email protected]>2025-12-09 00:29:51 +0800
commitba293bc4b8840da34220e1f54313d531d18dde8e (patch)
treee6b6325ccf446b845b26a3a21f06f0bd84914170
parentf1726a8f0f63160ae2185f28b38a740caf6d1ab1 (diff)
add binary readsyms
-rw-r--r--core/src/xmake/binutils/coff/prefix.h104
-rw-r--r--core/src/xmake/binutils/coff/readsyms.c133
-rw-r--r--core/src/xmake/binutils/elf/prefix.h69
-rw-r--r--core/src/xmake/binutils/elf/readsyms.c325
-rw-r--r--core/src/xmake/binutils/macho/prefix.h65
-rw-r--r--core/src/xmake/binutils/macho/readsyms.c285
-rw-r--r--core/src/xmake/binutils/prefix.h112
-rw-r--r--core/src/xmake/binutils/readsyms.c122
-rw-r--r--core/src/xmake/engine.c2
-rw-r--r--xmake/core/base/binutils.lua10
-rw-r--r--xmake/core/sandbox/modules/import/core/base/binutils.lua10
-rw-r--r--xmake/core/sandbox/modules/utils.lua2
-rw-r--r--xmake/modules/utils/binary/readsyms.lua92
13 files changed, 1320 insertions, 11 deletions
diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h
index 25ea52168..95737dc20 100644
--- a/core/src/xmake/binutils/coff/prefix.h
+++ b/core/src/xmake/binutils/coff/prefix.h
@@ -171,5 +171,109 @@ static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_
}
}
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * readsyms inline implementation
+ */
+
+/* read string from COFF string table
+ *
+ * @param istream the input stream
+ * @param strtab_offset the string table offset
+ * @param offset the string offset (relative to string table content, after size field)
+ * @return the string (static buffer, valid until next call)
+ */
+static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istream, tb_uint32_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) {
+ tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);
+
+ // read string table size
+ tb_uint32_t strtab_size = 0;
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ if (!tb_stream_seek(istream, strtab_offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_size, 4)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+
+ // check offset (offset is relative to start of string table, after the 4-byte size field)
+ if (offset >= strtab_size - 4) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+
+ // seek to string position (offset is from start of string table content, after size field)
+ if (!tb_stream_seek(istream, strtab_offset + 4 + offset)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+
+ // read string
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ // restore position
+ tb_stream_seek(istream, saved_pos);
+ return tb_true;
+}
+
+/* get symbol name from COFF symbol entry
+ *
+ * @param istream the input stream
+ * @param sym the symbol entry
+ * @param strtab_offset the string table offset
+ * @param name the buffer to store the symbol name
+ * @param name_size the size of the buffer
+ * @return tb_true on success, tb_false on failure
+ */
+static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t istream, xm_coff_symbol_t const *sym, tb_uint32_t strtab_offset, tb_char_t *name, tb_size_t name_size) {
+ tb_assert_and_check_return_val(istream && sym && name && name_size > 0, tb_false);
+
+ // check if it's a long name (first 4 bytes are zeros)
+ if (sym->n.longname.zeros == 0) {
+ // long name: read from string table
+ return xm_binutils_coff_read_string(istream, strtab_offset, sym->n.longname.offset, name, name_size);
+ } else {
+ // short name: use directly
+ tb_size_t len = tb_min(8, name_size - 1);
+ tb_strncpy(name, sym->n.shortname.name, len);
+ name[len] = '\0';
+ // trim trailing nulls
+ while (len > 0 && name[len - 1] == '\0') {
+ len--;
+ }
+ name[len] = '\0';
+ return tb_true;
+ }
+}
+
+/* get symbol type string from storage class
+ *
+ * @param scl the storage class
+ * @return the type string
+ */
+static __tb_inline__ tb_char_t const *xm_binutils_coff_get_symbol_type(tb_uint8_t scl) {
+ // IMAGE_SYM_CLASS_EXTERNAL = 2
+ // IMAGE_SYM_CLASS_STATIC = 3
+ // IMAGE_SYM_CLASS_LABEL = 6
+ switch (scl) {
+ case 2: return "external";
+ case 3: return "static";
+ case 6: return "label";
+ default: return "unknown";
+ }
+}
+
#endif
diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c
new file mode 100644
index 000000000..edc5cc3ee
--- /dev/null
+++ b/core/src/xmake/binutils/coff/readsyms.c
@@ -0,0 +1,133 @@
+/*!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 "readsyms_coff"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read COFF header
+ xm_coff_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // check if there are symbols
+ if (header.nsyms == 0 || header.symtabofs == 0) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read string table offset (after symbol table)
+ tb_uint32_t strtab_offset = header.symtabofs + header.nsyms * 18; // each symbol is 18 bytes
+
+ // read symbols
+ if (!tb_stream_seek(istream, header.symtabofs)) {
+ return tb_false;
+ }
+
+ tb_uint32_t sym_index = 0;
+ tb_uint32_t sym_count = 0;
+ while (sym_index < header.nsyms) {
+ // read symbol
+ xm_coff_symbol_t sym;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
+ return tb_false;
+ }
+
+ // get symbol name
+ tb_char_t name[256];
+ if (!xm_binutils_coff_get_symbol_name(istream, &sym, strtab_offset, name, sizeof(name)) || !name[0]) {
+ // skip empty names
+ sym_index++;
+ if (sym.naux > 0) {
+ sym_index += sym.naux; // skip auxiliary entries
+ // skip auxiliary data
+ tb_stream_seek(istream, tb_stream_offset(istream) + sym.naux * 18);
+ }
+ continue;
+ }
+
+ // create symbol table entry
+ lua_pushinteger(lua, sym_count + 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // value
+ lua_pushstring(lua, "value");
+ lua_pushinteger(lua, sym.value);
+ lua_settable(lua, -3);
+
+ // section
+ lua_pushstring(lua, "section");
+ lua_pushinteger(lua, sym.sect);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, xm_binutils_coff_get_symbol_type(sym.scl));
+ lua_settable(lua, -3);
+
+ // storage class
+ lua_pushstring(lua, "storage_class");
+ lua_pushinteger(lua, sym.scl);
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+
+ sym_count++;
+ sym_index++;
+
+ // skip auxiliary entries
+ if (sym.naux > 0) {
+ sym_index += sym.naux;
+ // skip auxiliary data
+ if (!tb_stream_seek(istream, tb_stream_offset(istream) + sym.naux * 18)) {
+ return tb_false;
+ }
+ }
+ }
+
+ return tb_true;
+}
diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h
index d1a13afb8..97d05a661 100644
--- a/core/src/xmake/binutils/elf/prefix.h
+++ b/core/src/xmake/binutils/elf/prefix.h
@@ -266,5 +266,74 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_is_64bit(tb_char_t const *arch) {
return tb_false;
}
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * readsyms inline implementation
+ */
+
+/* read string from ELF string table
+ *
+ * @param istream the input stream
+ * @param strtab_offset the string table offset
+ * @param offset the string offset
+ * @return the string (static buffer, valid until next call)
+ */
+static __tb_inline__ tb_bool_t xm_binutils_elf_read_string(tb_stream_ref_t istream, tb_uint32_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) {
+ tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);
+
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ if (!tb_stream_seek(istream, strtab_offset + offset)) {
+ return tb_false;
+ }
+
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ tb_stream_seek(istream, saved_pos);
+ return tb_true;
+}
+
+/* get symbol type string from ELF symbol info
+ *
+ * @param st_info the symbol info byte
+ * @return the type string
+ */
+static __tb_inline__ tb_char_t const *xm_binutils_elf_get_symbol_type(tb_uint8_t st_info) {
+ tb_uint8_t type = st_info & 0xf;
+ switch (type) {
+ case 0: return "notype";
+ case 1: return "object";
+ case 2: return "func";
+ case 3: return "section";
+ case 4: return "file";
+ default: return "unknown";
+ }
+}
+
+/* get symbol bind string from ELF symbol info
+ *
+ * @param st_info the symbol info byte
+ * @return the bind string
+ */
+static __tb_inline__ tb_char_t const *xm_binutils_elf_get_symbol_bind(tb_uint8_t st_info) {
+ tb_uint8_t bind = (st_info >> 4) & 0xf;
+ switch (bind) {
+ case 0: return "local";
+ case 1: return "global";
+ case 2: return "weak";
+ default: return "unknown";
+ }
+}
+
#endif
diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c
new file mode 100644
index 000000000..ce6653bfe
--- /dev/null
+++ b/core/src/xmake/binutils/elf/readsyms.c
@@ -0,0 +1,325 @@
+/*!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 "readsyms_elf"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read ELF header
+ xm_elf32_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find .symtab section
+ xm_elf32_section_t symtab_section;
+ xm_elf32_section_t strtab_section;
+ tb_bool_t found_symtab = tb_false;
+ tb_bool_t found_strtab = tb_false;
+
+ if (!tb_stream_seek(istream, header.e_shoff)) {
+ return tb_false;
+ }
+
+ for (tb_uint16_t i = 0; i < header.e_shnum; i++) {
+ xm_elf32_section_t section;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&section, sizeof(section))) {
+ return tb_false;
+ }
+
+ if (section.sh_type == XM_ELF_SHT_SYMTAB) {
+ symtab_section = section;
+ found_symtab = tb_true;
+ } else if (section.sh_type == XM_ELF_SHT_STRTAB && section.sh_link == 0) {
+ // .strtab is linked from .symtab, but we need to find it
+ // check if this is the string table for symbols
+ if (found_symtab && symtab_section.sh_link == i) {
+ strtab_section = section;
+ found_strtab = tb_true;
+ }
+ }
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // find string table
+ if (!found_strtab && symtab_section.sh_link < header.e_shnum) {
+ if (!tb_stream_seek(istream, header.e_shoff + symtab_section.sh_link * sizeof(xm_elf32_section_t))) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) {
+ return tb_false;
+ }
+ found_strtab = tb_true;
+ }
+
+ if (!found_strtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ tb_uint32_t sym_count = symtab_section.sh_size / sizeof(xm_elf32_symbol_t);
+ if (!tb_stream_seek(istream, symtab_section.sh_offset)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < sym_count; i++) {
+ xm_elf32_symbol_t sym;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
+ return tb_false;
+ }
+
+ // skip NULL symbol
+ if (sym.st_name == 0 && sym.st_value == 0 && sym.st_size == 0) {
+ continue;
+ }
+
+ // get symbol name
+ tb_char_t name[256];
+ if (!xm_binutils_elf_read_string(istream, strtab_section.sh_offset, sym.st_name, name, sizeof(name)) || !name[0]) {
+ continue;
+ }
+
+ // create symbol table entry
+ lua_pushinteger(lua, result_count + 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // value
+ lua_pushstring(lua, "value");
+ lua_pushinteger(lua, sym.st_value);
+ lua_settable(lua, -3);
+
+ // size
+ lua_pushstring(lua, "size");
+ lua_pushinteger(lua, sym.st_size);
+ lua_settable(lua, -3);
+
+ // section
+ lua_pushstring(lua, "section");
+ lua_pushinteger(lua, sym.st_shndx);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, xm_binutils_elf_get_symbol_type(sym.st_info));
+ lua_settable(lua, -3);
+
+ // bind
+ lua_pushstring(lua, "bind");
+ lua_pushstring(lua, xm_binutils_elf_get_symbol_bind(sym.st_info));
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ result_count++;
+ }
+
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read ELF header
+ xm_elf64_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find .symtab section
+ xm_elf64_section_t symtab_section;
+ xm_elf64_section_t strtab_section;
+ tb_bool_t found_symtab = tb_false;
+ tb_bool_t found_strtab = tb_false;
+
+ if (!tb_stream_seek(istream, header.e_shoff)) {
+ return tb_false;
+ }
+
+ for (tb_uint16_t i = 0; i < header.e_shnum; i++) {
+ xm_elf64_section_t section;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&section, sizeof(section))) {
+ return tb_false;
+ }
+
+ if (section.sh_type == XM_ELF_SHT_SYMTAB) {
+ symtab_section = section;
+ found_symtab = tb_true;
+ } else if (section.sh_type == XM_ELF_SHT_STRTAB && section.sh_link == 0) {
+ if (found_symtab && symtab_section.sh_link == i) {
+ strtab_section = section;
+ found_strtab = tb_true;
+ }
+ }
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // find string table
+ if (!found_strtab && symtab_section.sh_link < header.e_shnum) {
+ if (!tb_stream_seek(istream, header.e_shoff + symtab_section.sh_link * sizeof(xm_elf64_section_t))) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) {
+ return tb_false;
+ }
+ found_strtab = tb_true;
+ }
+
+ if (!found_strtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ tb_uint32_t sym_count = (tb_uint32_t)(symtab_section.sh_size / sizeof(xm_elf64_symbol_t));
+ if (!tb_stream_seek(istream, symtab_section.sh_offset)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < sym_count; i++) {
+ xm_elf64_symbol_t sym;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
+ return tb_false;
+ }
+
+ // skip NULL symbol
+ if (sym.st_name == 0 && sym.st_value == 0 && sym.st_size == 0) {
+ continue;
+ }
+
+ // get symbol name
+ tb_char_t name[256];
+ if (!xm_binutils_elf_read_string(istream, strtab_section.sh_offset, sym.st_name, name, sizeof(name)) || !name[0]) {
+ continue;
+ }
+
+ // create symbol table entry
+ lua_pushinteger(lua, result_count + 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // value
+ lua_pushstring(lua, "value");
+ lua_pushinteger(lua, sym.st_value);
+ lua_settable(lua, -3);
+
+ // size
+ lua_pushstring(lua, "size");
+ lua_pushinteger(lua, sym.st_size);
+ lua_settable(lua, -3);
+
+ // section
+ lua_pushstring(lua, "section");
+ lua_pushinteger(lua, sym.st_shndx);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, xm_binutils_elf_get_symbol_type(sym.st_info));
+ lua_settable(lua, -3);
+
+ // bind
+ lua_pushstring(lua, "bind");
+ lua_pushstring(lua, xm_binutils_elf_get_symbol_bind(sym.st_info));
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ result_count++;
+ }
+
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read and check ELF magic
+ tb_uint8_t magic[4];
+ if (!xm_binutils_read_magic(istream, magic, 4)) {
+ return tb_false;
+ }
+ if (magic[0] != 0x7f || magic[1] != 'E' || magic[2] != 'L' || magic[3] != 'F') {
+ return tb_false;
+ }
+
+ // check ELF class (32-bit or 64-bit)
+ tb_uint8_t elf_class;
+ if (!tb_stream_seek(istream, 4)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&elf_class, 1)) {
+ return tb_false;
+ }
+
+ if (elf_class == 1) {
+ return xm_binutils_elf_read_symbols_32(istream, lua);
+ } else if (elf_class == 2) {
+ return xm_binutils_elf_read_symbols_64(istream, lua);
+ }
+
+ return tb_false;
+}
+
+
diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h
index b3f0fb511..c11fbebb6 100644
--- a/core/src/xmake/binutils/macho/prefix.h
+++ b/core/src/xmake/binutils/macho/prefix.h
@@ -318,5 +318,70 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const
return (major << 16) | (minor << 8) | patch;
}
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * readsyms inline implementation
+ */
+
+/* read string from Mach-O string table
+ *
+ * @param istream the input stream
+ * @param strtab_offset the string table offset
+ * @param offset the string offset (relative to string table content, after size field)
+ * @param name the buffer to store the string
+ * @param name_size the size of the buffer
+ * @return tb_true on success, tb_false on failure
+ */
+static __tb_inline__ tb_bool_t xm_binutils_macho_read_string(tb_stream_ref_t istream, tb_uint32_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) {
+ tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);
+
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ // string table starts with 4-byte size field, so offset is from after that
+ if (!tb_stream_seek(istream, strtab_offset + 4 + offset)) {
+ return tb_false;
+ }
+
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ tb_stream_seek(istream, saved_pos);
+ return tb_true;
+}
+
+/* get symbol type string from Mach-O symbol type
+ *
+ * @param type the symbol type byte
+ * @return the type string
+ */
+static __tb_inline__ tb_char_t const *xm_binutils_macho_get_symbol_type(tb_uint8_t type) {
+ tb_uint8_t n_type = type & XM_MACHO_N_TYPE_MASK;
+ switch (n_type) {
+ case XM_MACHO_N_TYPE_SECT: return "section";
+ default: return "unknown";
+ }
+}
+
+/* get symbol bind string from Mach-O symbol type
+ *
+ * @param type the symbol type byte
+ * @return the bind string
+ */
+static __tb_inline__ tb_char_t const *xm_binutils_macho_get_symbol_bind(tb_uint8_t type) {
+ if (type & XM_MACHO_N_EXT) {
+ return "external";
+ }
+ return "local";
+}
+
#endif
diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c
new file mode 100644
index 000000000..a78daaa5a
--- /dev/null
+++ b/core/src/xmake/binutils/macho/readsyms.c
@@ -0,0 +1,285 @@
+/*!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 "readsyms_macho"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read Mach-O header
+ xm_macho_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find LC_SYMTAB command
+ xm_macho_symtab_command_t symtab_cmd;
+ tb_bool_t found_symtab = tb_false;
+
+ tb_uint32_t offset = sizeof(header);
+ for (tb_uint32_t i = 0; i < header.ncmds; i++) {
+ tb_uint32_t cmd;
+ tb_uint32_t cmdsize;
+
+ if (!tb_stream_seek(istream, offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&cmd, 4)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&cmdsize, 4)) {
+ return tb_false;
+ }
+
+ if (cmd == XM_MACHO_LC_SYMTAB) {
+ if (!tb_stream_seek(istream, offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&symtab_cmd, sizeof(symtab_cmd))) {
+ return tb_false;
+ }
+ found_symtab = tb_true;
+ break;
+ }
+
+ offset += cmdsize;
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ if (!tb_stream_seek(istream, symtab_cmd.symoff)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < symtab_cmd.nsyms; i++) {
+ xm_macho_nlist_t nlist;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&nlist, sizeof(nlist))) {
+ return tb_false;
+ }
+
+ // skip NULL symbols
+ if (nlist.strx == 0) {
+ continue;
+ }
+
+ // get symbol name
+ tb_char_t name[256];
+ if (!xm_binutils_macho_read_string(istream, symtab_cmd.stroff, nlist.strx, name, sizeof(name)) || !name[0]) {
+ continue;
+ }
+
+ // create symbol table entry
+ lua_pushinteger(lua, result_count + 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // value
+ lua_pushstring(lua, "value");
+ lua_pushinteger(lua, nlist.value);
+ lua_settable(lua, -3);
+
+ // section
+ lua_pushstring(lua, "section");
+ lua_pushinteger(lua, nlist.sect);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, xm_binutils_macho_get_symbol_type(nlist.type));
+ lua_settable(lua, -3);
+
+ // bind
+ lua_pushstring(lua, "bind");
+ lua_pushstring(lua, xm_binutils_macho_get_symbol_bind(nlist.type));
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ result_count++;
+ }
+
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read Mach-O header
+ xm_macho_header_64_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find LC_SYMTAB command
+ xm_macho_symtab_command_t symtab_cmd;
+ tb_bool_t found_symtab = tb_false;
+
+ tb_uint32_t offset = sizeof(header);
+ for (tb_uint32_t i = 0; i < header.ncmds; i++) {
+ tb_uint32_t cmd;
+ tb_uint32_t cmdsize;
+
+ if (!tb_stream_seek(istream, offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&cmd, 4)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&cmdsize, 4)) {
+ return tb_false;
+ }
+
+ if (cmd == XM_MACHO_LC_SYMTAB) {
+ if (!tb_stream_seek(istream, offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&symtab_cmd, sizeof(symtab_cmd))) {
+ return tb_false;
+ }
+ found_symtab = tb_true;
+ break;
+ }
+
+ offset += cmdsize;
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ if (!tb_stream_seek(istream, symtab_cmd.symoff)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < symtab_cmd.nsyms; i++) {
+ xm_macho_nlist_64_t nlist;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&nlist, sizeof(nlist))) {
+ return tb_false;
+ }
+
+ // skip NULL symbols
+ if (nlist.strx == 0) {
+ continue;
+ }
+
+ // get symbol name
+ tb_char_t name[256];
+ if (!xm_binutils_macho_read_string(istream, symtab_cmd.stroff, nlist.strx, name, sizeof(name)) || !name[0]) {
+ continue;
+ }
+
+ // create symbol table entry
+ lua_pushinteger(lua, result_count + 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // value
+ lua_pushstring(lua, "value");
+ lua_pushinteger(lua, nlist.value);
+ lua_settable(lua, -3);
+
+ // section
+ lua_pushstring(lua, "section");
+ lua_pushinteger(lua, nlist.sect);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, xm_binutils_macho_get_symbol_type(nlist.type));
+ lua_settable(lua, -3);
+
+ // bind
+ lua_pushstring(lua, "bind");
+ lua_pushstring(lua, xm_binutils_macho_get_symbol_bind(nlist.type));
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ result_count++;
+ }
+
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read and check magic
+ tb_uint8_t magic_bytes[4];
+ if (!xm_binutils_read_magic(istream, magic_bytes, 4)) {
+ return tb_false;
+ }
+
+ // convert to uint32_t (little endian, Mach-O uses native endian)
+ tb_uint32_t magic = (tb_uint32_t)magic_bytes[0] |
+ ((tb_uint32_t)magic_bytes[1] << 8) |
+ ((tb_uint32_t)magic_bytes[2] << 16) |
+ ((tb_uint32_t)magic_bytes[3] << 24);
+
+ if (magic == XM_MACHO_MAGIC_32) {
+ return xm_binutils_macho_read_symbols_32(istream, lua);
+ } else if (magic == XM_MACHO_MAGIC_64) {
+ return xm_binutils_macho_read_symbols_64(istream, lua);
+ }
+
+ return tb_false;
+}
+
+
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index 739e220f7..74cef7cfe 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -27,9 +27,106 @@
#include "../prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
+ * macros
+ */
+#define XM_BINUTILS_FORMAT_COFF 0
+#define XM_BINUTILS_FORMAT_ELF 1
+#define XM_BINUTILS_FORMAT_MACHO 2
+#define XM_BINUTILS_FORMAT_UNKNOWN 3
+
+/* COFF machine types (for format detection) */
+#define XM_BINUTILS_COFF_MACHINE_I386 0x014c
+#define XM_BINUTILS_COFF_MACHINE_AMD64 0x8664
+#define XM_BINUTILS_COFF_MACHINE_ARM 0x01c0
+#define XM_BINUTILS_COFF_MACHINE_ARM64 0xaa64
+
+/* //////////////////////////////////////////////////////////////////////////////////////
* inline implementation
*/
+/* read magic bytes from stream (preserves stream position)
+ *
+ * @param istream the input stream
+ * @param magic the buffer to store magic bytes (must be at least 4 bytes)
+ * @param size the number of bytes to read (typically 4)
+ *
+ * @return tb_true on success, tb_false on failure
+ */
+static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, tb_uint8_t *magic, tb_size_t size) {
+ tb_assert_and_check_return_val(istream && magic && size > 0, tb_false);
+
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+
+ tb_bool_t ok = tb_false;
+ if (tb_stream_bread(istream, magic, size)) {
+ ok = tb_true;
+ }
+
+ tb_stream_seek(istream, saved_pos);
+ return ok;
+}
+
+/* detect object file format from stream
+ *
+ * @param istream the input stream
+ * @return XM_BINUTILS_FORMAT_COFF, XM_BINUTILS_FORMAT_ELF, XM_BINUTILS_FORMAT_MACHO,
+ * XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error
+ */
+static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) {
+ tb_assert_and_check_return_val(istream, -1);
+
+ // read magic bytes
+ tb_uint8_t magic[4];
+ if (!xm_binutils_read_magic(istream, magic, 4)) {
+ return -1;
+ }
+
+ // check ELF magic (0x7f 'E' 'L' 'F')
+ if (magic[0] == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F') {
+ return XM_BINUTILS_FORMAT_ELF;
+ }
+
+ // check Mach-O magic
+ if (magic[0] == 0xfe && magic[1] == 0xed && magic[2] == 0xfa &&
+ (magic[3] == 0xce || magic[3] == 0xcf)) {
+ return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32/64 (big endian)
+ }
+ if (magic[0] == 0xce && magic[1] == 0xfa && magic[2] == 0xed && magic[3] == 0xfe) {
+ return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32 (little endian)
+ }
+ if (magic[0] == 0xcf && magic[1] == 0xfa && magic[2] == 0xed && magic[3] == 0xfe) {
+ return XM_BINUTILS_FORMAT_MACHO; // Mach-O 64 (little endian)
+ }
+
+ // check COFF (object files start with machine type, not a magic number)
+ // COFF header: machine (2 bytes) + nsects (2 bytes) + time (4 bytes) + ...
+ // Read machine type to verify if it's a valid COFF file
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ tb_uint16_t machine;
+ if (!tb_stream_seek(istream, 0)) {
+ return -1;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&machine, 2)) {
+ tb_stream_seek(istream, saved_pos);
+ return -1;
+ }
+ tb_stream_seek(istream, saved_pos);
+
+ // check if it's a valid COFF machine type
+ if (machine == XM_BINUTILS_COFF_MACHINE_I386 ||
+ machine == XM_BINUTILS_COFF_MACHINE_AMD64 ||
+ machine == XM_BINUTILS_COFF_MACHINE_ARM ||
+ machine == XM_BINUTILS_COFF_MACHINE_ARM64) {
+ return XM_BINUTILS_FORMAT_COFF;
+ }
+
+ // unknown format
+ return XM_BINUTILS_FORMAT_UNKNOWN;
+}
+
/* copy data from input stream to output stream
*
* @param istream the input stream
@@ -47,18 +144,13 @@ static __tb_inline__ tb_bool_t xm_binutils_stream_copy(tb_stream_ref_t istream,
tb_size_t need = (tb_size_t)tb_min(size - writ, (tb_hize_t)TB_STREAM_BLOCK_MAXN);
tb_check_break(need);
- tb_long_t real = tb_stream_read(istream, data, need);
- if (real > 0) {
- if (!tb_stream_bwrit(ostream, data, (tb_size_t)real)) {
- return tb_false;
- }
- writ += real;
- } else if (!real) {
- tb_long_t wait = tb_stream_wait(istream, TB_STREAM_WAIT_READ, tb_stream_timeout(istream));
- tb_check_break(wait > 0 && (wait & TB_STREAM_WAIT_READ));
- } else {
+ if (!tb_stream_bread(istream, data, need)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, data, need)) {
return tb_false;
}
+ writ += need;
tb_check_break(writ < size);
} while (1);
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
new file mode 100644
index 000000000..dbb8e5957
--- /dev/null
+++ b/core/src/xmake/binutils/readsyms.c
@@ -0,0 +1,122 @@
+/*!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 "readsyms"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+#include "coff/prefix.h"
+#include "elf/prefix.h"
+#include "macho/prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* read symbols from object file (auto-detect format)
+ *
+ * @param lua the lua state
+ * @return 1 on success (table on stack), 2 on failure (with error message on stack)
+ */
+tb_int_t xm_binutils_readsyms(lua_State *lua) {
+ tb_assert_and_check_return_val(lua, 0);
+
+ // get the object file path
+ tb_char_t const *objectfile = luaL_checkstring(lua, 1);
+ tb_check_return_val(objectfile, 0);
+
+ // open file
+ 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);
+ return 2;
+ }
+
+ tb_bool_t ok = tb_false;
+ do {
+ if (!tb_stream_open(istream)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: open %s failed", objectfile);
+ break;
+ }
+
+ // detect format
+ tb_int_t format = xm_binutils_detect_format(istream);
+ if (format < 0) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: cannot detect file format");
+ break;
+ }
+
+ // read symbols based on format
+ if (format == XM_BINUTILS_FORMAT_COFF) {
+ // COFF
+ if (!xm_binutils_coff_read_symbols(istream, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read COFF symbols failed");
+ break;
+ }
+ } else if (format == XM_BINUTILS_FORMAT_ELF) {
+ // ELF
+ if (!xm_binutils_elf_read_symbols(istream, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read ELF symbols failed");
+ break;
+ }
+ } else if (format == XM_BINUTILS_FORMAT_MACHO) {
+ // Mach-O
+ if (!xm_binutils_macho_read_symbols(istream, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read Mach-O symbols failed");
+ break;
+ }
+ } else {
+ // unknown or unsupported format
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: unsupported or unknown file format");
+ break;
+ }
+
+ ok = tb_true;
+ } while (0);
+
+ if (istream) {
+ tb_stream_clos(istream);
+ }
+ istream = tb_null;
+
+ return ok ? 1 : 2;
+}
+
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index 13e8747e0..4f2702f52 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -339,6 +339,7 @@ tb_int_t xm_binutils_bin2c(lua_State *lua);
tb_int_t xm_binutils_bin2coff(lua_State *lua);
tb_int_t xm_binutils_bin2macho(lua_State *lua);
tb_int_t xm_binutils_bin2elf(lua_State *lua);
+tb_int_t xm_binutils_readsyms(lua_State *lua);
#ifdef XM_CONFIG_API_HAVE_CURSES
// register curses functions
@@ -661,6 +662,7 @@ static luaL_Reg const g_binutils_functions[] = {
{ "bin2coff", xm_binutils_bin2coff },
{ "bin2macho", xm_binutils_bin2macho },
{ "bin2elf", xm_binutils_bin2elf },
+ { "readsyms", xm_binutils_readsyms },
{ tb_null, tb_null },
};
diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua
index b21de2276..8047046b2 100644
--- a/xmake/core/base/binutils.lua
+++ b/xmake/core/base/binutils.lua
@@ -26,6 +26,7 @@ binutils._bin2c = binutils._bin2c or binutils.bin2c
binutils._bin2coff = binutils._bin2coff or binutils.bin2coff
binutils._bin2macho = binutils._bin2macho or binutils.bin2macho
binutils._bin2elf = binutils._bin2elf or binutils.bin2elf
+binutils._readsyms = binutils._readsyms or binutils.readsyms
-- generate c/c++ code from the binary file
function binutils.bin2c(binaryfile, outputfile, opt)
@@ -56,6 +57,15 @@ function binutils.bin2elf(binaryfile, outputfile, opt)
return binutils._bin2elf(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.arch or "x86_64", opt.basename, opt.zeroend or false)
end
+-- read symbols from object file (auto-detect format: COFF, ELF, or Mach-O)
+function binutils.readsyms(binaryfile)
+ if binutils._readsyms then
+ return binutils._readsyms(binaryfile)
+ else
+ return nil, "readsyms: C implementation not available"
+ end
+end
+
-- return module
return binutils
diff --git a/xmake/core/sandbox/modules/import/core/base/binutils.lua b/xmake/core/sandbox/modules/import/core/base/binutils.lua
index 38aad9b73..9e371d777 100644
--- a/xmake/core/sandbox/modules/import/core/base/binutils.lua
+++ b/xmake/core/sandbox/modules/import/core/base/binutils.lua
@@ -57,6 +57,16 @@ function sandbox_core_base_binutils.bin2elf(binaryfile, outputfile, opt)
end
end
+-- read symbols from object file (auto-detect format: ELF, COFF, Mach-O)
+function sandbox_core_base_binutils.readsyms(binaryfile)
+ local symbols, errors = binutils.readsyms(binaryfile)
+ if symbols then
+ return symbols
+ else
+ raise("readsyms: %s", errors or "unknown errors")
+ end
+end
+
-- return module
return sandbox_core_base_binutils
diff --git a/xmake/core/sandbox/modules/utils.lua b/xmake/core/sandbox/modules/utils.lua
index df90492be..1be9c7c08 100644
--- a/xmake/core/sandbox/modules/utils.lua
+++ b/xmake/core/sandbox/modules/utils.lua
@@ -150,7 +150,7 @@ function sandbox_utils.assert(value, format, ...)
end
-- generate c/c++ code from the binary file (deprecated, use binutils.bin2c instead)
-function sandbox_utils.bin2c(binaryfile, outputfile, opt)
+ function sandbox_utils.bin2c(binaryfile, outputfile, opt)
deprecated.add("binutils.bin2c", "utils.bin2c")
return binutils.bin2c(binaryfile, outputfile, opt)
end
diff --git a/xmake/modules/utils/binary/readsyms.lua b/xmake/modules/utils/binary/readsyms.lua
new file mode 100644
index 000000000..ace1c8be3
--- /dev/null
+++ b/xmake/modules/utils/binary/readsyms.lua
@@ -0,0 +1,92 @@
+--!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.lua
+--
+
+-- imports
+import("core.base.binutils")
+
+-- get symbols from binary file (auto-detect format: ELF, COFF, Mach-O)
+--
+-- @param binaryfile the binary file path (required)
+-- @return the symbols table
+function _get_symbols(binaryfile)
+ assert(binaryfile, "usage: xmake l utils.binary.readsyms <binaryfile>")
+
+ binaryfile = path.absolute(binaryfile)
+ assert(os.isfile(binaryfile), "%s not found!", binaryfile)
+
+ if binutils.readsyms then
+ local ok, symbols = binutils.readsyms(binaryfile)
+ if ok then
+ return symbols
+ else
+ raise("readsyms: %s", symbols or "unknown error")
+ end
+ else
+ raise("readsyms: binutils.readsyms not available (C implementation not compiled)")
+ end
+end
+
+-- dump symbols to console
+--
+-- @param binaryfile the object file path (required)
+function dump(binaryfile)
+ local symbols = _get_symbols(binaryfile)
+ if symbols and #symbols > 0 then
+ print("")
+ print("Symbols:")
+ for i, sym in ipairs(symbols) do
+ local value_str = ""
+ if sym.value then
+ value_str = string.format("0x%x", sym.value)
+ end
+ local size_str = ""
+ if sym.size then
+ size_str = string.format(" size=%d", sym.size)
+ end
+ local section_str = ""
+ if sym.section and sym.section > 0 then
+ section_str = string.format(" section=%d", sym.section)
+ end
+ local type_str = ""
+ if sym.type then
+ type_str = string.format(" type=%s", sym.type)
+ end
+ local bind_str = ""
+ if sym.bind then
+ bind_str = string.format(" bind=%s", sym.bind)
+ end
+ print(string.format(" %s %s%s%s%s%s", sym.name or "", value_str, size_str, section_str, type_str, bind_str))
+ end
+ print("")
+ cprint("${bright}%d symbols found!", #symbols)
+ else
+ print("")
+ cprint("${bright}No symbols found!")
+ end
+end
+
+-- read symbols from object file (auto-detect format: ELF, COFF, Mach-O)
+--
+-- @param binaryfile the object file path (required)
+-- @return the symbols table
+function main(binaryfile)
+ return _get_symbols(binaryfile)
+end
+