diff options
| author | ruki <[email protected]> | 2026-01-30 17:28:24 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-01-30 17:28:24 +0800 |
| commit | 51a68ee33918b95811420beb6a56673a83abe6cc (patch) | |
| tree | aba2c0591c0285709eede0e6adf4f5a68b066674 | |
| parent | 45f856e3ccdec7da98ac7f07251f30d85640d268 (diff) | |
| parent | d309d7832a6b079d81c10f2f6449081a1d7881d2 (diff) | |
Merge pull request #7278 from xmake-io/isexec
Improve os.isexec
| -rw-r--r-- | core/src/xmake/binutils/ar/prefix.h | 1 | ||||
| -rw-r--r-- | core/src/xmake/binutils/ar/readsyms.c | 7 | ||||
| -rw-r--r-- | core/src/xmake/binutils/deplibs.c | 2 | ||||
| -rw-r--r-- | core/src/xmake/binutils/extractlib.c | 2 | ||||
| -rw-r--r-- | core/src/xmake/binutils/format.c | 250 | ||||
| -rw-r--r-- | core/src/xmake/binutils/mslib/readsyms.c | 2 | ||||
| -rw-r--r-- | core/src/xmake/binutils/prefix.h | 84 | ||||
| -rw-r--r-- | core/src/xmake/binutils/readsyms.c | 3 | ||||
| -rw-r--r-- | core/src/xmake/binutils/rpath.c | 4 | ||||
| -rw-r--r-- | core/src/xmake/engine.c | 12 | ||||
| -rw-r--r-- | core/src/xmake/os/access.c | 59 | ||||
| -rw-r--r-- | tests/modules/binutils/test.lua | 78 | ||||
| -rw-r--r-- | tests/modules/os/test.lua | 39 | ||||
| -rw-r--r-- | xmake/core/base/binutils.lua | 32 | ||||
| -rw-r--r-- | xmake/core/base/os.lua | 47 | ||||
| -rw-r--r-- | xmake/core/sandbox/modules/import/core/base/binutils.lua | 10 |
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) |
