summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-12 09:54:46 +0800
committerGitHub <[email protected]>2025-12-12 09:54:46 +0800
commit7544d22a944cb4702534a8fe54924d74a71d3178 (patch)
tree2df690db9f7ead09afde33fd321864c4d56b042a
parent2427bd350baa6331c5dfd62b2ab291737c7e5f49 (diff)
parente08e654aad4f829fcf1990dae32a14394055d96c (diff)
Merge pull request #7120 from xmake-io/ar
Add extractlib support in binutils
-rw-r--r--core/src/xmake/binutils/ar/extractlib.c230
-rw-r--r--core/src/xmake/binutils/ar/prefix.h162
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c399
-rw-r--r--core/src/xmake/binutils/coff/bin2coff.c6
-rw-r--r--core/src/xmake/binutils/coff/prefix.h25
-rw-r--r--core/src/xmake/binutils/coff/readsyms.c92
-rw-r--r--core/src/xmake/binutils/elf/bin2elf.c12
-rw-r--r--core/src/xmake/binutils/elf/readsyms.c50
-rw-r--r--core/src/xmake/binutils/extractlib.c148
-rw-r--r--core/src/xmake/binutils/macho/bin2macho.c12
-rw-r--r--core/src/xmake/binutils/macho/prefix.h6
-rw-r--r--core/src/xmake/binutils/macho/readsyms.c116
-rw-r--r--core/src/xmake/binutils/mslib/extractlib.c306
-rw-r--r--core/src/xmake/binutils/mslib/prefix.h72
-rw-r--r--core/src/xmake/binutils/mslib/readsyms.c402
-rw-r--r--core/src/xmake/binutils/prefix.h72
-rw-r--r--core/src/xmake/binutils/readsyms.c94
-rw-r--r--core/src/xmake/engine.c2
-rwxr-xr-xcore/src/xmake/xmake.sh1
-rw-r--r--temp_lua/lapi.c.objbin0 -> 98713 bytes
-rw-r--r--temp_lua/lauxlib.c.objbin0 -> 143996 bytes
-rw-r--r--temp_lua/lbaselib.c.objbin0 -> 120594 bytes
-rw-r--r--temp_lua/lcode.c.objbin0 -> 109013 bytes
-rw-r--r--temp_lua/lcorolib.c.objbin0 -> 92489 bytes
-rw-r--r--temp_lua/lctype.c.objbin0 -> 4121 bytes
-rw-r--r--temp_lua/ldblib.c.objbin0 -> 116461 bytes
-rw-r--r--temp_lua/ldebug.c.objbin0 -> 68613 bytes
-rw-r--r--temp_lua/ldo.c.objbin0 -> 68903 bytes
-rw-r--r--temp_lua/ldump.c.objbin0 -> 25539 bytes
-rw-r--r--temp_lua/lfunc.c.objbin0 -> 29146 bytes
-rw-r--r--temp_lua/lgc.c.objbin0 -> 163349 bytes
-rw-r--r--temp_lua/linit.c.objbin0 -> 82371 bytes
-rw-r--r--temp_lua/liolib.c.objbin0 -> 130873 bytes
-rw-r--r--temp_lua/llex.c.objbin0 -> 60112 bytes
-rw-r--r--temp_lua/lmathlib.c.objbin0 -> 119266 bytes
-rw-r--r--temp_lua/lmem.c.objbin0 -> 17664 bytes
-rw-r--r--temp_lua/loadlib.c.objbin0 -> 234645 bytes
-rw-r--r--temp_lua/lobject.c.objbin0 -> 132931 bytes
-rw-r--r--temp_lua/lopcodes.c.objbin0 -> 5290 bytes
-rw-r--r--temp_lua/loslib.c.objbin0 -> 110941 bytes
-rw-r--r--temp_lua/lparser.c.objbin0 -> 121736 bytes
-rw-r--r--temp_lua/lstate.c.objbin0 -> 44978 bytes
-rw-r--r--temp_lua/lstring.c.objbin0 -> 31889 bytes
-rw-r--r--temp_lua/lstrlib.c.objbin0 -> 188730 bytes
-rw-r--r--temp_lua/ltable.c.objbin0 -> 59139 bytes
-rw-r--r--temp_lua/ltablib.c.objbin0 -> 109876 bytes
-rw-r--r--temp_lua/ltests.c.objbin0 -> 93851 bytes
-rw-r--r--temp_lua/ltm.c.objbin0 -> 38474 bytes
-rw-r--r--temp_lua/lundump.c.objbin0 -> 39023 bytes
-rw-r--r--temp_lua/lutf8lib.c.objbin0 -> 99241 bytes
-rw-r--r--temp_lua/lvm.c.objbin0 -> 217873 bytes
-rw-r--r--temp_lua/lzio.c.objbin0 -> 18003 bytes
-rw-r--r--xmake/core/base/binutils.lua21
-rw-r--r--xmake/core/sandbox/modules/import/core/base/binutils.lua8
-rw-r--r--xmake/modules/cli/binutils/bin2c.lua49
-rw-r--r--xmake/modules/cli/binutils/bin2obj.lua54
-rw-r--r--xmake/modules/utils/archive/merge_staticlib.lua55
-rw-r--r--xmake/modules/utils/binary/bin2c.lua22
-rw-r--r--xmake/modules/utils/binary/bin2obj.lua27
-rw-r--r--xmake/modules/utils/binary/extractlib.lua51
-rw-r--r--xmake/modules/utils/binary/readsyms.lua93
-rw-r--r--xmake/rules/utils/bin2c/utils.lua2
-rw-r--r--xmake/rules/utils/bin2obj/utils.lua2
-rw-r--r--xmake/rules/utils/symbols/export_all/export_all.lua62
64 files changed, 2292 insertions, 361 deletions
diff --git a/core/src/xmake/binutils/ar/extractlib.c b/core/src/xmake/binutils/ar/extractlib.c
new file mode 100644
index 000000000..d9629394a
--- /dev/null
+++ b/core/src/xmake/binutils/ar/extractlib.c
@@ -0,0 +1,230 @@
+/*!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 extractlib.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "extractlib"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+
+/* generate unique filename to handle name conflicts
+ *
+ * @param base_name the base filename
+ * @param id the unique ID
+ * @param output output buffer
+ * @param output_size size of output buffer
+ * @param output_len output: actual output length
+ * @return tb_true on success
+ */
+static tb_bool_t xm_binutils_ar_generate_unique_name(tb_char_t const *base_name, tb_uint32_t id, tb_char_t *output, tb_size_t output_size, tb_size_t* output_len) {
+ tb_assert_and_check_return_val(base_name && output && output_size > 0 && output_len, tb_false);
+
+ // find the last dot for extension
+ tb_char_t const *ext = tb_strrchr(base_name, '.');
+ tb_long_t n = -1;
+ if (ext) {
+ tb_size_t base_len = (tb_size_t)(ext - base_name);
+ tb_size_t ext_len = tb_strlen(ext);
+ if (base_len + ext_len + 16 < output_size) {
+ n = tb_snprintf(output, output_size, "%.*s_%u%s", (tb_int_t)base_len, base_name, id, ext);
+ }
+ } else {
+ // no extension
+ if (tb_strlen(base_name) + 16 < output_size) {
+ n = tb_snprintf(output, output_size, "%s_%u", base_name, id);
+ }
+ }
+
+ if (n >= 0) {
+ *output_len = (tb_size_t)n;
+ return tb_true;
+ }
+ return tb_false;
+}
+
+/* extract AR archive to directory
+ *
+ * @param istream the input stream
+ * @param outputdir the output directory
+ * @return tb_true on success, tb_false on failure
+ */
+tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outputdir) {
+ tb_assert_and_check_return_val(istream && outputdir, tb_false);
+
+ // get output directory length
+ tb_size_t outputdir_len = tb_strlen(outputdir);
+
+ // check AR magic (!<arch>\n)
+ if (!xm_binutils_ar_check_magic(istream, 0)) {
+ return tb_false;
+ }
+
+ // ensure output directory exists
+ // check if directory already exists
+ if (!tb_file_info(outputdir, tb_null)) {
+ // directory doesn't exist, create it
+ if (!tb_directory_create(outputdir)) {
+ return tb_false;
+ }
+ }
+
+ tb_bool_t ok = tb_true;
+
+ // iterate through AR members
+ while (ok) {
+ // read AR header
+ // AR header is exactly 60 bytes: name[16] + date[12] + uid[6] + gid[6] + mode[8] + size[10] + fmag[2]
+ xm_ar_header_t header;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ // end of file
+ break;
+ }
+
+ // parse member size
+ tb_int64_t member_size = xm_binutils_ar_parse_decimal(header.size, 10);
+ if (member_size < 0) {
+ ok = tb_false;
+ break;
+ }
+
+ // get member name
+ tb_char_t member_name[256] = {0};
+ tb_size_t name_len = 0;
+ tb_hize_t name_bytes_read = 0;
+
+ // get member name (handles both regular and extended name formats)
+ tb_bool_t skip = tb_false;
+ if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len, &name_bytes_read)) {
+ skip = tb_true;
+ } else if (xm_binutils_ar_is_symbol_table(member_name)) {
+ // skip symbol tables
+ skip = tb_true;
+ } else if (!xm_binutils_ar_is_object_file(member_name)) {
+ // only extract object files
+ skip = tb_true;
+ }
+
+ if (skip) {
+ // skip remaining data + padding using sequential read
+ tb_hize_t skip_size = (tb_hize_t)member_size - name_bytes_read;
+ if (member_size % 2) {
+ skip_size++; // add padding
+ }
+ if (!tb_stream_skip(istream, skip_size)) {
+ ok = tb_false;
+ break;
+ }
+ continue;
+ }
+
+ // handle name conflicts by checking if file exists and renaming with ID
+ tb_char_t output_name[512] = {0};
+ tb_char_t output_path_check[1024] = {0};
+ if (outputdir_len + 1 + name_len >= sizeof(output_path_check)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path_check, sizeof(output_path_check), "%s/%s", outputdir, member_name);
+
+ // check if file already exists
+ tb_uint32_t conflict_id = 1;
+ tb_size_t output_name_len = name_len;
+ if (tb_file_info(output_path_check, tb_null)) {
+ // name conflict, try different IDs until we find an available name
+ while (conflict_id < 10000) { // reasonable limit
+ if (!xm_binutils_ar_generate_unique_name(member_name, conflict_id, output_name, sizeof(output_name), &output_name_len)) {
+ ok = tb_false;
+ break;
+ }
+ if (outputdir_len + 1 + output_name_len >= sizeof(output_path_check)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path_check, sizeof(output_path_check), "%s/%s", outputdir, output_name);
+ if (!tb_file_info(output_path_check, tb_null)) {
+ // found available name
+ break;
+ }
+ conflict_id++;
+ }
+ if (conflict_id >= 10000) {
+ ok = tb_false;
+ break;
+ }
+ } else {
+ // first occurrence, use original name
+ tb_strlcpy(output_name, member_name, sizeof(output_name));
+ }
+
+ // build output path
+ tb_char_t output_path[1024] = {0};
+ if (outputdir_len + 1 + output_name_len >= sizeof(output_path)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, output_name);
+
+ // create output file
+ tb_stream_ref_t ostream = tb_stream_init_from_file(output_path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC);
+ if (!ostream) {
+ ok = tb_false;
+ break;
+ }
+
+ if (!tb_stream_open(ostream)) {
+ tb_stream_exit(ostream);
+ ok = tb_false;
+ break;
+ }
+
+ // copy member data to output file
+ // member_size includes the name if extended format was used, so subtract name_bytes_read
+ tb_hize_t remaining = (tb_hize_t)member_size - name_bytes_read;
+ if (!xm_binutils_stream_copy(istream, ostream, remaining)) {
+ ok = tb_false;
+ }
+
+ tb_stream_clos(ostream);
+ tb_stream_exit(ostream);
+
+ tb_check_break(ok);
+
+ // align to 2-byte boundary (AR format requirement)
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+
+ return ok;
+}
diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h
index 406838d6f..862a3923b 100644
--- a/core/src/xmake/binutils/ar/prefix.h
+++ b/core/src/xmake/binutils/ar/prefix.h
@@ -32,9 +32,9 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* 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_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream);
/* //////////////////////////////////////////////////////////////////////////////////////
@@ -64,7 +64,7 @@ typedef struct __xm_ar_header_t {
*/
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') {
@@ -78,5 +78,157 @@ static __tb_inline__ tb_int64_t xm_binutils_ar_parse_decimal(tb_char_t const *st
return result;
}
-#endif
+/* get member name from AR header, handling extended names (#N/L format)
+ *
+ * @param istream the input stream
+ * @param header the AR header
+ * @param name output buffer for the name
+ * @param name_size size of the name buffer
+ * @param name_len output: actual name length
+ * @param bytes_read output: total bytes read from stream (including newline, for extended names)
+ * @return tb_true on success, tb_false on failure
+ */
+static __tb_inline__ tb_bool_t xm_binutils_ar_get_member_name(tb_stream_ref_t istream, xm_ar_header_t const* header, tb_char_t* name, tb_size_t name_size, tb_size_t* name_len, tb_hize_t* bytes_read) {
+ tb_assert_and_check_return_val(istream && header && name && name_size > 0 && name_len && bytes_read, tb_false);
+ *bytes_read = 0;
+
+ /* check for extended name format (#N/L or #1/N)
+ * In BSD AR format:
+ * - #1/N means name is directly after header, N is total length (including name)
+ * - #N/L means name length is N, total length is L
+ * - #1/N can also mean name is in long name table at offset 1
+ * We'll try to read the name directly from stream first
+ */
+ if (header->name[0] == '#') {
+ // find the '/' separator
+ tb_size_t slash_pos = 0;
+ for (tb_size_t i = 1; i < 16; i++) {
+ if (header->name[i] == '/') {
+ slash_pos = i;
+ break;
+ }
+ }
+
+ if (slash_pos > 0 && slash_pos < 16) {
+ // parse the number before '/' (could be name length or offset)
+ tb_int64_t first_num = xm_binutils_ar_parse_decimal(header->name + 1, slash_pos - 1);
+ // parse the number after '/' (total length)
+ tb_int64_t total_length = xm_binutils_ar_parse_decimal(header->name + slash_pos + 1, 16 - slash_pos - 1);
+
+ if (first_num <= 0 || total_length <= 0) {
+ return tb_false;
+ }
+
+ /* In BSD AR format, extended name is directly after header
+ * The name data starts immediately after the header, no newline
+ * Read exactly total_length bytes for the name section
+ */
+ tb_byte_t c;
+ tb_size_t name_bytes = 0;
+ tb_hize_t bytes_read_so_far = 0;
+
+ // Read name characters until we hit null terminator or reach total_length
+ while (bytes_read_so_far < (tb_hize_t)total_length && name_bytes < name_size - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ return tb_false;
+ }
+ bytes_read_so_far++;
+
+ if (c == '\0') {
+ // Stop reading name at null terminator, but continue reading to reach total_length
+ break;
+ }
+ // Include all characters in the name, including newlines if present
+ name[name_bytes++] = (tb_char_t)c;
+ }
+ name[name_bytes] = '\0';
+ *name_len = name_bytes;
+ // Skip remaining bytes to reach total_length (there may be padding or null terminators)
+ if (bytes_read_so_far < (tb_hize_t)total_length) {
+ tb_hize_t remaining_to_read = (tb_hize_t)total_length - bytes_read_so_far;
+ if (!tb_stream_skip(istream, remaining_to_read)) {
+ return tb_false;
+ }
+ }
+
+ // Total bytes read = name + padding = total_length
+ *bytes_read = (tb_hize_t)total_length;
+ return tb_true;
+ }
+ }
+
+ // regular name (null-terminated or space-padded)
+ tb_size_t i = 0;
+ for (i = 0; i < 16 && i < name_size - 1; i++) {
+ if (header->name[i] == ' ' || header->name[i] == '\0' || header->name[i] == '/') {
+ break;
+ }
+ name[i] = header->name[i];
+ }
+ name[i] = '\0';
+ *name_len = i;
+ *bytes_read = 0; // Regular names are in header, not read from stream
+ return tb_true;
+}
+
+/* check AR magic (!<arch>\n)
+ *
+ * @param istream the input stream
+ * @param base_offset the base offset
+ * @return tb_true on success, tb_false on failure
+ */
+static __tb_inline__ tb_bool_t xm_binutils_ar_check_magic(tb_stream_ref_t istream, tb_hize_t base_offset) {
+ tb_uint8_t magic[8];
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, magic, 8)) {
+ return tb_false;
+ }
+ if (magic[0] != '!' || magic[1] != '<' || magic[2] != 'a' || magic[3] != 'r' ||
+ magic[4] != 'c' || magic[5] != 'h' || (magic[6] != '>' && magic[6] != '\n') ||
+ (magic[7] != '\n' && magic[7] != '\r')) {
+ return tb_false;
+ }
+ return tb_true;
+}
+
+/* check if member is a symbol table (should be skipped)
+ *
+ * @param name the member name
+ * @return tb_true if it's a symbol table, tb_false otherwise
+ */
+static __tb_inline__ tb_bool_t xm_binutils_ar_is_symbol_table(tb_char_t const *name) {
+ tb_assert_and_check_return_val(name, tb_false);
+ return (tb_strcmp(name, "__.SYMDEF") == 0 || tb_strcmp(name, "__.SYMDEF SORTED") == 0 ||
+ tb_strcmp(name, "/") == 0 || tb_strcmp(name, "//") == 0 ||
+ tb_strncmp(name, "__.SYMDEF", 9) == 0);
+}
+
+/* check if member is an object file (based on extension)
+ *
+ * @param name the member name
+ * @return tb_true if it's likely an object file, tb_false otherwise
+ */
+static __tb_inline__ tb_bool_t xm_binutils_ar_is_object_file(tb_char_t const *name) {
+ tb_assert_and_check_return_val(name, tb_false);
+ tb_size_t len = tb_strlen(name);
+ if (len == 0) {
+ return tb_false;
+ }
+
+ // check common object file extensions
+ if (len >= 2 && name[len - 2] == '.' && name[len - 1] == 'o') {
+ return tb_true;
+ }
+ if (len >= 4 && tb_strcmp(name + len - 4, ".obj") == 0) {
+ return tb_true;
+ }
+
+ // check if it's a COFF/ELF/Mach-O file by detecting format
+ // For now, we'll extract all non-symbol-table members
+ return tb_true;
+}
+
+#endif
diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c
index 6d738565e..a41bf2e33 100644
--- a/core/src/xmake/binutils/ar/readsyms.c
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -19,25 +19,410 @@
*
*/
+#define TB_TRACE_MODULE_NAME "readsyms_ar"
+#define TB_TRACE_MODULE_DEBUG (0)
+
/* //////////////////////////////////////////////////////////////////////////////////////
* includes
*/
#include "prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+
+
+/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
*/
+/* parse BSD symbol table (__.SYMDEF or __.SYMDEF SORTED)
+ *
+ * Header:
+ * - ranlib_size (uint32_t)
+ * - ranlibs (struct ranlib[ranlib_size/8])
+ * - strtab_size (uint32_t)
+ * - strtab (char[strtab_size])
+ *
+ * struct ranlib {
+ * uint32_t ran_strx; // offset into string table
+ * uint32_t ran_off; // offset into archive
+ * };
+ */
+static tb_bool_t xm_binutils_ar_parse_bsd_symdef(tb_stream_ref_t istream, tb_hize_t member_size, lua_State* lua, int map_idx) {
+ tb_hize_t start_pos = tb_stream_offset(istream);
+
+ // read size of ranlib array
+ tb_uint32_t ranlib_size = 0;
+ if (!tb_stream_bread_u32_le(istream, &ranlib_size)) {
+ return tb_false;
+ }
+
+ // sanity check
+ if (ranlib_size == 0 || ranlib_size >= member_size) {
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ // read ranlib array
+ tb_size_t num_ranlibs = ranlib_size / 8;
+
+ // allocate buffers
+ tb_uint32_t* ran_strx = tb_nalloc_type(num_ranlibs, tb_uint32_t);
+ tb_uint32_t* ran_off = tb_nalloc_type(num_ranlibs, tb_uint32_t);
+
+ if (!ran_strx || !ran_off) {
+ if (ran_strx) {
+ tb_free(ran_strx);
+ }
+ if (ran_off) {
+ tb_free(ran_off);
+ }
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ tb_size_t i;
+ for (i = 0; i < num_ranlibs; i++) {
+ if (!tb_stream_bread_u32_le(istream, &ran_strx[i]) ||
+ !tb_stream_bread_u32_le(istream, &ran_off[i])) {
+ tb_free(ran_strx);
+ tb_free(ran_off);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+ }
+
+ // read string table size
+ tb_uint32_t strtab_size = 0;
+ if (!tb_stream_bread_u32_le(istream, &strtab_size)) {
+ tb_free(ran_strx);
+ tb_free(ran_off);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ // read string table
+ tb_char_t* strtab = (tb_char_t*)tb_malloc_bytes(strtab_size);
+ if (!strtab) {
+ tb_free(ran_strx);
+ tb_free(ran_off);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)strtab, strtab_size)) {
+ tb_free(strtab);
+ tb_free(ran_strx);
+ tb_free(ran_off);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ // populate map
+ for (i = 0; i < num_ranlibs; i++) {
+ tb_uint32_t off = ran_off[i];
+ tb_uint32_t strx = ran_strx[i];
+
+ if (strx < strtab_size) {
+ tb_char_t* name = strtab + strx;
+
+ // add to map: map[off] = { {name=name, type="T"}, ... }
+ lua_pushinteger(lua, off);
+ lua_rawget(lua, map_idx);
+ if (lua_isnil(lua, -1)) {
+ lua_pop(lua, 1);
+ lua_newtable(lua);
+ lua_pushinteger(lua, off);
+ lua_pushvalue(lua, -2);
+ lua_rawset(lua, map_idx);
+ }
+
+ int count = (int)lua_objlen(lua, -1);
+ lua_newtable(lua);
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, "T");
+ lua_settable(lua, -3);
+
+ lua_rawseti(lua, -2, count + 1);
+ lua_pop(lua, 1); // pop list
+ }
+ }
+
+ tb_free(strtab);
+ tb_free(ran_strx);
+ tb_free(ran_off);
+ return tb_true;
+}
+
+/* parse SysV symbol table (/)
+ *
+ * Header:
+ * - num_symbols (uint32_t BE)
+ * - offsets (uint32_t[num_symbols] BE)
+ * - string table (null-terminated strings)
+ */
+static tb_bool_t xm_binutils_ar_parse_sysv_symdef(tb_stream_ref_t istream, tb_hize_t member_size, lua_State* lua, int map_idx) {
+ tb_hize_t start_pos = tb_stream_offset(istream);
+
+ // read number of symbols
+ tb_uint32_t num_symbols = 0;
+ if (!tb_stream_bread_u32_be(istream, &num_symbols)) {
+ return tb_false;
+ }
+
+ // sanity check
+ if (num_symbols == 0 || num_symbols * 4 >= member_size) {
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ // read offsets
+ tb_uint32_t* offsets = tb_nalloc_type(num_symbols, tb_uint32_t);
+ if (!offsets) {
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ tb_size_t i;
+ for (i = 0; i < num_symbols; i++) {
+ if (!tb_stream_bread_u32_be(istream, &offsets[i])) {
+ tb_free(offsets);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+ }
+
+ // read string table
+ tb_hize_t current = tb_stream_offset(istream);
+ tb_hize_t strtab_size = member_size - (current - start_pos);
+
+ tb_char_t* strtab = (tb_char_t*)tb_malloc_bytes((tb_size_t)strtab_size);
+ if (!strtab) {
+ tb_free(offsets);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)strtab, (tb_size_t)strtab_size)) {
+ tb_free(strtab);
+ tb_free(offsets);
+ tb_stream_seek(istream, start_pos);
+ return tb_false;
+ }
+
+ // populate map
+ tb_char_t* p = strtab;
+ tb_char_t* end = strtab + strtab_size;
+
+ for (i = 0; i < num_symbols; i++) {
+ if (p >= end) {
+ break;
+ }
+
+ tb_char_t* name = p;
+ tb_size_t len = tb_strlen(name);
+ p += len + 1;
+
+ tb_uint32_t off = offsets[i];
+
+ // add to map
+ lua_pushinteger(lua, off);
+ lua_rawget(lua, map_idx);
+ if (lua_isnil(lua, -1)) {
+ lua_pop(lua, 1);
+ lua_newtable(lua);
+ lua_pushinteger(lua, off);
+ lua_pushvalue(lua, -2);
+ lua_rawset(lua, map_idx);
+ }
+
+ int count = (int)lua_objlen(lua, -1);
+ lua_newtable(lua);
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, "T");
+ lua_settable(lua, -3);
+
+ lua_rawseti(lua, -2, count + 1);
+ lua_pop(lua, 1); // pop list
+ }
+
+ tb_free(strtab);
+ tb_free(offsets);
+ return tb_true;
+}
+
/* read symbols from AR archive
*
- * @param istream the input stream
- * @param lua the lua state
- * @return tb_true on success, tb_false on failure
+ * @param istream the input stream
+ * @param base_offset the base offset
+ * @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_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, 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;
+ // check AR magic (!<arch>\n)
+ if (!xm_binutils_ar_check_magic(istream, base_offset)) {
+ return tb_false;
+ }
+
+ // get result list index
+ int list_idx = lua_gettop(lua);
+
+ // init map table for symbol table
+ lua_newtable(lua);
+ int map_idx = lua_gettop(lua);
+
+ tb_bool_t ok = tb_true;
+ tb_size_t object_count = 0;
+
+ // iterate through AR members
+ while (ok) {
+ // save member header position
+ tb_hize_t member_header_pos = tb_stream_offset(istream);
+
+ /* read AR header
+ * AR header is exactly 60 bytes: name[16] + date[12] + uid[6] + gid[6] + mode[8] + size[10] + fmag[2]
+ */
+ xm_ar_header_t header;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ // end of file
+ break;
+ }
+
+ // parse member size
+ tb_int64_t member_size = xm_binutils_ar_parse_decimal(header.size, 10);
+ if (member_size < 0) {
+ ok = tb_false;
+ break;
+ }
+
+ // get member name
+ tb_char_t member_name[256] = {0};
+ tb_size_t name_len = 0;
+ tb_hize_t name_bytes_read = 0;
+
+ // get member name (handles both regular and extended name formats)
+ tb_bool_t skip = tb_false;
+ if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len, &name_bytes_read)) {
+ skip = tb_true;
+ } else {
+ if (xm_binutils_ar_is_symbol_table(member_name)) {
+ /* parse symbol table
+ *
+ * The symbol table in the archive only contains symbol names and their offsets,
+ * but lacks detailed symbol type information (e.g., distinguishing between code and data).
+ * However, for object files that cannot be parsed (e.g., LTO bitcode) or unknown formats,
+ * parsing the symbol table serves as a robust fallback to ensure symbols are extracted.
+ */
+ tb_hize_t current = tb_stream_offset(istream);
+ if (tb_strcmp(member_name, "/") == 0) {
+ xm_binutils_ar_parse_sysv_symdef(istream, member_size, lua, map_idx);
+ } else if (tb_strcmp(member_name, "//") != 0) {
+ xm_binutils_ar_parse_bsd_symdef(istream, member_size, lua, map_idx);
+ }
+ tb_stream_seek(istream, current); // restore position for skip
+ skip = tb_true;
+ } else if (!xm_binutils_ar_is_object_file(member_name)) {
+ // only extract object files
+ skip = tb_true;
+ }
+ }
+
+ if (skip) {
+ // skip remaining data + padding using sequential read
+ tb_hize_t skip_size = (tb_hize_t)member_size - name_bytes_read;
+ if (member_size % 2) {
+ skip_size++; // add padding
+ }
+ if (!tb_stream_skip(istream, skip_size)) {
+ ok = tb_false;
+ break;
+ }
+ continue;
+ }
+
+ // save current position
+ tb_hize_t current_pos = tb_stream_offset(istream);
+
+ // detect format
+ tb_int_t format = xm_binutils_detect_format(istream);
+ if (format != XM_BINUTILS_FORMAT_AR) {
+ // create entry table
+ lua_newtable(lua);
+
+ // object name
+ lua_pushstring(lua, "objectfile");
+ lua_pushstring(lua, member_name);
+ lua_settable(lua, -3);
+
+ // symbols
+ lua_pushstring(lua, "symbols");
+ tb_bool_t read_ok = tb_false;
+ if (format == XM_BINUTILS_FORMAT_COFF) {
+ read_ok = xm_binutils_coff_read_symbols(istream, current_pos, lua);
+ } else if (format == XM_BINUTILS_FORMAT_ELF) {
+ read_ok = xm_binutils_elf_read_symbols(istream, current_pos, lua);
+ } else if (format == XM_BINUTILS_FORMAT_MACHO) {
+ read_ok = xm_binutils_macho_read_symbols(istream, current_pos, lua);
+ }
+
+ if (!read_ok) {
+ /* try get from map
+ *
+ * If parsing the object file fails (e.g. for LTO bitcode or unsupported formats),
+ * we fall back to using the symbols parsed from the archive symbol table.
+ * Although the type information is less accurate (defaulting to "T"),
+ * it guarantees that symbols are not lost.
+ *
+ * cast to lua_Integer to avoid warning C4244 on 32-bit MSVC
+ * member_header_pos is tb_hize_t (64-bit), but AR offsets are usually 32-bit
+ */
+ lua_pushinteger(lua, (lua_Integer)member_header_pos);
+ lua_rawget(lua, map_idx);
+ if (!lua_isnil(lua, -1)) {
+ read_ok = tb_true;
+ } else {
+ lua_pop(lua, 1);
+ }
+ }
+
+ if (read_ok) {
+ lua_settable(lua, -3);
+ lua_rawseti(lua, list_idx, (int)(++object_count));
+ } else {
+ lua_pop(lua, 2); // pop symbols key and entry table
+ }
+ }
+
+ // skip to next member
+ tb_hize_t member_data_read = tb_stream_offset(istream) - current_pos;
+ tb_hize_t remaining_size = (tb_hize_t)member_size - name_bytes_read - member_data_read;
+ if (member_size % 2) {
+ remaining_size++; // add padding
+ }
+
+ if (remaining_size > 0) {
+ if (!tb_stream_skip(istream, remaining_size)) {
+ ok = tb_false;
+ break;
+ }
+ } else if (remaining_size < 0) {
+ /* should not happen if readsyms functions respect boundaries, but just in case
+ * seek back to correct position
+ */
+ if (!tb_stream_seek(istream, current_pos + (tb_hize_t)member_size - name_bytes_read + (member_size % 2))) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+
+ lua_remove(lua, map_idx);
+ return ok;
}
diff --git a/core/src/xmake/binutils/coff/bin2coff.c b/core/src/xmake/binutils/coff/bin2coff.c
index 2a09cd504..ceb984a5c 100644
--- a/core/src/xmake/binutils/coff/bin2coff.c
+++ b/core/src/xmake/binutils/coff/bin2coff.c
@@ -90,11 +90,7 @@ static tb_bool_t xm_binutils_bin2coff_dump(tb_stream_ref_t istream,
}
// replace non-alphanumeric with underscore
- for (tb_size_t i = 0; symbol_name[i]; i++) {
- if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
- symbol_name[i] = '_';
- }
- }
+ xm_binutils_sanitize_symbol_name(symbol_name);
tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h
index 587d724df..6b009b056 100644
--- a/core/src/xmake/binutils/coff/prefix.h
+++ b/core/src/xmake/binutils/coff/prefix.h
@@ -55,6 +55,27 @@ typedef struct __xm_coff_header_t {
tb_uint16_t flags;
} __tb_packed__ xm_coff_header_t;
+typedef struct __xm_coff_import_header_t {
+ tb_uint16_t sig1; // 0
+ tb_uint16_t sig2; // 0xffff
+ tb_uint16_t version;
+ tb_uint16_t machine;
+ tb_uint32_t time;
+ tb_uint32_t size; // size of data
+ tb_uint16_t ordinal; // ordinal or hint
+ tb_uint16_t type; // type
+} __tb_packed__ xm_coff_import_header_t;
+
+typedef struct __xm_coff_anon_header_t {
+ tb_uint16_t sig1; // 0
+ tb_uint16_t sig2; // 0xffff
+ tb_uint16_t version;
+ tb_uint16_t machine;
+ tb_uint32_t time;
+ tb_uint8_t clsid[16];
+ tb_uint32_t size; // size of data
+} __tb_packed__ xm_coff_anon_header_t;
+
typedef struct __xm_coff_section_t {
tb_char_t name[8];
tb_uint32_t vsize;
@@ -188,7 +209,7 @@ static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_
* @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) {
+static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istream, tb_hize_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
@@ -250,7 +271,7 @@ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istr
* @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) {
+static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t istream, xm_coff_symbol_t const *sym, tb_hize_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)
diff --git a/core/src/xmake/binutils/coff/readsyms.c b/core/src/xmake/binutils/coff/readsyms.c
index 41ca5ee47..f45524a61 100644
--- a/core/src/xmake/binutils/coff/readsyms.c
+++ b/core/src/xmake/binutils/coff/readsyms.c
@@ -34,18 +34,92 @@
* private implementation
*/
-tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+static tb_bool_t xm_binutils_coff_read_import_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua, xm_coff_header_t const* header) {
+
+ // create result table
+ lua_newtable(lua);
+
+ /* check version
+ * version is the low 16 bits of the time field (offset 4)
+ * xm_coff_header_t: machine(2), nsects(2), time(4)
+ * xm_coff_import_header_t: sig1(2), sig2(2), version(2), machine(2)
+ */
+ tb_uint16_t version = header->time & 0xffff;
+ if (version == 1) {
+ // anonymous object header (used for CLSID)
+ /*
+ * @note we can not read symbols from the anonymous object (LTO/GL/LTCG),
+ * because it does not contain the symbol table.
+ */
+ xm_coff_anon_header_t anon_header;
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&anon_header, sizeof(anon_header))) {
+ return tb_false;
+ }
+ } else {
+ // import header
+ xm_coff_import_header_t import_header;
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, (tb_byte_t*)&import_header, sizeof(import_header))) {
+ return tb_false;
+ }
+
+ // read symbol name (it follows the header)
+ tb_char_t name[256] = {0};
+ tb_size_t pos = 0;
+ tb_byte_t c;
+ while (pos < sizeof(name) - 1) {
+ if (!tb_stream_bread(istream, &c, 1)) {
+ break;
+ }
+ if (c == 0) {
+ break;
+ }
+ name[pos++] = (tb_char_t)c;
+ }
+ name[pos] = '\0';
+
+ if (name[0]) {
+ lua_pushinteger(lua, 1);
+ lua_newtable(lua);
+
+ // name
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // type
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, "I");
+ lua_settable(lua, -3);
+
+ lua_settable(lua, -3);
+ }
+ }
+ return tb_true;
+}
+
+tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
tb_assert_and_check_return_val(istream && lua, tb_false);
// read COFF header
xm_coff_header_t header;
- if (!tb_stream_seek(istream, 0)) {
+ if (!tb_stream_seek(istream, base_offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
return tb_false;
}
+ // check if it is an import object
+ if (header.machine == 0 && header.nsects == 0xffff) {
+ return xm_binutils_coff_read_import_symbols(istream, base_offset, lua, &header);
+ }
+
// check if there are symbols
if (header.nsyms == 0 || header.symtabofs == 0) {
lua_newtable(lua);
@@ -66,7 +140,7 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua)
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)) {
+ if (tb_stream_seek(istream, base_offset + 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;
@@ -78,7 +152,7 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua)
}
// read symbols
- if (!tb_stream_seek(istream, header.symtabofs)) {
+ if (!tb_stream_seek(istream, base_offset + header.symtabofs)) {
if (sections) {
tb_free(sections);
}
@@ -91,13 +165,15 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua)
// read symbol
xm_coff_symbol_t sym;
if (!tb_stream_bread(istream, (tb_byte_t*)&sym, sizeof(sym))) {
- if (sections) tb_free(sections);
+ 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]) {
+ if (!xm_binutils_coff_get_symbol_name(istream, &sym, base_offset + strtab_offset, name, sizeof(name)) || !name[0]) {
skip = tb_true;
} else if (name[0] == '.') {
skip = tb_true;
@@ -134,7 +210,9 @@ tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua)
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);
+ if (sections) {
+ tb_free(sections);
+ }
return tb_false;
}
}
diff --git a/core/src/xmake/binutils/elf/bin2elf.c b/core/src/xmake/binutils/elf/bin2elf.c
index e6a9e7998..ab93bcde2 100644
--- a/core/src/xmake/binutils/elf/bin2elf.c
+++ b/core/src/xmake/binutils/elf/bin2elf.c
@@ -74,11 +74,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_32(tb_stream_ref_t istream,
}
// replace non-alphanumeric with underscore
- for (tb_size_t i = 0; symbol_name[i]; i++) {
- if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
- symbol_name[i] = '_';
- }
- }
+ xm_binutils_sanitize_symbol_name(symbol_name);
tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
@@ -378,11 +374,7 @@ static tb_bool_t xm_binutils_bin2elf_dump_64(tb_stream_ref_t istream,
}
// replace non-alphanumeric with underscore
- for (tb_size_t i = 0; symbol_name[i]; i++) {
- if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
- symbol_name[i] = '_';
- }
- }
+ xm_binutils_sanitize_symbol_name(symbol_name);
tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
diff --git a/core/src/xmake/binutils/elf/readsyms.c b/core/src/xmake/binutils/elf/readsyms.c
index f0d0a1061..03756d240 100644
--- a/core/src/xmake/binutils/elf/readsyms.c
+++ b/core/src/xmake/binutils/elf/readsyms.c
@@ -34,12 +34,12 @@
* private implementation
*/
-tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lua) {
+tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
tb_assert_and_check_return_val(istream && lua, tb_false);
// read ELF header
xm_elf32_header_t header;
- if (!tb_stream_seek(istream, 0)) {
+ if (!tb_stream_seek(istream, base_offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
@@ -52,7 +52,7 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
tb_bool_t found_symtab = tb_false;
tb_bool_t found_strtab = tb_false;
- if (!tb_stream_seek(istream, header.e_shoff)) {
+ if (!tb_stream_seek(istream, base_offset + header.e_shoff)) {
return tb_false;
}
@@ -66,8 +66,9 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
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
+ /* .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;
@@ -82,7 +83,7 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
// 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))) {
+ if (!tb_stream_seek(istream, base_offset + 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))) {
@@ -101,7 +102,7 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
// 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)) {
+ if (!tb_stream_seek(istream, base_offset + symtab_section.sh_offset)) {
return tb_false;
}
@@ -125,15 +126,15 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
// 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]) {
+ if (!xm_binutils_elf_read_string(istream, base_offset + 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
@@ -163,12 +164,12 @@ tb_bool_t xm_binutils_elf_read_symbols_32(tb_stream_ref_t istream, lua_State *lu
return tb_true;
}
-tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lua) {
+tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
tb_assert_and_check_return_val(istream && lua, tb_false);
// read ELF header
xm_elf64_header_t header;
- if (!tb_stream_seek(istream, 0)) {
+ if (!tb_stream_seek(istream, base_offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
@@ -181,7 +182,7 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu
tb_bool_t found_symtab = tb_false;
tb_bool_t found_strtab = tb_false;
- if (!tb_stream_seek(istream, header.e_shoff)) {
+ if (!tb_stream_seek(istream, base_offset + header.e_shoff)) {
return tb_false;
}
@@ -209,7 +210,7 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu
// 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))) {
+ if (!tb_stream_seek(istream, base_offset + 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))) {
@@ -228,7 +229,7 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu
// 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)) {
+ if (!tb_stream_seek(istream, base_offset + symtab_section.sh_offset)) {
return tb_false;
}
@@ -252,15 +253,15 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu
// 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]) {
+ if (!xm_binutils_elf_read_string(istream, base_offset + 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
@@ -290,12 +291,15 @@ tb_bool_t xm_binutils_elf_read_symbols_64(tb_stream_ref_t istream, lua_State *lu
return tb_true;
}
-tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
tb_assert_and_check_return_val(istream && lua, tb_false);
// read and check ELF magic
tb_uint8_t magic[4];
- if (!xm_binutils_read_magic(istream, magic, 4)) {
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+ if (!tb_stream_bread(istream, magic, 4)) {
return tb_false;
}
if (magic[0] != 0x7f || magic[1] != 'E' || magic[2] != 'L' || magic[3] != 'F') {
@@ -304,7 +308,7 @@ tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua)
// check ELF class (32-bit or 64-bit)
tb_uint8_t elf_class;
- if (!tb_stream_seek(istream, 4)) {
+ if (!tb_stream_seek(istream, base_offset + 4)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&elf_class, 1)) {
@@ -312,9 +316,9 @@ tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua)
}
if (elf_class == 1) {
- return xm_binutils_elf_read_symbols_32(istream, lua);
+ return xm_binutils_elf_read_symbols_32(istream, base_offset, lua);
} else if (elf_class == 2) {
- return xm_binutils_elf_read_symbols_64(istream, lua);
+ return xm_binutils_elf_read_symbols_64(istream, base_offset, lua);
}
return tb_false;
diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c
new file mode 100644
index 000000000..ec588f213
--- /dev/null
+++ b/core/src/xmake/binutils/extractlib.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 extractlib.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "extractlib"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+#include "ar/prefix.h"
+#include "mslib/prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+extern tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outputdir);
+extern tb_bool_t xm_binutils_mslib_extract(tb_stream_ref_t istream, tb_char_t const *outputdir, tb_bool_t plain);
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* extract static library to directory (Lua interface)
+ * Supports AR format (.a) and MSVC lib format (.lib)
+ *
+ * @param lua the lua state
+ *
+ * libraryfile = lua[1]
+ * outputdir = lua[2]
+ * plain = lua[3] (optional, default: true)
+ *
+ * @return 1 on success, 2 on failure (with error message)
+ */
+tb_int_t xm_binutils_extractlib(lua_State *lua) {
+ tb_assert_and_check_return_val(lua, 0);
+
+ // get the library file path
+ tb_char_t const *libraryfile = luaL_checkstring(lua, 1);
+ tb_check_return_val(libraryfile, 0);
+
+ // get the output directory
+ tb_char_t const *outputdir = luaL_checkstring(lua, 2);
+ tb_check_return_val(outputdir, 0);
+
+ // get the plain mode (optional)
+ tb_bool_t plain = tb_true;
+ if (lua_gettop(lua) >= 3 && !lua_isnil(lua, 3)) {
+ plain = lua_toboolean(lua, 3);
+ }
+
+ // open library file
+ tb_stream_ref_t istream = tb_stream_init_from_file(libraryfile, TB_FILE_MODE_RO);
+ if (!istream) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "extractlib: open %s failed", libraryfile);
+ return 2;
+ }
+
+ tb_bool_t ok = tb_false;
+ tb_char_t const* error_msg = tb_null;
+ do {
+ if (!tb_stream_open(istream)) {
+ error_msg = "open failed";
+ break;
+ }
+
+ // detect format
+ tb_int_t format = xm_binutils_detect_format(istream);
+ if (format < 0) {
+ error_msg = "cannot detect format";
+ break;
+ }
+
+ // extract based on format
+ if (format == XM_BINUTILS_FORMAT_AR) {
+ // AR archive format (.a or .lib in AR format)
+ // if the file extension is .lib, we use the msvc lib extractor to support long paths and subdirectories
+ tb_size_t n = tb_strlen(libraryfile);
+ if (n > 4 && !tb_strnicmp(libraryfile + n - 4, ".lib", 4)) {
+ if (!xm_binutils_mslib_extract(istream, outputdir, plain)) {
+ error_msg = "extract MSVC lib failed";
+ break;
+ }
+ } else {
+ if (!xm_binutils_ar_extract(istream, outputdir)) {
+ error_msg = "extract AR archive failed";
+ break;
+ }
+ }
+ ok = tb_true;
+ } else if (format == XM_BINUTILS_FORMAT_COFF) {
+ // MSVC lib format (.lib in COFF format)
+ // Check if it's actually a library (not just a single object file)
+ // MSVC lib files can be:
+ // 1. Import libraries (different format)
+ // 2. Static libraries (COFF archive format, similar to AR but different)
+ if (!xm_binutils_mslib_extract(istream, outputdir, plain)) {
+ error_msg = "extract MSVC lib failed";
+ break;
+ }
+ ok = tb_true;
+ } else {
+ error_msg = "unsupported format (only AR and MSVC lib are supported)";
+ break;
+ }
+
+ } while (0);
+
+ if (istream) {
+ tb_stream_clos(istream);
+ tb_stream_exit(istream);
+ }
+
+ if (ok) {
+ lua_pushboolean(lua, tb_true);
+ return 1;
+ } else {
+ lua_pushboolean(lua, tb_false);
+ if (error_msg) {
+ lua_pushfstring(lua, "extractlib: %s %s", error_msg, libraryfile);
+ } else {
+ lua_pushfstring(lua, "extractlib: unknown error for %s", libraryfile);
+ }
+ return 2;
+ }
+}
diff --git a/core/src/xmake/binutils/macho/bin2macho.c b/core/src/xmake/binutils/macho/bin2macho.c
index a9068625a..8abd020e9 100644
--- a/core/src/xmake/binutils/macho/bin2macho.c
+++ b/core/src/xmake/binutils/macho/bin2macho.c
@@ -79,11 +79,7 @@ static tb_bool_t xm_binutils_bin2macho_dump_64(tb_stream_ref_t istream,
}
// replace non-alphanumeric with underscore
- for (tb_size_t i = 0; symbol_name[i]; i++) {
- if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
- symbol_name[i] = '_';
- }
- }
+ xm_binutils_sanitize_symbol_name(symbol_name);
tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
@@ -324,11 +320,7 @@ static tb_bool_t xm_binutils_bin2macho_dump_32(tb_stream_ref_t istream,
}
// replace non-alphanumeric with underscore
- for (tb_size_t i = 0; symbol_name[i]; i++) {
- if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
- symbol_name[i] = '_';
- }
- }
+ xm_binutils_sanitize_symbol_name(symbol_name);
tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h
index f3c6e2302..2d224ff77 100644
--- a/core/src/xmake/binutils/macho/prefix.h
+++ b/core/src/xmake/binutils/macho/prefix.h
@@ -334,7 +334,7 @@ static __tb_inline__ tb_uint32_t xm_binutils_macho_parse_version(tb_char_t const
*/
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;
@@ -355,7 +355,7 @@ static __tb_inline__ tb_bool_t xm_binutils_macho_detect_format(tb_uint8_t const
*swap_bytes = tb_true;
return tb_true;
}
-
+
return tb_false;
}
@@ -368,7 +368,7 @@ static __tb_inline__ tb_bool_t xm_binutils_macho_detect_format(tb_uint8_t const
* @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) {
+static __tb_inline__ tb_bool_t xm_binutils_macho_read_string(tb_stream_ref_t istream, tb_hize_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)
diff --git a/core/src/xmake/binutils/macho/readsyms.c b/core/src/xmake/binutils/macho/readsyms.c
index fac925d22..2d4889c58 100644
--- a/core/src/xmake/binutils/macho/readsyms.c
+++ b/core/src/xmake/binutils/macho/readsyms.c
@@ -92,29 +92,29 @@ static __tb_inline__ tb_void_t xm_binutils_macho_swap_nlist_64(xm_macho_nlist_64
}
}
-tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *lua, tb_bool_t swap_bytes) {
+tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua, tb_bool_t swap_bytes) {
tb_assert_and_check_return_val(istream && lua, tb_false);
-
+
// read Mach-O header
xm_macho_header_t header;
- if (!tb_stream_seek(istream, 0)) {
+ if (!tb_stream_seek(istream, base_offset)) {
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)) {
+
+ if (!tb_stream_seek(istream, base_offset + offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&cmd, 4)) {
@@ -123,9 +123,9 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *
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)) {
+ if (!tb_stream_seek(istream, base_offset + offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&symtab_cmd, sizeof(symtab_cmd))) {
@@ -134,23 +134,23 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *
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)) {
+ if (!tb_stream_seek(istream, base_offset + 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;
@@ -158,69 +158,69 @@ tb_bool_t xm_binutils_macho_read_symbols_32(tb_stream_ref_t istream, lua_State *
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]) {
+ if (!xm_binutils_macho_read_string(istream, base_offset + 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_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua, tb_bool_t swap_bytes) {
tb_assert_and_check_return_val(istream && lua, tb_false);
-
+
// read Mach-O header
xm_macho_header_64_t header;
- if (!tb_stream_seek(istream, 0)) {
+ if (!tb_stream_seek(istream, base_offset)) {
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)) {
+
+ if (!tb_stream_seek(istream, base_offset + offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&cmd, 4)) {
@@ -229,14 +229,14 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *
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)) {
+ if (!tb_stream_seek(istream, base_offset + offset)) {
return tb_false;
}
if (!tb_stream_bread(istream, (tb_byte_t*)&symtab_cmd, sizeof(symtab_cmd))) {
@@ -246,23 +246,23 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *
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)) {
+ if (!tb_stream_seek(istream, base_offset + 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;
@@ -270,66 +270,70 @@ tb_bool_t xm_binutils_macho_read_symbols_64(tb_stream_ref_t istream, lua_State *
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]) {
+ if (!xm_binutils_macho_read_string(istream, base_offset + 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_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) {
tb_assert_and_check_return_val(istream && lua, tb_false);
-
+
+ if (!tb_stream_seek(istream, base_offset)) {
+ return tb_false;
+ }
+
// read and check magic
tb_uint8_t magic_bytes[4];
- if (!xm_binutils_read_magic(istream, magic_bytes, 4)) {
+ if (!tb_stream_bread(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);
+ return xm_binutils_macho_read_symbols_32(istream, base_offset, lua, swap_bytes);
} else {
- return xm_binutils_macho_read_symbols_64(istream, lua, swap_bytes);
+ return xm_binutils_macho_read_symbols_64(istream, base_offset, lua, swap_bytes);
}
}
diff --git a/core/src/xmake/binutils/mslib/extractlib.c b/core/src/xmake/binutils/mslib/extractlib.c
new file mode 100644
index 000000000..f61c1f720
--- /dev/null
+++ b/core/src/xmake/binutils/mslib/extractlib.c
@@ -0,0 +1,306 @@
+/*!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 extractlib.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "mslib_extract"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* generate unique name for output file
+ *
+ * @param base_name the base name
+ * @param id the unique id
+ * @param output output buffer
+ * @param output_size output buffer size
+ * @param output_len output: actual output length
+ * @return tb_true on success, tb_false on failure
+ */
+static tb_bool_t xm_binutils_mslib_generate_unique_name(tb_char_t const *base_name, tb_uint32_t id, tb_char_t *output, tb_size_t output_size, tb_size_t* output_len) {
+ tb_assert_and_check_return_val(base_name && output && output_size > 0 && output_len, tb_false);
+
+ // find the last dot for extension
+ tb_char_t const *ext = tb_strrchr(base_name, '.');
+ tb_long_t n = -1;
+ if (ext) {
+ tb_size_t base_len = (tb_size_t)(ext - base_name);
+ tb_size_t ext_len = tb_strlen(ext);
+ if (base_len + ext_len + 16 < output_size) {
+ n = tb_snprintf(output, output_size, "%.*s_%u%s", (tb_int_t)base_len, base_name, id, ext);
+ }
+ } else {
+ // no extension
+ if (tb_strlen(base_name) + 16 < output_size) {
+ n = tb_snprintf(output, output_size, "%s_%u", base_name, id);
+ }
+ }
+
+ if (n >= 0) {
+ *output_len = (tb_size_t)n;
+ return tb_true;
+ }
+ return tb_false;
+}
+
+/* extract MSVC lib archive to directory
+ *
+ * @param istream the input stream
+ * @param outputdir the output directory
+ * @param plain extract all object files to the same directory
+ * @return tb_true on success, tb_false on failure
+ */
+tb_bool_t xm_binutils_mslib_extract(tb_stream_ref_t istream, tb_char_t const *outputdir, tb_bool_t plain) {
+ tb_assert_and_check_return_val(istream && outputdir, tb_false);
+
+ // check magic (!<arch>\n)
+ if (!xm_binutils_mslib_check_magic(istream)) {
+ return tb_false;
+ }
+
+ /* ensure output directory exists
+ * check if directory already exists
+ */
+ if (!tb_file_info(outputdir, tb_null)) {
+ // directory doesn't exist, create it
+ if (!tb_directory_create(outputdir)) {
+ return tb_false;
+ }
+ }
+
+ tb_bool_t ok = tb_true;
+ tb_char_t* longnames = tb_null;
+ tb_size_t longnames_size = 0;
+
+ // iterate through members
+ while (ok) {
+ // read header
+ xm_mslib_header_t header;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ // end of file
+ break;
+ }
+
+ // parse member size
+ tb_int64_t member_size = xm_binutils_mslib_parse_decimal(header.size, 10);
+ if (member_size < 0) {
+ ok = tb_false;
+ break;
+ }
+
+ // parse member name
+ tb_char_t member_name[256] = {0};
+ tb_bool_t is_longname_table = tb_false;
+
+ if (header.name[0] == '/') {
+ if (header.name[1] == '/') {
+ // long name table (//)
+ is_longname_table = tb_true;
+ } else if (tb_isdigit(header.name[1])) {
+ // offset into long name table (/123)
+ tb_int64_t offset = xm_binutils_mslib_parse_decimal(header.name + 1, 15);
+ if (offset >= 0 && (tb_size_t)offset < longnames_size) {
+ /* copy from longnames
+ * names in longnames are null-terminated
+ */
+ tb_strlcpy(member_name, longnames + offset, sizeof(member_name));
+ }
+ } else {
+ /* symbol table or other special member (/)
+ * usually symbol table is just "/"
+ */
+ tb_strlcpy(member_name, "/", sizeof(member_name));
+ }
+ } else {
+ // short name, ends with /
+ tb_size_t i = 0;
+ for (i = 0; i < 16 && header.name[i] != '/'; i++) {
+ member_name[i] = header.name[i];
+ }
+ member_name[i] = '\0';
+ }
+
+ if (is_longname_table) {
+ tb_char_t* new_longnames = (tb_char_t*)tb_ralloc(longnames, (tb_size_t)member_size + 1);
+ if (!new_longnames) {
+ ok = tb_false;
+ break;
+ }
+ longnames = new_longnames;
+ if (!tb_stream_bread(istream, (tb_byte_t*)longnames, (tb_size_t)member_size)) {
+ ok = tb_false;
+ break;
+ }
+ longnames[member_size] = '\0';
+ longnames_size = (tb_size_t)member_size;
+
+ // align
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ continue;
+ }
+
+ /* check if we should extract
+ * skip empty names, symbol tables (/), long name table (//) - handled above,
+ * and __.SYMDEF (SysV/BSD style symbol table, just in case)
+ */
+ if (member_name[0] == '\0' || tb_strcmp(member_name, "/") == 0 || tb_strcmp(member_name, "//") == 0 ||
+ tb_strncmp(member_name, "__.SYMDEF", 9) == 0) {
+
+ // skip member data
+ if (!tb_stream_skip(istream, member_size)) {
+ ok = tb_false;
+ break;
+ }
+
+ // align
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ continue;
+ }
+
+ /* construct output path
+ * replace \ with /
+ */
+ tb_size_t name_len = tb_strlen(member_name);
+ for (tb_size_t i = 0; i < name_len; i++) {
+ if (member_name[i] == '\\') {
+ member_name[i] = '/';
+ }
+ }
+
+ // check output path length
+ tb_char_t output_path[1024];
+ if (plain) {
+ // get filename only
+ tb_char_t const* name = tb_strrchr(member_name, '/');
+ if (name) {
+ name++;
+ } else {
+ name = member_name;
+ }
+
+ // check conflicts
+ tb_char_t output_name[512];
+ tb_size_t output_name_len = tb_strlen(name);
+ tb_size_t outputdir_len = tb_strlen(outputdir);
+
+ if (outputdir_len + 1 + output_name_len >= sizeof(output_path)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, name);
+
+ if (tb_file_info(output_path, tb_null)) {
+ // name conflict, try different IDs
+ tb_uint32_t conflict_id = 1;
+ while (conflict_id < 10000) {
+ if (!xm_binutils_mslib_generate_unique_name(name, conflict_id, output_name, sizeof(output_name), &output_name_len)) {
+ ok = tb_false;
+ break;
+ }
+ if (outputdir_len + 1 + output_name_len >= sizeof(output_path)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, output_name);
+ if (!tb_file_info(output_path, tb_null)) {
+ break;
+ }
+ conflict_id++;
+ }
+ tb_check_break(ok);
+ if (conflict_id >= 10000) {
+ ok = tb_false;
+ break;
+ }
+ }
+ } else {
+ if (tb_strlen(outputdir) + 1 + name_len >= sizeof(output_path)) {
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, member_name);
+ }
+
+ // ensure directory exists
+ tb_char_t const* p = tb_strrchr(output_path, '/');
+ if (p) {
+ tb_char_t dir[1024];
+ tb_size_t len = p - output_path;
+ if (len < sizeof(dir)) {
+ tb_strncpy(dir, output_path, len);
+ dir[len] = '\0';
+ if (!tb_file_info(dir, tb_null)) {
+ if (!tb_directory_create(dir)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+ }
+
+ // write file
+ tb_stream_ref_t ostream = tb_stream_init_from_file(output_path, TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC);
+ if (ostream) {
+ if (tb_stream_open(ostream)) {
+ if (!xm_binutils_stream_copy(istream, ostream, member_size)) {
+ ok = tb_false;
+ }
+ } else {
+ ok = tb_false;
+ }
+ tb_stream_exit(ostream);
+ } else {
+ ok = tb_false;
+ }
+ tb_check_break(ok);
+
+ // align to 2-byte boundary
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+
+ if (longnames) {
+ tb_free(longnames);
+ }
+ return ok;
+}
diff --git a/core/src/xmake/binutils/mslib/prefix.h b/core/src/xmake/binutils/mslib/prefix.h
new file mode 100644
index 000000000..521755fb2
--- /dev/null
+++ b/core/src/xmake/binutils/mslib/prefix.h
@@ -0,0 +1,72 @@
+/*!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_MSLIB_PREFIX_H
+#define XM_BINUTILS_MSLIB_PREFIX_H
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "../prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * types
+ */
+
+// MSVC lib header
+#include "tbox/prefix/packed.h"
+typedef struct __xm_mslib_header_t {
+ tb_char_t name[16];
+ tb_char_t date[12];
+ tb_char_t uid[6];
+ tb_char_t gid[6];
+ tb_char_t mode[8];
+ tb_char_t size[10];
+ tb_char_t fmag[2];
+} __tb_packed__ xm_mslib_header_t;
+#include "tbox/prefix/packed.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * interfaces
+ */
+
+static __tb_inline__ tb_int64_t xm_binutils_mslib_parse_decimal(tb_char_t const *p, tb_size_t n) {
+ tb_assert_and_check_return_val(p && n > 0, -1);
+ tb_int64_t v = 0;
+ tb_char_t const* e = p + n;
+ while (p < e && *p == ' ') {
+ p++;
+ }
+ while (p < e && *p >= '0' && *p <= '9') {
+ v = v * 10 + (*p - '0');
+ p++;
+ }
+ return v;
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_mslib_check_magic(tb_stream_ref_t istream) {
+ tb_char_t magic[8];
+ if (!tb_stream_bread(istream, (tb_byte_t*)magic, 8)) {
+ return tb_false;
+ }
+ return tb_strncmp(magic, "!<arch>\n", 8) == 0;
+}
+
+#endif
diff --git a/core/src/xmake/binutils/mslib/readsyms.c b/core/src/xmake/binutils/mslib/readsyms.c
new file mode 100644
index 000000000..de9dc9d62
--- /dev/null
+++ b/core/src/xmake/binutils/mslib/readsyms.c
@@ -0,0 +1,402 @@
+/*!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 "mslib_readsyms"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+static tb_bool_t xm_binutils_mslib_parse_archive_symbols(tb_stream_ref_t istream, tb_hize_t member_size, lua_State* lua, int map_idx) {
+ // try to parse as Second Linker Member (LE)
+ tb_hize_t start_pos = tb_stream_offset(istream);
+ tb_uint32_t* offsets = tb_null;
+ tb_uint16_t* indices = tb_null;
+ tb_char_t* string_table = tb_null;
+ tb_bool_t ok = tb_false;
+
+ do {
+ // read number of members
+ tb_uint32_t num_members = 0;
+ if (!tb_stream_bread_u32_le(istream, &num_members)) {
+ break;
+ }
+
+ // sanity check
+ if (num_members == 0 || num_members > 65536 || num_members * 4 >= member_size) {
+ break;
+ }
+
+ // read offsets
+ offsets = tb_nalloc_type(num_members, tb_uint32_t);
+ tb_check_break(offsets);
+
+ tb_size_t i;
+ for (i = 0; i < num_members; i++) {
+ if (!tb_stream_bread_u32_le(istream, &offsets[i])) {
+ break;
+ }
+ }
+ if (i < num_members) {
+ break;
+ }
+
+ // read number of symbols
+ tb_uint32_t num_symbols = 0;
+ if (!tb_stream_bread_u32_le(istream, &num_symbols)) {
+ break;
+ }
+
+ if (num_symbols == 0 || num_symbols > 1000000) {
+ break;
+ }
+
+ // read indices
+ indices = tb_nalloc_type(num_symbols, tb_uint16_t);
+ tb_check_break(indices);
+
+ for (i = 0; i < num_symbols; i++) {
+ if (!tb_stream_bread_u16_le(istream, &indices[i])) {
+ break;
+ }
+ }
+ if (i < num_symbols) {
+ break;
+ }
+
+ // read string table
+ tb_hize_t current = tb_stream_offset(istream);
+ tb_hize_t string_table_size = member_size - (current - start_pos);
+
+ string_table = (tb_char_t*)tb_malloc_bytes((tb_size_t)string_table_size);
+ tb_check_break(string_table);
+
+ if (!tb_stream_bread(istream, (tb_byte_t*)string_table, (tb_size_t)string_table_size)) {
+ break;
+ }
+
+ // populate map
+ tb_char_t* p = string_table;
+ tb_char_t* end = string_table + string_table_size;
+
+ for (i = 0; i < num_symbols; i++) {
+ if (p >= end) {
+ break;
+ }
+
+ tb_char_t* sym_name = p;
+ tb_size_t sym_len = tb_strlen(sym_name);
+ p += sym_len + 1;
+
+ tb_uint16_t idx = indices[i];
+ if (idx > 0 && idx <= num_members) {
+ tb_uint32_t offset = offsets[idx - 1];
+
+ lua_pushinteger(lua, offset);
+ lua_rawget(lua, map_idx);
+ if (lua_isnil(lua, -1)) {
+ lua_pop(lua, 1);
+ lua_newtable(lua);
+ lua_pushinteger(lua, offset);
+ lua_pushvalue(lua, -2);
+ lua_rawset(lua, map_idx);
+ }
+ int count = (int)lua_objlen(lua, -1);
+ lua_pushstring(lua, sym_name);
+ lua_rawseti(lua, -2, count + 1);
+ lua_pop(lua, 1); // pop list
+ }
+ }
+ ok = tb_true;
+
+ } while (0);
+
+ if (offsets) {
+ tb_free(offsets);
+ }
+ if (indices) {
+ tb_free(indices);
+ }
+ if (string_table) {
+ tb_free(string_table);
+ }
+
+ if (!ok) {
+ tb_stream_seek(istream, start_pos);
+ }
+ return ok;
+}
+
+/* read symbols from MSVC lib archive
+ *
+ * @param istream the input stream
+ * @param base_offset the base offset
+ * @param lua the lua state
+ * @return tb_true on success, tb_false on failure
+ */
+tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State* lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // check magic (!<arch>\n)
+ if (!xm_binutils_mslib_check_magic(istream)) {
+ return tb_false;
+ }
+
+ // create map table (offset -> symbols)
+ lua_newtable(lua);
+ int map_idx = lua_gettop(lua);
+
+ tb_bool_t ok = tb_true;
+ tb_size_t object_count = 0;
+ tb_char_t* longnames = tb_null;
+ tb_size_t longnames_size = 0;
+
+ // iterate through members
+ while (ok) {
+ // read header
+ xm_mslib_header_t header;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ // end of file
+ break;
+ }
+
+ // parse member size
+ tb_int64_t member_size = xm_binutils_mslib_parse_decimal(header.size, 10);
+ if (member_size < 0) {
+ ok = tb_false;
+ break;
+ }
+
+ // parse member name
+ tb_char_t member_name[256] = {0};
+ tb_bool_t is_longname_table = tb_false;
+
+ if (header.name[0] == '/') {
+ if (header.name[1] == '/') {
+ // long name table (//)
+ is_longname_table = tb_true;
+ } else if (tb_isdigit(header.name[1])) {
+ // offset into long name table (/123)
+ tb_int64_t offset = xm_binutils_mslib_parse_decimal(header.name + 1, 15);
+ if (offset >= 0 && (tb_size_t)offset < longnames_size) {
+ /* copy from longnames
+ * names in longnames are null-terminated
+ */
+ tb_strlcpy(member_name, longnames + offset, sizeof(member_name));
+ }
+ } else {
+ /* symbol table or other special member (/)
+ * usually symbol table is just "/"
+ */
+ tb_strlcpy(member_name, "/", sizeof(member_name));
+ }
+ } else {
+ // short name, ends with /
+ tb_size_t i = 0;
+ for (i = 0; i < 16 && header.name[i] != '/'; i++) {
+ member_name[i] = header.name[i];
+ }
+ member_name[i] = '\0';
+ }
+
+ if (is_longname_table) {
+ longnames = (tb_char_t*)tb_ralloc(longnames, (tb_size_t)member_size + 1);
+ if (!longnames || !tb_stream_bread(istream, (tb_byte_t*)longnames, (tb_size_t)member_size)) {
+ ok = tb_false;
+ break;
+ }
+ longnames[member_size] = '\0';
+ longnames_size = (tb_size_t)member_size;
+
+ // align
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ continue;
+ }
+
+ // check if we should process
+ /* skip empty names, long name table (//) - handled above,
+ * and __.SYMDEF (SysV/BSD style symbol table, just in case)
+ */
+ if (member_name[0] == '\0' || tb_strcmp(member_name, "//") == 0 ||
+ tb_strncmp(member_name, "__.SYMDEF", 9) == 0) {
+
+ // skip member data
+ if (!tb_stream_skip(istream, member_size)) {
+ ok = tb_false;
+ break;
+ }
+
+ // align
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ continue;
+ }
+
+ if (tb_strcmp(member_name, "/") == 0) {
+ // try to parse archive symbols
+ if (!xm_binutils_mslib_parse_archive_symbols(istream, (tb_hize_t)member_size, lua, map_idx)) {
+ // if failed, skip member data
+ if (!tb_stream_skip(istream, member_size)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ // align
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ continue;
+ }
+
+ // save current position
+ tb_hize_t current_pos = tb_stream_offset(istream);
+ tb_hize_t header_offset = current_pos - sizeof(xm_mslib_header_t);
+
+ // detect format
+ tb_int_t format = xm_binutils_detect_format(istream);
+ if (format != XM_BINUTILS_FORMAT_UNKNOWN && format != XM_BINUTILS_FORMAT_AR) {
+ // create entry table
+ lua_newtable(lua);
+
+ // object name
+ lua_pushstring(lua, "objectfile");
+ lua_pushstring(lua, member_name);
+ lua_settable(lua, -3);
+
+ // symbols
+ lua_pushstring(lua, "symbols");
+ tb_bool_t read_ok = tb_false;
+ if (format == XM_BINUTILS_FORMAT_COFF) {
+ read_ok = xm_binutils_coff_read_symbols(istream, current_pos, lua);
+ } else if (format == XM_BINUTILS_FORMAT_ELF) {
+ read_ok = xm_binutils_elf_read_symbols(istream, current_pos, lua);
+ } else if (format == XM_BINUTILS_FORMAT_MACHO) {
+ read_ok = xm_binutils_macho_read_symbols(istream, current_pos, lua);
+ }
+
+ // if read failed or empty, try map
+ tb_bool_t has_symbols = tb_false;
+ if (read_ok) {
+ if (lua_objlen(lua, -1) > 0) {
+ has_symbols = tb_true;
+ } else {
+ lua_pop(lua, 1); // pop empty table
+ }
+ }
+
+ if (!has_symbols) {
+ // check map
+ lua_pushinteger(lua, header_offset);
+ lua_rawget(lua, map_idx);
+ if (lua_istable(lua, -1)) {
+ // convert list of names to list of {name=..., type="global"}
+ lua_newtable(lua); // result table
+ int count = (int)lua_objlen(lua, -2);
+ for (int i = 1; i <= count; i++) {
+ lua_rawgeti(lua, -2, i);
+ const char* name = lua_tostring(lua, -1);
+ if (name) {
+ lua_newtable(lua);
+ lua_pushstring(lua, "name");
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ lua_pushstring(lua, "type");
+ lua_pushstring(lua, "T");
+ lua_settable(lua, -3);
+
+ lua_rawseti(lua, -3, i);
+ }
+ lua_pop(lua, 1); // pop name
+ }
+ lua_remove(lua, -2); // remove map entry list
+ has_symbols = tb_true;
+ } else {
+ lua_pop(lua, 1); // pop nil
+ }
+ }
+
+ if (has_symbols) {
+ lua_settable(lua, -3);
+ lua_rawseti(lua, map_idx - 1, (int)(++object_count));
+ } else {
+ lua_pop(lua, 2); // pop symbols key and entry table
+ }
+ }
+
+ // skip to next member
+ tb_hize_t member_data_read = tb_stream_offset(istream) - current_pos;
+ tb_hize_t remaining_size = (tb_hize_t)member_size - member_data_read;
+
+ if (remaining_size > 0) {
+ if (!tb_stream_skip(istream, remaining_size)) {
+ ok = tb_false;
+ break;
+ }
+ } else if (remaining_size < 0) {
+ if (!tb_stream_seek(istream, current_pos + (tb_hize_t)member_size)) {
+ ok = tb_false;
+ break;
+ }
+ }
+
+ // align to 2-byte boundary
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+
+ if (longnames) {
+ tb_free(longnames);
+ }
+ lua_remove(lua, map_idx);
+ return ok;
+}
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index 1733fc7b2..492ed691c 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -79,55 +79,52 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t
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;
- }
+ // peek first 8 bytes
+ tb_byte_t* p = tb_null;
+ if (!tb_stream_peek(istream, &p, 8)) {
+ return -1;
}
- // read magic bytes
- tb_uint8_t magic[4];
- if (!xm_binutils_read_magic(istream, magic, 4)) {
- return -1;
+ // check AR archive format first (!<arch>\n)
+ if (p[0] == '!' && p[1] == '<' && p[2] == 'a' &&
+ p[3] == 'r' && p[4] == 'c' && p[5] == 'h' &&
+ (p[6] == '>' || p[6] == '\n') &&
+ (p[7] == '\n' || p[7] == '\r')) {
+ return XM_BINUTILS_FORMAT_AR;
}
// check ELF magic (0x7f 'E' 'L' 'F')
- if (magic[0] == 0x7f && magic[1] == 'E' && magic[2] == 'L' && magic[3] == 'F') {
+ if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[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)) {
+ if (p[0] == 0xfe && p[1] == 0xed && p[2] == 0xfa &&
+ (p[3] == 0xce || p[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) {
+ if (p[0] == 0xce && p[1] == 0xfa && p[2] == 0xed && p[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) {
+ if (p[0] == 0xcf && p[1] == 0xfa && p[2] == 0xed && p[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);
+ tb_uint16_t machine = (p[1] << 8) | p[0];
// check if it's a valid COFF machine type
+ // Import header: 0x0000 0xffff
+ if (machine == 0x0000) {
+ // read second word to check if it is import header
+ tb_uint16_t machine2 = (p[3] << 8) | p[2];
+ if (machine2 == 0xffff) {
+ return XM_BINUTILS_FORMAT_COFF;
+ }
+ }
+
if (machine == XM_BINUTILS_COFF_MACHINE_I386 ||
machine == XM_BINUTILS_COFF_MACHINE_AMD64 ||
machine == XM_BINUTILS_COFF_MACHINE_ARM ||
@@ -148,7 +145,10 @@ static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream)
* @return tb_true on success, tb_false on failure
*/
static __tb_inline__ tb_bool_t xm_binutils_stream_copy(tb_stream_ref_t istream, tb_stream_ref_t ostream, tb_hize_t size) {
- tb_assert_and_check_return_val(istream && ostream && size > 0, tb_false);
+ tb_assert_and_check_return_val(istream && ostream, tb_false);
+ if (size == 0) {
+ return tb_true;
+ }
tb_byte_t data[TB_STREAM_BLOCK_MAXN];
tb_hize_t writ = 0;
@@ -170,5 +170,19 @@ static __tb_inline__ tb_bool_t xm_binutils_stream_copy(tb_stream_ref_t istream,
return tb_true;
}
+/* sanitize symbol name (replace non-alphanumeric characters with underscores)
+ *
+ * @param name the symbol name
+ */
+static __tb_inline__ void xm_binutils_sanitize_symbol_name(tb_char_t* name) {
+ tb_assert_and_check_return(name);
+ for (tb_size_t i = 0; name[i]; i++) {
+ if (!tb_isalpha(name[i]) && !tb_isdigit(name[i]) && name[i] != '_') {
+ name[i] = '_';
+ }
+ }
+}
+
+
#endif
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
index 73908a562..407ba7399 100644
--- a/core/src/xmake/binutils/readsyms.c
+++ b/core/src/xmake/binutils/readsyms.c
@@ -36,10 +36,11 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* 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);
+extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
+extern tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
@@ -80,41 +81,76 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
lua_pushfstring(lua, "readsyms: cannot detect file format");
break;
}
+
+ // create result list
+ lua_newtable(lua);
// 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;
+ tb_bool_t is_mslib = tb_false;
+ if (objectfile) {
+ tb_size_t len = tb_strlen(objectfile);
+ if (len > 4 && tb_stricmp(objectfile + len - 4, ".lib") == 0) {
+ is_mslib = tb_true;
+ }
}
- } 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;
+
+ if (is_mslib) {
+ if (!xm_binutils_mslib_read_symbols(istream, 0, lua)) {
+ // fallback to ar
+ if (!xm_binutils_ar_read_symbols(istream, 0, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read AR/MSLIB archive symbols failed");
+ break;
+ }
+ }
+ } else {
+ if (!xm_binutils_ar_read_symbols(istream, 0, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read AR archive 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 {
+ // single object file (COFF, ELF, Mach-O)
+ // create entry table
+ lua_newtable(lua);
+
+ // object name
+ lua_pushstring(lua, "objectfile");
+ tb_char_t const* name = tb_strrchr(objectfile, '/');
+ if (!name) {
+ name = tb_strrchr(objectfile, '\\');
+ }
+ if (!name) {
+ name = objectfile;
+ } else {
+ name++;
+ }
+ lua_pushstring(lua, name);
+ lua_settable(lua, -3);
+
+ // symbols
+ lua_pushstring(lua, "symbols");
+ tb_bool_t read_ok = tb_false;
+ if (format == XM_BINUTILS_FORMAT_COFF) {
+ read_ok = xm_binutils_coff_read_symbols(istream, 0, lua);
+ } else if (format == XM_BINUTILS_FORMAT_ELF) {
+ read_ok = xm_binutils_elf_read_symbols(istream, 0, lua);
+ } else if (format == XM_BINUTILS_FORMAT_MACHO) {
+ read_ok = xm_binutils_macho_read_symbols(istream, 0, lua);
}
- } else if (format == XM_BINUTILS_FORMAT_MACHO) {
- // Mach-O
- if (!xm_binutils_macho_read_symbols(istream, lua)) {
+
+ if (read_ok) {
+ lua_settable(lua, -3);
+ lua_rawseti(lua, -2, 1);
+ } else {
+ lua_pop(lua, 2); // pop entry table and result list
lua_pushboolean(lua, tb_false);
- lua_pushfstring(lua, "readsyms: read Mach-O symbols failed");
+ lua_pushfstring(lua, "readsyms: read 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;
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index 4f2702f52..8e1878499 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -340,6 +340,7 @@ tb_int_t xm_binutils_bin2coff(lua_State *lua);
tb_int_t xm_binutils_bin2macho(lua_State *lua);
tb_int_t xm_binutils_bin2elf(lua_State *lua);
tb_int_t xm_binutils_readsyms(lua_State *lua);
+tb_int_t xm_binutils_extractlib(lua_State *lua);
#ifdef XM_CONFIG_API_HAVE_CURSES
// register curses functions
@@ -663,6 +664,7 @@ static luaL_Reg const g_binutils_functions[] = {
{ "bin2macho", xm_binutils_bin2macho },
{ "bin2elf", xm_binutils_bin2elf },
{ "readsyms", xm_binutils_readsyms },
+ { "extractlib", xm_binutils_extractlib },
{ tb_null, tb_null },
};
diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh
index e40051419..8a8f1224c 100755
--- a/core/src/xmake/xmake.sh
+++ b/core/src/xmake/xmake.sh
@@ -76,6 +76,7 @@ target "xmake"
add_files "binutils/macho/*.c"
add_files "binutils/elf/*.c"
add_files "binutils/ar/*.c"
+ add_files "binutils/mslib/*.c"
add_files "thread/*.c"
if is_plat "mingw"; then
add_files "winos/*.c"
diff --git a/temp_lua/lapi.c.obj b/temp_lua/lapi.c.obj
new file mode 100644
index 000000000..8aa42030d
--- /dev/null
+++ b/temp_lua/lapi.c.obj
Binary files differ
diff --git a/temp_lua/lauxlib.c.obj b/temp_lua/lauxlib.c.obj
new file mode 100644
index 000000000..e5a558d91
--- /dev/null
+++ b/temp_lua/lauxlib.c.obj
Binary files differ
diff --git a/temp_lua/lbaselib.c.obj b/temp_lua/lbaselib.c.obj
new file mode 100644
index 000000000..3a38af9a0
--- /dev/null
+++ b/temp_lua/lbaselib.c.obj
Binary files differ
diff --git a/temp_lua/lcode.c.obj b/temp_lua/lcode.c.obj
new file mode 100644
index 000000000..cf94e9f85
--- /dev/null
+++ b/temp_lua/lcode.c.obj
Binary files differ
diff --git a/temp_lua/lcorolib.c.obj b/temp_lua/lcorolib.c.obj
new file mode 100644
index 000000000..0c4adb7ec
--- /dev/null
+++ b/temp_lua/lcorolib.c.obj
Binary files differ
diff --git a/temp_lua/lctype.c.obj b/temp_lua/lctype.c.obj
new file mode 100644
index 000000000..d5de2985a
--- /dev/null
+++ b/temp_lua/lctype.c.obj
Binary files differ
diff --git a/temp_lua/ldblib.c.obj b/temp_lua/ldblib.c.obj
new file mode 100644
index 000000000..fdf74211f
--- /dev/null
+++ b/temp_lua/ldblib.c.obj
Binary files differ
diff --git a/temp_lua/ldebug.c.obj b/temp_lua/ldebug.c.obj
new file mode 100644
index 000000000..0eabb7bc7
--- /dev/null
+++ b/temp_lua/ldebug.c.obj
Binary files differ
diff --git a/temp_lua/ldo.c.obj b/temp_lua/ldo.c.obj
new file mode 100644
index 000000000..405fa7cbb
--- /dev/null
+++ b/temp_lua/ldo.c.obj
Binary files differ
diff --git a/temp_lua/ldump.c.obj b/temp_lua/ldump.c.obj
new file mode 100644
index 000000000..68b60f71d
--- /dev/null
+++ b/temp_lua/ldump.c.obj
Binary files differ
diff --git a/temp_lua/lfunc.c.obj b/temp_lua/lfunc.c.obj
new file mode 100644
index 000000000..ae3388413
--- /dev/null
+++ b/temp_lua/lfunc.c.obj
Binary files differ
diff --git a/temp_lua/lgc.c.obj b/temp_lua/lgc.c.obj
new file mode 100644
index 000000000..31e3e39ae
--- /dev/null
+++ b/temp_lua/lgc.c.obj
Binary files differ
diff --git a/temp_lua/linit.c.obj b/temp_lua/linit.c.obj
new file mode 100644
index 000000000..0ae8e9e9d
--- /dev/null
+++ b/temp_lua/linit.c.obj
Binary files differ
diff --git a/temp_lua/liolib.c.obj b/temp_lua/liolib.c.obj
new file mode 100644
index 000000000..64c24b5d9
--- /dev/null
+++ b/temp_lua/liolib.c.obj
Binary files differ
diff --git a/temp_lua/llex.c.obj b/temp_lua/llex.c.obj
new file mode 100644
index 000000000..c0623fd2b
--- /dev/null
+++ b/temp_lua/llex.c.obj
Binary files differ
diff --git a/temp_lua/lmathlib.c.obj b/temp_lua/lmathlib.c.obj
new file mode 100644
index 000000000..88ca44d99
--- /dev/null
+++ b/temp_lua/lmathlib.c.obj
Binary files differ
diff --git a/temp_lua/lmem.c.obj b/temp_lua/lmem.c.obj
new file mode 100644
index 000000000..09a2aba8c
--- /dev/null
+++ b/temp_lua/lmem.c.obj
Binary files differ
diff --git a/temp_lua/loadlib.c.obj b/temp_lua/loadlib.c.obj
new file mode 100644
index 000000000..7d20ac41a
--- /dev/null
+++ b/temp_lua/loadlib.c.obj
Binary files differ
diff --git a/temp_lua/lobject.c.obj b/temp_lua/lobject.c.obj
new file mode 100644
index 000000000..d80831725
--- /dev/null
+++ b/temp_lua/lobject.c.obj
Binary files differ
diff --git a/temp_lua/lopcodes.c.obj b/temp_lua/lopcodes.c.obj
new file mode 100644
index 000000000..91c20ed66
--- /dev/null
+++ b/temp_lua/lopcodes.c.obj
Binary files differ
diff --git a/temp_lua/loslib.c.obj b/temp_lua/loslib.c.obj
new file mode 100644
index 000000000..a67559763
--- /dev/null
+++ b/temp_lua/loslib.c.obj
Binary files differ
diff --git a/temp_lua/lparser.c.obj b/temp_lua/lparser.c.obj
new file mode 100644
index 000000000..dcab37ee4
--- /dev/null
+++ b/temp_lua/lparser.c.obj
Binary files differ
diff --git a/temp_lua/lstate.c.obj b/temp_lua/lstate.c.obj
new file mode 100644
index 000000000..5d950083e
--- /dev/null
+++ b/temp_lua/lstate.c.obj
Binary files differ
diff --git a/temp_lua/lstring.c.obj b/temp_lua/lstring.c.obj
new file mode 100644
index 000000000..5a0ddcd9b
--- /dev/null
+++ b/temp_lua/lstring.c.obj
Binary files differ
diff --git a/temp_lua/lstrlib.c.obj b/temp_lua/lstrlib.c.obj
new file mode 100644
index 000000000..b6ff1b573
--- /dev/null
+++ b/temp_lua/lstrlib.c.obj
Binary files differ
diff --git a/temp_lua/ltable.c.obj b/temp_lua/ltable.c.obj
new file mode 100644
index 000000000..55cae3cf0
--- /dev/null
+++ b/temp_lua/ltable.c.obj
Binary files differ
diff --git a/temp_lua/ltablib.c.obj b/temp_lua/ltablib.c.obj
new file mode 100644
index 000000000..ab578e5b0
--- /dev/null
+++ b/temp_lua/ltablib.c.obj
Binary files differ
diff --git a/temp_lua/ltests.c.obj b/temp_lua/ltests.c.obj
new file mode 100644
index 000000000..f11c42dc0
--- /dev/null
+++ b/temp_lua/ltests.c.obj
Binary files differ
diff --git a/temp_lua/ltm.c.obj b/temp_lua/ltm.c.obj
new file mode 100644
index 000000000..46939ec33
--- /dev/null
+++ b/temp_lua/ltm.c.obj
Binary files differ
diff --git a/temp_lua/lundump.c.obj b/temp_lua/lundump.c.obj
new file mode 100644
index 000000000..b20a5d5ec
--- /dev/null
+++ b/temp_lua/lundump.c.obj
Binary files differ
diff --git a/temp_lua/lutf8lib.c.obj b/temp_lua/lutf8lib.c.obj
new file mode 100644
index 000000000..758fcd891
--- /dev/null
+++ b/temp_lua/lutf8lib.c.obj
Binary files differ
diff --git a/temp_lua/lvm.c.obj b/temp_lua/lvm.c.obj
new file mode 100644
index 000000000..b57ba4a1e
--- /dev/null
+++ b/temp_lua/lvm.c.obj
Binary files differ
diff --git a/temp_lua/lzio.c.obj b/temp_lua/lzio.c.obj
new file mode 100644
index 000000000..73b1fd65b
--- /dev/null
+++ b/temp_lua/lzio.c.obj
Binary files differ
diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua
index 77b17dc92..9ddcfcf0c 100644
--- a/xmake/core/base/binutils.lua
+++ b/xmake/core/base/binutils.lua
@@ -30,6 +30,7 @@ binutils._bin2coff = binutils._bin2coff or binutils.bin2coff
binutils._bin2macho = binutils._bin2macho or binutils.bin2macho
binutils._bin2elf = binutils._bin2elf or binutils.bin2elf
binutils._readsyms = binutils._readsyms or binutils.readsyms
+binutils._extractlib = binutils._extractlib or binutils.extractlib
-- generate c/c++ code from the binary file
function binutils.bin2c(binaryfile, outputfile, opt)
@@ -100,6 +101,26 @@ function binutils.readsyms(binaryfile)
end
end
+-- extract static library to directory
+-- Supports AR format (.a) and MSVC lib format (.lib)
+-- @param libraryfile the static library file path (.a or .lib)
+-- @param outputdir the output directory to extract object files
+-- @param opt the options (optional)
+-- - plain: extract all object files to the same directory (default: true)
+-- @return true on success, false and error message on failure
+function binutils.extractlib(libraryfile, outputdir, opt)
+ if binutils._extractlib then
+ local ok, errors = binutils._extractlib(libraryfile, outputdir, opt and opt.plain)
+ if ok then
+ return true
+ else
+ return false, errors or "extractlib: unknown error"
+ end
+ else
+ return false, "extractlib: C implementation not available"
+ end
+end
+
-- return module
return binutils
diff --git a/xmake/core/sandbox/modules/import/core/base/binutils.lua b/xmake/core/sandbox/modules/import/core/base/binutils.lua
index b9a502f8d..5bc0e4f72 100644
--- a/xmake/core/sandbox/modules/import/core/base/binutils.lua
+++ b/xmake/core/sandbox/modules/import/core/base/binutils.lua
@@ -52,6 +52,14 @@ function sandbox_core_base_binutils.readsyms(binaryfile)
end
end
+-- extract static library to directory
+function sandbox_core_base_binutils.extractlib(libraryfile, outputdir, opt)
+ local ok, errors = binutils.extractlib(libraryfile, outputdir, opt)
+ if not ok then
+ raise("extractlib: %s", errors or "unknown errors")
+ end
+end
+
-- return module
return sandbox_core_base_binutils
diff --git a/xmake/modules/cli/binutils/bin2c.lua b/xmake/modules/cli/binutils/bin2c.lua
new file mode 100644
index 000000000..83e0a88c1
--- /dev/null
+++ b/xmake/modules/cli/binutils/bin2c.lua
@@ -0,0 +1,49 @@
+--!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 bin2c.lua
+--
+
+-- imports
+import("core.base.option")
+import("utils.binary.bin2c")
+
+local options = {
+ {'w', "linewidth", "kv", nil, "Set the line width"},
+ {nil, "nozeroend", "k", false, "Disable to patch zero terminating character"},
+ {'i', "binarypath", "kv", nil, "Set the binary file path."},
+ {'o', "outputpath", "kv", nil, "Set the output file path."}
+}
+
+function main(...)
+
+ -- parse arguments
+ local argv = {...}
+ local opt = option.parse(argv, options, "Print c/c++ code files from the given binary file."
+ , ""
+ , "Usage: xmake l cli.binutils.bin2c [options]")
+
+ -- check arguments
+ if not opt.binarypath or not opt.outputpath then
+ cprint("${bright}Usage: $${clear}xmake l cli.binutils.bin2c [options]")
+ option.show_options(options, "bin2c")
+ return
+ end
+
+ -- do bin2c
+ bin2c.main(opt.binarypath, opt.outputpath, opt)
+end
diff --git a/xmake/modules/cli/binutils/bin2obj.lua b/xmake/modules/cli/binutils/bin2obj.lua
new file mode 100644
index 000000000..6ee6c2158
--- /dev/null
+++ b/xmake/modules/cli/binutils/bin2obj.lua
@@ -0,0 +1,54 @@
+--!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 bin2obj.lua
+--
+
+-- imports
+import("core.base.option")
+import("utils.binary.bin2obj")
+
+local options = {
+ {'i', "binarypath", "kv", nil, "Set the binary file path."},
+ {'o', "outputpath", "kv", nil, "Set the output object file path."},
+ {'f', "format", "kv", nil, "Set the object file format (coff, elf, macho)."},
+ {nil, "symbol_prefix", "kv", nil, "Set the symbol prefix (default: _binary_)."},
+ {'a', "arch", "kv", nil, "Set the target architecture."},
+ {'p', "plat", "kv", nil, "Set the target platform (macosx, iphoneos, etc.)."},
+ {nil, "target_minver", "kv", nil, "Set the target minimum version (e.g., 10.0, 18.2)."},
+ {nil, "xcode_sdkver", "kv", nil, "Set the Xcode SDK version (e.g., 10.0, 18.2)."},
+ {nil, "zeroend", "k", nil, "Append a null terminator ('\\0') at the end of data."}
+}
+
+function main(...)
+
+ -- parse arguments
+ local argv = {...}
+ local opt = option.parse(argv, options, "Convert binary file to object file for direct linking."
+ , ""
+ , "Usage: xmake l cli.binutils.bin2obj [options]")
+
+ -- check arguments
+ if not opt.binarypath or not opt.outputpath then
+ cprint("${bright}Usage: $${clear}xmake l cli.binutils.bin2obj [options]")
+ option.show_options(options, "bin2obj")
+ return
+ end
+
+ -- do bin2obj
+ bin2obj.main(opt.binarypath, opt.outputpath, opt)
+end
diff --git a/xmake/modules/utils/archive/merge_staticlib.lua b/xmake/modules/utils/archive/merge_staticlib.lua
index f75aa64f6..2246a926e 100644
--- a/xmake/modules/utils/archive/merge_staticlib.lua
+++ b/xmake/modules/utils/archive/merge_staticlib.lua
@@ -20,6 +20,7 @@
-- imports
import("core.base.option")
+import("core.base.binutils")
import("private.tools.vstool")
-- merge *.a archive libraries using libtool
@@ -30,56 +31,20 @@ end
-- merge *.a archive libraries using fallback method (extract and repack)
-- Used for platforms where ar does not support -M option (e.g., Solaris)
function _merge_for_ar_fallback(target, program, outputfile, libraryfiles, opt)
- -- we need to handle duplicate object file names by adding prefixes
- -- convert all library files to absolute paths before changing directory
- local libraryfiles_abs = {}
- for _, libraryfile in ipairs(libraryfiles) do
- if os.isfile(libraryfile) then
- table.insert(libraryfiles_abs, path.absolute(libraryfile))
- end
- end
- if #libraryfiles_abs == 0 then
- return
- end
+
+ -- extract all archives to the temporary directory
local tmpdir = os.tmpfile() .. ".dir"
os.mkdir(tmpdir)
- -- check for duplicate object file names and warn
- for idx, libraryfile_abs in ipairs(libraryfiles_abs) do
- local list = os.iorunv(program, {"-t", libraryfile_abs}, {curdir = tmpdir})
- if list then
- local seen_files = {}
- local duplicates = {}
- for _, line in ipairs(list:split("\n")) do
- line = line:trim()
- if line:endswith(".o") then
- if seen_files[line] then
- if not duplicates[line] then
- duplicates[line] = {}
- end
- table.insert(duplicates[line], libraryfile_abs)
- else
- seen_files[line] = true
- end
- end
- end
- if not table.empty(duplicates) then
- local dup_names = table.keys(duplicates)
- wprint("duplicate object file names found in %s: %s (some files may be lost during merge)",
- path.filename(libraryfile_abs), table.concat(dup_names, ", "))
- end
- end
+ for _, libraryfile in ipairs(libraryfiles) do
+ binutils.extractlib(libraryfile, tmpdir)
end
- -- extract and merge all archives
+
+ -- collect all object files
local objectfiles = {}
- for idx, libraryfile_abs in ipairs(libraryfiles_abs) do
- -- extract all files from this archive
- os.vrunv(program, {"-x", libraryfile_abs}, {curdir = tmpdir})
- -- collect extracted object files (duplicate names will be overwritten)
- for _, objfile in ipairs(os.files(path.join(tmpdir, "*.o"))) do
- -- use relative path (filename only) since ar will run with curdir = tmpdir
- table.insert(objectfiles, path.filename(objfile))
- end
+ for _, objectfile in ipairs(os.files(path.join(tmpdir, "*.o"))) do
+ table.insert(objectfiles, path.filename(objectfile))
end
+
-- create new archive with all object files
if #objectfiles > 0 then
os.mkdir(path.directory(outputfile))
diff --git a/xmake/modules/utils/binary/bin2c.lua b/xmake/modules/utils/binary/bin2c.lua
index 34d9c4e02..7bbc797a4 100644
--- a/xmake/modules/utils/binary/bin2c.lua
+++ b/xmake/modules/utils/binary/bin2c.lua
@@ -20,16 +20,8 @@
-- imports
import("core.base.bytes")
-import("core.base.option")
import("core.base.binutils")
-local options = {
- {'w', "linewidth", "kv", nil, "Set the line width"},
- {nil, "nozeroend", "k", false, "Disable to patch zero terminating character"},
- {'i', "binarypath", "kv", nil, "Set the binary file path."},
- {'o', "outputpath", "kv", nil, "Set the output file path."}
-}
-
function _do_dump(binarydata, outputfile, opt)
local i = 0
local n = 147
@@ -71,7 +63,7 @@ function _do_dump(binarydata, outputfile, opt)
end
end
-function _do_bin2c(binarypath, outputpath, opt)
+function main(binarypath, outputpath, opt)
-- init source directory and options
opt = opt or {}
@@ -117,15 +109,3 @@ function _do_bin2c(binarypath, outputpath, opt)
cprint("${bright}%s generated!", outputpath)
end
-function main(...)
-
- -- parse arguments
- local argv = {...}
- local opt = option.parse(argv, options, "Print c/c++ code files from the given binary file."
- , ""
- , "Usage: xmake l utils.binary.bin2c [options]")
-
- -- do bin2c
- _do_bin2c(opt.binarypath, opt.outputpath, opt)
-end
-
diff --git a/xmake/modules/utils/binary/bin2obj.lua b/xmake/modules/utils/binary/bin2obj.lua
index 95e705ca6..13ff633e1 100644
--- a/xmake/modules/utils/binary/bin2obj.lua
+++ b/xmake/modules/utils/binary/bin2obj.lua
@@ -19,22 +19,9 @@
--
-- imports
-import("core.base.option")
import("core.base.binutils")
-local options = {
- {'i', "binarypath", "kv", nil, "Set the binary file path."},
- {'o', "outputpath", "kv", nil, "Set the output object file path."},
- {'f', "format", "kv", nil, "Set the object file format (coff, elf, macho)."},
- {nil, "symbol_prefix", "kv", nil, "Set the symbol prefix (default: _binary_)."},
- {'a', "arch", "kv", nil, "Set the target architecture."},
- {'p', "plat", "kv", nil, "Set the target platform (macosx, iphoneos, etc.)."},
- {nil, "target_minver", "kv", nil, "Set the target minimum version (e.g., 10.0, 18.2)."},
- {nil, "xcode_sdkver", "kv", nil, "Set the Xcode SDK version (e.g., 10.0, 18.2)."},
- {nil, "zeroend", "k", nil, "Append a null terminator ('\\0') at the end of data."}
-}
-
-function _do_bin2obj(binarypath, outputpath, opt)
+function main(binarypath, outputpath, opt)
-- init source directory and options
opt = opt or {}
binarypath = path.absolute(binarypath)
@@ -66,15 +53,3 @@ function _do_bin2obj(binarypath, outputpath, opt)
cprint("${bright}%s generated!", outputpath)
end
-function main(...)
-
- -- parse arguments
- local argv = {...}
- local opt = option.parse(argv, options, "Convert binary file to object file for direct linking."
- , ""
- , "Usage: xmake l utils.binary.bin2obj [options]")
-
- -- do bin2obj
- _do_bin2obj(opt.binarypath, opt.outputpath, opt)
-end
-
diff --git a/xmake/modules/utils/binary/extractlib.lua b/xmake/modules/utils/binary/extractlib.lua
new file mode 100644
index 000000000..818d343bc
--- /dev/null
+++ b/xmake/modules/utils/binary/extractlib.lua
@@ -0,0 +1,51 @@
+--!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 extractlib.lua
+--
+
+-- imports
+import("core.base.binutils")
+
+-- extract static library to directory
+--
+-- @param libraryfile the static library file path (.a or .lib)
+-- @param outputdir the output directory to extract object files
+-- @param opt the options (optional)
+-- - plain: extract all object files to the same directory (default: true)
+--
+function main(libraryfile, outputdir, opt)
+ -- init paths
+ libraryfile = path.absolute(libraryfile)
+ outputdir = path.absolute(outputdir)
+ assert(os.isfile(libraryfile), "%s not found!", libraryfile)
+
+ -- ensure output directory exists
+ if not os.isdir(outputdir) then
+ os.mkdir(outputdir)
+ end
+
+ -- trace
+ print("extracting static library %s to %s ..", libraryfile, outputdir)
+
+ -- do extraction
+ binutils.extractlib(libraryfile, outputdir, opt)
+
+ -- trace
+ cprint("${bright}extraction completed!")
+end
+
diff --git a/xmake/modules/utils/binary/readsyms.lua b/xmake/modules/utils/binary/readsyms.lua
index 6058e8121..d8aa8674a 100644
--- a/xmake/modules/utils/binary/readsyms.lua
+++ b/xmake/modules/utils/binary/readsyms.lua
@@ -38,69 +38,62 @@ end
--
-- @param binaryfile the object file path (required)
function dump(binaryfile)
- local symbols = _get_symbols(binaryfile)
- if symbols and #symbols > 0 then
- -- calculate column widths for alignment
- local max_name_len = 0
- local max_type_len = 0
+ local objects = _get_symbols(binaryfile)
+ if objects and #objects > 0 then
+ for _, obj in ipairs(objects) do
+ local symbols = obj.symbols
+ if symbols and #symbols > 0 then
+ -- print object file
+ print("")
+ cprint("${bright}Object: %s", obj.objectfile)
+ print(string.rep("-", 80))
- for i, sym in ipairs(symbols) do
- if sym.name then
- max_name_len = math.max(max_name_len, #sym.name)
- end
- if sym.type then
- max_type_len = math.max(max_type_len, #sym.type)
- end
- end
+ -- calculate column widths for alignment
+ local max_name_len = 0
+ local max_type_len = 0
- -- calculate column widths
- local type_width = math.max(max_type_len, 4)
- local name_width = math.max(max_name_len, 4)
+ for i, sym in ipairs(symbols) do
+ if sym.name then
+ max_name_len = math.max(max_name_len, #sym.name)
+ end
+ if sym.type then
+ max_type_len = math.max(max_type_len, #sym.type)
+ end
+ end
- -- print header
- print("")
- print("Symbols:")
- local header_format = " %-" .. type_width .. "s %s"
- print(string.format(header_format, "TYPE", "NAME"))
- print(string.rep("-", 80))
+ -- calculate column widths
+ local type_width = math.max(max_type_len, 4)
+ local name_width = math.max(max_name_len, 4)
- -- print symbols
- local format_str = " %-" .. type_width .. "s %s"
+ -- print header
+ local header_format = " %-" .. type_width .. "s %s"
+ print(string.format(header_format, "TYPE", "NAME"))
- for i, sym in ipairs(symbols) do
- local type_str = sym.type or "unknown"
- local name_str = sym.name or ""
+ -- print symbols
+ local format_str = " %-" .. type_width .. "s %s"
- print(string.format(format_str, type_str, name_str))
+ for i, sym in ipairs(symbols) do
+ local type_str = sym.type or "unknown"
+ local name_str = sym.name or ""
+
+ print(string.format(format_str, type_str, name_str))
+ end
+ print("")
+ cprint("${bright}%d symbols found!", #symbols)
+ end
end
- print("")
- cprint("${bright}%d symbols found!", #symbols)
else
print("")
cprint("${bright}No symbols found!")
end
end
--- read symbols from object file(s) (auto-detect format: ELF, COFF, Mach-O)
+-- read symbols from object file (auto-detect format: ELF, COFF, Mach-O)
--
--- @param binaryfiles the object file path or table of object files (required)
--- @return the symbols table (all symbols from all files if table is provided)
-function main(binaryfiles)
- assert(binaryfiles, "usage: xmake l utils.binary.readsyms <binaryfile> or readsyms(binaryfiles)")
-
- local all_symbols = {}
- if type(binaryfiles) == "string" then
- return _get_symbols(binaryfiles)
- else
- for _, binaryfile in ipairs(binaryfiles) do
- local symbols = _get_symbols(binaryfile)
- if symbols then
- for _, sym in ipairs(symbols) do
- table.insert(all_symbols, sym)
- end
- end
- end
- return all_symbols
- end
+-- @param binaryfile the object file path (required)
+-- @return the symbols table
+function main(binaryfile)
+ assert(binaryfile, "usage: xmake l utils.binary.readsyms <binaryfile>")
+ return _get_symbols(binaryfile)
end
diff --git a/xmake/rules/utils/bin2c/utils.lua b/xmake/rules/utils/bin2c/utils.lua
index cf2f074f2..405c2ed62 100644
--- a/xmake/rules/utils/bin2c/utils.lua
+++ b/xmake/rules/utils/bin2c/utils.lua
@@ -91,7 +91,7 @@ function generate_headerfile(target, batchcmds, binaryfile, opt)
table.insert(argv, "--nozeroend")
end
- batchcmds:vlua("utils.binary.bin2c", argv)
+ batchcmds:vlua("cli.binutils.bin2c", argv)
return headerfile
end
diff --git a/xmake/rules/utils/bin2obj/utils.lua b/xmake/rules/utils/bin2obj/utils.lua
index b658928a1..0e2bba9fd 100644
--- a/xmake/rules/utils/bin2obj/utils.lua
+++ b/xmake/rules/utils/bin2obj/utils.lua
@@ -108,7 +108,7 @@ function generate_objectfile(target, batchcmds, binaryfile, opt)
if zeroend then
table.insert(argv, "--zeroend")
end
- batchcmds:vlua("utils.binary.bin2obj", argv)
+ batchcmds:vlua("cli.binutils.bin2obj", argv)
return objectfile
end
diff --git a/xmake/rules/utils/symbols/export_all/export_all.lua b/xmake/rules/utils/symbols/export_all/export_all.lua
index 6e077aaf3..fa75038d1 100644
--- a/xmake/rules/utils/symbols/export_all/export_all.lua
+++ b/xmake/rules/utils/symbols/export_all/export_all.lua
@@ -158,38 +158,38 @@ function _get_allsymbols_by_readsyms(target, opt)
_get_sourcefiles_map(target, sourcefiles_map)
end
local objectfiles = target:objectfiles()
- local symbols = readsyms(objectfiles)
- if symbols then
- for _, sym in ipairs(symbols) do
- if sym.name and sym.type then
- -- only export function symbols (T/t) for DLL exports
- -- skip data (D/d), bss (B/b), other sections (S/s), and undefined (U) symbols
- if sym.type == "T" or sym.type == "t" then
- local symbol = sym.name
- -- we need ignore DllMain, https://github.com/xmake-io/xmake/issues/3992
- if target:is_arch("x86") and symbol:startswith("_") and not symbol:startswith("__") and not symbol:startswith("_DllMain@") then
- symbol = symbol:sub(2)
- end
- if export_filter then
- -- find sourcefile for this symbol (approximate match)
- local sourcefile = nil
- for objfile, srcfile in pairs(sourcefiles_map) do
- if objfile:find(path.basename(symbol), 1, true) then
- sourcefile = srcfile
- break
- end
- end
- if export_filter(symbol, {sourcefile = sourcefile}) then
- allsymbols:insert(symbol)
- end
- elseif not symbol:startswith("__") then
- if export_classes or not symbol:startswith("?") then
- if export_classes then
- if not symbol:startswith("??_G") and not symbol:startswith("??_E") then
- allsymbols:insert(symbol)
+ for _, objectfile in ipairs(objectfiles) do
+ local objects = readsyms(objectfile)
+ if objects then
+ local sourcefile = sourcefiles_map[objectfile]
+ for _, obj in ipairs(objects) do
+ local symbols = obj.symbols
+ if symbols then
+ for _, sym in ipairs(symbols) do
+ if sym.name and sym.type then
+ -- only export function symbols (T/t) for DLL exports
+ -- skip data (D/d), bss (B/b), other sections (S/s), and undefined (U) symbols
+ if sym.type == "T" or sym.type == "t" then
+ local symbol = sym.name
+ -- we need ignore DllMain, https://github.com/xmake-io/xmake/issues/3992
+ if target:is_arch("x86") and symbol:startswith("_") and not symbol:startswith("__") and not symbol:startswith("_DllMain@") then
+ symbol = symbol:sub(2)
+ end
+ if export_filter then
+ if export_filter(symbol, {sourcefile = sourcefile}) then
+ allsymbols:insert(symbol)
+ end
+ elseif not symbol:startswith("__") then
+ if export_classes or not symbol:startswith("?") then
+ if export_classes then
+ if not symbol:startswith("??_G") and not symbol:startswith("??_E") then
+ allsymbols:insert(symbol)
+ end
+ else
+ allsymbols:insert(symbol)
+ end
+ end
end
- else
- allsymbols:insert(symbol)
end
end
end