summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/xmake/binutils/ar/extractlib.c296
-rw-r--r--core/src/xmake/binutils/extractlib.c126
-rw-r--r--core/src/xmake/binutils/mslib/extractlib.c52
-rw-r--r--core/src/xmake/binutils/mslib/prefix.h30
-rw-r--r--core/src/xmake/engine.c2
-rwxr-xr-xcore/src/xmake/xmake.sh1
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"