/*!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 deplibs.c * */ /* ////////////////////////////////////////////////////////////////////////////////////// * trace */ #define TB_TRACE_MODULE_NAME "deplibs_coff" #define TB_TRACE_MODULE_DEBUG (0) /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * private implementation */ static tb_uint32_t xm_binutils_coff_get_import_rva(tb_stream_ref_t istream, tb_uint16_t opthdr_size) { // save current offset tb_hize_t opt_offset = tb_stream_offset(istream); tb_uint32_t import_rva = 0; // read magic tb_uint16_t magic = 0; if (tb_stream_bread(istream, (tb_byte_t*)&magic, sizeof(magic))) { // seek back if (tb_stream_seek(istream, opt_offset)) { if (magic == XM_PE32_MAGIC) { xm_pe32_opt_header_t opt_header = {0}; if (tb_stream_bread(istream, (tb_byte_t*)&opt_header, tb_min(sizeof(opt_header), opthdr_size))) { if (opt_header.number_of_rva_and_sizes > 1) { import_rva = opt_header.data_directory[1].vaddr; } } } else if (magic == XM_PE32P_MAGIC) { xm_pe32p_opt_header_t opt_header = {0}; if (tb_stream_bread(istream, (tb_byte_t*)&opt_header, tb_min(sizeof(opt_header), opthdr_size))) { if (opt_header.number_of_rva_and_sizes > 1) { import_rva = opt_header.data_directory[1].vaddr; } } } } } return import_rva; } /* ////////////////////////////////////////////////////////////////////////////////////// * implementation */ tb_bool_t xm_binutils_coff_deplibs(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua) { tb_assert_and_check_return_val(istream && lua, tb_false); tb_bool_t ok = tb_false; xm_coff_section_t* sections = tb_null; do { // read COFF header xm_coff_header_t header; if (!tb_stream_seek(istream, base_offset)) break; if (!tb_stream_bread(istream, (tb_byte_t*)&header, sizeof(header))) break; // read optional header and import rva tb_uint32_t import_rva = 0; if (header.opthdr > 0) { import_rva = xm_binutils_coff_get_import_rva(istream, header.opthdr); } // read section headers if (header.nsects > 0) { sections = tb_nalloc_type(header.nsects, xm_coff_section_t); if (!sections) break; tb_hize_t section_offset = base_offset + sizeof(xm_coff_header_t) + header.opthdr; if (!tb_stream_seek(istream, section_offset)) break; if (!tb_stream_bread(istream, (tb_byte_t*)sections, header.nsects * sizeof(xm_coff_section_t))) break; } if (!sections) { if (header.nsects > 0) break; ok = tb_true; break; } tb_size_t result_count = 0; tb_bool_t failed = tb_false; for (tb_uint16_t i = 0; i < header.nsects; i++) { xm_coff_section_t* section = §ions[i]; // check if it is .idata section (import directory table) tb_bool_t found_idt = tb_false; tb_uint32_t idt_offset = 0; if (import_rva != 0) { // check if import rva is in this section if (import_rva >= section->vaddr && import_rva < section->vaddr + section->vsize) { idt_offset = section->ofs + (import_rva - section->vaddr); found_idt = tb_true; } } else { // fallback to check section name if (tb_strncmp(section->name, ".idata", 6) == 0) { idt_offset = section->ofs; found_idt = tb_true; } } if (found_idt) { /* read import directory table * The .idata section contains the Import Directory Table. * Each entry is 20 bytes (IMAGE_IMPORT_DESCRIPTOR). * The table ends with a null entry. */ /* We need to iterate over IMAGE_IMPORT_DESCRIPTOR entries. * struct IMAGE_IMPORT_DESCRIPTOR { * DWORD OriginalFirstThunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) * DWORD TimeDateStamp; // 0 if not bound, * DWORD ForwarderChain; // -1 if no forwarders * DWORD Name; // RVA to DLL name * DWORD FirstThunk; // RVA to IAT (if bound this IAT has actual addresses) * }; */ if (!tb_stream_seek(istream, idt_offset)) { failed = tb_true; break; } while (1) { xm_coff_import_directory_table_t entry; if (!tb_stream_bread(istream, (tb_byte_t*)&entry, sizeof(entry))) { break; } // check for null entry (end of table) if (entry.original_first_thunk == 0 && entry.name_rva == 0) { break; } tb_uint32_t name_rva = tb_bits_le_to_ne_u32(entry.name_rva); if (name_rva != 0) { /* map RVA to file offset to read the name * We need to find the section that contains this RVA. */ tb_hize_t saved_pos_inner = tb_stream_offset(istream); tb_uint32_t name_file_offset = 0; // Find the section containing name_rva for (tb_uint16_t k = 0; k < header.nsects; k++) { xm_coff_section_t* s = §ions[k]; if (name_rva >= s->vaddr && name_rva < s->vaddr + s->vsize) { name_file_offset = s->ofs + (name_rva - s->vaddr); break; } } if (name_file_offset != 0) { tb_char_t dll_name[256]; if (xm_binutils_read_string(istream, name_file_offset, dll_name, sizeof(dll_name)) && dll_name[0]) { lua_pushinteger(lua, result_count + 1); lua_pushstring(lua, dll_name); lua_settable(lua, -3); result_count++; } } tb_stream_seek(istream, saved_pos_inner); } } break; } } tb_check_break(!failed); ok = tb_true; } while (0); if (sections) tb_free(sections); return ok; }