• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
main(int argc,char ** argv)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
getHostOSBitness()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*
getTargetEmulatorPath(const char * progName,const char * avdArch,const int force_32bit)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