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/panic.h> 28 #include <android/utils/path.h> 29 #include <android/utils/bufprint.h> 30 #include <android/avd/util.h> 31 32 /* Required by android/utils/debug.h */ 33 int android_verbose; 34 35 36 #define DEBUG 1 37 38 #if DEBUG 39 # define D(...) do { if (android_verbose) printf("emulator:" __VA_ARGS__); } while (0) 40 #else 41 # define D(...) do{}while(0) 42 #endif 43 44 /* The extension used by dynamic libraries on the host platform */ 45 #ifdef _WIN32 46 # define DLL_EXTENSION ".dll" 47 #elif defined(__APPLE__) 48 # define DLL_EXTENSION ".dylib" 49 #else 50 # define DLL_EXTENSION ".so" 51 #endif 52 53 #if defined(__x86_64__) 54 /* Normally emulator is compiled in 32-bit. In standalone it can be compiled 55 in 64-bit (with ,/android-configure.sh --try-64). In this case, emulator-$ARCH 56 are also compiled in 64-bit and will search for lib64*.so instead of lib*so */ 57 #define GLES_EMULATION_LIB "lib64OpenglRender" DLL_EXTENSION 58 #elif defined(__i386__) 59 #define GLES_EMULATION_LIB "libOpenglRender" DLL_EXTENSION 60 #else 61 #error Unknown architecture for codegen 62 #endif 63 64 65 /* Forward declarations */ 66 static char* getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit); 67 static char* getSharedLibraryPath(const char* progName, const char* libName); 68 static void prependSharedLibraryPath(const char* prefix); 69 70 #ifdef _WIN32 71 static char* quotePath(const char* path); 72 #endif 73 74 /* The execv() definition in mingw is slightly bogus. 75 * It takes a second argument of type 'const char* const*' 76 * while POSIX mandates char** instead. 77 * 78 * To avoid compiler warnings, define the safe_execv macro 79 * to perform an explicit cast with mingw. 80 */ 81 #ifdef _WIN32 82 # define safe_execv(_filepath,_argv) execv((_filepath),(const char* const*)(_argv)) 83 #else 84 # define safe_execv(_filepath,_argv) execv((_filepath),(_argv)) 85 #endif 86 87 /* Main routine */ 88 int main(int argc, char** argv) 89 { 90 const char* avdName = NULL; 91 char* avdArch = NULL; 92 char* emulatorPath; 93 int force_32bit = 0; 94 95 /* Define ANDROID_EMULATOR_DEBUG to 1 in your environment if you want to 96 * see the debug messages from this launcher program. 97 */ 98 const char* debug = getenv("ANDROID_EMULATOR_DEBUG"); 99 100 if (debug != NULL && *debug && *debug != '0') 101 android_verbose = 1; 102 103 /* Parse command-line and look for 104 * 1) an avd name either in the form or '-avd <name>' or '@<name>' 105 * 2) '-force-32bit' which always use 32-bit emulator on 64-bit platforms 106 */ 107 int nn; 108 for (nn = 1; nn < argc; nn++) { 109 const char* opt = argv[nn]; 110 111 if (!strcmp(opt,"-qemu")) 112 break; 113 114 if (!strcmp(opt,"-force-32bit")) { 115 force_32bit = 1; 116 continue; 117 } 118 119 if (!avdName) { 120 if (!strcmp(opt,"-avd") && nn+1 < argc) { 121 avdName = argv[nn+1]; 122 } 123 else if (opt[0] == '@' && opt[1] != '\0') { 124 avdName = opt+1; 125 } 126 } 127 } 128 129 /* If there is an AVD name, we're going to extract its target architecture 130 * by looking at its config.ini 131 */ 132 if (avdName != NULL) { 133 D("Found AVD name '%s'\n", avdName); 134 avdArch = path_getAvdTargetArch(avdName); 135 D("Found AVD target architecture: %s\n", avdArch); 136 } else { 137 /* Otherwise, using the ANDROID_PRODUCT_OUT directory */ 138 const char* androidOut = getenv("ANDROID_PRODUCT_OUT"); 139 140 if (androidOut != NULL && *androidOut != '\0') { 141 D("Found ANDROID_PRODUCT_OUT: %s\n", androidOut); 142 avdArch = path_getBuildTargetArch(androidOut); 143 D("Found build target architecture: %s\n", avdArch); 144 } 145 } 146 147 if (avdArch == NULL) { 148 avdArch = "arm"; 149 D("Can't determine target AVD architecture: defaulting to %s\n", avdArch); 150 } 151 152 /* Find the architecture-specific program in the same directory */ 153 emulatorPath = getTargetEmulatorPath(argv[0], avdArch, force_32bit); 154 D("Found target-specific emulator binary: %s\n", emulatorPath); 155 156 /* Replace it in our command-line */ 157 argv[0] = emulatorPath; 158 159 #ifdef _WIN32 160 /* Looks like execv() in mingw (or is it MSVCRT.DLL?) doesn't 161 * support a space in argv[0] unless we explicitely quote it. 162 * IMPORTANT: do not quote the first argument to execv() or it will fail. 163 * This was tested on a 32-bit Vista installation. 164 */ 165 if (strchr(emulatorPath, ' ')) { 166 argv[0] = quotePath(emulatorPath); 167 D("Quoted emulator binary path: %s\n", emulatorPath); 168 } 169 #endif 170 171 /* We need to find the location of the GLES emulation shared libraries 172 * and modify either LD_LIBRARY_PATH or PATH accordingly 173 */ 174 { 175 char* sharedLibPath = getSharedLibraryPath(emulatorPath, GLES_EMULATION_LIB); 176 177 if (sharedLibPath != NULL) { 178 D("Found OpenGLES emulation libraries in %s\n", sharedLibPath); 179 prependSharedLibraryPath(sharedLibPath); 180 } else { 181 D("Could not find OpenGLES emulation host libraries!\n"); 182 } 183 } 184 185 /* Launch it with the same set of options ! */ 186 safe_execv(emulatorPath, argv); 187 188 /* We could not launch the program ! */ 189 fprintf(stderr, "Could not launch '%s': %s\n", emulatorPath, strerror(errno)); 190 return errno; 191 } 192 193 static int 194 getHostOSBitness() 195 { 196 /* 197 This function returns 64 if host is running 64-bit OS, or 32 otherwise. 198 199 It uses the same technique in ndk/build/core/ndk-common.sh. 200 Here are comments from there: 201 202 ## On Linux or Darwin, a 64-bit kernel (*) doesn't mean that the user-land 203 ## is always 32-bit, so use "file" to determine the bitness of the shell 204 ## that invoked us. The -L option is used to de-reference symlinks. 205 ## 206 ## Note that on Darwin, a single executable can contain both x86 and 207 ## x86_64 machine code, so just look for x86_64 (darwin) or x86-64 (Linux) 208 ## in the output. 209 210 (*) ie. The following code doesn't always work: 211 struct utsname u; 212 int host_runs_64bit_OS = (uname(&u) == 0 && strcmp(u.machine, "x86_64") == 0); 213 */ 214 return system("file -L \"$SHELL\" | grep -q \"x86[_-]64\"") == 0 ? 64 : 32; 215 } 216 217 /* Find the target-specific emulator binary. This will be something 218 * like <programDir>/emulator-<targetArch>, where <programDir> is 219 * the directory of the current program. 220 */ 221 static char* 222 getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit) 223 { 224 char* progDir; 225 char path[PATH_MAX], *pathEnd=path+sizeof(path), *p; 226 const char* emulatorPrefix = "emulator-"; 227 const char* emulator64Prefix = "emulator64-"; 228 #ifdef _WIN32 229 const char* exeExt = ".exe"; 230 /* ToDo: currently amd64-mingw32msvc-gcc doesn't work (http://b/issue?id=5949152) 231 which prevents us from generating 64-bit emulator for Windows */ 232 int search_for_64bit_emulator = 0; 233 #else 234 const char* exeExt = ""; 235 int search_for_64bit_emulator = !force_32bit && getHostOSBitness() == 64; 236 #endif 237 238 /* Get program's directory name in progDir */ 239 path_split(progName, &progDir, NULL); 240 241 if (search_for_64bit_emulator) { 242 /* Find 64-bit emulator first */ 243 p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, emulator64Prefix, avdArch, exeExt); 244 if (p >= pathEnd) { 245 APANIC("Path too long: %s\n", progName); 246 } 247 if (path_exists(path)) { 248 free(progDir); 249 return strdup(path); 250 } 251 } 252 253 /* Find 32-bit emulator */ 254 p = bufprint(path, pathEnd, "%s/%s%s%s", progDir, emulatorPrefix, avdArch, exeExt); 255 free(progDir); 256 if (p >= pathEnd) { 257 APANIC("Path too long: %s\n", progName); 258 } 259 260 if (path_exists(path)) { 261 return strdup(path); 262 } 263 264 /* Mmm, the file doesn't exist, If there is no slash / backslash 265 * in our path, we're going to try to search it in our path. 266 */ 267 #ifdef _WIN32 268 if (strchr(progName, '/') == NULL && strchr(progName, '\\') == NULL) { 269 #else 270 if (strchr(progName, '/') == NULL) { 271 #endif 272 if (search_for_64bit_emulator) { 273 p = bufprint(path, pathEnd, "%s%s%s", emulator64Prefix, avdArch, exeExt); 274 if (p < pathEnd) { 275 char* resolved = path_search_exec(path); 276 if (resolved != NULL) 277 return resolved; 278 } 279 } 280 281 p = bufprint(path, pathEnd, "%s%s%s", emulatorPrefix, avdArch, exeExt); 282 if (p < pathEnd) { 283 char* resolved = path_search_exec(path); 284 if (resolved != NULL) 285 return resolved; 286 } 287 } 288 289 /* Otherwise, the program is missing */ 290 APANIC("Missing arch-specific emulator program: %s\n", path); 291 return NULL; 292 } 293 294 /* return 1 iff <path>/<filename> exists */ 295 static int 296 probePathForFile(const char* path, const char* filename) 297 { 298 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 299 p = bufprint(temp, end, "%s/%s", path, filename); 300 D("Probing for: %s\n", temp); 301 return (p < end && path_exists(temp)); 302 } 303 304 /* Find the directory containing a given shared library required by the 305 * emulator (for GLES emulation). We will probe several directories 306 * that correspond to various use-cases. 307 * 308 * Caller must free() result string. NULL if not found. 309 */ 310 311 static char* 312 getSharedLibraryPath(const char* progName, const char* libName) 313 { 314 char* progDir; 315 char* result = NULL; 316 char temp[PATH_MAX], *p=temp, *end=p+sizeof(temp); 317 318 /* Get program's directory name */ 319 path_split(progName, &progDir, NULL); 320 321 /* First, try to probe the program's directory itself, this corresponds 322 * to the standalone build with ./android-configure.sh where the script 323 * will copy the host shared library under external/qemu/objs where 324 * the binaries are located. 325 */ 326 if (probePathForFile(progDir, libName)) { 327 return progDir; 328 } 329 330 /* Try under $progDir/lib/, this should correspond to the SDK installation 331 * where the binary is under tools/, and the libraries under tools/lib/ 332 */ 333 { 334 p = bufprint(temp, end, "%s/lib", progDir); 335 if (p < end && probePathForFile(temp, libName)) { 336 result = strdup(temp); 337 goto EXIT; 338 } 339 } 340 341 /* try in $progDir/../lib, this corresponds to the platform build 342 * where the emulator binary is under out/host/<system>/bin and 343 * the libraries are under out/host/<system>/lib 344 */ 345 { 346 char* parentDir = path_parent(progDir, 1); 347 348 if (parentDir == NULL) { 349 parentDir = strdup("."); 350 } 351 p = bufprint(temp, end, "%s/lib", parentDir); 352 free(parentDir); 353 if (p < end && probePathForFile(temp, libName)) { 354 result = strdup(temp); 355 goto EXIT; 356 } 357 } 358 359 /* Nothing found! */ 360 EXIT: 361 free(progDir); 362 return result; 363 } 364 365 /* Prepend the path in 'prefix' to either LD_LIBRARY_PATH or PATH to 366 * ensure that the shared libraries inside the path will be available 367 * through dlopen() to the emulator program being launched. 368 */ 369 static void 370 prependSharedLibraryPath(const char* prefix) 371 { 372 char temp[2048], *p=temp, *end=p+sizeof(temp); 373 #ifdef _WIN32 374 const char* path = getenv("PATH"); 375 if (path == NULL || path[0] == '\0') { 376 p = bufprint(temp, end, "PATH=%s", prefix); 377 } else { 378 p = bufprint(temp, end, "PATH=%s;%s", path, prefix); 379 } 380 /* Ignore overflow, this will push some paths out of the variable, but 381 * so be it. */ 382 D("Setting %s\n", temp); 383 putenv(strdup(temp)); 384 #else 385 const char* path = getenv("LD_LIBRARY_PATH"); 386 if (path != NULL && path[0] != '\0') { 387 p = bufprint(temp, end, "%s:%s", prefix, path); 388 prefix = temp; 389 } 390 setenv("LD_LIBRARY_PATH",prefix,1); 391 D("Setting LD_LIBRARY_PATH=%s\n", prefix); 392 #endif 393 } 394 395 #ifdef _WIN32 396 static char* 397 quotePath(const char* path) 398 { 399 int len = strlen(path); 400 char* ret = malloc(len+3); 401 402 ret[0] = '"'; 403 memcpy(ret+1, path, len); 404 ret[len+1] = '"'; 405 ret[len+2] = '\0'; 406 407 return ret; 408 } 409 #endif /* _WIN32 */ 410