diff options
Diffstat (limited to 'core/src')
| -rw-r--r-- | core/src/xmake/binutils/ar/extractlib.c | 296 | ||||
| -rw-r--r-- | core/src/xmake/binutils/extractlib.c | 126 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/extractlib.c | 52 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/prefix.h | 30 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 2 | ||||
| -rwxr-xr-x | core/src/xmake/xmake.sh | 1 |
6 files changed, 507 insertions, 0 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..fb8ffa542 --- /dev/null +++ b/core/src/xmake/binutils/ar/extractlib.c @@ -0,0 +1,296 @@ +/*!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 + */ + +/* 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 + * @return tb_true on success, tb_false on failure + */ +static 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_assert_and_check_return_val(istream && header && name && name_size > 0 && name_len, tb_false); + + // check for extended name format (#N/L) + if (header->name[0] == '#' && header->name[1] == '/') { + // format: #N/L where N is name length, L is total data length (including name) + tb_int64_t name_length = xm_binutils_ar_parse_decimal(header->name + 2, 14); + if (name_length <= 0 || name_length >= (tb_int64_t)name_size) { + return tb_false; + } + + // read the actual name + if (!tb_stream_bread(istream, (tb_byte_t*)name, (tb_size_t)name_length)) { + return tb_false; + } + name[name_length] = '\0'; + *name_len = (tb_size_t)name_length; + return tb_true; + } else { + // 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; + 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) { + if (!name) return 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) { + if (!name) return 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; +} + +/* 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 + * @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_assert_and_check_return_val(base_name && output && output_size > 0, tb_false); + + // find the last dot for extension + tb_char_t const *ext = tb_strrchr(base_name, '.'); + 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) { + tb_snprintf(output, output_size, "%.*s_%u%s", (tb_int_t)base_len, base_name, id, ext); + return tb_true; + } + } else { + // no extension + if (tb_strlen(base_name) + 16 < output_size) { + tb_snprintf(output, output_size, "%s_%u", base_name, id); + 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); + + // check AR magic (!<arch>\n) + tb_uint8_t magic[8]; + if (!tb_stream_seek(istream, 0)) { + 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; + } + + // ensure output directory exists + if (!tb_directory_create(outputdir)) { + return tb_false; + } + + tb_bool_t ok = tb_true; + + // iterate through AR members + while (ok) { + // read AR header + 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; + if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len)) { + // skip this member + if (member_size > 0) { + tb_stream_seek(istream, tb_stream_offset(istream) + (tb_hize_t)member_size); + } + continue; + } + + // skip symbol tables + if (xm_binutils_ar_is_symbol_table(member_name)) { + if (member_size > 0) { + tb_stream_seek(istream, tb_stream_offset(istream) + (tb_hize_t)member_size); + } + continue; + } + + // only extract object files + if (!xm_binutils_ar_is_object_file(member_name)) { + if (member_size > 0) { + tb_stream_seek(istream, tb_stream_offset(istream) + (tb_hize_t)member_size); + } + 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}; + tb_snprintf(output_path_check, sizeof(output_path_check), "%s/%s", outputdir, member_name); + + // check if file already exists + tb_file_info_t info; + tb_uint32_t conflict_id = 1; + if (tb_file_info(output_path_check, &info)) { + // 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))) { + 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, &info)) { + // 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}; + 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 + tb_byte_t buffer[4096]; + tb_hize_t remaining = (tb_hize_t)member_size; + while (remaining > 0) { + tb_size_t to_read = (tb_size_t)tb_min(remaining, (tb_hize_t)sizeof(buffer)); + if (!tb_stream_bread(istream, buffer, to_read)) { + ok = tb_false; + break; + } + if (!tb_stream_bwrit(ostream, buffer, to_read)) { + ok = tb_false; + break; + } + remaining -= to_read; + } + + tb_stream_clos(ostream); + tb_stream_exit(ostream); + + if (!ok) { + break; + } + + // align to 2-byte boundary (AR format requirement) + tb_hize_t current_pos = tb_stream_offset(istream); + if (current_pos & 1) { + tb_byte_t padding; + tb_stream_bread(istream, &padding, 1); + } + + } + + return ok; +} + + diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c new file mode 100644 index 000000000..7a61d0bde --- /dev/null +++ b/core/src/xmake/binutils/extractlib.c @@ -0,0 +1,126 @@ +/*!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); + +/* ////////////////////////////////////////////////////////////////////////////////////// + * implementation + */ + +/* extract static library to directory (Lua interface) + * Supports AR format (.a) and MSVC lib format (.lib) + * + * @param lua the lua state + * @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); + + // 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; + do { + if (!tb_stream_open(istream)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "extractlib: open %s failed", libraryfile); + break; + } + + // detect format + tb_int_t format = xm_binutils_detect_format(istream); + if (format < 0) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "extractlib: cannot detect format of %s", libraryfile); + break; + } + + // extract based on format + if (format == XM_BINUTILS_FORMAT_AR) { + // AR archive format (.a or .lib in AR format) + if (!xm_binutils_ar_extract(istream, outputdir)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "extractlib: extract AR archive %s failed", libraryfile); + 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)) { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "extractlib: extract MSVC lib %s failed", libraryfile); + break; + } + ok = tb_true; + } else { + lua_pushboolean(lua, tb_false); + lua_pushfstring(lua, "extractlib: unsupported format for %s (only AR and MSVC lib are supported)", libraryfile); + break; + } + + if (ok) { + lua_pushboolean(lua, ok); + } + + } while (0); + + if (istream) { + tb_stream_clos(istream); + tb_stream_exit(istream); + } + + return ok ? 1 : 2; +} + diff --git a/core/src/xmake/binutils/mslib/extractlib.c b/core/src/xmake/binutils/mslib/extractlib.c new file mode 100644 index 000000000..5cc4a57b4 --- /dev/null +++ b/core/src/xmake/binutils/mslib/extractlib.c @@ -0,0 +1,52 @@ +/*!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 + */ + +/* extract MSVC lib archive to directory (placeholder, not yet implemented) + * + * @param istream the input stream + * @param outputdir the output 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_assert_and_check_return_val(istream && outputdir, tb_false); + + // TODO: implement MSVC lib extraction + // MSVC lib format extraction is not yet implemented + (void)istream; + (void)outputdir; + return tb_false; +} + diff --git a/core/src/xmake/binutils/mslib/prefix.h b/core/src/xmake/binutils/mslib/prefix.h new file mode 100644 index 000000000..9bd5fa4b6 --- /dev/null +++ b/core/src/xmake/binutils/mslib/prefix.h @@ -0,0 +1,30 @@ +/*!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" + +#endif + 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" |
