1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include <sys/types.h> // Include something that will define __GLIBC__. 6 7 // The entire file is wrapped in this #if. We do this so this .cc file can be 8 // compiled, even on a non-glibc build. 9 #if defined(__native_client__) && defined(__GLIBC__) 10 11 #include "nacl_io/kernel_wrap.h" 12 13 #include <alloca.h> 14 #include <assert.h> 15 #include <dirent.h> 16 #include <errno.h> 17 #include <irt.h> 18 #include <irt_syscalls.h> 19 #include <nacl_stat.h> 20 #include <string.h> 21 #include <sys/stat.h> 22 #include <sys/time.h> 23 24 #include "nacl_io/kernel_intercept.h" 25 #include "nacl_io/kernel_wrap_real.h" 26 #include "nacl_io/log.h" 27 #include "nacl_io/osmman.h" 28 #include "nacl_io/ostime.h" 29 30 namespace { 31 32 void stat_to_nacl_stat(const struct stat* buf, nacl_abi_stat* nacl_buf) { 33 memset(nacl_buf, 0, sizeof(struct nacl_abi_stat)); 34 nacl_buf->nacl_abi_st_dev = buf->st_dev; 35 nacl_buf->nacl_abi_st_ino = buf->st_ino; 36 nacl_buf->nacl_abi_st_mode = buf->st_mode; 37 nacl_buf->nacl_abi_st_nlink = buf->st_nlink; 38 nacl_buf->nacl_abi_st_uid = buf->st_uid; 39 nacl_buf->nacl_abi_st_gid = buf->st_gid; 40 nacl_buf->nacl_abi_st_rdev = buf->st_rdev; 41 nacl_buf->nacl_abi_st_size = buf->st_size; 42 nacl_buf->nacl_abi_st_blksize = buf->st_blksize; 43 nacl_buf->nacl_abi_st_blocks = buf->st_blocks; 44 nacl_buf->nacl_abi_st_atime = buf->st_atime; 45 nacl_buf->nacl_abi_st_atimensec = buf->st_atimensec; 46 nacl_buf->nacl_abi_st_mtime = buf->st_mtime; 47 nacl_buf->nacl_abi_st_mtimensec = buf->st_mtimensec; 48 nacl_buf->nacl_abi_st_ctime = buf->st_ctime; 49 nacl_buf->nacl_abi_st_ctimensec = buf->st_ctimensec; 50 } 51 52 void nacl_stat_to_stat(const nacl_abi_stat* nacl_buf, struct stat* buf) { 53 memset(buf, 0, sizeof(struct stat)); 54 buf->st_dev = nacl_buf->nacl_abi_st_dev; 55 buf->st_ino = nacl_buf->nacl_abi_st_ino; 56 buf->st_mode = nacl_buf->nacl_abi_st_mode; 57 buf->st_nlink = nacl_buf->nacl_abi_st_nlink; 58 buf->st_uid = nacl_buf->nacl_abi_st_uid; 59 buf->st_gid = nacl_buf->nacl_abi_st_gid; 60 buf->st_rdev = nacl_buf->nacl_abi_st_rdev; 61 buf->st_size = nacl_buf->nacl_abi_st_size; 62 buf->st_blksize = nacl_buf->nacl_abi_st_blksize; 63 buf->st_blocks = nacl_buf->nacl_abi_st_blocks; 64 buf->st_atime = nacl_buf->nacl_abi_st_atime; 65 buf->st_atimensec = nacl_buf->nacl_abi_st_atimensec; 66 buf->st_mtime = nacl_buf->nacl_abi_st_mtime; 67 buf->st_mtimensec = nacl_buf->nacl_abi_st_mtimensec; 68 buf->st_ctime = nacl_buf->nacl_abi_st_ctime; 69 buf->st_ctimensec = nacl_buf->nacl_abi_st_ctimensec; 70 } 71 72 } // namespace 73 74 // From native_client/src/trusted/service_runtime/include/sys/dirent.h 75 76 #ifndef nacl_abi___ino_t_defined 77 #define nacl_abi___ino_t_defined 78 typedef int64_t nacl_abi___ino_t; 79 typedef nacl_abi___ino_t nacl_abi_ino_t; 80 #endif 81 82 #ifndef nacl_abi___off_t_defined 83 #define nacl_abi___off_t_defined 84 typedef int64_t nacl_abi__off_t; 85 typedef nacl_abi__off_t nacl_abi_off_t; 86 #endif 87 88 /* We need a way to define the maximum size of a name. */ 89 #ifndef MAXNAMLEN 90 # ifdef NAME_MAX 91 # define MAXNAMLEN NAME_MAX 92 # else 93 # define MAXNAMLEN 255 94 # endif 95 #endif 96 97 struct nacl_abi_dirent { 98 nacl_abi_ino_t nacl_abi_d_ino; 99 nacl_abi_off_t nacl_abi_d_off; 100 uint16_t nacl_abi_d_reclen; 101 char nacl_abi_d_name[MAXNAMLEN + 1]; 102 }; 103 104 static const int d_name_shift = offsetof (dirent, d_name) - 105 offsetof (struct nacl_abi_dirent, nacl_abi_d_name); 106 107 EXTERN_C_BEGIN 108 109 // Macro to get the REAL function pointer 110 #define REAL(name) __nacl_irt_##name##_real 111 112 // Macro to get the WRAP function 113 #define WRAP(name) __nacl_irt_##name##_wrap 114 115 // Declare REAL function pointer. 116 #define DECLARE_REAL_PTR(name) typeof(__nacl_irt_##name) REAL(name); 117 118 // Assign the REAL function pointer. 119 #define ASSIGN_REAL_PTR(name) \ 120 assert(__nacl_irt_##name != NULL); \ 121 REAL(name) = __nacl_irt_##name; 122 123 // Switch IRT's pointer to the REAL pointer 124 #define USE_REAL(name) __nacl_irt_##name = (typeof(__nacl_irt_##name))REAL(name) 125 126 // Switch IRT's pointer to the WRAP function 127 #define USE_WRAP(name) __nacl_irt_##name = (typeof(__nacl_irt_##name))WRAP(name) 128 129 #define EXPAND_SYMBOL_LIST_OPERATION(OP) \ 130 OP(chdir); \ 131 OP(close); \ 132 OP(dup); \ 133 OP(dup2); \ 134 OP(exit); \ 135 OP(fstat); \ 136 OP(getcwd); \ 137 OP(getdents); \ 138 OP(mkdir); \ 139 OP(open); \ 140 OP(poll); \ 141 OP(read); \ 142 OP(rmdir); \ 143 OP(seek); \ 144 OP(stat); \ 145 OP(select); \ 146 OP(write); \ 147 OP(mmap); \ 148 OP(munmap); \ 149 OP(open_resource); \ 150 \ 151 OP(socket); \ 152 OP(accept); \ 153 OP(bind); \ 154 OP(listen); \ 155 OP(connect); \ 156 OP(send); \ 157 OP(sendmsg); \ 158 OP(sendto); \ 159 OP(recv); \ 160 OP(recvmsg); \ 161 OP(recvfrom); \ 162 OP(getpeername); \ 163 OP(getsockname); \ 164 OP(getsockopt); \ 165 OP(setsockopt); \ 166 OP(socketpair); \ 167 OP(shutdown); \ 168 \ 169 OP(chmod); \ 170 OP(access); \ 171 OP(unlink); \ 172 OP(fchdir); \ 173 OP(fchmod); \ 174 OP(fsync); \ 175 OP(fdatasync); \ 176 OP(lstat); \ 177 OP(link); \ 178 OP(readlink); \ 179 OP(utimes); 180 181 // TODO(bradnelson): Add these as well. 182 // OP(epoll_create); 183 // OP(epoll_create1); 184 // OP(epoll_ctl); 185 // OP(epoll_pwait); 186 // OP(ppoll); 187 // OP(pselect); 188 189 EXPAND_SYMBOL_LIST_OPERATION(DECLARE_REAL_PTR); 190 191 int WRAP(chdir)(const char* pathname) { 192 ERRNO_RTN(ki_chdir(pathname)); 193 } 194 195 int WRAP(close)(int fd) { 196 ERRNO_RTN(ki_close(fd)); 197 } 198 199 int WRAP(dup)(int fd, int* newfd) NOTHROW { 200 *newfd = ki_dup(fd); 201 RTN_ERRNO_IF(*newfd < 0); 202 return 0; 203 } 204 205 int WRAP(dup2)(int fd, int newfd) NOTHROW { 206 ERRNO_RTN(ki_dup2(fd, newfd)); 207 } 208 209 void WRAP(exit)(int status) { 210 ki_exit(status); 211 } 212 213 int WRAP(fstat)(int fd, struct nacl_abi_stat* nacl_buf) { 214 struct stat buf; 215 memset(&buf, 0, sizeof(struct stat)); 216 int res = ki_fstat(fd, &buf); 217 RTN_ERRNO_IF(res < 0); 218 stat_to_nacl_stat(&buf, nacl_buf); 219 return 0; 220 } 221 222 int WRAP(getcwd)(char* buf, size_t size) { 223 RTN_ERRNO_IF(ki_getcwd(buf, size) == NULL); 224 return 0; 225 } 226 227 int WRAP(getdents)(int fd, dirent* nacl_buf, size_t nacl_count, size_t* nread) { 228 int nacl_offset = 0; 229 // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s). 230 // nacl_abi_dirent(s) are smaller than dirent(s), so nacl_count bytes buffer 231 // is enough 232 char* buf = (char*)alloca(nacl_count); 233 int offset = 0; 234 int count; 235 236 count = ki_getdents(fd, buf, nacl_count); 237 RTN_ERRNO_IF(count < 0); 238 239 while (offset < count) { 240 dirent* d = (dirent*)(buf + offset); 241 nacl_abi_dirent* nacl_d = (nacl_abi_dirent*)((char*)nacl_buf + nacl_offset); 242 nacl_d->nacl_abi_d_ino = d->d_ino; 243 nacl_d->nacl_abi_d_off = d->d_off; 244 nacl_d->nacl_abi_d_reclen = d->d_reclen - d_name_shift; 245 size_t d_name_len = d->d_reclen - offsetof(dirent, d_name); 246 memcpy(nacl_d->nacl_abi_d_name, d->d_name, d_name_len); 247 248 offset += d->d_reclen; 249 nacl_offset += nacl_d->nacl_abi_d_reclen; 250 } 251 252 *nread = nacl_offset; 253 return 0; 254 } 255 256 int WRAP(mkdir)(const char* pathname, mode_t mode) { 257 RTN_ERRNO_IF(ki_mkdir(pathname, mode) < 0); 258 return 0; 259 } 260 261 int WRAP(mmap)(void** addr, 262 size_t length, 263 int prot, 264 int flags, 265 int fd, 266 off_t offset) { 267 if (flags & MAP_ANONYMOUS) 268 return REAL(mmap)(addr, length, prot, flags, fd, offset); 269 270 *addr = ki_mmap(*addr, length, prot, flags, fd, offset); 271 RTN_ERRNO_IF(*addr == (void*)-1); 272 return 0; 273 } 274 275 int WRAP(munmap)(void* addr, size_t length) { 276 // Always let the real munmap run on the address range. It is not an error if 277 // there are no mapped pages in that range. 278 ki_munmap(addr, length); 279 return REAL(munmap)(addr, length); 280 } 281 282 int WRAP(open)(const char* pathname, int oflag, mode_t mode, int* newfd) { 283 *newfd = ki_open(pathname, oflag, mode); 284 RTN_ERRNO_IF(*newfd < 0); 285 return 0; 286 } 287 288 int WRAP(open_resource)(const char* file, int* fd) { 289 *fd = ki_open_resource(file); 290 RTN_ERRNO_IF(*fd < 0); 291 return 0; 292 } 293 294 int WRAP(poll)(struct pollfd* fds, nfds_t nfds, int timeout, int* count) { 295 *count = ki_poll(fds, nfds, timeout); 296 RTN_ERRNO_IF(*count < 0); 297 return 0; 298 } 299 300 int WRAP(read)(int fd, void* buf, size_t count, size_t* nread) { 301 ssize_t signed_nread = ki_read(fd, buf, count); 302 *nread = static_cast<size_t>(signed_nread); 303 RTN_ERRNO_IF(signed_nread < 0); 304 return 0; 305 } 306 307 int WRAP(rmdir)(const char* pathname) { 308 RTN_ERRNO_IF(ki_rmdir(pathname) < 0); 309 return 0; 310 } 311 312 int WRAP(seek)(int fd, off_t offset, int whence, off_t* new_offset) { 313 *new_offset = ki_lseek(fd, offset, whence); 314 RTN_ERRNO_IF(*new_offset < 0); 315 return 0; 316 } 317 318 int WRAP(select)(int nfds, 319 fd_set* readfds, 320 fd_set* writefds, 321 fd_set* exceptfds, 322 struct timeval* timeout, 323 int* count) { 324 *count = ki_select(nfds, readfds, writefds, exceptfds, timeout); 325 RTN_ERRNO_IF(*count < 0); 326 return 0; 327 } 328 329 int WRAP(stat)(const char* pathname, struct nacl_abi_stat* nacl_buf) { 330 struct stat buf; 331 memset(&buf, 0, sizeof(struct stat)); 332 int res = ki_stat(pathname, &buf); 333 RTN_ERRNO_IF(res < 0); 334 stat_to_nacl_stat(&buf, nacl_buf); 335 return 0; 336 } 337 338 int WRAP(lstat)(const char* pathname, struct nacl_abi_stat* nacl_buf) { 339 struct stat buf; 340 memset(&buf, 0, sizeof(struct stat)); 341 int res = ki_lstat(pathname, &buf); 342 RTN_ERRNO_IF(res < 0); 343 stat_to_nacl_stat(&buf, nacl_buf); 344 return 0; 345 } 346 347 int WRAP(link)(const char* pathname, const char* newpath) { 348 ERRNO_RTN(ki_link(pathname, newpath)); 349 } 350 351 int WRAP(readlink)(const char* pathname, 352 char* buf, 353 size_t count, 354 size_t* nread) { 355 int rtn = ki_readlink(pathname, buf, count); 356 RTN_ERRNO_IF(rtn < 0); 357 *nread = rtn; 358 return 0; 359 } 360 361 int WRAP(utimes)(const char *filename, const struct timeval *times) { 362 ERRNO_RTN(ki_utimes(filename, times)); 363 } 364 365 int WRAP(chmod)(const char* pathname, mode_t mode) { 366 ERRNO_RTN(ki_chmod(pathname, mode)); 367 } 368 369 int WRAP(access)(const char* pathname, int amode) { 370 ERRNO_RTN(ki_access(pathname, amode)); 371 } 372 373 int WRAP(unlink)(const char* pathname) { 374 ERRNO_RTN(ki_unlink(pathname)); 375 } 376 377 int WRAP(fchdir)(int fd) { 378 ERRNO_RTN(ki_fchdir(fd)); 379 } 380 381 int WRAP(fchmod)(int fd, mode_t mode) { 382 ERRNO_RTN(ki_fchmod(fd, mode)); 383 } 384 385 int WRAP(fsync)(int fd) { 386 ERRNO_RTN(ki_fsync(fd)); 387 } 388 389 int WRAP(fdatasync)(int fd) { 390 ERRNO_RTN(ki_fdatasync(fd)); 391 } 392 393 int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) { 394 ssize_t signed_nwrote = ki_write(fd, buf, count); 395 *nwrote = static_cast<size_t>(signed_nwrote); 396 RTN_ERRNO_IF(signed_nwrote < 0); 397 return 0; 398 } 399 400 int WRAP(accept)(int sockfd, 401 struct sockaddr* addr, 402 socklen_t* addrlen, 403 int* sd) { 404 *sd = ki_accept(sockfd, addr, addrlen); 405 RTN_ERRNO_IF(*sd < 0); 406 return 0; 407 } 408 409 int WRAP(bind)(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { 410 ERRNO_RTN(ki_bind(sockfd, addr, addrlen)); 411 } 412 413 int WRAP(connect)(int sockfd, const struct sockaddr* addr, socklen_t addrlen) { 414 ERRNO_RTN(ki_connect(sockfd, addr, addrlen)); 415 } 416 417 int WRAP(getpeername)(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { 418 ERRNO_RTN(ki_getpeername(sockfd, addr, addrlen)); 419 } 420 421 int WRAP(getsockname)(int sockfd, struct sockaddr* addr, socklen_t* addrlen) { 422 ERRNO_RTN(ki_getsockname(sockfd, addr, addrlen)); 423 } 424 425 int WRAP(getsockopt)(int sockfd, 426 int level, 427 int optname, 428 void* optval, 429 socklen_t* optlen) { 430 ERRNO_RTN(ki_getsockopt(sockfd, level, optname, optval, optlen)); 431 } 432 433 int WRAP(setsockopt)(int sockfd, 434 int level, 435 int optname, 436 const void* optval, 437 socklen_t optlen) { 438 ERRNO_RTN(ki_setsockopt(sockfd, level, optname, optval, optlen)); 439 } 440 441 int WRAP(listen)(int sockfd, int backlog) { 442 ERRNO_RTN(ki_listen(sockfd, backlog)); 443 } 444 445 int WRAP(recv)(int sockfd, void* buf, size_t len, int flags, int* count) { 446 ssize_t signed_nread = ki_recv(sockfd, buf, len, flags); 447 *count = static_cast<int>(signed_nread); 448 RTN_ERRNO_IF(signed_nread < 0); 449 return 0; 450 } 451 452 int WRAP(recvfrom)(int sockfd, 453 void* buf, 454 size_t len, 455 int flags, 456 struct sockaddr* addr, 457 socklen_t* addrlen, 458 int* count) { 459 ssize_t signed_nread = ki_recvfrom(sockfd, buf, len, flags, addr, addrlen); 460 *count = static_cast<int>(signed_nread); 461 RTN_ERRNO_IF(signed_nread < 0); 462 return 0; 463 } 464 465 int WRAP(recvmsg)(int sockfd, struct msghdr* msg, int flags, int* count) { 466 ssize_t signed_nread = ki_recvmsg(sockfd, msg, flags); 467 *count = static_cast<int>(signed_nread); 468 RTN_ERRNO_IF(signed_nread < 0); 469 return 0; 470 } 471 472 ssize_t WRAP(send)(int sockfd, const void* buf, size_t len, int flags, 473 int* count) { 474 ssize_t signed_nread = ki_send(sockfd, buf, len, flags); 475 *count = static_cast<int>(signed_nread); 476 RTN_ERRNO_IF(signed_nread < 0); 477 return 0; 478 } 479 480 ssize_t WRAP(sendto)(int sockfd, 481 const void* buf, 482 size_t len, 483 int flags, 484 const struct sockaddr* addr, 485 socklen_t addrlen, 486 int* count) { 487 ssize_t signed_nread = ki_sendto(sockfd, buf, len, flags, addr, addrlen); 488 *count = static_cast<int>(signed_nread); 489 RTN_ERRNO_IF(signed_nread < 0); 490 return 0; 491 } 492 493 ssize_t WRAP(sendmsg)(int sockfd, 494 const struct msghdr* msg, 495 int flags, 496 int* count) { 497 ssize_t signed_nread = ki_sendmsg(sockfd, msg, flags); 498 *count = static_cast<int>(signed_nread); 499 RTN_ERRNO_IF(signed_nread < 0); 500 return 0; 501 } 502 503 int WRAP(shutdown)(int sockfd, int how) { 504 RTN_ERRNO_IF(ki_shutdown(sockfd, how) < 0); 505 return 0; 506 } 507 508 int WRAP(socket)(int domain, int type, int protocol, int* sd) { 509 *sd = ki_socket(domain, type, protocol); 510 RTN_ERRNO_IF(*sd < 0); 511 return 0; 512 } 513 514 int WRAP(socketpair)(int domain, int type, int protocol, int* sv) { 515 RTN_ERRNO_IF(ki_socketpair(domain, type, protocol, sv) < 0); 516 return 0; 517 } 518 519 static void assign_real_pointers() { 520 static bool assigned = false; 521 if (!assigned) { 522 EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR) 523 assigned = true; 524 } 525 } 526 527 #define CHECK_REAL(func) \ 528 if (!REAL(func)) { \ 529 assign_real_pointers(); \ 530 if (!REAL(func)) \ 531 return ENOSYS; \ 532 } 533 534 // "real" functions, i.e. the unwrapped original functions. 535 536 int _real_close(int fd) { 537 CHECK_REAL(close); 538 return REAL(close)(fd); 539 } 540 541 void _real_exit(int status) { 542 if (!REAL(exit)) 543 assign_real_pointers(); 544 REAL(exit)(status); 545 } 546 547 int _real_fstat(int fd, struct stat* buf) { 548 struct nacl_abi_stat st; 549 CHECK_REAL(fstat); 550 int err = REAL(fstat)(fd, &st); 551 if (err) { 552 errno = err; 553 return -1; 554 } 555 556 nacl_stat_to_stat(&st, buf); 557 return 0; 558 } 559 560 int _real_getdents(int fd, void* buf, size_t count, size_t* nread) { 561 // "buf" contains dirent(s); "nacl_buf" contains nacl_abi_dirent(s). 562 // See WRAP(getdents) above. 563 char* nacl_buf = (char*)alloca(count); 564 size_t offset = 0; 565 size_t nacl_offset = 0; 566 size_t nacl_nread; 567 CHECK_REAL(getdents); 568 int err = REAL(getdents)(fd, (dirent*)nacl_buf, count, &nacl_nread); 569 if (err) 570 return err; 571 572 while (nacl_offset < nacl_nread) { 573 dirent* d = (dirent*)((char*)buf + offset); 574 nacl_abi_dirent* nacl_d = (nacl_abi_dirent*)(nacl_buf + nacl_offset); 575 d->d_ino = nacl_d->nacl_abi_d_ino; 576 d->d_off = nacl_d->nacl_abi_d_off; 577 d->d_reclen = nacl_d->nacl_abi_d_reclen + d_name_shift; 578 size_t d_name_len = 579 nacl_d->nacl_abi_d_reclen - offsetof(nacl_abi_dirent, nacl_abi_d_name); 580 memcpy(d->d_name, nacl_d->nacl_abi_d_name, d_name_len); 581 582 offset += d->d_reclen; 583 offset += nacl_d->nacl_abi_d_reclen; 584 } 585 586 *nread = offset; 587 return 0; 588 } 589 590 int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { 591 CHECK_REAL(seek); 592 return REAL(seek)(fd, offset, whence, new_offset); 593 } 594 595 int _real_mkdir(const char* pathname, mode_t mode) { 596 CHECK_REAL(mkdir); 597 return REAL(mkdir)(pathname, mode); 598 } 599 600 int _real_mmap(void** addr, 601 size_t length, 602 int prot, 603 int flags, 604 int fd, 605 off_t offset) { 606 CHECK_REAL(mmap); 607 return REAL(mmap)(addr, length, prot, flags, fd, offset); 608 } 609 610 int _real_munmap(void* addr, size_t length) { 611 CHECK_REAL(munmap); 612 return REAL(munmap)(addr, length); 613 } 614 615 int _real_open(const char* pathname, int oflag, mode_t mode, int* newfd) { 616 CHECK_REAL(open); 617 return REAL(open)(pathname, oflag, mode, newfd); 618 } 619 620 int _real_open_resource(const char* file, int* fd) { 621 CHECK_REAL(open_resource); 622 return REAL(open_resource)(file, fd); 623 } 624 625 int _real_read(int fd, void* buf, size_t count, size_t* nread) { 626 CHECK_REAL(read); 627 return REAL(read)(fd, buf, count, nread); 628 } 629 630 int _real_rmdir(const char* pathname) { 631 CHECK_REAL(rmdir); 632 return REAL(rmdir)(pathname); 633 } 634 635 int _real_write(int fd, const void* buf, size_t count, size_t* nwrote) { 636 CHECK_REAL(write); 637 return REAL(write)(fd, buf, count, nwrote); 638 } 639 640 int _real_getcwd(char* pathname, size_t len) { 641 CHECK_REAL(getcwd); 642 return REAL(getcwd)(pathname, len); 643 } 644 645 static bool s_wrapped = false; 646 void kernel_wrap_init() { 647 if (!s_wrapped) { 648 LOG_TRACE("kernel_wrap_init"); 649 assign_real_pointers(); 650 EXPAND_SYMBOL_LIST_OPERATION(USE_WRAP) 651 s_wrapped = true; 652 } 653 } 654 655 void kernel_wrap_uninit() { 656 if (s_wrapped) { 657 LOG_TRACE("kernel_wrap_uninit"); 658 EXPAND_SYMBOL_LIST_OPERATION(USE_REAL) 659 s_wrapped = false; 660 } 661 } 662 663 EXTERN_C_END 664 665 #endif // defined(__native_client__) && defined(__GLIBC__) 666