diff options
| author | ruki <[email protected]> | 2025-12-12 09:54:46 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-12-12 09:54:46 +0800 |
| commit | 7544d22a944cb4702534a8fe54924d74a71d3178 (patch) | |
| tree | 2df690db9f7ead09afde33fd321864c4d56b042a | |
| parent | 2427bd350baa6331c5dfd62b2ab291737c7e5f49 (diff) | |
| parent | e08e654aad4f829fcf1990dae32a14394055d96c (diff) | |
Merge pull request #7120 from xmake-io/ar
Add extractlib support in binutils
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*)§ions[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 Binary files differnew file mode 100644 index 000000000..8aa42030d --- /dev/null +++ b/temp_lua/lapi.c.obj diff --git a/temp_lua/lauxlib.c.obj b/temp_lua/lauxlib.c.obj Binary files differnew file mode 100644 index 000000000..e5a558d91 --- /dev/null +++ b/temp_lua/lauxlib.c.obj diff --git a/temp_lua/lbaselib.c.obj b/temp_lua/lbaselib.c.obj Binary files differnew file mode 100644 index 000000000..3a38af9a0 --- /dev/null +++ b/temp_lua/lbaselib.c.obj diff --git a/temp_lua/lcode.c.obj b/temp_lua/lcode.c.obj Binary files differnew file mode 100644 index 000000000..cf94e9f85 --- /dev/null +++ b/temp_lua/lcode.c.obj diff --git a/temp_lua/lcorolib.c.obj b/temp_lua/lcorolib.c.obj Binary files differnew file mode 100644 index 000000000..0c4adb7ec --- /dev/null +++ b/temp_lua/lcorolib.c.obj diff --git a/temp_lua/lctype.c.obj b/temp_lua/lctype.c.obj Binary files differnew file mode 100644 index 000000000..d5de2985a --- /dev/null +++ b/temp_lua/lctype.c.obj diff --git a/temp_lua/ldblib.c.obj b/temp_lua/ldblib.c.obj Binary files differnew file mode 100644 index 000000000..fdf74211f --- /dev/null +++ b/temp_lua/ldblib.c.obj diff --git a/temp_lua/ldebug.c.obj b/temp_lua/ldebug.c.obj Binary files differnew file mode 100644 index 000000000..0eabb7bc7 --- /dev/null +++ b/temp_lua/ldebug.c.obj diff --git a/temp_lua/ldo.c.obj b/temp_lua/ldo.c.obj Binary files differnew file mode 100644 index 000000000..405fa7cbb --- /dev/null +++ b/temp_lua/ldo.c.obj diff --git a/temp_lua/ldump.c.obj b/temp_lua/ldump.c.obj Binary files differnew file mode 100644 index 000000000..68b60f71d --- /dev/null +++ b/temp_lua/ldump.c.obj diff --git a/temp_lua/lfunc.c.obj b/temp_lua/lfunc.c.obj Binary files differnew file mode 100644 index 000000000..ae3388413 --- /dev/null +++ b/temp_lua/lfunc.c.obj diff --git a/temp_lua/lgc.c.obj b/temp_lua/lgc.c.obj Binary files differnew file mode 100644 index 000000000..31e3e39ae --- /dev/null +++ b/temp_lua/lgc.c.obj diff --git a/temp_lua/linit.c.obj b/temp_lua/linit.c.obj Binary files differnew file mode 100644 index 000000000..0ae8e9e9d --- /dev/null +++ b/temp_lua/linit.c.obj diff --git a/temp_lua/liolib.c.obj b/temp_lua/liolib.c.obj Binary files differnew file mode 100644 index 000000000..64c24b5d9 --- /dev/null +++ b/temp_lua/liolib.c.obj diff --git a/temp_lua/llex.c.obj b/temp_lua/llex.c.obj Binary files differnew file mode 100644 index 000000000..c0623fd2b --- /dev/null +++ b/temp_lua/llex.c.obj diff --git a/temp_lua/lmathlib.c.obj b/temp_lua/lmathlib.c.obj Binary files differnew file mode 100644 index 000000000..88ca44d99 --- /dev/null +++ b/temp_lua/lmathlib.c.obj diff --git a/temp_lua/lmem.c.obj b/temp_lua/lmem.c.obj Binary files differnew file mode 100644 index 000000000..09a2aba8c --- /dev/null +++ b/temp_lua/lmem.c.obj diff --git a/temp_lua/loadlib.c.obj b/temp_lua/loadlib.c.obj Binary files differnew file mode 100644 index 000000000..7d20ac41a --- /dev/null +++ b/temp_lua/loadlib.c.obj diff --git a/temp_lua/lobject.c.obj b/temp_lua/lobject.c.obj Binary files differnew file mode 100644 index 000000000..d80831725 --- /dev/null +++ b/temp_lua/lobject.c.obj diff --git a/temp_lua/lopcodes.c.obj b/temp_lua/lopcodes.c.obj Binary files differnew file mode 100644 index 000000000..91c20ed66 --- /dev/null +++ b/temp_lua/lopcodes.c.obj diff --git a/temp_lua/loslib.c.obj b/temp_lua/loslib.c.obj Binary files differnew file mode 100644 index 000000000..a67559763 --- /dev/null +++ b/temp_lua/loslib.c.obj diff --git a/temp_lua/lparser.c.obj b/temp_lua/lparser.c.obj Binary files differnew file mode 100644 index 000000000..dcab37ee4 --- /dev/null +++ b/temp_lua/lparser.c.obj diff --git a/temp_lua/lstate.c.obj b/temp_lua/lstate.c.obj Binary files differnew file mode 100644 index 000000000..5d950083e --- /dev/null +++ b/temp_lua/lstate.c.obj diff --git a/temp_lua/lstring.c.obj b/temp_lua/lstring.c.obj Binary files differnew file mode 100644 index 000000000..5a0ddcd9b --- /dev/null +++ b/temp_lua/lstring.c.obj diff --git a/temp_lua/lstrlib.c.obj b/temp_lua/lstrlib.c.obj Binary files differnew file mode 100644 index 000000000..b6ff1b573 --- /dev/null +++ b/temp_lua/lstrlib.c.obj diff --git a/temp_lua/ltable.c.obj b/temp_lua/ltable.c.obj Binary files differnew file mode 100644 index 000000000..55cae3cf0 --- /dev/null +++ b/temp_lua/ltable.c.obj diff --git a/temp_lua/ltablib.c.obj b/temp_lua/ltablib.c.obj Binary files differnew file mode 100644 index 000000000..ab578e5b0 --- /dev/null +++ b/temp_lua/ltablib.c.obj diff --git a/temp_lua/ltests.c.obj b/temp_lua/ltests.c.obj Binary files differnew file mode 100644 index 000000000..f11c42dc0 --- /dev/null +++ b/temp_lua/ltests.c.obj diff --git a/temp_lua/ltm.c.obj b/temp_lua/ltm.c.obj Binary files differnew file mode 100644 index 000000000..46939ec33 --- /dev/null +++ b/temp_lua/ltm.c.obj diff --git a/temp_lua/lundump.c.obj b/temp_lua/lundump.c.obj Binary files differnew file mode 100644 index 000000000..b20a5d5ec --- /dev/null +++ b/temp_lua/lundump.c.obj diff --git a/temp_lua/lutf8lib.c.obj b/temp_lua/lutf8lib.c.obj Binary files differnew file mode 100644 index 000000000..758fcd891 --- /dev/null +++ b/temp_lua/lutf8lib.c.obj diff --git a/temp_lua/lvm.c.obj b/temp_lua/lvm.c.obj Binary files differnew file mode 100644 index 000000000..b57ba4a1e --- /dev/null +++ b/temp_lua/lvm.c.obj diff --git a/temp_lua/lzio.c.obj b/temp_lua/lzio.c.obj Binary files differnew file mode 100644 index 000000000..73b1fd65b --- /dev/null +++ b/temp_lua/lzio.c.obj 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 |
