summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-12-07 14:19:46 +0800
committerruki <[email protected]>2025-12-07 17:16:00 +0800
commit832de3edc75ba2bb1e6ea4f26ee39513f35d4bd4 (patch)
treec4d473bd9d269d2fd559a97e262685565038eeea
parentb18cf3e29067c0b9159880bf2028eb5555176841 (diff)
add bin2elf
-rw-r--r--core/src/xmake/engine.c2
-rw-r--r--core/src/xmake/utils/bin2elf.c469
-rw-r--r--xmake/core/sandbox/modules/utils.lua10
-rw-r--r--xmake/modules/private/utils/bin2obj.lua27
4 files changed, 507 insertions, 1 deletions
diff --git a/core/src/xmake/engine.c b/core/src/xmake/engine.c
index 802543700..71a4d4b95 100644
--- a/core/src/xmake/engine.c
+++ b/core/src/xmake/engine.c
@@ -338,6 +338,7 @@ tb_int_t xm_package_loadxmi(lua_State *lua);
tb_int_t xm_utils_bin2c(lua_State *lua);
tb_int_t xm_utils_bin2coff(lua_State *lua);
tb_int_t xm_utils_bin2macho(lua_State *lua);
+tb_int_t xm_utils_bin2elf(lua_State *lua);
#ifdef XM_CONFIG_API_HAVE_CURSES
// register curses functions
@@ -659,6 +660,7 @@ static luaL_Reg const g_utils_functions[] = {
{ "bin2c", xm_utils_bin2c },
{ "bin2coff", xm_utils_bin2coff },
{ "bin2macho", xm_utils_bin2macho },
+ { "bin2elf", xm_utils_bin2elf },
{ tb_null, tb_null },
};
diff --git a/core/src/xmake/utils/bin2elf.c b/core/src/xmake/utils/bin2elf.c
new file mode 100644
index 000000000..a55cf8b1f
--- /dev/null
+++ b/core/src/xmake/utils/bin2elf.c
@@ -0,0 +1,469 @@
+/*!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 bin2elf.c
+ *
+ */
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * trace
+ */
+#define TB_TRACE_MODULE_NAME "bin2elf"
+#define TB_TRACE_MODULE_DEBUG (0)
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * includes
+ */
+#include "prefix.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * macros
+ */
+#define XM_ELF_MACHINE_X86_64 0x3e
+#define XM_ELF_MACHINE_ARM64 0xb7
+#define XM_ELF_MACHINE_ARM 0x28
+#define XM_ELF_MACHINE_I386 0x03
+
+#define XM_ELF_SHT_PROGBITS 0x1
+#define XM_ELF_SHT_SYMTAB 0x2
+#define XM_ELF_SHT_STRTAB 0x3
+
+#define XM_ELF_SHF_ALLOC 0x2
+#define XM_ELF_SHF_WRITE 0x1
+
+#define XM_ELF_STB_GLOBAL 0x1
+#define XM_ELF_STT_OBJECT 0x1
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * types
+ */
+#include "tbox/prefix/packed.h"
+typedef struct __xm_elf64_header_t {
+ tb_uint8_t e_ident[16];
+ tb_uint16_t e_type;
+ tb_uint16_t e_machine;
+ tb_uint32_t e_version;
+ tb_uint64_t e_entry;
+ tb_uint64_t e_phoff;
+ tb_uint64_t e_shoff;
+ tb_uint32_t e_flags;
+ tb_uint16_t e_ehsize;
+ tb_uint16_t e_phentsize;
+ tb_uint16_t e_phnum;
+ tb_uint16_t e_shentsize;
+ tb_uint16_t e_shnum;
+ tb_uint16_t e_shstrndx;
+} __tb_packed__ xm_elf64_header_t;
+
+typedef struct __xm_elf64_section_t {
+ tb_uint32_t sh_name;
+ tb_uint32_t sh_type;
+ tb_uint64_t sh_flags;
+ tb_uint64_t sh_addr;
+ tb_uint64_t sh_offset;
+ tb_uint64_t sh_size;
+ tb_uint32_t sh_link;
+ tb_uint32_t sh_info;
+ tb_uint64_t sh_addralign;
+ tb_uint64_t sh_entsize;
+} __tb_packed__ xm_elf64_section_t;
+
+typedef struct __xm_elf64_symbol_t {
+ tb_uint32_t st_name;
+ tb_uint8_t st_info;
+ tb_uint8_t st_other;
+ tb_uint16_t st_shndx;
+ tb_uint64_t st_value;
+ tb_uint64_t st_size;
+} __tb_packed__ xm_elf64_symbol_t;
+
+#include "tbox/prefix/packed.h"
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * private implementation
+ */
+static tb_uint16_t xm_utils_bin2elf_get_machine(tb_char_t const *arch) {
+ if (!arch) {
+ return XM_ELF_MACHINE_X86_64;
+ }
+ if (tb_strcmp(arch, "x86_64") == 0 || tb_strcmp(arch, "x64") == 0) {
+ return XM_ELF_MACHINE_X86_64;
+ } else if (tb_strcmp(arch, "arm64") == 0 || tb_strcmp(arch, "aarch64") == 0) {
+ return XM_ELF_MACHINE_ARM64;
+ } else if (tb_strcmp(arch, "arm") == 0 || tb_strcmp(arch, "armv7") == 0) {
+ return XM_ELF_MACHINE_ARM;
+ } else if (tb_strcmp(arch, "i386") == 0 || tb_strcmp(arch, "x86") == 0) {
+ return XM_ELF_MACHINE_I386;
+ }
+ return XM_ELF_MACHINE_X86_64;
+}
+
+static tb_bool_t xm_utils_bin2elf_dump(tb_stream_ref_t istream,
+ tb_stream_ref_t ostream,
+ tb_char_t const *symbol_prefix,
+ tb_char_t const *arch,
+ tb_char_t const *basename,
+ tb_bool_t zeroend) {
+ tb_assert_and_check_return_val(istream && ostream, tb_false);
+
+ // get file size
+ tb_hong_t filesize = tb_stream_size(istream);
+ if (filesize < 0 || filesize > 0xffffffffU) {
+ return tb_false;
+ }
+ tb_uint32_t datasize = (tb_uint32_t)filesize;
+ // add null terminator if zeroend is true
+ if (zeroend) {
+ if (datasize >= 0xffffffffU) {
+ return tb_false; // would overflow
+ }
+ datasize++;
+ }
+
+ // generate symbol names from filename
+ tb_char_t symbol_name[256] = {0};
+ tb_char_t symbol_start[256] = {0};
+ tb_char_t symbol_end[256] = {0};
+
+ // use basename or default to "data"
+ if (!basename || !basename[0]) {
+ basename = "data";
+ }
+
+ // build symbol name
+ if (symbol_prefix) {
+ tb_snprintf(symbol_name, sizeof(symbol_name), "%s%s", symbol_prefix, basename);
+ } else {
+ tb_snprintf(symbol_name, sizeof(symbol_name), "_binary_%s", basename);
+ }
+
+ // replace non-alphanumeric with underscore
+ for (tb_size_t i = 0; symbol_name[i]; i++) {
+ if (!tb_isalpha(symbol_name[i]) && !tb_isdigit(symbol_name[i]) && symbol_name[i] != '_') {
+ symbol_name[i] = '_';
+ }
+ }
+
+ tb_snprintf(symbol_start, sizeof(symbol_start), "%s_start", symbol_name);
+ tb_snprintf(symbol_end, sizeof(symbol_end), "%s_end", symbol_name);
+
+ // calculate offsets
+ tb_uint32_t header_size = sizeof(xm_elf64_header_t);
+ tb_uint32_t section_header_size = sizeof(xm_elf64_section_t);
+ tb_uint32_t section_count = 5; // NULL, .rodata, .symtab, .strtab, .shstrtab
+ tb_uint32_t section_headers_ofs = header_size;
+ tb_uint32_t rodata_ofs = section_headers_ofs + section_count * section_header_size;
+ tb_uint32_t rodata_size = datasize;
+ tb_uint32_t rodata_padding = (8 - (rodata_size & 7)) & 7;
+ tb_uint32_t symtab_ofs = rodata_ofs + rodata_size + rodata_padding;
+ tb_uint32_t symtab_size = 3 * sizeof(xm_elf64_symbol_t); // NULL, start, end
+ tb_uint32_t symtab_padding = (8 - (symtab_size & 7)) & 7;
+ tb_uint32_t strtab_ofs = symtab_ofs + symtab_size + symtab_padding;
+
+ // calculate string table size
+ tb_size_t start_len = tb_strlen(symbol_start);
+ tb_size_t end_len = tb_strlen(symbol_end);
+ tb_uint32_t strtab_size = 1; // initial null byte
+ strtab_size += (tb_uint32_t)(start_len + 1);
+ strtab_size += (tb_uint32_t)(end_len + 1);
+ tb_uint32_t strtab_padding = (8 - (strtab_size & 7)) & 7;
+
+ // calculate section header string table size
+ tb_uint32_t shstrtab_size = 1; // initial null byte
+ shstrtab_size += 8; // ".rodata\0" (7 + 1)
+ shstrtab_size += 8; // ".symtab\0" (7 + 1)
+ shstrtab_size += 8; // ".strtab\0" (7 + 1)
+ shstrtab_size += 10; // ".shstrtab\0" (9 + 1)
+ tb_uint32_t shstrtab_ofs = strtab_ofs + strtab_size + strtab_padding;
+
+ // write ELF header
+ xm_elf64_header_t header;
+ tb_memset(&header, 0, sizeof(header));
+ header.e_ident[0] = 0x7f;
+ header.e_ident[1] = 'E';
+ header.e_ident[2] = 'L';
+ header.e_ident[3] = 'F';
+ header.e_ident[4] = 2; // ELFCLASS64
+ header.e_ident[5] = 1; // ELFDATA2LSB
+ header.e_ident[6] = 1; // EV_CURRENT
+ header.e_ident[7] = 0; // ELFOSABI_SYSV
+ header.e_type = 1; // ET_REL
+ header.e_machine = xm_utils_bin2elf_get_machine(arch);
+ header.e_version = 1;
+ header.e_shoff = section_headers_ofs;
+ header.e_ehsize = header_size;
+ header.e_shentsize = section_header_size;
+ header.e_shnum = section_count;
+ header.e_shstrndx = 4; // .shstrtab section index
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&header, sizeof(header))) {
+ return tb_false;
+ }
+
+ // write section headers
+ xm_elf64_section_t section_null;
+ tb_memset(&section_null, 0, sizeof(section_null));
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_null, sizeof(section_null))) {
+ return tb_false;
+ }
+
+ // write .rodata section header
+ xm_elf64_section_t section_rodata;
+ tb_memset(&section_rodata, 0, sizeof(section_rodata));
+ section_rodata.sh_name = 1; // ".rodata" in shstrtab
+ section_rodata.sh_type = XM_ELF_SHT_PROGBITS;
+ section_rodata.sh_flags = XM_ELF_SHF_ALLOC;
+ section_rodata.sh_offset = rodata_ofs;
+ section_rodata.sh_size = rodata_size;
+ section_rodata.sh_addralign = 8;
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_rodata, sizeof(section_rodata))) {
+ return tb_false;
+ }
+
+ // write .symtab section header
+ xm_elf64_section_t section_symtab;
+ tb_memset(&section_symtab, 0, sizeof(section_symtab));
+ section_symtab.sh_name = 9; // ".symtab" in shstrtab
+ section_symtab.sh_type = XM_ELF_SHT_SYMTAB;
+ section_symtab.sh_offset = symtab_ofs;
+ section_symtab.sh_size = symtab_size;
+ section_symtab.sh_link = 3; // .strtab section index
+ section_symtab.sh_info = 1; // first global symbol index
+ section_symtab.sh_addralign = 8;
+ section_symtab.sh_entsize = sizeof(xm_elf64_symbol_t);
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_symtab, sizeof(section_symtab))) {
+ return tb_false;
+ }
+
+ // write .strtab section header
+ xm_elf64_section_t section_strtab;
+ tb_memset(&section_strtab, 0, sizeof(section_strtab));
+ section_strtab.sh_name = 17; // ".strtab" in shstrtab
+ section_strtab.sh_type = XM_ELF_SHT_STRTAB;
+ section_strtab.sh_offset = strtab_ofs;
+ section_strtab.sh_size = strtab_size;
+ section_strtab.sh_addralign = 1;
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_strtab, sizeof(section_strtab))) {
+ return tb_false;
+ }
+
+ // write .shstrtab section header
+ xm_elf64_section_t section_shstrtab;
+ tb_memset(&section_shstrtab, 0, sizeof(section_shstrtab));
+ section_shstrtab.sh_name = 25; // ".shstrtab" in shstrtab
+ section_shstrtab.sh_type = XM_ELF_SHT_STRTAB;
+ section_shstrtab.sh_offset = shstrtab_ofs; // points to initial null byte
+ section_shstrtab.sh_size = shstrtab_size; // size includes initial null and all strings
+ section_shstrtab.sh_addralign = 1;
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&section_shstrtab, sizeof(section_shstrtab))) {
+ return tb_false;
+ }
+
+ // write .rodata section data
+ if (!xm_utils_stream_copy(istream, ostream, filesize)) {
+ return tb_false;
+ }
+ // append null terminator if zeroend is true
+ if (zeroend) {
+ tb_byte_t zero = 0;
+ if (!tb_stream_bwrit(ostream, &zero, 1)) {
+ return tb_false;
+ }
+ }
+
+ // align .rodata to 8 bytes
+ if (rodata_padding > 0) {
+ tb_byte_t zero = 0;
+ while (rodata_padding-- > 0) {
+ if (!tb_stream_bwrit(ostream, &zero, 1)) {
+ return tb_false;
+ }
+ }
+ }
+
+ // write symbol table
+ // symbol 0: NULL symbol
+ xm_elf64_symbol_t sym_null;
+ tb_memset(&sym_null, 0, sizeof(sym_null));
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_null, sizeof(sym_null))) {
+ return tb_false;
+ }
+
+ // symbol 1: _binary_xxx_start
+ xm_elf64_symbol_t sym_start;
+ tb_memset(&sym_start, 0, sizeof(sym_start));
+ sym_start.st_name = 1; // offset in .strtab (after initial null)
+ sym_start.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT;
+ sym_start.st_shndx = 1; // .rodata section index
+ sym_start.st_value = 0;
+ sym_start.st_size = 0;
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_start, sizeof(sym_start))) {
+ return tb_false;
+ }
+
+ // symbol 2: _binary_xxx_end
+ xm_elf64_symbol_t sym_end;
+ tb_memset(&sym_end, 0, sizeof(sym_end));
+ sym_end.st_name = 1 + (tb_uint32_t)(start_len + 1); // offset in .strtab
+ sym_end.st_info = (XM_ELF_STB_GLOBAL << 4) | XM_ELF_STT_OBJECT;
+ sym_end.st_shndx = 1; // .rodata section index
+ sym_end.st_value = rodata_size;
+ sym_end.st_size = 0;
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)&sym_end, sizeof(sym_end))) {
+ return tb_false;
+ }
+
+ // align .symtab to 8 bytes
+ if (symtab_padding > 0) {
+ tb_byte_t zero = 0;
+ while (symtab_padding-- > 0) {
+ if (!tb_stream_bwrit(ostream, &zero, 1)) {
+ return tb_false;
+ }
+ }
+ }
+
+ // write string table
+ tb_byte_t null = 0;
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_start, start_len)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)symbol_end, end_len)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+
+ // align .strtab to 8 bytes
+ if (strtab_padding > 0) {
+ tb_byte_t zero = 0;
+ while (strtab_padding-- > 0) {
+ if (!tb_stream_bwrit(ostream, &zero, 1)) {
+ return tb_false;
+ }
+ }
+ }
+
+ // write section header string table
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".rodata", 7)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".symtab", 7)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".strtab", 7)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, (tb_byte_t const *)".shstrtab", 9)) {
+ return tb_false;
+ }
+ if (!tb_stream_bwrit(ostream, &null, 1)) {
+ return tb_false;
+ }
+
+ return tb_true;
+}
+
+/* //////////////////////////////////////////////////////////////////////////////////////
+ * implementation
+ */
+
+/* generate ELF object file from binary file
+ *
+ * local ok, errors = utils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend)
+ */
+tb_int_t xm_utils_bin2elf(lua_State *lua) {
+ tb_assert_and_check_return_val(lua, 0);
+
+ // get the binaryfile
+ tb_char_t const *binaryfile = luaL_checkstring(lua, 1);
+ tb_check_return_val(binaryfile, 0);
+
+ // get the outputfile
+ tb_char_t const *outputfile = luaL_checkstring(lua, 2);
+ tb_check_return_val(outputfile, 0);
+
+ // get symbol prefix (optional)
+ tb_char_t const *symbol_prefix = lua_isstring(lua, 3) ? lua_tostring(lua, 3) : tb_null;
+
+ // get arch (optional)
+ tb_char_t const *arch = lua_isstring(lua, 4) ? lua_tostring(lua, 4) : tb_null;
+
+ // get basename (optional)
+ tb_char_t const *basename = lua_isstring(lua, 5) ? lua_tostring(lua, 5) : tb_null;
+
+ // get zeroend (optional, default: false)
+ tb_bool_t zeroend = lua_toboolean(lua, 6);
+
+ // do dump
+ tb_bool_t ok = tb_false;
+ tb_stream_ref_t istream = tb_stream_init_from_file(binaryfile, TB_FILE_MODE_RO);
+ tb_stream_ref_t ostream = tb_stream_init_from_file(outputfile,
+ TB_FILE_MODE_RW | TB_FILE_MODE_CREAT | TB_FILE_MODE_TRUNC);
+ do {
+ if (!tb_stream_open(istream)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "bin2elf: open %s failed", binaryfile);
+ break;
+ }
+
+ if (!tb_stream_open(ostream)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "bin2elf: open %s failed", outputfile);
+ break;
+ }
+
+ if (!xm_utils_bin2elf_dump(istream, ostream, symbol_prefix, arch, basename, zeroend)) {
+ lua_pushboolean(lua, tb_false);
+ lua_pushfstring(lua, "bin2elf: dump data failed");
+ break;
+ }
+
+ ok = tb_true;
+ lua_pushboolean(lua, ok);
+
+ } while (0);
+
+ if (istream)
+ tb_stream_clos(istream);
+ istream = tb_null;
+
+ if (ostream)
+ tb_stream_clos(ostream);
+ ostream = tb_null;
+
+ return ok ? 1 : 2;
+}
+
diff --git a/xmake/core/sandbox/modules/utils.lua b/xmake/core/sandbox/modules/utils.lua
index e1017863b..ef1361db9 100644
--- a/xmake/core/sandbox/modules/utils.lua
+++ b/xmake/core/sandbox/modules/utils.lua
@@ -178,6 +178,16 @@ if utils.bin2macho then
end
end
+-- generate ELF object file from the binary file
+if utils.bin2elf then
+ function sandbox_utils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend)
+ local ok, errors = utils.bin2elf(binaryfile, outputfile, symbol_prefix, arch, basename, zeroend)
+ if not ok then
+ os.raise(errors)
+ end
+ end
+end
+
-- return module
return sandbox_utils
diff --git a/xmake/modules/private/utils/bin2obj.lua b/xmake/modules/private/utils/bin2obj.lua
index 9b8dfa4c2..caa8b2e22 100644
--- a/xmake/modules/private/utils/bin2obj.lua
+++ b/xmake/modules/private/utils/bin2obj.lua
@@ -63,7 +63,32 @@ function _do_bin2obj_coff(binarypath, outputpath, opt)
end
function _do_bin2obj_elf(binarypath, outputpath, opt)
- raise("bin2obj: ELF format not yet implemented")
+ -- get symbol prefix
+ local symbol_prefix = opt.symbol_prefix or "_binary_"
+
+ -- get architecture (default: x86_64)
+ local arch = opt.arch or "x86_64"
+
+ -- get zeroend (default: false)
+ local zeroend = opt.zeroend or false
+
+ -- get filename from binary path (with extension, dots replaced with underscores)
+ local filename = path.filename(binarypath)
+ -- replace dots with underscores for symbol name (e.g., data.bin -> data_bin)
+ local basename = filename:gsub("%.", "_")
+
+ -- trace
+ print("converting binary file %s to ELF object file %s ..", binarypath, outputpath)
+
+ -- do dump
+ if utils.bin2elf then
+ utils.bin2elf(binarypath, outputpath, symbol_prefix, arch, basename, zeroend)
+ else
+ raise("bin2obj: utils.bin2elf not available (C implementation not compiled)")
+ end
+
+ -- trace
+ cprint("${bright}%s generated!", outputpath)
end
function _do_bin2obj_macho(binarypath, outputpath, opt)