diff options
| author | ruki <[email protected]> | 2025-12-09 00:46:58 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-12-09 00:46:58 +0800 |
| commit | 9459655ebc9cca1f945c71aa79dbe27a921f827e (patch) | |
| tree | 7cb8a6b32de06ea51a56102e48069ef7bcb32ac7 | |
| parent | d1849e1b1a9d5accc1a3555272e27c96b863bbd8 (diff) | |
read symbols from .a
| -rw-r--r-- | core/src/xmake/binutils/ar/prefix.h | 82 | ||||
| -rw-r--r-- | core/src/xmake/binutils/ar/readsyms.c | 242 | ||||
| -rw-r--r-- | core/src/xmake/binutils/coff/prefix.h | 1 | ||||
| -rw-r--r-- | core/src/xmake/binutils/elf/prefix.h | 1 | ||||
| -rw-r--r-- | core/src/xmake/binutils/macho/prefix.h | 1 | ||||
| -rw-r--r-- | core/src/xmake/binutils/prefix.h | 22 | ||||
| -rw-r--r-- | core/src/xmake/binutils/readsyms.c | 10 | ||||
| -rwxr-xr-x | core/src/xmake/xmake.sh | 1 |
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" |
