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 */
36int 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 */
61static char* getTargetEmulatorPath(const char* progName, const char* avdArch, const int force_32bit);
62static char* getSharedLibraryPath(const char* progName, const char* libName);
63static 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 */
79int 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 */
217static char*
218probeTargetEmulatorPath(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
289static char*
290getTargetEmulatorPath(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 */
373static int
374probePathForFile(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
389static char*
390getSharedLibraryPath(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! */
438EXIT:
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 */
447static void
448prependSharedLibraryPath(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