From f1726a8f0f63160ae2185f28b38a740caf6d1ab1 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:18:25 +0800 Subject: update binutils dirs --- core/src/xmake/binutils/bin2coff.c | 440 -------------- core/src/xmake/binutils/bin2elf.c | 952 ------------------------------ core/src/xmake/binutils/bin2macho.c | 887 ---------------------------- core/src/xmake/binutils/coff/bin2coff.c | 321 ++++++++++ core/src/xmake/binutils/coff/prefix.h | 175 ++++++ core/src/xmake/binutils/elf/bin2elf.c | 725 +++++++++++++++++++++++ core/src/xmake/binutils/elf/prefix.h | 270 +++++++++ core/src/xmake/binutils/macho/bin2macho.c | 628 ++++++++++++++++++++ core/src/xmake/binutils/macho/prefix.h | 322 ++++++++++ core/src/xmake/xmake.sh | 3 + 10 files changed, 2444 insertions(+), 2279 deletions(-) delete mode 100644 core/src/xmake/binutils/bin2coff.c delete mode 100644 core/src/xmake/binutils/bin2elf.c delete mode 100644 core/src/xmake/binutils/bin2macho.c create mode 100644 core/src/xmake/binutils/coff/bin2coff.c create mode 100644 core/src/xmake/binutils/coff/prefix.h create mode 100644 core/src/xmake/binutils/elf/bin2elf.c create mode 100644 core/src/xmake/binutils/elf/prefix.h create mode 100644 core/src/xmake/binutils/macho/bin2macho.c create mode 100644 core/src/xmake/binutils/macho/prefix.h (limited to 'core/src') diff --git a/core/src/xmake/binutils/bin2coff.c b/core/src/xmake/binutils/bin2coff.c deleted file mode 100644 index 9a095a2cd..000000000 --- a/core/src/xmake/binutils/bin2coff.c +++ /dev/null @@ -1,440 +0,0 @@ -/*!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 bin2coff.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "bin2coff" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ -#define XM_COFF_MACHINE_I386 0x014c -#define XM_COFF_MACHINE_AMD64 0x8664 -#define XM_COFF_MACHINE_ARM 0x01c0 -#define XM_COFF_MACHINE_ARM64 0xaa64 - -#define XM_COFF_SECTION_RDATA 0x40000040 // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * types - */ -#include "tbox/prefix/packed.h" -typedef struct __xm_coff_header_t { - tb_uint16_t machine; - tb_uint16_t nsects; - tb_uint32_t time; - tb_uint32_t symtabofs; - tb_uint32_t nsyms; - tb_uint16_t opthdr; - tb_uint16_t flags; -} __tb_packed__ xm_coff_header_t; - -typedef struct __xm_coff_section_t { - tb_char_t name[8]; - tb_uint32_t vsize; - tb_uint32_t vaddr; - tb_uint32_t size; - tb_uint32_t ofs; - tb_uint32_t relocofs; - tb_uint32_t linenoofs; - tb_uint16_t nreloc; - tb_uint16_t nlineno; - tb_uint32_t flags; -} __tb_packed__ xm_coff_section_t; - -typedef struct __xm_coff_symbol_t { - union { - struct { - tb_char_t name[8]; - } shortname; - struct { - tb_uint32_t zeros; - tb_uint32_t offset; - } longname; - } n; - tb_uint32_t value; - tb_int16_t sect; - tb_uint16_t type; - tb_uint8_t scl; - tb_uint8_t naux; -} __tb_packed__ xm_coff_symbol_t; - -typedef struct __xm_coff_aux_section_t { - tb_uint32_t length; - tb_uint16_t nreloc; - tb_uint16_t nlineno; - tb_uint8_t reserved[10]; -} __tb_packed__ xm_coff_aux_section_t; - -typedef struct __xm_coff_symbol_tail_t { - tb_uint32_t value; - tb_int16_t sect; - tb_uint16_t type; - tb_uint8_t scl; - tb_uint8_t naux; -} __tb_packed__ xm_coff_symbol_tail_t; -#include "tbox/prefix/packed.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * private implementation - */ -static tb_uint16_t xm_binutils_bin2coff_get_machine(tb_char_t const *arch) { - if (!arch) { - return XM_COFF_MACHINE_I386; - } - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return XM_COFF_MACHINE_AMD64; - } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { - return XM_COFF_MACHINE_ARM64; - } else if (tb_strcmp(arch, "arm") == 0) { - return XM_COFF_MACHINE_ARM; - } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) { - return XM_COFF_MACHINE_I386; - } - return XM_COFF_MACHINE_I386; -} - -static tb_void_t xm_binutils_bin2coff_write_string(tb_stream_ref_t ostream, tb_char_t const *str, tb_size_t len) { - tb_assert_and_check_return(ostream && str); - if (len == 0) { - len = tb_strlen(str); - } - tb_stream_bwrit(ostream, (tb_byte_t const *)str, len); -} - -static tb_void_t xm_binutils_bin2coff_write_padding(tb_stream_ref_t ostream, tb_size_t count) { - tb_assert_and_check_return(ostream); - tb_byte_t zero = 0; - while (count-- > 0) { - tb_stream_bwrit(ostream, &zero, 1); - } -} - -static tb_void_t xm_binutils_bin2coff_write_symbol_name(tb_stream_ref_t ostream, tb_char_t const *name, tb_uint32_t *strtab_offset) { - tb_assert_and_check_return(ostream && name && strtab_offset); - tb_size_t len = tb_strlen(name); - if (len <= 8) { - // short name: store directly in symbol name field - xm_binutils_bin2coff_write_string(ostream, name, len); - if (len < 8) { - xm_binutils_bin2coff_write_padding(ostream, 8 - len); - } - } else { - // long name: store offset in string table - tb_uint32_t zeros = 0; - tb_stream_bwrit(ostream, (tb_byte_t const *)&zeros, 4); - tb_stream_bwrit(ostream, (tb_byte_t const *)strtab_offset, 4); - *strtab_offset += (tb_uint32_t)(len + 1); // +1 for null terminator - } -} - -static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *arch, - tb_char_t const *basename, - tb_bool_t zeroend) { - tb_assert_and_check_return_val(istream && ostream, tb_false); - - // get file size - tb_hong_t filesize = tb_stream_size(istream); - if (filesize < 0 || filesize > 0xffffffffU) { - return tb_false; - } - tb_uint32_t datasize = (tb_uint32_t)filesize; - // add null terminator if zeroend is true - if (zeroend) { - if (datasize >= 0xffffffffU) { - return tb_false; // would overflow - } - datasize++; - } - - // determine architecture for symbol prefix adjustment - tb_uint16_t machine = xm_binutils_bin2coff_get_machine(arch); - tb_bool_t is_i386 = (machine == XM_COFF_MACHINE_I386); - - // generate symbol names from filename - tb_char_t symbol_name[256] = {0}; - tb_char_t symbol_start[256] = {0}; - tb_char_t symbol_end[256] = {0}; - - // use basename or default to "data" - if (!basename || !basename[0]) { - basename = "data"; - } - - // build symbol name - // note: on i386 Windows, C compiler automatically adds an underscore prefix to external symbols - // so if we use "_binary_", the actual symbol becomes "__binary_" after compilation - // to match, we need to ensure the prefix has two underscores for i386 - if (symbol_prefix) { - if (is_i386 && symbol_prefix[0] == '_' && symbol_prefix[1] != '_') { - // i386: if prefix starts with single underscore, add another one - tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); - } - } else { - if (is_i386) { - tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); - } - } - - // replace non-alphanumeric with underscore - for (tb_size_t i = 0; symbol_name[i]; i++) { - if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { - symbol_name[i] = '_'; - } - } - - tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); - tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); - - // calculate offsets - tb_uint32_t header_size = sizeof(xm_coff_header_t); - tb_uint32_t section_header_size = sizeof(xm_coff_section_t); - tb_uint32_t section_data_ofs = header_size + section_header_size; - tb_uint32_t section_data_size = datasize; - tb_uint32_t section_data_padding = (4 - (section_data_size & 3)) & 3; - tb_uint32_t symbol_table_ofs = section_data_ofs + section_data_size + section_data_padding; - - // calculate string table size (content only, excluding the 4-byte size field) - tb_uint32_t string_table_content_size = 0; - tb_size_t start_len = tb_strlen(symbol_start); - tb_size_t end_len = tb_strlen(symbol_end); - if (start_len > 8) { - string_table_content_size += (tb_uint32_t)(start_len + 1); - } - if (end_len > 8) { - string_table_content_size += (tb_uint32_t)(end_len + 1); - } - // string table size field should include the size field itself - tb_uint32_t string_table_size = 4 + string_table_content_size; - - // write COFF header - xm_coff_header_t header; - tb_memset(&header, 0, sizeof(header)); - header.machine = machine; - header.nsects = 1; - header.time = 0; - header.symtabofs = symbol_table_ofs; - // note: COFF spec says nsyms is the number of symbol table entries (including aux entries) - // section symbol (1) + aux entry (1) + start symbol (1) + end symbol (1) = 4 entries - // total size: 4 * 18 = 72 bytes - // i386 linker calculates string table as symtabofs + nsyms * 18 = symtabofs + 72 (correct) - // when reading symbols, linker follows naux fields to skip aux entries correctly - // from mingw i386 analysis: section symbols MUST have aux entry (naux=1) - header.nsyms = 4; // 3 symbols + 1 aux entry - header.opthdr = 0; - header.flags = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { - return tb_false; - } - - // write section header (.rdata) - xm_coff_section_t section; - tb_memset(§ion, 0, sizeof(section)); - tb_strncpy(section.name, ".rdata", 8); - section.vsize = datasize; - section.vaddr = 0; - section.size = datasize; - section.ofs = section_data_ofs; - section.relocofs = 0; - section.linenoofs = 0; - section.nreloc = 0; - section.nlineno = 0; - section.flags = XM_COFF_SECTION_RDATA; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { - return tb_false; - } - - // write section data - if (!xm_binutils_stream_copy(istream, ostream, filesize)) { - return tb_false; - } - // append null terminator if zeroend is true - if (zeroend) { - tb_byte_t zero = 0; - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - - // align to 4 bytes - if (section_data_padding > 0) { - xm_binutils_bin2coff_write_padding(ostream, section_data_padding); - } - - // write symbol table - // symbol 0: .rdata section symbol - xm_coff_symbol_t sym_section; - tb_memset(&sym_section, 0, sizeof(sym_section)); - tb_strncpy(sym_section.n.shortname.name, ".rdata", 8); - sym_section.value = 0; - sym_section.sect = 1; // section index (1-based) - sym_section.type = 0; // IMAGE_SYM_TYPE_NULL - sym_section.scl = 3; // IMAGE_SYM_CLASS_STATIC - // section symbol MUST have auxiliary entry for i386 compatibility (as seen in mingw-generated files) - sym_section.naux = 1; // auxiliary entry (required for i386) - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_section, sizeof(sym_section))) { - return tb_false; - } - // auxiliary entry for section (18 bytes total) - xm_coff_aux_section_t aux_section; - tb_memset(&aux_section, 0, sizeof(aux_section)); - aux_section.length = datasize; - aux_section.nreloc = 0; - aux_section.nlineno = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&aux_section, sizeof(aux_section))) { - return tb_false; - } - - // symbol 1: _binary_xxx_start (or __binary_xxx_start for i386) - tb_uint32_t strtab_offset = 4; // start after size field - xm_binutils_bin2coff_write_symbol_name(ostream, symbol_start, &strtab_offset); - xm_coff_symbol_tail_t sym_start_tail; - tb_memset(&sym_start_tail, 0, sizeof(sym_start_tail)); - sym_start_tail.value = 0; - sym_start_tail.sect = 1; - sym_start_tail.type = 0; // IMAGE_SYM_TYPE_NULL - sym_start_tail.scl = 2; // IMAGE_SYM_CLASS_EXTERNAL - sym_start_tail.naux = 0; // no auxiliary entry - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start_tail, sizeof(sym_start_tail))) { - return tb_false; - } - - // symbol 2: _binary_xxx_end (or __binary_xxx_end for i386) - xm_binutils_bin2coff_write_symbol_name(ostream, symbol_end, &strtab_offset); - xm_coff_symbol_tail_t sym_end_tail; - tb_memset(&sym_end_tail, 0, sizeof(sym_end_tail)); - sym_end_tail.value = datasize; - sym_end_tail.sect = 1; - sym_end_tail.type = 0; // IMAGE_SYM_TYPE_NULL - sym_end_tail.scl = 2; // IMAGE_SYM_CLASS_EXTERNAL - sym_end_tail.naux = 0; // no auxiliary entry - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end_tail, sizeof(sym_end_tail))) { - return tb_false; - } - - // write string table - // symbol table size: section symbol (18) + aux entry (18) + start symbol (18) + end symbol (18) = 72 bytes - // string table starts at symtabofs + 72, which matches nsyms * 18 = 4 * 18 = 72 - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&string_table_size, 4)) { - return tb_false; - } - if (start_len > 8) { - xm_binutils_bin2coff_write_string(ostream, symbol_start, start_len); - tb_byte_t null = 0; - tb_stream_bwrit(ostream, &null, 1); - } - if (end_len > 8) { - xm_binutils_bin2coff_write_string(ostream, symbol_end, end_len); - tb_byte_t null = 0; - tb_stream_bwrit(ostream, &null, 1); - } - - return tb_true; -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* generate COFF object file from binary file - * - * @param lua the lua state - * @return 1 on success, 2 on failure (with error message on stack) - */ -tb_int_t xm_binutils_bin2coff(lua_State *lua) { - tb_assert_and_check_return_val(lua, 0); - - // get the binaryfile - tb_char_t const *binaryfile = luaL_checkstring(lua, 1); - tb_check_return_val(binaryfile, 0); - - // get the outputfile - tb_char_t const *outputfile = luaL_checkstring(lua, 2); - tb_check_return_val(outputfile, 0); - - // get symbol prefix (optional) - tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; - - // get arch (optional) - tb_char_t const *arch = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; - - // get basename (optional) - tb_char_t const *basename = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; - - // get zeroend (optional, default: false) - tb_bool_t zeroend = lua_toboolean(lua, 6); - - // do dump - tb_bool_t ok = tb_false; - tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); - tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, - TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); - do { - if (!tb_stream_open(istream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2coff: open %s failed", binaryfile); - break; - } - - if (!tb_stream_open(ostream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2coff: open %s failed", outputfile); - break; - } - - if (!xm_binutils_bin2coff_dump(istream, ostream, symbol_prefix, arch, basename, zeroend)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2coff: dump data failed"); - break; - } - - ok = tb_true; - lua_pushboolean(lua, ok); - - } while (0); - - if (istream) { - tb_stream_clos(istream); - } - istream = tb_null; - - if (ostream) { - tb_stream_clos(ostream); - } - ostream = tb_null; - - return ok ? 1 : 2; -} - diff --git a/core/src/xmake/binutils/bin2elf.c b/core/src/xmake/binutils/bin2elf.c deleted file mode 100644 index a536143c4..000000000 --- a/core/src/xmake/binutils/bin2elf.c +++ /dev/null @@ -1,952 +0,0 @@ -/*!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 bin2elf.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "bin2elf" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ -#define XM_ELF_MACHINE_NONE 0x00 -#define XM_ELF_MACHINE_SPARC 0x02 -#define XM_ELF_MACHINE_I386 0x03 -#define XM_ELF_MACHINE_MIPS 0x08 -#define XM_ELF_MACHINE_POWERPC 0x14 -#define XM_ELF_MACHINE_POWERPC64 0x15 -#define XM_ELF_MACHINE_S390 0x16 -#define XM_ELF_MACHINE_ARM 0x28 -#define XM_ELF_MACHINE_SUPERH 0x2a -#define XM_ELF_MACHINE_SPARC64 0x2b -#define XM_ELF_MACHINE_IA_64 0x32 -#define XM_ELF_MACHINE_X86_64 0x3e -#define XM_ELF_MACHINE_RISCV 0xf3 -#define XM_ELF_MACHINE_ARM64 0xb7 -#define XM_ELF_MACHINE_WASM 0xe7 -#define XM_ELF_MACHINE_LOONGARCH 0x102 - -#define XM_ELF_SHT_PROGBITS 0x1 -#define XM_ELF_SHT_SYMTAB 0x2 -#define XM_ELF_SHT_STRTAB 0x3 - -#define XM_ELF_SHF_ALLOC 0x2 -#define XM_ELF_SHF_WRITE 0x1 - -#define XM_ELF_STB_GLOBAL 0x1 -#define XM_ELF_STT_OBJECT 0x1 - -/* ////////////////////////////////////////////////////////////////////////////////////// - * types - */ -#include "tbox/prefix/packed.h" -typedef struct __xm_elf32_header_t { - tb_uint8_t e_ident[16]; - tb_uint16_t e_type; - tb_uint16_t e_machine; - tb_uint32_t e_version; - tb_uint32_t e_entry; - tb_uint32_t e_phoff; - tb_uint32_t e_shoff; - tb_uint32_t e_flags; - tb_uint16_t e_ehsize; - tb_uint16_t e_phentsize; - tb_uint16_t e_phnum; - tb_uint16_t e_shentsize; - tb_uint16_t e_shnum; - tb_uint16_t e_shstrndx; -} __tb_packed__ xm_elf32_header_t; - -typedef struct __xm_elf32_section_t { - tb_uint32_t sh_name; - tb_uint32_t sh_type; - tb_uint32_t sh_flags; - tb_uint32_t sh_addr; - tb_uint32_t sh_offset; - tb_uint32_t sh_size; - tb_uint32_t sh_link; - tb_uint32_t sh_info; - tb_uint32_t sh_addralign; - tb_uint32_t sh_entsize; -} __tb_packed__ xm_elf32_section_t; - -typedef struct __xm_elf32_symbol_t { - tb_uint32_t st_name; - tb_uint32_t st_value; - tb_uint32_t st_size; - tb_uint8_t st_info; - tb_uint8_t st_other; - tb_uint16_t st_shndx; -} __tb_packed__ xm_elf32_symbol_t; - -typedef struct __xm_elf64_header_t { - tb_uint8_t e_ident[16]; - tb_uint16_t e_type; - tb_uint16_t e_machine; - tb_uint32_t e_version; - tb_uint64_t e_entry; - tb_uint64_t e_phoff; - tb_uint64_t e_shoff; - tb_uint32_t e_flags; - tb_uint16_t e_ehsize; - tb_uint16_t e_phentsize; - tb_uint16_t e_phnum; - tb_uint16_t e_shentsize; - tb_uint16_t e_shnum; - tb_uint16_t e_shstrndx; -} __tb_packed__ xm_elf64_header_t; - -typedef struct __xm_elf64_section_t { - tb_uint32_t sh_name; - tb_uint32_t sh_type; - tb_uint64_t sh_flags; - tb_uint64_t sh_addr; - tb_uint64_t sh_offset; - tb_uint64_t sh_size; - tb_uint32_t sh_link; - tb_uint32_t sh_info; - tb_uint64_t sh_addralign; - tb_uint64_t sh_entsize; -} __tb_packed__ xm_elf64_section_t; - -typedef struct __xm_elf64_symbol_t { - tb_uint32_t st_name; - tb_uint8_t st_info; - tb_uint8_t st_other; - tb_uint16_t st_shndx; - tb_uint64_t st_value; - tb_uint64_t st_size; -} __tb_packed__ xm_elf64_symbol_t; - -#include "tbox/prefix/packed.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * private implementation - */ -static tb_uint16_t xm_binutils_bin2elf_get_machine(tb_char_t const *arch) { - if (!arch) { - return XM_ELF_MACHINE_X86_64; - } - // x86/x86_64 - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return XM_ELF_MACHINE_X86_64; - } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) { - return XM_ELF_MACHINE_I386; - } - // ARM - else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 || - tb_strcmp(arch, "arm64-v8a") == 0) { - return XM_ELF_MACHINE_ARM64; - } else if (tb_strcmp(arch, "arm") == 0 || tb_strcmp(arch, "armv7") == 0 || - tb_strcmp(arch, "armeabi-v7a") == 0 || tb_strcmp(arch, "armv6") == 0 || - tb_strcmp(arch, "armv5") == 0) { - return XM_ELF_MACHINE_ARM; - } - // MIPS (MIPS and MIPS64 use same machine type, distinguished by ELF class) - else if (tb_strncmp(arch, "mips", 4) == 0) { - return XM_ELF_MACHINE_MIPS; - } - // PowerPC - else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) { - return XM_ELF_MACHINE_POWERPC64; - } else if (tb_strncmp(arch, "ppc", 3) == 0 || tb_strncmp(arch, "powerpc", 7) == 0) { - return XM_ELF_MACHINE_POWERPC; - } - // RISC-V (RISC-V and RISC-V64 use different machine types) - else if (tb_strncmp(arch, "riscv64", 7) == 0 || - (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) { - return XM_ELF_MACHINE_RISCV; // RISC-V 64-bit uses same machine type, distinguished by ELF class - } else if (tb_strncmp(arch, "riscv", 5) == 0) { - return XM_ELF_MACHINE_RISCV; - } - // SPARC - else if (tb_strncmp(arch, "sparc64", 7) == 0) { - return XM_ELF_MACHINE_SPARC64; - } else if (tb_strncmp(arch, "sparc", 5) == 0) { - return XM_ELF_MACHINE_SPARC; - } - // s390x - else if (tb_strcmp(arch, "s390x") == 0 || tb_strcmp(arch, "s390") == 0) { - return XM_ELF_MACHINE_S390; - } - // LoongArch (LoongArch and LoongArch64 use same machine type, distinguished by ELF class) - else if (tb_strncmp(arch, "loongarch", 9) == 0 || tb_strncmp(arch, "loong64", 7) == 0) { - return XM_ELF_MACHINE_LOONGARCH; - } - // WebAssembly (WASM and WASM64 use same machine type, distinguished by ELF class) - else if (tb_strncmp(arch, "wasm", 4) == 0) { - return XM_ELF_MACHINE_WASM; - } - // SuperH - else if (tb_strncmp(arch, "sh", 2) == 0 || tb_strncmp(arch, "superh", 6) == 0) { - return XM_ELF_MACHINE_SUPERH; - } - // IA-64 (Itanium) - else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) { - return XM_ELF_MACHINE_IA_64; - } - return XM_ELF_MACHINE_X86_64; -} - -static tb_bool_t xm_binutils_bin2elf_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; -} - -static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *arch, - tb_char_t const *basename, - tb_bool_t zeroend) { - tb_assert_and_check_return_val(istream && ostream, tb_false); - - // get file size - tb_hong_t filesize = tb_stream_size(istream); - if (filesize < 0 || filesize > 0xffffffffU) { - return tb_false; - } - tb_uint32_t datasize = (tb_uint32_t)filesize; - // add null terminator if zeroend is true - if (zeroend) { - if (datasize >= 0xffffffffU) { - return tb_false; // would overflow - } - datasize++; - } - - // generate symbol names from filename - tb_char_t symbol_name[256] = {0}; - tb_char_t symbol_start[256] = {0}; - tb_char_t symbol_end[256] = {0}; - - // use basename or default to "data" - if (!basename || !basename[0]) { - basename = "data"; - } - - // build symbol name - if (symbol_prefix) { - tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); - } - - // replace non-alphanumeric with underscore - for (tb_size_t i = 0; symbol_name[i]; i++) { - if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { - symbol_name[i] = '_'; - } - } - - tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); - tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); - - // calculate offsets - tb_uint32_t header_size = sizeof(xm_elf32_header_t); - tb_uint32_t section_header_size = sizeof(xm_elf32_section_t); - tb_uint32_t section_count = 6; // NULL, .rodata, .symtab, .strtab, .shstrtab, .note.GNU-stack - tb_uint32_t section_headers_ofs = header_size; - tb_uint32_t rodata_ofs = section_headers_ofs + section_count * section_header_size; - tb_uint32_t rodata_size = datasize; - tb_uint32_t rodata_padding = (4 - (rodata_size & 3)) & 3; // align to 4 bytes for 32-bit - tb_uint32_t symtab_ofs = rodata_ofs + rodata_size + rodata_padding; - tb_uint32_t symtab_size = 3 * sizeof(xm_elf32_symbol_t); // NULL, start, end - tb_uint32_t symtab_padding = (4 - (symtab_size & 3)) & 3; // align to 4 bytes - tb_uint32_t strtab_ofs = symtab_ofs + symtab_size + symtab_padding; - - // calculate string table size - tb_size_t start_len = tb_strlen(symbol_start); - tb_size_t end_len = tb_strlen(symbol_end); - tb_uint32_t strtab_size = 1; // initial null byte - strtab_size += (tb_uint32_t)(start_len + 1); - strtab_size += (tb_uint32_t)(end_len + 1); - tb_uint32_t strtab_padding = (4 - (strtab_size & 3)) & 3; // align to 4 bytes - tb_uint32_t shstrtab_ofs = strtab_ofs + strtab_size + strtab_padding; - - // calculate section header string table size - tb_uint32_t shstrtab_size = 1; // initial null byte - shstrtab_size += 8; // ".rodata\0" (7 + 1) - shstrtab_size += 8; // ".symtab\0" (7 + 1) - shstrtab_size += 8; // ".strtab\0" (7 + 1) - shstrtab_size += 10; // ".shstrtab\0" (9 + 1) - shstrtab_size += 16; // ".note.GNU-stack\0" (15 + 1) - - // write ELF header - xm_elf32_header_t header; - tb_memset(&header, 0, sizeof(header)); - header.e_ident[0] = 0x7f; - header.e_ident[1] = 'E'; - header.e_ident[2] = 'L'; - header.e_ident[3] = 'F'; - header.e_ident[4] = 1; // ELFCLASS32 - header.e_ident[5] = 1; // ELFDATA2LSB - header.e_ident[6] = 1; // EV_CURRENT - header.e_ident[7] = 0; // ELFOSABI_SYSV - header.e_type = 1; // ET_REL - header.e_machine = xm_binutils_bin2elf_get_machine(arch); - header.e_version = 1; - header.e_shoff = section_headers_ofs; - header.e_ehsize = header_size; - header.e_shentsize = section_header_size; - header.e_shnum = section_count; - header.e_shstrndx = 4; // .shstrtab section index - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { - return tb_false; - } - - // write section headers - xm_elf32_section_t section_null; - tb_memset(§ion_null, 0, sizeof(section_null)); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_null, sizeof(section_null))) { - return tb_false; - } - - // write .rodata section header - xm_elf32_section_t section_rodata; - tb_memset(§ion_rodata, 0, sizeof(section_rodata)); - section_rodata.sh_name = 1; // ".rodata" in shstrtab - section_rodata.sh_type = XM_ELF_SHT_PROGBITS; - section_rodata.sh_flags = XM_ELF_SHF_ALLOC; - section_rodata.sh_offset = rodata_ofs; - section_rodata.sh_size = rodata_size; - section_rodata.sh_addralign = 4; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_rodata, sizeof(section_rodata))) { - return tb_false; - } - - // write .symtab section header - xm_elf32_section_t section_symtab; - tb_memset(§ion_symtab, 0, sizeof(section_symtab)); - section_symtab.sh_name = 9; // ".symtab" in shstrtab - section_symtab.sh_type = XM_ELF_SHT_SYMTAB; - section_symtab.sh_offset = symtab_ofs; - section_symtab.sh_size = symtab_size; - section_symtab.sh_link = 3; // .strtab section index - section_symtab.sh_info = 1; // first global symbol index - section_symtab.sh_addralign = 4; - section_symtab.sh_entsize = sizeof(xm_elf32_symbol_t); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_symtab, sizeof(section_symtab))) { - return tb_false; - } - - // write .strtab section header - xm_elf32_section_t section_strtab; - tb_memset(§ion_strtab, 0, sizeof(section_strtab)); - section_strtab.sh_name = 17; // ".strtab" in shstrtab - section_strtab.sh_type = XM_ELF_SHT_STRTAB; - section_strtab.sh_offset = strtab_ofs; - section_strtab.sh_size = strtab_size; - section_strtab.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_strtab, sizeof(section_strtab))) { - return tb_false; - } - - // write .shstrtab section header - xm_elf32_section_t section_shstrtab; - tb_memset(§ion_shstrtab, 0, sizeof(section_shstrtab)); - section_shstrtab.sh_name = 25; // ".shstrtab" in shstrtab - section_shstrtab.sh_type = XM_ELF_SHT_STRTAB; - section_shstrtab.sh_offset = shstrtab_ofs; - section_shstrtab.sh_size = shstrtab_size; - section_shstrtab.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_shstrtab, sizeof(section_shstrtab))) { - return tb_false; - } - - // write .note.GNU-stack section header (empty section to mark stack as non-executable) - xm_elf32_section_t section_note_gnu_stack; - tb_memset(§ion_note_gnu_stack, 0, sizeof(section_note_gnu_stack)); - section_note_gnu_stack.sh_name = 35; // ".note.GNU-stack" in shstrtab (25 + 10) - section_note_gnu_stack.sh_type = XM_ELF_SHT_PROGBITS; - section_note_gnu_stack.sh_flags = 0; // no flags - section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab - section_note_gnu_stack.sh_size = 0; // empty section - section_note_gnu_stack.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_note_gnu_stack, sizeof(section_note_gnu_stack))) { - return tb_false; - } - - // write .rodata section data - if (!xm_binutils_stream_copy(istream, ostream, filesize)) { - return tb_false; - } - // append null terminator if zeroend is true - if (zeroend) { - tb_byte_t zero = 0; - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - - // align .rodata to 4 bytes - if (rodata_padding > 0) { - tb_byte_t zero = 0; - while (rodata_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write symbol table - // symbol 0: NULL symbol - xm_elf32_symbol_t sym_null; - tb_memset(&sym_null, 0, sizeof(sym_null)); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) { - return tb_false; - } - - // symbol 1: _binary_xxx_start - xm_elf32_symbol_t sym_start; - tb_memset(&sym_start, 0, sizeof(sym_start)); - sym_start.st_name = 1; // offset in .strtab (after initial null) - sym_start.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; - sym_start.st_shndx = 1; // .rodata section index - sym_start.st_value = 0; - sym_start.st_size = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) { - return tb_false; - } - - // symbol 2: _binary_xxx_end - xm_elf32_symbol_t sym_end; - tb_memset(&sym_end, 0, sizeof(sym_end)); - sym_end.st_name = 1 + (tb_uint32_t)(start_len + 1); // offset in .strtab - sym_end.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; - sym_end.st_shndx = 1; // .rodata section index - sym_end.st_value = rodata_size; - sym_end.st_size = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) { - return tb_false; - } - - // align .symtab to 4 bytes - if (symtab_padding > 0) { - tb_byte_t zero = 0; - while (symtab_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write string table - tb_byte_t null = 0; - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - - // align .strtab to 4 bytes - if (strtab_padding > 0) { - tb_byte_t zero = 0; - while (strtab_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write section header string table - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".rodata", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".symtab", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".strtab", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".shstrtab", 9)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".note.GNU-stack", 15)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - - return tb_true; -} - -static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *arch, - tb_char_t const *basename, - tb_bool_t zeroend) { - tb_assert_and_check_return_val(istream && ostream, tb_false); - - // get file size - tb_hong_t filesize = tb_stream_size(istream); - if (filesize < 0 || filesize > 0xffffffffU) { - return tb_false; - } - tb_uint32_t datasize = (tb_uint32_t)filesize; - // add null terminator if zeroend is true - if (zeroend) { - if (datasize >= 0xffffffffU) { - return tb_false; // would overflow - } - datasize++; - } - - // generate symbol names from filename - tb_char_t symbol_name[256] = {0}; - tb_char_t symbol_start[256] = {0}; - tb_char_t symbol_end[256] = {0}; - - // use basename or default to "data" - if (!basename || !basename[0]) { - basename = "data"; - } - - // build symbol name - if (symbol_prefix) { - tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); - } - - // replace non-alphanumeric with underscore - for (tb_size_t i = 0; symbol_name[i]; i++) { - if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { - symbol_name[i] = '_'; - } - } - - tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); - tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); - - // calculate offsets - tb_uint32_t header_size = sizeof(xm_elf64_header_t); - tb_uint32_t section_header_size = sizeof(xm_elf64_section_t); - tb_uint32_t section_count = 6; // NULL, .rodata, .symtab, .strtab, .shstrtab, .note.GNU-stack - tb_uint32_t section_headers_ofs = header_size; - tb_uint32_t rodata_ofs = section_headers_ofs + section_count * section_header_size; - tb_uint32_t rodata_size = datasize; - tb_uint32_t rodata_padding = (8 - (rodata_size & 7)) & 7; - tb_uint32_t symtab_ofs = rodata_ofs + rodata_size + rodata_padding; - tb_uint32_t symtab_size = 3 * sizeof(xm_elf64_symbol_t); // NULL, start, end - tb_uint32_t symtab_padding = (8 - (symtab_size & 7)) & 7; - tb_uint32_t strtab_ofs = symtab_ofs + symtab_size + symtab_padding; - - // calculate string table size - tb_size_t start_len = tb_strlen(symbol_start); - tb_size_t end_len = tb_strlen(symbol_end); - tb_uint32_t strtab_size = 1; // initial null byte - strtab_size += (tb_uint32_t)(start_len + 1); - strtab_size += (tb_uint32_t)(end_len + 1); - tb_uint32_t strtab_padding = (8 - (strtab_size & 7)) & 7; - - // calculate section header string table size - tb_uint32_t shstrtab_size = 1; // initial null byte - shstrtab_size += 8; // ".rodata\0" (7 + 1) - shstrtab_size += 8; // ".symtab\0" (7 + 1) - shstrtab_size += 8; // ".strtab\0" (7 + 1) - shstrtab_size += 10; // ".shstrtab\0" (9 + 1) - shstrtab_size += 16; // ".note.GNU-stack\0" (15 + 1) - tb_uint32_t shstrtab_ofs = strtab_ofs + strtab_size + strtab_padding; - - // write ELF header - xm_elf64_header_t header; - tb_memset(&header, 0, sizeof(header)); - header.e_ident[0] = 0x7f; - header.e_ident[1] = 'E'; - header.e_ident[2] = 'L'; - header.e_ident[3] = 'F'; - header.e_ident[4] = 2; // ELFCLASS64 - header.e_ident[5] = 1; // ELFDATA2LSB - header.e_ident[6] = 1; // EV_CURRENT - header.e_ident[7] = 0; // ELFOSABI_SYSV - header.e_type = 1; // ET_REL - header.e_machine = xm_binutils_bin2elf_get_machine(arch); - header.e_version = 1; - header.e_shoff = section_headers_ofs; - header.e_ehsize = header_size; - header.e_shentsize = section_header_size; - header.e_shnum = section_count; - header.e_shstrndx = 4; // .shstrtab section index - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { - return tb_false; - } - - // write section headers - xm_elf64_section_t section_null; - tb_memset(§ion_null, 0, sizeof(section_null)); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_null, sizeof(section_null))) { - return tb_false; - } - - // write .rodata section header - xm_elf64_section_t section_rodata; - tb_memset(§ion_rodata, 0, sizeof(section_rodata)); - section_rodata.sh_name = 1; // ".rodata" in shstrtab - section_rodata.sh_type = XM_ELF_SHT_PROGBITS; - section_rodata.sh_flags = XM_ELF_SHF_ALLOC; - section_rodata.sh_offset = rodata_ofs; - section_rodata.sh_size = rodata_size; - section_rodata.sh_addralign = 8; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_rodata, sizeof(section_rodata))) { - return tb_false; - } - - // write .symtab section header - xm_elf64_section_t section_symtab; - tb_memset(§ion_symtab, 0, sizeof(section_symtab)); - section_symtab.sh_name = 9; // ".symtab" in shstrtab - section_symtab.sh_type = XM_ELF_SHT_SYMTAB; - section_symtab.sh_offset = symtab_ofs; - section_symtab.sh_size = symtab_size; - section_symtab.sh_link = 3; // .strtab section index - section_symtab.sh_info = 1; // first global symbol index - section_symtab.sh_addralign = 8; - section_symtab.sh_entsize = sizeof(xm_elf64_symbol_t); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_symtab, sizeof(section_symtab))) { - return tb_false; - } - - // write .strtab section header - xm_elf64_section_t section_strtab; - tb_memset(§ion_strtab, 0, sizeof(section_strtab)); - section_strtab.sh_name = 17; // ".strtab" in shstrtab - section_strtab.sh_type = XM_ELF_SHT_STRTAB; - section_strtab.sh_offset = strtab_ofs; - section_strtab.sh_size = strtab_size; - section_strtab.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_strtab, sizeof(section_strtab))) { - return tb_false; - } - - // write .shstrtab section header - xm_elf64_section_t section_shstrtab; - tb_memset(§ion_shstrtab, 0, sizeof(section_shstrtab)); - section_shstrtab.sh_name = 25; // ".shstrtab" in shstrtab - section_shstrtab.sh_type = XM_ELF_SHT_STRTAB; - section_shstrtab.sh_offset = shstrtab_ofs; // points to initial null byte - section_shstrtab.sh_size = shstrtab_size; // size includes initial null and all strings - section_shstrtab.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_shstrtab, sizeof(section_shstrtab))) { - return tb_false; - } - - // write .note.GNU-stack section header (empty section to mark stack as non-executable) - xm_elf64_section_t section_note_gnu_stack; - tb_memset(§ion_note_gnu_stack, 0, sizeof(section_note_gnu_stack)); - section_note_gnu_stack.sh_name = 35; // ".note.GNU-stack" in shstrtab (25 + 10) - section_note_gnu_stack.sh_type = XM_ELF_SHT_PROGBITS; - section_note_gnu_stack.sh_flags = 0; // no flags - section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab - section_note_gnu_stack.sh_size = 0; // empty section - section_note_gnu_stack.sh_addralign = 1; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_note_gnu_stack, sizeof(section_note_gnu_stack))) { - return tb_false; - } - - // write .rodata section data - if (!xm_binutils_stream_copy(istream, ostream, filesize)) { - return tb_false; - } - // append null terminator if zeroend is true - if (zeroend) { - tb_byte_t zero = 0; - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - - // align .rodata to 8 bytes - if (rodata_padding > 0) { - tb_byte_t zero = 0; - while (rodata_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write symbol table - // symbol 0: NULL symbol - xm_elf64_symbol_t sym_null; - tb_memset(&sym_null, 0, sizeof(sym_null)); - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) { - return tb_false; - } - - // symbol 1: _binary_xxx_start - xm_elf64_symbol_t sym_start; - tb_memset(&sym_start, 0, sizeof(sym_start)); - sym_start.st_name = 1; // offset in .strtab (after initial null) - sym_start.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; - sym_start.st_shndx = 1; // .rodata section index - sym_start.st_value = 0; - sym_start.st_size = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) { - return tb_false; - } - - // symbol 2: _binary_xxx_end - xm_elf64_symbol_t sym_end; - tb_memset(&sym_end, 0, sizeof(sym_end)); - sym_end.st_name = 1 + (tb_uint32_t)(start_len + 1); // offset in .strtab - sym_end.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; - sym_end.st_shndx = 1; // .rodata section index - sym_end.st_value = rodata_size; - sym_end.st_size = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) { - return tb_false; - } - - // align .symtab to 8 bytes - if (symtab_padding > 0) { - tb_byte_t zero = 0; - while (symtab_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write string table - tb_byte_t null = 0; - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - - // align .strtab to 8 bytes - if (strtab_padding > 0) { - tb_byte_t zero = 0; - while (strtab_padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write section header string table - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".rodata", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".symtab", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".strtab", 7)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".shstrtab", 9)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".note.GNU-stack", 15)) { - return tb_false; - } - if (!tb_stream_bwrit(ostream, &null, 1)) { - return tb_false; - } - - return tb_true; -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* generate ELF object file from binary file - * - * local ok, errors = binutils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend) - */ -tb_int_t xm_binutils_bin2elf(lua_State *lua) { - tb_assert_and_check_return_val(lua, 0); - - // get the binaryfile - tb_char_t const *binaryfile = luaL_checkstring(lua, 1); - tb_check_return_val(binaryfile, 0); - - // get the outputfile - tb_char_t const *outputfile = luaL_checkstring(lua, 2); - tb_check_return_val(outputfile, 0); - - // get symbol prefix (optional) - tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; - - // get arch (optional) - tb_char_t const *arch = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; - - // get basename (optional) - tb_char_t const *basename = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; - - // get zeroend (optional, default: false) - tb_bool_t zeroend = lua_toboolean(lua, 6); - - // do dump - tb_bool_t ok = tb_false; - tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); - tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, - TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); - do { - if (!tb_stream_open(istream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2elf: open %s failed", binaryfile); - break; - } - - if (!tb_stream_open(ostream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2elf: open %s failed", outputfile); - break; - } - - // choose 32-bit or 64-bit ELF based on architecture - tb_bool_t is_64bit = xm_binutils_bin2elf_is_64bit(arch); - if (is_64bit) { - if (!xm_binutils_bin2elf_dump_64(istream, ostream, symbol_prefix, arch, basename, zeroend)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2elf: dump data failed"); - break; - } - } else { - if (!xm_binutils_bin2elf_dump_32(istream, ostream, symbol_prefix, arch, basename, zeroend)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2elf: dump data failed"); - break; - } - } - - ok = tb_true; - lua_pushboolean(lua, ok); - - } while (0); - - if (istream) - tb_stream_clos(istream); - istream = tb_null; - - if (ostream) - tb_stream_clos(ostream); - ostream = tb_null; - - return ok ? 1 : 2; -} - diff --git a/core/src/xmake/binutils/bin2macho.c b/core/src/xmake/binutils/bin2macho.c deleted file mode 100644 index 45d244316..000000000 --- a/core/src/xmake/binutils/bin2macho.c +++ /dev/null @@ -1,887 +0,0 @@ -/*!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 bin2macho.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "bin2macho" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * macros - */ -#define XM_MACHO_MAGIC_32 0xfeedface -#define XM_MACHO_MAGIC_64 0xfeedfacf -#define XM_MACHO_MAGIC_FAT 0xcafebabe - -#define XM_MACHO_CPU_TYPE_X86 7 -#define XM_MACHO_CPU_TYPE_X86_64 0x01000007 -#define XM_MACHO_CPU_TYPE_ARM 12 -#define XM_MACHO_CPU_TYPE_ARM64 0x0100000c - -#define XM_MACHO_CPU_SUBTYPE_X86 3 -#define XM_MACHO_CPU_SUBTYPE_X86_64 3 -#define XM_MACHO_CPU_SUBTYPE_ARM 9 -#define XM_MACHO_CPU_SUBTYPE_ARM64 0 - -#define XM_MACHO_FILE_TYPE_OBJECT 1 - -#define XM_MACHO_LC_SEGMENT 0x1 -#define XM_MACHO_LC_SEGMENT_64 0x19 -#define XM_MACHO_LC_SYMTAB 0x2 -#define XM_MACHO_LC_BUILD_VERSION 0x32 - -#define XM_MACHO_PLATFORM_MACOS 1 -#define XM_MACHO_PLATFORM_IOS 2 -#define XM_MACHO_PLATFORM_TVOS 3 -#define XM_MACHO_PLATFORM_WATCHOS 4 - -#define XM_MACHO_SECT_TYPE_REGULAR 0x0 -#define XM_MACHO_SECT_ATTR_SOME_INITS 0x400 -#define XM_MACHO_SECT_ATTR_PURE_INSTRUCTIONS 0x80000000 - -#define XM_MACHO_N_TYPE_MASK 0x0e -#define XM_MACHO_N_TYPE_SECT 0x0e -#define XM_MACHO_N_EXT 0x01 - -#define XM_MACHO_VM_PROT_READ 1 -#define XM_MACHO_VM_PROT_WRITE 2 -#define XM_MACHO_VM_PROT_EXECUTE 4 - -/* ////////////////////////////////////////////////////////////////////////////////////// - * types - */ -#include "tbox/prefix/packed.h" -typedef struct __xm_macho_header_t { - tb_uint32_t magic; - tb_uint32_t cputype; - tb_uint32_t cpusubtype; - tb_uint32_t filetype; - tb_uint32_t ncmds; - tb_uint32_t sizeofcmds; - tb_uint32_t flags; -} __tb_packed__ xm_macho_header_t; - -typedef struct __xm_macho_header_64_t { - tb_uint32_t magic; - tb_uint32_t cputype; - tb_uint32_t cpusubtype; - tb_uint32_t filetype; - tb_uint32_t ncmds; - tb_uint32_t sizeofcmds; - tb_uint32_t flags; - tb_uint32_t reserved; -} __tb_packed__ xm_macho_header_64_t; - -typedef struct __xm_macho_segment_command_t { - tb_uint32_t cmd; - tb_uint32_t cmdsize; - tb_char_t segname[16]; - tb_uint32_t vmaddr; - tb_uint32_t vmsize; - tb_uint32_t fileoff; - tb_uint32_t filesize; - tb_uint32_t maxprot; - tb_uint32_t initprot; - tb_uint32_t nsects; - tb_uint32_t flags; -} __tb_packed__ xm_macho_segment_command_t; - -typedef struct __xm_macho_segment_command_64_t { - tb_uint32_t cmd; - tb_uint32_t cmdsize; - tb_char_t segname[16]; - tb_uint64_t vmaddr; - tb_uint64_t vmsize; - tb_uint64_t fileoff; - tb_uint64_t filesize; - tb_uint32_t maxprot; - tb_uint32_t initprot; - tb_uint32_t nsects; - tb_uint32_t flags; -} __tb_packed__ xm_macho_segment_command_64_t; - -typedef struct __xm_macho_section_t { - tb_char_t sectname[16]; - tb_char_t segname[16]; - tb_uint32_t addr; - tb_uint32_t size; - tb_uint32_t offset; - tb_uint32_t align; - tb_uint32_t reloff; - tb_uint32_t nreloc; - tb_uint32_t flags; - tb_uint32_t reserved1; - tb_uint32_t reserved2; -} __tb_packed__ xm_macho_section_t; - -typedef struct __xm_macho_section_64_t { - tb_char_t sectname[16]; - tb_char_t segname[16]; - tb_uint64_t addr; - tb_uint64_t size; - tb_uint32_t offset; - tb_uint32_t align; - tb_uint32_t reloff; - tb_uint32_t nreloc; - tb_uint32_t flags; - tb_uint32_t reserved1; - tb_uint32_t reserved2; - tb_uint32_t reserved3; -} __tb_packed__ xm_macho_section_64_t; - -typedef struct __xm_macho_symtab_command_t { - tb_uint32_t cmd; - tb_uint32_t cmdsize; - tb_uint32_t symoff; - tb_uint32_t nsyms; - tb_uint32_t stroff; - tb_uint32_t strsize; -} __tb_packed__ xm_macho_symtab_command_t; - -typedef struct __xm_macho_build_version_command_t { - tb_uint32_t cmd; - tb_uint32_t cmdsize; - tb_uint32_t platform; - tb_uint32_t minos; - tb_uint32_t sdk; - tb_uint32_t ntools; -} __tb_packed__ xm_macho_build_version_command_t; - -typedef struct __xm_macho_nlist_t { - tb_uint32_t strx; - tb_uint8_t type; - tb_uint8_t sect; - tb_int16_t desc; - tb_uint32_t value; -} __tb_packed__ xm_macho_nlist_t; - -typedef struct __xm_macho_nlist_64_t { - tb_uint32_t strx; - tb_uint8_t type; - tb_uint8_t sect; - tb_uint16_t desc; - tb_uint64_t value; -} __tb_packed__ xm_macho_nlist_64_t; -#include "tbox/prefix/packed.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * private implementation - */ -static tb_uint32_t xm_binutils_bin2macho_get_cputype(tb_char_t const *arch) { - if (!arch) { - return XM_MACHO_CPU_TYPE_X86_64; - } - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return XM_MACHO_CPU_TYPE_X86_64; - } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { - return XM_MACHO_CPU_TYPE_ARM64; - } else if (tb_strcmp(arch, "arm") == 0) { - return XM_MACHO_CPU_TYPE_ARM; - } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { - return XM_MACHO_CPU_TYPE_X86; - } - return XM_MACHO_CPU_TYPE_X86_64; -} - -static tb_uint32_t xm_binutils_bin2macho_get_cpusubtype(tb_char_t const *arch) { - if (!arch) { - return XM_MACHO_CPU_SUBTYPE_X86_64; - } - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return XM_MACHO_CPU_SUBTYPE_X86_64; - } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { - return XM_MACHO_CPU_SUBTYPE_ARM64; - } else if (tb_strcmp(arch, "arm") == 0) { - return XM_MACHO_CPU_SUBTYPE_ARM; - } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { - return XM_MACHO_CPU_SUBTYPE_X86; - } - return XM_MACHO_CPU_SUBTYPE_X86_64; -} - -static tb_bool_t xm_binutils_bin2macho_is_64bit(tb_char_t const *arch) { - if (!arch) { - return tb_true; - } - if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { - return tb_true; - } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { - return tb_true; - } else if (tb_strcmp(arch, "arm") == 0) { - return tb_false; - } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { - return tb_false; - } - return tb_true; -} - -static tb_uint32_t xm_binutils_bin2macho_align(tb_uint32_t value, tb_uint32_t align) { - return ((value + align - 1) & ~(align - 1)); -} - -static tb_uint32_t xm_binutils_bin2macho_get_platform(tb_char_t const *platform) { - if (!platform) { - return XM_MACHO_PLATFORM_MACOS; - } - if (tb_strcmp(platform, "macosx") == 0 || tb_strcmp(platform, "macos") == 0) { - return XM_MACHO_PLATFORM_MACOS; - } else if (tb_strcmp(platform, "iphoneos") == 0 || tb_strcmp(platform, "ios") == 0) { - return XM_MACHO_PLATFORM_IOS; - } else if (tb_strcmp(platform, "appletvos") == 0 || tb_strcmp(platform, "tvos") == 0) { - return XM_MACHO_PLATFORM_TVOS; - } else if (tb_strcmp(platform, "watchos") == 0) { - return XM_MACHO_PLATFORM_WATCHOS; - } - return XM_MACHO_PLATFORM_MACOS; -} - -// parse version string (e.g., "10.0" or "18.2") to Mach-O format (0x000a0000) -// format: (major << 16) | (minor << 8) | patch -static tb_uint32_t xm_binutils_bin2macho_parse_version(tb_char_t const *version_str) { - if (!version_str || !version_str[0]) { - return 0x000a0000; // default: 10.0.0 - } - tb_uint32_t major = 0; - tb_uint32_t minor = 0; - tb_uint32_t patch = 0; - tb_char_t const *p = version_str; - // parse major - while (*p && tb_isdigit(*p)) { - major = major * 10 + (*p - '0'); - p++; - } - if (*p == '.') { - p++; - // parse minor - while (*p && tb_isdigit(*p)) { - minor = minor * 10 + (*p - '0'); - p++; - } - if (*p == '.') { - p++; - // parse patch - while (*p && tb_isdigit(*p)) { - patch = patch * 10 + (*p - '0'); - p++; - } - } - } - return (major << 16) | (minor << 8) | patch; -} - -static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *plat, - tb_char_t const *arch, - tb_char_t const *basename, - tb_uint32_t minos, - tb_uint32_t sdk, - tb_bool_t zeroend) { - tb_assert_and_check_return_val(istream && ostream, tb_false); - - // get file size - tb_hong_t filesize = tb_stream_size(istream); - if (filesize < 0 || filesize > 0xffffffffU) { - return tb_false; - } - tb_uint32_t datasize = (tb_uint32_t)filesize; - // add null terminator if zeroend is true - if (zeroend) { - if (datasize >= 0xffffffffU) { - return tb_false; // would overflow - } - datasize++; - } - - // generate symbol names from filename - tb_char_t symbol_name[256] = {0}; - tb_char_t symbol_start[256] = {0}; - tb_char_t symbol_end[256] = {0}; - - // use basename or default to "data" - if (!basename || !basename[0]) { - basename = "data"; - } - - // build symbol name - // On macOS, C compiler adds an underscore prefix, so we generate symbols with two underscores - // (C code declares _binary_xxx, compiler generates __binary_xxx in object file, so we define __binary_xxx) - if (symbol_prefix) { - tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); - } - - // replace non-alphanumeric with underscore - for (tb_size_t i = 0; symbol_name[i]; i++) { - if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { - symbol_name[i] = '_'; - } - } - - tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); - tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); - - // calculate offsets - tb_uint32_t header_size = sizeof(xm_macho_header_64_t); - tb_uint32_t segment_cmd_size = sizeof(xm_macho_segment_command_64_t); - tb_uint32_t section_size = sizeof(xm_macho_section_64_t); - tb_uint32_t symtab_cmd_size = sizeof(xm_macho_symtab_command_t); - tb_uint32_t build_version_cmd_size = sizeof(xm_macho_build_version_command_t); - tb_uint32_t segment_cmd_total_size = segment_cmd_size + section_size; - tb_uint32_t data_offset = xm_binutils_bin2macho_align(header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size, 8); - tb_uint32_t data_size = datasize; - tb_uint32_t data_end_offset = data_offset + data_size; - tb_uint32_t symtab_offset = xm_binutils_bin2macho_align(data_end_offset, 8); - tb_uint32_t nlist_size = sizeof(xm_macho_nlist_64_t); - tb_uint32_t nlist_count = 2; // start, end - tb_uint32_t strtab_offset = symtab_offset + nlist_size * nlist_count; - tb_uint32_t strtab_size = 4; // initial 4-byte size field - tb_size_t start_len = tb_strlen(symbol_start); - tb_size_t end_len = tb_strlen(symbol_end); - strtab_size += (tb_uint32_t)(start_len + 1); - strtab_size += (tb_uint32_t)(end_len + 1); - strtab_size = xm_binutils_bin2macho_align(strtab_size, 8); - - // write Mach-O header - xm_macho_header_64_t header; - tb_memset(&header, 0, sizeof(header)); - header.magic = XM_MACHO_MAGIC_64; - header.cputype = xm_binutils_bin2macho_get_cputype(arch); - header.cpusubtype = xm_binutils_bin2macho_get_cpusubtype(arch); - header.filetype = XM_MACHO_FILE_TYPE_OBJECT; - header.ncmds = 3; // segment + symtab + build_version - header.sizeofcmds = segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size; - header.flags = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { - return tb_false; - } - - // write segment command - xm_macho_segment_command_64_t segment; - tb_memset(&segment, 0, sizeof(segment)); - segment.cmd = XM_MACHO_LC_SEGMENT_64; - segment.cmdsize = segment_cmd_total_size; - tb_strncpy(segment.segname, "__TEXT", 16); - segment.vmaddr = 0; - segment.vmsize = data_size; - segment.fileoff = data_offset; - segment.filesize = data_size; - segment.maxprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) - segment.initprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) - segment.nsects = 1; - segment.flags = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&segment, sizeof(segment))) { - return tb_false; - } - - // write section - xm_macho_section_64_t section; - tb_memset(§ion, 0, sizeof(section)); - tb_strncpy(section.sectname, "__const", 16); - tb_strncpy(section.segname, "__TEXT", 16); - section.addr = 0; - section.size = data_size; - section.offset = data_offset; - section.align = 3; // 2^3 = 8 bytes - section.reloff = 0; - section.nreloc = 0; - section.flags = XM_MACHO_SECT_TYPE_REGULAR | XM_MACHO_SECT_ATTR_SOME_INITS; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { - return tb_false; - } - - // write symtab command - xm_macho_symtab_command_t symtab; - tb_memset(&symtab, 0, sizeof(symtab)); - symtab.cmd = XM_MACHO_LC_SYMTAB; - symtab.cmdsize = symtab_cmd_size; - symtab.symoff = symtab_offset; - symtab.nsyms = nlist_count; - symtab.stroff = strtab_offset; - symtab.strsize = strtab_size; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&symtab, sizeof(symtab))) { - return tb_false; - } - - // write build version command - xm_macho_build_version_command_t build_version; - tb_memset(&build_version, 0, sizeof(build_version)); - build_version.cmd = XM_MACHO_LC_BUILD_VERSION; - build_version.cmdsize = build_version_cmd_size; - build_version.platform = xm_binutils_bin2macho_get_platform(plat); - build_version.minos = minos; - build_version.sdk = sdk; - build_version.ntools = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&build_version, sizeof(build_version))) { - return tb_false; - } - - // align to 8 bytes - tb_uint32_t padding = data_offset - (header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write section data - if (!xm_binutils_stream_copy(istream, ostream, filesize)) { - return tb_false; - } - // append null terminator if zeroend is true - if (zeroend) { - tb_byte_t zero = 0; - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - - // align to 8 bytes - padding = symtab_offset - data_end_offset; - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write symbol table - // strx starts from 4 (after 4-byte size field) - tb_uint32_t strx = 4; - // symbol 0: _binary_xxx_start - xm_macho_nlist_64_t nlist_start; - tb_memset(&nlist_start, 0, sizeof(nlist_start)); - nlist_start.strx = strx; - nlist_start.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT - nlist_start.sect = 1; - nlist_start.desc = 0; - nlist_start.value = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_start, sizeof(nlist_start))) { - return tb_false; - } - strx += (tb_uint32_t)(start_len + 1); - - // symbol 1: _binary_xxx_end - xm_macho_nlist_64_t nlist_end; - tb_memset(&nlist_end, 0, sizeof(nlist_end)); - nlist_end.strx = strx; - nlist_end.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT - nlist_end.sect = 1; - nlist_end.desc = 0; - nlist_end.value = data_size; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_end, sizeof(nlist_end))) { - return tb_false; - } - strx += (tb_uint32_t)(end_len + 1); - - // align to 8 bytes - padding = strtab_offset - (symtab_offset + nlist_size * nlist_count); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write string table - tb_stream_bwrit(ostream, (tb_byte_t const *)&strtab_size, 4); - tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len); - tb_byte_t null = 0; - tb_stream_bwrit(ostream, &null, 1); - tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len); - tb_stream_bwrit(ostream, &null, 1); - - // align string table to 8 bytes - padding = strtab_size - (4 + (tb_uint32_t)start_len + 1 + (tb_uint32_t)end_len + 1); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - return tb_true; -} - -static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *plat, - tb_char_t const *arch, - tb_char_t const *basename, - tb_uint32_t minos, - tb_uint32_t sdk, - tb_bool_t zeroend) { - tb_assert_and_check_return_val(istream && ostream, tb_false); - - // get file size - tb_hong_t filesize = tb_stream_size(istream); - if (filesize < 0 || filesize > 0xffffffffU) { - return tb_false; - } - tb_uint32_t datasize = (tb_uint32_t)filesize; - // add null terminator if zeroend is true - if (zeroend) { - if (datasize >= 0xffffffffU) { - return tb_false; // would overflow - } - datasize++; - } - - // generate symbol names from filename - tb_char_t symbol_name[256] = {0}; - tb_char_t symbol_start[256] = {0}; - tb_char_t symbol_end[256] = {0}; - - // use basename or default to "data" - if (!basename || !basename[0]) { - basename = "data"; - } - - // build symbol name - // On macOS, C compiler adds an underscore prefix, so we generate symbols with two underscores - // (C code declares _binary_xxx, compiler generates __binary_xxx in object file, so we define __binary_xxx) - if (symbol_prefix) { - tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); - } else { - tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); - } - - // replace non-alphanumeric with underscore - for (tb_size_t i = 0; symbol_name[i]; i++) { - if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { - symbol_name[i] = '_'; - } - } - - tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); - tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); - - // calculate offsets - tb_uint32_t header_size = sizeof(xm_macho_header_t); - tb_uint32_t segment_cmd_size = sizeof(xm_macho_segment_command_t); - tb_uint32_t section_size = sizeof(xm_macho_section_t); - tb_uint32_t symtab_cmd_size = sizeof(xm_macho_symtab_command_t); - tb_uint32_t build_version_cmd_size = sizeof(xm_macho_build_version_command_t); - tb_uint32_t segment_cmd_total_size = segment_cmd_size + section_size; - tb_uint32_t data_offset = xm_binutils_bin2macho_align(header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size, 4); - tb_uint32_t data_size = datasize; - tb_uint32_t data_end_offset = data_offset + data_size; - tb_uint32_t symtab_offset = xm_binutils_bin2macho_align(data_end_offset, 4); - tb_uint32_t nlist_size = sizeof(xm_macho_nlist_t); - tb_uint32_t nlist_count = 2; // start, end - tb_uint32_t strtab_offset = symtab_offset + nlist_size * nlist_count; - tb_uint32_t strtab_size = 4; // initial 4-byte size field - tb_size_t start_len = tb_strlen(symbol_start); - tb_size_t end_len = tb_strlen(symbol_end); - strtab_size += (tb_uint32_t)(start_len + 1); - strtab_size += (tb_uint32_t)(end_len + 1); - strtab_size = xm_binutils_bin2macho_align(strtab_size, 4); - - // write Mach-O header - xm_macho_header_t header; - tb_memset(&header, 0, sizeof(header)); - header.magic = XM_MACHO_MAGIC_32; - header.cputype = xm_binutils_bin2macho_get_cputype(arch); - header.cpusubtype = xm_binutils_bin2macho_get_cpusubtype(arch); - header.filetype = XM_MACHO_FILE_TYPE_OBJECT; - header.ncmds = 3; // segment + symtab + build_version - header.sizeofcmds = segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size; - header.flags = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { - return tb_false; - } - - // write segment command - xm_macho_segment_command_t segment; - tb_memset(&segment, 0, sizeof(segment)); - segment.cmd = XM_MACHO_LC_SEGMENT; - segment.cmdsize = segment_cmd_total_size; - tb_strncpy(segment.segname, "__TEXT", 16); - segment.vmaddr = 0; - segment.vmsize = data_size; - segment.fileoff = data_offset; - segment.filesize = data_size; - segment.maxprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) - segment.initprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) - segment.nsects = 1; - segment.flags = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&segment, sizeof(segment))) { - return tb_false; - } - - // write section - xm_macho_section_t section; - tb_memset(§ion, 0, sizeof(section)); - tb_strncpy(section.sectname, "__const", 16); - tb_strncpy(section.segname, "__TEXT", 16); - section.addr = 0; - section.size = data_size; - section.offset = data_offset; - section.align = 2; // 2^2 = 4 bytes - section.reloff = 0; - section.nreloc = 0; - section.flags = XM_MACHO_SECT_TYPE_REGULAR | XM_MACHO_SECT_ATTR_SOME_INITS; - section.reserved1 = 0; - section.reserved2 = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { - return tb_false; - } - - // write symtab command - xm_macho_symtab_command_t symtab; - tb_memset(&symtab, 0, sizeof(symtab)); - symtab.cmd = XM_MACHO_LC_SYMTAB; - symtab.cmdsize = symtab_cmd_size; - symtab.symoff = symtab_offset; - symtab.nsyms = nlist_count; - symtab.stroff = strtab_offset; - symtab.strsize = strtab_size; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&symtab, sizeof(symtab))) { - return tb_false; - } - - // write build version command - xm_macho_build_version_command_t build_version; - tb_memset(&build_version, 0, sizeof(build_version)); - build_version.cmd = XM_MACHO_LC_BUILD_VERSION; - build_version.cmdsize = build_version_cmd_size; - build_version.platform = xm_binutils_bin2macho_get_platform(plat); - build_version.minos = minos; - build_version.sdk = sdk; - build_version.ntools = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&build_version, sizeof(build_version))) { - return tb_false; - } - - // align to 4 bytes - tb_uint32_t padding = data_offset - (header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write section data - if (!xm_binutils_stream_copy(istream, ostream, filesize)) { - return tb_false; - } - // append null terminator if zeroend is true - if (zeroend) { - tb_byte_t zero = 0; - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - - // align to 4 bytes - padding = symtab_offset - data_end_offset; - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write symbol table - // strx starts from 4 (after 4-byte size field) - tb_uint32_t strx = 4; - // symbol 0: _binary_xxx_start - xm_macho_nlist_t nlist_start; - tb_memset(&nlist_start, 0, sizeof(nlist_start)); - nlist_start.strx = strx; - nlist_start.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT - nlist_start.sect = 1; - nlist_start.desc = 0; - nlist_start.value = 0; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_start, sizeof(nlist_start))) { - return tb_false; - } - strx += (tb_uint32_t)(start_len + 1); - - // symbol 1: _binary_xxx_end - xm_macho_nlist_t nlist_end; - tb_memset(&nlist_end, 0, sizeof(nlist_end)); - nlist_end.strx = strx; - nlist_end.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT - nlist_end.sect = 1; - nlist_end.desc = 0; - nlist_end.value = data_size; - if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_end, sizeof(nlist_end))) { - return tb_false; - } - strx += (tb_uint32_t)(end_len + 1); - - // align to 4 bytes - padding = strtab_offset - (symtab_offset + nlist_size * nlist_count); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - // write string table - tb_stream_bwrit(ostream, (tb_byte_t const *)&strtab_size, 4); - tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len); - tb_byte_t null = 0; - tb_stream_bwrit(ostream, &null, 1); - tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len); - tb_stream_bwrit(ostream, &null, 1); - - // align string table to 4 bytes - padding = strtab_size - (4 + (tb_uint32_t)start_len + 1 + (tb_uint32_t)end_len + 1); - if (padding > 0) { - tb_byte_t zero = 0; - while (padding-- > 0) { - if (!tb_stream_bwrit(ostream, &zero, 1)) { - return tb_false; - } - } - } - - return tb_true; -} - -static tb_bool_t xm_binutils_bin2macho_dump(tb_stream_ref_t istream, - tb_stream_ref_t ostream, - tb_char_t const *symbol_prefix, - tb_char_t const *plat, - tb_char_t const *arch, - tb_char_t const *basename, - tb_uint32_t minos, - tb_uint32_t sdk, - tb_bool_t zeroend) { - if (xm_binutils_bin2macho_is_64bit(arch)) { - return xm_binutils_bin2macho_dump_64(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend); - } else { - return xm_binutils_bin2macho_dump_32(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend); - } -} - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* generate Mach-O object file from binary file - * - * local ok, errors = binutils.bin2macho(binaryfile, outputfile, symbol_prefix, plat, arch, basename) - */ -tb_int_t xm_binutils_bin2macho(lua_State *lua) { - tb_assert_and_check_return_val(lua, 0); - - // get the binaryfile - tb_char_t const *binaryfile = luaL_checkstring(lua, 1); - tb_check_return_val(binaryfile, 0); - - // get the outputfile - tb_char_t const *outputfile = luaL_checkstring(lua, 2); - tb_check_return_val(outputfile, 0); - - // get symbol prefix (optional) - tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; - - // get plat (optional) - tb_char_t const *plat = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; - - // get arch (optional) - tb_char_t const *arch = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; - - // get basename (optional) - tb_char_t const *basename = lua_isstring(lua, 6) ? lua_tostring(lua, 6) : tb_null; - - // get minos version string (optional) - tb_char_t const *minos_str = lua_isstring(lua, 7) ? lua_tostring(lua, 7) : tb_null; - tb_uint32_t minos = xm_binutils_bin2macho_parse_version(minos_str); - - // get sdk version string (optional) - tb_char_t const *sdk_str = lua_isstring(lua, 8) ? lua_tostring(lua, 8) : tb_null; - tb_uint32_t sdk = xm_binutils_bin2macho_parse_version(sdk_str); - - // get zeroend (optional, default: false) - tb_bool_t zeroend = lua_toboolean(lua, 9); - - // do dump - tb_bool_t ok = tb_false; - tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); - tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, - TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); - do { - if (!tb_stream_open(istream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2macho: open %s failed", binaryfile); - break; - } - - if (!tb_stream_open(ostream)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2macho: open %s failed", outputfile); - break; - } - - if (!xm_binutils_bin2macho_dump(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend)) { - lua_pushboolean(lua, tb_false); - lua_pushfstring(lua, "bin2macho: dump data failed"); - break; - } - - ok = tb_true; - lua_pushboolean(lua, ok); - - } while (0); - - if (istream) { - tb_stream_clos(istream); - } - istream = tb_null; - - if (ostream) { - tb_stream_clos(ostream); - } - ostream = tb_null; - - return ok ? 1 : 2; -} - diff --git a/core/src/xmake/binutils/coff/bin2coff.c b/core/src/xmake/binutils/coff/bin2coff.c new file mode 100644 index 000000000..2a09cd504 --- /dev/null +++ b/core/src/xmake/binutils/coff/bin2coff.c @@ -0,0 +1,321 @@ +/*!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 bin2coff.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "bin2coff" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *arch, + tb_char_t const *basename, + tb_bool_t zeroend) { + tb_assert_and_check_return_val(istream && ostream, tb_false); + + // get file size + tb_hong_t filesize = tb_stream_size(istream); + if (filesize < 0 || filesize > 0xffffffffU) { + return tb_false; + } + tb_uint32_t datasize = (tb_uint32_t)filesize; + // add null terminator if zeroend is true + if (zeroend) { + if (datasize >= 0xffffffffU) { + return tb_false; // would overflow + } + datasize++; + } + + // determine architecture for symbol prefix adjustment + tb_uint16_t machine = xm_binutils_coff_get_machine(arch); + tb_bool_t is_i386 = (machine == XM_COFF_MACHINE_I386); + + // generate symbol names from filename + tb_char_t symbol_name[256] = {0}; + tb_char_t symbol_start[256] = {0}; + tb_char_t symbol_end[256] = {0}; + + // use basename or default to "data" + if (!basename || !basename[0]) { + basename = "data"; + } + + // build symbol name + // note: on i386 Windows, C compiler automatically adds an underscore prefix to external symbols + // so if we use "_binary_", the actual symbol becomes "__binary_" after compilation + // to match, we need to ensure the prefix has two underscores for i386 + if (symbol_prefix) { + if (is_i386 && symbol_prefix[0] == '_' && symbol_prefix[1] != '_') { + // i386: if prefix starts with single underscore, add another one + tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); + } + } else { + if (is_i386) { + tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); + } + } + + // replace non-alphanumeric with underscore + for (tb_size_t i = 0; symbol_name[i]; i++) { + if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { + symbol_name[i] = '_'; + } + } + + tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); + tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); + + // calculate offsets + tb_uint32_t header_size = sizeof(xm_coff_header_t); + tb_uint32_t section_header_size = sizeof(xm_coff_section_t); + tb_uint32_t section_data_ofs = header_size + section_header_size; + tb_uint32_t section_data_size = datasize; + tb_uint32_t section_data_padding = (4 - (section_data_size & 3)) & 3; + tb_uint32_t symbol_table_ofs = section_data_ofs + section_data_size + section_data_padding; + + // calculate string table size (content only, excluding the 4-byte size field) + tb_uint32_t string_table_content_size = 0; + tb_size_t start_len = tb_strlen(symbol_start); + tb_size_t end_len = tb_strlen(symbol_end); + if (start_len > 8) { + string_table_content_size += (tb_uint32_t)(start_len + 1); + } + if (end_len > 8) { + string_table_content_size += (tb_uint32_t)(end_len + 1); + } + // string table size field should include the size field itself + tb_uint32_t string_table_size = 4 + string_table_content_size; + + // write COFF header + xm_coff_header_t header; + tb_memset(&header, 0, sizeof(header)); + header.machine = machine; + header.nsects = 1; + header.time = 0; + header.symtabofs = symbol_table_ofs; + // note: COFF spec says nsyms is the number of symbol table entries (including aux entries) + // section symbol (1) + aux entry (1) + start symbol (1) + end symbol (1) = 4 entries + // total size: 4 * 18 = 72 bytes + // i386 linker calculates string table as symtabofs + nsyms * 18 = symtabofs + 72 (correct) + // when reading symbols, linker follows naux fields to skip aux entries correctly + // from mingw i386 analysis: section symbols MUST have aux entry (naux=1) + header.nsyms = 4; // 3 symbols + 1 aux entry + header.opthdr = 0; + header.flags = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { + return tb_false; + } + + // write section header (.rdata) + xm_coff_section_t section; + tb_memset(§ion, 0, sizeof(section)); + tb_strncpy(section.name, ".rdata", 8); + section.vsize = datasize; + section.vaddr = 0; + section.size = datasize; + section.ofs = section_data_ofs; + section.relocofs = 0; + section.linenoofs = 0; + section.nreloc = 0; + section.nlineno = 0; + section.flags = XM_COFF_SECTION_RDATA; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { + return tb_false; + } + + // write section data + if (!xm_binutils_stream_copy(istream, ostream, filesize)) { + return tb_false; + } + // append null terminator if zeroend is true + if (zeroend) { + tb_byte_t zero = 0; + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + + // align to 4 bytes + if (section_data_padding > 0) { + xm_binutils_coff_write_padding(ostream, section_data_padding); + } + + // write symbol table + // symbol 0: .rdata section symbol + xm_coff_symbol_t sym_section; + tb_memset(&sym_section, 0, sizeof(sym_section)); + tb_strncpy(sym_section.n.shortname.name, ".rdata", 8); + sym_section.value = 0; + sym_section.sect = 1; // section index (1-based) + sym_section.type = 0; // IMAGE_SYM_TYPE_NULL + sym_section.scl = 3; // IMAGE_SYM_CLASS_STATIC + // section symbol MUST have auxiliary entry for i386 compatibility (as seen in mingw-generated files) + sym_section.naux = 1; // auxiliary entry (required for i386) + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_section, sizeof(sym_section))) { + return tb_false; + } + // auxiliary entry for section (18 bytes total) + xm_coff_aux_section_t aux_section; + tb_memset(&aux_section, 0, sizeof(aux_section)); + aux_section.length = datasize; + aux_section.nreloc = 0; + aux_section.nlineno = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&aux_section, sizeof(aux_section))) { + return tb_false; + } + + // symbol 1: _binary_xxx_start (or __binary_xxx_start for i386) + tb_uint32_t strtab_offset = 4; // start after size field + xm_binutils_coff_write_symbol_name(ostream, symbol_start, &strtab_offset); + xm_coff_symbol_tail_t sym_start_tail; + tb_memset(&sym_start_tail, 0, sizeof(sym_start_tail)); + sym_start_tail.value = 0; + sym_start_tail.sect = 1; + sym_start_tail.type = 0; // IMAGE_SYM_TYPE_NULL + sym_start_tail.scl = 2; // IMAGE_SYM_CLASS_EXTERNAL + sym_start_tail.naux = 0; // no auxiliary entry + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start_tail, sizeof(sym_start_tail))) { + return tb_false; + } + + // symbol 2: _binary_xxx_end (or __binary_xxx_end for i386) + xm_binutils_coff_write_symbol_name(ostream, symbol_end, &strtab_offset); + xm_coff_symbol_tail_t sym_end_tail; + tb_memset(&sym_end_tail, 0, sizeof(sym_end_tail)); + sym_end_tail.value = datasize; + sym_end_tail.sect = 1; + sym_end_tail.type = 0; // IMAGE_SYM_TYPE_NULL + sym_end_tail.scl = 2; // IMAGE_SYM_CLASS_EXTERNAL + sym_end_tail.naux = 0; // no auxiliary entry + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end_tail, sizeof(sym_end_tail))) { + return tb_false; + } + + // write string table + // symbol table size: section symbol (18) + aux entry (18) + start symbol (18) + end symbol (18) = 72 bytes + // string table starts at symtabofs + 72, which matches nsyms * 18 = 4 * 18 = 72 + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&string_table_size, 4)) { + return tb_false; + } + if (start_len > 8) { + xm_binutils_coff_write_string(ostream, symbol_start, start_len); + tb_byte_t null = 0; + tb_stream_bwrit(ostream, &null, 1); + } + if (end_len > 8) { + xm_binutils_coff_write_string(ostream, symbol_end, end_len); + tb_byte_t null = 0; + tb_stream_bwrit(ostream, &null, 1); + } + + return tb_true; +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* generate COFF object file from binary file + * + * @param lua the lua state + * @return 1 on success, 2 on failure (with error message on stack) + */ +tb_int_t xm_binutils_bin2coff(lua_State *lua) { + tb_assert_and_check_return_val(lua, 0); + + // get the binaryfile + tb_char_t const *binaryfile = luaL_checkstring(lua, 1); + tb_check_return_val(binaryfile, 0); + + // get the outputfile + tb_char_t const *outputfile = luaL_checkstring(lua, 2); + tb_check_return_val(outputfile, 0); + + // get symbol prefix (optional) + tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; + + // get arch (optional) + tb_char_t const *arch = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; + + // get basename (optional) + tb_char_t const *basename = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; + + // get zeroend (optional, default: false) + tb_bool_t zeroend = lua_toboolean(lua, 6); + + // do dump + tb_bool_t ok = tb_false; + tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); + tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, + TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); + do { + if (!tb_stream_open(istream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2coff: open %s failed", binaryfile); + break; + } + + if (!tb_stream_open(ostream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2coff: open %s failed", outputfile); + break; + } + + if (!xm_binutils_bin2coff_dump(istream, ostream, symbol_prefix, arch, basename, zeroend)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2coff: dump data failed"); + break; + } + + ok = tb_true; + lua_pushboolean(lua, ok); + + } while (0); + + if (istream) { + tb_stream_clos(istream); + } + istream = tb_null; + + if (ostream) { + tb_stream_clos(ostream); + } + ostream = tb_null; + + return ok ? 1 : 2; +} + diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h new file mode 100644 index 000000000..25ea52168 --- /dev/null +++ b/core/src/xmake/binutils/coff/prefix.h @@ -0,0 +1,175 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file prefix.h + * + */ +#ifndef XM_BINUTILS_COFF_PREFIX_H +#define XM_BINUTILS_COFF_PREFIX_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "../prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ +#define XM_COFF_MACHINE_I386 0x014c +#define XM_COFF_MACHINE_AMD64 0x8664 +#define XM_COFF_MACHINE_ARM 0x01c0 +#define XM_COFF_MACHINE_ARM64 0xaa64 + +#define XM_COFF_SECTION_RDATA 0x40000040 // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ +#include "tbox/prefix/packed.h" +typedef struct __xm_coff_header_t { + tb_uint16_t machine; + tb_uint16_t nsects; + tb_uint32_t time; + tb_uint32_t symtabofs; + tb_uint32_t nsyms; + tb_uint16_t opthdr; + tb_uint16_t flags; +} __tb_packed__ xm_coff_header_t; + +typedef struct __xm_coff_section_t { + tb_char_t name[8]; + tb_uint32_t vsize; + tb_uint32_t vaddr; + tb_uint32_t size; + tb_uint32_t ofs; + tb_uint32_t relocofs; + tb_uint32_t linenoofs; + tb_uint16_t nreloc; + tb_uint16_t nlineno; + tb_uint32_t flags; +} __tb_packed__ xm_coff_section_t; + +typedef struct __xm_coff_symbol_t { + union { + struct { + tb_char_t name[8]; + } shortname; + struct { + tb_uint32_t zeros; + tb_uint32_t offset; + } longname; + } n; + tb_uint32_t value; + tb_int16_t sect; + tb_uint16_t type; + tb_uint8_t scl; + tb_uint8_t naux; +} __tb_packed__ xm_coff_symbol_t; + +typedef struct __xm_coff_aux_section_t { + tb_uint32_t length; + tb_uint16_t nreloc; + tb_uint16_t nlineno; + tb_uint8_t reserved[10]; +} __tb_packed__ xm_coff_aux_section_t; + +typedef struct __xm_coff_symbol_tail_t { + tb_uint32_t value; + tb_int16_t sect; + tb_uint16_t type; + tb_uint8_t scl; + tb_uint8_t naux; +} __tb_packed__ xm_coff_symbol_tail_t; + +/* ////////////////////////////////////////////////////////////////////////////////////// + * inline implementation + */ + +/* get machine type from architecture string + * + * @param arch the architecture string (e.g., "x86_64", "i386", "arm64") + * @return the machine type + */ +static __tb_inline__ tb_uint16_t xm_binutils_coff_get_machine(tb_char_t const *arch) { + if (!arch) { + return XM_COFF_MACHINE_I386; + } + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return XM_COFF_MACHINE_AMD64; + } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { + return XM_COFF_MACHINE_ARM64; + } else if (tb_strcmp(arch, "arm") == 0) { + return XM_COFF_MACHINE_ARM; + } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) { + return XM_COFF_MACHINE_I386; + } + return XM_COFF_MACHINE_I386; +} + +/* write string to stream + * + * @param ostream the output stream + * @param str the string to write + * @param len the length (0 for auto-detect) + */ +static __tb_inline__ tb_void_t xm_binutils_coff_write_string(tb_stream_ref_t ostream, tb_char_t const *str, tb_size_t len) { + tb_assert_and_check_return(ostream && str); + if (len == 0) { + len = tb_strlen(str); + } + tb_stream_bwrit(ostream, (tb_byte_t const *)str, len); +} + +/* write padding bytes to stream + * + * @param ostream the output stream + * @param count the number of padding bytes + */ +static __tb_inline__ tb_void_t xm_binutils_coff_write_padding(tb_stream_ref_t ostream, tb_size_t count) { + tb_assert_and_check_return(ostream); + tb_byte_t zero = 0; + while (count-- > 0) { + tb_stream_bwrit(ostream, &zero, 1); + } +} + +/* write symbol name to stream (handles short and long names) + * + * @param ostream the output stream + * @param name the symbol name + * @param strtab_offset the string table offset (updated if long name) + */ +static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_t ostream, tb_char_t const *name, tb_uint32_t *strtab_offset) { + tb_assert_and_check_return(ostream && name && strtab_offset); + tb_size_t len = tb_strlen(name); + if (len <= 8) { + // short name: store directly in symbol name field + xm_binutils_coff_write_string(ostream, name, len); + if (len < 8) { + xm_binutils_coff_write_padding(ostream, 8 - len); + } + } else { + // long name: store offset in string table + tb_uint32_t zeros = 0; + tb_stream_bwrit(ostream, (tb_byte_t const *)&zeros, 4); + tb_stream_bwrit(ostream, (tb_byte_t const *)strtab_offset, 4); + *strtab_offset += (tb_uint32_t)(len + 1); // +1 for null terminator + } +} + +#endif + diff --git a/core/src/xmake/binutils/elf/bin2elf.c b/core/src/xmake/binutils/elf/bin2elf.c new file mode 100644 index 000000000..e6a9e7998 --- /dev/null +++ b/core/src/xmake/binutils/elf/bin2elf.c @@ -0,0 +1,725 @@ +/*!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 bin2elf.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "bin2elf" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *arch, + tb_char_t const *basename, + tb_bool_t zeroend) { + tb_assert_and_check_return_val(istream && ostream, tb_false); + + // get file size + tb_hong_t filesize = tb_stream_size(istream); + if (filesize < 0 || filesize > 0xffffffffU) { + return tb_false; + } + tb_uint32_t datasize = (tb_uint32_t)filesize; + // add null terminator if zeroend is true + if (zeroend) { + if (datasize >= 0xffffffffU) { + return tb_false; // would overflow + } + datasize++; + } + + // generate symbol names from filename + tb_char_t symbol_name[256] = {0}; + tb_char_t symbol_start[256] = {0}; + tb_char_t symbol_end[256] = {0}; + + // use basename or default to "data" + if (!basename || !basename[0]) { + basename = "data"; + } + + // build symbol name + if (symbol_prefix) { + tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); + } + + // replace non-alphanumeric with underscore + for (tb_size_t i = 0; symbol_name[i]; i++) { + if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { + symbol_name[i] = '_'; + } + } + + tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); + tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); + + // calculate offsets + tb_uint32_t header_size = sizeof(xm_elf32_header_t); + tb_uint32_t section_header_size = sizeof(xm_elf32_section_t); + tb_uint32_t section_count = 6; // NULL, .rodata, .symtab, .strtab, .shstrtab, .note.GNU-stack + tb_uint32_t section_headers_ofs = header_size; + tb_uint32_t rodata_ofs = section_headers_ofs + section_count * section_header_size; + tb_uint32_t rodata_size = datasize; + tb_uint32_t rodata_padding = (4 - (rodata_size & 3)) & 3; // align to 4 bytes for 32-bit + tb_uint32_t symtab_ofs = rodata_ofs + rodata_size + rodata_padding; + tb_uint32_t symtab_size = 3 * sizeof(xm_elf32_symbol_t); // NULL, start, end + tb_uint32_t symtab_padding = (4 - (symtab_size & 3)) & 3; // align to 4 bytes + tb_uint32_t strtab_ofs = symtab_ofs + symtab_size + symtab_padding; + + // calculate string table size + tb_size_t start_len = tb_strlen(symbol_start); + tb_size_t end_len = tb_strlen(symbol_end); + tb_uint32_t strtab_size = 1; // initial null byte + strtab_size += (tb_uint32_t)(start_len + 1); + strtab_size += (tb_uint32_t)(end_len + 1); + tb_uint32_t strtab_padding = (4 - (strtab_size & 3)) & 3; // align to 4 bytes + tb_uint32_t shstrtab_ofs = strtab_ofs + strtab_size + strtab_padding; + + // calculate section header string table size + tb_uint32_t shstrtab_size = 1; // initial null byte + shstrtab_size += 8; // ".rodata\0" (7 + 1) + shstrtab_size += 8; // ".symtab\0" (7 + 1) + shstrtab_size += 8; // ".strtab\0" (7 + 1) + shstrtab_size += 10; // ".shstrtab\0" (9 + 1) + shstrtab_size += 16; // ".note.GNU-stack\0" (15 + 1) + + // write ELF header + xm_elf32_header_t header; + tb_memset(&header, 0, sizeof(header)); + header.e_ident[0] = 0x7f; + header.e_ident[1] = 'E'; + header.e_ident[2] = 'L'; + header.e_ident[3] = 'F'; + header.e_ident[4] = 1; // ELFCLASS32 + header.e_ident[5] = 1; // ELFDATA2LSB + header.e_ident[6] = 1; // EV_CURRENT + header.e_ident[7] = 0; // ELFOSABI_SYSV + header.e_type = 1; // ET_REL + header.e_machine = xm_binutils_elf_get_machine(arch); + header.e_version = 1; + header.e_shoff = section_headers_ofs; + header.e_ehsize = header_size; + header.e_shentsize = section_header_size; + header.e_shnum = section_count; + header.e_shstrndx = 4; // .shstrtab section index + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { + return tb_false; + } + + // write section headers + xm_elf32_section_t section_null; + tb_memset(§ion_null, 0, sizeof(section_null)); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_null, sizeof(section_null))) { + return tb_false; + } + + // write .rodata section header + xm_elf32_section_t section_rodata; + tb_memset(§ion_rodata, 0, sizeof(section_rodata)); + section_rodata.sh_name = 1; // ".rodata" in shstrtab + section_rodata.sh_type = XM_ELF_SHT_PROGBITS; + section_rodata.sh_flags = XM_ELF_SHF_ALLOC; + section_rodata.sh_offset = rodata_ofs; + section_rodata.sh_size = rodata_size; + section_rodata.sh_addralign = 4; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_rodata, sizeof(section_rodata))) { + return tb_false; + } + + // write .symtab section header + xm_elf32_section_t section_symtab; + tb_memset(§ion_symtab, 0, sizeof(section_symtab)); + section_symtab.sh_name = 9; // ".symtab" in shstrtab + section_symtab.sh_type = XM_ELF_SHT_SYMTAB; + section_symtab.sh_offset = symtab_ofs; + section_symtab.sh_size = symtab_size; + section_symtab.sh_link = 3; // .strtab section index + section_symtab.sh_info = 1; // first global symbol index + section_symtab.sh_addralign = 4; + section_symtab.sh_entsize = sizeof(xm_elf32_symbol_t); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_symtab, sizeof(section_symtab))) { + return tb_false; + } + + // write .strtab section header + xm_elf32_section_t section_strtab; + tb_memset(§ion_strtab, 0, sizeof(section_strtab)); + section_strtab.sh_name = 17; // ".strtab" in shstrtab + section_strtab.sh_type = XM_ELF_SHT_STRTAB; + section_strtab.sh_offset = strtab_ofs; + section_strtab.sh_size = strtab_size; + section_strtab.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_strtab, sizeof(section_strtab))) { + return tb_false; + } + + // write .shstrtab section header + xm_elf32_section_t section_shstrtab; + tb_memset(§ion_shstrtab, 0, sizeof(section_shstrtab)); + section_shstrtab.sh_name = 25; // ".shstrtab" in shstrtab + section_shstrtab.sh_type = XM_ELF_SHT_STRTAB; + section_shstrtab.sh_offset = shstrtab_ofs; + section_shstrtab.sh_size = shstrtab_size; + section_shstrtab.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_shstrtab, sizeof(section_shstrtab))) { + return tb_false; + } + + // write .note.GNU-stack section header (empty section to mark stack as non-executable) + xm_elf32_section_t section_note_gnu_stack; + tb_memset(§ion_note_gnu_stack, 0, sizeof(section_note_gnu_stack)); + section_note_gnu_stack.sh_name = 35; // ".note.GNU-stack" in shstrtab (25 + 10) + section_note_gnu_stack.sh_type = XM_ELF_SHT_PROGBITS; + section_note_gnu_stack.sh_flags = 0; // no flags + section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab + section_note_gnu_stack.sh_size = 0; // empty section + section_note_gnu_stack.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_note_gnu_stack, sizeof(section_note_gnu_stack))) { + return tb_false; + } + + // write .rodata section data + if (!xm_binutils_stream_copy(istream, ostream, filesize)) { + return tb_false; + } + // append null terminator if zeroend is true + if (zeroend) { + tb_byte_t zero = 0; + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + + // align .rodata to 4 bytes + if (rodata_padding > 0) { + tb_byte_t zero = 0; + while (rodata_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write symbol table + // symbol 0: NULL symbol + xm_elf32_symbol_t sym_null; + tb_memset(&sym_null, 0, sizeof(sym_null)); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) { + return tb_false; + } + + // symbol 1: _binary_xxx_start + xm_elf32_symbol_t sym_start; + tb_memset(&sym_start, 0, sizeof(sym_start)); + sym_start.st_name = 1; // offset in .strtab (after initial null) + sym_start.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; + sym_start.st_shndx = 1; // .rodata section index + sym_start.st_value = 0; + sym_start.st_size = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) { + return tb_false; + } + + // symbol 2: _binary_xxx_end + xm_elf32_symbol_t sym_end; + tb_memset(&sym_end, 0, sizeof(sym_end)); + sym_end.st_name = 1 + (tb_uint32_t)(start_len + 1); // offset in .strtab + sym_end.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; + sym_end.st_shndx = 1; // .rodata section index + sym_end.st_value = rodata_size; + sym_end.st_size = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) { + return tb_false; + } + + // align .symtab to 4 bytes + if (symtab_padding > 0) { + tb_byte_t zero = 0; + while (symtab_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write string table + tb_byte_t null = 0; + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + + // align .strtab to 4 bytes + if (strtab_padding > 0) { + tb_byte_t zero = 0; + while (strtab_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write section header string table + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".rodata", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".symtab", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".strtab", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".shstrtab", 9)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".note.GNU-stack", 15)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + + return tb_true; +} + +static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *arch, + tb_char_t const *basename, + tb_bool_t zeroend) { + tb_assert_and_check_return_val(istream && ostream, tb_false); + + // get file size + tb_hong_t filesize = tb_stream_size(istream); + if (filesize < 0 || filesize > 0xffffffffU) { + return tb_false; + } + tb_uint32_t datasize = (tb_uint32_t)filesize; + // add null terminator if zeroend is true + if (zeroend) { + if (datasize >= 0xffffffffU) { + return tb_false; // would overflow + } + datasize++; + } + + // generate symbol names from filename + tb_char_t symbol_name[256] = {0}; + tb_char_t symbol_start[256] = {0}; + tb_char_t symbol_end[256] = {0}; + + // use basename or default to "data" + if (!basename || !basename[0]) { + basename = "data"; + } + + // build symbol name + if (symbol_prefix) { + tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename); + } + + // replace non-alphanumeric with underscore + for (tb_size_t i = 0; symbol_name[i]; i++) { + if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { + symbol_name[i] = '_'; + } + } + + tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); + tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); + + // calculate offsets + tb_uint32_t header_size = sizeof(xm_elf64_header_t); + tb_uint32_t section_header_size = sizeof(xm_elf64_section_t); + tb_uint32_t section_count = 6; // NULL, .rodata, .symtab, .strtab, .shstrtab, .note.GNU-stack + tb_uint32_t section_headers_ofs = header_size; + tb_uint32_t rodata_ofs = section_headers_ofs + section_count * section_header_size; + tb_uint32_t rodata_size = datasize; + tb_uint32_t rodata_padding = (8 - (rodata_size & 7)) & 7; + tb_uint32_t symtab_ofs = rodata_ofs + rodata_size + rodata_padding; + tb_uint32_t symtab_size = 3 * sizeof(xm_elf64_symbol_t); // NULL, start, end + tb_uint32_t symtab_padding = (8 - (symtab_size & 7)) & 7; + tb_uint32_t strtab_ofs = symtab_ofs + symtab_size + symtab_padding; + + // calculate string table size + tb_size_t start_len = tb_strlen(symbol_start); + tb_size_t end_len = tb_strlen(symbol_end); + tb_uint32_t strtab_size = 1; // initial null byte + strtab_size += (tb_uint32_t)(start_len + 1); + strtab_size += (tb_uint32_t)(end_len + 1); + tb_uint32_t strtab_padding = (8 - (strtab_size & 7)) & 7; + + // calculate section header string table size + tb_uint32_t shstrtab_size = 1; // initial null byte + shstrtab_size += 8; // ".rodata\0" (7 + 1) + shstrtab_size += 8; // ".symtab\0" (7 + 1) + shstrtab_size += 8; // ".strtab\0" (7 + 1) + shstrtab_size += 10; // ".shstrtab\0" (9 + 1) + shstrtab_size += 16; // ".note.GNU-stack\0" (15 + 1) + tb_uint32_t shstrtab_ofs = strtab_ofs + strtab_size + strtab_padding; + + // write ELF header + xm_elf64_header_t header; + tb_memset(&header, 0, sizeof(header)); + header.e_ident[0] = 0x7f; + header.e_ident[1] = 'E'; + header.e_ident[2] = 'L'; + header.e_ident[3] = 'F'; + header.e_ident[4] = 2; // ELFCLASS64 + header.e_ident[5] = 1; // ELFDATA2LSB + header.e_ident[6] = 1; // EV_CURRENT + header.e_ident[7] = 0; // ELFOSABI_SYSV + header.e_type = 1; // ET_REL + header.e_machine = xm_binutils_elf_get_machine(arch); + header.e_version = 1; + header.e_shoff = section_headers_ofs; + header.e_ehsize = header_size; + header.e_shentsize = section_header_size; + header.e_shnum = section_count; + header.e_shstrndx = 4; // .shstrtab section index + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { + return tb_false; + } + + // write section headers + xm_elf64_section_t section_null; + tb_memset(§ion_null, 0, sizeof(section_null)); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_null, sizeof(section_null))) { + return tb_false; + } + + // write .rodata section header + xm_elf64_section_t section_rodata; + tb_memset(§ion_rodata, 0, sizeof(section_rodata)); + section_rodata.sh_name = 1; // ".rodata" in shstrtab + section_rodata.sh_type = XM_ELF_SHT_PROGBITS; + section_rodata.sh_flags = XM_ELF_SHF_ALLOC; + section_rodata.sh_offset = rodata_ofs; + section_rodata.sh_size = rodata_size; + section_rodata.sh_addralign = 8; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_rodata, sizeof(section_rodata))) { + return tb_false; + } + + // write .symtab section header + xm_elf64_section_t section_symtab; + tb_memset(§ion_symtab, 0, sizeof(section_symtab)); + section_symtab.sh_name = 9; // ".symtab" in shstrtab + section_symtab.sh_type = XM_ELF_SHT_SYMTAB; + section_symtab.sh_offset = symtab_ofs; + section_symtab.sh_size = symtab_size; + section_symtab.sh_link = 3; // .strtab section index + section_symtab.sh_info = 1; // first global symbol index + section_symtab.sh_addralign = 8; + section_symtab.sh_entsize = sizeof(xm_elf64_symbol_t); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_symtab, sizeof(section_symtab))) { + return tb_false; + } + + // write .strtab section header + xm_elf64_section_t section_strtab; + tb_memset(§ion_strtab, 0, sizeof(section_strtab)); + section_strtab.sh_name = 17; // ".strtab" in shstrtab + section_strtab.sh_type = XM_ELF_SHT_STRTAB; + section_strtab.sh_offset = strtab_ofs; + section_strtab.sh_size = strtab_size; + section_strtab.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_strtab, sizeof(section_strtab))) { + return tb_false; + } + + // write .shstrtab section header + xm_elf64_section_t section_shstrtab; + tb_memset(§ion_shstrtab, 0, sizeof(section_shstrtab)); + section_shstrtab.sh_name = 25; // ".shstrtab" in shstrtab + section_shstrtab.sh_type = XM_ELF_SHT_STRTAB; + section_shstrtab.sh_offset = shstrtab_ofs; // points to initial null byte + section_shstrtab.sh_size = shstrtab_size; // size includes initial null and all strings + section_shstrtab.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_shstrtab, sizeof(section_shstrtab))) { + return tb_false; + } + + // write .note.GNU-stack section header (empty section to mark stack as non-executable) + xm_elf64_section_t section_note_gnu_stack; + tb_memset(§ion_note_gnu_stack, 0, sizeof(section_note_gnu_stack)); + section_note_gnu_stack.sh_name = 35; // ".note.GNU-stack" in shstrtab (25 + 10) + section_note_gnu_stack.sh_type = XM_ELF_SHT_PROGBITS; + section_note_gnu_stack.sh_flags = 0; // no flags + section_note_gnu_stack.sh_offset = shstrtab_ofs + shstrtab_size; // after .shstrtab + section_note_gnu_stack.sh_size = 0; // empty section + section_note_gnu_stack.sh_addralign = 1; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion_note_gnu_stack, sizeof(section_note_gnu_stack))) { + return tb_false; + } + + // write .rodata section data + if (!xm_binutils_stream_copy(istream, ostream, filesize)) { + return tb_false; + } + // append null terminator if zeroend is true + if (zeroend) { + tb_byte_t zero = 0; + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + + // align .rodata to 8 bytes + if (rodata_padding > 0) { + tb_byte_t zero = 0; + while (rodata_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write symbol table + // symbol 0: NULL symbol + xm_elf64_symbol_t sym_null; + tb_memset(&sym_null, 0, sizeof(sym_null)); + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) { + return tb_false; + } + + // symbol 1: _binary_xxx_start + xm_elf64_symbol_t sym_start; + tb_memset(&sym_start, 0, sizeof(sym_start)); + sym_start.st_name = 1; // offset in .strtab (after initial null) + sym_start.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; + sym_start.st_shndx = 1; // .rodata section index + sym_start.st_value = 0; + sym_start.st_size = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) { + return tb_false; + } + + // symbol 2: _binary_xxx_end + xm_elf64_symbol_t sym_end; + tb_memset(&sym_end, 0, sizeof(sym_end)); + sym_end.st_name = 1 + (tb_uint32_t)(start_len + 1); // offset in .strtab + sym_end.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT; + sym_end.st_shndx = 1; // .rodata section index + sym_end.st_value = rodata_size; + sym_end.st_size = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) { + return tb_false; + } + + // align .symtab to 8 bytes + if (symtab_padding > 0) { + tb_byte_t zero = 0; + while (symtab_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write string table + tb_byte_t null = 0; + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + + // align .strtab to 8 bytes + if (strtab_padding > 0) { + tb_byte_t zero = 0; + while (strtab_padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write section header string table + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".rodata", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".symtab", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".strtab", 7)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".shstrtab", 9)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".note.GNU-stack", 15)) { + return tb_false; + } + if (!tb_stream_bwrit(ostream, &null, 1)) { + return tb_false; + } + + return tb_true; +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* generate ELF object file from binary file + * + * local ok, errors = binutils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend) + */ +tb_int_t xm_binutils_bin2elf(lua_State *lua) { + tb_assert_and_check_return_val(lua, 0); + + // get the binaryfile + tb_char_t const *binaryfile = luaL_checkstring(lua, 1); + tb_check_return_val(binaryfile, 0); + + // get the outputfile + tb_char_t const *outputfile = luaL_checkstring(lua, 2); + tb_check_return_val(outputfile, 0); + + // get symbol prefix (optional) + tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; + + // get arch (optional) + tb_char_t const *arch = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; + + // get basename (optional) + tb_char_t const *basename = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; + + // get zeroend (optional, default: false) + tb_bool_t zeroend = lua_toboolean(lua, 6); + + // do dump + tb_bool_t ok = tb_false; + tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); + tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, + TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); + do { + if (!tb_stream_open(istream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2elf: open %s failed", binaryfile); + break; + } + + if (!tb_stream_open(ostream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2elf: open %s failed", outputfile); + break; + } + + // choose 32-bit or 64-bit ELF based on architecture + tb_bool_t is_64bit = xm_binutils_elf_is_64bit(arch); + if (is_64bit) { + if (!xm_binutils_bin2elf_dump_64(istream, ostream, symbol_prefix, arch, basename, zeroend)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2elf: dump data failed"); + break; + } + } else { + if (!xm_binutils_bin2elf_dump_32(istream, ostream, symbol_prefix, arch, basename, zeroend)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2elf: dump data failed"); + break; + } + } + + ok = tb_true; + lua_pushboolean(lua, ok); + + } while (0); + + if (istream) + tb_stream_clos(istream); + istream = tb_null; + + if (ostream) + tb_stream_clos(ostream); + ostream = tb_null; + + return ok ? 1 : 2; +} + diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h new file mode 100644 index 000000000..d1a13afb8 --- /dev/null +++ b/core/src/xmake/binutils/elf/prefix.h @@ -0,0 +1,270 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file prefix.h + * + */ +#ifndef XM_BINUTILS_ELF_PREFIX_H +#define XM_BINUTILS_ELF_PREFIX_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "../prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ +#define XM_ELF_MACHINE_NONE 0x00 +#define XM_ELF_MACHINE_SPARC 0x02 +#define XM_ELF_MACHINE_I386 0x03 +#define XM_ELF_MACHINE_MIPS 0x08 +#define XM_ELF_MACHINE_POWERPC 0x14 +#define XM_ELF_MACHINE_POWERPC64 0x15 +#define XM_ELF_MACHINE_S390 0x16 +#define XM_ELF_MACHINE_ARM 0x28 +#define XM_ELF_MACHINE_SUPERH 0x2a +#define XM_ELF_MACHINE_SPARC64 0x2b +#define XM_ELF_MACHINE_IA_64 0x32 +#define XM_ELF_MACHINE_X86_64 0x3e +#define XM_ELF_MACHINE_RISCV 0xf3 +#define XM_ELF_MACHINE_ARM64 0xb7 +#define XM_ELF_MACHINE_WASM 0xe7 +#define XM_ELF_MACHINE_LOONGARCH 0x102 + +#define XM_ELF_SHT_PROGBITS 0x1 +#define XM_ELF_SHT_SYMTAB 0x2 +#define XM_ELF_SHT_STRTAB 0x3 + +#define XM_ELF_SHF_ALLOC 0x2 +#define XM_ELF_SHF_WRITE 0x1 + +#define XM_ELF_STB_GLOBAL 0x1 +#define XM_ELF_STT_OBJECT 0x1 + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ +#include "tbox/prefix/packed.h" +typedef struct __xm_elf32_header_t { + tb_uint8_t e_ident[16]; + tb_uint16_t e_type; + tb_uint16_t e_machine; + tb_uint32_t e_version; + tb_uint32_t e_entry; + tb_uint32_t e_phoff; + tb_uint32_t e_shoff; + tb_uint32_t e_flags; + tb_uint16_t e_ehsize; + tb_uint16_t e_phentsize; + tb_uint16_t e_phnum; + tb_uint16_t e_shentsize; + tb_uint16_t e_shnum; + tb_uint16_t e_shstrndx; +} __tb_packed__ xm_elf32_header_t; + +typedef struct __xm_elf32_section_t { + tb_uint32_t sh_name; + tb_uint32_t sh_type; + tb_uint32_t sh_flags; + tb_uint32_t sh_addr; + tb_uint32_t sh_offset; + tb_uint32_t sh_size; + tb_uint32_t sh_link; + tb_uint32_t sh_info; + tb_uint32_t sh_addralign; + tb_uint32_t sh_entsize; +} __tb_packed__ xm_elf32_section_t; + +typedef struct __xm_elf32_symbol_t { + tb_uint32_t st_name; + tb_uint32_t st_value; + tb_uint32_t st_size; + tb_uint8_t st_info; + tb_uint8_t st_other; + tb_uint16_t st_shndx; +} __tb_packed__ xm_elf32_symbol_t; + +typedef struct __xm_elf64_header_t { + tb_uint8_t e_ident[16]; + tb_uint16_t e_type; + tb_uint16_t e_machine; + tb_uint32_t e_version; + tb_uint64_t e_entry; + tb_uint64_t e_phoff; + tb_uint64_t e_shoff; + tb_uint32_t e_flags; + tb_uint16_t e_ehsize; + tb_uint16_t e_phentsize; + tb_uint16_t e_phnum; + tb_uint16_t e_shentsize; + tb_uint16_t e_shnum; + tb_uint16_t e_shstrndx; +} __tb_packed__ xm_elf64_header_t; + +typedef struct __xm_elf64_section_t { + tb_uint32_t sh_name; + tb_uint32_t sh_type; + tb_uint64_t sh_flags; + tb_uint64_t sh_addr; + tb_uint64_t sh_offset; + tb_uint64_t sh_size; + tb_uint32_t sh_link; + tb_uint32_t sh_info; + tb_uint64_t sh_addralign; + tb_uint64_t sh_entsize; +} __tb_packed__ xm_elf64_section_t; + +typedef struct __xm_elf64_symbol_t { + tb_uint32_t st_name; + tb_uint8_t st_info; + tb_uint8_t st_other; + tb_uint16_t st_shndx; + tb_uint64_t st_value; + tb_uint64_t st_size; +} __tb_packed__ xm_elf64_symbol_t; + +/* ////////////////////////////////////////////////////////////////////////////////////// + * inline implementation + */ + +/* get machine type from architecture string + * + * @param arch the architecture string (e.g., "x86_64", "i386", "arm64", "riscv") + * @return the machine type + */ +static __tb_inline__ tb_uint16_t xm_binutils_elf_get_machine(tb_char_t const *arch) { + if (!arch) { + return XM_ELF_MACHINE_X86_64; + } + // x86/x86_64 + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return XM_ELF_MACHINE_X86_64; + } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) { + return XM_ELF_MACHINE_I386; + } + // ARM + else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 || + tb_strcmp(arch, "arm64-v8a") == 0) { + return XM_ELF_MACHINE_ARM64; + } else if (tb_strcmp(arch, "arm") == 0 || tb_strcmp(arch, "armv7") == 0 || + tb_strcmp(arch, "armeabi-v7a") == 0 || tb_strcmp(arch, "armv6") == 0 || + tb_strcmp(arch, "armv5") == 0) { + return XM_ELF_MACHINE_ARM; + } + // MIPS (MIPS and MIPS64 use same machine type, distinguished by ELF class) + else if (tb_strncmp(arch, "mips", 4) == 0) { + return XM_ELF_MACHINE_MIPS; + } + // PowerPC + else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) { + return XM_ELF_MACHINE_POWERPC64; + } else if (tb_strncmp(arch, "ppc", 3) == 0 || tb_strncmp(arch, "powerpc", 7) == 0) { + return XM_ELF_MACHINE_POWERPC; + } + // RISC-V (RISC-V and RISC-V64 use different machine types) + else if (tb_strncmp(arch, "riscv64", 7) == 0 || + (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) { + return XM_ELF_MACHINE_RISCV; // RISC-V 64-bit uses same machine type, distinguished by ELF class + } else if (tb_strncmp(arch, "riscv", 5) == 0) { + return XM_ELF_MACHINE_RISCV; + } + // SPARC + else if (tb_strncmp(arch, "sparc64", 7) == 0) { + return XM_ELF_MACHINE_SPARC64; + } else if (tb_strncmp(arch, "sparc", 5) == 0) { + return XM_ELF_MACHINE_SPARC; + } + // s390x + else if (tb_strcmp(arch, "s390x") == 0 || tb_strcmp(arch, "s390") == 0) { + return XM_ELF_MACHINE_S390; + } + // LoongArch (LoongArch and LoongArch64 use same machine type, distinguished by ELF class) + else if (tb_strncmp(arch, "loongarch", 9) == 0 || tb_strncmp(arch, "loong64", 7) == 0) { + return XM_ELF_MACHINE_LOONGARCH; + } + // WebAssembly (WASM and WASM64 use same machine type, distinguished by ELF class) + else if (tb_strncmp(arch, "wasm", 4) == 0) { + return XM_ELF_MACHINE_WASM; + } + // SuperH + else if (tb_strncmp(arch, "sh", 2) == 0 || tb_strncmp(arch, "superh", 6) == 0) { + return XM_ELF_MACHINE_SUPERH; + } + // IA-64 (Itanium) + else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) { + return XM_ELF_MACHINE_IA_64; + } + return XM_ELF_MACHINE_X86_64; +} + +/* 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_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; +} + +#endif + diff --git a/core/src/xmake/binutils/macho/bin2macho.c b/core/src/xmake/binutils/macho/bin2macho.c new file mode 100644 index 000000000..a9068625a --- /dev/null +++ b/core/src/xmake/binutils/macho/bin2macho.c @@ -0,0 +1,628 @@ +/*!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 bin2macho.c + * + */ + +/* ////////////////////////////////////////////////////////////////////////////////////// + * trace + */ +#define TB_TRACE_MODULE_NAME "bin2macho" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * private implementation + */ + +static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *plat, + tb_char_t const *arch, + tb_char_t const *basename, + tb_uint32_t minos, + tb_uint32_t sdk, + tb_bool_t zeroend) { + tb_assert_and_check_return_val(istream && ostream, tb_false); + + // get file size + tb_hong_t filesize = tb_stream_size(istream); + if (filesize < 0 || filesize > 0xffffffffU) { + return tb_false; + } + tb_uint32_t datasize = (tb_uint32_t)filesize; + // add null terminator if zeroend is true + if (zeroend) { + if (datasize >= 0xffffffffU) { + return tb_false; // would overflow + } + datasize++; + } + + // generate symbol names from filename + tb_char_t symbol_name[256] = {0}; + tb_char_t symbol_start[256] = {0}; + tb_char_t symbol_end[256] = {0}; + + // use basename or default to "data" + if (!basename || !basename[0]) { + basename = "data"; + } + + // build symbol name + // On macOS, C compiler adds an underscore prefix, so we generate symbols with two underscores + // (C code declares _binary_xxx, compiler generates __binary_xxx in object file, so we define __binary_xxx) + if (symbol_prefix) { + tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); + } + + // replace non-alphanumeric with underscore + for (tb_size_t i = 0; symbol_name[i]; i++) { + if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { + symbol_name[i] = '_'; + } + } + + tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); + tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); + + // calculate offsets + tb_uint32_t header_size = sizeof(xm_macho_header_64_t); + tb_uint32_t segment_cmd_size = sizeof(xm_macho_segment_command_64_t); + tb_uint32_t section_size = sizeof(xm_macho_section_64_t); + tb_uint32_t symtab_cmd_size = sizeof(xm_macho_symtab_command_t); + tb_uint32_t build_version_cmd_size = sizeof(xm_macho_build_version_command_t); + tb_uint32_t segment_cmd_total_size = segment_cmd_size + section_size; + tb_uint32_t data_offset = xm_binutils_macho_align(header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size, 8); + tb_uint32_t data_size = datasize; + tb_uint32_t data_end_offset = data_offset + data_size; + tb_uint32_t symtab_offset = xm_binutils_macho_align(data_end_offset, 8); + tb_uint32_t nlist_size = sizeof(xm_macho_nlist_64_t); + tb_uint32_t nlist_count = 2; // start, end + tb_uint32_t strtab_offset = symtab_offset + nlist_size * nlist_count; + tb_uint32_t strtab_size = 4; // initial 4-byte size field + tb_size_t start_len = tb_strlen(symbol_start); + tb_size_t end_len = tb_strlen(symbol_end); + strtab_size += (tb_uint32_t)(start_len + 1); + strtab_size += (tb_uint32_t)(end_len + 1); + strtab_size = xm_binutils_macho_align(strtab_size, 8); + + // write Mach-O header + xm_macho_header_64_t header; + tb_memset(&header, 0, sizeof(header)); + header.magic = XM_MACHO_MAGIC_64; + header.cputype = xm_binutils_macho_get_cputype(arch); + header.cpusubtype = xm_binutils_macho_get_cpusubtype(arch); + header.filetype = XM_MACHO_FILE_TYPE_OBJECT; + header.ncmds = 3; // segment + symtab + build_version + header.sizeofcmds = segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size; + header.flags = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { + return tb_false; + } + + // write segment command + xm_macho_segment_command_64_t segment; + tb_memset(&segment, 0, sizeof(segment)); + segment.cmd = XM_MACHO_LC_SEGMENT_64; + segment.cmdsize = segment_cmd_total_size; + tb_strncpy(segment.segname, "__TEXT", 16); + segment.vmaddr = 0; + segment.vmsize = data_size; + segment.fileoff = data_offset; + segment.filesize = data_size; + segment.maxprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) + segment.initprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) + segment.nsects = 1; + segment.flags = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&segment, sizeof(segment))) { + return tb_false; + } + + // write section + xm_macho_section_64_t section; + tb_memset(§ion, 0, sizeof(section)); + tb_strncpy(section.sectname, "__const", 16); + tb_strncpy(section.segname, "__TEXT", 16); + section.addr = 0; + section.size = data_size; + section.offset = data_offset; + section.align = 3; // 2^3 = 8 bytes + section.reloff = 0; + section.nreloc = 0; + section.flags = XM_MACHO_SECT_TYPE_REGULAR | XM_MACHO_SECT_ATTR_SOME_INITS; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { + return tb_false; + } + + // write symtab command + xm_macho_symtab_command_t symtab; + tb_memset(&symtab, 0, sizeof(symtab)); + symtab.cmd = XM_MACHO_LC_SYMTAB; + symtab.cmdsize = symtab_cmd_size; + symtab.symoff = symtab_offset; + symtab.nsyms = nlist_count; + symtab.stroff = strtab_offset; + symtab.strsize = strtab_size; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&symtab, sizeof(symtab))) { + return tb_false; + } + + // write build version command + xm_macho_build_version_command_t build_version; + tb_memset(&build_version, 0, sizeof(build_version)); + build_version.cmd = XM_MACHO_LC_BUILD_VERSION; + build_version.cmdsize = build_version_cmd_size; + build_version.platform = xm_binutils_macho_get_platform(plat); + build_version.minos = minos; + build_version.sdk = sdk; + build_version.ntools = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&build_version, sizeof(build_version))) { + return tb_false; + } + + // align to 8 bytes + tb_uint32_t padding = data_offset - (header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write section data + if (!xm_binutils_stream_copy(istream, ostream, filesize)) { + return tb_false; + } + // append null terminator if zeroend is true + if (zeroend) { + tb_byte_t zero = 0; + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + + // align to 8 bytes + padding = symtab_offset - data_end_offset; + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write symbol table + // strx starts from 4 (after 4-byte size field) + tb_uint32_t strx = 4; + // symbol 0: _binary_xxx_start + xm_macho_nlist_64_t nlist_start; + tb_memset(&nlist_start, 0, sizeof(nlist_start)); + nlist_start.strx = strx; + nlist_start.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT + nlist_start.sect = 1; + nlist_start.desc = 0; + nlist_start.value = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_start, sizeof(nlist_start))) { + return tb_false; + } + strx += (tb_uint32_t)(start_len + 1); + + // symbol 1: _binary_xxx_end + xm_macho_nlist_64_t nlist_end; + tb_memset(&nlist_end, 0, sizeof(nlist_end)); + nlist_end.strx = strx; + nlist_end.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT + nlist_end.sect = 1; + nlist_end.desc = 0; + nlist_end.value = data_size; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_end, sizeof(nlist_end))) { + return tb_false; + } + strx += (tb_uint32_t)(end_len + 1); + + // align to 8 bytes + padding = strtab_offset - (symtab_offset + nlist_size * nlist_count); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write string table + tb_stream_bwrit(ostream, (tb_byte_t const *)&strtab_size, 4); + tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len); + tb_byte_t null = 0; + tb_stream_bwrit(ostream, &null, 1); + tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len); + tb_stream_bwrit(ostream, &null, 1); + + // align string table to 8 bytes + padding = strtab_size - (4 + (tb_uint32_t)start_len + 1 + (tb_uint32_t)end_len + 1); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + return tb_true; +} + +static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *plat, + tb_char_t const *arch, + tb_char_t const *basename, + tb_uint32_t minos, + tb_uint32_t sdk, + tb_bool_t zeroend) { + tb_assert_and_check_return_val(istream && ostream, tb_false); + + // get file size + tb_hong_t filesize = tb_stream_size(istream); + if (filesize < 0 || filesize > 0xffffffffU) { + return tb_false; + } + tb_uint32_t datasize = (tb_uint32_t)filesize; + // add null terminator if zeroend is true + if (zeroend) { + if (datasize >= 0xffffffffU) { + return tb_false; // would overflow + } + datasize++; + } + + // generate symbol names from filename + tb_char_t symbol_name[256] = {0}; + tb_char_t symbol_start[256] = {0}; + tb_char_t symbol_end[256] = {0}; + + // use basename or default to "data" + if (!basename || !basename[0]) { + basename = "data"; + } + + // build symbol name + // On macOS, C compiler adds an underscore prefix, so we generate symbols with two underscores + // (C code declares _binary_xxx, compiler generates __binary_xxx in object file, so we define __binary_xxx) + if (symbol_prefix) { + tb_snprintf(symbol_name, sizeof(symbol_name), "_%s%s", symbol_prefix, basename); + } else { + tb_snprintf(symbol_name, sizeof(symbol_name), "__binary_%s", basename); + } + + // replace non-alphanumeric with underscore + for (tb_size_t i = 0; symbol_name[i]; i++) { + if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') { + symbol_name[i] = '_'; + } + } + + tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name); + tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name); + + // calculate offsets + tb_uint32_t header_size = sizeof(xm_macho_header_t); + tb_uint32_t segment_cmd_size = sizeof(xm_macho_segment_command_t); + tb_uint32_t section_size = sizeof(xm_macho_section_t); + tb_uint32_t symtab_cmd_size = sizeof(xm_macho_symtab_command_t); + tb_uint32_t build_version_cmd_size = sizeof(xm_macho_build_version_command_t); + tb_uint32_t segment_cmd_total_size = segment_cmd_size + section_size; + tb_uint32_t data_offset = xm_binutils_macho_align(header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size, 4); + tb_uint32_t data_size = datasize; + tb_uint32_t data_end_offset = data_offset + data_size; + tb_uint32_t symtab_offset = xm_binutils_macho_align(data_end_offset, 4); + tb_uint32_t nlist_size = sizeof(xm_macho_nlist_t); + tb_uint32_t nlist_count = 2; // start, end + tb_uint32_t strtab_offset = symtab_offset + nlist_size * nlist_count; + tb_uint32_t strtab_size = 4; // initial 4-byte size field + tb_size_t start_len = tb_strlen(symbol_start); + tb_size_t end_len = tb_strlen(symbol_end); + strtab_size += (tb_uint32_t)(start_len + 1); + strtab_size += (tb_uint32_t)(end_len + 1); + strtab_size = xm_binutils_macho_align(strtab_size, 4); + + // write Mach-O header + xm_macho_header_t header; + tb_memset(&header, 0, sizeof(header)); + header.magic = XM_MACHO_MAGIC_32; + header.cputype = xm_binutils_macho_get_cputype(arch); + header.cpusubtype = xm_binutils_macho_get_cpusubtype(arch); + header.filetype = XM_MACHO_FILE_TYPE_OBJECT; + header.ncmds = 3; // segment + symtab + build_version + header.sizeofcmds = segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size; + header.flags = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) { + return tb_false; + } + + // write segment command + xm_macho_segment_command_t segment; + tb_memset(&segment, 0, sizeof(segment)); + segment.cmd = XM_MACHO_LC_SEGMENT; + segment.cmdsize = segment_cmd_total_size; + tb_strncpy(segment.segname, "__TEXT", 16); + segment.vmaddr = 0; + segment.vmsize = data_size; + segment.fileoff = data_offset; + segment.filesize = data_size; + segment.maxprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) + segment.initprot = XM_MACHO_VM_PROT_READ | XM_MACHO_VM_PROT_EXECUTE; // VM_PROT_READ | VM_PROT_EXECUTE (r-x) + segment.nsects = 1; + segment.flags = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&segment, sizeof(segment))) { + return tb_false; + } + + // write section + xm_macho_section_t section; + tb_memset(§ion, 0, sizeof(section)); + tb_strncpy(section.sectname, "__const", 16); + tb_strncpy(section.segname, "__TEXT", 16); + section.addr = 0; + section.size = data_size; + section.offset = data_offset; + section.align = 2; // 2^2 = 4 bytes + section.reloff = 0; + section.nreloc = 0; + section.flags = XM_MACHO_SECT_TYPE_REGULAR | XM_MACHO_SECT_ATTR_SOME_INITS; + section.reserved1 = 0; + section.reserved2 = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)§ion, sizeof(section))) { + return tb_false; + } + + // write symtab command + xm_macho_symtab_command_t symtab; + tb_memset(&symtab, 0, sizeof(symtab)); + symtab.cmd = XM_MACHO_LC_SYMTAB; + symtab.cmdsize = symtab_cmd_size; + symtab.symoff = symtab_offset; + symtab.nsyms = nlist_count; + symtab.stroff = strtab_offset; + symtab.strsize = strtab_size; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&symtab, sizeof(symtab))) { + return tb_false; + } + + // write build version command + xm_macho_build_version_command_t build_version; + tb_memset(&build_version, 0, sizeof(build_version)); + build_version.cmd = XM_MACHO_LC_BUILD_VERSION; + build_version.cmdsize = build_version_cmd_size; + build_version.platform = xm_binutils_macho_get_platform(plat); + build_version.minos = minos; + build_version.sdk = sdk; + build_version.ntools = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&build_version, sizeof(build_version))) { + return tb_false; + } + + // align to 4 bytes + tb_uint32_t padding = data_offset - (header_size + segment_cmd_total_size + symtab_cmd_size + build_version_cmd_size); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write section data + if (!xm_binutils_stream_copy(istream, ostream, filesize)) { + return tb_false; + } + // append null terminator if zeroend is true + if (zeroend) { + tb_byte_t zero = 0; + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + + // align to 4 bytes + padding = symtab_offset - data_end_offset; + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write symbol table + // strx starts from 4 (after 4-byte size field) + tb_uint32_t strx = 4; + // symbol 0: _binary_xxx_start + xm_macho_nlist_t nlist_start; + tb_memset(&nlist_start, 0, sizeof(nlist_start)); + nlist_start.strx = strx; + nlist_start.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT + nlist_start.sect = 1; + nlist_start.desc = 0; + nlist_start.value = 0; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_start, sizeof(nlist_start))) { + return tb_false; + } + strx += (tb_uint32_t)(start_len + 1); + + // symbol 1: _binary_xxx_end + xm_macho_nlist_t nlist_end; + tb_memset(&nlist_end, 0, sizeof(nlist_end)); + nlist_end.strx = strx; + nlist_end.type = XM_MACHO_N_TYPE_SECT | XM_MACHO_N_EXT; // N_SECT | N_EXT + nlist_end.sect = 1; + nlist_end.desc = 0; + nlist_end.value = data_size; + if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&nlist_end, sizeof(nlist_end))) { + return tb_false; + } + strx += (tb_uint32_t)(end_len + 1); + + // align to 4 bytes + padding = strtab_offset - (symtab_offset + nlist_size * nlist_count); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + // write string table + tb_stream_bwrit(ostream, (tb_byte_t const *)&strtab_size, 4); + tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len); + tb_byte_t null = 0; + tb_stream_bwrit(ostream, &null, 1); + tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len); + tb_stream_bwrit(ostream, &null, 1); + + // align string table to 4 bytes + padding = strtab_size - (4 + (tb_uint32_t)start_len + 1 + (tb_uint32_t)end_len + 1); + if (padding > 0) { + tb_byte_t zero = 0; + while (padding-- > 0) { + if (!tb_stream_bwrit(ostream, &zero, 1)) { + return tb_false; + } + } + } + + return tb_true; +} + +static tb_bool_t xm_binutils_bin2macho_dump(tb_stream_ref_t istream, + tb_stream_ref_t ostream, + tb_char_t const *symbol_prefix, + tb_char_t const *plat, + tb_char_t const *arch, + tb_char_t const *basename, + tb_uint32_t minos, + tb_uint32_t sdk, + tb_bool_t zeroend) { + if (xm_binutils_macho_is_64bit(arch)) { + return xm_binutils_bin2macho_dump_64(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend); + } else { + return xm_binutils_bin2macho_dump_32(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend); + } +} + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* generate Mach-O object file from binary file + * + * local ok, errors = binutils.bin2macho(binaryfile, outputfile, symbol_prefix, plat, arch, basename) + */ +tb_int_t xm_binutils_bin2macho(lua_State *lua) { + tb_assert_and_check_return_val(lua, 0); + + // get the binaryfile + tb_char_t const *binaryfile = luaL_checkstring(lua, 1); + tb_check_return_val(binaryfile, 0); + + // get the outputfile + tb_char_t const *outputfile = luaL_checkstring(lua, 2); + tb_check_return_val(outputfile, 0); + + // get symbol prefix (optional) + tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null; + + // get plat (optional) + tb_char_t const *plat = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null; + + // get arch (optional) + tb_char_t const *arch = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null; + + // get basename (optional) + tb_char_t const *basename = lua_isstring(lua, 6) ? lua_tostring(lua, 6) : tb_null; + + // get minos version string (optional) + tb_char_t const *minos_str = lua_isstring(lua, 7) ? lua_tostring(lua, 7) : tb_null; + tb_uint32_t minos = xm_binutils_macho_parse_version(minos_str); + + // get sdk version string (optional) + tb_char_t const *sdk_str = lua_isstring(lua, 8) ? lua_tostring(lua, 8) : tb_null; + tb_uint32_t sdk = xm_binutils_macho_parse_version(sdk_str); + + // get zeroend (optional, default: false) + tb_bool_t zeroend = lua_toboolean(lua, 9); + + // do dump + tb_bool_t ok = tb_false; + tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO); + tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile, + TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC); + do { + if (!tb_stream_open(istream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2macho: open %s failed", binaryfile); + break; + } + + if (!tb_stream_open(ostream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2macho: open %s failed", outputfile); + break; + } + + if (!xm_binutils_bin2macho_dump(istream, ostream, symbol_prefix, plat, arch, basename, minos, sdk, zeroend)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "bin2macho: dump data failed"); + break; + } + + ok = tb_true; + lua_pushboolean(lua, ok); + + } while (0); + + if (istream) { + tb_stream_clos(istream); + } + istream = tb_null; + + if (ostream) { + tb_stream_clos(ostream); + } + ostream = tb_null; + + return ok ? 1 : 2; +} + diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h new file mode 100644 index 000000000..b3f0fb511 --- /dev/null +++ b/core/src/xmake/binutils/macho/prefix.h @@ -0,0 +1,322 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file prefix.h + * + */ +#ifndef XM_BINUTILS_MACHO_PREFIX_H +#define XM_BINUTILS_MACHO_PREFIX_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "../prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * macros + */ +#define XM_MACHO_MAGIC_32 0xfeedface +#define XM_MACHO_MAGIC_64 0xfeedfacf +#define XM_MACHO_MAGIC_FAT 0xcafebabe + +#define XM_MACHO_CPU_TYPE_X86 7 +#define XM_MACHO_CPU_TYPE_X86_64 0x01000007 +#define XM_MACHO_CPU_TYPE_ARM 12 +#define XM_MACHO_CPU_TYPE_ARM64 0x0100000c + +#define XM_MACHO_CPU_SUBTYPE_X86 3 +#define XM_MACHO_CPU_SUBTYPE_X86_64 3 +#define XM_MACHO_CPU_SUBTYPE_ARM 9 +#define XM_MACHO_CPU_SUBTYPE_ARM64 0 + +#define XM_MACHO_FILE_TYPE_OBJECT 1 + +#define XM_MACHO_LC_SEGMENT 0x1 +#define XM_MACHO_LC_SEGMENT_64 0x19 +#define XM_MACHO_LC_SYMTAB 0x2 +#define XM_MACHO_LC_BUILD_VERSION 0x32 + +#define XM_MACHO_PLATFORM_MACOS 1 +#define XM_MACHO_PLATFORM_IOS 2 +#define XM_MACHO_PLATFORM_TVOS 3 +#define XM_MACHO_PLATFORM_WATCHOS 4 + +#define XM_MACHO_SECT_TYPE_REGULAR 0x0 +#define XM_MACHO_SECT_ATTR_SOME_INITS 0x400 +#define XM_MACHO_SECT_ATTR_PURE_INSTRUCTIONS 0x80000000 + +#define XM_MACHO_N_TYPE_MASK 0x0e +#define XM_MACHO_N_TYPE_SECT 0x0e +#define XM_MACHO_N_EXT 0x01 + +#define XM_MACHO_VM_PROT_READ 1 +#define XM_MACHO_VM_PROT_WRITE 2 +#define XM_MACHO_VM_PROT_EXECUTE 4 + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ +#include "tbox/prefix/packed.h" +typedef struct __xm_macho_header_t { + tb_uint32_t magic; + tb_uint32_t cputype; + tb_uint32_t cpusubtype; + tb_uint32_t filetype; + tb_uint32_t ncmds; + tb_uint32_t sizeofcmds; + tb_uint32_t flags; +} __tb_packed__ xm_macho_header_t; + +typedef struct __xm_macho_header_64_t { + tb_uint32_t magic; + tb_uint32_t cputype; + tb_uint32_t cpusubtype; + tb_uint32_t filetype; + tb_uint32_t ncmds; + tb_uint32_t sizeofcmds; + tb_uint32_t flags; + tb_uint32_t reserved; +} __tb_packed__ xm_macho_header_64_t; + +typedef struct __xm_macho_segment_command_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; + tb_char_t segname[16]; + tb_uint32_t vmaddr; + tb_uint32_t vmsize; + tb_uint32_t fileoff; + tb_uint32_t filesize; + tb_uint32_t maxprot; + tb_uint32_t initprot; + tb_uint32_t nsects; + tb_uint32_t flags; +} __tb_packed__ xm_macho_segment_command_t; + +typedef struct __xm_macho_segment_command_64_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; + tb_char_t segname[16]; + tb_uint64_t vmaddr; + tb_uint64_t vmsize; + tb_uint64_t fileoff; + tb_uint64_t filesize; + tb_uint32_t maxprot; + tb_uint32_t initprot; + tb_uint32_t nsects; + tb_uint32_t flags; +} __tb_packed__ xm_macho_segment_command_64_t; + +typedef struct __xm_macho_section_t { + tb_char_t sectname[16]; + tb_char_t segname[16]; + tb_uint32_t addr; + tb_uint32_t size; + tb_uint32_t offset; + tb_uint32_t align; + tb_uint32_t reloff; + tb_uint32_t nreloc; + tb_uint32_t flags; + tb_uint32_t reserved1; + tb_uint32_t reserved2; +} __tb_packed__ xm_macho_section_t; + +typedef struct __xm_macho_section_64_t { + tb_char_t sectname[16]; + tb_char_t segname[16]; + tb_uint64_t addr; + tb_uint64_t size; + tb_uint32_t offset; + tb_uint32_t align; + tb_uint32_t reloff; + tb_uint32_t nreloc; + tb_uint32_t flags; + tb_uint32_t reserved1; + tb_uint32_t reserved2; + tb_uint32_t reserved3; +} __tb_packed__ xm_macho_section_64_t; + +typedef struct __xm_macho_symtab_command_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; + tb_uint32_t symoff; + tb_uint32_t nsyms; + tb_uint32_t stroff; + tb_uint32_t strsize; +} __tb_packed__ xm_macho_symtab_command_t; + +typedef struct __xm_macho_build_version_command_t { + tb_uint32_t cmd; + tb_uint32_t cmdsize; + tb_uint32_t platform; + tb_uint32_t minos; + tb_uint32_t sdk; + tb_uint32_t ntools; +} __tb_packed__ xm_macho_build_version_command_t; + +typedef struct __xm_macho_nlist_t { + tb_uint32_t strx; + tb_uint8_t type; + tb_uint8_t sect; + tb_int16_t desc; + tb_uint32_t value; +} __tb_packed__ xm_macho_nlist_t; + +typedef struct __xm_macho_nlist_64_t { + tb_uint32_t strx; + tb_uint8_t type; + tb_uint8_t sect; + tb_uint16_t desc; + tb_uint64_t value; +} __tb_packed__ xm_macho_nlist_64_t; + +/* ////////////////////////////////////////////////////////////////////////////////////// + * inline implementation + */ + +/* get CPU type from architecture string + * + * @param arch the architecture string (e.g., "x86_64", "i386", "arm64") + * @return the CPU type + */ +static __tb_inline__ tb_uint32_t xm_binutils_macho_get_cputype(tb_char_t const *arch) { + if (!arch) { + return XM_MACHO_CPU_TYPE_X86_64; + } + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return XM_MACHO_CPU_TYPE_X86_64; + } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { + return XM_MACHO_CPU_TYPE_ARM64; + } else if (tb_strcmp(arch, "arm") == 0) { + return XM_MACHO_CPU_TYPE_ARM; + } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { + return XM_MACHO_CPU_TYPE_X86; + } + return XM_MACHO_CPU_TYPE_X86_64; +} + +/* get CPU subtype from architecture string + * + * @param arch the architecture string + * @return the CPU subtype + */ +static __tb_inline__ tb_uint32_t xm_binutils_macho_get_cpusubtype(tb_char_t const *arch) { + if (!arch) { + return XM_MACHO_CPU_SUBTYPE_X86_64; + } + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return XM_MACHO_CPU_SUBTYPE_X86_64; + } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { + return XM_MACHO_CPU_SUBTYPE_ARM64; + } else if (tb_strcmp(arch, "arm") == 0) { + return XM_MACHO_CPU_SUBTYPE_ARM; + } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { + return XM_MACHO_CPU_SUBTYPE_X86; + } + return XM_MACHO_CPU_SUBTYPE_X86_64; +} + +/* 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_macho_is_64bit(tb_char_t const *arch) { + if (!arch) { + return tb_true; + } + if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { + return tb_true; + } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { + return tb_true; + } else if (tb_strcmp(arch, "arm") == 0) { + return tb_false; + } else if (tb_strcmp(arch, "x86") == 0 || tb_strcmp(arch, "i386") == 0) { + return tb_false; + } + return tb_true; +} + +/* align value to specified alignment + * + * @param value the value to align + * @param align the alignment (must be power of 2) + * @return the aligned value + */ +static __tb_inline__ tb_uint32_t xm_binutils_macho_align(tb_uint32_t value, tb_uint32_t align) { + return ((value + align - 1) & ~(align - 1)); +} + +/* get platform from platform string + * + * @param platform the platform string (e.g., "macosx", "ios", "tvos", "watchos") + * @return the platform constant + */ +static __tb_inline__ tb_uint32_t xm_binutils_macho_get_platform(tb_char_t const *platform) { + if (!platform) { + return XM_MACHO_PLATFORM_MACOS; + } + if (tb_strcmp(platform, "macosx") == 0 || tb_strcmp(platform, "macos") == 0) { + return XM_MACHO_PLATFORM_MACOS; + } else if (tb_strcmp(platform, "iphoneos") == 0 || tb_strcmp(platform, "ios") == 0) { + return XM_MACHO_PLATFORM_IOS; + } else if (tb_strcmp(platform, "appletvos") == 0 || tb_strcmp(platform, "tvos") == 0) { + return XM_MACHO_PLATFORM_TVOS; + } else if (tb_strcmp(platform, "watchos") == 0) { + return XM_MACHO_PLATFORM_WATCHOS; + } + return XM_MACHO_PLATFORM_MACOS; +} + +/* parse version string to Mach-O format + * + * @param version_str the version string (e.g., "10.0" or "18.2") + * @return the version in Mach-O format (major << 16) | (minor << 8) | patch + */ +static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const *version_str) { + if (!version_str || !version_str[0]) { + return 0x000a0000; // default: 10.0.0 + } + tb_uint32_t major = 0; + tb_uint32_t minor = 0; + tb_uint32_t patch = 0; + tb_char_t const *p = version_str; + // parse major + while (*p && tb_isdigit(*p)) { + major = major * 10 + (*p - '0'); + p++; + } + if (*p == '.') { + p++; + // parse minor + while (*p && tb_isdigit(*p)) { + minor = minor * 10 + (*p - '0'); + p++; + } + if (*p == '.') { + p++; + // parse patch + while (*p && tb_isdigit(*p)) { + patch = patch * 10 + (*p - '0'); + p++; + } + } + } + return (major << 16) | (minor << 8) | patch; +} + +#endif + diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh index 6cdbb042d..c65c5c1ef 100755 --- a/core/src/xmake/xmake.sh +++ b/core/src/xmake/xmake.sh @@ -72,6 +72,9 @@ target "xmake" add_files "string/*.c" add_files "tty/*.c" add_files "binutils/*.c" + add_files "binutils/coff/*.c" + add_files "binutils/macho/*.c" + add_files "binutils/elf/*.c" add_files "thread/*.c" if is_plat "mingw"; then add_files "winos/*.c" -- cgit v1.3.1 From ba293bc4b8840da34220e1f54313d531d18dde8e Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:29:51 +0800 Subject: add binary readsyms --- core/src/xmake/binutils/coff/prefix.h | 104 +++++++ core/src/xmake/binutils/coff/readsyms.c | 133 +++++++++ core/src/xmake/binutils/elf/prefix.h | 69 +++++ core/src/xmake/binutils/elf/readsyms.c | 325 +++++++++++++++++++++ core/src/xmake/binutils/macho/prefix.h | 65 +++++ core/src/xmake/binutils/macho/readsyms.c | 285 ++++++++++++++++++ core/src/xmake/binutils/prefix.h | 112 ++++++- core/src/xmake/binutils/readsyms.c | 122 ++++++++ core/src/xmake/engine.c | 2 + xmake/core/base/binutils.lua | 10 + .../sandbox/modules/import/core/base/binutils.lua | 10 + xmake/core/sandbox/modules/utils.lua | 2 +- xmake/modules/utils/binary/readsyms.lua | 92 ++++++ 13 files changed, 1320 insertions(+), 11 deletions(-) create mode 100644 core/src/xmake/binutils/coff/readsyms.c create mode 100644 core/src/xmake/binutils/elf/readsyms.c create mode 100644 core/src/xmake/binutils/macho/readsyms.c create mode 100644 core/src/xmake/binutils/readsyms.c create mode 100644 xmake/modules/utils/binary/readsyms.lua (limited to 'core/src') 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 @@ -26,10 +26,107 @@ */ #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 = 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 + -- cgit v1.3.1 From acd4bfbcf3b73349f6b56197f3c0f27f204cc135 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:32:47 +0800 Subject: improve dump --- core/src/xmake/binutils/coff/readsyms.c | 34 ++++++------ core/src/xmake/binutils/elf/readsyms.c | 96 ++++++++++++++++----------------- core/src/xmake/binutils/readsyms.c | 16 +++--- xmake/modules/utils/binary/readsyms.lua | 68 +++++++++++++++++------ 4 files changed, 125 insertions(+), 89 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c index edc5cc3ee..d863c2fdf 100644 --- a/core/src/xmake/binutils/coff/readsyms.c +++ b/core/src/xmake/binutils/coff/readsyms.c @@ -36,7 +36,7 @@ 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)) { @@ -45,24 +45,24 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) 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) { @@ -71,7 +71,7 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) 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]) { @@ -84,41 +84,41 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) } 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; @@ -128,6 +128,6 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) } } } - + return tb_true; } diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c index ce6653bfe..06c17a697 100644 --- a/core/src/xmake/binutils/elf/readsyms.c +++ b/core/src/xmake/binutils/elf/readsyms.c @@ -36,7 +36,7 @@ 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)) { @@ -45,23 +45,23 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu 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; @@ -74,12 +74,12 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu } } } - + 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))) { @@ -90,83 +90,83 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu } 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)) { @@ -175,23 +175,23 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu 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; @@ -202,12 +202,12 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu } } } - + 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))) { @@ -218,83 +218,83 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu } 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)) { @@ -303,7 +303,7 @@ tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua) 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)) { @@ -312,13 +312,13 @@ tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua) 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/readsyms.c b/core/src/xmake/binutils/readsyms.c index dbb8e5957..ae261782f 100644 --- a/core/src/xmake/binutils/readsyms.c +++ b/core/src/xmake/binutils/readsyms.c @@ -51,11 +51,11 @@ extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_Sta */ 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) { @@ -63,7 +63,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) { lua_pushfstring(lua, "readsyms: open %s failed", objectfile); return 2; } - + tb_bool_t ok = tb_false; do { if (!tb_stream_open(istream)) { @@ -71,7 +71,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) { lua_pushfstring(lua, "readsyms: open %s failed", objectfile); break; } - + // detect format tb_int_t format = xm_binutils_detect_format(istream); if (format < 0) { @@ -79,7 +79,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) { lua_pushfstring(lua, "readsyms: cannot detect file format"); break; } - + // read symbols based on format if (format == XM_BINUTILS_FORMAT_COFF) { // COFF @@ -108,15 +108,15 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) { 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/xmake/modules/utils/binary/readsyms.lua b/xmake/modules/utils/binary/readsyms.lua index 212c99401..899b43f71 100644 --- a/xmake/modules/utils/binary/readsyms.lua +++ b/xmake/modules/utils/binary/readsyms.lua @@ -40,30 +40,66 @@ end function dump(binaryfile) local symbols = _get_symbols(binaryfile) if symbols and #symbols > 0 then - print("") - print("Symbols:") + -- calculate column widths for alignment + local max_value_len = 0 + local max_name_len = 0 + local max_type_len = 0 + local max_bind_len = 0 + local max_section_len = 0 + local max_size_len = 0 + 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) + local value_str = string.format("0x%x", sym.value) + max_value_len = math.max(max_value_len, #value_str) end - local section_str = "" - if sym.section and sym.section > 0 then - section_str = string.format(" section=%d", sym.section) + if sym.name then + max_name_len = math.max(max_name_len, #sym.name) end - local type_str = "" if sym.type then - type_str = string.format(" type=%s", sym.type) + max_type_len = math.max(max_type_len, #sym.type) end - local bind_str = "" if sym.bind then - bind_str = string.format(" bind=%s", sym.bind) + max_bind_len = math.max(max_bind_len, #sym.bind) + end + if sym.section then + local section_str = tostring(sym.section) + max_section_len = math.max(max_section_len, #section_str) + end + if sym.size then + local size_str = tostring(sym.size) + max_size_len = math.max(max_size_len, #size_str) end - print(string.format(" %s %s%s%s%s%s", sym.name or "", value_str, size_str, section_str, type_str, bind_str)) + end + + -- calculate column widths + local value_width = math.max(max_value_len, 10) + local type_width = math.max(max_type_len, 4) + local bind_width = math.max(max_bind_len, 4) + local section_width = math.max(max_section_len, 7) + local size_width = math.max(max_size_len, 4) + + -- print header + print("") + print("Symbols:") + local header_format = string.format(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s", + value_width, type_width, bind_width, section_width, size_width) + print(string.format(header_format, "VALUE", "TYPE", "BIND", "SECTION", "SIZE", "NAME")) + print(string.rep("-", 80)) + + -- print symbols + local format_str = string.format(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s", + value_width, type_width, bind_width, section_width, size_width) + + for i, sym in ipairs(symbols) do + local value_str = sym.value and string.format("0x%x", sym.value) or "" + local type_str = sym.type or "" + local bind_str = sym.bind or "" + local section_str = sym.section and sym.section > 0 and tostring(sym.section) or "" + local size_str = sym.size and tostring(sym.size) or "" + local name_str = sym.name or "" + + print(string.format(format_str, value_str, type_str, bind_str, section_str, size_str, name_str)) end print("") cprint("${bright}%d symbols found!", #symbols) -- cgit v1.3.1 From 333af0b66dfe73fabb2c900bce00ba2d50bfe622 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:33:18 +0800 Subject: fix readsyms for macho --- core/src/xmake/binutils/macho/prefix.h | 6 +++--- xmake/modules/utils/binary/readsyms.lua | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index c11fbebb6..39ac857f1 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -326,7 +326,7 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const * * @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 offset the string offset (nlist.strx, relative to string table start, including 4-byte 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 @@ -335,8 +335,8 @@ static __tb_inline__ tb_bool_t xm_binutils_macho_read_string(tb_stream_ref_t ist 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)) { + // nlist.strx is offset from string table start (including 4-byte size field) + if (!tb_stream_seek(istream, strtab_offset + offset)) { return tb_false; } diff --git a/xmake/modules/utils/binary/readsyms.lua b/xmake/modules/utils/binary/readsyms.lua index 899b43f71..103b13891 100644 --- a/xmake/modules/utils/binary/readsyms.lua +++ b/xmake/modules/utils/binary/readsyms.lua @@ -82,14 +82,12 @@ function dump(binaryfile) -- print header print("") print("Symbols:") - local header_format = string.format(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s", - value_width, type_width, bind_width, section_width, size_width) + local header_format = " %-" .. value_width .. "s %-" .. type_width .. "s %-" .. bind_width .. "s %-" .. section_width .. "s %-" .. size_width .. "s %s" print(string.format(header_format, "VALUE", "TYPE", "BIND", "SECTION", "SIZE", "NAME")) print(string.rep("-", 80)) -- print symbols - local format_str = string.format(" %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds %%s", - value_width, type_width, bind_width, section_width, size_width) + local format_str = " %-" .. value_width .. "s %-" .. type_width .. "s %-" .. bind_width .. "s %-" .. section_width .. "s %-" .. size_width .. "s %s" for i, sym in ipairs(symbols) do local value_str = sym.value and string.format("0x%x", sym.value) or "" -- cgit v1.3.1 From 2ce4dfa1c8029498cfa6b99ed28daca6c2e350d4 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:36:15 +0800 Subject: improve symbol type --- core/src/xmake/binutils/coff/prefix.h | 35 +++++++++++++++++++---------- core/src/xmake/binutils/coff/readsyms.c | 17 ++++++++++++-- core/src/xmake/binutils/elf/prefix.h | 36 +++++++++++++++++++++--------- core/src/xmake/binutils/elf/readsyms.c | 34 ++++++++++++++++++++++++---- core/src/xmake/binutils/macho/prefix.h | 38 ++++++++++++++++++++++++++------ core/src/xmake/binutils/macho/readsyms.c | 22 ++++++++++++++---- 6 files changed, 142 insertions(+), 40 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h index 95737dc20..ccc7f6292 100644 --- a/core/src/xmake/binutils/coff/prefix.h +++ b/core/src/xmake/binutils/coff/prefix.h @@ -258,20 +258,31 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t } } -/* get symbol type string from storage class +/* get symbol type character (nm-style) from COFF symbol * - * @param scl the storage class - * @return the type string + * @param scl the storage class + * @param sect the section number (0 = undefined) + * @return the type character (T/t/D/d/B/b/U) */ -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"; +static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t scl, tb_int16_t sect) { + // undefined symbol + if (sect == 0) { + return 'U'; + } + + // check if external + tb_bool_t is_external = (scl == 2); // IMAGE_SYM_CLASS_EXTERNAL + + // For COFF, section 1 is usually .text, section 2 is .data, section 3 is .bss + // This is a heuristic and may not be 100% accurate + if (sect == 1) { + return is_external ? 'T' : 't'; // text section + } else if (sect == 2) { + return is_external ? 'D' : 'd'; // data section + } else if (sect == 3) { + return is_external ? 'B' : 'b'; // bss section + } else { + return is_external ? 'S' : 's'; // other section } } diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c index d863c2fdf..abe374367 100644 --- a/core/src/xmake/binutils/coff/readsyms.c +++ b/core/src/xmake/binutils/coff/readsyms.c @@ -84,6 +84,17 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) } continue; } + + // skip internal symbols (starting with .) + if (name[0] == '.') { + 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); @@ -104,9 +115,11 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) lua_pushinteger(lua, sym.sect); lua_settable(lua, -3); - // type + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_coff_get_symbol_type_char(sym.scl, sym.sect); + tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); - lua_pushstring(lua, xm_binutils_coff_get_symbol_type(sym.scl)); + lua_pushstring(lua, type_str); lua_settable(lua, -3); // storage class diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index 97d05a661..7ddc0ef85 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -303,21 +303,35 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_read_string(tb_stream_ref_t istre return tb_true; } -/* get symbol type string from ELF symbol info +/* get symbol type character (nm-style) from ELF symbol * - * @param st_info the symbol info byte - * @return the type string + * @param st_info the symbol info byte + * @param st_shndx the section index (0 = undefined) + * @return the type character (T/t/D/d/B/b/U) */ -static __tb_inline__ tb_char_t const *xm_binutils_elf_get_symbol_type(tb_uint8_t st_info) { +static __tb_inline__ tb_char_t xm_binutils_elf_get_symbol_type_char(tb_uint8_t st_info, tb_uint16_t st_shndx) { + // undefined symbol + if (st_shndx == 0) { + return 'U'; + } + + // check bind (global = uppercase, local = lowercase) + tb_uint8_t bind = (st_info >> 4) & 0xf; + tb_bool_t is_global = (bind == 1); // STB_GLOBAL + + // check type 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"; + if (type == 2) { // STT_FUNC + return is_global ? 'T' : 't'; // text (function) + } else if (type == 1) { // STT_OBJECT + // For object symbols, we need section info to determine data/bss + // For simplicity, we'll use 'D' for data, 'B' for bss + // This is a heuristic - in practice, we'd need to check section flags + return is_global ? 'D' : 'd'; // data (assume data section) } + + // other types + return is_global ? 'S' : 's'; // other section } /* get symbol bind string from ELF symbol info diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c index 06c17a697..1afc80fc6 100644 --- a/core/src/xmake/binutils/elf/readsyms.c +++ b/core/src/xmake/binutils/elf/readsyms.c @@ -117,11 +117,22 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu continue; } + // skip section and file symbols + tb_uint8_t type = sym.st_info & 0xf; + if (type == 3 || type == 4) { // STT_SECTION or STT_FILE + 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; } + + // skip internal symbols (starting with .) + if (name[0] == '.') { + continue; + } // create symbol table entry lua_pushinteger(lua, result_count + 1); @@ -147,9 +158,11 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu lua_pushinteger(lua, sym.st_shndx); lua_settable(lua, -3); - // type + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_elf_get_symbol_type_char(sym.st_info, sym.st_shndx); + tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); - lua_pushstring(lua, xm_binutils_elf_get_symbol_type(sym.st_info)); + lua_pushstring(lua, type_str); lua_settable(lua, -3); // bind @@ -245,11 +258,22 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu continue; } + // skip section and file symbols + tb_uint8_t type = sym.st_info & 0xf; + if (type == 3 || type == 4) { // STT_SECTION or STT_FILE + 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; } + + // skip internal symbols (starting with .) + if (name[0] == '.') { + continue; + } // create symbol table entry lua_pushinteger(lua, result_count + 1); @@ -275,9 +299,11 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu lua_pushinteger(lua, sym.st_shndx); lua_settable(lua, -3); - // type + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_elf_get_symbol_type_char(sym.st_info, sym.st_shndx); + tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); - lua_pushstring(lua, xm_binutils_elf_get_symbol_type(sym.st_info)); + lua_pushstring(lua, type_str); lua_settable(lua, -3); // bind diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index 39ac857f1..7664940b1 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -358,17 +358,41 @@ static __tb_inline__ tb_bool_t xm_binutils_macho_read_string(tb_stream_ref_t ist return tb_true; } -/* get symbol type string from Mach-O symbol type +/* get symbol type character (nm-style) from Mach-O symbol * - * @param type the symbol type byte - * @return the type string + * @param type the symbol type byte + * @param sect the section number (0 = undefined) + * @return the type character (T/t/D/d/B/b/U) */ -static __tb_inline__ tb_char_t const *xm_binutils_macho_get_symbol_type(tb_uint8_t type) { +static __tb_inline__ tb_char_t xm_binutils_macho_get_symbol_type_char(tb_uint8_t type, tb_uint8_t sect) { + // undefined symbol + if (sect == 0) { + return 'U'; + } + + // check if external + tb_bool_t is_external = (type & XM_MACHO_N_EXT) != 0; + + // check if in section 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"; + if (n_type == XM_MACHO_N_TYPE_SECT) { + // section 1 is usually __TEXT,__text (text) + // section 2 is usually __DATA,__data (data) + // section 3 is usually __DATA,__bss (bss) + // For simplicity, we'll use section number to determine type + // This is a heuristic and may not be 100% accurate + if (sect == 1) { + return is_external ? 'T' : 't'; // text section + } else if (sect == 2) { + return is_external ? 'D' : 'd'; // data section + } else if (sect == 3) { + return is_external ? 'B' : 'b'; // bss section + } else { + return is_external ? 'S' : 's'; // other section + } } + + return '?'; // unknown } /* get symbol bind string from Mach-O symbol type diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c index a78daaa5a..62596c45e 100644 --- a/core/src/xmake/binutils/macho/readsyms.c +++ b/core/src/xmake/binutils/macho/readsyms.c @@ -110,6 +110,11 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * continue; } + // skip internal symbols (starting with .) + if (name[0] == '.') { + continue; + } + // create symbol table entry lua_pushinteger(lua, result_count + 1); lua_newtable(lua); @@ -129,9 +134,11 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * lua_pushinteger(lua, nlist.sect); lua_settable(lua, -3); - // type + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_macho_get_symbol_type_char(nlist.type, nlist.sect); + tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); - lua_pushstring(lua, xm_binutils_macho_get_symbol_type(nlist.type)); + lua_pushstring(lua, type_str); lua_settable(lua, -3); // bind @@ -222,6 +229,11 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * continue; } + // skip internal symbols (starting with .) + if (name[0] == '.') { + continue; + } + // create symbol table entry lua_pushinteger(lua, result_count + 1); lua_newtable(lua); @@ -241,9 +253,11 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * lua_pushinteger(lua, nlist.sect); lua_settable(lua, -3); - // type + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_macho_get_symbol_type_char(nlist.type, nlist.sect); + tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); - lua_pushstring(lua, xm_binutils_macho_get_symbol_type(nlist.type)); + lua_pushstring(lua, type_str); lua_settable(lua, -3); // bind -- cgit v1.3.1 From d48b2fcf37ed5c323d8e2d2d407bda9679dc01dc Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:42:30 +0800 Subject: improve symbol dump --- core/src/xmake/binutils/coff/prefix.h | 60 ++++++++++++++++++++++++-------- core/src/xmake/binutils/coff/readsyms.c | 59 ++++++++++++++++++++++--------- core/src/xmake/binutils/elf/readsyms.c | 40 --------------------- core/src/xmake/binutils/macho/readsyms.c | 30 ---------------- xmake/modules/utils/binary/readsyms.lua | 38 ++++---------------- 5 files changed, 93 insertions(+), 134 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h index ccc7f6292..1b1c2298e 100644 --- a/core/src/xmake/binutils/coff/prefix.h +++ b/core/src/xmake/binutils/coff/prefix.h @@ -34,6 +34,11 @@ #define XM_COFF_MACHINE_ARM 0x01c0 #define XM_COFF_MACHINE_ARM64 0xaa64 +// 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 +#define XM_COFF_SCN_CNT_UNINITIALIZED_DATA 0x80 // IMAGE_SCN_CNT_UNINITIALIZED_DATA + #define XM_COFF_SECTION_RDATA 0x40000040 // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ /* ////////////////////////////////////////////////////////////////////////////////////// @@ -178,14 +183,18 @@ static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_ /* 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) + * @param strtab_offset the string table offset (including 4-byte size field) + * @param offset the string offset (from start of 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 + // In COFF format, the offset in symbol table is from the start of string table + // (including the 4-byte size field). So offset=4 points to the first string after + // the size field, offset=74 points to a string at position 74 from the start. + + // read string table size first to validate offset tb_uint32_t strtab_size = 0; tb_hize_t saved_pos = tb_stream_offset(istream); if (!tb_stream_seek(istream, strtab_offset)) { @@ -196,14 +205,17 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr return tb_false; } - // check offset (offset is relative to start of string table, after the 4-byte size field) - if (offset >= strtab_size - 4) { + // check offset (must be >= 4 to skip the size field, and < strtab_size) + if (offset < 4 || offset >= strtab_size) { 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)) { + // 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; } @@ -260,11 +272,13 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t /* get symbol type character (nm-style) from COFF symbol * - * @param scl the storage class - * @param sect the section number (0 = undefined) - * @return the type character (T/t/D/d/B/b/U) + * @param scl the storage class + * @param sect the section number (0 = undefined, 1-based) + * @param sections the section headers array + * @param nsects the number of sections + * @return the type character (T/t/D/d/B/b/U) */ -static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t scl, tb_int16_t sect) { +static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t scl, tb_int16_t sect, xm_coff_section_t const *sections, tb_uint16_t nsects) { // undefined symbol if (sect == 0) { return 'U'; @@ -273,17 +287,33 @@ static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t // check if external tb_bool_t is_external = (scl == 2); // IMAGE_SYM_CLASS_EXTERNAL - // For COFF, section 1 is usually .text, section 2 is .data, section 3 is .bss - // This is a heuristic and may not be 100% accurate + // check section flags to determine type + if (sections && sect > 0 && sect <= nsects) { + tb_uint32_t flags = sections[sect - 1].flags; // section numbers are 1-based + // IMAGE_SCN_CNT_CODE (0x20) - code section + if (flags & XM_COFF_SCN_CNT_CODE) { + return is_external ? 'T' : 't'; // text section + } + // IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x80) - bss section + if (flags & XM_COFF_SCN_CNT_UNINITIALIZED_DATA) { + return is_external ? 'B' : 'b'; // bss section + } + // IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) - data section + if (flags & XM_COFF_SCN_CNT_INITIALIZED_DATA) { + return is_external ? 'D' : 'd'; // data section + } + } + + // fallback: use section number heuristic if (sect == 1) { return is_external ? 'T' : 't'; // text section } else if (sect == 2) { return is_external ? 'D' : 'd'; // data section } else if (sect == 3) { return is_external ? 'B' : 'b'; // bss section - } else { - return is_external ? 'S' : 's'; // other section } + + return is_external ? 'S' : 's'; // other section } #endif diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c index abe374367..9a10eba9d 100644 --- a/core/src/xmake/binutils/coff/readsyms.c +++ b/core/src/xmake/binutils/coff/readsyms.c @@ -58,8 +58,30 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) // read string table offset (after symbol table) tb_uint32_t strtab_offset = header.symtabofs + header.nsyms * 18; // each symbol is 18 bytes + // read section headers to determine section types + xm_coff_section_t *sections = tb_null; + if (header.nsects > 0) { + sections = (xm_coff_section_t*)tb_malloc(header.nsects * sizeof(xm_coff_section_t)); + if (sections) { + tb_hize_t saved_pos = tb_stream_offset(istream); + // section headers are after COFF header and optional header + tb_uint32_t section_offset = sizeof(xm_coff_header_t) + (header.opthdr > 0 ? header.opthdr : 0); + if (tb_stream_seek(istream, section_offset)) { + for (tb_uint16_t i = 0; i < header.nsects; i++) { + if (!tb_stream_bread(istream, (tb_byte_t*)§ions[i], sizeof(xm_coff_section_t))) { + break; + } + } + } + tb_stream_seek(istream, saved_pos); + } + } + // read symbols if (!tb_stream_seek(istream, header.symtabofs)) { + if (sections) { + tb_free(sections); + } return tb_false; } @@ -84,7 +106,7 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) } continue; } - + // skip internal symbols (starting with .) if (name[0] == '.') { sym_index++; @@ -96,6 +118,20 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) continue; } + // skip compiler-generated symbols (containing $ or .constprop or .startup, etc.) + if (tb_strchr(name, '$') != tb_null || + tb_strstr(name, ".constprop") != tb_null || + tb_strstr(name, ".startup") != tb_null || + tb_strstr(name, "ta$") != tb_null) { + 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); @@ -105,28 +141,13 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) 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 (nm-style: T/t/D/d/B/b/U) - tb_char_t type_char = xm_binutils_coff_get_symbol_type_char(sym.scl, sym.sect); + tb_char_t type_char = xm_binutils_coff_get_symbol_type_char(sym.scl, sym.sect, sections, header.nsects); tb_char_t type_str[2] = {type_char, '\0'}; lua_pushstring(lua, "type"); lua_pushstring(lua, type_str); 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++; @@ -142,5 +163,9 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) } } + if (sections) { + tb_free(sections); + } + return tb_true; } diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c index 1afc80fc6..6a9f333c7 100644 --- a/core/src/xmake/binutils/elf/readsyms.c +++ b/core/src/xmake/binutils/elf/readsyms.c @@ -143,21 +143,6 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu 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 (nm-style: T/t/D/d/B/b/U) tb_char_t type_char = xm_binutils_elf_get_symbol_type_char(sym.st_info, sym.st_shndx); tb_char_t type_str[2] = {type_char, '\0'}; @@ -165,11 +150,6 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu lua_pushstring(lua, type_str); 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++; } @@ -284,21 +264,6 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu 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 (nm-style: T/t/D/d/B/b/U) tb_char_t type_char = xm_binutils_elf_get_symbol_type_char(sym.st_info, sym.st_shndx); tb_char_t type_str[2] = {type_char, '\0'}; @@ -306,11 +271,6 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu lua_pushstring(lua, type_str); 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++; } diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c index 62596c45e..dbe7925a4 100644 --- a/core/src/xmake/binutils/macho/readsyms.c +++ b/core/src/xmake/binutils/macho/readsyms.c @@ -124,16 +124,6 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * 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 (nm-style: T/t/D/d/B/b/U) tb_char_t type_char = xm_binutils_macho_get_symbol_type_char(nlist.type, nlist.sect); tb_char_t type_str[2] = {type_char, '\0'}; @@ -141,11 +131,6 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * lua_pushstring(lua, type_str); 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++; } @@ -243,16 +228,6 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * 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 (nm-style: T/t/D/d/B/b/U) tb_char_t type_char = xm_binutils_macho_get_symbol_type_char(nlist.type, nlist.sect); tb_char_t type_str[2] = {type_char, '\0'}; @@ -260,11 +235,6 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * lua_pushstring(lua, type_str); 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++; } diff --git a/xmake/modules/utils/binary/readsyms.lua b/xmake/modules/utils/binary/readsyms.lua index 103b13891..7225177e9 100644 --- a/xmake/modules/utils/binary/readsyms.lua +++ b/xmake/modules/utils/binary/readsyms.lua @@ -41,63 +41,37 @@ function dump(binaryfile) local symbols = _get_symbols(binaryfile) if symbols and #symbols > 0 then -- calculate column widths for alignment - local max_value_len = 0 local max_name_len = 0 local max_type_len = 0 - local max_bind_len = 0 - local max_section_len = 0 - local max_size_len = 0 for i, sym in ipairs(symbols) do - if sym.value then - local value_str = string.format("0x%x", sym.value) - max_value_len = math.max(max_value_len, #value_str) - end if sym.name then max_name_len = math.max(max_name_len, #sym.name) end if sym.type then max_type_len = math.max(max_type_len, #sym.type) end - if sym.bind then - max_bind_len = math.max(max_bind_len, #sym.bind) - end - if sym.section then - local section_str = tostring(sym.section) - max_section_len = math.max(max_section_len, #section_str) - end - if sym.size then - local size_str = tostring(sym.size) - max_size_len = math.max(max_size_len, #size_str) - end end -- calculate column widths - local value_width = math.max(max_value_len, 10) local type_width = math.max(max_type_len, 4) - local bind_width = math.max(max_bind_len, 4) - local section_width = math.max(max_section_len, 7) - local size_width = math.max(max_size_len, 4) + local name_width = math.max(max_name_len, 4) -- print header print("") print("Symbols:") - local header_format = " %-" .. value_width .. "s %-" .. type_width .. "s %-" .. bind_width .. "s %-" .. section_width .. "s %-" .. size_width .. "s %s" - print(string.format(header_format, "VALUE", "TYPE", "BIND", "SECTION", "SIZE", "NAME")) + local header_format = " %-" .. type_width .. "s %s" + print(string.format(header_format, "TYPE", "NAME")) print(string.rep("-", 80)) -- print symbols - local format_str = " %-" .. value_width .. "s %-" .. type_width .. "s %-" .. bind_width .. "s %-" .. section_width .. "s %-" .. size_width .. "s %s" + local format_str = " %-" .. type_width .. "s %s" for i, sym in ipairs(symbols) do - local value_str = sym.value and string.format("0x%x", sym.value) or "" - local type_str = sym.type or "" - local bind_str = sym.bind or "" - local section_str = sym.section and sym.section > 0 and tostring(sym.section) or "" - local size_str = sym.size and tostring(sym.size) or "" + local type_str = sym.type or "unknown" local name_str = sym.name or "" - print(string.format(format_str, value_str, type_str, bind_str, section_str, size_str, name_str)) + print(string.format(format_str, type_str, name_str)) end print("") cprint("${bright}%d symbols found!", #symbols) -- cgit v1.3.1 From b5b4ed3cf17b9c3d11e24d16e3fe692377ecaf95 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:42:58 +0800 Subject: fix dump elf symbols --- core/src/xmake/binutils/elf/readsyms.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c index 6a9f333c7..f0d0a1061 100644 --- a/core/src/xmake/binutils/elf/readsyms.c +++ b/core/src/xmake/binutils/elf/readsyms.c @@ -129,8 +129,14 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu continue; } - // skip internal symbols (starting with .) - if (name[0] == '.') { + // skip internal symbols (starting with . or $) + if (name[0] == '.' || name[0] == '$') { + continue; + } + + // skip local symbols (unless undefined) + tb_uint8_t bind = (sym.st_info >> 4) & 0xf; + if (bind == 0 && sym.st_shndx != 0) { // STB_LOCAL and not undefined continue; } @@ -250,8 +256,14 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu continue; } - // skip internal symbols (starting with .) - if (name[0] == '.') { + // skip internal symbols (starting with . or $) + if (name[0] == '.' || name[0] == '$') { + continue; + } + + // skip local symbols (unless undefined) + tb_uint8_t bind = (sym.st_info >> 4) & 0xf; + if (bind == 0 && sym.st_shndx != 0) { // STB_LOCAL and not undefined continue; } -- cgit v1.3.1 From 5381ba967a7484f49c3dd3fb75e6443bacb74f46 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:44:23 +0800 Subject: format code --- core/src/xmake/binutils/coff/prefix.h | 22 +++++++++++----------- core/src/xmake/binutils/elf/prefix.h | 12 ++++++------ core/src/xmake/binutils/macho/prefix.h | 12 ++++++------ core/src/xmake/binutils/prefix.h | 28 ++++++++++++++-------------- 4 files changed, 37 insertions(+), 37 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h index 1b1c2298e..b9732a4ec 100644 --- a/core/src/xmake/binutils/coff/prefix.h +++ b/core/src/xmake/binutils/coff/prefix.h @@ -189,11 +189,11 @@ static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_ */ 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); - + // In COFF format, the offset in symbol table is from the start of string table // (including the 4-byte size field). So offset=4 points to the first string after // the size field, offset=74 points to a string at position 74 from the start. - + // read string table size first to validate offset tb_uint32_t strtab_size = 0; tb_hize_t saved_pos = tb_stream_offset(istream); @@ -204,13 +204,13 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr tb_stream_seek(istream, saved_pos); return tb_false; } - + // check offset (must be >= 4 to skip the size field, and < strtab_size) if (offset < 4 || offset >= strtab_size) { tb_stream_seek(istream, saved_pos); 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) @@ -219,7 +219,7 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr tb_stream_seek(istream, saved_pos); return tb_false; } - + // read string tb_size_t pos = 0; tb_byte_t c; @@ -234,7 +234,7 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr name[pos++] = (tb_char_t)c; } name[pos] = '\0'; - + // restore position tb_stream_seek(istream, saved_pos); return tb_true; @@ -251,7 +251,7 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr */ 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 @@ -283,10 +283,10 @@ static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t if (sect == 0) { return 'U'; } - + // check if external tb_bool_t is_external = (scl == 2); // IMAGE_SYM_CLASS_EXTERNAL - + // check section flags to determine type if (sections && sect > 0 && sect <= nsects) { tb_uint32_t flags = sections[sect - 1].flags; // section numbers are 1-based @@ -303,7 +303,7 @@ static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t return is_external ? 'D' : 'd'; // data section } } - + // fallback: use section number heuristic if (sect == 1) { return is_external ? 'T' : 't'; // text section @@ -312,7 +312,7 @@ static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t } else if (sect == 3) { return is_external ? 'B' : 'b'; // bss section } - + return is_external ? 'S' : 's'; // other section } diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index 7ddc0ef85..6eba9ac51 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -279,12 +279,12 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_is_64bit(tb_char_t const *arch) { */ 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) { @@ -298,7 +298,7 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_read_string(tb_stream_ref_t istre name[pos++] = (tb_char_t)c; } name[pos] = '\0'; - + tb_stream_seek(istream, saved_pos); return tb_true; } @@ -314,11 +314,11 @@ static __tb_inline__ tb_char_t xm_binutils_elf_get_symbol_type_char(tb_uint8_t s if (st_shndx == 0) { return 'U'; } - + // check bind (global = uppercase, local = lowercase) tb_uint8_t bind = (st_info >> 4) & 0xf; tb_bool_t is_global = (bind == 1); // STB_GLOBAL - + // check type tb_uint8_t type = st_info & 0xf; if (type == 2) { // STT_FUNC @@ -329,7 +329,7 @@ static __tb_inline__ tb_char_t xm_binutils_elf_get_symbol_type_char(tb_uint8_t s // This is a heuristic - in practice, we'd need to check section flags return is_global ? 'D' : 'd'; // data (assume data section) } - + // other types return is_global ? 'S' : 's'; // other section } diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index 7664940b1..0b0ea00c3 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -333,13 +333,13 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const */ 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); // nlist.strx is offset from string table start (including 4-byte size field) 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) { @@ -353,7 +353,7 @@ static __tb_inline__ tb_bool_t xm_binutils_macho_read_string(tb_stream_ref_t ist name[pos++] = (tb_char_t)c; } name[pos] = '\0'; - + tb_stream_seek(istream, saved_pos); return tb_true; } @@ -369,10 +369,10 @@ static __tb_inline__ tb_char_t xm_binutils_macho_get_symbol_type_char(tb_uint8_t if (sect == 0) { return 'U'; } - + // check if external tb_bool_t is_external = (type & XM_MACHO_N_EXT) != 0; - + // check if in section tb_uint8_t n_type = type & XM_MACHO_N_TYPE_MASK; if (n_type == XM_MACHO_N_TYPE_SECT) { @@ -391,7 +391,7 @@ static __tb_inline__ tb_char_t xm_binutils_macho_get_symbol_type_char(tb_uint8_t return is_external ? 'S' : 's'; // other section } } - + return '?'; // unknown } diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h index 74cef7cfe..048e74ee2 100644 --- a/core/src/xmake/binutils/prefix.h +++ b/core/src/xmake/binutils/prefix.h @@ -54,17 +54,17 @@ */ 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; } @@ -72,25 +72,25 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t /* detect object file format from stream * * @param istream the input stream - * @return XM_BINUTILS_FORMAT_COFF, XM_BINUTILS_FORMAT_ELF, XM_BINUTILS_FORMAT_MACHO, + * @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 && + 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) } @@ -100,7 +100,7 @@ static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) 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 @@ -114,15 +114,15 @@ static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) 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 || + 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; } -- cgit v1.3.1 From d1849e1b1a9d5accc1a3555272e27c96b863bbd8 Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:44:40 +0800 Subject: format code --- core/src/xmake/binutils/macho/prefix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index 0b0ea00c3..2179ac344 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -334,8 +334,8 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const 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); // nlist.strx is offset from string table start (including 4-byte size field) + tb_hize_t saved_pos = tb_stream_offset(istream); if (!tb_stream_seek(istream, strtab_offset + offset)) { return tb_false; } -- cgit v1.3.1 From 9459655ebc9cca1f945c71aa79dbe27a921f827e Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 00:46:58 +0800 Subject: read symbols from .a --- core/src/xmake/binutils/ar/prefix.h | 82 +++++++++++ core/src/xmake/binutils/ar/readsyms.c | 242 +++++++++++++++++++++++++++++++++ core/src/xmake/binutils/coff/prefix.h | 1 + core/src/xmake/binutils/elf/prefix.h | 1 + core/src/xmake/binutils/macho/prefix.h | 1 + core/src/xmake/binutils/prefix.h | 22 ++- core/src/xmake/binutils/readsyms.c | 10 +- core/src/xmake/xmake.sh | 1 + 8 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 core/src/xmake/binutils/ar/prefix.h create mode 100644 core/src/xmake/binutils/ar/readsyms.c (limited to 'core/src') diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h new file mode 100644 index 000000000..406838d6f --- /dev/null +++ b/core/src/xmake/binutils/ar/prefix.h @@ -0,0 +1,82 @@ +/*!A cross-platform build utility based on Lua + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright (C) 2015-present, Xmake Open Source Community. + * + * @author ruki + * @file prefix.h + * + */ +#ifndef XM_BINUTILS_AR_PREFIX_H +#define XM_BINUTILS_AR_PREFIX_H + +/* ////////////////////////////////////////////////////////////////////////////////////// + * 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); +extern tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream); + +/* ////////////////////////////////////////////////////////////////////////////////////// + * types + */ +#include "tbox/prefix/packed.h" +typedef struct __xm_ar_header_t { + tb_char_t name[16]; // file name (null-padded) + tb_char_t date[12]; // modification time (decimal) + tb_char_t uid[6]; // user ID (decimal) + tb_char_t gid[6]; // group ID (decimal) + tb_char_t mode[8]; // file mode (octal) + tb_char_t size[10]; // file size (decimal) + tb_char_t fmag[2]; // magic: "`\n" +} __tb_packed__ xm_ar_header_t; +#include "tbox/prefix/packed.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * inline implementation + */ + +/* parse decimal number from string + * + * @param str the string + * @param len the length + * @return the parsed number, or -1 on error + */ +static __tb_inline__ tb_int64_t xm_binutils_ar_parse_decimal(tb_char_t const *str, tb_size_t len) { + tb_assert_and_check_return_val(str && len > 0, -1); + + tb_int64_t result = 0; + for (tb_size_t i = 0; i < len; i++) { + if (str[i] == ' ' || str[i] == '\0') { + break; + } + if (str[i] < '0' || str[i] > '9') { + return -1; + } + result = result * 10 + (str[i] - '0'); + } + return result; +} + +#endif + diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c new file mode 100644 index 000000000..7f5f0b077 --- /dev/null +++ b/core/src/xmake/binutils/ar/readsyms.c @@ -0,0 +1,242 @@ +/*!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 "ar_readsyms" +#define TB_TRACE_MODULE_DEBUG (0) + +/* ////////////////////////////////////////////////////////////////////////////////////// + * includes + */ +#include "prefix.h" + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* read symbols from AR archive + * + * @param istream the input stream + * @param lua the lua state + * @return tb_true on success, tb_false on failure + */ +tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, lua_State *lua) { + tb_assert_and_check_return_val(istream && lua, tb_false); + + // create result table + lua_newtable(lua); + + // skip AR magic (!\n or !\r\n) + if (!tb_stream_seek(istream, 0)) { + return tb_false; + } + tb_uint8_t magic[8]; + if (!tb_stream_bread(istream, magic, 8)) { + return tb_false; + } + // check AR magic: ! + if (magic[0] != '!' || magic[1] != '<' || magic[2] != 'a' || + magic[3] != 'r' || magic[4] != 'c' || magic[5] != 'h' || + magic[6] != '>') { + return tb_false; + } + // handle both \n and \r\n + if (magic[7] == '\n') { + // Standard format: !\n + // already at position 8, ready to read first member + } else if (magic[7] == '\r') { + // Windows format: !\r\n, read one more byte + tb_uint8_t next_byte; + if (!tb_stream_bread(istream, &next_byte, 1) || next_byte != '\n') { + return tb_false; + } + } else { + return tb_false; + } + + tb_uint32_t result_count = 0; + tb_hize_t file_size = tb_stream_size(istream); + + // read archive members + while (tb_stream_offset(istream) < file_size) { + // read member header + xm_ar_header_t header; + if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { + break; + } + + // check magic + if (header.fmag[0] != '`' || header.fmag[1] != '\n') { + break; + } + + // parse file size + tb_int64_t member_size = xm_binutils_ar_parse_decimal(header.size, 10); + if (member_size < 0 || member_size == 0) { + break; + } + + // get member name (handle extended names) + tb_char_t member_name[256] = {0}; + tb_size_t name_len = 0; + if (header.name[0] == '/' && header.name[1] >= '0' && header.name[1] <= '9') { + // extended name reference: /123 (offset in string table) + // For simplicity, skip extended names for now + // TODO: implement extended name support + } else { + // regular name + for (tb_size_t i = 0; i < 16 && header.name[i] != ' ' && header.name[i] != '/' && header.name[i] != '\0'; i++) { + member_name[name_len++] = header.name[i]; + } + // remove trailing '/' + if (name_len > 0 && member_name[name_len - 1] == '/') { + name_len--; + } + member_name[name_len] = '\0'; + } + + // skip special members (symbol table, string table, etc.) + if (member_name[0] == '/' || name_len == 0) { + // skip to next member (align to 2 bytes) + tb_hize_t current_pos = tb_stream_offset(istream); + tb_hize_t next_pos = current_pos + member_size; + if (next_pos & 1) { + next_pos++; // align to 2 bytes + } + if (!tb_stream_seek(istream, next_pos)) { + break; + } + continue; + } + + // read member data + tb_byte_t *member_data = (tb_byte_t*)tb_malloc((tb_size_t)member_size); + if (!member_data) { + break; + } + + if (!tb_stream_bread(istream, member_data, (tb_size_t)member_size)) { + tb_free(member_data); + break; + } + + // create stream from member data + tb_stream_ref_t member_stream = tb_stream_init_from_data(member_data, (tb_size_t)member_size); + if (!member_stream) { + tb_free(member_data); + break; + } + + if (!tb_stream_open(member_stream)) { + tb_free(member_data); + tb_stream_exit(member_stream); + break; + } + + // detect member format and read symbols + tb_int_t member_format = xm_binutils_detect_format(member_stream); + if (member_format == XM_BINUTILS_FORMAT_COFF) { + // create temporary table for member symbols + lua_newtable(lua); + if (xm_binutils_coff_read_symbols(member_stream, lua)) { + // merge symbols into result table + // Stack: result_table(-2), member_table(-1) + tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); + for (tb_int_t i = 1; i <= member_sym_count; i++) { + lua_pushinteger(lua, i); + lua_gettable(lua, -2); // get member_table[i] + // Stack: result_table(-3), member_table(-2), symbol(-1) + if (lua_istable(lua, -1)) { + lua_pushinteger(lua, result_count + 1); + lua_pushvalue(lua, -2); // copy symbol + // Stack: result_table(-5), member_table(-4), symbol(-3), index(-2), symbol(-1) + lua_settable(lua, -5); // result_table[index] = symbol + // Stack: result_table(-3), member_table(-2) + result_count++; + } + lua_pop(lua, 1); // remove symbol + // Stack: result_table(-2), member_table(-1) + } + } + lua_pop(lua, 1); // remove temporary table + // Stack: result_table(-1) + } else if (member_format == XM_BINUTILS_FORMAT_ELF) { + // create temporary table for member symbols + lua_newtable(lua); + if (xm_binutils_elf_read_symbols(member_stream, lua)) { + // merge symbols into result table + tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); + for (tb_int_t i = 1; i <= member_sym_count; i++) { + lua_pushinteger(lua, i); + lua_gettable(lua, -2); + if (lua_istable(lua, -1)) { + lua_pushinteger(lua, result_count + 1); + lua_pushvalue(lua, -2); + lua_settable(lua, -5); + result_count++; + } + lua_pop(lua, 1); + } + } + lua_pop(lua, 1); // remove temporary table + } else if (member_format == XM_BINUTILS_FORMAT_MACHO) { + // create temporary table for member symbols + lua_newtable(lua); + if (xm_binutils_macho_read_symbols(member_stream, lua)) { + // merge symbols into result table + tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); + for (tb_int_t i = 1; i <= member_sym_count; i++) { + lua_pushinteger(lua, i); + lua_gettable(lua, -2); + if (lua_istable(lua, -1)) { + lua_pushinteger(lua, result_count + 1); + lua_pushvalue(lua, -2); + lua_settable(lua, -5); + result_count++; + } + lua_pop(lua, 1); + } + } + lua_pop(lua, 1); // remove temporary table + } + + // cleanup + tb_stream_clos(member_stream); + tb_stream_exit(member_stream); + // Note: tb_stream_init_from_data sets bref=true, so it only references the data. + // We need to free the data after the stream is closed. + tb_free(member_data); + + // align to 2 bytes for next member + tb_hize_t current_pos = tb_stream_offset(istream); + if (current_pos & 1) { + tb_byte_t padding; + if (!tb_stream_bread(istream, &padding, 1)) { + break; + } + } + } + + return tb_true; +} + diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h index b9732a4ec..587d724df 100644 --- a/core/src/xmake/binutils/coff/prefix.h +++ b/core/src/xmake/binutils/coff/prefix.h @@ -99,6 +99,7 @@ typedef struct __xm_coff_symbol_tail_t { tb_uint8_t scl; tb_uint8_t naux; } __tb_packed__ xm_coff_symbol_tail_t; +#include "tbox/prefix/packed.h" /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index 6eba9ac51..d79fe3e11 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -137,6 +137,7 @@ typedef struct __xm_elf64_symbol_t { tb_uint64_t st_value; tb_uint64_t st_size; } __tb_packed__ xm_elf64_symbol_t; +#include "tbox/prefix/packed.h" /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index 2179ac344..bc7efb6d2 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -182,6 +182,7 @@ typedef struct __xm_macho_nlist_64_t { tb_uint16_t desc; tb_uint64_t value; } __tb_packed__ xm_macho_nlist_64_t; +#include "tbox/prefix/packed.h" /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h index 048e74ee2..1733fc7b2 100644 --- a/core/src/xmake/binutils/prefix.h +++ b/core/src/xmake/binutils/prefix.h @@ -29,10 +29,11 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * 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 +#define XM_BINUTILS_FORMAT_COFF 1 +#define XM_BINUTILS_FORMAT_ELF 2 +#define XM_BINUTILS_FORMAT_MACHO 3 +#define XM_BINUTILS_FORMAT_AR 4 +#define XM_BINUTILS_FORMAT_UNKNOWN 0 /* COFF machine types (for format detection) */ #define XM_BINUTILS_COFF_MACHINE_I386 0x014c @@ -73,11 +74,22 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t * * @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 + * XM_BINUTILS_FORMAT_AR, 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); + // check AR archive format first (!\n) + tb_uint8_t ar_magic[8]; + if (xm_binutils_read_magic(istream, ar_magic, 8)) { + if (ar_magic[0] == '!' && ar_magic[1] == '<' && ar_magic[2] == 'a' && + ar_magic[3] == 'r' && ar_magic[4] == 'c' && ar_magic[5] == 'h' && + (ar_magic[6] == '>' || ar_magic[6] == '\n') && + (ar_magic[7] == '\n' || ar_magic[7] == '\r')) { + return XM_BINUTILS_FORMAT_AR; + } + } + // read magic bytes tb_uint8_t magic[4]; if (!xm_binutils_read_magic(istream, magic, 4)) { diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c index ae261782f..73908a562 100644 --- a/core/src/xmake/binutils/readsyms.c +++ b/core/src/xmake/binutils/readsyms.c @@ -39,6 +39,7 @@ 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); +extern tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, lua_State *lua); /* ////////////////////////////////////////////////////////////////////////////////////// * implementation @@ -81,7 +82,14 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) { } // read symbols based on format - if (format == XM_BINUTILS_FORMAT_COFF) { + if (format == XM_BINUTILS_FORMAT_AR) { + // AR archive (.a or .lib) + if (!xm_binutils_ar_read_symbols(istream, lua)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "readsyms: read AR archive symbols failed"); + break; + } + } else if (format == XM_BINUTILS_FORMAT_COFF) { // COFF if (!xm_binutils_coff_read_symbols(istream, lua)) { lua_pushboolean(lua, tb_false); diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh index c65c5c1ef..e40051419 100755 --- a/core/src/xmake/xmake.sh +++ b/core/src/xmake/xmake.sh @@ -75,6 +75,7 @@ target "xmake" add_files "binutils/coff/*.c" add_files "binutils/macho/*.c" add_files "binutils/elf/*.c" + add_files "binutils/ar/*.c" add_files "thread/*.c" if is_plat "mingw"; then add_files "winos/*.c" -- cgit v1.3.1 From d048344bb19e409fe830bf888a4d604700a4ab5b Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 22:30:56 +0800 Subject: add ar readsyms stub --- core/src/xmake/binutils/ar/readsyms.c | 205 +--------------------------------- 1 file changed, 3 insertions(+), 202 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c index 7f5f0b077..6d738565e 100644 --- a/core/src/xmake/binutils/ar/readsyms.c +++ b/core/src/xmake/binutils/ar/readsyms.c @@ -19,12 +19,6 @@ * */ -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "ar_readsyms" -#define TB_TRACE_MODULE_DEBUG (0) - /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ @@ -43,200 +37,7 @@ tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, lua_State *lua) { tb_assert_and_check_return_val(istream && lua, tb_false); - // create result table - lua_newtable(lua); - - // skip AR magic (!\n or !\r\n) - if (!tb_stream_seek(istream, 0)) { - return tb_false; - } - tb_uint8_t magic[8]; - if (!tb_stream_bread(istream, magic, 8)) { - return tb_false; - } - // check AR magic: ! - if (magic[0] != '!' || magic[1] != '<' || magic[2] != 'a' || - magic[3] != 'r' || magic[4] != 'c' || magic[5] != 'h' || - magic[6] != '>') { - return tb_false; - } - // handle both \n and \r\n - if (magic[7] == '\n') { - // Standard format: !\n - // already at position 8, ready to read first member - } else if (magic[7] == '\r') { - // Windows format: !\r\n, read one more byte - tb_uint8_t next_byte; - if (!tb_stream_bread(istream, &next_byte, 1) || next_byte != '\n') { - return tb_false; - } - } else { - return tb_false; - } - - tb_uint32_t result_count = 0; - tb_hize_t file_size = tb_stream_size(istream); - - // read archive members - while (tb_stream_offset(istream) < file_size) { - // read member header - xm_ar_header_t header; - if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { - break; - } - - // check magic - if (header.fmag[0] != '`' || header.fmag[1] != '\n') { - break; - } - - // parse file size - tb_int64_t member_size = xm_binutils_ar_parse_decimal(header.size, 10); - if (member_size < 0 || member_size == 0) { - break; - } - - // get member name (handle extended names) - tb_char_t member_name[256] = {0}; - tb_size_t name_len = 0; - if (header.name[0] == '/' && header.name[1] >= '0' && header.name[1] <= '9') { - // extended name reference: /123 (offset in string table) - // For simplicity, skip extended names for now - // TODO: implement extended name support - } else { - // regular name - for (tb_size_t i = 0; i < 16 && header.name[i] != ' ' && header.name[i] != '/' && header.name[i] != '\0'; i++) { - member_name[name_len++] = header.name[i]; - } - // remove trailing '/' - if (name_len > 0 && member_name[name_len - 1] == '/') { - name_len--; - } - member_name[name_len] = '\0'; - } - - // skip special members (symbol table, string table, etc.) - if (member_name[0] == '/' || name_len == 0) { - // skip to next member (align to 2 bytes) - tb_hize_t current_pos = tb_stream_offset(istream); - tb_hize_t next_pos = current_pos + member_size; - if (next_pos & 1) { - next_pos++; // align to 2 bytes - } - if (!tb_stream_seek(istream, next_pos)) { - break; - } - continue; - } - - // read member data - tb_byte_t *member_data = (tb_byte_t*)tb_malloc((tb_size_t)member_size); - if (!member_data) { - break; - } - - if (!tb_stream_bread(istream, member_data, (tb_size_t)member_size)) { - tb_free(member_data); - break; - } - - // create stream from member data - tb_stream_ref_t member_stream = tb_stream_init_from_data(member_data, (tb_size_t)member_size); - if (!member_stream) { - tb_free(member_data); - break; - } - - if (!tb_stream_open(member_stream)) { - tb_free(member_data); - tb_stream_exit(member_stream); - break; - } - - // detect member format and read symbols - tb_int_t member_format = xm_binutils_detect_format(member_stream); - if (member_format == XM_BINUTILS_FORMAT_COFF) { - // create temporary table for member symbols - lua_newtable(lua); - if (xm_binutils_coff_read_symbols(member_stream, lua)) { - // merge symbols into result table - // Stack: result_table(-2), member_table(-1) - tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); - for (tb_int_t i = 1; i <= member_sym_count; i++) { - lua_pushinteger(lua, i); - lua_gettable(lua, -2); // get member_table[i] - // Stack: result_table(-3), member_table(-2), symbol(-1) - if (lua_istable(lua, -1)) { - lua_pushinteger(lua, result_count + 1); - lua_pushvalue(lua, -2); // copy symbol - // Stack: result_table(-5), member_table(-4), symbol(-3), index(-2), symbol(-1) - lua_settable(lua, -5); // result_table[index] = symbol - // Stack: result_table(-3), member_table(-2) - result_count++; - } - lua_pop(lua, 1); // remove symbol - // Stack: result_table(-2), member_table(-1) - } - } - lua_pop(lua, 1); // remove temporary table - // Stack: result_table(-1) - } else if (member_format == XM_BINUTILS_FORMAT_ELF) { - // create temporary table for member symbols - lua_newtable(lua); - if (xm_binutils_elf_read_symbols(member_stream, lua)) { - // merge symbols into result table - tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); - for (tb_int_t i = 1; i <= member_sym_count; i++) { - lua_pushinteger(lua, i); - lua_gettable(lua, -2); - if (lua_istable(lua, -1)) { - lua_pushinteger(lua, result_count + 1); - lua_pushvalue(lua, -2); - lua_settable(lua, -5); - result_count++; - } - lua_pop(lua, 1); - } - } - lua_pop(lua, 1); // remove temporary table - } else if (member_format == XM_BINUTILS_FORMAT_MACHO) { - // create temporary table for member symbols - lua_newtable(lua); - if (xm_binutils_macho_read_symbols(member_stream, lua)) { - // merge symbols into result table - tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1); - for (tb_int_t i = 1; i <= member_sym_count; i++) { - lua_pushinteger(lua, i); - lua_gettable(lua, -2); - if (lua_istable(lua, -1)) { - lua_pushinteger(lua, result_count + 1); - lua_pushvalue(lua, -2); - lua_settable(lua, -5); - result_count++; - } - lua_pop(lua, 1); - } - } - lua_pop(lua, 1); // remove temporary table - } - - // cleanup - tb_stream_clos(member_stream); - tb_stream_exit(member_stream); - // Note: tb_stream_init_from_data sets bref=true, so it only references the data. - // We need to free the data after the stream is closed. - tb_free(member_data); - - // align to 2 bytes for next member - tb_hize_t current_pos = tb_stream_offset(istream); - if (current_pos & 1) { - tb_byte_t padding; - if (!tb_stream_bread(istream, &padding, 1)) { - break; - } - } - } - - return tb_true; + // TODO: implement AR archive symbol reading + // This feature is not yet implemented + return tb_false; } - -- cgit v1.3.1 From 5931e48282914758fd7c4eeaf9ca05a16521692b Mon Sep 17 00:00:00 2001 From: ruki Date: Tue, 9 Dec 2025 22:34:36 +0800 Subject: fix compile error --- core/src/xmake/binutils/elf/prefix.h | 2 +- xmake/core/base/binutils.lua | 13 ++++++++++++- xmake/modules/utils/binary/bin2obj.lua | 14 +++++++------- 3 files changed, 20 insertions(+), 9 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h index d79fe3e11..bd3cc9129 100644 --- a/core/src/xmake/binutils/elf/prefix.h +++ b/core/src/xmake/binutils/elf/prefix.h @@ -278,7 +278,7 @@ static __tb_inline__ tb_bool_t xm_binutils_elf_is_64bit(tb_char_t const *arch) { * @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) { +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); diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua index 995f0d0a7..77b17dc92 100644 --- a/xmake/core/base/binutils.lua +++ b/xmake/core/base/binutils.lua @@ -21,6 +21,9 @@ -- define module local binutils = binutils or {} +-- load modules +local os = require("base/os") + -- save original interfaces binutils._bin2c = binutils._bin2c or binutils.bin2c binutils._bin2coff = binutils._bin2coff or binutils.bin2coff @@ -55,7 +58,15 @@ function binutils.bin2obj(binaryfile, outputfile, opt) opt = opt or {} local format = opt.format if not format then - return nil, "bin2obj: format is required (coff, elf, or macho)" + -- auto-detect format based on host platform + local host = os.host() + if host == "windows" or host == "mingw" or host == "msys" or host == "cygwin" then + format = "coff" + elseif host == "macosx" or host == "iphoneos" or host == "watchos" or host == "appletvos" then + format = "macho" + else + format = "elf" + end end format = format:lower() diff --git a/xmake/modules/utils/binary/bin2obj.lua b/xmake/modules/utils/binary/bin2obj.lua index 7870e2544..95e705ca6 100644 --- a/xmake/modules/utils/binary/bin2obj.lua +++ b/xmake/modules/utils/binary/bin2obj.lua @@ -47,17 +47,17 @@ function _do_bin2obj(binarypath, outputpath, opt) local basename = filename:gsub("%.", "_") opt.basename = basename - -- get format (default: coff) - local format = opt.format or "coff" - format = format:lower() - -- validate format - if format ~= "coff" and format ~= "elf" and format ~= "macho" then - raise("bin2obj: unsupported format '%s' (supported: coff, elf, macho)", format) + local format = opt.format + if format then + format = format:lower() + if format ~= "coff" and format ~= "elf" and format ~= "macho" then + raise("bin2obj: unsupported format '%s' (supported: coff, elf, macho)", format) + end end -- trace - print("converting binary file %s to %s object file %s ..", binarypath, format, outputpath) + print("converting binary file %s to %s object file %s ..", binarypath, format or "coff", outputpath) -- do conversion binutils.bin2obj(binarypath, outputpath, opt) -- cgit v1.3.1 From 628394671673da0f8bba98ff64f61fc405b63206 Mon Sep 17 00:00:00 2001 From: ruki Date: Wed, 10 Dec 2025 00:31:09 +0800 Subject: fix coff readsyms --- core/src/xmake/binutils/coff/readsyms.c | 87 ++++++++++++--------------------- xmake/core/sandbox/modules/utils.lua | 2 +- 2 files changed, 33 insertions(+), 56 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c index 9a10eba9d..41ca5ee47 100644 --- a/core/src/xmake/binutils/coff/readsyms.c +++ b/core/src/xmake/binutils/coff/readsyms.c @@ -91,73 +91,50 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) // read symbol xm_coff_symbol_t sym; if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) { + if (sections) tb_free(sections); return tb_false; } - // get symbol name - tb_char_t name[256]; + tb_bool_t skip = tb_false; + tb_char_t name[256] = {0}; 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; - } - - // skip internal symbols (starting with .) - if (name[0] == '.') { - 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; + skip = tb_true; + } else if (name[0] == '.') { + skip = tb_true; + } else if (tb_strchr(name, '$') != tb_null || + tb_strstr(name, ".constprop") != tb_null || + tb_strstr(name, ".startup") != tb_null || + tb_strstr(name, "ta$") != tb_null) { + skip = tb_true; } - // skip compiler-generated symbols (containing $ or .constprop or .startup, etc.) - if (tb_strchr(name, '$') != tb_null || - tb_strstr(name, ".constprop") != tb_null || - tb_strstr(name, ".startup") != tb_null || - tb_strstr(name, "ta$") != tb_null) { - 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; + if (!skip) { + // 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); + + // type (nm-style: T/t/D/d/B/b/U) + tb_char_t type_char = xm_binutils_coff_get_symbol_type_char(sym.scl, sym.sect, sections, header.nsects); + tb_char_t type_str[2] = {type_char, '\0'}; + lua_pushstring(lua, "type"); + lua_pushstring(lua, type_str); + lua_settable(lua, -3); + + lua_settable(lua, -3); + sym_count++; } - // 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); - - // type (nm-style: T/t/D/d/B/b/U) - tb_char_t type_char = xm_binutils_coff_get_symbol_type_char(sym.scl, sym.sect, sections, header.nsects); - tb_char_t type_str[2] = {type_char, '\0'}; - lua_pushstring(lua, "type"); - lua_pushstring(lua, type_str); - lua_settable(lua, -3); - - lua_settable(lua, -3); - - sym_count++; + // skip to the next symbol, including auxiliary entries 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)) { + if (sections) tb_free(sections); return tb_false; } } diff --git a/xmake/core/sandbox/modules/utils.lua b/xmake/core/sandbox/modules/utils.lua index 1be9c7c08..df90492be 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 -- cgit v1.3.1 From 4f042fbca4fa2417a3ea69925df49e9cb30915ac Mon Sep 17 00:00:00 2001 From: ruki Date: Wed, 10 Dec 2025 00:32:41 +0800 Subject: improve readsyms for macho --- core/src/xmake/binutils/macho/prefix.h | 6 +- core/src/xmake/binutils/macho/readsyms.c | 108 +++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 13 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index bc7efb6d2..fc1023356 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -29,8 +29,10 @@ /* ////////////////////////////////////////////////////////////////////////////////////// * macros */ -#define XM_MACHO_MAGIC_32 0xfeedface -#define XM_MACHO_MAGIC_64 0xfeedfacf +#define XM_MACHO_MAGIC_32 0xfeedface /* MH_MAGIC - little endian */ +#define XM_MACHO_MAGIC_64 0xfeedfacf /* MH_MAGIC_64 - little endian */ +#define XM_MACHO_MAGIC_32_BE 0xcefaedfe /* MH_CIGAM - big endian */ +#define XM_MACHO_MAGIC_64_BE 0xcffaedfe /* MH_CIGAM_64 - big endian */ #define XM_MACHO_MAGIC_FAT 0xcafebabe #define XM_MACHO_CPU_TYPE_X86 7 diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c index dbe7925a4..f1bb9511e 100644 --- a/core/src/xmake/binutils/macho/readsyms.c +++ b/core/src/xmake/binutils/macho/readsyms.c @@ -29,12 +29,70 @@ * includes */ #include "prefix.h" +#include "tbox/utils/bits.h" /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ -tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *lua) { +/* 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, lua_State *lua, tb_bool_t swap_bytes) { tb_assert_and_check_return_val(istream && lua, tb_false); // read Mach-O header @@ -45,6 +103,7 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { return tb_false; } + xm_binutils_macho_swap_header_32(&header, swap_bytes); // find LC_SYMTAB command xm_macho_symtab_command_t symtab_cmd; @@ -98,6 +157,7 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * if (!tb_stream_bread(istream, (tb_byte_t*)&nlist, sizeof(nlist))) { return tb_false; } + xm_binutils_macho_swap_nlist_32(&nlist, swap_bytes); // skip NULL symbols if (nlist.strx == 0) { @@ -138,7 +198,7 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State * return tb_true; } -tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *lua) { +tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *lua, tb_bool_t swap_bytes) { tb_assert_and_check_return_val(istream && lua, tb_false); // read Mach-O header @@ -149,6 +209,7 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) { return tb_false; } + xm_binutils_macho_swap_header_64(&header, swap_bytes); // find LC_SYMTAB command xm_macho_symtab_command_t symtab_cmd; @@ -169,6 +230,11 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * return tb_false; } + if (swap_bytes) { + cmd = tb_bits_swap_u32(cmd); + cmdsize = tb_bits_swap_u32(cmdsize); + } + if (cmd == XM_MACHO_LC_SYMTAB) { if (!tb_stream_seek(istream, offset)) { return tb_false; @@ -176,6 +242,7 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * if (!tb_stream_bread(istream, (tb_byte_t*)&symtab_cmd, sizeof(symtab_cmd))) { return tb_false; } + xm_binutils_macho_swap_symtab_command(&symtab_cmd, swap_bytes); found_symtab = tb_true; break; } @@ -202,6 +269,7 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State * if (!tb_stream_bread(istream, (tb_byte_t*)&nlist, sizeof(nlist))) { return tb_false; } + xm_binutils_macho_swap_nlist_64(&nlist, swap_bytes); // skip NULL symbols if (nlist.strx == 0) { @@ -251,16 +319,34 @@ tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua 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); + // check magic bytes directly (byte order independent) + tb_bool_t swap_bytes = tb_false; + tb_bool_t is_32bit = tb_false; + tb_bool_t is_64bit = tb_false; + + // check for little-endian magic numbers + if (magic_bytes[0] == 0xce && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { + is_32bit = tb_true; + swap_bytes = tb_false; + } else if (magic_bytes[0] == 0xcf && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { + is_64bit = tb_true; + swap_bytes = tb_false; + } + // check for big-endian magic numbers + else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xce) { + is_32bit = tb_true; + swap_bytes = tb_true; + } else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xcf) { + is_64bit = tb_true; + swap_bytes = tb_true; + } else { + return tb_false; + } - 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); + if (is_32bit) { + return xm_binutils_macho_read_symbols_32(istream, lua, swap_bytes); + } else if (is_64bit) { + return xm_binutils_macho_read_symbols_64(istream, lua, swap_bytes); } return tb_false; -- cgit v1.3.1 From 19859fd57908ea9b08b8b6baa426051d2e7c7814 Mon Sep 17 00:00:00 2001 From: ruki Date: Wed, 10 Dec 2025 00:36:41 +0800 Subject: improve macho magic --- core/src/xmake/binutils/macho/prefix.h | 34 ++++++++++++++++++++++++++++++++ core/src/xmake/binutils/macho/readsyms.c | 27 ++++--------------------- 2 files changed, 38 insertions(+), 23 deletions(-) (limited to 'core/src') diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h index fc1023356..f3c6e2302 100644 --- a/core/src/xmake/binutils/macho/prefix.h +++ b/core/src/xmake/binutils/macho/prefix.h @@ -325,6 +325,40 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const * readsyms inline implementation */ +/* detect Mach-O format from magic bytes + * + * @param magic_bytes the magic bytes (4 bytes) + * @param is_32bit output: tb_true if 32-bit, tb_false if 64-bit + * @param swap_bytes output: tb_true if byte-swapping needed (big-endian), tb_false otherwise + * @return tb_true if valid Mach-O magic, tb_false otherwise + */ +static __tb_inline__ tb_bool_t xm_binutils_macho_detect_format(tb_uint8_t const *magic_bytes, tb_bool_t *is_32bit, tb_bool_t *swap_bytes) { + tb_assert_and_check_return_val(magic_bytes && is_32bit && swap_bytes, tb_false); + + // check for little-endian magic numbers + if (magic_bytes[0] == 0xce && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { + *is_32bit = tb_true; + *swap_bytes = tb_false; + return tb_true; + } else if (magic_bytes[0] == 0xcf && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { + *is_32bit = tb_false; + *swap_bytes = tb_false; + return tb_true; + } + // check for big-endian magic numbers + else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xce) { + *is_32bit = tb_true; + *swap_bytes = tb_true; + return tb_true; + } else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xcf) { + *is_32bit = tb_false; + *swap_bytes = tb_true; + return tb_true; + } + + return tb_false; +} + /* read string from Mach-O string table * * @param istream the input stream diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c index f1bb9511e..fac925d22 100644 --- a/core/src/xmake/binutils/macho/readsyms.c +++ b/core/src/xmake/binutils/macho/readsyms.c @@ -319,37 +319,18 @@ tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua return tb_false; } - // check magic bytes directly (byte order independent) - tb_bool_t swap_bytes = tb_false; + // detect Mach-O format tb_bool_t is_32bit = tb_false; - tb_bool_t is_64bit = tb_false; - - // check for little-endian magic numbers - if (magic_bytes[0] == 0xce && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { - is_32bit = tb_true; - swap_bytes = tb_false; - } else if (magic_bytes[0] == 0xcf && magic_bytes[1] == 0xfa && magic_bytes[2] == 0xed && magic_bytes[3] == 0xfe) { - is_64bit = tb_true; - swap_bytes = tb_false; - } - // check for big-endian magic numbers - else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xce) { - is_32bit = tb_true; - swap_bytes = tb_true; - } else if (magic_bytes[0] == 0xfe && magic_bytes[1] == 0xed && magic_bytes[2] == 0xfa && magic_bytes[3] == 0xcf) { - is_64bit = tb_true; - swap_bytes = tb_true; - } else { + tb_bool_t swap_bytes = tb_false; + if (!xm_binutils_macho_detect_format(magic_bytes, &is_32bit, &swap_bytes)) { return tb_false; } if (is_32bit) { return xm_binutils_macho_read_symbols_32(istream, lua, swap_bytes); - } else if (is_64bit) { + } else { return xm_binutils_macho_read_symbols_64(istream, lua, swap_bytes); } - - return tb_false; } -- cgit v1.3.1