/*!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_COFF_PREFIX_H #define XM_BINUTILS_COFF_PREFIX_H /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "../prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * macros */ #define XM_COFF_MACHINE_I386 0x014c #define XM_COFF_MACHINE_AMD64 0x8664 #define XM_COFF_MACHINE_ARM 0x01c0 #define XM_COFF_MACHINE_ARM64 0xaa64 // PE Optional Header Magic #define XM_PE32_MAGIC 0x10b #define XM_PE32P_MAGIC 0x20b // COFF section flags #define XM_COFF_SCN_CNT_CODE 0x20 // IMAGE_SCN_CNT_CODE #define XM_COFF_SCN_CNT_INITIALIZED_DATA 0x40 // IMAGE_SCN_CNT_INITIALIZED_DATA #define XM_COFF_SCN_CNT_UNINITIALIZED_DATA 0x80 // IMAGE_SCN_CNT_UNINITIALIZED_DATA #define XM_COFF_SECTION_RDATA 0x40000040 // IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ /* ////////////////////////////////////////////////////////////////////////////////////// * types */ #include "tbox/prefix/packed.h" typedef struct __xm_coff_header_t { tb_uint16_t machine; tb_uint16_t nsects; tb_uint32_t time; tb_uint32_t symtabofs; tb_uint32_t nsyms; tb_uint16_t opthdr; tb_uint16_t flags; } __tb_packed__ xm_coff_header_t; typedef struct __xm_pe32_data_directory_t { tb_uint32_t vaddr; tb_uint32_t size; } __tb_packed__ xm_pe32_data_directory_t; typedef struct __xm_pe32_opt_header_t { tb_uint16_t magic; tb_uint8_t major_linker_version; tb_uint8_t minor_linker_version; tb_uint32_t size_of_code; tb_uint32_t size_of_initialized_data; tb_uint32_t size_of_uninitialized_data; tb_uint32_t address_of_entry_point; tb_uint32_t base_of_code; tb_uint32_t base_of_data; tb_uint32_t image_base; tb_uint32_t section_alignment; tb_uint32_t file_alignment; tb_uint16_t major_operating_system_version; tb_uint16_t minor_operating_system_version; tb_uint16_t major_image_version; tb_uint16_t minor_image_version; tb_uint16_t major_subsystem_version; tb_uint16_t minor_subsystem_version; tb_uint32_t win32_version_value; tb_uint32_t size_of_image; tb_uint32_t size_of_headers; tb_uint32_t checksum; tb_uint16_t subsystem; tb_uint16_t dll_characteristics; tb_uint32_t size_of_stack_reserve; tb_uint32_t size_of_stack_commit; tb_uint32_t size_of_heap_reserve; tb_uint32_t size_of_heap_commit; tb_uint32_t loader_flags; tb_uint32_t number_of_rva_and_sizes; xm_pe32_data_directory_t data_directory[16]; } __tb_packed__ xm_pe32_opt_header_t; typedef struct __xm_pe32p_opt_header_t { tb_uint16_t magic; tb_uint8_t major_linker_version; tb_uint8_t minor_linker_version; tb_uint32_t size_of_code; tb_uint32_t size_of_initialized_data; tb_uint32_t size_of_uninitialized_data; tb_uint32_t address_of_entry_point; tb_uint32_t base_of_code; tb_uint64_t image_base; tb_uint32_t section_alignment; tb_uint32_t file_alignment; tb_uint16_t major_operating_system_version; tb_uint16_t minor_operating_system_version; tb_uint16_t major_image_version; tb_uint16_t minor_image_version; tb_uint16_t major_subsystem_version; tb_uint16_t minor_subsystem_version; tb_uint32_t win32_version_value; tb_uint32_t size_of_image; tb_uint32_t size_of_headers; tb_uint32_t checksum; tb_uint16_t subsystem; tb_uint16_t dll_characteristics; tb_uint64_t size_of_stack_reserve; tb_uint64_t size_of_stack_commit; tb_uint64_t size_of_heap_reserve; tb_uint64_t size_of_heap_commit; tb_uint32_t loader_flags; tb_uint32_t number_of_rva_and_sizes; xm_pe32_data_directory_t data_directory[16]; } __tb_packed__ xm_pe32p_opt_header_t; typedef struct __xm_coff_import_directory_table_t { tb_uint32_t original_first_thunk; // RVA to original unbound IAT (PIMAGE_THUNK_DATA) tb_uint32_t time_date_stamp; // 0 if not bound, -1 if bound, and real date\time stamp in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND) O.W. date/time stamp of DLL bound to (Old BIND) tb_uint32_t forwarder_chain; // -1 if no forwarders tb_uint32_t name_rva; // RVA to name tb_uint32_t first_thunk; // RVA to IAT (if bound this IAT has actual addresses) } __tb_packed__ xm_coff_import_directory_table_t; typedef struct __xm_coff_import_header_t { tb_uint16_t sig1; // 0 tb_uint16_t sig2; // 0xffff tb_uint16_t version; tb_uint16_t machine; tb_uint32_t time; tb_uint32_t size; // size of data tb_uint16_t ordinal; // ordinal or hint tb_uint16_t type; // type } __tb_packed__ xm_coff_import_header_t; typedef struct __xm_coff_anon_header_t { tb_uint16_t sig1; // 0 tb_uint16_t sig2; // 0xffff tb_uint16_t version; tb_uint16_t machine; tb_uint32_t time; tb_uint8_t clsid[16]; tb_uint32_t size; // size of data } __tb_packed__ xm_coff_anon_header_t; typedef struct __xm_coff_section_t { tb_char_t name[8]; tb_uint32_t vsize; tb_uint32_t vaddr; tb_uint32_t size; tb_uint32_t ofs; tb_uint32_t relocofs; tb_uint32_t linenoofs; tb_uint16_t nreloc; tb_uint16_t nlineno; tb_uint32_t flags; } __tb_packed__ xm_coff_section_t; typedef struct __xm_coff_symbol_t { union { struct { tb_char_t name[8]; } shortname; struct { tb_uint32_t zeros; tb_uint32_t offset; } longname; } n; tb_uint32_t value; tb_int16_t sect; tb_uint16_t type; tb_uint8_t scl; tb_uint8_t naux; } __tb_packed__ xm_coff_symbol_t; typedef struct __xm_coff_aux_section_t { tb_uint32_t length; tb_uint16_t nreloc; tb_uint16_t nlineno; tb_uint8_t reserved[10]; } __tb_packed__ xm_coff_aux_section_t; typedef struct __xm_coff_symbol_tail_t { tb_uint32_t value; tb_int16_t sect; tb_uint16_t type; tb_uint8_t scl; tb_uint8_t naux; } __tb_packed__ xm_coff_symbol_tail_t; #include "tbox/prefix/packed.h" /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation */ /* get machine type from architecture string * * @param arch the architecture string (e.g., "x86_64", "i386", "arm64") * @return the machine type */ static __tb_inline__ tb_uint16_t xm_binutils_coff_get_machine(tb_char_t const *arch) { if (!arch) { return XM_COFF_MACHINE_I386; } if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { return XM_COFF_MACHINE_AMD64; } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) { return XM_COFF_MACHINE_ARM64; } else if (tb_strcmp(arch, "arm") == 0) { return XM_COFF_MACHINE_ARM; } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) { return XM_COFF_MACHINE_I386; } return XM_COFF_MACHINE_I386; } /* write string to stream * * @param ostream the output stream * @param str the string to write * @param len the length (0 for auto-detect) */ static __tb_inline__ tb_void_t xm_binutils_coff_write_string(tb_stream_ref_t ostream, tb_char_t const *str, tb_size_t len) { tb_assert_and_check_return(ostream && str); if (len == 0) { len = tb_strlen(str); } tb_stream_bwrit(ostream, (tb_byte_t const *)str, len); } /* write padding bytes to stream * * @param ostream the output stream * @param count the number of padding bytes */ static __tb_inline__ tb_void_t xm_binutils_coff_write_padding(tb_stream_ref_t ostream, tb_size_t count) { tb_assert_and_check_return(ostream); tb_byte_t zero = 0; while (count-- > 0) { tb_stream_bwrit(ostream, &zero, 1); } } /* write symbol name to stream (handles short and long names) * * @param ostream the output stream * @param name the symbol name * @param strtab_offset the string table offset (updated if long name) */ static __tb_inline__ tb_void_t xm_binutils_coff_write_symbol_name(tb_stream_ref_t ostream, tb_char_t const *name, tb_uint32_t *strtab_offset) { tb_assert_and_check_return(ostream && name && strtab_offset); tb_size_t len = tb_strlen(name); if (len <= 8) { // short name: store directly in symbol name field xm_binutils_coff_write_string(ostream, name, len); if (len < 8) { xm_binutils_coff_write_padding(ostream, 8 - len); } } else { // long name: store offset in string table tb_uint32_t zeros = 0; tb_stream_bwrit(ostream, (tb_byte_t const *)&zeros, 4); tb_stream_bwrit(ostream, (tb_byte_t const *)strtab_offset, 4); *strtab_offset += (tb_uint32_t)(len + 1); // +1 for null terminator } } /* ////////////////////////////////////////////////////////////////////////////////////// * readsyms inline implementation */ /* read string from COFF string table * * @param istream the input stream * @param strtab_offset the string table offset (including 4-byte size field) * @param offset the string offset (from start of string table content, after size field) * @return the string (static buffer, valid until next call) */ static __tb_inline__ tb_bool_t xm_binutils_coff_read_string(tb_stream_ref_t istream, tb_hize_t strtab_offset, tb_uint32_t offset, tb_char_t *name, tb_size_t name_size) { tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false); // In COFF format, the offset in symbol table is from the start of string table // (including the 4-byte size field). So offset=4 points to the first string after // the size field, offset=74 points to a string at position 74 from the start. // read string table size first to validate offset tb_uint32_t strtab_size = 0; tb_hize_t saved_pos = tb_stream_offset(istream); if (!tb_stream_seek(istream, strtab_offset)) { return tb_false; } if (!tb_stream_bread(istream, (tb_byte_t*)&strtab_size, 4)) { tb_stream_seek(istream, saved_pos); return tb_false; } // check offset (must be >= 4 to skip the size field, and < strtab_size) if (offset < 4 || offset >= strtab_size) { tb_stream_seek(istream, saved_pos); return tb_false; } // restore position and use common implementation tb_stream_seek(istream, saved_pos); return xm_binutils_read_string(istream, strtab_offset + offset, name, name_size); } /* get symbol name from COFF symbol entry * * @param istream the input stream * @param sym the symbol entry * @param strtab_offset the string table offset * @param name the buffer to store the symbol name * @param name_size the size of the buffer * @return tb_true on success, tb_false on failure */ static __tb_inline__ tb_bool_t xm_binutils_coff_get_symbol_name(tb_stream_ref_t istream, xm_coff_symbol_t const *sym, tb_hize_t strtab_offset, tb_char_t *name, tb_size_t name_size) { tb_assert_and_check_return_val(istream && sym && name && name_size > 0, tb_false); // check if it's a long name (first 4 bytes are zeros) if (sym->n.longname.zeros == 0) { // long name: read from string table return xm_binutils_coff_read_string(istream, strtab_offset, sym->n.longname.offset, name, name_size); } else { // short name: use directly tb_size_t len = tb_min(8, name_size - 1); tb_strncpy(name, sym->n.shortname.name, len); name[len] = '\0'; // trim trailing nulls while (len > 0 && name[len - 1] == '\0') { len--; } name[len] = '\0'; return tb_true; } } /* get symbol type character (nm-style) from COFF symbol * * @param scl the storage class * @param sect the section number (0 = undefined, 1-based) * @param sections the section headers array * @param nsects the number of sections * @return the type character (T/t/D/d/B/b/U) */ static __tb_inline__ tb_char_t xm_binutils_coff_get_symbol_type_char(tb_uint8_t scl, tb_int16_t sect, xm_coff_section_t const *sections, tb_uint16_t nsects) { // undefined symbol if (sect == 0) { return 'U'; } // check if external tb_bool_t is_external = (scl == 2); // IMAGE_SYM_CLASS_EXTERNAL // check section flags to determine type if (sections && sect > 0 && sect <= nsects) { tb_uint32_t flags = sections[sect - 1].flags; // section numbers are 1-based // IMAGE_SCN_CNT_CODE (0x20) - code section if (flags & XM_COFF_SCN_CNT_CODE) { return is_external ? 'T' : 't'; // text section } // IMAGE_SCN_CNT_UNINITIALIZED_DATA (0x80) - bss section if (flags & XM_COFF_SCN_CNT_UNINITIALIZED_DATA) { return is_external ? 'B' : 'b'; // bss section } // IMAGE_SCN_CNT_INITIALIZED_DATA (0x40) - data section if (flags & XM_COFF_SCN_CNT_INITIALIZED_DATA) { return is_external ? 'D' : 'd'; // data section } } // fallback: use section number heuristic if (sect == 1) { return is_external ? 'T' : 't'; // text section } else if (sect == 2) { return is_external ? 'D' : 'd'; // data section } else if (sect == 3) { return is_external ? 'B' : 'b'; // bss section } return is_external ? 'S' : 's'; // other section } #endif