diff options
| author | ruki <[email protected]> | 2026-01-30 00:54:29 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2026-01-30 00:54:29 +0800 |
| commit | 148404f78b74d376eba5922cb197162a9f05b1a7 (patch) | |
| tree | 8690a371d977fc87014e87377dd2cc7617fc762a | |
| parent | 0d0e9a3a86aeee263c3a8e843bcaae32da990ae3 (diff) | |
add bintuils.format
| -rw-r--r-- | core/src/xmake/binutils/prefix.h | 169 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 4 | ||||
| -rw-r--r-- | core/src/xmake/os/access.c | 5 | ||||
| -rw-r--r-- | core/src/xmake/winos/is_pefile.c | 94 | ||||
| -rw-r--r-- | xmake/core/base/binutils.lua | 11 | ||||
| -rw-r--r-- | xmake/core/base/os.lua | 7 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/binutils.lua | 10 |
7 files changed, 146 insertions, 154 deletions
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h index e7811652a..7df5b55a7 100644 --- a/core/src/xmake/binutils/prefix.h +++ b/core/src/xmake/binutils/prefix.h @@ -42,6 +42,11 @@ #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 + /* ////////////////////////////////////////////////////////////////////////////////////// * inline implementation */ @@ -71,75 +76,134 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t return ok; } +/* check if it's a PE file from the beginning of stream + * + * @note the stream offset will be preserved by this function + */ +static __tb_inline__ tb_bool_t xm_binutils_detect_pe(tb_stream_ref_t istream, tb_byte_t* first8) { + tb_assert_and_check_return_val(istream && first8, tb_false); + + // check PE/DOS magic ("MZ" or "ZM") and verify NT signature + if (!((first8[0] == 'M' && first8[1] == 'Z') || (first8[0] == 'Z' && first8[1] == 'M'))) { + return tb_false; + } + + tb_bool_t ok = tb_false; + do { + tb_hong_t size = tb_stream_size(istream); + if (size > 0 && size < XM_BINUTILS_PE_DOS_STUB_MIN_SIZE + 4) { + break; + } + + tb_size_t max_peek = 4096; + if (size > 0) { + max_peek = (tb_size_t)tb_min((tb_hize_t)max_peek, (tb_hize_t)size); + } + + tb_byte_t* p = tb_null; + if (!tb_stream_peek(istream, &p, max_peek)) { + break; + } + + tb_uint32_t e_lfanew = (tb_uint32_t)( (tb_uint32_t)p[XM_BINUTILS_PE_DOS_ELFANEW_OFFSET] + | ((tb_uint32_t)p[XM_BINUTILS_PE_DOS_ELFANEW_OFFSET + 1] << 8) + | ((tb_uint32_t)p[XM_BINUTILS_PE_DOS_ELFANEW_OFFSET + 2] << 16) + | ((tb_uint32_t)p[XM_BINUTILS_PE_DOS_ELFANEW_OFFSET + 3] << 24)); + tb_check_break(e_lfanew >= XM_BINUTILS_PE_DOS_STUB_MIN_SIZE); + tb_check_break((tb_size_t)e_lfanew + 4 <= max_peek); + tb_check_break(size <= 0 || (tb_hize_t)e_lfanew + 4 <= (tb_hize_t)size); + + tb_byte_t const* signature = p + (tb_size_t)e_lfanew; + ok = (signature[0] == 'P' && signature[1] == 'E' && signature[2] == 0 && signature[3] == 0); + } while (0); + + return ok; +} + /* detect object file format from stream * * @param istream the input stream * @return XM_BINUTILS_FORMAT_COFF, XM_BINUTILS_FORMAT_ELF, XM_BINUTILS_FORMAT_MACHO, - * XM_BINUTILS_FORMAT_AR, XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error + * XM_BINUTILS_FORMAT_AR, XM_BINUTILS_FORMAT_PE, 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); + tb_assert_and_check_return_val(tb_stream_offset(istream) == 0, -1); - // peek first 8 bytes - tb_byte_t* p = tb_null; - if (!tb_stream_peek(istream, &p, 8)) { - return -1; - } + tb_int_t format = -1; + do { + // peek first 8 bytes + tb_byte_t* p = tb_null; + if (!tb_stream_peek(istream, &p, 8)) { + tb_hong_t size = tb_stream_size(istream); + if (size > 0 && size < 8) { + format = XM_BINUTILS_FORMAT_UNKNOWN; + } + break; + } - // check AR archive format first (!<arch>\n) - if (p[0] == '!' && p[1] == '<' && p[2] == 'a' && - p[3] == 'r' && p[4] == 'c' && p[5] == 'h' && - (p[6] == '>' || p[6] == '\n') && - (p[7] == '\n' || p[7] == '\r')) { - return XM_BINUTILS_FORMAT_AR; - } + // check AR archive format first (!<arch>\n) + if (p[0] == '!' && p[1] == '<' && p[2] == 'a' && + p[3] == 'r' && p[4] == 'c' && p[5] == 'h' && + (p[6] == '>' || p[6] == '\n') && + (p[7] == '\n' || p[7] == '\r')) { + format = XM_BINUTILS_FORMAT_AR; + break; + } - // check PE/DOS magic (0x5A4D 'M' 'Z') - if (p[0] == 'M' && p[1] == 'Z') { - return XM_BINUTILS_FORMAT_PE; - } + if (xm_binutils_detect_pe(istream, p)) { + format = XM_BINUTILS_FORMAT_PE; + break; + } - // check ELF magic (0x7f 'E' 'L' 'F') - if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F') { - return XM_BINUTILS_FORMAT_ELF; - } + // check ELF magic (0x7f 'E' 'L' 'F') + if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F') { + format = XM_BINUTILS_FORMAT_ELF; + break; + } - // check Mach-O magic - if (p[0] == 0xfe && p[1] == 0xed && p[2] == 0xfa && - (p[3] == 0xce || p[3] == 0xcf)) { - return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32/64 (big endian) - } - if (p[0] == 0xce && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) { - return XM_BINUTILS_FORMAT_MACHO; // Mach-O 32 (little endian) - } - if (p[0] == 0xcf && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) { - return XM_BINUTILS_FORMAT_MACHO; // Mach-O 64 (little endian) - } + // check Mach-O magic + if (p[0] == 0xfe && p[1] == 0xed && p[2] == 0xfa && + (p[3] == 0xce || p[3] == 0xcf)) { + format = XM_BINUTILS_FORMAT_MACHO; // Mach-O 32/64 (big endian) + break; + } + if (p[0] == 0xce && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) { + format = XM_BINUTILS_FORMAT_MACHO; // Mach-O 32 (little endian) + break; + } + if (p[0] == 0xcf && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) { + format = XM_BINUTILS_FORMAT_MACHO; // Mach-O 64 (little endian) + break; + } - // check COFF (object files start with machine type, not a magic number) - // COFF header: machine (2 bytes) + nsects (2 bytes) + time (4 bytes) + ... - // Read machine type to verify if it's a valid COFF file - tb_uint16_t machine = (p[1] << 8) | p[0]; + // check COFF (object files start with machine type, not a magic number) + // COFF header: machine (2 bytes) + nsects (2 bytes) + time (4 bytes) + ... + tb_uint16_t machine = (tb_uint16_t)((p[1] << 8) | p[0]); - // check if it's a valid COFF machine type - // Import header: 0x0000 0xffff - if (machine == 0x0000) { - // read second word to check if it is import header - tb_uint16_t machine2 = (p[3] << 8) | p[2]; - if (machine2 == 0xffff) { - return XM_BINUTILS_FORMAT_COFF; + // Import header: 0x0000 0xffff + if (machine == 0x0000) { + tb_uint16_t machine2 = (tb_uint16_t)((p[3] << 8) | p[2]); + if (machine2 == 0xffff) { + format = XM_BINUTILS_FORMAT_COFF; + break; + } } - } - if (machine == XM_BINUTILS_COFF_MACHINE_I386 || - machine == XM_BINUTILS_COFF_MACHINE_AMD64 || - machine == XM_BINUTILS_COFF_MACHINE_ARM || - machine == XM_BINUTILS_COFF_MACHINE_ARM64) { - return XM_BINUTILS_FORMAT_COFF; - } + if (machine == XM_BINUTILS_COFF_MACHINE_I386 || + machine == XM_BINUTILS_COFF_MACHINE_AMD64 || + machine == XM_BINUTILS_COFF_MACHINE_ARM || + machine == XM_BINUTILS_COFF_MACHINE_ARM64) { + format = XM_BINUTILS_FORMAT_COFF; + break; + } - // unknown format - return XM_BINUTILS_FORMAT_UNKNOWN; + // unknown format + format = XM_BINUTILS_FORMAT_UNKNOWN; + + } while (0); + + return format; } /* copy data from input stream to output stream @@ -280,4 +344,3 @@ static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch) #endif - diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c index 2ffa70c4c..2e3ad61b7 100644 --- a/core/src/xmake/engine.c +++ b/core/src/xmake/engine.c @@ -278,7 +278,6 @@ tb_int_t xm_winos_registry_query(lua_State *lua); tb_int_t xm_winos_registry_keys(lua_State *lua); tb_int_t xm_winos_registry_values(lua_State *lua); tb_int_t xm_winos_short_path(lua_State *lua); -tb_int_t xm_winos_is_pefile(lua_State *lua); #endif // the utf8 functions @@ -363,6 +362,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua); tb_int_t xm_binutils_rpath_list(lua_State *lua); tb_int_t xm_binutils_rpath_clean(lua_State *lua); tb_int_t xm_binutils_extractlib(lua_State *lua); +tb_int_t xm_binutils_format(lua_State *lua); #ifdef XM_CONFIG_API_HAVE_CURSES // register curses functions @@ -483,7 +483,6 @@ static luaL_Reg const g_winos_functions[] = { { "registry_keys", xm_winos_registry_keys }, { "registry_values", xm_winos_registry_values }, { "short_path", xm_winos_short_path }, - { "is_pefile", xm_winos_ispefile }, { tb_null, tb_null }, }; #endif @@ -715,6 +714,7 @@ static luaL_Reg const g_binutils_functions[] = { { "rpath_list", xm_binutils_rpath_list }, { "rpath_clean", xm_binutils_rpath_clean }, { "extractlib", xm_binutils_extractlib }, + { "format", xm_binutils_format }, { tb_null, tb_null }, }; diff --git a/core/src/xmake/os/access.c b/core/src/xmake/os/access.c index 8b79bf9fc..1bfcd072f 100644 --- a/core/src/xmake/os/access.c +++ b/core/src/xmake/os/access.c @@ -36,7 +36,7 @@ tb_int_t xm_os_access(lua_State *lua) { tb_assert_and_check_return_val(lua, 0); - // check + // get the path tb_char_t const* path = luaL_checkstring(lua, 1); tb_char_t const* mode_str = luaL_checkstring(lua, 2); tb_check_return_val(path && mode_str, 0); @@ -48,11 +48,12 @@ tb_int_t xm_os_access(lua_State *lua) { case 'r': mode |= TB_FILE_MODE_RO; break; case 'w': mode |= TB_FILE_MODE_WO; break; case 'x': mode |= TB_FILE_MODE_EXEC; break; + default: break; } mode_str++; } - // check access + // os.access(path, mode) lua_pushboolean(lua, tb_file_access(path, mode)); return 1; } diff --git a/core/src/xmake/winos/is_pefile.c b/core/src/xmake/winos/is_pefile.c deleted file mode 100644 index 8b2016421..000000000 --- a/core/src/xmake/winos/is_pefile.c +++ /dev/null @@ -1,94 +0,0 @@ -/*!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 is_pefile.c - * - */ - -/* ////////////////////////////////////////////////////////////////////////////////////// - * trace - */ -#define TB_TRACE_MODULE_NAME "is_pefile" -#define TB_TRACE_MODULE_DEBUG (0) - -/* ////////////////////////////////////////////////////////////////////////////////////// - * includes - */ -#include "prefix.h" - -/* ////////////////////////////////////////////////////////////////////////////////////// - * implementation - */ - -/* is pe file? - * - * local is_pe = winos.is_pefile(filepath) - */ -tb_int_t xm_winos_is_pefile(lua_State *lua) { - - // check - tb_assert_and_check_return_val(lua, 0); - - // get the arguments - tb_char_t const *filepath = luaL_checkstring(lua, 1); - tb_check_return_val(filepath, 0); - - // check pe file - tb_bool_t ok = tb_false; - tb_stream_ref_t stream = tb_stream_init_from_file(filepath, TB_FILE_MODE_RO); - if (stream) { - - // open stream - if (tb_stream_open(stream)) { - - // check mz signature - tb_byte_t data[4]; - if (tb_stream_read(stream, data, 2) == 2 && data[0] == 'M' && data[1] == 'Z') { - - // seek to pe header offset (0x3c) - if (tb_stream_seek(stream, 0x3c)) { - - // read the pe header offset - if (tb_stream_read(stream, data, 4) == 4) { - - // get the pe header offset - tb_uint32_t offset = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); - if (offset) { - - // seek to the pe header - if (tb_stream_seek(stream, offset)) { - - // check pe signature - if (tb_stream_read(stream, data, 4) == 4 && - data[0] == 'P' && data[1] == 'E' && data[2] == 0 && data[3] == 0) { - ok = tb_true; - } - } - } - } - } - } - } - - // exit stream - tb_stream_exit(stream); - } - - // return result - lua_pushboolean(lua, ok); - return 1; -} diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua index b513be0b1..0c855c71e 100644 --- a/xmake/core/base/binutils.lua +++ b/xmake/core/base/binutils.lua @@ -34,6 +34,7 @@ binutils._deplibs = binutils._deplibs or binutils.deplibs binutils._rpath_list = binutils._rpath_list or binutils.rpath_list binutils._rpath_clean = binutils._rpath_clean or binutils.rpath_clean binutils._extractlib = binutils._extractlib or binutils.extractlib +binutils._format = binutils._format or binutils.format -- generate c/c++ code from the binary file function binutils.bin2c(binaryfile, outputfile, opt) @@ -94,12 +95,20 @@ function binutils.bin2obj(binaryfile, outputfile, opt) end end +-- get binary file format (auto-detect format: coff, elf, macho, ar, pe, unknown) +function binutils.format(binaryfile) + if binutils._format then + return binutils._format(binaryfile) + else + return nil, "format: C implementation not available" + end +end + -- read symbols from object file (auto-detect format: COFF, ELF, or Mach-O) function binutils.readsyms(binaryfile) if binutils._readsyms then return binutils._readsyms(binaryfile) - else return nil, "readsyms: C implementation not available" end end diff --git a/xmake/core/base/os.lua b/xmake/core/base/os.lua index 351dbd68c..cbd4cefeb 100644 --- a/xmake/core/base/os.lua +++ b/xmake/core/base/os.lua @@ -1163,7 +1163,11 @@ function os.isexec(filepath) end -- detect file header - if winos.is_pefile(filepath) then + local format = nil + if binutils and binutils.format then + format = binutils.format(filepath) + end + if format == "pe" then return true end @@ -1638,4 +1642,3 @@ end -- return module return os - diff --git a/xmake/core/sandbox/modules/import/core/base/binutils.lua b/xmake/core/sandbox/modules/import/core/base/binutils.lua index 6622836ea..0fc2b6796 100644 --- a/xmake/core/sandbox/modules/import/core/base/binutils.lua +++ b/xmake/core/sandbox/modules/import/core/base/binutils.lua @@ -41,6 +41,16 @@ function sandbox_core_base_binutils.bin2obj(binaryfile, outputfile, opt) end end +-- get binary file format (auto-detect format: coff, elf, macho, ar, pe, unknown) +function sandbox_core_base_binutils.format(binaryfile) + local format, errors = binutils.format(binaryfile) + if format then + return format + else + raise("format: %s", errors or "unknown errors") + end +end + -- read symbols from object file (auto-detect format: ELF, COFF, Mach-O) function sandbox_core_base_binutils.readsyms(binaryfile) |
