diff options
| author | ruki <[email protected]> | 2025-12-15 06:59:18 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-15 06:59:18 +0800 |
| commit | 777fdde191dc443aba4b987c07247446e10bb5d9 (patch) | |
| tree | 0a655c0f866b956847c5960dbf9fca1d0663577a /core/src | |
| parent | 85f1ef3e70620b3919f4e980eb442025f74d1bdd (diff) | |
| parent | 722a8f23bf29d07b5db27eeeb30e2afe8d8defc5 (diff) | |
Merge pull request #7127 from xmake-io/deplibs
Add deplibs in binutils
Diffstat (limited to 'core/src')
| -rw-r--r-- | core/src/xmake/binutils/coff/deplibs.c | 203 | ||||
| -rw-r--r-- | core/src/xmake/binutils/coff/prefix.h | 112 | ||||
| -rw-r--r-- | core/src/xmake/binutils/deplibs.c | 152 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/deplibs.c | 504 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/prefix.h | 133 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/readsyms.c | 4 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/deplibs.c | 152 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/prefix.h | 100 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/readsyms.c | 58 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/readsyms.c | 2 | ||||
| -rw-r--r-- | core/src/xmake/binutils/prefix.h | 95 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 2 |
12 files changed, 1351 insertions, 166 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..0d9d67ca3 --- /dev/null +++ b/core/src/xmake/binutils/coff/deplibs.c @@ -0,0 +1,203 @@ +/*!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 + */ +static tb_uint32_t xm_binutils_coff_get_import_rva(tb_stream_ref_t istream, tb_uint16_t opthdr_size) { + + // save current offset + tb_hize_t opt_offset = tb_stream_offset(istream); + tb_uint32_t import_rva = 0; + + // read magic + tb_uint16_t magic = 0; + if (tb_stream_bread(istream, (tb_byte_t*)&magic, sizeof(magic))) { + // seek back + if (tb_stream_seek(istream, opt_offset)) { + if (magic == XM_PE32_MAGIC) { + xm_pe32_opt_header_t opt_header = {0}; + if (tb_stream_bread(istream, (tb_byte_t*)&opt_header, tb_min(sizeof(opt_header), opthdr_size))) { + if (opt_header.number_of_rva_and_sizes > 1) { + import_rva = opt_header.data_directory[1].vaddr; + } + } + } else if (magic == XM_PE32P_MAGIC) { + xm_pe32p_opt_header_t opt_header = {0}; + if (tb_stream_bread(istream, (tb_byte_t*)&opt_header, tb_min(sizeof(opt_header), opthdr_size))) { + if (opt_header.number_of_rva_and_sizes > 1) { + import_rva = opt_header.data_directory[1].vaddr; + } + } + } + } + } + return import_rva; +} + + +/* ////////////////////////////////////////////////////////////////////////////////////// + * 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); + + tb_bool_t ok = tb_false; + xm_coff_section_t* sections = tb_null; + do { + // read COFF header + xm_coff_header_t header; + if (!tb_stream_seek(istream, base_offset)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) break; + + // read optional header and import rva + tb_uint32_t import_rva = 0; + if (header.opthdr > 0) { + import_rva = xm_binutils_coff_get_import_rva(istream, header.opthdr); + } + + // read section headers + if (header.nsects > 0) { + sections = tb_nalloc_type(header.nsects, xm_coff_section_t); + if (!sections) break; + + tb_hize_t section_offset = base_offset + sizeof(xm_coff_header_t) + header.opthdr; + if (!tb_stream_seek(istream, section_offset)) break; + if (!tb_stream_bread(istream, (tb_byte_t*)sections, header.nsects * sizeof(xm_coff_section_t))) break; + } + + if (!sections) { + if (header.nsects > 0) break; + ok = tb_true; + break; + } + + tb_size_t result_count = 0; + tb_bool_t failed = tb_false; + for (tb_uint16_t i = 0; i < header.nsects; i++) { + xm_coff_section_t* section = §ions[i]; + + // check if it is .idata section (import directory table) + tb_bool_t found_idt = tb_false; + tb_uint32_t idt_offset = 0; + if (import_rva != 0) { + // check if import rva is in this section + if (import_rva >= section->vaddr && import_rva < section->vaddr + section->vsize) { + idt_offset = section->ofs + (import_rva - section->vaddr); + found_idt = tb_true; + } + } else { + // fallback to check section name + if (tb_strncmp(section->name, ".idata", 6) == 0) { + idt_offset = section->ofs; + found_idt = tb_true; + } + } + + if (found_idt) { + /* 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. + */ + + /* 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) + * }; + */ + + if (!tb_stream_seek(istream, idt_offset)) { + failed = tb_true; + break; + } + + while (1) { + xm_coff_import_directory_table_t entry; + if (!tb_stream_bread(istream, (tb_byte_t*)&entry, sizeof(entry))) { + break; + } + + // check for null entry (end of table) + if (entry.original_first_thunk == 0 && entry.name_rva == 0) { + break; + } + + tb_uint32_t name_rva = tb_bits_le_to_ne_u32(entry.name_rva); + + if (name_rva != 0) { + /* map RVA to file offset to read the name + * We need to find the section that contains this RVA. + */ + + tb_hize_t saved_pos_inner = tb_stream_offset(istream); + tb_uint32_t name_file_offset = 0; + + // Find the section containing name_rva + for (tb_uint16_t k = 0; k < header.nsects; k++) { + xm_coff_section_t* s = §ions[k]; + if (name_rva >= s->vaddr && name_rva < s->vaddr + s->vsize) { + name_file_offset = s->ofs + (name_rva - s->vaddr); + break; + } + } + + if (name_file_offset != 0) { + tb_char_t dll_name[256]; + if (xm_binutils_read_string(istream, name_file_offset, dll_name, sizeof(dll_name)) && dll_name[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); + } + } + break; + } + } + + tb_check_break(!failed); + + ok = tb_true; + + } while (0); + + if (sections) tb_free(sections); + return ok; +} diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h index 6b009b056..8017beeff 100644 --- a/core/src/xmake/binutils/coff/prefix.h +++ b/core/src/xmake/binutils/coff/prefix.h @@ -34,6 +34,10 @@ #define XM_COFF_MACHINE_ARM 0x01c0 #define XM_COFF_MACHINE_ARM64 0xaa64 +/* PE Optional Header Magic */ +#define XM_PE32_MAGIC 0x10b +#define XM_PE32P_MAGIC 0x20b + // COFF section flags #define XM_COFF_SCN_CNT_CODE 0x20 // IMAGE_SCN_CNT_CODE #define XM_COFF_SCN_CNT_INITIALIZED_DATA 0x40 // IMAGE_SCN_CNT_INITIALIZED_DATA @@ -55,6 +59,86 @@ typedef struct __xm_coff_header_t { tb_uint16_t flags; } __tb_packed__ xm_coff_header_t; +typedef struct __xm_pe32_data_directory_t { + tb_uint32_t vaddr; + tb_uint32_t size; +} __tb_packed__ xm_pe32_data_directory_t; + +typedef struct __xm_pe32_opt_header_t { + tb_uint16_t magic; + tb_uint8_t major_linker_version; + tb_uint8_t minor_linker_version; + tb_uint32_t size_of_code; + tb_uint32_t size_of_initialized_data; + tb_uint32_t size_of_uninitialized_data; + tb_uint32_t address_of_entry_point; + tb_uint32_t base_of_code; + tb_uint32_t base_of_data; + tb_uint32_t image_base; + tb_uint32_t section_alignment; + tb_uint32_t file_alignment; + tb_uint16_t major_operating_system_version; + tb_uint16_t minor_operating_system_version; + tb_uint16_t major_image_version; + tb_uint16_t minor_image_version; + tb_uint16_t major_subsystem_version; + tb_uint16_t minor_subsystem_version; + tb_uint32_t win32_version_value; + tb_uint32_t size_of_image; + tb_uint32_t size_of_headers; + tb_uint32_t checksum; + tb_uint16_t subsystem; + tb_uint16_t dll_characteristics; + tb_uint32_t size_of_stack_reserve; + tb_uint32_t size_of_stack_commit; + tb_uint32_t size_of_heap_reserve; + tb_uint32_t size_of_heap_commit; + tb_uint32_t loader_flags; + tb_uint32_t number_of_rva_and_sizes; + xm_pe32_data_directory_t data_directory[16]; +} __tb_packed__ xm_pe32_opt_header_t; + +typedef struct __xm_pe32p_opt_header_t { + tb_uint16_t magic; + tb_uint8_t major_linker_version; + tb_uint8_t minor_linker_version; + tb_uint32_t size_of_code; + tb_uint32_t size_of_initialized_data; + tb_uint32_t size_of_uninitialized_data; + tb_uint32_t address_of_entry_point; + tb_uint32_t base_of_code; + tb_uint64_t image_base; + tb_uint32_t section_alignment; + tb_uint32_t file_alignment; + tb_uint16_t major_operating_system_version; + tb_uint16_t minor_operating_system_version; + tb_uint16_t major_image_version; + tb_uint16_t minor_image_version; + tb_uint16_t major_subsystem_version; + tb_uint16_t minor_subsystem_version; + tb_uint32_t win32_version_value; + tb_uint32_t size_of_image; + tb_uint32_t size_of_headers; + tb_uint32_t checksum; + tb_uint16_t subsystem; + tb_uint16_t dll_characteristics; + tb_uint64_t size_of_stack_reserve; + tb_uint64_t size_of_stack_commit; + tb_uint64_t size_of_heap_reserve; + tb_uint64_t size_of_heap_commit; + tb_uint32_t loader_flags; + tb_uint32_t number_of_rva_and_sizes; + xm_pe32_data_directory_t data_directory[16]; +} __tb_packed__ xm_pe32p_opt_header_t; + +typedef struct __xm_coff_import_directory_table_t { + tb_uint32_t original_first_thunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) + tb_uint32_t time_date_stamp; // 0 if not bound, -1 if bound, and real date\time stamp in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) O.W. date/time stamp of DLL bound to (Old BIND) + tb_uint32_t forwarder_chain; // -1 if no forwarders + tb_uint32_t name_rva; // RVA to name + tb_uint32_t first_thunk; // RVA to IAT (if bound this IAT has actual addresses) +} __tb_packed__ xm_coff_import_directory_table_t; + typedef struct __xm_coff_import_header_t { tb_uint16_t sig1; // 0 tb_uint16_t sig2; // 0xffff @@ -233,33 +317,9 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr return tb_false; } - // seek to string position (offset is from start of string table, including size field) - // strtab_offset points to the start of string table (including 4-byte size field) - // offset is from the start of string table (including size field) - // So we use strtab_offset + offset directly - if (!tb_stream_seek(istream, strtab_offset + 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 + // restore position and use common implementation tb_stream_seek(istream, saved_pos); - return tb_true; + return xm_binutils_read_string(istream, strtab_offset + offset, name, name_size); } /* get symbol name from COFF symbol entry diff --git a/core/src/xmake/binutils/deplibs.c b/core/src/xmake/binutils/deplibs.c new file mode 100644 index 000000000..289e656a0 --- /dev/null +++ b/core/src/xmake/binutils/deplibs.c @@ -0,0 +1,152 @@ +/*!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_PE) { + // seek to e_lfanew + if (!tb_stream_seek(istream, 0x3c)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to seek to e_lfanew"); + break; + } + + // read e_lfanew + tb_uint32_t e_lfanew = 0; + if (!tb_stream_bread(istream, (tb_byte_t*)&e_lfanew, 4)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to read e_lfanew"); + break; + } + + // e_lfanew is little endian + e_lfanew = tb_bits_le_to_ne_u32(e_lfanew); + + // call coff deplibs with offset = e_lfanew + 4 (skip PE signature) + if (!xm_binutils_coff_deplibs(istream, e_lfanew + 4, lua)) { + lua_pop(lua, 1); // pop table + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "deplibs: failed to parse PE/COFF"); + 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 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 { + 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..2b58902ef --- /dev/null +++ b/core/src/xmake/binutils/elf/deplibs.c @@ -0,0 +1,504 @@ +/*!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_check_path(tb_char_t const* path, tb_char_t const* name, tb_char_t* output, tb_size_t output_size) { + tb_char_t fullpath[TB_PATH_MAXN]; + tb_snprintf(fullpath, sizeof(fullpath), "%s/%s", path, name); + if (tb_file_info(fullpath, tb_null)) { + if (output) { + tb_strlcpy(output, fullpath, output_size); + } + return tb_true; + } + return tb_false; +} + +static tb_void_t xm_binutils_elf_resolve_path(tb_char_t const* name, tb_char_t const* rpath, tb_char_t const* binary_dir, tb_char_t* output, tb_size_t output_size) { + + // absolute path? + if (tb_path_is_absolute(name)) { + tb_strlcpy(output, name, output_size); + return; + } + + // try rpath/runpath + if (rpath && binary_dir) { + tb_char_t const* p = rpath; + tb_char_t const* e = tb_null; + while (*p) { + e = tb_strchr(p, ':'); + tb_size_t n = e ? (tb_size_t)(e - p) : tb_strlen(p); + if (n > 0) { + tb_char_t path[TB_PATH_MAXN]; + tb_char_t expanded_path[TB_PATH_MAXN]; + tb_strncpy(path, p, n); + path[n] = '\0'; + + // replace $ORIGIN + tb_char_t* origin = tb_strstr(path, "$ORIGIN"); + if (origin) { + tb_long_t len = tb_snprintf(expanded_path, sizeof(expanded_path), "%.*s%s%s", (tb_int_t)(origin - path), path, binary_dir, origin + 7); + if (len >= 0) { + if (xm_binutils_elf_check_path(expanded_path, name, output, output_size)) { + return; + } + } + } else { + if (xm_binutils_elf_check_path(path, name, output, output_size)) { + return; + } + } + } + if (e) p = e + 1; + else break; + } + } + + // try system paths + static tb_char_t const* s_sys_paths[] = { + "/lib", + "/usr/lib", + "/lib64", + "/usr/lib64", + "/usr/local/lib", + "/usr/local/lib64", + "/lib/x86_64-linux-gnu", // Debian/Ubuntu + "/usr/lib/x86_64-linux-gnu", + "/lib/i386-linux-gnu", + "/usr/lib/i386-linux-gnu", + "/lib/aarch64-linux-gnu", + "/usr/lib/aarch64-linux-gnu", + "/lib/arm-linux-gnueabihf", + "/usr/lib/arm-linux-gnueabihf", + tb_null + }; + + for (tb_char_t const** sys_path = s_sys_paths; *sys_path; sys_path++) { + if (xm_binutils_elf_check_path(*sys_path, name, output, output_size)) { + return; + } + } + + // not found, return original name + tb_strlcpy(output, name, output_size); +} + +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; + } + + tb_size_t result_count = 0; + + // find program interpreter (PT_INTERP) + if (header.e_phoff != 0 && header.e_phnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf32_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_INTERP) { + tb_char_t name[256]; + if (xm_binutils_read_string(istream, base_offset + phdr.p_offset, name, sizeof(name)) && name[0]) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, name); + lua_settable(lua, -3); + result_count++; + } + break; + } + } + } + } + + // find .dynamic section info + tb_uint32_t dynamic_offset = 0; + tb_uint32_t dynamic_size = 0; + tb_uint32_t strtab_offset = 0; + + // try to find from section headers first + if (header.e_shoff != 0 && header.e_shnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_shoff)) { + 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))) { + break; + } + + if (section.sh_type == XM_ELF_SHT_DYNAMIC) { + dynamic_offset = section.sh_offset; + dynamic_size = section.sh_size; + + // find string table via sh_link + xm_elf32_section_t strtab_section; + if (tb_stream_seek(istream, base_offset + header.e_shoff + section.sh_link * sizeof(xm_elf32_section_t)) && + tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) { + strtab_offset = strtab_section.sh_offset; + } + break; + } + } + } + } + + // fallback to program headers if not found in sections + if ((dynamic_offset == 0 || strtab_offset == 0) && header.e_phoff != 0 && header.e_phnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf32_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_DYNAMIC) { + dynamic_offset = phdr.p_offset; + dynamic_size = phdr.p_memsz; // usually p_filesz == p_memsz for dynamic + break; + } + } + } + + if (dynamic_offset > 0 && dynamic_size > 0) { + // read dynamic entries to find strtab address + tb_uint32_t strtab_vaddr = 0; + tb_uint32_t count = dynamic_size / sizeof(xm_elf32_dynamic_t); + if (tb_stream_seek(istream, base_offset + dynamic_offset)) { + 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))) { + break; + } + if (dyn.d_tag == XM_ELF_DT_STRTAB) { + strtab_vaddr = dyn.d_un.d_val; + break; + } + } + } + + if (strtab_vaddr > 0) { + // map strtab vaddr to file offset using PT_LOAD + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf32_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_LOAD && strtab_vaddr >= phdr.p_vaddr && strtab_vaddr < phdr.p_vaddr + phdr.p_memsz) { + strtab_offset = phdr.p_offset + (strtab_vaddr - phdr.p_vaddr); + break; + } + } + } + } + } + } + + if (dynamic_offset == 0 || strtab_offset == 0) { + return tb_true; // no dynamic section or strtab found + } + + // read dynamic entries + tb_uint32_t count = dynamic_size / sizeof(xm_elf32_dynamic_t); + if (!tb_stream_seek(istream, base_offset + dynamic_offset)) { + return tb_false; + } + + tb_char_t rpath[8192] = {0}; + tb_char_t runpath[8192] = {0}; + tb_vector_ref_t needed_libs = tb_vector_init(0, tb_element_str(tb_true)); + if (needed_libs) { + 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))) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NULL) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NEEDED || dyn.d_tag == XM_ELF_DT_SONAME || dyn.d_tag == XM_ELF_DT_AUXILIARY || dyn.d_tag == XM_ELF_DT_FILTER) { + tb_char_t name[256]; + if (xm_binutils_read_string(istream, base_offset + strtab_offset + dyn.d_un.d_val, name, sizeof(name)) && name[0]) { + tb_vector_insert_tail(needed_libs, name); + } + } else if (dyn.d_tag == XM_ELF_DT_RPATH) { + xm_binutils_read_string(istream, base_offset + strtab_offset + dyn.d_un.d_val, rpath, sizeof(rpath)); + } else if (dyn.d_tag == XM_ELF_DT_RUNPATH) { + xm_binutils_read_string(istream, base_offset + strtab_offset + dyn.d_un.d_val, runpath, sizeof(runpath)); + } + } + + // get binary directory + tb_char_t const* binary_path = tb_null; + tb_char_t binary_dir[TB_PATH_MAXN] = {0}; + if (tb_stream_ctrl(istream, TB_STREAM_CTRL_GET_PATH, &binary_path) && binary_path) { + tb_path_directory(binary_path, binary_dir, sizeof(binary_dir)); + } + + // resolve and push paths + tb_for_all (tb_char_t const*, name, needed_libs) { + tb_char_t fullpath[TB_PATH_MAXN]; + xm_binutils_elf_resolve_path(name, runpath[0]? runpath : rpath, binary_dir[0]? binary_dir : tb_null, fullpath, sizeof(fullpath)); + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, fullpath); + lua_settable(lua, -3); + result_count++; + } + tb_vector_exit(needed_libs); + } + + 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; + } + + tb_size_t result_count = 0; + + // find program interpreter (PT_INTERP) + if (header.e_phoff != 0 && header.e_phnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf64_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_INTERP) { + tb_char_t name[256]; + if (xm_binutils_read_string(istream, base_offset + phdr.p_offset, name, sizeof(name)) && name[0]) { + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, name); + lua_settable(lua, -3); + result_count++; + } + break; + } + } + } + } + + // find .dynamic section info + tb_uint64_t dynamic_offset = 0; + tb_uint64_t dynamic_size = 0; + tb_uint64_t strtab_offset = 0; + + // try to find from section headers first + if (header.e_shoff != 0 && header.e_shnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_shoff)) { + 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))) { + break; + } + + if (section.sh_type == XM_ELF_SHT_DYNAMIC) { + dynamic_offset = section.sh_offset; + dynamic_size = section.sh_size; + + // find string table via sh_link + xm_elf64_section_t strtab_section; + if (tb_stream_seek(istream, base_offset + header.e_shoff + section.sh_link * sizeof(xm_elf64_section_t)) && + tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) { + strtab_offset = strtab_section.sh_offset; + } + break; + } + } + } + } + + // fallback to program headers if not found in sections + if ((dynamic_offset == 0 || strtab_offset == 0) && header.e_phoff != 0 && header.e_phnum > 0) { + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf64_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_DYNAMIC) { + dynamic_offset = phdr.p_offset; + dynamic_size = phdr.p_memsz; + break; + } + } + } + + if (dynamic_offset > 0 && dynamic_size > 0) { + // read dynamic entries to find strtab address + tb_uint64_t strtab_vaddr = 0; + tb_uint32_t count = (tb_uint32_t)(dynamic_size / sizeof(xm_elf64_dynamic_t)); + if (tb_stream_seek(istream, base_offset + dynamic_offset)) { + 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))) { + break; + } + if (dyn.d_tag == XM_ELF_DT_STRTAB) { + strtab_vaddr = dyn.d_un.d_val; + break; + } + } + } + + if (strtab_vaddr > 0) { + // map strtab vaddr to file offset using PT_LOAD + if (tb_stream_seek(istream, base_offset + header.e_phoff)) { + for (tb_uint16_t i = 0; i < header.e_phnum; i++) { + xm_elf64_phdr_t phdr; + if (!tb_stream_bread(istream, (tb_byte_t*)&phdr, sizeof(phdr))) { + break; + } + if (phdr.p_type == XM_ELF_PT_LOAD && strtab_vaddr >= phdr.p_vaddr && strtab_vaddr < phdr.p_vaddr + phdr.p_memsz) { + strtab_offset = phdr.p_offset + (strtab_vaddr - phdr.p_vaddr); + break; + } + } + } + } + } + } + + if (dynamic_offset == 0 || strtab_offset == 0) { + return tb_true; // no dynamic section or strtab found + } + + // read dynamic entries + tb_uint32_t count = (tb_uint32_t)(dynamic_size / sizeof(xm_elf64_dynamic_t)); + if (!tb_stream_seek(istream, base_offset + dynamic_offset)) { + return tb_false; + } + + tb_char_t rpath[8192] = {0}; + tb_char_t runpath[8192] = {0}; + tb_vector_ref_t needed_libs = tb_vector_init(0, tb_element_str(tb_true)); + if (needed_libs) { + 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))) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NULL) { + break; + } + + if (dyn.d_tag == XM_ELF_DT_NEEDED || dyn.d_tag == XM_ELF_DT_SONAME || dyn.d_tag == XM_ELF_DT_AUXILIARY || dyn.d_tag == XM_ELF_DT_FILTER) { + tb_char_t name[256]; + if (xm_binutils_read_string(istream, base_offset + strtab_offset + (tb_uint32_t)dyn.d_un.d_val, name, sizeof(name)) && name[0]) { + tb_vector_insert_tail(needed_libs, name); + } + } else if (dyn.d_tag == XM_ELF_DT_RPATH) { + xm_binutils_read_string(istream, base_offset + strtab_offset + (tb_uint32_t)dyn.d_un.d_val, rpath, sizeof(rpath)); + } else if (dyn.d_tag == XM_ELF_DT_RUNPATH) { + xm_binutils_read_string(istream, base_offset + strtab_offset + (tb_uint32_t)dyn.d_un.d_val, runpath, sizeof(runpath)); + } + } + + // get binary directory + tb_char_t const* binary_path = tb_null; + tb_char_t binary_dir[TB_PATH_MAXN] = {0}; + if (tb_stream_ctrl(istream, TB_STREAM_CTRL_GET_PATH, &binary_path) && binary_path) { + tb_path_directory(binary_path, binary_dir, sizeof(binary_dir)); + } + + // resolve and push paths + tb_for_all (tb_char_t const*, name, needed_libs) { + tb_char_t fullpath[TB_PATH_MAXN]; + xm_binutils_elf_resolve_path(name, runpath[0]? runpath : rpath, binary_dir[0]? binary_dir : tb_null, fullpath, sizeof(fullpath)); + lua_pushinteger(lua, result_count + 1); + lua_pushstring(lua, fullpath); + lua_settable(lua, -3); + result_count++; + } + tb_vector_exit(needed_libs); + } + + 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..c3876fd9f 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -49,6 +49,21 @@ #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_PT_LOAD 1 +#define XM_ELF_PT_DYNAMIC 2 +#define XM_ELF_PT_INTERP 3 + +#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_DT_SONAME 14 +#define XM_ELF_DT_RPATH 15 +#define XM_ELF_DT_RUNPATH 29 +#define XM_ELF_DT_AUXILIARY 0x7ffffffd +#define XM_ELF_DT_FILTER 0x7fffffff #define XM_ELF_SHF_ALLOC 0x2 #define XM_ELF_SHF_WRITE 0x1 @@ -99,6 +114,17 @@ typedef struct __xm_elf32_symbol_t { tb_uint16_t st_shndx; } __tb_packed__ xm_elf32_symbol_t; +typedef struct __xm_elf32_phdr_t { + tb_uint32_t p_type; + tb_uint32_t p_offset; + tb_uint32_t p_vaddr; + tb_uint32_t p_paddr; + tb_uint32_t p_filesz; + tb_uint32_t p_memsz; + tb_uint32_t p_flags; + tb_uint32_t p_align; +} __tb_packed__ xm_elf32_phdr_t; + typedef struct __xm_elf64_header_t { tb_uint8_t e_ident[16]; tb_uint16_t e_type; @@ -137,6 +163,33 @@ 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_elf64_phdr_t { + tb_uint32_t p_type; + tb_uint32_t p_flags; + tb_uint64_t p_offset; + tb_uint64_t p_vaddr; + tb_uint64_t p_paddr; + tb_uint64_t p_filesz; + tb_uint64_t p_memsz; + tb_uint64_t p_align; +} __tb_packed__ xm_elf64_phdr_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" /* ////////////////////////////////////////////////////////////////////////////////////// @@ -219,91 +272,13 @@ static __tb_inline__ tb_uint16_t xm_binutils_elf_get_machine(tb_char_t const *ar * @return tb_true if 64-bit, tb_false otherwise */ static __tb_inline__ tb_bool_t xm_binutils_elf_is_64bit(tb_char_t const *arch) { - if (!arch) { - return tb_true; - } - // x86_64 - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return tb_true; - } - // ARM64 - else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 || - tb_strcmp(arch, "arm64-v8a") == 0) { - return tb_true; - } - // MIPS64 - else if (tb_strncmp(arch, "mips64", 6) == 0) { - return tb_true; - } - // PowerPC64 - else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) { - return tb_true; - } - // RISC-V 64 - else if (tb_strncmp(arch, "riscv64", 7) == 0 || - (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) { - return tb_true; - } - // SPARC64 - else if (tb_strncmp(arch, "sparc64", 7) == 0) { - return tb_true; - } - // s390x - else if (tb_strcmp(arch, "s390x") == 0) { - return tb_true; - } - // LoongArch64 - else if (tb_strncmp(arch, "loongarch64", 11) == 0) { - return tb_true; - } - // WebAssembly 64 - else if (tb_strcmp(arch, "wasm64") == 0) { - return tb_true; - } - // IA-64 - else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) { - return tb_true; - } - return tb_false; + return xm_binutils_arch_is_64bit(arch); } /* ////////////////////////////////////////////////////////////////////////////////////// * 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_uint64_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 character (nm-style) from ELF symbol * * @param st_info the symbol info byte diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c index 03756d240..770ead980 100644 --- a/core/src/xmake/binutils/elf/readsyms.c +++ b/core/src/xmake/binutils/elf/readsyms.c @@ -126,7 +126,7 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, tb_hize_t bas // get symbol name tb_char_t name[256]; - if (!xm_binutils_elf_read_string(istream, base_offset + strtab_section.sh_offset, sym.st_name, name, sizeof(name)) || !name[0]) { + if (!xm_binutils_read_string(istream, base_offset + strtab_section.sh_offset + sym.st_name, name, sizeof(name)) || !name[0]) { continue; } @@ -253,7 +253,7 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, tb_hize_t bas // get symbol name tb_char_t name[256]; - if (!xm_binutils_elf_read_string(istream, base_offset + strtab_section.sh_offset, sym.st_name, name, sizeof(name)) || !name[0]) { + if (!xm_binutils_read_string(istream, base_offset + strtab_section.sh_offset + sym.st_name, name, sizeof(name)) || !name[0]) { continue; } diff --git a/core/src/xmake/binutils/macho/deplibs.c b/core/src/xmake/binutils/macho/deplibs.c new file mode 100644 index 000000000..40d19b5cb --- /dev/null +++ b/core/src/xmake/binutils/macho/deplibs.c @@ -0,0 +1,152 @@ +/*!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))) { + return tb_false; + } + xm_binutils_macho_swap_load_command(&lc, swap); + + /* check for LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_ID_DYLIB */ + if (lc.cmd == XM_MACHO_LC_LOAD_DYLIB || lc.cmd == XM_MACHO_LC_ID_DYLIB || + lc.cmd == XM_MACHO_LC_LOAD_WEAK_DYLIB || lc.cmd == XM_MACHO_LC_REEXPORT_DYLIB) { + + 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..4216bbdd1 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -50,6 +50,10 @@ #define XM_MACHO_LC_SEGMENT 0x1 #define XM_MACHO_LC_SEGMENT_64 0x19 #define XM_MACHO_LC_SYMTAB 0x2 +#define XM_MACHO_LC_LOAD_DYLIB 0xc +#define XM_MACHO_LC_ID_DYLIB 0xd +#define XM_MACHO_LC_LOAD_WEAK_DYLIB (0x18 | 0x80000000) +#define XM_MACHO_LC_REEXPORT_DYLIB (0x1f | 0x80000000) #define XM_MACHO_LC_BUILD_VERSION 0x32 #define XM_MACHO_PLATFORM_MACOS 1 @@ -184,12 +188,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/binutils/mslib/readsyms.c b/core/src/xmake/binutils/mslib/readsyms.c index de9dc9d62..55857bb58 100644 --- a/core/src/xmake/binutils/mslib/readsyms.c +++ b/core/src/xmake/binutils/mslib/readsyms.c @@ -331,7 +331,7 @@ tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base if (!has_symbols) { // check map - lua_pushinteger(lua, header_offset); + lua_pushinteger(lua, (lua_Integer)header_offset); lua_rawget(lua, map_idx); if (lua_istable(lua, -1)) { // convert list of names to list of {name=..., type="global"} diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h index 492ed691c..68408eea5 100644 --- a/core/src/xmake/binutils/prefix.h +++ b/core/src/xmake/binutils/prefix.h @@ -33,6 +33,7 @@ #define XM_BINUTILS_FORMAT_ELF 2 #define XM_BINUTILS_FORMAT_MACHO 3 #define XM_BINUTILS_FORMAT_AR 4 +#define XM_BINUTILS_FORMAT_PE 5 #define XM_BINUTILS_FORMAT_UNKNOWN 0 /* COFF machine types (for format detection) */ @@ -93,6 +94,11 @@ static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) return XM_BINUTILS_FORMAT_AR; } + // check PE/DOS magic (0x5A4D 'M' 'Z') + if (p[0] == 'M' && p[1] == 'Z') { + return XM_BINUTILS_FORMAT_PE; + } + // check ELF magic (0x7f 'E' 'L' 'F') if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F') { return XM_BINUTILS_FORMAT_ELF; @@ -183,6 +189,95 @@ static __tb_inline__ void xm_binutils_sanitize_symbol_name(tb_char_t* name) { } } +/* read string from stream at specified offset + * + * @param istream the input stream + * @param offset the offset to read from + * @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_read_string(tb_stream_ref_t istream, tb_hize_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, 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; +} + +/* check if architecture is 64-bit + * + * @param arch the architecture string + * @return tb_true if 64-bit, tb_false otherwise + */ +static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch) { + if (!arch) { + return tb_true; + } + // x86_64 + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return tb_true; + } + // ARM64 + else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 || + tb_strcmp(arch, "arm64-v8a") == 0) { + return tb_true; + } + // MIPS64 + else if (tb_strncmp(arch, "mips64", 6) == 0) { + return tb_true; + } + // PowerPC64 + else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) { + return tb_true; + } + // RISC-V 64 + else if (tb_strncmp(arch, "riscv64", 7) == 0 || + (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) { + return tb_true; + } + // SPARC64 + else if (tb_strncmp(arch, "sparc64", 7) == 0) { + return tb_true; + } + // s390x + else if (tb_strcmp(arch, "s390x") == 0) { + return tb_true; + } + // LoongArch64 + else if (tb_strncmp(arch, "loongarch64", 11) == 0) { + return tb_true; + } + // WebAssembly 64 + else if (tb_strcmp(arch, "wasm64") == 0) { + return tb_true; + } + // IA-64 + else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) { + return tb_true; + } + return tb_false; +} + #endif 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 }, }; |
