summaryrefslogtreecommitdiff
path: root/core/src/xmake/binutils
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-11 22:43:52 +0800
committerruki <[email protected]>2025-12-12 09:00:43 +0800
commita6262f1fadbd0ae360dc336da298b86d4b9c1628 (patch)
treef9522a852620a0b64d7e6c03efe6aa2d8ee28c67 /core/src/xmake/binutils
parent32f8a196cec67ea6cc409819a82235a2caf43921 (diff)
extract success for ar
Diffstat (limited to 'core/src/xmake/binutils')
-rw-r--r--core/src/xmake/binutils/ar/extractlib.c177
-rw-r--r--core/src/xmake/binutils/extractlib.c12
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;
+ }
}