summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-01-30 17:28:24 +0800
committerGitHub <[email protected]>2026-01-30 17:28:24 +0800
commit51a68ee33918b95811420beb6a56673a83abe6cc (patch)
treeaba2c0591c0285709eede0e6adf4f5a68b066674
parent45f856e3ccdec7da98ac7f07251f30d85640d268 (diff)
parentd309d7832a6b079d81c10f2f6449081a1d7881d2 (diff)
Merge pull request #7278 from xmake-io/isexec
Improve os.isexec
-rw-r--r--core/src/xmake/binutils/ar/prefix.h1
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c7
-rw-r--r--core/src/xmake/binutils/deplibs.c2
-rw-r--r--core/src/xmake/binutils/extractlib.c2
-rw-r--r--core/src/xmake/binutils/format.c250
-rw-r--r--core/src/xmake/binutils/mslib/readsyms.c2
-rw-r--r--core/src/xmake/binutils/prefix.h84
-rw-r--r--core/src/xmake/binutils/readsyms.c3
-rw-r--r--core/src/xmake/binutils/rpath.c4
-rw-r--r--core/src/xmake/engine.c12
-rw-r--r--core/src/xmake/os/access.c59
-rw-r--r--tests/modules/binutils/test.lua78
-rw-r--r--tests/modules/os/test.lua39
-rw-r--r--xmake/core/base/binutils.lua32
-rw-r--r--xmake/core/base/os.lua47
-rw-r--r--xmake/core/sandbox/modules/import/core/base/binutils.lua10
16 files changed, 523 insertions, 109 deletions
diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h
index 862a3923b..53df74e5d 100644
--- a/core/src/xmake/binutils/ar/prefix.h
+++ b/core/src/xmake/binutils/ar/prefix.h
@@ -35,7 +35,6 @@
extern tb_bool_t xm_binutils_coff_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_elf_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
extern tb_bool_t xm_binutils_macho_read_symbols(tb_stream_ref_t istream, tb_hize_t base_offset, lua_State *lua);
-extern tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream);
/* //////////////////////////////////////////////////////////////////////////////////////
* types
diff --git a/core/src/xmake/binutils/ar/readsyms.c b/core/src/xmake/binutils/ar/readsyms.c
index a41bf2e33..1a0d3b540 100644
--- a/core/src/xmake/binutils/ar/readsyms.c
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -28,11 +28,6 @@
#include "prefix.h"
/* //////////////////////////////////////////////////////////////////////////////////////
- * private implementation
- */
-
-
-/* //////////////////////////////////////////////////////////////////////////////////////
* implementation
*/
@@ -351,7 +346,7 @@ tb_bool_t xm_binutils_ar_read_symbols(tb_stream_ref_t istream, tb_hize_t base_of
tb_hize_t current_pos = tb_stream_offset(istream);
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format != XM_BINUTILS_FORMAT_AR) {
// create entry table
lua_newtable(lua);
diff --git a/core/src/xmake/binutils/deplibs.c b/core/src/xmake/binutils/deplibs.c
index 289e656a0..48a7cd200 100644
--- a/core/src/xmake/binutils/deplibs.c
+++ b/core/src/xmake/binutils/deplibs.c
@@ -73,7 +73,7 @@ tb_int_t xm_binutils_deplibs(lua_State *lua) {
}
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "deplibs: cannot detect file format");
diff --git a/core/src/xmake/binutils/extractlib.c b/core/src/xmake/binutils/extractlib.c
index ec588f213..5e6559082 100644
--- a/core/src/xmake/binutils/extractlib.c
+++ b/core/src/xmake/binutils/extractlib.c
@@ -87,7 +87,7 @@ tb_int_t xm_binutils_extractlib(lua_State *lua) {
}
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
error_msg = "cannot detect format";
break;
diff --git a/core/src/xmake/binutils/format.c b/core/src/xmake/binutils/format.c
new file mode 100644
index 000000000..dd7e836f3
--- /dev/null
+++ b/core/src/xmake/binutils/format.c
@@ -0,0 +1,250 @@
+/*!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 format.c
+ *
+ */
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "format"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+/* check PE by validating DOS header (MZ/ZM) and PE signature at e_lfanew
+ *
+ * note: unlike ELF/Mach-O/AR, PE needs an additional read/peek to locate the PE signature.
+ */
+static tb_bool_t xm_binutils_format_is_pe(tb_stream_ref_t istream, tb_byte_t* first8) {
+ tb_assert_and_check_return_val(istream && first8, tb_false);
+ tb_bool_t ok = tb_false;
+ do {
+ // fast reject: DOS magic
+ tb_check_break((first8[0] == 'M' && first8[1] == 'Z') || (first8[0] == 'Z' && first8[1] == 'M'));
+
+ // ensure we can read enough for DOS stub + PE signature
+ tb_hong_t size = tb_stream_size(istream);
+ if (size > 0 && size < XM_BINUTILS_PE_DOS_STUB_MIN_SIZE + 4) {
+ break;
+ }
+
+ // peek a bounded prefix to locate e_lfanew and PE\\0\\0 signature
+ 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;
+ }
+
+ // e_lfanew points to PE signature offset
+ tb_uint32_t e_lfanew = tb_bits_get_u32_le(p + XM_BINUTILS_PE_DOS_ELFANEW_OFFSET);
+ 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;
+}
+
+// quick header checks from the first 8 bytes
+static __tb_inline__ tb_bool_t xm_binutils_format_is_ar(tb_byte_t const* first8) {
+ return first8[0] == '!' && first8[1] == '<' && first8[2] == 'a' &&
+ first8[3] == 'r' && first8[4] == 'c' && first8[5] == 'h' &&
+ (first8[6] == '>' || first8[6] == '\n') &&
+ (first8[7] == '\n' || first8[7] == '\r');
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_format_is_shebang(tb_byte_t const* first2) {
+ return first2[0] == '#' && first2[1] == '!';
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_format_is_ape(tb_byte_t const* first8) {
+ return first8[0] == 'M' && first8[1] == 'Z' &&
+ first8[2] == 'q' && first8[3] == 'F' &&
+ first8[4] == 'p' && first8[5] == 'D';
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_format_is_elf(tb_byte_t const* first8) {
+ return first8[0] == 0x7f && first8[1] == 'E' && first8[2] == 'L' && first8[3] == 'F';
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_format_is_macho(tb_byte_t const* first8) {
+ return (first8[0] == 0xfe && first8[1] == 0xed && first8[2] == 0xfa && (first8[3] == 0xce || first8[3] == 0xcf)) ||
+ (first8[0] == 0xce && first8[1] == 0xfa && first8[2] == 0xed && first8[3] == 0xfe) ||
+ (first8[0] == 0xcf && first8[1] == 0xfa && first8[2] == 0xed && first8[3] == 0xfe);
+}
+
+static __tb_inline__ tb_bool_t xm_binutils_format_is_coff(tb_byte_t const* first8) {
+ tb_uint16_t machine = tb_bits_get_u16_le(first8);
+ if (machine == 0x0000) {
+ tb_uint16_t machine2 = tb_bits_get_u16_le(first8 + 2);
+ if (machine2 == 0xffff) {
+ return tb_true;
+ }
+ }
+
+ return machine == XM_BINUTILS_COFF_MACHINE_I386 ||
+ machine == XM_BINUTILS_COFF_MACHINE_AMD64 ||
+ machine == XM_BINUTILS_COFF_MACHINE_ARM ||
+ machine == XM_BINUTILS_COFF_MACHINE_ARM64;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* 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_PE, XM_BINUTILS_FORMAT_UNKNOWN, or -1 on error
+ */
+tb_int_t xm_binutils_format_detect(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);
+
+ tb_int_t format = -1;
+ do {
+ tb_byte_t* p2 = tb_null;
+ if (!tb_stream_peek(istream, &p2, 2)) {
+ tb_hong_t size = tb_stream_size(istream);
+ if (size > 0 && size < 2) {
+ format = XM_BINUTILS_FORMAT_UNKNOWN;
+ }
+ break;
+ }
+ if (xm_binutils_format_is_shebang(p2)) {
+ format = XM_BINUTILS_FORMAT_SHEBANG;
+ break;
+ }
+
+ 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;
+ }
+
+ if (xm_binutils_format_is_ar(p)) {
+ format = XM_BINUTILS_FORMAT_AR;
+ break;
+ }
+
+ if (xm_binutils_format_is_ape(p)) {
+ format = XM_BINUTILS_FORMAT_APE;
+ break;
+ }
+
+ if (xm_binutils_format_is_elf(p)) {
+ format = XM_BINUTILS_FORMAT_ELF;
+ break;
+ }
+
+ if (xm_binutils_format_is_macho(p)) {
+ format = XM_BINUTILS_FORMAT_MACHO;
+ break;
+ }
+
+ if (xm_binutils_format_is_pe(istream, p)) {
+ format = XM_BINUTILS_FORMAT_PE;
+ break;
+ }
+
+ if (xm_binutils_format_is_coff(p)) {
+ format = XM_BINUTILS_FORMAT_COFF;
+ break;
+ }
+
+ format = XM_BINUTILS_FORMAT_UNKNOWN;
+
+ } while (0);
+
+ return format;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * lua implementation
+ */
+
+/* get binary file format (auto-detect format)
+ *
+ * local format, errors = binutils.format(filepath)
+ */
+tb_int_t xm_binutils_format(lua_State *lua) {
+ tb_assert_and_check_return_val(lua, 0);
+
+ // get the binary file path
+ tb_char_t const *binaryfile = luaL_checkstring(lua, 1);
+ tb_check_return_val(binaryfile, 0);
+
+ // open file
+ tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO);
+ if (!istream) {
+ lua_pushnil(lua);
+ lua_pushfstring(lua, "open %s failed", binaryfile);
+ return 2;
+ }
+
+ tb_bool_t ok = tb_false;
+ do {
+ if (!tb_stream_open(istream)) {
+ lua_pushnil(lua);
+ lua_pushfstring(lua, "open %s failed", binaryfile);
+ break;
+ }
+
+ tb_int_t format = xm_binutils_format_detect(istream);
+ if (format < 0) {
+ lua_pushnil(lua);
+ lua_pushliteral(lua, "cannot detect file format");
+ break;
+ }
+
+ switch (format) {
+ case XM_BINUTILS_FORMAT_COFF: lua_pushliteral(lua, "coff"); break;
+ case XM_BINUTILS_FORMAT_ELF: lua_pushliteral(lua, "elf"); break;
+ case XM_BINUTILS_FORMAT_MACHO: lua_pushliteral(lua, "macho"); break;
+ case XM_BINUTILS_FORMAT_AR: lua_pushliteral(lua, "ar"); break;
+ case XM_BINUTILS_FORMAT_PE: lua_pushliteral(lua, "pe"); break;
+ case XM_BINUTILS_FORMAT_SHEBANG: lua_pushliteral(lua, "shebang"); break;
+ case XM_BINUTILS_FORMAT_APE: lua_pushliteral(lua, "ape"); break;
+ default: lua_pushliteral(lua, "unknown"); break;
+ }
+
+ ok = tb_true;
+ } while (0);
+
+ if (istream) {
+ tb_stream_exit(istream);
+ }
+ return ok ? 1 : 2;
+}
diff --git a/core/src/xmake/binutils/mslib/readsyms.c b/core/src/xmake/binutils/mslib/readsyms.c
index 55857bb58..e314d08f3 100644
--- a/core/src/xmake/binutils/mslib/readsyms.c
+++ b/core/src/xmake/binutils/mslib/readsyms.c
@@ -298,7 +298,7 @@ tb_bool_t xm_binutils_mslib_read_symbols(tb_stream_ref_t istream, tb_hize_t base
tb_hize_t header_offset = current_pos - sizeof(xm_mslib_header_t);
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format != XM_BINUTILS_FORMAT_UNKNOWN && format != XM_BINUTILS_FORMAT_AR) {
// create entry table
lua_newtable(lua);
diff --git a/core/src/xmake/binutils/prefix.h b/core/src/xmake/binutils/prefix.h
index e7811652a..b3ffd8c27 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -34,6 +34,8 @@
#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_UNKNOWN 0
// COFF machine types (for format detection)
@@ -42,6 +44,16 @@
#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
*/
@@ -71,77 +83,6 @@ static __tb_inline__ tb_bool_t xm_binutils_read_magic(tb_stream_ref_t istream, t
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
- */
-static __tb_inline__ tb_int_t xm_binutils_detect_format(tb_stream_ref_t istream) {
- tb_assert_and_check_return_val(istream, -1);
-
- // peek first 8 bytes
- tb_byte_t* p = tb_null;
- if (!tb_stream_peek(istream, &p, 8)) {
- return -1;
- }
-
- // 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 PE/DOS magic (0x5A4D 'M' 'Z')
- if (p[0] == 'M' && p[1] == 'Z') {
- return XM_BINUTILS_FORMAT_PE;
- }
-
- // 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 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 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 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;
- }
- }
-
- 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;
- }
-
- // unknown format
- return XM_BINUTILS_FORMAT_UNKNOWN;
-}
-
/* copy data from input stream to output stream
*
* @param istream the input stream
@@ -280,4 +221,3 @@ static __tb_inline__ tb_bool_t xm_binutils_arch_is_64bit(tb_char_t const *arch)
#endif
-
diff --git a/core/src/xmake/binutils/readsyms.c b/core/src/xmake/binutils/readsyms.c
index 407ba7399..e29010314 100644
--- a/core/src/xmake/binutils/readsyms.c
+++ b/core/src/xmake/binutils/readsyms.c
@@ -75,7 +75,7 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
}
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "readsyms: cannot detect file format");
@@ -163,4 +163,3 @@ tb_int_t xm_binutils_readsyms(lua_State *lua) {
return ok ? 1 : 2;
}
-
diff --git a/core/src/xmake/binutils/rpath.c b/core/src/xmake/binutils/rpath.c
index 63754c891..c720141c2 100644
--- a/core/src/xmake/binutils/rpath.c
+++ b/core/src/xmake/binutils/rpath.c
@@ -74,7 +74,7 @@ tb_int_t xm_binutils_rpath_list(lua_State *lua) {
}
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "rpath_list: cannot detect file format");
@@ -142,7 +142,7 @@ tb_int_t xm_binutils_rpath_clean(lua_State *lua) {
}
// detect format
- tb_int_t format = xm_binutils_detect_format(istream);
+ tb_int_t format = xm_binutils_format_detect(istream);
if (format < 0) {
lua_pushboolean(lua, tb_false);
lua_pushfstring(lua, "rpath_clean: cannot detect file format");
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index 9ceabd156..eb07fe2b5 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -151,6 +151,7 @@ tb_int_t xm_os_cpuinfo(lua_State *lua);
tb_int_t xm_os_meminfo(lua_State *lua);
tb_int_t xm_os_readlink(lua_State *lua);
tb_int_t xm_os_filesize(lua_State *lua);
+tb_int_t xm_os_access(lua_State *lua);
tb_int_t xm_os_emptydir(lua_State *lua);
tb_int_t xm_os_syserror(lua_State *lua);
tb_int_t xm_os_strerror(lua_State *lua);
@@ -361,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
@@ -446,6 +448,7 @@ static luaL_Reg const g_os_functions[] = {
{ "fscase", xm_os_fscase },
{ "rename", xm_os_rename },
{ "exists", xm_os_exists },
+ { "access", xm_os_access },
{ "setenv", xm_os_setenv },
{ "getenv", xm_os_getenv },
{ "getenvs", xm_os_getenvs },
@@ -711,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 },
};
@@ -911,7 +915,13 @@ static tb_bool_t xm_engine_get_program_file(xm_engine_t *engine, tb_char_t **arg
if (!ok && argv) {
tb_char_t const *p = argv[0];
if (p && tb_file_info(p, tb_null)) {
- tb_strlcpy(path, p, maxn);
+ if (tb_path_is_absolute(p)) {
+ tb_strlcpy(path, p, maxn);
+ } else {
+ if (!tb_path_absolute(p, path, maxn)) {
+ tb_strlcpy(path, p, maxn);
+ }
+ }
ok = tb_true;
}
}
diff --git a/core/src/xmake/os/access.c b/core/src/xmake/os/access.c
new file mode 100644
index 000000000..1bfcd072f
--- /dev/null
+++ b/core/src/xmake/os/access.c
@@ -0,0 +1,59 @@
+/*!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 access.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "access"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+tb_int_t xm_os_access(lua_State *lua) {
+ tb_assert_and_check_return_val(lua, 0);
+
+ // 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);
+
+ // parse mode
+ tb_size_t mode = 0;
+ while (*mode_str) {
+ switch (*mode_str) {
+ 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++;
+ }
+
+ // os.access(path, mode)
+ lua_pushboolean(lua, tb_file_access(path, mode));
+ return 1;
+}
diff --git a/tests/modules/binutils/test.lua b/tests/modules/binutils/test.lua
new file mode 100644
index 000000000..bbb466144
--- /dev/null
+++ b/tests/modules/binutils/test.lua
@@ -0,0 +1,78 @@
+import("core.base.binutils")
+
+function test_format(t)
+ local tempdir = "temp/binutils_format"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ local unknown = path.join(tempdir, "unknown.bin")
+ io.writefile(unknown, "12345678")
+ local format = binutils.format(unknown)
+ t:are_equal(format, "unknown")
+
+ local scriptfile = path.join(tempdir, "a.sh")
+ io.writefile(scriptfile, "#!/bin/sh\nexit 0\n")
+ t:are_equal(binutils.format(scriptfile), "shebang")
+
+ local apefile = path.join(tempdir, "a.ape")
+ io.writefile(apefile, "MZqFpD00")
+ t:are_equal(binutils.format(apefile), "ape")
+
+ local apecom = path.join(tempdir, "a.com")
+ io.writefile(apecom, "MZqFpD00")
+ t:are_equal(binutils.format(apecom), "ape")
+
+ local comfile = path.join(tempdir, "b.com")
+ io.writefile(comfile, "12345678")
+ t:are_equal(binutils.format(comfile), "unknown")
+
+ local programfile = os.programfile()
+ if programfile then
+ local expected = "elf"
+ if is_host("windows") then
+ expected = "pe"
+ elseif is_host("macosx", "iphoneos", "watchos", "appletvos") then
+ expected = "macho"
+ end
+ local format = binutils.format(programfile)
+ if format ~= "ape" then
+ t:are_equal(format, expected)
+ end
+ end
+
+ local ar = path.join(tempdir, "a.a")
+ io.writefile(ar, "!<arch>\n")
+ t:are_equal(binutils.format(ar), "ar")
+
+ local elf = path.join(tempdir, "a.elf")
+ io.writefile(elf, string.char(0x7f, string.byte("E"), string.byte("L"), string.byte("F"), 0, 0, 0, 0))
+ t:are_equal(binutils.format(elf), "elf")
+
+ local macho = path.join(tempdir, "a.macho")
+ io.writefile(macho, string.char(0xfe, 0xed, 0xfa, 0xcf, 0, 0, 0, 0))
+ t:are_equal(binutils.format(macho), "macho")
+
+ local coff = path.join(tempdir, "a.obj")
+ io.writefile(coff, string.char(0x4c, 0x01, 0, 0, 0, 0, 0, 0))
+ t:are_equal(binutils.format(coff), "coff")
+
+ local pefile = path.join(tempdir, "a.exe")
+ local pe = {}
+ for _ = 1, 0x44 do
+ table.insert(pe, 0)
+ end
+ pe[1] = string.byte("M")
+ pe[2] = string.byte("Z")
+ pe[0x3c + 1] = 0x40
+ pe[0x3c + 2] = 0
+ pe[0x3c + 3] = 0
+ pe[0x3c + 4] = 0
+ pe[0x40 + 1] = string.byte("P")
+ pe[0x40 + 2] = string.byte("E")
+ pe[0x40 + 3] = 0
+ pe[0x40 + 4] = 0
+ io.writefile(pefile, string.char(table.unpack(pe)))
+ t:are_equal(binutils.format(pefile), "pe")
+
+ os.tryrm(tempdir)
+end
diff --git a/tests/modules/os/test.lua b/tests/modules/os/test.lua
index 2e04e1b35..2dcab35c6 100644
--- a/tests/modules/os/test.lua
+++ b/tests/modules/os/test.lua
@@ -169,3 +169,42 @@ function test_async(t)
t:require(os.isdir(tmpdir2))
os.rm(tmpdir2, {async = true, detach = true})
end
+
+function test_isexec(t)
+ local tempdir = "temp/isexec"
+ os.tryrm(tempdir)
+ os.mkdir(tempdir)
+
+ local programfile = os.programfile()
+ if programfile then
+ t:require(os.isexec(programfile))
+ end
+
+ local filepath = path.join(tempdir, "script")
+ io.writefile(filepath, "echo test\n")
+
+ if is_host("windows") then
+ local batfile = path.join(tempdir, "a.bat")
+ io.writefile(batfile, "echo test\r\n")
+ t:require(os.isexec(batfile))
+
+ local comfile = path.join(tempdir, "a.com")
+ io.writefile(comfile, "12345678")
+ t:require(os.isexec(comfile))
+
+ local suffix = path.join(tempdir, "prog")
+ io.writefile(suffix .. ".exe", "")
+ t:require(os.isexec(suffix))
+
+ local suffix2 = path.join(tempdir, "prog2")
+ io.writefile(suffix2 .. ".com", "")
+ t:require(os.isexec(suffix2))
+ else
+ os.vrunv("chmod", {"-x", filepath})
+ t:require_not(os.isexec(filepath))
+ os.vrunv("chmod", {"+x", filepath})
+ t:require(os.isexec(filepath))
+ end
+
+ os.tryrm(tempdir)
+end
diff --git a/xmake/core/base/binutils.lua b/xmake/core/base/binutils.lua
index b513be0b1..7e08ac317 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)
@@ -42,7 +43,7 @@ function binutils.bin2c(binaryfile, outputfile, opt)
return binutils._bin2c(binaryfile, outputfile, opt.linewidth or 32, opt.nozeroend or false)
else
-- fallback to old implementation if C implementation not available
- return nil, "bin2c: C implementation not available"
+ return nil, "C implementation not available"
end
end
@@ -76,21 +77,30 @@ function binutils.bin2obj(binaryfile, outputfile, opt)
if format == "coff" then
if not binutils._bin2coff then
- return nil, "bin2obj: binutils._bin2coff not available (C implementation not compiled)"
+ return nil, "binutils._bin2coff not available (C implementation not compiled)"
end
return binutils._bin2coff(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.arch or "x86_64", opt.basename, opt.zeroend or false)
elseif format == "macho" then
if not binutils._bin2macho then
- return nil, "bin2obj: binutils._bin2macho not available (C implementation not compiled)"
+ return nil, "binutils._bin2macho not available (C implementation not compiled)"
end
return binutils._bin2macho(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.plat or "macosx", opt.arch or "x86_64", opt.basename, opt.target_minver, opt.xcode_sdkver, opt.zeroend or false)
elseif format == "elf" then
if not binutils._bin2elf then
- return nil, "bin2obj: binutils._bin2elf not available (C implementation not compiled)"
+ return nil, "binutils._bin2elf not available (C implementation not compiled)"
end
return binutils._bin2elf(binaryfile, outputfile, opt.symbol_prefix or "_binary_", opt.arch or "x86_64", opt.basename, opt.zeroend or false)
else
- return nil, string.format("bin2obj: unsupported format '%s' (supported: coff, elf, macho)", format)
+ return nil, string.format("unsupported format '%s' (supported: coff, elf, macho)", format)
+ 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, "C implementation not available"
end
end
@@ -100,7 +110,7 @@ function binutils.readsyms(binaryfile)
if binutils._readsyms then
return binutils._readsyms(binaryfile)
else
- return nil, "readsyms: C implementation not available"
+ return nil, "C implementation not available"
end
end
@@ -109,7 +119,7 @@ function binutils.deplibs(binaryfile)
if binutils._deplibs then
return binutils._deplibs(binaryfile)
else
- return nil, "deplibs: C implementation not available"
+ return nil, "C implementation not available"
end
end
@@ -118,7 +128,7 @@ function binutils.rpath_list(binaryfile)
if binutils._rpath_list then
return binutils._rpath_list(binaryfile)
else
- return nil, "binutils: internal rpath_list not supported!"
+ return nil, "internal rpath_list not supported!"
end
end
@@ -129,7 +139,7 @@ function binutils.rpath_clean(binaryfile)
if binutils._rpath_clean then
return binutils._rpath_clean(binaryfile)
else
- return false, "binutils: internal rpath_clean not supported!"
+ return false, "internal rpath_clean not supported!"
end
end
@@ -146,10 +156,10 @@ function binutils.extractlib(libraryfile, outputdir, opt)
if ok then
return true
else
- return false, errors or "extractlib: unknown error"
+ return false, errors or "unknown error"
end
else
- return false, "extractlib: C implementation not available"
+ return false, "C implementation not available"
end
end
diff --git a/xmake/core/base/os.lua b/xmake/core/base/os.lua
index dcd5ab5d1..12bc30c1d 100644
--- a/xmake/core/base/os.lua
+++ b/xmake/core/base/os.lua
@@ -46,6 +46,7 @@ os._getenvs = os._getenvs or os.getenvs
os._cpuinfo = os._cpuinfo or os.cpuinfo
os._meminfo = os._meminfo or os.meminfo
os._readlink = os._readlink or os.readlink
+os._access = os._access or os.access
-- syserror code
os.SYSERR_UNKNOWN = -1
@@ -1148,22 +1149,47 @@ end
-- is executable program file?
function os.isexec(filepath)
- assert(filepath)
-
- -- TODO
- -- check permission
-
- -- check executable program exist
- if os.isfile(filepath) then
- return true
- end
if os.host() == "windows" then
- for _, suffix in ipairs({".exe", ".cmd", ".bat"}) do
+ local exts_map = os._ISEXEC_WINDOWS_EXTS_MAP
+ if not exts_map then
+ local exts = {".exe", ".com", ".cmd", ".bat", ".ps1", ".sh"}
+ exts_map = {}
+ for _, ext in ipairs(exts) do
+ exts_map[ext] = true
+ end
+ os._ISEXEC_WINDOWS_EXTS_MAP = exts_map
+ end
+ if os.isfile(filepath) then
+ local extension = path.extension(filepath)
+ if extension and #extension > 0 then
+ if exts_map[extension:lower()] then
+ return true
+ end
+ else
+ -- detect executable file header
+ --
+ -- @note only for files without extension, because .dll is also PE
+ -- pe: native windows executables
+ -- ape: cosmocc/APE executables (e.g. `cosmocc -o foo.exe ...`)
+ -- shebang: scripts starting with `#!`
+ local format = nil
+ if binutils and binutils.format then
+ format = binutils.format(filepath)
+ end
+ if format == "pe" or format == "ape" or format == "shebang" then
+ return true
+ end
+ end
+ end
+ for suffix, _ in pairs(exts_map) 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
-- get system host
@@ -1616,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)