diff options
| author | ruki <[email protected]> | 2025-12-12 23:12:57 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-12 23:12:57 +0800 |
| commit | 97c17dc338a66bbbae36eef59c4d4ca537bff8ec (patch) | |
| tree | 3cb7a0c517871c44265d709bb91102cac520b73c /core/src | |
| parent | b7e9857eff9a4425237e8eeb5c666fb4570eec76 (diff) | |
add deplibs in binutils
Diffstat (limited to 'core/src')
| -rw-r--r-- | core/src/xmake/binutils/coff/deplibs.c | 192 | ||||
| -rw-r--r-- | core/src/xmake/binutils/deplibs.c | 124 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/deplibs.c | 227 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/prefix.h | 22 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/deplibs.c | 155 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/prefix.h | 96 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/readsyms.c | 58 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 2 |
8 files changed, 818 insertions, 58 deletions
diff --git a/core/src/xmake/binutils/coff/deplibs.c b/core/src/xmake/binutils/coff/deplibs.c new file mode 100644 index 000000000..40d9d2823 --- /dev/null +++ b/core/src/xmake/binutils/coff/deplibs.c @@ -0,0 +1,192 @@ +/*!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 deplibs.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "deplibs_coff" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +tb_bool_t xm_binutils_coff_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, 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, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { + return tb_false; + } + + // read section headers + tb_hize_t section_offset = base_offset + sizeof(xm_coff_header_t) + header.opthdr; + if (!tb_stream_seek(istream, section_offset)) { + return tb_false; + } + + tb_size_t result_count = 0; + for (tb_uint16_t i = 0; i < header.nsects; i++) { + xm_coff_section_t section; + if (!tb_stream_bread(istream, (tb_byte_t*)§ion, sizeof(section))) { + return tb_false; + } + + // check if it is .idata section (import directory table) + // section name is 8 bytes, null-padded if shorter, or starts with '/' for long names + // standard import section is named ".idata" + if (tb_strncmp(section.name, ".idata", 6) == 0) { + // read import directory table + // The .idata section contains the Import Directory Table. + // Each entry is 20 bytes (IMAGE_IMPORT_DESCRIPTOR). + // The table ends with a null entry. + + // The .idata section usually contains multiple parts. + // We need to parse the Import Directory Table which is typically at the beginning of the section data. + // However, the section data might be raw data at 'ofs' + + // The VirtualAddress (vaddr) in the section header is the RVA where the section is loaded. + // The PointerToRawData (ofs) is the file offset. + + // We need to iterate over IMAGE_IMPORT_DESCRIPTOR entries. + // struct IMAGE_IMPORT_DESCRIPTOR { + // DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + // DWORD TimeDateStamp; // 0 if not bound, + // DWORD ForwarderChain; // -1 if no forwarders + // DWORD Name; // RVA to DLL name + // DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) + // }; + + // We need to map RVA to file offset. + // RVA = vaddr + offset_in_section + // FileOffset = ofs + offset_in_section + // So, offset_in_section = RVA - vaddr + // FileOffset = ofs + (RVA - vaddr) + + tb_uint32_t import_descriptors_offset = section.ofs; + if (!tb_stream_seek(istream, base_offset + import_descriptors_offset)) { + return tb_false; + } + + while (1) { + tb_uint32_t original_first_thunk; + tb_uint32_t time_date_stamp; + tb_uint32_t forwarder_chain; + tb_uint32_t name_rva; + tb_uint32_t first_thunk; + + if (!tb_stream_bread(istream, (tb_byte_t*)&original_first_thunk, 4)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)&time_date_stamp, 4)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)&forwarder_chain, 4)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)&name_rva, 4)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)&first_thunk, 4)) break; + + // check for null entry (end of table) + if (original_first_thunk == 0 && name_rva == 0) { + break; + } + + if (name_rva != 0) { + // map RVA to file offset to read the name + // We need to find the section that contains this RVA. + // Since we are iterating sections, we might need to seek back to read section headers again or cache them. + // For simplicity, we assume the name string is within the same .idata section or we can find it by scanning sections. + + // To do this correctly, we should scan all sections to find which one contains the RVA. + // But here we are inside a loop iterating sections. + // We can save current position and iterate sections from the beginning (or cached) to find the RVA. + + // Optimization: usually the name is in the same .idata section or a nearby .rdata section. + + tb_hize_t saved_pos_inner = tb_stream_offset(istream); + + // Find the section containing name_rva + tb_uint32_t name_file_offset = 0; + + // check current section first + if (name_rva >= section.vaddr && name_rva < section.vaddr + section.vsize) { + name_file_offset = section.ofs + (name_rva - section.vaddr); + } else { + // Scan all sections to find the RVA + tb_hize_t saved_pos_sections = tb_stream_offset(istream); + if (tb_stream_seek(istream, section_offset)) { + for (tb_uint16_t k = 0; k < header.nsects; k++) { + xm_coff_section_t s; + if (!tb_stream_bread(istream, (tb_byte_t*)&s, sizeof(s))) { + break; + } + if (name_rva >= s.vaddr && name_rva < s.vaddr + s.vsize) { + name_file_offset = s.ofs + (name_rva - s.vaddr); + break; + } + } + } + tb_stream_seek(istream, saved_pos_sections); // restore to current descriptor + } + + if (name_file_offset != 0) { + if (tb_stream_seek(istream, base_offset + name_file_offset)) { + tb_char_t dll_name[256]; + tb_size_t pos = 0; + tb_byte_t c; + while (pos < sizeof(dll_name) - 1) { + if (!tb_stream_bread(istream, &c, 1)) break; + if (c == 0) break; + dll_name[pos++] = (tb_char_t)c; + } + dll_name[pos] = '\0'; + + if (pos > 0) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, dll_name); + lua_settable(lua, -3); + result_count++; + } + } + } + + tb_stream_seek(istream, saved_pos_inner); + } + } + // We found .idata and processed it. Usually there is only one import table. + // But we should continue just in case or break? + // Typically break is enough after processing the import table. + break; + } + } + + if (result_count == 0) { + lua_newtable(lua); + } + return tb_true; +} diff --git a/core/src/xmake/binutils/deplibs.c b/core/src/xmake/binutils/deplibs.c new file mode 100644 index 000000000..9ca621f1a --- /dev/null +++ b/core/src/xmake/binutils/deplibs.c @@ -0,0 +1,124 @@ +/*!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 deplibs.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "deplibs" +#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_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); +extern tb_bool_t xm_binutils_elf_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); +extern tb_bool_t xm_binutils_macho_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* get dependent libraries from binary 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_deplibs(lua_State *lua) { + tb_assert_and_check_return_val(lua, 0); + + // get the binary file path + tb_char_t const *binaryfile = luaL_checkstring(lua, 1); + tb_check_return_val(binaryfile, 0); + + // open file + tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); + if (!istream) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: open %s failed", binaryfile); + return 2; + } + + tb_bool_t ok = tb_false; + do { + if (!tb_stream_open(istream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: open %s failed", binaryfile); + break; + } + + // detect format + tb_int_t format = xm_binutils_detect_format(istream); + if (format < 0) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: cannot detect file format"); + break; + } + + // create result list + lua_newtable(lua); + + // get dependents based on format + if (format == XM_BINUTILS_FORMAT_COFF) { + if (!xm_binutils_coff_deplibs(istream, 0, lua)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to parse COFF"); + break; + } + } else if (format == XM_BINUTILS_FORMAT_ELF) { + if (!xm_binutils_elf_deplibs(istream, 0, lua)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to parse ELF"); + break; + } + } else if (format == XM_BINUTILS_FORMAT_MACHO) { + if (!xm_binutils_macho_deplibs(istream, 0, lua)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to parse Mach-O"); + break; + } + } else { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: unsupported format %d", format); + break; + } + + + ok = tb_true; + + } while (0); + + if (istream) { + tb_stream_exit(istream); + } + return ok ? 1 : 2; +} diff --git a/core/src/xmake/binutils/elf/deplibs.c b/core/src/xmake/binutils/elf/deplibs.c new file mode 100644 index 000000000..85e346192 --- /dev/null +++ b/core/src/xmake/binutils/elf/deplibs.c @@ -0,0 +1,227 @@ +/*!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 deplibs.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "deplibs_elf" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +static tb_bool_t xm_binutils_elf_deplibs_32(tb_stream_ref_t istream, tb_hize_t base_offset, 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, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { + return tb_false; + } + + // find .dynamic section + xm_elf32_section_t dynamic_section; + tb_bool_t found_dynamic = tb_false; + + if (!tb_stream_seek(istream, base_offset + 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_DYNAMIC) { + dynamic_section = section; + found_dynamic = tb_true; + break; + } + } + + if (!found_dynamic) { + return tb_true; // no dynamic section + } + + // find string table for dynamic section + // sh_link of dynamic section contains the section header index of the string table + xm_elf32_section_t strtab_section; + if (!tb_stream_seek(istream, base_offset + header.e_shoff + dynamic_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; + } + + // read dynamic entries + tb_uint32_t count = dynamic_section.sh_size / sizeof(xm_elf32_dynamic_t); + if (!tb_stream_seek(istream, base_offset + dynamic_section.sh_offset)) { + return tb_false; + } + + tb_size_t result_count = 0; + for (tb_uint32_t i = 0; i < count; i++) { + xm_elf32_dynamic_t dyn; + if (!tb_stream_bread(istream, (tb_byte_t*)&dyn, sizeof(dyn))) { + return tb_false; + } + + if (dyn.d_tag == XM_ELF_DT_NULL) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NEEDED) { + tb_char_t name[256]; + if (xm_binutils_elf_read_string(istream, base_offset + strtab_section.sh_offset, dyn.d_un.d_val, name, sizeof(name)) && name[0]) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, name); + lua_settable(lua, -3); + result_count++; + } + } + } + + return tb_true; +} + +static tb_bool_t xm_binutils_elf_deplibs_64(tb_stream_ref_t istream, tb_hize_t base_offset, 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, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { + return tb_false; + } + + // find .dynamic section + xm_elf64_section_t dynamic_section; + tb_bool_t found_dynamic = tb_false; + + if (!tb_stream_seek(istream, base_offset + 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_DYNAMIC) { + dynamic_section = section; + found_dynamic = tb_true; + break; + } + } + + if (!found_dynamic) { + return tb_true; // no dynamic section + } + + // find string table for dynamic section + // sh_link of dynamic section contains the section header index of the string table + xm_elf64_section_t strtab_section; + if (!tb_stream_seek(istream, base_offset + header.e_shoff + dynamic_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; + } + + // read dynamic entries + tb_uint32_t count = (tb_uint32_t)(dynamic_section.sh_size / sizeof(xm_elf64_dynamic_t)); + if (!tb_stream_seek(istream, base_offset + dynamic_section.sh_offset)) { + return tb_false; + } + + tb_size_t result_count = 0; + for (tb_uint32_t i = 0; i < count; i++) { + xm_elf64_dynamic_t dyn; + if (!tb_stream_bread(istream, (tb_byte_t*)&dyn, sizeof(dyn))) { + return tb_false; + } + + if (dyn.d_tag == XM_ELF_DT_NULL) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NEEDED) { + tb_char_t name[256]; + if (xm_binutils_elf_read_string(istream, base_offset + strtab_section.sh_offset, (tb_uint32_t)dyn.d_un.d_val, name, sizeof(name)) && name[0]) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, name); + lua_settable(lua, -3); + result_count++; + } + } + } + + return tb_true; +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +tb_bool_t xm_binutils_elf_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) { + tb_assert_and_check_return_val(istream && lua, tb_false); + + // read and check ELF magic + tb_uint8_t magic[4]; + if (!tb_stream_seek(istream, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(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, base_offset + 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_deplibs_32(istream, base_offset, lua); + } else if (elf_class == 2) { + return xm_binutils_elf_deplibs_64(istream, base_offset, lua); + } + + return tb_false; +} diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index bd3cc9129..ed22d3963 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -49,6 +49,12 @@ #define XM_ELF_SHT_PROGBITS 0x1 #define XM_ELF_SHT_SYMTAB 0x2 #define XM_ELF_SHT_STRTAB 0x3 +#define XM_ELF_SHT_DYNAMIC 0x6 + +#define XM_ELF_DT_NULL 0 +#define XM_ELF_DT_NEEDED 1 +#define XM_ELF_DT_STRTAB 5 +#define XM_ELF_DT_STRSZ 10 #define XM_ELF_SHF_ALLOC 0x2 #define XM_ELF_SHF_WRITE 0x1 @@ -137,6 +143,22 @@ typedef struct __xm_elf64_symbol_t { tb_uint64_t st_value; tb_uint64_t st_size; } __tb_packed__ xm_elf64_symbol_t; + +typedef struct __xm_elf32_dynamic_t { + tb_int32_t d_tag; + union { + tb_uint32_t d_val; + tb_uint32_t d_ptr; + } d_un; +} __tb_packed__ xm_elf32_dynamic_t; + +typedef struct __xm_elf64_dynamic_t { + tb_int64_t d_tag; + union { + tb_uint64_t d_val; + tb_uint64_t d_ptr; + } d_un; +} __tb_packed__ xm_elf64_dynamic_t; #include "tbox/prefix/packed.h" /* ////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/xmake/binutils/macho/deplibs.c b/core/src/xmake/binutils/macho/deplibs.c new file mode 100644 index 000000000..fbea979c7 --- /dev/null +++ b/core/src/xmake/binutils/macho/deplibs.c @@ -0,0 +1,155 @@ +/*!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 deplibs.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "deplibs_macho" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ +tb_bool_t xm_binutils_macho_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, 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, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { + return tb_false; + } + + tb_bool_t swap = tb_false; + tb_bool_t is64 = tb_false; + + // check magic + if (header.magic == XM_MACHO_MAGIC_32) { + is64 = tb_false; + } else if (header.magic == XM_MACHO_MAGIC_32_BE) { + is64 = tb_false; + swap = tb_true; + } else if (header.magic == XM_MACHO_MAGIC_64) { + is64 = tb_true; + } else if (header.magic == XM_MACHO_MAGIC_64_BE) { + is64 = tb_true; + swap = tb_true; + } else { + return tb_false; // Not a Mach-O file + } + + // re-read header for 64-bit if needed, or just use 32-bit part and skip reserved + tb_uint32_t ncmds = 0; + + if (is64) { + xm_macho_header_64_t header64; + if (!tb_stream_seek(istream, base_offset)) { + return tb_false; + } + if (!tb_stream_bread(istream, (tb_byte_t*)&header64, sizeof(header64))) { + return tb_false; + } + xm_binutils_macho_swap_header_64(&header64, swap); + ncmds = header64.ncmds; + } else { + xm_binutils_macho_swap_header_32(&header, swap); + ncmds = header.ncmds; + } + + // skip header to reach load commands + tb_size_t header_size = is64 ? sizeof(xm_macho_header_64_t) : sizeof(xm_macho_header_t); + if (!tb_stream_seek(istream, base_offset + header_size)) { + return tb_false; + } + + lua_newtable(lua); + tb_size_t result_count = 0; + + // iterate load commands + for (tb_uint32_t i = 0; i < ncmds; i++) { + xm_macho_load_command_t lc; + tb_hize_t current_cmd_offset = tb_stream_offset(istream); + + if (!tb_stream_bread(istream, (tb_byte_t*)&lc, sizeof(lc))) { + break; + } + xm_binutils_macho_swap_load_command(&lc, swap); + + // check for LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB + // LC_LOAD_DYLIB = 0xc + // LC_LOAD_WEAK_DYLIB = 0x18 | 0x80000000 + // LC_REEXPORT_DYLIB = 0x1f | 0x80000000 + + if (lc.cmd == 0xc || lc.cmd == (0x18 | 0x80000000) || lc.cmd == (0x1f | 0x80000000)) { + + xm_macho_dylib_command_t dc; + if (tb_stream_seek(istream, current_cmd_offset)) { + if (tb_stream_bread(istream, (tb_byte_t*)&dc, sizeof(dc))) { + xm_binutils_macho_swap_dylib_command(&dc, swap); + + tb_uint32_t name_offset = dc.dylib.offset; + if (name_offset < lc.cmdsize) { + // name is at current_cmd_offset + name_offset + if (tb_stream_seek(istream, current_cmd_offset + name_offset)) { + tb_char_t dylib_path[1024]; + tb_size_t max_len = lc.cmdsize - name_offset; + if (max_len > sizeof(dylib_path) - 1) max_len = sizeof(dylib_path) - 1; + + tb_size_t pos = 0; + tb_byte_t c; + while (pos < max_len) { + if (!tb_stream_bread(istream, &c, 1)) break; + if (c == 0) break; + dylib_path[pos++] = (tb_char_t)c; + } + dylib_path[pos] = '\0'; + + if (pos > 0) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, dylib_path); + lua_settable(lua, -3); + result_count++; + } + } + } + } + } + } + + // move to next command + if (!tb_stream_seek(istream, current_cmd_offset + lc.cmdsize)) { + break; + } + } + + return tb_true; +} diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index 2d224ff77..d8b8fa157 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -184,12 +184,108 @@ typedef struct __xm_macho_nlist_64_t { tb_uint16_t desc; tb_uint64_t value; } __tb_packed__ xm_macho_nlist_64_t; + +typedef struct __xm_macho_load_command_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; +} __tb_packed__ xm_macho_load_command_t; + +typedef struct __xm_macho_dylib_t { + tb_uint32_t offset; + tb_uint32_t timestamp; + tb_uint32_t current_version; + tb_uint32_t compatibility_version; +} __tb_packed__ xm_macho_dylib_t; + +typedef struct __xm_macho_dylib_command_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; + xm_macho_dylib_t dylib; +} __tb_packed__ xm_macho_dylib_command_t; #include "tbox/prefix/packed.h" + /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation */ +/* byte-swap Mach-O header fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_32(xm_macho_header_t *header, tb_bool_t swap) { + if (swap) { + header->magic = tb_bits_swap_u32(header->magic); + header->cputype = tb_bits_swap_u32(header->cputype); + header->cpusubtype = tb_bits_swap_u32(header->cpusubtype); + header->filetype = tb_bits_swap_u32(header->filetype); + header->ncmds = tb_bits_swap_u32(header->ncmds); + header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds); + header->flags = tb_bits_swap_u32(header->flags); + } +} + +/* byte-swap Mach-O header 64 fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_64(xm_macho_header_64_t *header, tb_bool_t swap) { + if (swap) { + header->magic = tb_bits_swap_u32(header->magic); + header->cputype = tb_bits_swap_u32(header->cputype); + header->cpusubtype = tb_bits_swap_u32(header->cpusubtype); + header->filetype = tb_bits_swap_u32(header->filetype); + header->ncmds = tb_bits_swap_u32(header->ncmds); + header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds); + header->flags = tb_bits_swap_u32(header->flags); + header->reserved = tb_bits_swap_u32(header->reserved); + } +} + +/* byte-swap load command fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_load_command(xm_macho_load_command_t *lc, tb_bool_t swap) { + if (swap) { + lc->cmd = tb_bits_swap_u32(lc->cmd); + lc->cmdsize = tb_bits_swap_u32(lc->cmdsize); + } +} + +/* byte-swap dylib command fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_dylib_command(xm_macho_dylib_command_t *dc, tb_bool_t swap) { + if (swap) { + dc->cmd = tb_bits_swap_u32(dc->cmd); + dc->cmdsize = tb_bits_swap_u32(dc->cmdsize); + dc->dylib.offset = tb_bits_swap_u32(dc->dylib.offset); + dc->dylib.timestamp = tb_bits_swap_u32(dc->dylib.timestamp); + dc->dylib.current_version = tb_bits_swap_u32(dc->dylib.current_version); + dc->dylib.compatibility_version = tb_bits_swap_u32(dc->dylib.compatibility_version); + } +} + +/* byte-swap symtab command fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_symtab_command(xm_macho_symtab_command_t *cmd, tb_bool_t swap) { + if (swap) { + cmd->cmd = tb_bits_swap_u32(cmd->cmd); + cmd->cmdsize = tb_bits_swap_u32(cmd->cmdsize); + cmd->symoff = tb_bits_swap_u32(cmd->symoff); + cmd->nsyms = tb_bits_swap_u32(cmd->nsyms); + cmd->stroff = tb_bits_swap_u32(cmd->stroff); + cmd->strsize = tb_bits_swap_u32(cmd->strsize); + } +} + +/* byte-swap nlist 32 fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_32(xm_macho_nlist_t *nlist, tb_bool_t swap) { + if (swap) { + nlist->strx = tb_bits_swap_u32(nlist->strx); + nlist->desc = tb_bits_swap_u16(nlist->desc); + nlist->value = tb_bits_swap_u32(nlist->value); + } +} + +/* byte-swap nlist 64 fields if needed */ +static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_64(xm_macho_nlist_64_t *nlist, tb_bool_t swap) { + if (swap) { + nlist->strx = tb_bits_swap_u32(nlist->strx); + nlist->desc = tb_bits_swap_u16(nlist->desc); + nlist->value = tb_bits_swap_u64(nlist->value); + } +} + /* get CPU type from architecture string * * @param arch the architecture string (e.g., "x86_64", "i386", "arm64") diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c index 2d4889c58..216975d2a 100644 --- a/core/src/xmake/binutils/macho/readsyms.c +++ b/core/src/xmake/binutils/macho/readsyms.c @@ -29,69 +29,11 @@ * includes */ #include "prefix.h" -#include "tbox/utils/bits.h" /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ -/* byte-swap Mach-O header fields if needed */ -static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_32(xm_macho_header_t *header, tb_bool_t swap) { - if (swap) { - header->magic = tb_bits_swap_u32(header->magic); - header->cputype = tb_bits_swap_u32(header->cputype); - header->cpusubtype = tb_bits_swap_u32(header->cpusubtype); - header->filetype = tb_bits_swap_u32(header->filetype); - header->ncmds = tb_bits_swap_u32(header->ncmds); - header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds); - header->flags = tb_bits_swap_u32(header->flags); - } -} - -/* byte-swap Mach-O header 64 fields if needed */ -static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_64(xm_macho_header_64_t *header, tb_bool_t swap) { - if (swap) { - header->magic = tb_bits_swap_u32(header->magic); - header->cputype = tb_bits_swap_u32(header->cputype); - header->cpusubtype = tb_bits_swap_u32(header->cpusubtype); - header->filetype = tb_bits_swap_u32(header->filetype); - header->ncmds = tb_bits_swap_u32(header->ncmds); - header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds); - header->flags = tb_bits_swap_u32(header->flags); - header->reserved = tb_bits_swap_u32(header->reserved); - } -} - -/* byte-swap symtab command fields if needed */ -static __tb_inline__ tb_void_t xm_binutils_macho_swap_symtab_command(xm_macho_symtab_command_t *cmd, tb_bool_t swap) { - if (swap) { - cmd->cmd = tb_bits_swap_u32(cmd->cmd); - cmd->cmdsize = tb_bits_swap_u32(cmd->cmdsize); - cmd->symoff = tb_bits_swap_u32(cmd->symoff); - cmd->nsyms = tb_bits_swap_u32(cmd->nsyms); - cmd->stroff = tb_bits_swap_u32(cmd->stroff); - cmd->strsize = tb_bits_swap_u32(cmd->strsize); - } -} - -/* byte-swap nlist 32 fields if needed */ -static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_32(xm_macho_nlist_t *nlist, tb_bool_t swap) { - if (swap) { - nlist->strx = tb_bits_swap_u32(nlist->strx); - nlist->desc = tb_bits_swap_u16(nlist->desc); - nlist->value = tb_bits_swap_u32(nlist->value); - } -} - -/* byte-swap nlist 64 fields if needed */ -static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_64(xm_macho_nlist_64_t *nlist, tb_bool_t swap) { - if (swap) { - nlist->strx = tb_bits_swap_u32(nlist->strx); - nlist->desc = tb_bits_swap_u16(nlist->desc); - nlist->value = tb_bits_swap_u64(nlist->value); - } -} - tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua, tb_bool_t swap_bytes) { tb_assert_and_check_return_val(istream && lua, tb_false); diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index 8e1878499..b7d071b65 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -340,6 +340,7 @@ 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); +tb_int_t xm_binutils_deplibs(lua_State *lua); tb_int_t xm_binutils_extractlib(lua_State *lua); #ifdef XM_CONFIG_API_HAVE_CURSES @@ -664,6 +665,7 @@ static luaL_Reg const g_binutils_functions[] = { { "bin2macho", xm_binutils_bin2macho }, { "bin2elf", xm_binutils_bin2elf }, { "readsyms", xm_binutils_readsyms }, + { "deplibs", xm_binutils_deplibs }, { "extractlib", xm_binutils_extractlib }, { tb_null, tb_null }, }; |
