diff options
| author | ruki <[email protected]> | 2025-12-11 22:43:52 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-12 09:00:43 +0800 |
| commit | a6262f1fadbd0ae360dc336da298b86d4b9c1628 (patch) | |
| tree | f9522a852620a0b64d7e6c03efe6aa2d8ee28c67 /core/src/xmake/binutils | |
| parent | 32f8a196cec67ea6cc409819a82235a2caf43921 (diff) | |
extract success for ar
Diffstat (limited to 'core/src/xmake/binutils')
| -rw-r--r-- | core/src/xmake/binutils/ar/extractlib.c | 177 | ||||
| -rw-r--r-- | core/src/xmake/binutils/extractlib.c | 12 |
2 files changed, 127 insertions, 62 deletions
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; + } } |
