1 /* Copyright (C) 2011 The Android Open Source Project 2 ** 3 ** This software is licensed under the terms of the GNU General Public 4 ** License version 2, as published by the Free Software Foundation, and 5 ** may be copied, distributed, and modified under those terms. 6 ** 7 ** This program is distributed in the hope that it will be useful, 8 ** but WITHOUT ANY WARRANTY; without even the implied warranty of 9 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 ** GNU General Public License for more details. 11 */ 12 13 /* This is the source code to the tiny "emulator" launcher program 14 * that is in charge of starting the target-specific emulator binary 15 * for a given AVD, i.e. either 'emulator-arm' or 'emulator-x86' 16 * 17 * This program will be replaced in the future by what is currently 18 * known as 'emulator-ui', but is a good placeholder until this 19 * migration is completed. 20 */ 21 22 #include <errno.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 #include <android/utils/compiler.h> 28 #include <android/utils/host_bitness.h> 29 #include <android/utils/panic.h> 30 #include <android/utils/path.h> 31 #include <android/utils/bufprint.h> 32 #include <android/utils/win32_cmdline_quote.h> 33 #include <android/avd/util.h> 34 35 /* Required by android/utils/debug.h */ 36 int android_verbose; 37 38 39 #define DEBUG 1 40 41 #if DEBUG 42 # define D(...) do { if (android_verbose) printf("emulator:" __VA_ARGS__); } while (0) 43 #else 44 # define D(...) do{}while(0) 45 #endif 46 47 /* The extension used by dynamic libraries on the host platform */ 48 #ifdef _WIN32 49 # define DLL_EXTENSION ".dll" 50 #elif defined(__APPLE__) 51 # define DLL_EXTENSION ".dylib" 52 #else 53 # define DLL_EXTENSION ".so" 54 #endif 55 56 // Name of GPU emulation main library for (32-bit and 64-bit versions) 57 #define GLES_EMULATION_LIB "libOpenglRender" DLL_EXTENSION 58 #define GLES_EMULATION_LIB64 "lib64OpenglRender" DLL_EXTENSION 59 60 /* Forward declarations */ 61 static char* getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit); 62 static char* getSharedLibraryPath(const char* progName, const char* libName); 63 static void prependSharedLibraryPath(const char* prefix); 64 65 /* The execv() definition in older mingw is slightly bogus. 66 * It takes a second argument of type 'const char* const*' 67 * while POSIX mandates char** instead. 68 * 69 * To avoid compiler warnings, define the safe_execv macro 70 * to perform an explicit cast with mingw. 71 */ 72 #if defined(_WIN32) && !ANDROID_GCC_PREREQ(4,4) 73 # define safe_execv(_filepath,_argv) execv((_filepath),(const char* const*)(_argv)) 74 #else 75 # define safe_execv(_filepath,_argv) execv((_filepath),(_argv)) 76 #endif 77 78 /* Main routine */ 79 int main(int argc, char** argv) 80 { 81 const char* avdName = NULL; 82 char* avdArch = NULL; 83 char* emulatorPath; 84 int force_32bit = 0; 85 86 /* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to 87 * see the debug messages from this launcher program. 88 */ 89 const char* debug = getenv("ANDROID_EMULATOR_DEBUG"); 90 91 if (debug != NULL && *debug && *debug != '0') 92 android_verbose = 1; 93 94 /* Parse command-line and look for 95 * 1) an avd name either in the form or '-avd <name>' or '@<name>' 96 * 2) '-force-32bit' which always use 32-bit emulator on 64-bit platforms 97 * 3) '-verbose', or '-debug-all' or '-debug all' to enable verbose mode. 98 */ 99 int nn; 100 for (nn = 1; nn < argc; nn++) { 101 const char* opt = argv[nn]; 102 103 if (!strcmp(opt,"-qemu")) 104 break; 105 106 if (!strcmp(opt,"-verbose") || !strcmp(opt,"-debug-all")) { 107 android_verbose = 1; 108 } 109 110 if (!strcmp(opt,"-debug") && nn + 1 < argc && 111 !strcmp(argv[nn + 1], "all")) { 112 android_verbose = 1; 113 } 114 115 if (!strcmp(opt,"-force-32bit")) { 116 force_32bit = 1; 117 continue; 118 } 119 120 if (!avdName) { 121 if (!strcmp(opt,"-avd") && nn+1 < argc) { 122 avdName = argv[nn+1]; 123 } 124 else if (opt[0] == '@' && opt[1] != '\0') { 125 avdName = opt+1; 126 } 127 } 128 } 129 130 /* If there is an AVD name, we're going to extract its target architecture 131 * by looking at its config.ini 132 */ 133 if (avdName != NULL) { 134 D("Found AVD name '%s'\n", avdName); 135 avdArch = path_getAvdTargetArch(avdName); 136 D("Found AVD target architecture: %s\n", avdArch); 137 } else { 138 /* Otherwise, using the ANDROID_PRODUCT_OUT directory */ 139 const char* androidOut = getenv("ANDROID_PRODUCT_OUT"); 140 141 if (androidOut != NULL) { 142 D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut); 143 avdArch = path_getBuildTargetArch(androidOut); 144 D("Found build target architecture: %s\n", 145 avdArch ? avdArch : "<NULL>"); 146 } 147 } 148 149 if (avdArch == NULL) { 150 avdArch = "arm"; 151 D("Can't determine target AVD architecture: defaulting to %s\n", avdArch); 152 } 153 154 /* Find the architecture-specific program in the same directory */ 155 emulatorPath = getTargetEmulatorPath(argv[0], avdArch, force_32bit); 156 D("Found target-specific emulator binary: %s\n", emulatorPath); 157 158 /* Replace it in our command-line */ 159 argv[0] = emulatorPath; 160 161 /* We need to find the location of the GLES emulation shared libraries 162 * and modify either LD_LIBRARY_PATH or PATH accordingly 163 */ 164 { 165 char* sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB); 166 167 if (!sharedLibPath) { 168 // Sometimes, only the 64-bit libraries are available, for example 169 // when storing binaries under $AOSP/prebuilts/android-emulator/<system>/ 170 sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB64); 171 } 172 173 if (sharedLibPath != NULL) { 174 D("Found OpenGLES emulation libraries in %s\n", sharedLibPath); 175 prependSharedLibraryPath(sharedLibPath); 176 } else { 177 D("Could not find OpenGLES emulation host libraries!\n"); 178 } 179 } 180 181 #ifdef _WIN32 182 // Take care of quoting all parameters before sending them to execv(). 183 // See the "Eveyone quotes command line arguments the wrong way" on 184 // MSDN. 185 int n; 186 for (n = 0; n < argc; ++n) { 187 // Technically, this leaks the quoted strings, but we don't care 188 // since this process will terminate after the execv() anyway. 189 argv[n] = win32_cmdline_quote(argv[n]); 190 D("Quoted param: [%s]\n", argv[n]); 191 } 192 #endif 193 194 // Launch it with the same set of options ! 195 // Note that on Windows, the first argument must _not_ be quoted or 196 // Windows will fail to find the program. 197 safe_execv(emulatorPath, argv); 198 199 /* We could not launch the program ! */ 200 fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno)); 201 return errno; 202 } 203 204 /* Probe the filesystem to check if an emulator executable named like 205 * <progDir>/<prefix><arch> exists. 206 * 207 * |progDir| is an optional program directory. If NULL, the executable 208 * will be searched in the current directory. 209 * |archSuffix| is an architecture-specific suffix, like "arm", or 'x86" 210 * If |search_for_64bit_emulator| is true, lookup for 64-bit emulator first, 211 * then the 32-bit version. 212 * If |try_current_path|, try to look into the current path if no 213 * executable was found under |progDir|. 214 * On success, returns the path of the executable (string must be freed by 215 * the caller). On failure, return NULL. 216 */ 217 static char* 218 probeTargetEmulatorPath(const char* progDir, 219 const char* archSuffix, 220 bool search_for_64bit_emulator, 221 bool try_current_path) 222 { 223 char path[PATH_MAX], *pathEnd = path + sizeof(path), *p; 224 225 static const char kEmulatorPrefix[] = "emulator-"; 226 static const char kEmulator64Prefix[] = "emulator64-"; 227 #ifdef _WIN32 228 const char kExeExtension[] = ".exe"; 229 #else 230 const char kExeExtension[] = ""; 231 #endif 232 233 // First search for the 64-bit emulator binary. 234 if (search_for_64bit_emulator) { 235 p = path; 236 if (progDir) { 237 p = bufprint(p, pathEnd, "%s/", progDir); 238 } 239 p = bufprint(p, pathEnd, "%s%s%s", kEmulator64Prefix, 240 archSuffix, kExeExtension); 241 D("Probing program: %s\n", path); 242 if (p < pathEnd && path_exists(path)) { 243 return strdup(path); 244 } 245 } 246 247 // Then for the 32-bit one. 248 p = path; 249 if (progDir) { 250 p = bufprint(p, pathEnd, "%s/", progDir); 251 } 252 p = bufprint(p, pathEnd, "%s%s%s", kEmulatorPrefix, 253 archSuffix, kExeExtension); 254 D("Probing program: %s\n", path); 255 if (p < pathEnd && path_exists(path)) { 256 return strdup(path); 257 } 258 259 // Not found, try in the current path then 260 if (try_current_path) { 261 char* result; 262 263 if (search_for_64bit_emulator) { 264 p = bufprint(path, pathEnd, "%s%s%s", kEmulator64Prefix, 265 archSuffix, kExeExtension); 266 if (p < pathEnd) { 267 D("Probing path for: %s\n", path); 268 result = path_search_exec(path); 269 if (result) { 270 return result; 271 } 272 } 273 } 274 275 p = bufprint(path, pathEnd, "%s%s%s", kEmulatorPrefix, 276 archSuffix, kExeExtension); 277 if (p < pathEnd) { 278 D("Probing path for: %s\n", path); 279 result = path_search_exec(path); 280 if (result) { 281 return result; 282 } 283 } 284 } 285 286 return NULL; 287 } 288 289 static char* 290 getTargetEmulatorPath(const char* progName, 291 const char* avdArch, 292 const int force_32bit) 293 { 294 char* progDir; 295 char* result; 296 #ifdef _WIN32 297 /* TODO: currently amd64-mingw32msvc-gcc doesn't work which prevents 298 generating 64-bit binaries for Windows */ 299 bool search_for_64bit_emulator = false; 300 #else 301 bool search_for_64bit_emulator = 302 !force_32bit && android_getHostBitness() == 64; 303 #endif 304 305 /* Only search in current path if there is no directory separator 306 * in |progName|. */ 307 #ifdef _WIN32 308 bool try_current_path = 309 (!strchr(progName, '/') && !strchr(progName, '\\')); 310 #else 311 bool try_current_path = !strchr(progName, '/'); 312 #endif 313 314 /* Get program's directory name in progDir */ 315 path_split(progName, &progDir, NULL); 316 317 const char* emulatorSuffix; 318 319 // Special case: for x86_64, first try to find emulator-x86_64 before 320 // looking for emulator-x86. 321 if (!strcmp(avdArch, "x86_64")) { 322 emulatorSuffix = "x86_64"; 323 324 D("Looking for emulator backend for %s CPU\n", avdArch); 325 326 result = probeTargetEmulatorPath(progDir, 327 emulatorSuffix, 328 search_for_64bit_emulator, 329 try_current_path); 330 if (result) { 331 return result; 332 } 333 } 334 335 // Special case: for arm64, first try to find emulator-arm64 before 336 // looking for emulator-arm. 337 if (!strcmp(avdArch, "arm64")) { 338 emulatorSuffix = "arm64"; 339 340 D("Looking for emulator backend for %s CPU\n", avdArch); 341 342 result = probeTargetEmulatorPath(progDir, 343 emulatorSuffix, 344 search_for_64bit_emulator, 345 try_current_path); 346 if (result) { 347 return result; 348 } 349 } 350 351 // Now for the regular case. 352 emulatorSuffix = emulator_getBackendSuffix(avdArch); 353 if (!emulatorSuffix) { 354 APANIC("This emulator cannot emulate %s CPUs!\n", avdArch); 355 } 356 D("Looking for emulator-%s to emulate '%s' CPU\n", emulatorSuffix, 357 avdArch); 358 359 result = probeTargetEmulatorPath(progDir, 360 emulatorSuffix, 361 search_for_64bit_emulator, 362 try_current_path); 363 if (result) { 364 return result; 365 } 366 367 /* Otherwise, the program is missing */ 368 APANIC("Missing emulator engine program for '%s' CPUS.\n", avdArch); 369 return NULL; 370 } 371 372 /* return 1 iff <path>/<filename> exists */ 373 static int 374 probePathForFile(const char* path, const char* filename) 375 { 376 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 377 p = bufprint(temp, end, "%s/%s", path, filename); 378 D("Probing for: %s\n", temp); 379 return (p < end && path_exists(temp)); 380 } 381 382 /* Find the directory containing a given shared library required by the 383 * emulator (for GLES emulation). We will probe several directories 384 * that correspond to various use-cases. 385 * 386 * Caller must free() result string. NULL if not found. 387 */ 388 389 static char* 390 getSharedLibraryPath(const char* progName, const char* libName) 391 { 392 char* progDir; 393 char* result = NULL; 394 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 395 396 /* Get program's directory name */ 397 path_split(progName, &progDir, NULL); 398 399 /* First, try to probe the program's directory itself, this corresponds 400 * to the standalone build with ./android-configure.sh where the script 401 * will copy the host shared library under external/qemu/objs where 402 * the binaries are located. 403 */ 404 if (probePathForFile(progDir, libName)) { 405 return progDir; 406 } 407 408 /* Try under $progDir/lib/, this should correspond to the SDK installation 409 * where the binary is under tools/, and the libraries under tools/lib/ 410 */ 411 { 412 p = bufprint(temp, end, "%s/lib", progDir); 413 if (p < end && probePathForFile(temp, libName)) { 414 result = strdup(temp); 415 goto EXIT; 416 } 417 } 418 419 /* try in $progDir/../lib, this corresponds to the platform build 420 * where the emulator binary is under out/host/<system>/bin and 421 * the libraries are under out/host/<system>/lib 422 */ 423 { 424 char* parentDir = path_parent(progDir, 1); 425 426 if (parentDir == NULL) { 427 parentDir = strdup("."); 428 } 429 p = bufprint(temp, end, "%s/lib", parentDir); 430 free(parentDir); 431 if (p < end && probePathForFile(temp, libName)) { 432 result = strdup(temp); 433 goto EXIT; 434 } 435 } 436 437 /* Nothing found! */ 438 EXIT: 439 free(progDir); 440 return result; 441 } 442 443 /* Prepend the path in 'prefix' to either LD_LIBRARY_PATH or PATH to 444 * ensure that the shared libraries inside the path will be available 445 * through dlopen() to the emulator program being launched. 446 */ 447 static void 448 prependSharedLibraryPath(const char* prefix) 449 { 450 size_t len = 0; 451 char *temp = NULL; 452 const char* path = NULL; 453 454 #ifdef _WIN32 455 path = getenv("PATH"); 456 #else 457 path = getenv("LD_LIBRARY_PATH"); 458 #endif 459 460 /* Will need up to 7 extra characters: "PATH=", ';' or ':', and '\0' */ 461 len = 7 + strlen(prefix) + (path ? strlen(path) : 0); 462 temp = malloc(len); 463 if (!temp) 464 return; 465 466 if (path && path[0] != '\0') { 467 #ifdef _WIN32 468 bufprint(temp, temp + len, "PATH=%s;%s", prefix, path); 469 #else 470 bufprint(temp, temp + len, "%s:%s", prefix, path); 471 #endif 472 } else { 473 #ifdef _WIN32 474 bufprint(temp, temp + len, "PATH=%s", prefix); 475 #else 476 strcpy(temp, prefix); 477 #endif 478 } 479 480 #ifdef _WIN32 481 D("Setting %s\n", temp); 482 putenv(strdup(temp)); 483 #else 484 D("Setting LD_LIBRARY_PATH=%s\n", temp); 485 setenv("LD_LIBRARY_PATH", temp, 1); 486 #endif 487 } 488