summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-09 22:09:11 +0800
committerGitHub <[email protected]>2025-12-09 22:09:11 +0800
commitf2dcbc2e1450c84ed192e57c7f8b03e51717d71e (patch)
tree6abc959ce5b5ea6a19823021bb00ae364eb8e66b /core/src
parent35d91e1636263028b8431cea85b0fd7854392a8a (diff)
parent19859fd57908ea9b08b8b6baa426051d2e7c7814 (diff)
Merge pull request #7109 from xmake-io/binutils
Improve binutils to readsyms from binary file
Diffstat (limited to 'core/src')
-rw-r--r--core/src/xmake/binutils/ar/prefix.h82
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c43
-rw-r--r--core/src/xmake/binutils/coff/bin2coff.c (renamed from core/src/xmake/binutils/bin2coff.c)131
-rw-r--r--core/src/xmake/binutils/coff/prefix.h321
-rw-r--r--core/src/xmake/binutils/coff/readsyms.c148
-rw-r--r--core/src/xmake/binutils/elf/bin2elf.c (renamed from core/src/xmake/binutils/bin2elf.c)233
-rw-r--r--core/src/xmake/binutils/elf/prefix.h354
-rw-r--r--core/src/xmake/binutils/elf/readsyms.c323
-rw-r--r--core/src/xmake/binutils/macho/bin2macho.c (renamed from core/src/xmake/binutils/bin2macho.c)289
-rw-r--r--core/src/xmake/binutils/macho/prefix.h448
-rw-r--r--core/src/xmake/binutils/macho/readsyms.c336
-rw-r--r--core/src/xmake/binutils/prefix.h124
-rw-r--r--core/src/xmake/binutils/readsyms.c130
-rw-r--r--core/src/xmake/engine.c2
-rwxr-xr-xcore/src/xmake/xmake.sh4
15 files changed, 2329 insertions, 639 deletions
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..6d738565e
--- /dev/null
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -0,0 +1,43 @@
+/*!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
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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);
+
+ // TODO: implement AR archive symbol reading
+ // This feature is not yet implemented
+ return tb_false;
+}
diff --git a/core/src/xmake/binutils/bin2coff.c b/core/src/xmake/binutils/coff/bin2coff.c
index 9a095a2cd..2a09cd504 100644
--- a/core/src/xmake/binutils/bin2coff.c
+++ b/core/src/xmake/binutils/coff/bin2coff.c
@@ -31,127 +31,8 @@
#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,
@@ -176,7 +57,7 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
}
// determine architecture for symbol prefix adjustment
- tb_uint16_t machine = xm_binutils_bin2coff_get_machine(arch);
+ 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
@@ -290,7 +171,7 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
// align to 4 bytes
if (section_data_padding > 0) {
- xm_binutils_bin2coff_write_padding(ostream, section_data_padding);
+ xm_binutils_coff_write_padding(ostream, section_data_padding);
}
// write symbol table
@@ -319,7 +200,7 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
// 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_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;
@@ -332,7 +213,7 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
}
// symbol 2: _binary_xxx_end (or __binary_xxx_end for i386)
- xm_binutils_bin2coff_write_symbol_name(ostream, symbol_end, &strtab_offset);
+ 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;
@@ -351,12 +232,12 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
return tb_false;
}
if (start_len > 8) {
- xm_binutils_bin2coff_write_string(ostream, symbol_start, start_len);
+ 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_bin2coff_write_string(ostream, symbol_end, end_len);
+ xm_binutils_coff_write_string(ostream, symbol_end, end_len);
tb_byte_t null = 0;
tb_stream_bwrit(ostream, &null, 1);
}
diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h
new file mode 100644
index 000000000..587d724df
--- /dev/null
+++ b/core/src/xmake/binutils/coff/prefix.h
@@ -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 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
+
+// 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
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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
+ }
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * readsyms inline implementation
+ */
+
+/* read string from COFF string table
+ *
+ * @param istream the input stream
+ * @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);
+
+ // 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)) {
+ 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 (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)
+ // So we use strtab_offset + offset directly
+ if (!tb_stream_seek(istream, strtab_offset + offset)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+
+ // read string
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ // restore position
+ 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 character (nm-style) from COFF symbol
+ *
+ * @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, xm_coff_section_t const *sections, tb_uint16_t nsects) {
+ // undefined symbol
+ 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
+ // 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
+ }
+
+ 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
new file mode 100644
index 000000000..41ca5ee47
--- /dev/null
+++ b/core/src/xmake/binutils/coff/readsyms.c
@@ -0,0 +1,148 @@
+/*!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 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*)&sections[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;
+ }
+
+ 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))) {
+ if (sections) tb_free(sections);
+ return tb_false;
+ }
+
+ 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 = 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;
+ }
+
+ 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++;
+ }
+
+ // skip to the next symbol, including auxiliary entries
+ sym_index++;
+ if (sym.naux > 0) {
+ sym_index += sym.naux;
+ if (!tb_stream_seek(istream, tb_stream_offset(istream) + sym.naux * 18)) {
+ if (sections) tb_free(sections);
+ return tb_false;
+ }
+ }
+ }
+
+ if (sections) {
+ tb_free(sections);
+ }
+
+ return tb_true;
+}
diff --git a/core/src/xmake/binutils/bin2elf.c b/core/src/xmake/binutils/elf/bin2elf.c
index a536143c4..e6a9e7998 100644
--- a/core/src/xmake/binutils/bin2elf.c
+++ b/core/src/xmake/binutils/elf/bin2elf.c
@@ -31,235 +31,8 @@
#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,
@@ -352,7 +125,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
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_machine = xm_binutils_elf_get_machine(arch);
header.e_version = 1;
header.e_shoff = section_headers_ofs;
header.e_ehsize = header_size;
@@ -656,7 +429,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
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_machine = xm_binutils_elf_get_machine(arch);
header.e_version = 1;
header.e_shoff = section_headers_ofs;
header.e_ehsize = header_size;
@@ -919,7 +692,7 @@ tb_int_t xm_binutils_bin2elf(lua_State *lua) {
}
// choose 32-bit or 64-bit ELF based on architecture
- tb_bool_t is_64bit = xm_binutils_bin2elf_is_64bit(arch);
+ 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);
diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h
new file mode 100644
index 000000000..bd3cc9129
--- /dev/null
+++ b/core/src/xmake/binutils/elf/prefix.h
@@ -0,0 +1,354 @@
+/*!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;
+#include "tbox/prefix/packed.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * readsyms inline implementation
+ */
+
+/* read string from ELF string table
+ *
+ * @param istream the input stream
+ * @param strtab_offset the string table offset
+ * @param offset the string offset
+ * @return the string (static buffer, valid until next call)
+ */
+static __tb_inline__ tb_bool_t xm_binutils_elf_read_string(tb_stream_ref_t istream, tb_uint64_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) {
+ tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false);
+
+ tb_hize_t saved_pos = tb_stream_offset(istream);
+ if (!tb_stream_seek(istream, strtab_offset + offset)) {
+ return tb_false;
+ }
+
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ tb_stream_seek(istream, saved_pos);
+ return tb_true;
+}
+
+/* get symbol type character (nm-style) from ELF symbol
+ *
+ * @param st_info the symbol info byte
+ * @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 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;
+ 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
+ *
+ * @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..f0d0a1061
--- /dev/null
+++ b/core/src/xmake/binutils/elf/readsyms.c
@@ -0,0 +1,323 @@
+/*!A cross-platform build utility based on Lua
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2015-present, Xmake Open Source Community.
+ *
+ * @author ruki
+ * @file readsyms.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "readsyms_elf"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read ELF header
+ xm_elf32_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find .symtab section
+ xm_elf32_section_t symtab_section;
+ xm_elf32_section_t strtab_section;
+ tb_bool_t found_symtab = tb_false;
+ tb_bool_t found_strtab = tb_false;
+
+ if (!tb_stream_seek(istream, header.e_shoff)) {
+ return tb_false;
+ }
+
+ for (tb_uint16_t i = 0; i < header.e_shnum; i++) {
+ xm_elf32_section_t section;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&section, sizeof(section))) {
+ return tb_false;
+ }
+
+ if (section.sh_type == XM_ELF_SHT_SYMTAB) {
+ symtab_section = section;
+ found_symtab = tb_true;
+ } else if (section.sh_type == XM_ELF_SHT_STRTAB && section.sh_link == 0) {
+ // .strtab is linked from .symtab, but we need to find it
+ // check if this is the string table for symbols
+ if (found_symtab && symtab_section.sh_link == i) {
+ strtab_section = section;
+ found_strtab = tb_true;
+ }
+ }
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // find string table
+ if (!found_strtab && symtab_section.sh_link < header.e_shnum) {
+ if (!tb_stream_seek(istream, header.e_shoff + symtab_section.sh_link * sizeof(xm_elf32_section_t))) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) {
+ return tb_false;
+ }
+ found_strtab = tb_true;
+ }
+
+ if (!found_strtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ tb_uint32_t sym_count = symtab_section.sh_size / sizeof(xm_elf32_symbol_t);
+ if (!tb_stream_seek(istream, symtab_section.sh_offset)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < sym_count; i++) {
+ xm_elf32_symbol_t sym;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
+ return tb_false;
+ }
+
+ // skip NULL symbol
+ if (sym.st_name == 0 && sym.st_value == 0 && sym.st_size == 0) {
+ continue;
+ }
+
+ // 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 . 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;
+ }
+
+ // 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);
+
+ // 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, type_str);
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ result_count++;
+ }
+
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // read ELF header
+ xm_elf64_header_t header;
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // find .symtab section
+ xm_elf64_section_t symtab_section;
+ xm_elf64_section_t strtab_section;
+ tb_bool_t found_symtab = tb_false;
+ tb_bool_t found_strtab = tb_false;
+
+ if (!tb_stream_seek(istream, header.e_shoff)) {
+ return tb_false;
+ }
+
+ for (tb_uint16_t i = 0; i < header.e_shnum; i++) {
+ xm_elf64_section_t section;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&section, sizeof(section))) {
+ return tb_false;
+ }
+
+ if (section.sh_type == XM_ELF_SHT_SYMTAB) {
+ symtab_section = section;
+ found_symtab = tb_true;
+ } else if (section.sh_type == XM_ELF_SHT_STRTAB && section.sh_link == 0) {
+ if (found_symtab && symtab_section.sh_link == i) {
+ strtab_section = section;
+ found_strtab = tb_true;
+ }
+ }
+ }
+
+ if (!found_symtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // find string table
+ if (!found_strtab && symtab_section.sh_link < header.e_shnum) {
+ if (!tb_stream_seek(istream, header.e_shoff + symtab_section.sh_link * sizeof(xm_elf64_section_t))) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_section, sizeof(strtab_section))) {
+ return tb_false;
+ }
+ found_strtab = tb_true;
+ }
+
+ if (!found_strtab) {
+ lua_newtable(lua);
+ return tb_true;
+ }
+
+ // create result table
+ lua_newtable(lua);
+
+ // read symbols
+ tb_uint32_t sym_count = (tb_uint32_t)(symtab_section.sh_size / sizeof(xm_elf64_symbol_t));
+ if (!tb_stream_seek(istream, symtab_section.sh_offset)) {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ for (tb_uint32_t i = 0; i < sym_count; i++) {
+ xm_elf64_symbol_t sym;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
+ return tb_false;
+ }
+
+ // skip NULL symbol
+ if (sym.st_name == 0 && sym.st_value == 0 && sym.st_size == 0) {
+ continue;
+ }
+
+ // 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 . 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;
+ }
+
+ // 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);
+
+ // 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, type_str);
+ 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/bin2macho.c b/core/src/xmake/binutils/macho/bin2macho.c
index 45d244316..a9068625a 100644
--- a/core/src/xmake/binutils/bin2macho.c
+++ b/core/src/xmake/binutils/macho/bin2macho.c
@@ -31,267 +31,8 @@
#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,
@@ -354,10 +95,10 @@ static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream,
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_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_bin2macho_align(data_end_offset, 8);
+ 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;
@@ -366,14 +107,14 @@ static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream,
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);
+ 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_bin2macho_get_cputype(arch);
- header.cpusubtype = xm_binutils_bin2macho_get_cpusubtype(arch);
+ 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;
@@ -434,7 +175,7 @@ static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream,
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.platform = xm_binutils_macho_get_platform(plat);
build_version.minos = minos;
build_version.sdk = sdk;
build_version.ntools = 0;
@@ -599,10 +340,10 @@ static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream,
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_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_bin2macho_align(data_end_offset, 4);
+ 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;
@@ -611,14 +352,14 @@ static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream,
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);
+ 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_bin2macho_get_cputype(arch);
- header.cpusubtype = xm_binutils_bin2macho_get_cpusubtype(arch);
+ 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;
@@ -681,7 +422,7 @@ static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream,
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.platform = xm_binutils_macho_get_platform(plat);
build_version.minos = minos;
build_version.sdk = sdk;
build_version.ntools = 0;
@@ -794,7 +535,7 @@ static tb_bool_t xm_binutils_bin2macho_dump(tb_stream_ref_t istream,
tb_uint32_t minos,
tb_uint32_t sdk,
tb_bool_t zeroend) {
- if (xm_binutils_bin2macho_is_64bit(arch)) {
+ 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);
@@ -834,11 +575,11 @@ tb_int_t xm_binutils_bin2macho(lua_State *lua) {
// 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);
+ 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_bin2macho_parse_version(sdk_str);
+ tb_uint32_t sdk = xm_binutils_macho_parse_version(sdk_str);
// get zeroend (optional, default: false)
tb_bool_t zeroend = lua_toboolean(lua, 9);
diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h
new file mode 100644
index 000000000..f3c6e2302
--- /dev/null
+++ b/core/src/xmake/binutils/macho/prefix.h
@@ -0,0 +1,448 @@
+/*!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 /* 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
+#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"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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
+ * @param strtab_offset the string table offset
+ * @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
+ */
+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);
+
+ // 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;
+ }
+
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ tb_stream_seek(istream, saved_pos);
+ return tb_false;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ tb_stream_seek(istream, saved_pos);
+ return tb_true;
+}
+
+/* get symbol type character (nm-style) from Mach-O symbol
+ *
+ * @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 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;
+ 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
+ *
+ * @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..fac925d22
--- /dev/null
+++ b/core/src/xmake/binutils/macho/readsyms.c
@@ -0,0 +1,336 @@
+/*!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"
+#include "tbox/utils/bits.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+/* byte-swap Mach-O header fields if needed */
+static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_32(xm_macho_header_t *header, tb_bool_t swap) {
+ if (swap) {
+ header->magic = tb_bits_swap_u32(header->magic);
+ header->cputype = tb_bits_swap_u32(header->cputype);
+ header->cpusubtype = tb_bits_swap_u32(header->cpusubtype);
+ header->filetype = tb_bits_swap_u32(header->filetype);
+ header->ncmds = tb_bits_swap_u32(header->ncmds);
+ header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds);
+ header->flags = tb_bits_swap_u32(header->flags);
+ }
+}
+
+/* byte-swap Mach-O header 64 fields if needed */
+static __tb_inline__ tb_void_t xm_binutils_macho_swap_header_64(xm_macho_header_64_t *header, tb_bool_t swap) {
+ if (swap) {
+ header->magic = tb_bits_swap_u32(header->magic);
+ header->cputype = tb_bits_swap_u32(header->cputype);
+ header->cpusubtype = tb_bits_swap_u32(header->cpusubtype);
+ header->filetype = tb_bits_swap_u32(header->filetype);
+ header->ncmds = tb_bits_swap_u32(header->ncmds);
+ header->sizeofcmds = tb_bits_swap_u32(header->sizeofcmds);
+ header->flags = tb_bits_swap_u32(header->flags);
+ header->reserved = tb_bits_swap_u32(header->reserved);
+ }
+}
+
+/* byte-swap symtab command fields if needed */
+static __tb_inline__ tb_void_t xm_binutils_macho_swap_symtab_command(xm_macho_symtab_command_t *cmd, tb_bool_t swap) {
+ if (swap) {
+ cmd->cmd = tb_bits_swap_u32(cmd->cmd);
+ cmd->cmdsize = tb_bits_swap_u32(cmd->cmdsize);
+ cmd->symoff = tb_bits_swap_u32(cmd->symoff);
+ cmd->nsyms = tb_bits_swap_u32(cmd->nsyms);
+ cmd->stroff = tb_bits_swap_u32(cmd->stroff);
+ cmd->strsize = tb_bits_swap_u32(cmd->strsize);
+ }
+}
+
+/* byte-swap nlist 32 fields if needed */
+static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_32(xm_macho_nlist_t *nlist, tb_bool_t swap) {
+ if (swap) {
+ nlist->strx = tb_bits_swap_u32(nlist->strx);
+ nlist->desc = tb_bits_swap_u16(nlist->desc);
+ nlist->value = tb_bits_swap_u32(nlist->value);
+ }
+}
+
+/* byte-swap nlist 64 fields if needed */
+static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_64(xm_macho_nlist_64_t *nlist, tb_bool_t swap) {
+ if (swap) {
+ nlist->strx = tb_bits_swap_u32(nlist->strx);
+ nlist->desc = tb_bits_swap_u16(nlist->desc);
+ nlist->value = tb_bits_swap_u64(nlist->value);
+ }
+}
+
+tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *lua, tb_bool_t swap_bytes) {
+ 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;
+ }
+ xm_binutils_macho_swap_header_32(&header, swap_bytes);
+
+ // 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;
+ }
+ xm_binutils_macho_swap_nlist_32(&nlist, swap_bytes);
+
+ // 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;
+ }
+
+ // skip internal symbols (starting with .)
+ if (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);
+
+ // 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, type_str);
+ 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_bool_t swap_bytes) {
+ 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;
+ }
+ xm_binutils_macho_swap_header_64(&header, swap_bytes);
+
+ // 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 (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;
+ }
+ 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;
+ }
+
+ 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;
+ }
+ xm_binutils_macho_swap_nlist_64(&nlist, swap_bytes);
+
+ // 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;
+ }
+
+ // skip internal symbols (starting with .)
+ if (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);
+
+ // 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, type_str);
+ 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;
+ }
+
+ // detect Mach-O format
+ tb_bool_t is_32bit = tb_false;
+ 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 {
+ return xm_binutils_macho_read_symbols_64(istream, lua, swap_bytes);
+ }
+}
+
+
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index 739e220f7..1733fc7b2 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -27,9 +27,118 @@
#include "../prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
+ * macros
+ */
+#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
+#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_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 (!<arch>\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)) {
+ 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 +156,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..73908a562
--- /dev/null
+++ b/core/src/xmake/binutils/readsyms.c
@@ -0,0 +1,130 @@
+/*!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);
+extern tb_bool_t xm_binutils_ar_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_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);
+ 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/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh
index 6cdbb042d..e40051419 100755
--- a/core/src/xmake/xmake.sh
+++ b/core/src/xmake/xmake.sh
@@ -72,6 +72,10 @@ 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 "binutils/ar/*.c"
add_files "thread/*.c"
if is_plat "mingw"; then
add_files "winos/*.c"