Home | History | Annotate | Download | only in android
      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