summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-09 00:46:58 +0800
committerruki <[email protected]>2025-12-09 00:46:58 +0800
commit9459655ebc9cca1f945c71aa79dbe27a921f827e (patch)
tree7cb8a6b32de06ea51a56102e48069ef7bcb32ac7
parentd1849e1b1a9d5accc1a3555272e27c96b863bbd8 (diff)
read symbols from .a
-rw-r--r--core/src/xmake/binutils/ar/prefix.h82
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c242
-rw-r--r--core/src/xmake/binutils/coff/prefix.h1
-rw-r--r--core/src/xmake/binutils/elf/prefix.h1
-rw-r--r--core/src/xmake/binutils/macho/prefix.h1
-rw-r--r--core/src/xmake/binutils/prefix.h22
-rw-r--r--core/src/xmake/binutils/readsyms.c10
-rwxr-xr-xcore/src/xmake/xmake.sh1
8 files changed, 354 insertions, 6 deletions
diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h
new file mode 100644
index 000000000..406838d6f
--- /dev/null
+++ b/core/src/xmake/binutils/ar/prefix.h
@@ -0,0 +1,82 @@
+/*!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_AR_PREFIX_H
+#define XM_BINUTILS_AR_PREFIX_H
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "../prefix.h"
+#include "../coff/prefix.h"
+#include "../elf/prefix.h"
+#include "../macho/prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * forward declarations
+ */
+extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream);
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * types
+ */
+#include "tbox/prefix/packed.h"
+typedef struct __xm_ar_header_t {
+ tb_char_t name[16]; // file name (null-padded)
+ tb_char_t date[12]; // modification time (decimal)
+ tb_char_t uid[6]; // user ID (decimal)
+ tb_char_t gid[6]; // group ID (decimal)
+ tb_char_t mode[8]; // file mode (octal)
+ tb_char_t size[10]; // file size (decimal)
+ tb_char_t fmag[2]; // magic: "`\n"
+} __tb_packed__ xm_ar_header_t;
+#include "tbox/prefix/packed.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * inline implementation
+ */
+
+/* parse decimal number from string
+ *
+ * @param str the string
+ * @param len the length
+ * @return the parsed number, or -1 on error
+ */
+static __tb_inline__ tb_int64_t xm_binutils_ar_parse_decimal(tb_char_t const *str, tb_size_t len) {
+ tb_assert_and_check_return_val(str && len > 0, -1);
+
+ tb_int64_t result = 0;
+ for (tb_size_t i = 0; i < len; i++) {
+ if (str[i] == ' ' || str[i] == '\0') {
+ break;
+ }
+ if (str[i] < '0' || str[i] > '9') {
+ return -1;
+ }
+ result = result * 10 + (str[i] - '0');
+ }
+ return result;
+}
+
+#endif
+
diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c
new file mode 100644
index 000000000..7f5f0b077
--- /dev/null
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -0,0 +1,242 @@
+/*!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 readsyms.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "ar_readsyms"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* read symbols from AR archive
+ *
+ * @param istream the input stream
+ * @param lua the lua state
+ * @return tb_true on success, tb_false on failure
+ */
+tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, lua_State *lua) {
+ tb_assert_and_check_return_val(istream && lua, tb_false);
+
+ // create result table
+ lua_newtable(lua);
+
+ // skip AR magic (!<arch>\n or !<arch>\r\n)
+ if (!tb_stream_seek(istream, 0)) {
+ return tb_false;
+ }
+ tb_uint8_t magic[8];
+ if (!tb_stream_bread(istream, magic, 8)) {
+ return tb_false;
+ }
+ // check AR magic: !<arch>
+ if (magic[0] != '!' || magic[1] != '<' || magic[2] != 'a' ||
+ magic[3] != 'r' || magic[4] != 'c' || magic[5] != 'h' ||
+ magic[6] != '>') {
+ return tb_false;
+ }
+ // handle both \n and \r\n
+ if (magic[7] == '\n') {
+ // Standard format: !<arch>\n
+ // already at position 8, ready to read first member
+ } else if (magic[7] == '\r') {
+ // Windows format: !<arch>\r\n, read one more byte
+ tb_uint8_t next_byte;
+ if (!tb_stream_bread(istream, &next_byte, 1) || next_byte != '\n') {
+ return tb_false;
+ }
+ } else {
+ return tb_false;
+ }
+
+ tb_uint32_t result_count = 0;
+ tb_hize_t file_size = tb_stream_size(istream);
+
+ // read archive members
+ while (tb_stream_offset(istream) < file_size) {
+ // read member header
+ xm_ar_header_t header;
+ if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) {
+ break;
+ }
+
+ // check magic
+ if (header.fmag[0] != '`' || header.fmag[1] != '\n') {
+ break;
+ }
+
+ // parse file size
+ tb_int64_t member_size = xm_binutils_ar_parse_decimal(header.size, 10);
+ if (member_size < 0 || member_size == 0) {
+ break;
+ }
+
+ // get member name (handle extended names)
+ tb_char_t member_name[256] = {0};
+ tb_size_t name_len = 0;
+ if (header.name[0] == '/' && header.name[1] >= '0' && header.name[1] <= '9') {
+ // extended name reference: /123 (offset in string table)
+ // For simplicity, skip extended names for now
+ // TODO: implement extended name support
+ } else {
+ // regular name
+ for (tb_size_t i = 0; i < 16 && header.name[i] != ' ' && header.name[i] != '/' && header.name[i] != '\0'; i++) {
+ member_name[name_len++] = header.name[i];
+ }
+ // remove trailing '/'
+ if (name_len > 0 && member_name[name_len - 1] == '/') {
+ name_len--;
+ }
+ member_name[name_len] = '\0';
+ }
+
+ // skip special members (symbol table, string table, etc.)
+ if (member_name[0] == '/' || name_len == 0) {
+ // skip to next member (align to 2 bytes)
+ tb_hize_t current_pos = tb_stream_offset(istream);
+ tb_hize_t next_pos = current_pos + member_size;
+ if (next_pos & 1) {
+ next_pos++; // align to 2 bytes
+ }
+ if (!tb_stream_seek(istream, next_pos)) {
+ break;
+ }
+ continue;
+ }
+
+ // read member data
+ tb_byte_t *member_data = (tb_byte_t*)tb_malloc((tb_size_t)member_size);
+ if (!member_data) {
+ break;
+ }
+
+ if (!tb_stream_bread(istream, member_data, (tb_size_t)member_size)) {
+ tb_free(member_data);
+ break;
+ }
+
+ // create stream from member data
+ tb_stream_ref_t member_stream = tb_stream_init_from_data(member_data, (tb_size_t)member_size);
+ if (!member_stream) {
+ tb_free(member_data);
+ break;
+ }
+
+ if (!tb_stream_open(member_stream)) {
+ tb_free(member_data);
+ tb_stream_exit(member_stream);
+ break;
+ }
+
+ // detect member format and read symbols
+ tb_int_t member_format = xm_binutils_detect_format(member_stream);
+ if (member_format == XM_BINUTILS_FORMAT_COFF) {
+ // create temporary table for member symbols
+ lua_newtable(lua);
+ if (xm_binutils_coff_read_symbols(member_stream, lua)) {
+ // merge symbols into result table
+ // Stack: result_table(-2), member_table(-1)
+ tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1);
+ for (tb_int_t i = 1; i <= member_sym_count; i++) {
+ lua_pushinteger(lua, i);
+ lua_gettable(lua, -2); // get member_table[i]
+ // Stack: result_table(-3), member_table(-2), symbol(-1)
+ if (lua_istable(lua, -1)) {
+ lua_pushinteger(lua, result_count + 1);
+ lua_pushvalue(lua, -2); // copy symbol
+ // Stack: result_table(-5), member_table(-4), symbol(-3), index(-2), symbol(-1)
+ lua_settable(lua, -5); // result_table[index] = symbol
+ // Stack: result_table(-3), member_table(-2)
+ result_count++;
+ }
+ lua_pop(lua, 1); // remove symbol
+ // Stack: result_table(-2), member_table(-1)
+ }
+ }
+ lua_pop(lua, 1); // remove temporary table
+ // Stack: result_table(-1)
+ } else if (member_format == XM_BINUTILS_FORMAT_ELF) {
+ // create temporary table for member symbols
+ lua_newtable(lua);
+ if (xm_binutils_elf_read_symbols(member_stream, lua)) {
+ // merge symbols into result table
+ tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1);
+ for (tb_int_t i = 1; i <= member_sym_count; i++) {
+ lua_pushinteger(lua, i);
+ lua_gettable(lua, -2);
+ if (lua_istable(lua, -1)) {
+ lua_pushinteger(lua, result_count + 1);
+ lua_pushvalue(lua, -2);
+ lua_settable(lua, -5);
+ result_count++;
+ }
+ lua_pop(lua, 1);
+ }
+ }
+ lua_pop(lua, 1); // remove temporary table
+ } else if (member_format == XM_BINUTILS_FORMAT_MACHO) {
+ // create temporary table for member symbols
+ lua_newtable(lua);
+ if (xm_binutils_macho_read_symbols(member_stream, lua)) {
+ // merge symbols into result table
+ tb_int_t member_sym_count = (tb_int_t)luaL_len(lua, -1);
+ for (tb_int_t i = 1; i <= member_sym_count; i++) {
+ lua_pushinteger(lua, i);
+ lua_gettable(lua, -2);
+ if (lua_istable(lua, -1)) {
+ lua_pushinteger(lua, result_count + 1);
+ lua_pushvalue(lua, -2);
+ lua_settable(lua, -5);
+ result_count++;
+ }
+ lua_pop(lua, 1);
+ }
+ }
+ lua_pop(lua, 1); // remove temporary table
+ }
+
+ // cleanup
+ tb_stream_clos(member_stream);
+ tb_stream_exit(member_stream);
+ // Note: tb_stream_init_from_data sets bref=true, so it only references the data.
+ // We need to free the data after the stream is closed.
+ tb_free(member_data);
+
+ // align to 2 bytes for next member
+ tb_hize_t current_pos = tb_stream_offset(istream);
+ if (current_pos & 1) {
+ tb_byte_t padding;
+ if (!tb_stream_bread(istream, &padding, 1)) {
+ break;
+ }
+ }
+ }
+
+ return tb_true;
+}
+
diff --git a/core/src/xmake/binutils/coff/prefix.h b/core/src/xmake/binutils/coff/prefix.h
index b9732a4ec..587d724df 100644
--- a/core/src/xmake/binutils/coff/prefix.h
+++ b/core/src/xmake/binutils/coff/prefix.h
@@ -99,6 +99,7 @@ typedef struct __xm_coff_symbol_tail_t {
tb_uint8_t scl;
tb_uint8_t naux;
} __tb_packed__ xm_coff_symbol_tail_t;
+#include "tbox/prefix/packed.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* inline implementation
diff --git a/core/src/xmake/binutils/elf/prefix.h b/core/src/xmake/binutils/elf/prefix.h
index 6eba9ac51..d79fe3e11 100644
--- a/core/src/xmake/binutils/elf/prefix.h
+++ b/core/src/xmake/binutils/elf/prefix.h
@@ -137,6 +137,7 @@ typedef struct __xm_elf64_symbol_t {
tb_uint64_t st_value;
tb_uint64_t st_size;
} __tb_packed__ xm_elf64_symbol_t;
+#include "tbox/prefix/packed.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* inline implementation
diff --git a/core/src/xmake/binutils/macho/prefix.h b/core/src/xmake/binutils/macho/prefix.h
index 2179ac344..bc7efb6d2 100644
--- a/core/src/xmake/binutils/macho/prefix.h
+++ b/core/src/xmake/binutils/macho/prefix.h
@@ -182,6 +182,7 @@ typedef struct __xm_macho_nlist_64_t {
tb_uint16_t desc;
tb_uint64_t value;
} __tb_packed__ xm_macho_nlist_64_t;
+#include "tbox/prefix/packed.h"
/* //////////////////////////////////////////////////////////////////////////////////////
* inline implementation
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index 048e74ee2..1733fc7b2 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -29,10 +29,11 @@
/* //////////////////////////////////////////////////////////////////////////////////////
* macros
*/
-#define XM_BINUTILS_FORMAT_COFF 0
-#define XM_BINUTILS_FORMAT_ELF 1
-#define XM_BINUTILS_FORMAT_MACHO 2
-#define XM_BINUTILS_FORMAT_UNKNOWN 3
+#define XM_BINUTILS_FORMAT_COFF 1
+#define XM_BINUTILS_FORMAT_ELF 2
+#define XM_BINUTILS_FORMAT_MACHO 3
+#define XM_BINUTILS_FORMAT_AR 4
+#define XM_BINUTILS_FORMAT_UNKNOWN 0
/* COFF machine types (for format detection) */
#define XM_BINUTILS_COFF_MACHINE_I386 0x014c
@@ -73,11 +74,22 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t
*
* @param istream the input stream
* @return XM_BINUTILS_FORMAT_COFF, XM_BINUTILS_FORMAT_ELF, XM_BINUTILS_FORMAT_MACHO,
- * XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error
+ * XM_BINUTILS_FORMAT_AR, XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error
*/
static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) {
tb_assert_and_check_return_val(istream, -1);
+ // check AR archive format first (!<arch>\n)
+ tb_uint8_t ar_magic[8];
+ if (xm_binutils_read_magic(istream, ar_magic, 8)) {
+ if (ar_magic[0] == '!' && ar_magic[1] == '<' && ar_magic[2] == 'a' &&
+ ar_magic[3] == 'r' && ar_magic[4] == 'c' && ar_magic[5] == 'h' &&
+ (ar_magic[6] == '>' || ar_magic[6] == '\n') &&
+ (ar_magic[7] == '\n' || ar_magic[7] == '\r')) {
+ return XM_BINUTILS_FORMAT_AR;
+ }
+ }
+
// read magic bytes
tb_uint8_t magic[4];
if (!xm_binutils_read_magic(istream, magic, 4)) {
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
index ae261782f..73908a562 100644
--- a/core/src/xmake/binutils/readsyms.c
+++ b/core/src/xmake/binutils/readsyms.c
@@ -39,6 +39,7 @@
extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, lua_State *lua);
extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, lua_State *lua);
extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, lua_State *lua);
+extern tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, lua_State *lua);
/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
@@ -81,7 +82,14 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
}
// read symbols based on format
- if (format == XM_BINUTILS_FORMAT_COFF) {
+ if (format == XM_BINUTILS_FORMAT_AR) {
+ // AR archive (.a or .lib)
+ if (!xm_binutils_ar_read_symbols(istream, lua)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "readsyms: read AR archive symbols failed");
+ break;
+ }
+ } else if (format == XM_BINUTILS_FORMAT_COFF) {
// COFF
if (!xm_binutils_coff_read_symbols(istream, lua)) {
lua_pushboolean(lua, tb_false);
diff --git a/core/src/xmake/xmake.sh b/core/src/xmake/xmake.sh
index c65c5c1ef..e40051419 100755
--- a/core/src/xmake/xmake.sh
+++ b/core/src/xmake/xmake.sh
@@ -75,6 +75,7 @@ target "xmake"
add_files "binutils/coff/*.c"
add_files "binutils/macho/*.c"
add_files "binutils/elf/*.c"
+ add_files "binutils/ar/*.c"
add_files "thread/*.c"
if is_plat "mingw"; then
add_files "winos/*.c"