From 094b9b26786db5960947819680ccc49985f5ed55 Mon Sep 17 00:00:00 2001 From: ruki Date: Wed, 10 Dec 2025 00:48:15 +0800 Subject: add binutils extractlib --- xmake/modules/utils/binary/extractlib.lua | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 xmake/modules/utils/binary/extractlib.lua (limited to 'xmake/modules/utils/binary/extractlib.lua') diff --git a/xmake/modules/utils/binary/extractlib.lua b/xmake/modules/utils/binary/extractlib.lua new file mode 100644 index 000000000..254f06950 --- /dev/null +++ b/xmake/modules/utils/binary/extractlib.lua @@ -0,0 +1,64 @@ +--!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.option") +import("core.base.binutils") + +local options = { + {'i', "libraryfile", "kv", nil, "Set the static library file path (.a or .lib)."}, + {'o', "outputdir", "kv", nil, "Set the output directory to extract object files."} +} + +function _do_extractlib(libraryfile, outputdir) + -- 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 + local ok, errors = binutils.extractlib(libraryfile, outputdir) + if not ok then + raise("extractlib: %s", errors or "unknown error") + end + + -- trace + cprint("${bright}extraction completed!") +end + +function main(...) + -- parse arguments + local argv = {...} + local opt = option.parse(argv, options, "Extract object files from static library (AR or MSVC lib format)." + , "" + , "Usage: xmake l utils.binary.extractlib [options]") + + -- do extractlib + _do_extractlib(opt.libraryfile, opt.outputdir) +end + -- cgit v1.3.1 From a6262f1fadbd0ae360dc336da298b86d4b9c1628 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 11 Dec 2025 22:43:52 +0800 Subject: extract success for ar --- core/src/xmake/binutils/ar/extractlib.c | 177 ++++++++++++++++++++---------- core/src/xmake/binutils/extractlib.c | 12 +- xmake/modules/utils/binary/extractlib.lua | 5 +- 3 files changed, 128 insertions(+), 66 deletions(-) (limited to 'xmake/modules/utils/binary/extractlib.lua') diff --git a/core/src/xmake/binutils/ar/extractlib.c b/core/src/xmake/binutils/ar/extractlib.c index 2a5c354b9..5517eaceb 100644 --- a/core/src/xmake/binutils/ar/extractlib.c +++ b/core/src/xmake/binutils/ar/extractlib.c @@ -36,44 +36,94 @@ /* 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 + * @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_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); +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_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) - 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; + // 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; + } } - // 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; + 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 || total_length >= (tb_int64_t)name_size) { + return tb_false; } - name[i] = header->name[i]; + + // 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] = '\0'; - *name_len = i; - return tb_true; + 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 if member is a symbol table (should be skipped) @@ -175,6 +225,7 @@ tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outpu // 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 @@ -193,56 +244,65 @@ tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outpu tb_size_t name_len = 0; tb_hize_t name_bytes_read = 0; - // check if extended name format (#N/L) was used - if (header.name[0] == '#' && header.name[1] == '/') { - // extended name: name is read from stream, so we need to track bytes read - if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len)) { - // skip this member - if (member_size > 0) { - if (!tb_stream_seek(istream, tb_stream_offset(istream) + (tb_hize_t)member_size)) { - ok = tb_false; - break; - } + // get member name (handles both regular and extended name formats) + if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len, &name_bytes_read)) { + // skip this member using sequential read + if (member_size > name_bytes_read) { + tb_hize_t data_size = (tb_hize_t)member_size - name_bytes_read; + if (!tb_stream_skip(istream, data_size)) { + ok = tb_false; + break; } - continue; } - // name was read from stream, adjust member_size - name_bytes_read = (tb_hize_t)name_len; - } else { - // regular name: name is in header, not read from stream - if (!xm_binutils_ar_get_member_name(istream, &header, member_name, sizeof(member_name), &name_len)) { - // skip this member - if (member_size > 0) { - if (!tb_stream_seek(istream, tb_stream_offset(istream) + (tb_hize_t)member_size)) { - ok = tb_false; - break; - } + continue; + } + + // check if extended name format was used (name starts with '#' and was read from stream) + // In extended format, the name is read from the stream, so we need to track bytes read + if (header.name[0] == '#') { + // find the '/' separator to confirm it's extended format + tb_size_t slash_pos = 0; + for (tb_size_t i = 1; i < 16; i++) { + if (header.name[i] == '/') { + slash_pos = i; + break; } - continue; + } + if (slash_pos > 0) { + // extended name: name was read from stream + // name_bytes_read is already set by xm_binutils_ar_get_member_name } } // skip symbol tables if (xm_binutils_ar_is_symbol_table(member_name)) { + // Skip remaining data using sequential read + // member_size is the total size including the name section + // name_bytes_read is the size of the name section (from #N/L format) + // So we need to skip: member_size - name_bytes_read if (member_size > name_bytes_read) { tb_hize_t data_size = (tb_hize_t)member_size - name_bytes_read; - if (!tb_stream_seek(istream, tb_stream_offset(istream) + data_size)) { + if (!tb_stream_skip(istream, data_size)) { ok = tb_false; break; } } + // AR format requires 2-byte alignment, but member_size already accounts for this + // So we don't need additional alignment here continue; } // only extract object files if (!xm_binutils_ar_is_object_file(member_name)) { + // Skip remaining data using sequential read if (member_size > name_bytes_read) { tb_hize_t data_size = (tb_hize_t)member_size - name_bytes_read; - if (!tb_stream_seek(istream, tb_stream_offset(istream) + data_size)) { + if (!tb_stream_skip(istream, data_size)) { ok = tb_false; break; } } + // AR format requires 2-byte alignment, but member_size already accounts for this continue; } @@ -322,7 +382,10 @@ tb_bool_t xm_binutils_ar_extract(tb_stream_ref_t istream, tb_char_t const *outpu tb_hize_t current_pos = tb_stream_offset(istream); if (current_pos & 1) { tb_byte_t padding; - tb_stream_bread(istream, &padding, 1); + if (!tb_stream_bread(istream, &padding, 1)) { + ok = tb_false; + break; + } } } diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c index 7a61d0bde..9385bc60f 100644 --- a/core/src/xmake/binutils/extractlib.c +++ b/core/src/xmake/binutils/extractlib.c @@ -110,10 +110,6 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) { break; } - if (ok) { - lua_pushboolean(lua, ok); - } - } while (0); if (istream) { @@ -121,6 +117,12 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) { tb_stream_exit(istream); } - return ok ? 1 : 2; + if (ok) { + lua_pushboolean(lua, tb_true); + return 1; + } else { + // error message should already be pushed + return 2; + } } diff --git a/xmake/modules/utils/binary/extractlib.lua b/xmake/modules/utils/binary/extractlib.lua index 254f06950..d707ac22f 100644 --- a/xmake/modules/utils/binary/extractlib.lua +++ b/xmake/modules/utils/binary/extractlib.lua @@ -42,10 +42,7 @@ function _do_extractlib(libraryfile, outputdir) print("extracting static library %s to %s ..", libraryfile, outputdir) -- do extraction - local ok, errors = binutils.extractlib(libraryfile, outputdir) - if not ok then - raise("extractlib: %s", errors or "unknown error") - end + binutils.extractlib(libraryfile, outputdir) -- trace cprint("${bright}extraction completed!") -- cgit v1.3.1 From c8d424baeb7b93e2a1e900b271bcc09144fdce94 Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 11 Dec 2025 23:01:24 +0800 Subject: improve binutils and add cli modules --- xmake/modules/cli/binutils/bin2c.lua | 42 +++++++++++++++++++++++++++ xmake/modules/cli/binutils/bin2obj.lua | 47 +++++++++++++++++++++++++++++++ xmake/modules/utils/binary/bin2c.lua | 22 +-------------- xmake/modules/utils/binary/bin2obj.lua | 27 +----------------- xmake/modules/utils/binary/extractlib.lua | 19 +------------ xmake/rules/utils/bin2c/utils.lua | 2 +- xmake/rules/utils/bin2obj/utils.lua | 2 +- 7 files changed, 94 insertions(+), 67 deletions(-) create mode 100644 xmake/modules/cli/binutils/bin2c.lua create mode 100644 xmake/modules/cli/binutils/bin2obj.lua (limited to 'xmake/modules/utils/binary/extractlib.lua') diff --git a/xmake/modules/cli/binutils/bin2c.lua b/xmake/modules/cli/binutils/bin2c.lua new file mode 100644 index 000000000..f2b03acdf --- /dev/null +++ b/xmake/modules/cli/binutils/bin2c.lua @@ -0,0 +1,42 @@ +--!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]") + + -- 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..33eb0511e --- /dev/null +++ b/xmake/modules/cli/binutils/bin2obj.lua @@ -0,0 +1,47 @@ +--!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]") + + -- do bin2obj + bin2obj.main(opt.binarypath, opt.outputpath, opt) +end 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 index d707ac22f..9097db73a 100644 --- a/xmake/modules/utils/binary/extractlib.lua +++ b/xmake/modules/utils/binary/extractlib.lua @@ -19,15 +19,9 @@ -- -- imports -import("core.base.option") import("core.base.binutils") -local options = { - {'i', "libraryfile", "kv", nil, "Set the static library file path (.a or .lib)."}, - {'o', "outputdir", "kv", nil, "Set the output directory to extract object files."} -} - -function _do_extractlib(libraryfile, outputdir) +function main(libraryfile, outputdir) -- init paths libraryfile = path.absolute(libraryfile) outputdir = path.absolute(outputdir) @@ -48,14 +42,3 @@ function _do_extractlib(libraryfile, outputdir) cprint("${bright}extraction completed!") end -function main(...) - -- parse arguments - local argv = {...} - local opt = option.parse(argv, options, "Extract object files from static library (AR or MSVC lib format)." - , "" - , "Usage: xmake l utils.binary.extractlib [options]") - - -- do extractlib - _do_extractlib(opt.libraryfile, opt.outputdir) -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 -- cgit v1.3.1 From 4ccb8514f37fdf7108b41dbcd8ca77143841e1cd Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 11 Dec 2025 23:35:23 +0800 Subject: set plain as default --- core/src/xmake/binutils/extractlib.c | 17 ++++- core/src/xmake/binutils/mslib/extractlib.c | 88 ++++++++++++++++++++-- xmake/core/base/binutils.lua | 6 +- .../sandbox/modules/import/core/base/binutils.lua | 4 +- xmake/modules/utils/binary/extractlib.lua | 11 ++- 5 files changed, 112 insertions(+), 14 deletions(-) (limited to 'xmake/modules/utils/binary/extractlib.lua') diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c index d5e8b3759..ec588f213 100644 --- a/core/src/xmake/binutils/extractlib.c +++ b/core/src/xmake/binutils/extractlib.c @@ -36,7 +36,7 @@ * 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); +extern tb_bool_t xm_binutils_mslib_extract(tb_stream_ref_t istream, tb_char_t const *outputdir, tb_bool_t plain); /* ////////////////////////////////////////////////////////////////////////////////////// * implementation @@ -46,6 +46,11 @@ extern tb_bool_t xm_binutils_mslib_extract(tb_stream_ref_t istream, tb_char_t co * 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) { @@ -59,6 +64,12 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) { 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) { @@ -88,7 +99,7 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) { // 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)) { + if (!xm_binutils_mslib_extract(istream, outputdir, plain)) { error_msg = "extract MSVC lib failed"; break; } @@ -105,7 +116,7 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) { // 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)) { + if (!xm_binutils_mslib_extract(istream, outputdir, plain)) { error_msg = "extract MSVC lib failed"; break; } diff --git a/core/src/xmake/binutils/mslib/extractlib.c b/core/src/xmake/binutils/mslib/extractlib.c index f56d3eb3e..5dec97f3e 100644 --- a/core/src/xmake/binutils/mslib/extractlib.c +++ b/core/src/xmake/binutils/mslib/extractlib.c @@ -34,13 +34,49 @@ * 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 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 (!\n) @@ -158,11 +194,53 @@ tb_bool_t xm_binutils_mslib_extract(tb_stream_ref_t istream, tb_char_t const *ou // check output path length tb_char_t output_path[1024]; - if (tb_strlen(outputdir) + 1 + name_len >= sizeof(output_path)) { - ok = tb_false; - break; + 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++; + } + if (!ok || 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); } - tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, member_name); // ensure directory exists tb_char_t const* p = tb_strrchr(output_path, '/'); diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua index ea5d04ba8..9ddcfcf0c 100644 --- a/xmake/core/base/binutils.lua +++ b/xmake/core/base/binutils.lua @@ -105,10 +105,12 @@ end -- 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) +function binutils.extractlib(libraryfile, outputdir, opt) if binutils._extractlib then - local ok, errors = binutils._extractlib(libraryfile, outputdir) + local ok, errors = binutils._extractlib(libraryfile, outputdir, opt and opt.plain) if ok then return true else diff --git a/xmake/core/sandbox/modules/import/core/base/binutils.lua b/xmake/core/sandbox/modules/import/core/base/binutils.lua index e2306a2da..5bc0e4f72 100644 --- a/xmake/core/sandbox/modules/import/core/base/binutils.lua +++ b/xmake/core/sandbox/modules/import/core/base/binutils.lua @@ -53,8 +53,8 @@ function sandbox_core_base_binutils.readsyms(binaryfile) end -- extract static library to directory -function sandbox_core_base_binutils.extractlib(libraryfile, outputdir) - local ok, errors = binutils.extractlib(libraryfile, outputdir) +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 diff --git a/xmake/modules/utils/binary/extractlib.lua b/xmake/modules/utils/binary/extractlib.lua index 9097db73a..818d343bc 100644 --- a/xmake/modules/utils/binary/extractlib.lua +++ b/xmake/modules/utils/binary/extractlib.lua @@ -21,7 +21,14 @@ -- imports import("core.base.binutils") -function main(libraryfile, outputdir) +-- 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) @@ -36,7 +43,7 @@ function main(libraryfile, outputdir) print("extracting static library %s to %s ..", libraryfile, outputdir) -- do extraction - binutils.extractlib(libraryfile, outputdir) + binutils.extractlib(libraryfile, outputdir, opt) -- trace cprint("${bright}extraction completed!") -- cgit v1.3.1