summaryrefslogtreecommitdiff
path: root/core/src/xmake/binutils
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-11 23:24:49 +0800
committerruki <[email protected]>2025-12-12 09:00:44 +0800
commit2821dc7bf3ccee9b725697836ac97b4036e6eb24 (patch)
tree17700668a597d29aaa4af6bf9141d5f593401d33 /core/src/xmake/binutils
parentcec022212a4f04973d98534cb7c56767d2c656c5 (diff)
add mslib extract support
Diffstat (limited to 'core/src/xmake/binutils')
-rw-r--r--core/src/xmake/binutils/extractlib.c15
-rw-r--r--core/src/xmake/binutils/mslib/extractlib.c205
-rw-r--r--core/src/xmake/binutils/mslib/prefix.h38
3 files changed, 246 insertions, 12 deletions
diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c
index e1d76c28e..d5e8b3759 100644
--- a/core/src/xmake/binutils/extractlib.c
+++ b/core/src/xmake/binutils/extractlib.c
@@ -85,9 +85,18 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) {
// 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)) {
- error_msg = "extract AR archive failed";
- break;
+ // 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)) {
+ 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) {
diff --git a/core/src/xmake/binutils/mslib/extractlib.c b/core/src/xmake/binutils/mslib/extractlib.c
index 5cc4a57b4..c09734468 100644
--- a/core/src/xmake/binutils/mslib/extractlib.c
+++ b/core/src/xmake/binutils/mslib/extractlib.c
@@ -34,7 +34,7 @@
* implementation
*/
-/* extract MSVC lib archive to directory (placeholder, not yet implemented)
+/* extract MSVC lib archive to directory
*
* @param istream the input stream
* @param outputdir the output directory
@@ -42,11 +42,200 @@
*/
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;
-}
+ // check magic (!<arch>\n)
+ if (!xm_binutils_mslib_check_magic(istream)) {
+ return tb_false;
+ }
+
+ tb_trace_d("extracting mslib to %s", outputdir);
+
+ // 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_byte_t* buffer = tb_null;
+ 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;
+ tb_trace_d("found longname table, size: %lld", member_size);
+ } else if (tb_isdigit(header.name[1])) {
+ // offset into long name table (/123)
+ tb_long_t offset = xm_binutils_mslib_parse_decimal(header.name + 1, 15);
+ if (offset >= 0 && 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) {
+ if (longnames) tb_free(longnames);
+ longnames = (tb_char_t*)tb_malloc_bytes((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 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) {
+
+ tb_trace_d("skipping member: %s", member_name);
+
+ // 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 (tb_strlen(outputdir) + 1 + name_len >= sizeof(output_path)) {
+ tb_trace_e("output path is too long!");
+ ok = tb_false;
+ break;
+ }
+ tb_snprintf(output_path, sizeof(output_path), "%s/%s", outputdir, member_name);
+
+ tb_trace_d("extracting member: %s to %s", member_name, output_path);
+
+ // 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) {
+ ok = tb_false;
+ break;
+ }
+
+ if (!tb_stream_open(ostream)) {
+ tb_stream_exit(ostream);
+ ok = tb_false;
+ break;
+ }
+
+ // copy data
+ if (!buffer) buffer = tb_malloc_bytes(TB_STREAM_BLOCK_MAXN);
+ if (!buffer) {
+ tb_stream_exit(ostream);
+ ok = tb_false;
+ break;
+ }
+
+ tb_hize_t remaining = member_size;
+ while (remaining > 0) {
+ tb_size_t to_read = (tb_size_t)tb_min(remaining, TB_STREAM_BLOCK_MAXN);
+ 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_exit(ostream);
+ if (!ok) break;
+
+ // align to 2-byte boundary
+ if (member_size % 2) {
+ if (!tb_stream_skip(istream, 1)) {
+ ok = tb_false;
+ break;
+ }
+ }
+ }
+
+ if (buffer) tb_free(buffer);
+ 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
index 9bd5fa4b6..1ea9e40b2 100644
--- a/core/src/xmake/binutils/mslib/prefix.h
+++ b/core/src/xmake/binutils/mslib/prefix.h
@@ -26,5 +26,41 @@
*/
#include "../prefix.h"
-#endif
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * types
+ */
+
+// MSVC lib header
+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];
+} xm_mslib_header_t;
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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