OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (c) 2013 The Native Client Authors. All rights reserved. | |
3 * Use of this source code is governed by a BSD-style license that can be | |
4 * found in the LICENSE file. | |
5 */ | |
6 | |
7 #include <assert.h> | |
8 #include <errno.h> | |
9 #include <fcntl.h> | |
10 #include <limits.h> | |
11 #include <pthread.h> | |
12 #include <stdio.h> | |
13 #include <stdlib.h> | |
14 #include <string.h> | |
15 #include <sys/mman.h> | |
16 #include <sys/stat.h> | |
17 #include <sys/syscall.h> | |
18 #include <unistd.h> | |
19 | |
20 #if defined(__linux__) | |
21 # include <linux/futex.h> | |
22 #endif | |
23 | |
24 #include "native_client/src/include/elf32.h" | |
25 #include "native_client/src/include/elf_auxv.h" | |
26 #include "native_client/src/include/nacl_macros.h" | |
27 #include "native_client/src/trusted/service_runtime/include/machine/_types.h" | |
28 #include "native_client/src/trusted/service_runtime/include/sys/mman.h" | |
29 #include "native_client/src/trusted/service_runtime/include/sys/stat.h" | |
30 #include "native_client/src/trusted/service_runtime/include/sys/time.h" | |
31 #include "native_client/src/trusted/service_runtime/include/sys/unistd.h" | |
32 #include "native_client/src/untrusted/irt/irt.h" | |
33 #include "native_client/src/untrusted/irt/irt_dev.h" | |
34 | |
35 /* | |
36 * This is an implementation of NaCl's IRT interfaces that runs | |
37 * outside of the NaCl sandbox. | |
38 * | |
39 * This allows PNaCl to be used as a portability layer without the | |
40 * SFI-based sandboxing. PNaCl pexes can be translated to | |
41 * non-SFI-sandboxed native code and linked against this IRT | |
42 * implementation. | |
43 */ | |
44 | |
45 | |
46 #if defined(__ANDROID__) && !defined(FUTEX_PRIVATE_FLAG) | |
47 /* Android's Linux headers currently don't define this flag. */ | |
48 # define FUTEX_PRIVATE_FLAG 128 | |
49 #endif | |
50 | |
51 #if defined(__GLIBC__) | |
52 /* | |
53 * glibc's headers will define st_atimensec etc. fields, but only if | |
54 * _POSIX_SOURCE is defined, which disables many other declarations, | |
55 * such as nanosleep(), getpagesize(), MAP_ANON and clock_gettime(). | |
56 */ | |
57 # define st_atimensec st_atim.tv_nsec | |
58 # define st_mtimensec st_mtim.tv_nsec | |
59 # define st_ctimensec st_ctim.tv_nsec | |
60 #elif defined(__APPLE__) | |
61 /* | |
62 * Similarly, Mac OS X's headers will define st_atimensec etc. fields, | |
63 * but only if _POSIX_SOURCE is defined, which disables declarations | |
64 * such as _SC_NPROCESSORS_ONLN. | |
65 */ | |
66 # define st_atimensec st_atimespec.tv_nsec | |
67 # define st_mtimensec st_mtimespec.tv_nsec | |
68 # define st_ctimensec st_ctimespec.tv_nsec | |
69 #endif | |
70 | |
71 void _user_start(void *info); | |
72 void _start(void *info); | |
73 | |
74 /* TODO(mseaborn): Make threads work on Mac OS X. */ | |
75 #if defined(__APPLE__) | |
76 # define __thread /* nothing */ | |
77 #endif | |
78 static __thread void *g_tls_value; | |
79 | |
80 | |
81 /* | |
82 * The IRT functions in irt.h are declared as taking "struct timespec" | |
83 * and "struct timeval" pointers, but these are really "struct | |
84 * nacl_abi_timespec" and "struct nacl_abi_timeval" pointers in this | |
85 * unsandboxed context. | |
86 * | |
87 * To avoid changing irt.h for now and also avoid casting function | |
88 * pointers, we use the same type signatures as in irt.h and do the | |
89 * casting here. | |
90 */ | |
91 static void convert_from_nacl_timespec(struct timespec *dest, | |
92 const struct timespec *src_nacl) { | |
93 const struct nacl_abi_timespec *src = | |
94 (const struct nacl_abi_timespec *) src_nacl; | |
95 dest->tv_sec = src->tv_sec; | |
96 dest->tv_nsec = src->tv_nsec; | |
97 } | |
98 | |
99 static void convert_to_nacl_timespec(struct timespec *dest_nacl, | |
100 const struct timespec *src) { | |
101 struct nacl_abi_timespec *dest = (struct nacl_abi_timespec *) dest_nacl; | |
102 dest->tv_sec = src->tv_sec; | |
103 dest->tv_nsec = src->tv_nsec; | |
104 } | |
105 | |
106 static void convert_to_nacl_timeval(struct timeval *dest_nacl, | |
107 const struct timeval *src) { | |
108 struct nacl_abi_timeval *dest = (struct nacl_abi_timeval *) dest_nacl; | |
109 dest->nacl_abi_tv_sec = src->tv_sec; | |
110 dest->nacl_abi_tv_usec = src->tv_usec; | |
111 } | |
112 | |
113 static void convert_to_nacl_stat(struct stat *dest_nacl, | |
114 const struct stat *src) { | |
115 struct nacl_abi_stat *dest = (struct nacl_abi_stat *) dest_nacl; | |
116 dest->nacl_abi_st_dev = src->st_dev; | |
117 dest->nacl_abi_st_ino = src->st_ino; | |
118 dest->nacl_abi_st_mode = src->st_mode; | |
119 dest->nacl_abi_st_nlink = src->st_nlink; | |
120 dest->nacl_abi_st_uid = src->st_uid; | |
121 dest->nacl_abi_st_gid = src->st_gid; | |
122 dest->nacl_abi_st_rdev = src->st_rdev; | |
123 dest->nacl_abi_st_size = src->st_size; | |
124 dest->nacl_abi_st_blksize = src->st_blksize; | |
125 dest->nacl_abi_st_blocks = src->st_blocks; | |
126 dest->nacl_abi_st_atime = src->st_atime; | |
127 dest->nacl_abi_st_atimensec = src->st_atimensec; | |
128 dest->nacl_abi_st_mtime = src->st_mtime; | |
129 dest->nacl_abi_st_mtimensec = src->st_mtimensec; | |
130 dest->nacl_abi_st_ctime = src->st_ctime; | |
131 dest->nacl_abi_st_ctimensec = src->st_ctimensec; | |
132 } | |
133 | |
134 static void copy_flag(int *dest, int src, int new_flag, int old_flag) { | |
135 if ((src & old_flag) != 0) | |
136 *dest |= new_flag; | |
137 } | |
138 | |
139 /* Returns whether the conversion was successful. */ | |
140 static int convert_from_nacl_mmap_prot(int *prot, int prot_nacl) { | |
141 if ((prot_nacl & ~NACL_ABI_PROT_MASK) != 0) | |
142 return 0; | |
143 *prot = 0; | |
144 copy_flag(prot, prot_nacl, PROT_READ, NACL_ABI_PROT_READ); | |
145 copy_flag(prot, prot_nacl, PROT_WRITE, NACL_ABI_PROT_WRITE); | |
146 copy_flag(prot, prot_nacl, PROT_EXEC, NACL_ABI_PROT_EXEC); | |
147 return 1; | |
148 } | |
149 | |
150 /* Returns whether the conversion was successful. */ | |
151 static int convert_from_nacl_mmap_flags(int *flags, int flags_nacl) { | |
152 int allowed = NACL_ABI_MAP_SHARED | | |
153 NACL_ABI_MAP_PRIVATE | | |
154 NACL_ABI_MAP_FIXED | | |
155 NACL_ABI_MAP_ANON; | |
156 if ((flags_nacl & ~allowed) != 0) | |
157 return 0; | |
158 *flags = 0; | |
159 copy_flag(flags, flags_nacl, MAP_SHARED, NACL_ABI_MAP_SHARED); | |
160 copy_flag(flags, flags_nacl, MAP_PRIVATE, NACL_ABI_MAP_PRIVATE); | |
161 copy_flag(flags, flags_nacl, MAP_FIXED, NACL_ABI_MAP_FIXED); | |
162 copy_flag(flags, flags_nacl, MAP_ANON, NACL_ABI_MAP_ANON); | |
163 return 1; | |
164 } | |
165 | |
166 static int check_error(int result) { | |
167 if (result != 0) { | |
168 /* | |
169 * Check that we really have an error and don't indicate success | |
170 * mistakenly. | |
171 */ | |
172 assert(errno != 0); | |
173 return errno; | |
174 } | |
175 return 0; | |
176 } | |
177 | |
178 static int irt_close(int fd) { | |
179 return check_error(close(fd)); | |
180 } | |
181 | |
182 static int irt_dup(int fd, int *new_fd) { | |
183 int result = dup(fd); | |
184 if (result < 0) | |
185 return errno; | |
186 *new_fd = result; | |
187 return 0; | |
188 } | |
189 | |
190 static int irt_dup2(int fd, int new_fd) { | |
191 int result = dup2(fd, new_fd); | |
192 if (result < 0) | |
193 return errno; | |
194 assert(result == new_fd); | |
195 return 0; | |
196 } | |
197 | |
198 static int irt_read(int fd, void *buf, size_t count, size_t *nread) { | |
199 int result = read(fd, buf, count); | |
200 if (result < 0) | |
201 return errno; | |
202 *nread = result; | |
203 return 0; | |
204 } | |
205 | |
206 static int irt_write(int fd, const void *buf, size_t count, size_t *nwrote) { | |
207 int result = write(fd, buf, count); | |
208 if (result < 0) | |
209 return errno; | |
210 *nwrote = result; | |
211 return 0; | |
212 } | |
213 | |
214 static int irt_seek(int fd, nacl_abi_off_t offset, int whence, | |
215 nacl_abi_off_t *new_offset) { | |
216 off_t result = lseek(fd, offset, whence); | |
217 if (result < 0) | |
218 return errno; | |
219 *new_offset = result; | |
220 return 0; | |
221 } | |
222 | |
223 static int irt_fstat(int fd, struct stat *st) { | |
224 /* TODO(mseaborn): Implement this and convert "struct stat". */ | |
225 return ENOSYS; | |
226 } | |
227 | |
228 static void irt_exit(int status) { | |
229 _exit(status); | |
230 } | |
231 | |
232 static int irt_clock_func(clock_t *ticks) { | |
233 clock_t result = clock(); | |
234 if (result == (clock_t) -1) | |
235 return errno; | |
236 *ticks = result; | |
237 return 0; | |
238 } | |
239 | |
240 static int irt_gettod(struct timeval *time_nacl) { | |
241 struct timeval time; | |
242 int result = check_error(gettimeofday(&time, NULL)); | |
243 convert_to_nacl_timeval(time_nacl, &time); | |
244 return result; | |
245 } | |
246 | |
247 static int irt_sched_yield(void) { | |
248 return check_error(sched_yield()); | |
249 } | |
250 | |
251 static int irt_nanosleep(const struct timespec *requested_nacl, | |
252 struct timespec *remaining_nacl) { | |
253 struct timespec requested; | |
254 struct timespec remaining; | |
255 convert_from_nacl_timespec(&requested, requested_nacl); | |
256 int result = check_error(nanosleep(&requested, &remaining)); | |
257 if (remaining_nacl != NULL) | |
258 convert_to_nacl_timespec(remaining_nacl, &remaining); | |
259 return result; | |
260 } | |
261 | |
262 static int irt_sysconf(int name, int *value) { | |
263 switch (name) { | |
264 case NACL_ABI__SC_PAGESIZE: | |
265 /* | |
266 * For now, return the host's page size (typically 4k) rather | |
267 * than 64k (NaCl's usual page size), which pexes will usually | |
268 * be tested with. We could change this to 64k, but then the | |
269 * mmap() we define here should round up requested sizes to | |
270 * multiples of 64k. | |
271 */ | |
272 *value = getpagesize(); | |
273 return 0; | |
274 case NACL_ABI__SC_NPROCESSORS_ONLN: { | |
275 int result = sysconf(_SC_NPROCESSORS_ONLN); | |
276 if (result == 0) | |
277 return errno; | |
278 *value = result; | |
279 return 0; | |
280 } | |
281 default: | |
282 return EINVAL; | |
283 } | |
284 } | |
285 | |
286 static int irt_mmap(void **addr, size_t len, int prot, int flags, | |
287 int fd, nacl_irt_off_t off) { | |
288 int host_prot; | |
289 int host_flags; | |
290 if (!convert_from_nacl_mmap_prot(&host_prot, prot) || | |
291 !convert_from_nacl_mmap_flags(&host_flags, flags)) { | |
292 return EINVAL; | |
293 } | |
294 void *result = mmap(*addr, len, host_prot, host_flags, fd, off); | |
295 if (result == MAP_FAILED) | |
296 return errno; | |
297 *addr = result; | |
298 return 0; | |
299 } | |
300 | |
301 static int irt_munmap(void *addr, size_t len) { | |
302 return check_error(munmap(addr, len)); | |
303 } | |
304 | |
305 static int tls_init(void *ptr) { | |
306 g_tls_value = ptr; | |
307 return 0; | |
308 } | |
309 | |
310 static void *tls_get(void) { | |
311 return g_tls_value; | |
312 } | |
313 | |
314 void *__nacl_read_tp(void) { | |
315 return g_tls_value; | |
316 } | |
317 | |
318 struct thread_args { | |
319 void (*start_func)(void); | |
320 void *thread_ptr; | |
321 }; | |
322 | |
323 static void *start_thread(void *arg) { | |
324 struct thread_args args = *(struct thread_args *) arg; | |
325 free(arg); | |
326 g_tls_value = args.thread_ptr; | |
327 args.start_func(); | |
328 abort(); | |
329 } | |
330 | |
331 static int thread_create(void (*start_func)(void), void *stack, | |
332 void *thread_ptr) { | |
333 /* | |
334 * For now, we ignore the stack that user code provides and just use | |
335 * the stack that the host libpthread allocates. | |
336 */ | |
337 pthread_attr_t attr; | |
338 int error = pthread_attr_init(&attr); | |
339 if (error != 0) | |
340 return error; | |
341 error = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
342 if (error != 0) | |
343 return error; | |
344 struct thread_args *args = malloc(sizeof(struct thread_args)); | |
345 if (args == NULL) { | |
346 error = ENOMEM; | |
347 goto cleanup; | |
348 } | |
349 args->start_func = start_func; | |
350 args->thread_ptr = thread_ptr; | |
351 pthread_t tid; | |
352 error = pthread_create(&tid, &attr, start_thread, args); | |
353 if (error != 0) | |
354 free(args); | |
355 cleanup: | |
356 pthread_attr_destroy(&attr); | |
357 return error; | |
358 } | |
359 | |
360 static void thread_exit(int32_t *stack_flag) { | |
361 *stack_flag = 0; /* Indicate that the user code's stack can be freed. */ | |
362 pthread_exit(NULL); | |
363 } | |
364 | |
365 static int thread_nice(const int nice) { | |
366 return 0; | |
367 } | |
368 | |
369 /* | |
370 * Mac OS X does not provide futexes or clock_gettime()/getres() natively. | |
371 * TODO(mseaborn): Make threads and clock_gettime() work on Mac OS X. | |
372 */ | |
373 #if defined(__linux__) | |
374 static int futex_wait_abs(volatile int *addr, int value, | |
375 const struct timespec *abstime_nacl) { | |
376 struct timespec reltime; | |
377 struct timespec *reltime_ptr = NULL; | |
378 if (abstime_nacl != NULL) { | |
379 struct timespec time_now; | |
380 if (clock_gettime(CLOCK_REALTIME, &time_now) != 0) | |
381 return errno; | |
382 | |
383 /* Convert the absolute time to a relative time. */ | |
384 const struct nacl_abi_timespec *abstime = | |
385 (const struct nacl_abi_timespec *) abstime_nacl; | |
386 reltime.tv_sec = abstime->tv_sec - time_now.tv_sec; | |
387 reltime.tv_nsec = abstime->tv_nsec - time_now.tv_nsec; | |
388 if (reltime.tv_nsec < 0) { | |
389 reltime.tv_sec -= 1; | |
390 reltime.tv_nsec += 1000000000; | |
391 } | |
392 /* | |
393 * Linux's FUTEX_WAIT returns EINVAL if given a negative relative | |
394 * time. But an absolute time that's in the past is a valid | |
395 * argument, for which we need to return ETIMEDOUT instead. | |
396 */ | |
397 if (reltime.tv_sec < 0) | |
398 return ETIMEDOUT; | |
399 reltime_ptr = &reltime; | |
400 } | |
401 return check_error(syscall(__NR_futex, addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, | |
402 value, reltime_ptr, 0, 0)); | |
403 } | |
404 | |
405 static int futex_wake(volatile int *addr, int nwake, int *count) { | |
406 int result = syscall(__NR_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, | |
407 nwake, 0, 0, 0); | |
408 if (result < 0) | |
409 return errno; | |
410 *count = result; | |
411 return 0; | |
412 } | |
413 | |
414 static int irt_clock_getres(nacl_irt_clockid_t clk_id, | |
415 struct timespec *time_nacl) { | |
416 struct timespec time; | |
417 int result = check_error(clock_getres(clk_id, &time)); | |
418 convert_to_nacl_timespec(time_nacl, &time); | |
419 return result; | |
420 } | |
421 | |
422 static int irt_clock_gettime(nacl_irt_clockid_t clk_id, | |
423 struct timespec *time_nacl) { | |
424 struct timespec time; | |
425 int result = check_error(clock_gettime(clk_id, &time)); | |
426 convert_to_nacl_timespec(time_nacl, &time); | |
427 return result; | |
428 } | |
429 #endif | |
430 | |
431 static int irt_open(const char *pathname, int flags, mode_t mode, int *new_fd) { | |
432 int fd = open(pathname, flags, mode); | |
433 if (fd < 0) | |
434 return errno; | |
435 *new_fd = fd; | |
436 return 0; | |
437 } | |
438 | |
439 static int irt_stat(const char *pathname, struct stat *stat_info_nacl) { | |
440 struct stat stat_info; | |
441 if (stat(pathname, &stat_info) != 0) | |
442 return errno; | |
443 convert_to_nacl_stat(stat_info_nacl, &stat_info); | |
444 return 0; | |
445 } | |
446 | |
447 static int irt_mkdir(const char *pathname, mode_t mode) { | |
448 return check_error(mkdir(pathname, mode)); | |
449 } | |
450 | |
451 static int irt_rmdir(const char *pathname) { | |
452 return check_error(rmdir(pathname)); | |
453 } | |
454 | |
455 static int irt_chdir(const char *pathname) { | |
456 return check_error(chdir(pathname)); | |
457 } | |
458 | |
459 static int irt_getcwd(char *pathname, size_t len) { | |
460 if (getcwd(pathname, len) == NULL) | |
461 return errno; | |
462 return 0; | |
463 } | |
464 | |
465 static int irt_unlink(const char *pathname) { | |
466 return check_error(unlink(pathname)); | |
467 } | |
468 | |
469 static int irt_getpid(int *pid) { | |
470 *pid = getpid(); | |
471 return 0; | |
472 } | |
473 | |
474 static void irt_stub_func(const char *name) { | |
475 fprintf(stderr, "Error: Unimplemented IRT function: %s\n", name); | |
476 abort(); | |
477 } | |
478 | |
479 #define DEFINE_STUB(name) \ | |
480 static void irt_stub_##name() { irt_stub_func(#name); } | |
481 #define USE_STUB(s, name) (typeof(s.name)) irt_stub_##name | |
482 | |
483 static const struct nacl_irt_basic irt_basic = { | |
484 irt_exit, | |
485 irt_gettod, | |
486 irt_clock_func, | |
487 irt_nanosleep, | |
488 irt_sched_yield, | |
489 irt_sysconf, | |
490 }; | |
491 | |
492 DEFINE_STUB(getdents) | |
493 static const struct nacl_irt_fdio irt_fdio = { | |
494 irt_close, | |
495 irt_dup, | |
496 irt_dup2, | |
497 irt_read, | |
498 irt_write, | |
499 irt_seek, | |
500 irt_fstat, | |
501 USE_STUB(irt_fdio, getdents), | |
502 }; | |
503 | |
504 DEFINE_STUB(mprotect) | |
505 static const struct nacl_irt_memory irt_memory = { | |
506 irt_mmap, | |
507 irt_munmap, | |
508 USE_STUB(irt_memory, mprotect), | |
509 }; | |
510 | |
511 static const struct nacl_irt_tls irt_tls = { | |
512 tls_init, | |
513 tls_get, | |
514 }; | |
515 | |
516 static const struct nacl_irt_thread irt_thread = { | |
517 thread_create, | |
518 thread_exit, | |
519 thread_nice, | |
520 }; | |
521 | |
522 #if defined(__linux__) | |
523 static const struct nacl_irt_futex irt_futex = { | |
524 futex_wait_abs, | |
525 futex_wake, | |
526 }; | |
527 | |
528 static const struct nacl_irt_clock irt_clock = { | |
529 irt_clock_getres, | |
530 irt_clock_gettime, | |
531 }; | |
532 #else | |
533 DEFINE_STUB(futex_wait_abs) | |
534 DEFINE_STUB(futex_wake) | |
535 static const struct nacl_irt_futex irt_futex = { | |
536 USE_STUB(irt_futex, futex_wait_abs), | |
537 USE_STUB(irt_futex, futex_wake), | |
538 }; | |
539 #endif | |
540 | |
541 DEFINE_STUB(truncate) | |
542 DEFINE_STUB(lstat) | |
543 DEFINE_STUB(link) | |
544 DEFINE_STUB(rename) | |
545 DEFINE_STUB(symlink) | |
546 DEFINE_STUB(chmod) | |
547 DEFINE_STUB(access) | |
548 DEFINE_STUB(readlink) | |
549 DEFINE_STUB(utimes) | |
550 static const struct nacl_irt_dev_filename irt_dev_filename = { | |
551 irt_open, | |
552 irt_stat, | |
553 irt_mkdir, | |
554 irt_rmdir, | |
555 irt_chdir, | |
556 irt_getcwd, | |
557 irt_unlink, | |
558 USE_STUB(irt_dev_filename, truncate), | |
559 USE_STUB(irt_dev_filename, lstat), | |
560 USE_STUB(irt_dev_filename, link), | |
561 USE_STUB(irt_dev_filename, rename), | |
562 USE_STUB(irt_dev_filename, symlink), | |
563 USE_STUB(irt_dev_filename, chmod), | |
564 USE_STUB(irt_dev_filename, access), | |
565 USE_STUB(irt_dev_filename, readlink), | |
566 USE_STUB(irt_dev_filename, utimes), | |
567 }; | |
568 | |
569 static const struct nacl_irt_dev_getpid irt_dev_getpid = { | |
570 irt_getpid, | |
571 }; | |
572 | |
573 struct nacl_interface_table { | |
574 const char *name; | |
575 const void *table; | |
576 size_t size; | |
577 }; | |
578 | |
579 static const struct nacl_interface_table irt_interfaces[] = { | |
580 { NACL_IRT_BASIC_v0_1, &irt_basic, sizeof(irt_basic) }, | |
581 { NACL_IRT_FDIO_v0_1, &irt_fdio, sizeof(irt_fdio) }, | |
582 { NACL_IRT_MEMORY_v0_3, &irt_memory, sizeof(irt_memory) }, | |
583 { NACL_IRT_TLS_v0_1, &irt_tls, sizeof(irt_tls) }, | |
584 { NACL_IRT_THREAD_v0_1, &irt_thread, sizeof(irt_thread) }, | |
585 { NACL_IRT_FUTEX_v0_1, &irt_futex, sizeof(irt_futex) }, | |
586 #if defined(__linux__) | |
587 { NACL_IRT_CLOCK_v0_1, &irt_clock, sizeof(irt_clock) }, | |
588 #endif | |
589 { NACL_IRT_DEV_FILENAME_v0_3, &irt_dev_filename, sizeof(irt_dev_filename) }, | |
590 { NACL_IRT_DEV_GETPID_v0_1, &irt_dev_getpid, sizeof(irt_dev_getpid) }, | |
591 }; | |
592 | |
593 static size_t irt_interface_query(const char *interface_ident, | |
594 void *table, size_t tablesize) { | |
595 unsigned i; | |
596 for (i = 0; i < NACL_ARRAY_SIZE(irt_interfaces); ++i) { | |
597 if (0 == strcmp(interface_ident, irt_interfaces[i].name)) { | |
598 const size_t size = irt_interfaces[i].size; | |
599 if (size <= tablesize) { | |
600 memcpy(table, irt_interfaces[i].table, size); | |
601 return size; | |
602 } | |
603 break; | |
604 } | |
605 } | |
606 fprintf(stderr, "Warning: unavailable IRT interface queried: %s\n", | |
607 interface_ident); | |
608 return 0; | |
609 } | |
610 | |
611 int main(int argc, char **argv, char **environ) { | |
612 /* Find size of environ array. */ | |
613 size_t env_count = 0; | |
614 while (environ[env_count] != NULL) | |
615 env_count++; | |
616 | |
617 size_t count = | |
618 1 /* cleanup_func pointer */ | |
619 + 2 /* envc and argc counts */ | |
620 + argc + 1 /* argv array, with terminator */ | |
621 + env_count + 1 /* environ array, with terminator */ | |
622 + 4; /* auxv: 2 entries, one of them the terminator */ | |
623 uintptr_t *data = malloc(count * sizeof(uintptr_t)); | |
624 if (data == NULL) { | |
625 fprintf(stderr, "Failed to allocate argv/env/auxv array\n"); | |
626 return 1; | |
627 } | |
628 size_t pos = 0; | |
629 data[pos++] = 0; /* cleanup_func pointer */ | |
630 data[pos++] = env_count; | |
631 data[pos++] = argc; | |
632 /* Copy arrays, with terminators. */ | |
633 size_t i; | |
634 for (i = 0; i < (size_t) argc; i++) | |
635 data[pos++] = (uintptr_t) argv[i]; | |
636 data[pos++] = 0; | |
637 for (i = 0; i < env_count; i++) | |
638 data[pos++] = (uintptr_t) environ[i]; | |
639 data[pos++] = 0; | |
640 /* auxv[0] */ | |
641 data[pos++] = AT_SYSINFO; | |
642 data[pos++] = (uintptr_t) irt_interface_query; | |
643 /* auxv[1] */ | |
644 data[pos++] = 0; | |
645 data[pos++] = 0; | |
646 assert(pos == count); | |
647 | |
648 /* | |
649 * On Linux, we rename _start() to _user_start() to avoid a clash | |
650 * with the "_start" routine in the host toolchain. On Mac OS X, | |
651 * lacking objcopy, doing the symbol renaming is trickier, but also | |
652 * unnecessary, because the host toolchain doesn't have a "_start" | |
653 * routine. | |
654 */ | |
655 #if defined(__APPLE__) | |
656 _start(data); | |
657 #else | |
658 _user_start(data); | |
659 #endif | |
660 return 1; | |
661 } | |
OLD | NEW |