diff options
| author | ruki <[email protected]> | 2025-12-11 23:24:49 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-12 09:00:44 +0800 |
| commit | 2821dc7bf3ccee9b725697836ac97b4036e6eb24 (patch) | |
| tree | 17700668a597d29aaa4af6bf9141d5f593401d33 /core/src/xmake/binutils | |
| parent | cec022212a4f04973d98534cb7c56767d2c656c5 (diff) | |
add mslib extract support
Diffstat (limited to 'core/src/xmake/binutils')
| -rw-r--r-- | core/src/xmake/binutils/extractlib.c | 15 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/extractlib.c | 205 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/prefix.h | 38 |
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 |
