/*!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_PREFIX_H #define XM_BINUTILS_PREFIX_H /* ////////////////////////////////////////////////////////////////////////////////////// * includes */ #include "../prefix.h" /* ////////////////////////////////////////////////////////////////////////////////////// * macros */ #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_PE 5 #define XM_BINUTILS_FORMAT_SHEBANG 6 #define XM_BINUTILS_FORMAT_APE 7 #define XM_BINUTILS_FORMAT_WASM 8 #define XM_BINUTILS_FORMAT_UNKNOWN 0 // COFF machine types (for format detection) #define XM_BINUTILS_COFF_MACHINE_I386 0x014c #define XM_BINUTILS_COFF_MACHINE_AMD64 0x8664 #define XM_BINUTILS_COFF_MACHINE_ARM 0x01c0 #define XM_BINUTILS_COFF_MACHINE_ARM64 0xaa64 // PE/DOS offsets/signatures (for format detection) #define XM_BINUTILS_PE_DOS_STUB_MIN_SIZE (0x40) #define XM_BINUTILS_PE_DOS_ELFANEW_OFFSET (0x3c) #define XM_BINUTILS_PE_NT_SIGNATURE (0x00004550) // "PE\0\0" little endian /* ////////////////////////////////////////////////////////////////////////////////////// * interfaces */ tb_int_t xm_binutils_format_detect(tb_stream_ref_t istream); /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation */ /* read magic bytes from stream (preserves stream position) * * @param istream the input stream * @param magic the buffer to store magic bytes (must be at least 4 bytes) * @param size the number of bytes to read (typically 4) * * @return tb_true on success, tb_false on failure */ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, tb_uint8_t *magic, tb_size_t size) { tb_assert_and_check_return_val(istream && magic && size > 0, tb_false); tb_hize_t saved_pos = tb_stream_offset(istream); if (!tb_stream_seek(istream, 0)) { return tb_false; } tb_bool_t ok = tb_false; if (tb_stream_bread(istream, magic, size)) { ok = tb_true; } tb_stream_seek(istream, saved_pos); return ok; } /* copy data from input stream to output stream * * @param istream the input stream * @param ostream the output stream * @param size the size to copy * * @return tb_true on success, tb_false on failure */ static __tb_inline__ tb_bool_t xm_binutils_stream_copy(tb_stream_ref_t istream, tb_stream_ref_t ostream, tb_hize_t size) { tb_assert_and_check_return_val(istream && ostream, tb_false); if (size == 0) { return tb_true; } tb_byte_t data[TB_STREAM_BLOCK_MAXN]; tb_hize_t writ = 0; do { tb_size_t need = (tb_size_t)tb_min(size - writ, (tb_hize_t)TB_STREAM_BLOCK_MAXN); tb_check_break(need); if (!tb_stream_bread(istream, data, need)) { return tb_false; } if (!tb_stream_bwrit(ostream, data, need)) { return tb_false; } writ += need; tb_check_break(writ < size); } while (1); return tb_true; } /* sanitize symbol name (replace non-alphanumeric characters with underscores) * * @param name the symbol name */ static __tb_inline__ void xm_binutils_sanitize_symbol_name(tb_char_t* name) { tb_assert_and_check_return(name); for (tb_size_t i = 0; name[i]; i++) { if (!tb_isalpha(name[i]) && !tb_isdigit(name[i]) && name[i] != '_') { name[i] = '_'; } } } /* read string from stream at specified offset * * @param istream the input stream * @param offset the offset to read from * @param name the buffer to store the string * @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_read_string(tb_stream_ref_t istream, tb_hize_t offset, tb_char_t *name, tb_size_t name_size) { tb_assert_and_check_return_val(istream && name && name_size > 0, tb_false); tb_hize_t saved_pos = tb_stream_offset(istream); if (!tb_stream_seek(istream, offset)) { return tb_false; } tb_size_t pos = 0; tb_byte_t c; while (pos < name_size - 1) { if (!tb_stream_bread(istream, &c, 1)) { tb_stream_seek(istream, saved_pos); return tb_false; } if (c == 0) { break; } name[pos++] = (tb_char_t)c; } name[pos] = '\0'; tb_stream_seek(istream, saved_pos); return tb_true; } /* check if architecture is 64-bit * * @param arch the architecture string * @return tb_true if 64-bit, tb_false otherwise */ static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch) { if (!arch) { return tb_true; } // x86_64 if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) { return tb_true; } // ARM64 else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0 || tb_strcmp(arch, "arm64-v8a") == 0) { return tb_true; } // MIPS64 else if (tb_strncmp(arch, "mips64", 6) == 0) { return tb_true; } // PowerPC64 else if (tb_strncmp(arch, "ppc64", 5) == 0 || tb_strncmp(arch, "powerpc64", 9) == 0) { return tb_true; } // RISC-V 64 else if (tb_strncmp(arch, "riscv64", 7) == 0 || (tb_strncmp(arch, "riscv", 5) == 0 && tb_strstr(arch, "64"))) { return tb_true; } // SPARC64 else if (tb_strncmp(arch, "sparc64", 7) == 0) { return tb_true; } // s390x else if (tb_strcmp(arch, "s390x") == 0) { return tb_true; } // LoongArch64 (xmake uses "loong64" as the canonical arch name) else if (tb_strncmp(arch, "loongarch64", 11) == 0 || tb_strcmp(arch, "loong64") == 0) { return tb_true; } // WebAssembly 64 else if (tb_strcmp(arch, "wasm64") == 0) { return tb_true; } // IA-64 else if (tb_strcmp(arch, "ia64") == 0 || tb_strcmp(arch, "itanium") == 0) { return tb_true; } return tb_false; } /* check if architecture is big-endian * * @param arch the architecture string * @return tb_true if big-endian, tb_false otherwise */ static __tb_inline__ tb_bool_t xm_binutils_arch_is_bigendian(tb_char_t const *arch) { if (!arch) { return tb_false; } // s390/s390x are always big-endian if (tb_strcmp(arch, "s390x") == 0 || tb_strcmp(arch, "s390") == 0) { return tb_true; } // SPARC is big-endian else if (tb_strncmp(arch, "sparc", 5) == 0) { return tb_true; } // MIPS big-endian variants (mips, mips64), the little-endian ones end with "el" (mipsel, mips64el) else if (tb_strncmp(arch, "mips", 4) == 0) { return tb_strstr(arch, "el") == tb_null; } /* PowerPC endianness: * - an explicit "le"/"be" suffix always wins (ppc64le/powerpc64le, ppc64be) * - otherwise 64-bit ppc64/powerpc64 defaults to little-endian: xmake has no separate * ppc64le arch (find_platform maps powerpc64le -> ppc64) and ppc64le is the dominant * modern target, so a plain "ppc64" is treated as ppc64le (LE + OpenPOWER ELFv2) * - 32-bit ppc stays traditional big-endian */ else if (tb_strncmp(arch, "ppc", 3) == 0 || tb_strncmp(arch, "powerpc", 7) == 0) { if (tb_strstr(arch, "le")) { return tb_false; } else if (tb_strstr(arch, "be")) { return tb_true; } return !xm_binutils_arch_is_64bit(arch); } return tb_false; } #endif