diff options
| author | ruki <[email protected]> | 2025-12-09 00:29:51 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-09 00:29:51 +0800 |
| commit | ba293bc4b8840da34220e1f54313d531d18dde8e (patch) | |
| tree | e6b6325ccf446b845b26a3a21f06f0bd84914170 | |
| parent | f1726a8f0f63160ae2185f28b38a740caf6d1ab1 (diff) | |
add binary readsyms
| -rw-r--r-- | core/src/xmake/binutils/coff/prefix.h | 104 | ||||
| -rw-r--r-- | core/src/xmake/binutils/coff/readsyms.c | 133 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/prefix.h | 69 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/readsyms.c | 325 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/prefix.h | 65 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/readsyms.c | 285 | ||||
| -rw-r--r-- | core/src/xmake/binutils/prefix.h | 112 | ||||
| -rw-r--r-- | core/src/xmake/binutils/readsyms.c | 122 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 2 | ||||
| -rw-r--r-- | xmake/core/base/binutils.lua | 10 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/binutils.lua | 10 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/utils.lua | 2 | ||||
| -rw-r--r-- | xmake/modules/utils/binary/readsyms.lua | 92 |
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*)§ion, 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*)§ion, 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 + |
