summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-01-30 00:54:29 +0800
committerruki <[email protected]>2026-01-30 00:54:29 +0800
commit148404f78b74d376eba5922cb197162a9f05b1a7 (patch)
tree8690a371d977fc87014e87377dd2cc7617fc762a
parent0d0e9a3a86aeee263c3a8e843bcaae32da990ae3 (diff)
add bintuils.format
-rw-r--r--core/src/xmake/binutils/prefix.h169
-rw-r--r--core/src/xmake/engine.c4
-rw-r--r--core/src/xmake/os/access.c5
-rw-r--r--core/src/xmake/winos/is_pefile.c94
-rw-r--r--xmake/core/base/binutils.lua11
-rw-r--r--xmake/core/base/os.lua7
-rw-r--r--xmake/core/sandbox/modules/import/core/base/binutils.lua10
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)