summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2026-01-30 00:58:01 +0800
committerruki <[email protected]>2026-01-30 00:58:01 +0800
commit499feec6a33984747dd235b860af7f6b5cc2a1e3 (patch)
treedfb74cb21a36f487b9b581f92e913126e9c2e317
parent148404f78b74d376eba5922cb197162a9f05b1a7 (diff)
improve binutils.format
-rw-r--r--core/src/xmake/binutils/ar/prefix.h2
-rw-r--r--core/src/xmake/binutils/ar/readsyms.c2
-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.c209
-rw-r--r--core/src/xmake/binutils/mslib/readsyms.c2
-rw-r--r--core/src/xmake/binutils/prefix.h135
-rw-r--r--core/src/xmake/binutils/readsyms.c3
-rw-r--r--core/src/xmake/binutils/rpath.c4
9 files changed, 222 insertions, 139 deletions
diff --git a/core/src/xmake/binutils/ar/prefix.h b/core/src/xmake/binutils/ar/prefix.h
index 862a3923b..cf6f58d03 100644
--- a/core/src/xmake/binutils/ar/prefix.h
+++ b/core/src/xmake/binutils/ar/prefix.h
@@ -35,7 +35,7 @@
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);
+extern tb_int_t xm_binutils_format_detect(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..9a4ff97de 100644
--- a/core/src/xmake/binutils/ar/readsyms.c
+++ b/core/src/xmake/binutils/ar/readsyms.c
@@ -351,7 +351,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..7adf38aa0
--- /dev/null
+++ b/core/src/xmake/binutils/format.c
@@ -0,0 +1,209 @@
+/*!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
+ */
+static tb_bool_t xm_binutils_format_detect_pe(tb_stream_ref_t istream, tb_byte_t* first8) {
+ tb_assert_and_check_return_val(istream && first8, tb_false);
+ 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;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * 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* 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 (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;
+ }
+
+ if (xm_binutils_format_detect_pe(istream, p)) {
+ format = XM_BINUTILS_FORMAT_PE;
+ break;
+ }
+
+ if (p[0] == 0x7f && p[1] == 'E' && p[2] == 'L' && p[3] == 'F') {
+ format = XM_BINUTILS_FORMAT_ELF;
+ break;
+ }
+
+ if (p[0] == 0xfe && p[1] == 0xed && p[2] == 0xfa &&
+ (p[3] == 0xce || p[3] == 0xcf)) {
+ format = XM_BINUTILS_FORMAT_MACHO;
+ break;
+ }
+ if (p[0] == 0xce && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) {
+ format = XM_BINUTILS_FORMAT_MACHO;
+ break;
+ }
+ if (p[0] == 0xcf && p[1] == 0xfa && p[2] == 0xed && p[3] == 0xfe) {
+ format = XM_BINUTILS_FORMAT_MACHO;
+ break;
+ }
+
+ tb_uint16_t machine = (tb_uint16_t)((p[1] << 8) | p[0]);
+
+ 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) {
+ 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, "format: open %s failed", binaryfile);
+ return 2;
+ }
+
+ tb_bool_t ok = tb_false;
+ do {
+ if (!tb_stream_open(istream)) {
+ lua_pushnil(lua);
+ lua_pushfstring(lua, "format: open %s failed", binaryfile);
+ break;
+ }
+
+ tb_int_t format = xm_binutils_format_detect(istream);
+ if (format < 0) {
+ lua_pushnil(lua);
+ lua_pushliteral(lua, "format: 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;
+ 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 7df5b55a7..a38eef658 100644
--- a/core/src/xmake/binutils/prefix.h
+++ b/core/src/xmake/binutils/prefix.h
@@ -48,6 +48,11 @@
#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
*/
@@ -76,136 +81,6 @@ 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_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);
-
- 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')) {
- format = XM_BINUTILS_FORMAT_AR;
- break;
- }
-
- 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') {
- 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)) {
- 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) + ...
- tb_uint16_t machine = (tb_uint16_t)((p[1] << 8) | p[0]);
-
- // 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) {
- format = XM_BINUTILS_FORMAT_COFF;
- break;
- }
-
- // unknown format
- format = XM_BINUTILS_FORMAT_UNKNOWN;
-
- } while (0);
-
- return format;
-}
-
/* copy data from input stream to output stream
*
* @param istream the input stream
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");