/*!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 "mslib_readsyms" #define TB_TRACE_MODULE_DEBUG (0) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * forward declarations */ extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua); /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ static tb_bool_t xm_binutils_mslib_parse_archive_symbols(tb_stream_ref_t istream, tb_hize_t member_size, lua_State* lua, int map_idx) { // try to parse as Second Linker Member (LE) tb_hize_t start_pos = tb_stream_offset(istream); tb_uint32_t* offsets = tb_null; tb_uint16_t* indices = tb_null; tb_char_t* string_table = tb_null; tb_bool_t ok = tb_false; do { // read number of members tb_uint32_t num_members = 0; if (!tb_stream_bread_u32_le(istream, &num_members)) { break; } // sanity check if (num_members == 0 || num_members > 65536 || num_members * 4 >= member_size) { break; } // read offsets offsets = tb_nalloc_type(num_members, tb_uint32_t); tb_check_break(offsets); tb_size_t i; for (i = 0; i < num_members; i++) { if (!tb_stream_bread_u32_le(istream, &offsets[i])) { break; } } if (i < num_members) { break; } // read number of symbols tb_uint32_t num_symbols = 0; if (!tb_stream_bread_u32_le(istream, &num_symbols)) { break; } if (num_symbols == 0 || num_symbols > 1000000) { break; } // read indices indices = tb_nalloc_type(num_symbols, tb_uint16_t); tb_check_break(indices); for (i = 0; i < num_symbols; i++) { if (!tb_stream_bread_u16_le(istream, &indices[i])) { break; } } if (i < num_symbols) { break; } // read string table tb_hize_t current = tb_stream_offset(istream); tb_hize_t string_table_size = member_size - (current - start_pos); string_table = (tb_char_t*)tb_malloc_bytes((tb_size_t)string_table_size); tb_check_break(string_table); if (!tb_stream_bread(istream, (tb_byte_t*)string_table, (tb_size_t)string_table_size)) { break; } // populate map tb_char_t* p = string_table; tb_char_t* end = string_table + string_table_size; for (i = 0; i < num_symbols; i++) { if (p >= end) { break; } tb_char_t* sym_name = p; tb_size_t sym_len = tb_strlen(sym_name); p += sym_len + 1; tb_uint16_t idx = indices[i]; if (idx > 0 && idx <= num_members) { tb_uint32_t offset = offsets[idx - 1]; lua_pushinteger(lua, offset); lua_rawget(lua, map_idx); if (lua_isnil(lua, -1)) { lua_pop(lua, 1); lua_newtable(lua); lua_pushinteger(lua, offset); lua_pushvalue(lua, -2); lua_rawset(lua, map_idx); } int count = (int)lua_objlen(lua, -1); lua_pushstring(lua, sym_name); lua_rawseti(lua, -2, count + 1); lua_pop(lua, 1); // pop list } } ok = tb_true; } while (0); if (offsets) { tb_free(offsets); } if (indices) { tb_free(indices); } if (string_table) { tb_free(string_table); } if (!ok) { tb_stream_seek(istream, start_pos); } return ok; } /* read symbols from MSVC lib archive * * @param istream the input stream * @param base_offset the base offset * @param lua the lua state * @return tb_true on success, tb_false on failure */ tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State* lua) { tb_assert_and_check_return_val(istream && lua, tb_false); // check magic (!\n) if (!xm_binutils_mslib_check_magic(istream)) { return tb_false; } // create map table (offset -> symbols) lua_newtable(lua); int map_idx = lua_gettop(lua); tb_bool_t ok = tb_true; tb_size_t object_count = 0; 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; } else if (tb_isdigit(header.name[1])) { // offset into long name table (/123) tb_int64_t offset = xm_binutils_mslib_parse_decimal(header.name + 1, 15); if (offset >= 0 && (tb_size_t)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) { longnames = (tb_char_t*)tb_ralloc(longnames, (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 process /* skip empty names, 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_strncmp(member_name, "__.SYMDEF", 9) == 0) { // 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; } if (tb_strcmp(member_name, "/") == 0) { // try to parse archive symbols if (!xm_binutils_mslib_parse_archive_symbols(istream, (tb_hize_t)member_size, lua, map_idx)) { // if failed, 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; } // save current position tb_hize_t current_pos = tb_stream_offset(istream); tb_hize_t header_offset = current_pos - sizeof(xm_mslib_header_t); // detect format tb_int_t format = xm_binutils_format_detect(istream); if (format != XM_BINUTILS_FORMAT_UNKNOWN && format != XM_BINUTILS_FORMAT_AR) { // create entry table lua_newtable(lua); // object name lua_pushstring(lua, "objectfile"); lua_pushstring(lua, member_name); lua_settable(lua, -3); // symbols lua_pushstring(lua, "symbols"); tb_bool_t read_ok = tb_false; if (format == XM_BINUTILS_FORMAT_COFF) { read_ok = xm_binutils_coff_read_symbols(istream, current_pos, lua); } else if (format == XM_BINUTILS_FORMAT_ELF) { read_ok = xm_binutils_elf_read_symbols(istream, current_pos, lua); } else if (format == XM_BINUTILS_FORMAT_MACHO) { read_ok = xm_binutils_macho_read_symbols(istream, current_pos, lua); } // if read failed or empty, try map tb_bool_t has_symbols = tb_false; if (read_ok) { if (lua_objlen(lua, -1) > 0) { has_symbols = tb_true; } else { lua_pop(lua, 1); // pop empty table } } if (!has_symbols) { // check map lua_pushinteger(lua, (lua_Integer)header_offset); lua_rawget(lua, map_idx); if (lua_istable(lua, -1)) { // convert list of names to list of {name=..., type="global"} lua_newtable(lua); // result table int count = (int)lua_objlen(lua, -2); for (int i = 1; i <= count; i++) { lua_rawgeti(lua, -2, i); const char* name = lua_tostring(lua, -1); if (name) { lua_newtable(lua); lua_pushstring(lua, "name"); lua_pushstring(lua, name); lua_settable(lua, -3); lua_pushstring(lua, "type"); lua_pushstring(lua, "T"); lua_settable(lua, -3); lua_rawseti(lua, -3, i); } lua_pop(lua, 1); // pop name } lua_remove(lua, -2); // remove map entry list has_symbols = tb_true; } else { lua_pop(lua, 1); // pop nil } } if (has_symbols) { lua_settable(lua, -3); lua_rawseti(lua, map_idx - 1, (int)(++object_count)); } else { lua_pop(lua, 2); // pop symbols key and entry table } } // skip to next member tb_hize_t member_data_read = tb_stream_offset(istream) - current_pos; tb_hize_t remaining_size = (tb_hize_t)member_size - member_data_read; if (remaining_size > 0) { if (!tb_stream_skip(istream, remaining_size)) { ok = tb_false; break; } } else if (remaining_size < 0) { if (!tb_stream_seek(istream, current_pos + (tb_hize_t)member_size)) { ok = tb_false; break; } } // align to 2-byte boundary if (member_size % 2) { if (!tb_stream_skip(istream, 1)) { ok = tb_false; break; } } } if (longnames) { tb_free(longnames); } lua_remove(lua, map_idx); return ok; }