| /* Copyright (C) 2011 The Android Open Source Project |
| ** |
| ** This software is licensed under the terms of the GNU General Public |
| ** License version 2, as published by the Free Software Foundation, and |
| ** may be copied, distributed, and modified under those terms. |
| ** |
| ** This program is distributed in the hope that it will be useful, |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| ** GNU General Public License for more details. |
| */ |
| |
| /* This is the source code to the tiny "emulator" launcher program |
| * that is in charge of starting the target-specific emulator binary |
| * for a given AVD, i.e. either 'emulator-arm' or 'emulator-x86' |
| * |
| * This program will be replaced in the future by what is currently |
| * known as 'emulator-ui', but is a good placeholder until this |
| * migration is completed. |
| */ |
| |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <android/utils/compiler.h> |
| #include <android/utils/panic.h> |
| #include <android/utils/path.h> |
| #include <android/utils/bufprint.h> |
| #include <android/utils/win32_cmdline_quote.h> |
| #include <android/avd/util.h> |
| |
| /* Required by android/utils/debug.h */ |
| int android_verbose; |
| |
| |
| #define DEBUG 1 |
| |
| #if DEBUG |
| # define D(...) do { if (android_verbose) printf("emulator:" __VA_ARGS__); } while (0) |
| #else |
| # define D(...) do{}while(0) |
| #endif |
| |
| /* The extension used by dynamic libraries on the host platform */ |
| #ifdef _WIN32 |
| # define DLL_EXTENSION ".dll" |
| #elif defined(__APPLE__) |
| # define DLL_EXTENSION ".dylib" |
| #else |
| # define DLL_EXTENSION ".so" |
| #endif |
| |
| // Name of GPU emulation main library for (32-bit and 64-bit versions) |
| #define GLES_EMULATION_LIB "libOpenglRender" DLL_EXTENSION |
| #define GLES_EMULATION_LIB64 "lib64OpenglRender" DLL_EXTENSION |
| |
| /* Forward declarations */ |
| static char* getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit); |
| static char* getSharedLibraryPath(const char* progName, const char* libName); |
| static void prependSharedLibraryPath(const char* prefix); |
| |
| /* The execv() definition in older mingw is slightly bogus. |
| * It takes a second argument of type 'const char* const*' |
| * while POSIX mandates char** instead. |
| * |
| * To avoid compiler warnings, define the safe_execv macro |
| * to perform an explicit cast with mingw. |
| */ |
| #if defined(_WIN32) && !ANDROID_GCC_PREREQ(4,4) |
| # define safe_execv(_filepath,_argv) execv((_filepath),(const char* const*)(_argv)) |
| #else |
| # define safe_execv(_filepath,_argv) execv((_filepath),(_argv)) |
| #endif |
| |
| /* Main routine */ |
| int main(int argc, char** argv) |
| { |
| const char* avdName = NULL; |
| char* avdArch = NULL; |
| char* emulatorPath; |
| int force_32bit = 0; |
| |
| /* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to |
| * see the debug messages from this launcher program. |
| */ |
| const char* debug = getenv("ANDROID_EMULATOR_DEBUG"); |
| |
| if (debug != NULL && *debug && *debug != '0') |
| android_verbose = 1; |
| |
| /* Parse command-line and look for |
| * 1) an avd name either in the form or '-avd <name>' or '@<name>' |
| * 2) '-force-32bit' which always use 32-bit emulator on 64-bit platforms |
| * 3) '-verbose', or '-debug-all' or '-debug all' to enable verbose mode. |
| */ |
| int nn; |
| for (nn = 1; nn < argc; nn++) { |
| const char* opt = argv[nn]; |
| |
| if (!strcmp(opt,"-qemu")) |
| break; |
| |
| if (!strcmp(opt,"-verbose") || !strcmp(opt,"-debug-all")) { |
| android_verbose = 1; |
| } |
| |
| if (!strcmp(opt,"-debug") && nn + 1 < argc && |
| !strcmp(argv[nn + 1], "all")) { |
| android_verbose = 1; |
| } |
| |
| if (!strcmp(opt,"-force-32bit")) { |
| force_32bit = 1; |
| continue; |
| } |
| |
| if (!avdName) { |
| if (!strcmp(opt,"-avd") && nn+1 < argc) { |
| avdName = argv[nn+1]; |
| } |
| else if (opt[0] == '@' && opt[1] != '\0') { |
| avdName = opt+1; |
| } |
| } |
| } |
| |
| /* If there is an AVD name, we're going to extract its target architecture |
| * by looking at its config.ini |
| */ |
| if (avdName != NULL) { |
| D("Found AVD name '%s'\n", avdName); |
| avdArch = path_getAvdTargetArch(avdName); |
| D("Found AVD target architecture: %s\n", avdArch); |
| } else { |
| /* Otherwise, using the ANDROID_PRODUCT_OUT directory */ |
| const char* androidOut = getenv("ANDROID_PRODUCT_OUT"); |
| |
| if (androidOut != NULL) { |
| D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut); |
| avdArch = path_getBuildTargetArch(androidOut); |
| D("Found build target architecture: %s\n", |
| avdArch ? avdArch : "<NULL>"); |
| } |
| } |
| |
| if (avdArch == NULL) { |
| avdArch = "arm"; |
| D("Can't determine target AVD architecture: defaulting to %s\n", avdArch); |
| } |
| |
| /* Find the architecture-specific program in the same directory */ |
| emulatorPath = getTargetEmulatorPath(argv[0], avdArch, force_32bit); |
| D("Found target-specific emulator binary: %s\n", emulatorPath); |
| |
| /* Replace it in our command-line */ |
| argv[0] = emulatorPath; |
| |
| /* We need to find the location of the GLES emulation shared libraries |
| * and modify either LD_LIBRARY_PATH or PATH accordingly |
| */ |
| { |
| char* sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB); |
| |
| if (!sharedLibPath) { |
| // Sometimes, only the 64-bit libraries are available, for example |
| // when storing binaries under $AOSP/prebuilts/android-emulator/<system>/ |
| sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB64); |
| } |
| |
| if (sharedLibPath != NULL) { |
| D("Found OpenGLES emulation libraries in %s\n", sharedLibPath); |
| prependSharedLibraryPath(sharedLibPath); |
| } else { |
| D("Could not find OpenGLES emulation host libraries!\n"); |
| } |
| } |
| |
| #ifdef _WIN32 |
| // Take care of quoting all parameters before sending them to execv(). |
| // See the "Eveyone quotes command line arguments the wrong way" on |
| // MSDN. |
| int n; |
| for (n = 0; n < argc; ++n) { |
| // Technically, this leaks the quoted strings, but we don't care |
| // since this process will terminate after the execv() anyway. |
| argv[n] = win32_cmdline_quote(argv[n]); |
| D("Quoted param: [%s]\n", argv[n]); |
| } |
| #endif |
| |
| // Launch it with the same set of options ! |
| // Note that on Windows, the first argument must _not_ be quoted or |
| // Windows will fail to find the program. |
| safe_execv(emulatorPath, argv); |
| |
| /* We could not launch the program ! */ |
| fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno)); |
| return errno; |
| } |
| |
| #ifndef _WIN32 |
| static int |
| getHostOSBitness() |
| { |
| /* |
| This function returns 64 if host is running 64-bit OS, or 32 otherwise. |
| |
| It uses the same technique in ndk/build/core/ndk-common.sh. |
| Here are comments from there: |
| |
| ## On Linux or Darwin, a 64-bit kernel (*) doesn't mean that the user-land |
| ## is always 32-bit, so use "file" to determine the bitness of the shell |
| ## that invoked us. The -L option is used to de-reference symlinks. |
| ## |
| ## Note that on Darwin, a single executable can contain both x86 and |
| ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux) |
| ## in the output. |
| |
| (*) ie. The following code doesn't always work: |
| struct utsname u; |
| int host_runs_64bit_OS = (uname(&u) == 0 && strcmp(u.machine, "x86_64") == 0); |
| */ |
| return system("file -L \"$SHELL\" | grep -q \"x86[_-]64\"") == 0 ? 64 : 32; |
| } |
| #endif // !_WIN32 |
| |
| /* Find the target-specific emulator binary. This will be something |
| * like <programDir>/emulator-<targetArch>, where <programDir> is |
| * the directory of the current program. |
| */ |
| static char* |
| getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit) |
| { |
| char* progDir; |
| char path[PATH_MAX], *pathEnd=path+sizeof(path), *p; |
| const char* emulatorPrefix = "emulator-"; |
| const char* emulator64Prefix = "emulator64-"; |
| #ifdef _WIN32 |
| const char* exeExt = ".exe"; |
| /* ToDo: currently amd64-mingw32msvc-gcc doesn't work (http://b/issue?id=5949152) |
| which prevents us from generating 64-bit emulator for Windows */ |
| int search_for_64bit_emulator = 0; |
| #else |
| const char* exeExt = ""; |
| int search_for_64bit_emulator = !force_32bit && getHostOSBitness() == 64; |
| #endif |
| |
| const char* emulatorSuffix = emulator_getBackendSuffix(avdArch); |
| if (!emulatorSuffix) { |
| APANIC("This emulator cannot emulate %s CPUs!\n", avdArch); |
| } |
| D("Using emulator-%s to emulate '%s' CPUs\n", emulatorSuffix, avdArch); |
| |
| /* Get program's directory name in progDir */ |
| path_split(progName, &progDir, NULL); |
| |
| if (search_for_64bit_emulator) { |
| /* Find 64-bit emulator first */ |
| p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, |
| emulator64Prefix, emulatorSuffix, exeExt); |
| if (p >= pathEnd) { |
| APANIC("Path too long: %s\n", progName); |
| } |
| if (path_exists(path)) { |
| free(progDir); |
| return strdup(path); |
| } |
| } |
| |
| /* Find 32-bit emulator */ |
| p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, emulatorPrefix, |
| emulatorSuffix, exeExt); |
| free(progDir); |
| if (p >= pathEnd) { |
| APANIC("Path too long: %s\n", progName); |
| } |
| |
| if (path_exists(path)) { |
| return strdup(path); |
| } |
| |
| /* Mmm, the file doesn't exist, If there is no slash / backslash |
| * in our path, we're going to try to search it in our path. |
| */ |
| #ifdef _WIN32 |
| if (strchr(progName, '/') == NULL && strchr(progName, '\\') == NULL) { |
| #else |
| if (strchr(progName, '/') == NULL) { |
| #endif |
| if (search_for_64bit_emulator) { |
| p = bufprint(path, pathEnd, "%s%s%s", emulator64Prefix, |
| emulatorSuffix, exeExt); |
| if (p < pathEnd) { |
| char* resolved = path_search_exec(path); |
| if (resolved != NULL) |
| return resolved; |
| } |
| } |
| |
| p = bufprint(path, pathEnd, "%s%s%s", emulatorPrefix, |
| emulatorSuffix, exeExt); |
| if (p < pathEnd) { |
| char* resolved = path_search_exec(path); |
| if (resolved != NULL) |
| return resolved; |
| } |
| } |
| |
| /* Otherwise, the program is missing */ |
| APANIC("Missing arch-specific emulator program: %s\n", path); |
| return NULL; |
| } |
| |
| /* return 1 iff <path>/<filename> exists */ |
| static int |
| probePathForFile(const char* path, const char* filename) |
| { |
| char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); |
| p = bufprint(temp, end, "%s/%s", path, filename); |
| D("Probing for: %s\n", temp); |
| return (p < end && path_exists(temp)); |
| } |
| |
| /* Find the directory containing a given shared library required by the |
| * emulator (for GLES emulation). We will probe several directories |
| * that correspond to various use-cases. |
| * |
| * Caller must free() result string. NULL if not found. |
| */ |
| |
| static char* |
| getSharedLibraryPath(const char* progName, const char* libName) |
| { |
| char* progDir; |
| char* result = NULL; |
| char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); |
| |
| /* Get program's directory name */ |
| path_split(progName, &progDir, NULL); |
| |
| /* First, try to probe the program's directory itself, this corresponds |
| * to the standalone build with ./android-configure.sh where the script |
| * will copy the host shared library under external/qemu/objs where |
| * the binaries are located. |
| */ |
| if (probePathForFile(progDir, libName)) { |
| return progDir; |
| } |
| |
| /* Try under $progDir/lib/, this should correspond to the SDK installation |
| * where the binary is under tools/, and the libraries under tools/lib/ |
| */ |
| { |
| p = bufprint(temp, end, "%s/lib", progDir); |
| if (p < end && probePathForFile(temp, libName)) { |
| result = strdup(temp); |
| goto EXIT; |
| } |
| } |
| |
| /* try in $progDir/../lib, this corresponds to the platform build |
| * where the emulator binary is under out/host/<system>/bin and |
| * the libraries are under out/host/<system>/lib |
| */ |
| { |
| char* parentDir = path_parent(progDir, 1); |
| |
| if (parentDir == NULL) { |
| parentDir = strdup("."); |
| } |
| p = bufprint(temp, end, "%s/lib", parentDir); |
| free(parentDir); |
| if (p < end && probePathForFile(temp, libName)) { |
| result = strdup(temp); |
| goto EXIT; |
| } |
| } |
| |
| /* Nothing found! */ |
| EXIT: |
| free(progDir); |
| return result; |
| } |
| |
| /* Prepend the path in 'prefix' to either LD_LIBRARY_PATH or PATH to |
| * ensure that the shared libraries inside the path will be available |
| * through dlopen() to the emulator program being launched. |
| */ |
| static void |
| prependSharedLibraryPath(const char* prefix) |
| { |
| size_t len = 0; |
| char *temp = NULL; |
| const char* path = NULL; |
| |
| #ifdef _WIN32 |
| path = getenv("PATH"); |
| #else |
| path = getenv("LD_LIBRARY_PATH"); |
| #endif |
| |
| /* Will need up to 7 extra characters: "PATH=", ';' or ':', and '\0' */ |
| len = 7 + strlen(prefix) + (path ? strlen(path) : 0); |
| temp = malloc(len); |
| if (!temp) |
| return; |
| |
| if (path && path[0] != '\0') { |
| #ifdef _WIN32 |
| bufprint(temp, temp + len, "PATH=%s;%s", prefix, path); |
| #else |
| bufprint(temp, temp + len, "%s:%s", prefix, path); |
| #endif |
| } else { |
| #ifdef _WIN32 |
| bufprint(temp, temp + len, "PATH=%s", prefix); |
| #else |
| strcpy(temp, prefix); |
| #endif |
| } |
| |
| #ifdef _WIN32 |
| D("Setting %s\n", temp); |
| putenv(strdup(temp)); |
| #else |
| D("Setting LD_LIBRARY_PATH=%s\n", temp); |
| setenv("LD_LIBRARY_PATH", temp, 1); |
| #endif |
| } |