summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSaikari <[email protected]>2026-01-29 12:24:27 +0300
committerSaikari <[email protected]>2026-01-29 12:24:27 +0300
commitbe399abceea8da01cbcd2e5391bc6aead52cfcfe (patch)
treefd2592b035938ea622a720add6165dc1baddb124
parent38a8b6998f5ef826725ed565b46d50f097840ed0 (diff)
Add is_pefile function to check for PE file signatures on Windows
-rw-r--r--core/src/xmake/engine.c2
-rw-r--r--core/src/xmake/winos/is_pefile.c94
-rw-r--r--xmake/core/base/os.lua38
3 files changed, 123 insertions, 11 deletions
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index a77cc4a9b..3356e4456 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -278,6 +278,7 @@ 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
@@ -482,6 +483,7 @@ 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_pe_file", xm_winos_is_pefile },
{ tb_null, tb_null },
};
#endif
diff --git a/core/src/xmake/winos/is_pefile.c b/core/src/xmake/winos/is_pefile.c
new file mode 100644
index 000000000..8b2016421
--- /dev/null
+++ b/core/src/xmake/winos/is_pefile.c
@@ -0,0 +1,94 @@
+/*!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/os.lua b/xmake/core/base/os.lua
index 6c4c1fa48..351dbd68c 100644
--- a/xmake/core/base/os.lua
+++ b/xmake/core/base/os.lua
@@ -1151,23 +1151,39 @@ end
function os.isexec(filepath)
assert(filepath)
- -- TODO
- -- check permission
+ if os.host() == "windows" then
+ local exts = {".exe", ".cmd", ".bat", ".ps1", ".sh"}
+ if os.isfile(filepath) then
+ -- detect file extension first
+ local extension = path.extension(filepath):lower()
+ if extension then
+ if table.contains(exts, extension) then
+ return true
+ end
+ end
- -- check executable program exist
- if os.isfile(filepath) then
- if os.host() == "windows" then
- return true
- else
- return os._access(filepath, "x")
+ -- detect file header
+ if winos.is_pefile(filepath) then
+ return true
+ end
+
+ -- detect shebang scripts
+ local file = io.open(filepath, "rb")
+ if file then
+ local header = file:read(2)
+ file:close()
+ if header == "#!" then
+ return true
+ end
+ end
end
- end
- if os.host() == "windows" then
- for _, suffix in ipairs({".exe", ".cmd", ".bat", ".ps1"}) do
+ for _, suffix in ipairs(exts) do
if os.isfile(filepath .. suffix) then
return true
end
end
+ elseif os.isfile(filepath) then
+ return os._access(filepath, "x")
end
return false
end