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