diff --git a/native/jni/Android.mk b/native/jni/Android.mk index aff05ed14..1307d6703 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -29,10 +29,10 @@ LOCAL_SRC_FILES := \ su/pts.cpp \ su/su_daemon.cpp \ zygisk/entry.cpp \ + zygisk/main.cpp \ zygisk/utils.cpp \ zygisk/hook.cpp \ zygisk/memory.cpp \ - zygisk/companion.cpp \ zygisk/deny/cli.cpp \ zygisk/deny/utils.cpp \ zygisk/deny/revert.cpp diff --git a/native/jni/core/applets.cpp b/native/jni/core/applets.cpp index 4a8b90469..4dfa52d89 100644 --- a/native/jni/core/applets.cpp +++ b/native/jni/core/applets.cpp @@ -10,14 +10,15 @@ using namespace std; using main_fun = int (*)(int, char *[]); -static main_fun applet_main[] = { su_client_main, resetprop_main, nullptr }; +constexpr const char *applets[] = { "su", "resetprop", "zygisk", nullptr }; +static main_fun applet_mains[] = { su_client_main, resetprop_main, zygisk_main, nullptr }; static int call_applet(int argc, char *argv[]) { // Applets string_view base = basename(argv[0]); - for (int i = 0; applet_names[i]; ++i) { - if (base == applet_names[i]) { - return (*applet_main[i])(argc, argv); + for (int i = 0; applets[i]; ++i) { + if (base == applets[i]) { + return (*applet_mains[i])(argc, argv); } } if (str_starts(base, "app_process")) { diff --git a/native/jni/core/core.hpp b/native/jni/core/core.hpp index 6a9cc95d9..68049197d 100644 --- a/native/jni/core/core.hpp +++ b/native/jni/core/core.hpp @@ -34,6 +34,3 @@ void exec_common_scripts(const char *stage); void exec_module_scripts(const char *stage, const std::vector &modules); void install_apk(const char *apk); [[noreturn]] void install_module(const char *file); - -// Zygisk companion entrypoint -[[noreturn]] void zygiskd(int socket); diff --git a/native/jni/core/daemon.cpp b/native/jni/core/daemon.cpp index e4dee67bf..034b1371e 100644 --- a/native/jni/core/daemon.cpp +++ b/native/jni/core/daemon.cpp @@ -157,6 +157,7 @@ static void handle_request_async(int client, int code, const sock_cred &cred) { reboot(); break; case ZYGISK_REQUEST: + case ZYGISK_PASSTHROUGH: zygisk_handler(client, &cred); break; default: diff --git a/native/jni/core/magisk.cpp b/native/jni/core/magisk.cpp index 6b7c482aa..b27af63fe 100644 --- a/native/jni/core/magisk.cpp +++ b/native/jni/core/magisk.cpp @@ -39,7 +39,6 @@ Advanced Options (Internal APIs): --sqlite SQL exec SQL commands to Magisk database --path print Magisk tmpfs mount path --denylist ARGS denylist config CLI - --companion FD start zygisk root companion Available applets: )EOF"); @@ -128,8 +127,6 @@ int magisk_main(int argc, char *argv[]) { return 0; } else if (argc >= 3 && argv[1] == "--install-module"sv) { install_module(argv[2]); - } else if (argc >= 3 && argv[1] == "--companion"sv) { - zygiskd(parse_int(argv[2])); } #if 0 /* Entry point for testing stuffs */ diff --git a/native/jni/core/module.cpp b/native/jni/core/module.cpp index 7af22ee1b..0449fe703 100644 --- a/native/jni/core/module.cpp +++ b/native/jni/core/module.cpp @@ -550,18 +550,21 @@ struct module_info { }; static vector *modules; +int app_process_32 = -1; +int app_process_64 = -1; -#define mount_zygisk(bit) \ -if (access("/system/bin/app_process" #bit, F_OK) == 0) { \ - string zbin = zygisk_bin + "/app_process" #bit; \ - string mbin = MAGISKTMP + "/magisk" #bit; \ - int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \ - int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \ - xsendfile(out, src, nullptr, INT_MAX); \ - close(src); \ - close(out); \ - clone_attr("/system/bin/app_process" #bit, zbin.data()); \ - bind_mount(zbin.data(), "/system/bin/app_process" #bit); \ +#define mount_zygisk(bit) \ +if (access("/system/bin/app_process" #bit, F_OK) == 0) { \ + app_process_##bit = xopen("/system/bin/app_process" #bit, O_RDONLY | O_CLOEXEC); \ + string zbin = zygisk_bin + "/app_process" #bit; \ + string mbin = MAGISKTMP + "/magisk" #bit; \ + int src = xopen(mbin.data(), O_RDONLY | O_CLOEXEC); \ + int out = xopen(zbin.data(), O_CREAT | O_WRONLY | O_CLOEXEC, 0); \ + xsendfile(out, src, nullptr, INT_MAX); \ + close(src); \ + close(out); \ + clone_attr("/system/bin/app_process" #bit, zbin.data()); \ + bind_mount(zbin.data(), "/system/bin/app_process" #bit); \ } void magic_mount() { diff --git a/native/jni/include/daemon.hpp b/native/jni/include/daemon.hpp index 1a01cee6b..b1f3b8049 100644 --- a/native/jni/include/daemon.hpp +++ b/native/jni/include/daemon.hpp @@ -30,6 +30,7 @@ enum : int { SQLITE_CMD, REMOVE_MODULES, ZYGISK_REQUEST, + ZYGISK_PASSTHROUGH, DAEMON_CODE_END, }; @@ -42,6 +43,8 @@ enum : int { }; extern bool zygisk_enabled; +extern int app_process_32; +extern int app_process_64; int connect_daemon(bool create = false); diff --git a/native/jni/include/magisk.hpp b/native/jni/include/magisk.hpp index 147ffbe92..c145d38c4 100644 --- a/native/jni/include/magisk.hpp +++ b/native/jni/include/magisk.hpp @@ -43,3 +43,4 @@ int magiskpolicy_main(int argc, char *argv[]); int su_client_main(int argc, char *argv[]); int resetprop_main(int argc, char *argv[]); int app_process_main(int argc, char *argv[]); +int zygisk_main(int argc, char *argv[]); diff --git a/native/jni/su/connect.cpp b/native/jni/su/connect.cpp index 78bec8c6c..3ec8215c6 100644 --- a/native/jni/su/connect.cpp +++ b/native/jni/su/connect.cpp @@ -5,6 +5,7 @@ #include #include "su.hpp" +#include "daemon.hpp" using namespace std; @@ -15,11 +16,11 @@ enum { }; #define CALL_PROVIDER \ -"/system/bin/app_process", "/system/bin", "com.android.commands.content.Content", \ +exe, "/system/bin", "com.android.commands.content.Content", \ "call", "--uri", target, "--user", user, "--method", action #define START_ACTIVITY \ -"/system/bin/app_process", "/system/bin", "com.android.commands.am.Am", \ +exe, "/system/bin", "com.android.commands.am.Am", \ "start", "-p", target, "--user", user, "-a", "android.intent.action.VIEW", \ "-f", "0x18000020", "--es", "action", action @@ -100,9 +101,20 @@ static bool check_no_error(int fd) { static void exec_cmd(const char *action, vector &data, const shared_ptr &info, int mode = CONTENT_PROVIDER) { + char exe[128]; char target[128]; char user[4]; - sprintf(user, "%d", get_user(info)); + snprintf(user, sizeof(user), "%d", get_user(info)); + + if (zygisk_enabled) { +#if defined(__LP64__) + snprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_64); +#else + snprintf(exe, sizeof(exe), "/proc/self/fd/%d", app_process_32); +#endif + } else { + strlcpy(exe, "/system/bin/app_process", sizeof(exe)); + } // First try content provider call method if (mode >= CONTENT_PROVIDER) { diff --git a/native/jni/zygisk/companion.cpp b/native/jni/zygisk/companion.cpp deleted file mode 100644 index 129b9b0e1..000000000 --- a/native/jni/zygisk/companion.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include - -#include -#include -#include -#include - -#include "zygisk.hpp" - -using namespace std; - -void zygiskd(int socket) { - if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) - exit(-1); - android_logging(); - -#if defined(__LP64__) - set_nice_name("zygiskd64"); - LOGI("* Launching zygiskd64\n"); -#else - set_nice_name("zygiskd32"); - LOGI("* Launching zygiskd32\n"); -#endif - - // Load modules - using comp_entry = void(*)(int); - vector modules; - { - vector module_fds = recv_fds(socket); - char buf[256]; - for (int fd : module_fds) { - snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); - comp_entry entry = nullptr; - if (void *h = dlopen(buf, RTLD_LAZY)) { - *(void **) &entry = dlsym(h, "zygisk_companion_entry"); - } - modules.push_back(entry); - } - } - - // ack - write_int(socket, 0); - - // Start accepting requests - pollfd pfd = { socket, POLLIN, 0 }; - for (;;) { - poll(&pfd, 1, -1); - if (!(pfd.revents & POLLIN)) { - // Something bad happened in magiskd, terminate zygiskd - exit(0); - } - int client = recv_fd(socket); - int module_id = read_int(client); - if (module_id < modules.size() && modules[module_id]) { - exec_task([=, entry = modules[module_id]] { - int dup = fcntl(client, F_DUPFD_CLOEXEC); - entry(client); - // Only close client if it is the same as dup so we don't - // accidentally close a re-used file descriptor. - // This check is required because the module companion - // handler could've closed the file descriptor already. - if (struct stat s1; fstat(client, &s1) == 0) { - struct stat s2{}; - fstat(dup, &s2); - if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { - close(client); - } - } - close(dup); - }); - } else { - close(client); - } - } -} - -static int zygiskd_sockets[] = { -1, -1 }; -#define zygiskd_socket zygiskd_sockets[is_64_bit] - -void connect_companion(int client, bool is_64_bit) { - if (zygiskd_socket >= 0) { - // Make sure the socket is still valid - pollfd pfd = { zygiskd_socket, 0, 0 }; - poll(&pfd, 1, 0); - if (pfd.revents) { - // Any revent means error - close(zygiskd_socket); - zygiskd_socket = -1; - } - } - if (zygiskd_socket < 0) { - int fds[2]; - socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); - zygiskd_socket = fds[0]; - if (fork_dont_care() == 0) { - string exe = MAGISKTMP + "/magisk" + (is_64_bit ? "64" : "32"); - // This fd has to survive exec - fcntl(fds[1], F_SETFD, 0); - char buf[16]; - snprintf(buf, sizeof(buf), "%d", fds[1]); - execlp(exe.data(), "magisk", "--companion", buf, (char *) nullptr); - exit(-1); - } - close(fds[1]); - vector module_fds = zygisk_module_fds(is_64_bit); - send_fds(zygiskd_socket, module_fds.data(), module_fds.size()); - // Wait for ack - if (read_int(zygiskd_socket) != 0) { - return; - } - } - send_fd(zygiskd_socket, client); -} diff --git a/native/jni/zygisk/entry.cpp b/native/jni/zygisk/entry.cpp index 51a84641c..0bd455015 100644 --- a/native/jni/zygisk/entry.cpp +++ b/native/jni/zygisk/entry.cpp @@ -1,7 +1,5 @@ #include #include -#include -#include #include #include @@ -145,37 +143,7 @@ static void zygisk_init() { } } -// Start code for magiskd IPC - -int app_process_main(int argc, char *argv[]) { - android_logging(); - - if (int fd = connect_daemon(); fd >= 0) { - write_int(fd, ZYGISK_REQUEST); - write_int(fd, ZYGISK_SETUP); - - if (read_int(fd) == 0) { - string path = read_string(fd); - string lib = path + ".1.so"; - if (char *ld = getenv("LD_PRELOAD")) { - char env[256]; - sprintf(env, "%s:%s", ld, lib.data()); - setenv("LD_PRELOAD", env, 1); - } else { - setenv("LD_PRELOAD", lib.data(), 1); - } - setenv(INJECT_ENV_1, "1", 1); - } - close(fd); - } - - // Execute real app_process - char buf[256]; - xreadlink("/proc/self/exe", buf, sizeof(buf)); - xumount2("/proc/self/exe", MNT_DETACH); - execve(buf, argv, environ); - return 1; -} +// The following code runs in zygote/app process static int zygisk_log(int prio, const char *fmt, va_list ap) { // If we don't have log pipe set, ask magiskd for it @@ -248,6 +216,50 @@ static bool get_exe(int pid, char *buf, size_t sz) { return xreadlink(buf, buf, sz) > 0; } +static int zygiskd_sockets[] = { -1, -1 }; +#define zygiskd_socket zygiskd_sockets[is_64_bit] + +static void connect_companion(int client, bool is_64_bit) { + if (zygiskd_socket >= 0) { + // Make sure the socket is still valid + pollfd pfd = { zygiskd_socket, 0, 0 }; + poll(&pfd, 1, 0); + if (pfd.revents) { + // Any revent means error + close(zygiskd_socket); + zygiskd_socket = -1; + } + } + if (zygiskd_socket < 0) { + int fds[2]; + socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); + zygiskd_socket = fds[0]; + if (fork_dont_care() == 0) { + string exe = MAGISKTMP + "/magisk" + (is_64_bit ? "64" : "32"); + // This fd has to survive exec + fcntl(fds[1], F_SETFD, 0); + char buf[16]; + snprintf(buf, sizeof(buf), "%d", fds[1]); + execl(exe.data(), "zygisk", "companion", buf, (char *) nullptr); + exit(-1); + } + close(fds[1]); + vector module_fds = zygisk_module_fds(is_64_bit); + send_fds(zygiskd_socket, module_fds.data(), module_fds.size()); + // Wait for ack + if (read_int(zygiskd_socket) != 0) { + return; + } + } + send_fd(zygiskd_socket, client); +} + +static timespec last_zygote_start; +static int zygote_start_counts[] = { 0, 0 }; +#define zygote_start_count zygote_start_counts[is_64_bit] +#define zygote_started (zygote_start_counts[0] + zygote_start_counts[1]) +#define zygote_start_reset(val) { zygote_start_counts[0] = val; zygote_start_counts[1] = val; } + static void setup_files(int client, const sock_cred *cred) { LOGD("zygisk: setup files for pid=[%d]\n", cred->pid); @@ -257,15 +269,54 @@ static void setup_files(int client, const sock_cred *cred) { return; } + bool is_64_bit = str_ends(buf, "64"); + + if (!zygote_started) { + // First zygote launch, record time + clock_gettime(CLOCK_MONOTONIC, &last_zygote_start); + } + + if (zygote_start_count) { + // This zygote ABI had started before, kill existing zygiskd + close(zygiskd_sockets[0]); + close(zygiskd_sockets[1]); + zygiskd_sockets[0] = -1; + zygiskd_sockets[1] = -1; + } + ++zygote_start_count; + + if (zygote_start_count >= 5) { + // Bootloop prevention + timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + if (ts.tv_sec - last_zygote_start.tv_sec > 60) { + // This is very likely manual soft reboot + memcpy(&last_zygote_start, &ts, sizeof(ts)); + zygote_start_reset(1); + } else { + // If any zygote relaunched more than 5 times within a minute, + // don't do any setups further to prevent bootloop. + zygote_start_reset(999); + write_int(client, 1); + return; + } + } + write_int(client, 0); + send_fd(client, is_64_bit ? app_process_64 : app_process_32); string path = MAGISKTMP + "/" ZYGISKBIN "/zygisk." + basename(buf); cp_afc(buf, (path + ".1.so").data()); cp_afc(buf, (path + ".2.so").data()); - write_string(client, path); } +static void magiskd_passthrough(int client) { + bool is_64_bit = read_int(client); + write_int(client, 0); + send_fd(client, is_64_bit ? app_process_64 : app_process_32); +} + int cached_manager_app_id = -1; static time_t last_modified = 0; @@ -344,6 +395,9 @@ void zygisk_handler(int client, const sock_cred *cred) { case ZYGISK_SETUP: setup_files(client, cred); break; + case ZYGISK_PASSTHROUGH: + magiskd_passthrough(client); + break; case ZYGISK_GET_INFO: get_process_info(client, cred); break; diff --git a/native/jni/zygisk/hook.cpp b/native/jni/zygisk/hook.cpp index 36a1dae8c..0498e9578 100644 --- a/native/jni/zygisk/hook.cpp +++ b/native/jni/zygisk/hook.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -143,6 +144,16 @@ DCL_HOOK_FUNC(int, fork) { return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork(); } +// Unmount app_process overlays in the process's private mount namespace +DCL_HOOK_FUNC(int, unshare, int flags) { + int res = old_unshare(flags); + if (g_ctx && res == 0) { + umount2("/system/bin/app_process64", MNT_DETACH); + umount2("/system/bin/app_process32", MNT_DETACH); + } + return res; +} + // This is the latest point where we can still connect to the magiskd main socket DCL_HOOK_FUNC(int, selinux_android_setcontext, uid_t uid, int isSystemServer, const char *seinfo, const char *pkgname) { @@ -501,6 +512,7 @@ void hook_functions() { default_new(jni_method_map); XHOOK_REGISTER(ANDROID_RUNTIME, fork); + XHOOK_REGISTER(ANDROID_RUNTIME, unshare); XHOOK_REGISTER(ANDROID_RUNTIME, selinux_android_setcontext); XHOOK_REGISTER(ANDROID_RUNTIME, jniRegisterNativeMethods); XHOOK_REGISTER_SYM(ANDROID_RUNTIME, "__android_log_close", android_log_close); diff --git a/native/jni/zygisk/main.cpp b/native/jni/zygisk/main.cpp new file mode 100644 index 000000000..46864550a --- /dev/null +++ b/native/jni/zygisk/main.cpp @@ -0,0 +1,199 @@ +#include +#include + +#include +#include +#include +#include + +#include "zygisk.hpp" + +using namespace std; + +// Entrypoint for app_process overlay +int app_process_main(int argc, char *argv[]) { + android_logging(); + char buf[256]; + + bool zygote = false; + if (auto fp = open_file("/proc/self/attr/current", "r")) { + fscanf(fp.get(), "%s", buf); + zygote = (buf == "u:r:zygote:s0"sv); + } + + if (!zygote) { + // For the non zygote case, we need to get real app_process via passthrough + // We have to connect magiskd via exec-ing magisk due to SELinux restrictions + + // This is actually only relevant for calling app_process via ADB shell + // because zygisk shall already have the app_process overlays unmounted + // during app process specialization within its private mount namespace. + + int fds[2]; + socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds); + if (fork_dont_care() == 0) { + // This fd has to survive exec + fcntl(fds[1], F_SETFD, 0); + snprintf(buf, sizeof(buf), "%d", fds[1]); +#if defined(__LP64__) + execlp("magisk", "zygisk", "passthrough", buf, "1", (char *) nullptr); +#else + execlp("magisk", "zygisk", "passthrough", buf, "0", (char *) nullptr); +#endif + exit(-1); + } + + close(fds[1]); + if (read_int(fds[0]) != 0) + return 1; + int app_proc_fd = recv_fd(fds[0]); + if (app_proc_fd < 0) + return 1; + close(fds[0]); + + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", app_proc_fd); + fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC); + execve(buf, argv, environ); + return 1; + } + + if (int socket = connect_daemon(); socket >= 0) { + do { + write_int(socket, ZYGISK_REQUEST); + write_int(socket, ZYGISK_SETUP); + + if (read_int(socket) != 0) + break; + + int app_proc_fd = recv_fd(socket); + if (app_proc_fd < 0) + break; + + string path = read_string(socket); + string lib = path + ".1.so"; + if (char *ld = getenv("LD_PRELOAD")) { + char env[256]; + sprintf(env, "%s:%s", ld, lib.data()); + setenv("LD_PRELOAD", env, 1); + } else { + setenv("LD_PRELOAD", lib.data(), 1); + } + setenv(INJECT_ENV_1, "1", 1); + + close(socket); + + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", app_proc_fd); + fcntl(app_proc_fd, F_SETFD, FD_CLOEXEC); + execve(buf, argv, environ); + } while (false); + + close(socket); + } + + // If encountering any errors, unmount and execute the original app_process + xreadlink("/proc/self/exe", buf, sizeof(buf)); + xumount2("/proc/self/exe", MNT_DETACH); + execve(buf, argv, environ); + return 1; +} + +static void zygiskd(int socket) { + if (getuid() != 0 || fcntl(socket, F_GETFD) < 0) + exit(-1); + +#if defined(__LP64__) + set_nice_name("zygiskd64"); + LOGI("* Launching zygiskd64\n"); +#else + set_nice_name("zygiskd32"); + LOGI("* Launching zygiskd32\n"); +#endif + + // Load modules + using comp_entry = void(*)(int); + vector modules; + { + vector module_fds = recv_fds(socket); + char buf[256]; + for (int fd : module_fds) { + snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd); + comp_entry entry = nullptr; + if (void *h = dlopen(buf, RTLD_LAZY)) { + *(void **) &entry = dlsym(h, "zygisk_companion_entry"); + } + modules.push_back(entry); + } + } + + // ack + write_int(socket, 0); + + // Start accepting requests + pollfd pfd = { socket, POLLIN, 0 }; + for (;;) { + poll(&pfd, 1, -1); + if (pfd.revents && !(pfd.revents & POLLIN)) { + // Something bad happened in magiskd, terminate zygiskd + exit(0); + } + int client = recv_fd(socket); + if (client < 0) { + // Something bad happened in magiskd, terminate zygiskd + exit(0); + } + int module_id = read_int(client); + if (module_id >= 0 && module_id < modules.size() && modules[module_id]) { + exec_task([=, entry = modules[module_id]] { + int dup = fcntl(client, F_DUPFD_CLOEXEC); + entry(client); + // Only close client if it is the same as dup so we don't + // accidentally close a re-used file descriptor. + // This check is required because the module companion + // handler could've closed the file descriptor already. + if (struct stat s1; fstat(client, &s1) == 0) { + struct stat s2{}; + fstat(dup, &s2); + if (s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino) { + close(client); + } + } + close(dup); + }); + } else { + close(client); + } + } +} + +// Entrypoint where we need to re-exec ourselves +// This should only ever be called internally +int zygisk_main(int argc, char *argv[]) { + android_logging(); + + if (argc == 3 && argv[1] == "companion"sv) { + zygiskd(parse_int(argv[2])); + } else if (argc == 4 && argv[1] == "passthrough"sv) { + int client = parse_int(argv[2]); + int is_64_bit = parse_int(argv[3]); + if (fcntl(client, F_GETFD) < 0) + return 1; + if (int magiskd = connect_daemon(); magiskd >= 0) { + write_int(magiskd, ZYGISK_PASSTHROUGH); + write_int(magiskd, ZYGISK_PASSTHROUGH); + write_int(magiskd, is_64_bit); + + if (read_int(magiskd) != 0) { + write_int(client, 1); + return 0; + } + + write_int(client, 0); + int real_app_fd = recv_fd(magiskd); + send_fd(client, real_app_fd); + } else { + write_int(client, 1); + return 0; + } + } + return 0; +} diff --git a/native/jni/zygisk/zygisk.hpp b/native/jni/zygisk/zygisk.hpp index 56a4ba3a0..529df903c 100644 --- a/native/jni/zygisk/zygisk.hpp +++ b/native/jni/zygisk/zygisk.hpp @@ -47,4 +47,3 @@ void hook_functions(); bool unhook_functions(); std::vector remote_get_info(int uid, const char *process, AppInfo *info); int remote_request_unmount(); -void connect_companion(int client, bool is_64_bit);