syscall_emul.cc revision 13883
1/* 2 * Copyright (c) 2003-2005 The Regents of The University of Michigan 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer; 9 * redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution; 12 * neither the name of the copyright holders nor the names of its 13 * contributors may be used to endorse or promote products derived from 14 * this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * Authors: Steve Reinhardt 29 * Ali Saidi 30 */ 31 32#include "sim/syscall_emul.hh" 33 34#include <fcntl.h> 35#include <sys/syscall.h> 36#include <unistd.h> 37 38#include <csignal> 39#include <iostream> 40#include <mutex> 41#include <string> 42 43#include "arch/utility.hh" 44#include "base/chunk_generator.hh" 45#include "base/trace.hh" 46#include "config/the_isa.hh" 47#include "cpu/thread_context.hh" 48#include "dev/net/dist_iface.hh" 49#include "mem/page_table.hh" 50#include "sim/byteswap.hh" 51#include "sim/process.hh" 52#include "sim/sim_exit.hh" 53#include "sim/syscall_debug_macros.hh" 54#include "sim/syscall_desc.hh" 55#include "sim/system.hh" 56 57using namespace std; 58using namespace TheISA; 59 60SyscallReturn 61unimplementedFunc(SyscallDesc *desc, int callnum, Process *process, 62 ThreadContext *tc) 63{ 64 fatal("syscall %s (#%d) unimplemented.", desc->name(), callnum); 65 66 return 1; 67} 68 69 70SyscallReturn 71ignoreFunc(SyscallDesc *desc, int callnum, Process *process, 72 ThreadContext *tc) 73{ 74 if (desc->needWarning()) { 75 warn("ignoring syscall %s(...)%s", desc->name(), desc->warnOnce() ? 76 "\n (further warnings will be suppressed)" : ""); 77 } 78 79 return 0; 80} 81 82static void 83exitFutexWake(ThreadContext *tc, Addr addr, uint64_t tgid) 84{ 85 // Clear value at address pointed to by thread's childClearTID field. 86 BufferArg ctidBuf(addr, sizeof(long)); 87 long *ctid = (long *)ctidBuf.bufferPtr(); 88 *ctid = 0; 89 ctidBuf.copyOut(tc->getMemProxy()); 90 91 FutexMap &futex_map = tc->getSystemPtr()->futexMap; 92 // Wake one of the waiting threads. 93 futex_map.wakeup(addr, tgid, 1); 94} 95 96static SyscallReturn 97exitImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 98 bool group) 99{ 100 int index = 0; 101 int status = p->getSyscallArg(tc, index); 102 103 System *sys = tc->getSystemPtr(); 104 105 if (group) 106 *p->exitGroup = true; 107 108 if (p->childClearTID) 109 exitFutexWake(tc, p->childClearTID, p->tgid()); 110 111 bool last_thread = true; 112 Process *parent = nullptr, *tg_lead = nullptr; 113 for (int i = 0; last_thread && i < sys->numContexts(); i++) { 114 Process *walk; 115 if (!(walk = sys->threadContexts[i]->getProcessPtr())) 116 continue; 117 118 /** 119 * Threads in a thread group require special handing. For instance, 120 * we send the SIGCHLD signal so that it appears that it came from 121 * the head of the group. We also only delete file descriptors if 122 * we are the last thread in the thread group. 123 */ 124 if (walk->pid() == p->tgid()) 125 tg_lead = walk; 126 127 if ((sys->threadContexts[i]->status() != ThreadContext::Halted) && 128 (sys->threadContexts[i]->status() != ThreadContext::Halting) && 129 (walk != p)) { 130 /** 131 * Check if we share thread group with the pointer; this denotes 132 * that we are not the last thread active in the thread group. 133 * Note that setting this to false also prevents further 134 * iterations of the loop. 135 */ 136 if (walk->tgid() == p->tgid()) { 137 /** 138 * If p is trying to exit_group and both walk and p are in 139 * the same thread group (i.e., sharing the same tgid), 140 * we need to halt walk's thread context. After all threads 141 * except p are halted, p becomes the last thread in the 142 * group. 143 * 144 * If p is not doing exit_group and there exists another 145 * active thread context in the group, last_thread is 146 * set to false to prevent the parent thread from killing 147 * all threads in the group. 148 */ 149 if (*(p->exitGroup)) { 150 sys->threadContexts[i]->halt(); 151 } else { 152 last_thread = false; 153 } 154 } 155 156 /** 157 * A corner case exists which involves execve(). After execve(), 158 * the execve will enable SIGCHLD in the process. The problem 159 * occurs when the exiting process is the root process in the 160 * system; there is no parent to receive the signal. We obviate 161 * this problem by setting the root process' ppid to zero in the 162 * Python configuration files. We really should handle the 163 * root/execve specific case more gracefully. 164 */ 165 if (*p->sigchld && (p->ppid() != 0) && (walk->pid() == p->ppid())) 166 parent = walk; 167 } 168 } 169 170 if (last_thread) { 171 if (parent) { 172 assert(tg_lead); 173 sys->signalList.push_back(BasicSignal(tg_lead, parent, SIGCHLD)); 174 } 175 176 /** 177 * Run though FD array of the exiting process and close all file 178 * descriptors except for the standard file descriptors. 179 * (The standard file descriptors are shared with gem5.) 180 */ 181 for (int i = 0; i < p->fds->getSize(); i++) { 182 if ((*p->fds)[i]) 183 p->fds->closeFDEntry(i); 184 } 185 } 186 187 tc->halt(); 188 189 /** 190 * check to see if there is no more active thread in the system. If so, 191 * exit the simulation loop 192 */ 193 int activeContexts = 0; 194 for (auto &system: sys->systemList) 195 activeContexts += system->numRunningContexts(); 196 197 if (activeContexts == 0) { 198 /** 199 * Even though we are terminating the final thread context, dist-gem5 200 * requires the simulation to remain active and provide 201 * synchronization messages to the switch process. So we just halt 202 * the last thread context and return. The simulation will be 203 * terminated by dist-gem5 in a coordinated manner once all nodes 204 * have signaled their readiness to exit. For non dist-gem5 205 * simulations, readyToExit() always returns true. 206 */ 207 if (!DistIface::readyToExit(0)) { 208 return status; 209 } 210 211 exitSimLoop("exiting with last active thread context", status & 0xff); 212 return status; 213 } 214 215 return status; 216} 217 218SyscallReturn 219exitFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 220{ 221 return exitImpl(desc, callnum, p, tc, false); 222} 223 224SyscallReturn 225exitGroupFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 226{ 227 return exitImpl(desc, callnum, p, tc, true); 228} 229 230SyscallReturn 231getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 232{ 233 return (int)PageBytes; 234} 235 236 237SyscallReturn 238brkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 239{ 240 // change brk addr to first arg 241 int index = 0; 242 Addr new_brk = p->getSyscallArg(tc, index); 243 244 std::shared_ptr<MemState> mem_state = p->memState; 245 Addr brk_point = mem_state->getBrkPoint(); 246 247 // in Linux at least, brk(0) returns the current break value 248 // (note that the syscall and the glibc function have different behavior) 249 if (new_brk == 0) 250 return brk_point; 251 252 if (new_brk > brk_point) { 253 // might need to allocate some new pages 254 for (ChunkGenerator gen(brk_point, 255 new_brk - brk_point, 256 PageBytes); !gen.done(); gen.next()) { 257 if (!p->pTable->translate(gen.addr())) 258 p->allocateMem(roundDown(gen.addr(), PageBytes), PageBytes); 259 260 // if the address is already there, zero it out 261 else { 262 uint8_t zero = 0; 263 SETranslatingPortProxy &tp = tc->getMemProxy(); 264 265 // split non-page aligned accesses 266 Addr next_page = roundUp(gen.addr(), PageBytes); 267 uint32_t size_needed = next_page - gen.addr(); 268 tp.memsetBlob(gen.addr(), zero, size_needed); 269 if (gen.addr() + PageBytes > next_page && 270 next_page < new_brk && 271 p->pTable->translate(next_page)) { 272 size_needed = PageBytes - size_needed; 273 tp.memsetBlob(next_page, zero, size_needed); 274 } 275 } 276 } 277 } 278 279 mem_state->setBrkPoint(new_brk); 280 DPRINTF_SYSCALL(Verbose, "brk: break point changed to: %#X\n", 281 mem_state->getBrkPoint()); 282 return mem_state->getBrkPoint(); 283} 284 285SyscallReturn 286setTidAddressFunc(SyscallDesc *desc, int callnum, Process *process, 287 ThreadContext *tc) 288{ 289 int index = 0; 290 uint64_t tidPtr = process->getSyscallArg(tc, index); 291 292 process->childClearTID = tidPtr; 293 return process->pid(); 294} 295 296SyscallReturn 297closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 298{ 299 int index = 0; 300 int tgt_fd = p->getSyscallArg(tc, index); 301 302 return p->fds->closeFDEntry(tgt_fd); 303} 304 305SyscallReturn 306lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 307{ 308 int index = 0; 309 int tgt_fd = p->getSyscallArg(tc, index); 310 uint64_t offs = p->getSyscallArg(tc, index); 311 int whence = p->getSyscallArg(tc, index); 312 313 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 314 if (!ffdp) 315 return -EBADF; 316 int sim_fd = ffdp->getSimFD(); 317 318 off_t result = lseek(sim_fd, offs, whence); 319 320 return (result == (off_t)-1) ? -errno : result; 321} 322 323 324SyscallReturn 325_llseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 326{ 327 int index = 0; 328 int tgt_fd = p->getSyscallArg(tc, index); 329 uint64_t offset_high = p->getSyscallArg(tc, index); 330 uint32_t offset_low = p->getSyscallArg(tc, index); 331 Addr result_ptr = p->getSyscallArg(tc, index); 332 int whence = p->getSyscallArg(tc, index); 333 334 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 335 if (!ffdp) 336 return -EBADF; 337 int sim_fd = ffdp->getSimFD(); 338 339 uint64_t offset = (offset_high << 32) | offset_low; 340 341 uint64_t result = lseek(sim_fd, offset, whence); 342 result = TheISA::htog(result); 343 344 if (result == (off_t)-1) 345 return -errno; 346 // Assuming that the size of loff_t is 64 bits on the target platform 347 BufferArg result_buf(result_ptr, sizeof(result)); 348 memcpy(result_buf.bufferPtr(), &result, sizeof(result)); 349 result_buf.copyOut(tc->getMemProxy()); 350 return 0; 351} 352 353 354SyscallReturn 355munmapFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 356{ 357 // With mmap more fully implemented, it might be worthwhile to bite 358 // the bullet and implement munmap. Should allow us to reuse simulated 359 // memory. 360 return 0; 361} 362 363 364const char *hostname = "m5.eecs.umich.edu"; 365 366SyscallReturn 367gethostnameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 368{ 369 int index = 0; 370 Addr buf_ptr = p->getSyscallArg(tc, index); 371 int name_len = p->getSyscallArg(tc, index); 372 BufferArg name(buf_ptr, name_len); 373 374 strncpy((char *)name.bufferPtr(), hostname, name_len); 375 376 name.copyOut(tc->getMemProxy()); 377 378 return 0; 379} 380 381SyscallReturn 382getcwdFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 383{ 384 int result = 0; 385 int index = 0; 386 Addr buf_ptr = p->getSyscallArg(tc, index); 387 unsigned long size = p->getSyscallArg(tc, index); 388 BufferArg buf(buf_ptr, size); 389 390 // Is current working directory defined? 391 string cwd = p->tgtCwd; 392 if (!cwd.empty()) { 393 if (cwd.length() >= size) { 394 // Buffer too small 395 return -ERANGE; 396 } 397 strncpy((char *)buf.bufferPtr(), cwd.c_str(), size); 398 result = cwd.length(); 399 } else { 400 if (getcwd((char *)buf.bufferPtr(), size)) { 401 result = strlen((char *)buf.bufferPtr()); 402 } else { 403 result = -1; 404 } 405 } 406 407 buf.copyOut(tc->getMemProxy()); 408 409 return (result == -1) ? -errno : result; 410} 411 412SyscallReturn 413readlinkFunc(SyscallDesc *desc, int callnum, Process *process, 414 ThreadContext *tc) 415{ 416 return readlinkFunc(desc, callnum, process, tc, 0); 417} 418 419SyscallReturn 420readlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc, 421 int index) 422{ 423 string path; 424 425 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 426 return -EFAULT; 427 428 // Adjust path for cwd and redirection 429 path = p->checkPathRedirect(path); 430 431 Addr buf_ptr = p->getSyscallArg(tc, index); 432 size_t bufsiz = p->getSyscallArg(tc, index); 433 434 BufferArg buf(buf_ptr, bufsiz); 435 436 int result = -1; 437 if (path != "/proc/self/exe") { 438 result = readlink(path.c_str(), (char *)buf.bufferPtr(), bufsiz); 439 } else { 440 // Emulate readlink() called on '/proc/self/exe' should return the 441 // absolute path of the binary running in the simulated system (the 442 // Process' executable). It is possible that using this path in 443 // the simulated system will result in unexpected behavior if: 444 // 1) One binary runs another (e.g., -c time -o "my_binary"), and 445 // called binary calls readlink(). 446 // 2) The host's full path to the running benchmark changes from one 447 // simulation to another. This can result in different simulated 448 // performance since the simulated system will process the binary 449 // path differently, even if the binary itself does not change. 450 451 // Get the absolute canonical path to the running application 452 char real_path[PATH_MAX]; 453 char *check_real_path = realpath(p->progName(), real_path); 454 if (!check_real_path) { 455 fatal("readlink('/proc/self/exe') unable to resolve path to " 456 "executable: %s", p->progName()); 457 } 458 strncpy((char*)buf.bufferPtr(), real_path, bufsiz); 459 size_t real_path_len = strlen(real_path); 460 if (real_path_len > bufsiz) { 461 // readlink will truncate the contents of the 462 // path to ensure it is no more than bufsiz 463 result = bufsiz; 464 } else { 465 result = real_path_len; 466 } 467 468 // Issue a warning about potential unexpected results 469 warn_once("readlink() called on '/proc/self/exe' may yield unexpected " 470 "results in various settings.\n Returning '%s'\n", 471 (char*)buf.bufferPtr()); 472 } 473 474 buf.copyOut(tc->getMemProxy()); 475 476 return (result == -1) ? -errno : result; 477} 478 479SyscallReturn 480unlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 481{ 482 return unlinkHelper(desc, num, p, tc, 0); 483} 484 485SyscallReturn 486unlinkHelper(SyscallDesc *desc, int num, Process *p, ThreadContext *tc, 487 int index) 488{ 489 string path; 490 491 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 492 return -EFAULT; 493 494 path = p->checkPathRedirect(path); 495 496 int result = unlink(path.c_str()); 497 return (result == -1) ? -errno : result; 498} 499 500SyscallReturn 501linkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 502{ 503 string path; 504 string new_path; 505 506 int index = 0; 507 auto &virt_mem = tc->getMemProxy(); 508 if (!virt_mem.tryReadString(path, p->getSyscallArg(tc, index))) 509 return -EFAULT; 510 if (!virt_mem.tryReadString(new_path, p->getSyscallArg(tc, index))) 511 return -EFAULT; 512 513 path = p->absolutePath(path, true); 514 new_path = p->absolutePath(new_path, true); 515 516 int result = link(path.c_str(), new_path.c_str()); 517 return (result == -1) ? -errno : result; 518} 519 520SyscallReturn 521symlinkFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 522{ 523 string path; 524 string new_path; 525 526 int index = 0; 527 auto &virt_mem = tc->getMemProxy(); 528 if (!virt_mem.tryReadString(path, p->getSyscallArg(tc, index))) 529 return -EFAULT; 530 if (!virt_mem.tryReadString(new_path, p->getSyscallArg(tc, index))) 531 return -EFAULT; 532 533 path = p->absolutePath(path, true); 534 new_path = p->absolutePath(new_path, true); 535 536 int result = symlink(path.c_str(), new_path.c_str()); 537 return (result == -1) ? -errno : result; 538} 539 540SyscallReturn 541mkdirFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 542{ 543 int index = 0; 544 std::string path; 545 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 546 return -EFAULT; 547 548 path = p->checkPathRedirect(path); 549 mode_t mode = p->getSyscallArg(tc, index); 550 551 auto result = mkdir(path.c_str(), mode); 552 return (result == -1) ? -errno : result; 553} 554 555SyscallReturn 556renameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 557{ 558 string old_name; 559 560 int index = 0; 561 if (!tc->getMemProxy().tryReadString(old_name, p->getSyscallArg(tc, index))) 562 return -EFAULT; 563 564 string new_name; 565 566 if (!tc->getMemProxy().tryReadString(new_name, p->getSyscallArg(tc, index))) 567 return -EFAULT; 568 569 // Adjust path for cwd and redirection 570 old_name = p->checkPathRedirect(old_name); 571 new_name = p->checkPathRedirect(new_name); 572 573 int64_t result = rename(old_name.c_str(), new_name.c_str()); 574 return (result == -1) ? -errno : result; 575} 576 577SyscallReturn 578truncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 579{ 580 string path; 581 582 int index = 0; 583 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 584 return -EFAULT; 585 586 off_t length = p->getSyscallArg(tc, index); 587 588 // Adjust path for cwd and redirection 589 path = p->checkPathRedirect(path); 590 591 int result = truncate(path.c_str(), length); 592 return (result == -1) ? -errno : result; 593} 594 595SyscallReturn 596ftruncateFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 597{ 598 int index = 0; 599 int tgt_fd = p->getSyscallArg(tc, index); 600 off_t length = p->getSyscallArg(tc, index); 601 602 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 603 if (!ffdp) 604 return -EBADF; 605 int sim_fd = ffdp->getSimFD(); 606 607 int result = ftruncate(sim_fd, length); 608 return (result == -1) ? -errno : result; 609} 610 611SyscallReturn 612truncate64Func(SyscallDesc *desc, int num, 613 Process *process, ThreadContext *tc) 614{ 615 int index = 0; 616 string path; 617 618 if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) 619 return -EFAULT; 620 621 int64_t length = process->getSyscallArg(tc, index, 64); 622 623 // Adjust path for cwd and redirection 624 path = process->checkPathRedirect(path); 625 626#if NO_STAT64 627 int result = truncate(path.c_str(), length); 628#else 629 int result = truncate64(path.c_str(), length); 630#endif 631 return (result == -1) ? -errno : result; 632} 633 634SyscallReturn 635ftruncate64Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 636{ 637 int index = 0; 638 int tgt_fd = p->getSyscallArg(tc, index); 639 int64_t length = p->getSyscallArg(tc, index, 64); 640 641 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 642 if (!ffdp) 643 return -EBADF; 644 int sim_fd = ffdp->getSimFD(); 645 646#if NO_STAT64 647 int result = ftruncate(sim_fd, length); 648#else 649 int result = ftruncate64(sim_fd, length); 650#endif 651 return (result == -1) ? -errno : result; 652} 653 654SyscallReturn 655umaskFunc(SyscallDesc *desc, int num, Process *process, ThreadContext *tc) 656{ 657 // Letting the simulated program change the simulator's umask seems like 658 // a bad idea. Compromise by just returning the current umask but not 659 // changing anything. 660 mode_t oldMask = umask(0); 661 umask(oldMask); 662 return (int)oldMask; 663} 664 665SyscallReturn 666chownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 667{ 668 string path; 669 670 int index = 0; 671 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 672 return -EFAULT; 673 674 /* XXX endianess */ 675 uint32_t owner = p->getSyscallArg(tc, index); 676 uid_t hostOwner = owner; 677 uint32_t group = p->getSyscallArg(tc, index); 678 gid_t hostGroup = group; 679 680 // Adjust path for cwd and redirection 681 path = p->checkPathRedirect(path); 682 683 int result = chown(path.c_str(), hostOwner, hostGroup); 684 return (result == -1) ? -errno : result; 685} 686 687SyscallReturn 688fchownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 689{ 690 int index = 0; 691 int tgt_fd = p->getSyscallArg(tc, index); 692 693 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 694 if (!ffdp) 695 return -EBADF; 696 int sim_fd = ffdp->getSimFD(); 697 698 /* XXX endianess */ 699 uint32_t owner = p->getSyscallArg(tc, index); 700 uid_t hostOwner = owner; 701 uint32_t group = p->getSyscallArg(tc, index); 702 gid_t hostGroup = group; 703 704 int result = fchown(sim_fd, hostOwner, hostGroup); 705 return (result == -1) ? -errno : result; 706} 707 708/** 709 * FIXME: The file description is not shared among file descriptors created 710 * with dup. Really, it's difficult to maintain fields like file offset or 711 * flags since an update to such a field won't be reflected in the metadata 712 * for the fd entries that we maintain for checkpoint restoration. 713 */ 714SyscallReturn 715dupFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 716{ 717 int index = 0; 718 int tgt_fd = p->getSyscallArg(tc, index); 719 720 auto old_hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 721 if (!old_hbfdp) 722 return -EBADF; 723 int sim_fd = old_hbfdp->getSimFD(); 724 725 int result = dup(sim_fd); 726 if (result == -1) 727 return -errno; 728 729 auto new_hbfdp = std::dynamic_pointer_cast<HBFDEntry>(old_hbfdp->clone()); 730 new_hbfdp->setSimFD(result); 731 new_hbfdp->setCOE(false); 732 return p->fds->allocFD(new_hbfdp); 733} 734 735SyscallReturn 736dup2Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 737{ 738 int index = 0; 739 740 int old_tgt_fd = p->getSyscallArg(tc, index); 741 auto old_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[old_tgt_fd]); 742 if (!old_hbp) 743 return -EBADF; 744 int old_sim_fd = old_hbp->getSimFD(); 745 746 /** 747 * We need a valid host file descriptor number to be able to pass into 748 * the second parameter for dup2 (newfd), but we don't know what the 749 * viable numbers are; we execute the open call to retrieve one. 750 */ 751 int res_fd = dup2(old_sim_fd, open("/dev/null", O_RDONLY)); 752 if (res_fd == -1) 753 return -errno; 754 755 int new_tgt_fd = p->getSyscallArg(tc, index); 756 auto new_hbp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[new_tgt_fd]); 757 if (new_hbp) 758 p->fds->closeFDEntry(new_tgt_fd); 759 new_hbp = std::dynamic_pointer_cast<HBFDEntry>(old_hbp->clone()); 760 new_hbp->setSimFD(res_fd); 761 new_hbp->setCOE(false); 762 763 return p->fds->allocFD(new_hbp); 764} 765 766SyscallReturn 767fcntlFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 768{ 769 int arg; 770 int index = 0; 771 int tgt_fd = p->getSyscallArg(tc, index); 772 int cmd = p->getSyscallArg(tc, index); 773 774 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 775 if (!hbfdp) 776 return -EBADF; 777 int sim_fd = hbfdp->getSimFD(); 778 779 int coe = hbfdp->getCOE(); 780 781 switch (cmd) { 782 case F_GETFD: 783 return coe & FD_CLOEXEC; 784 785 case F_SETFD: { 786 arg = p->getSyscallArg(tc, index); 787 arg ? hbfdp->setCOE(true) : hbfdp->setCOE(false); 788 return 0; 789 } 790 791 // Rely on the host to maintain the file status flags for this file 792 // description rather than maintain it ourselves. Admittedly, this 793 // is suboptimal (and possibly error prone), but it is difficult to 794 // maintain the flags by tracking them across the different descriptors 795 // (that refer to this file description) caused by clone, dup, and 796 // subsequent fcntls. 797 case F_GETFL: 798 case F_SETFL: { 799 arg = p->getSyscallArg(tc, index); 800 int rv = fcntl(sim_fd, cmd, arg); 801 return (rv == -1) ? -errno : rv; 802 } 803 804 default: 805 warn("fcntl: unsupported command %d\n", cmd); 806 return 0; 807 } 808} 809 810SyscallReturn 811fcntl64Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 812{ 813 int index = 0; 814 int tgt_fd = p->getSyscallArg(tc, index); 815 816 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 817 if (!hbfdp) 818 return -EBADF; 819 int sim_fd = hbfdp->getSimFD(); 820 821 int cmd = p->getSyscallArg(tc, index); 822 switch (cmd) { 823 case 33: //F_GETLK64 824 warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", tgt_fd); 825 return -EMFILE; 826 827 case 34: // F_SETLK64 828 case 35: // F_SETLKW64 829 warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", 830 tgt_fd); 831 return -EMFILE; 832 833 default: 834 // not sure if this is totally valid, but we'll pass it through 835 // to the underlying OS 836 warn("fcntl64(%d, %d) passed through to host\n", tgt_fd, cmd); 837 return fcntl(sim_fd, cmd); 838 } 839} 840 841SyscallReturn 842pipeImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 843 bool pseudoPipe) 844{ 845 Addr tgt_addr = 0; 846 if (!pseudoPipe) { 847 int index = 0; 848 tgt_addr = p->getSyscallArg(tc, index); 849 } 850 851 int sim_fds[2], tgt_fds[2]; 852 853 int pipe_retval = pipe(sim_fds); 854 if (pipe_retval == -1) 855 return -errno; 856 857 auto rend = PipeFDEntry::EndType::read; 858 auto rpfd = std::make_shared<PipeFDEntry>(sim_fds[0], O_WRONLY, rend); 859 tgt_fds[0] = p->fds->allocFD(rpfd); 860 861 auto wend = PipeFDEntry::EndType::write; 862 auto wpfd = std::make_shared<PipeFDEntry>(sim_fds[1], O_RDONLY, wend); 863 tgt_fds[1] = p->fds->allocFD(wpfd); 864 865 /** 866 * Now patch the read object to record the target file descriptor chosen 867 * as the write end of the pipe. 868 */ 869 rpfd->setPipeReadSource(tgt_fds[1]); 870 871 /** 872 * Alpha Linux convention for pipe() is that fd[0] is returned as 873 * the return value of the function, and fd[1] is returned in r20. 874 */ 875 if (pseudoPipe) { 876 tc->setIntReg(SyscallPseudoReturnReg, tgt_fds[1]); 877 return tgt_fds[0]; 878 } 879 880 /** 881 * Copy the target file descriptors into buffer space and then copy 882 * the buffer space back into the target address space. 883 */ 884 BufferArg tgt_handle(tgt_addr, sizeof(int[2])); 885 int *buf_ptr = (int*)tgt_handle.bufferPtr(); 886 buf_ptr[0] = tgt_fds[0]; 887 buf_ptr[1] = tgt_fds[1]; 888 tgt_handle.copyOut(tc->getMemProxy()); 889 return 0; 890} 891 892SyscallReturn 893pipePseudoFunc(SyscallDesc *desc, int callnum, Process *process, 894 ThreadContext *tc) 895{ 896 return pipeImpl(desc, callnum, process, tc, true); 897} 898 899SyscallReturn 900pipeFunc(SyscallDesc *desc, int callnum, Process *process, ThreadContext *tc) 901{ 902 return pipeImpl(desc, callnum, process, tc, false); 903} 904 905SyscallReturn 906setpgidFunc(SyscallDesc *desc, int callnum, Process *process, 907 ThreadContext *tc) 908{ 909 int index = 0; 910 int pid = process->getSyscallArg(tc, index); 911 int pgid = process->getSyscallArg(tc, index); 912 913 if (pgid < 0) 914 return -EINVAL; 915 916 if (pid == 0) { 917 process->setpgid(process->pid()); 918 return 0; 919 } 920 921 Process *matched_ph = nullptr; 922 System *sysh = tc->getSystemPtr(); 923 924 // Retrieves process pointer from active/suspended thread contexts. 925 for (int i = 0; i < sysh->numContexts(); i++) { 926 if (sysh->threadContexts[i]->status() != ThreadContext::Halted) { 927 Process *temp_h = sysh->threadContexts[i]->getProcessPtr(); 928 Process *walk_ph = (Process*)temp_h; 929 930 if (walk_ph && walk_ph->pid() == process->pid()) 931 matched_ph = walk_ph; 932 } 933 } 934 935 assert(matched_ph); 936 matched_ph->setpgid((pgid == 0) ? matched_ph->pid() : pgid); 937 938 return 0; 939} 940 941SyscallReturn 942getpidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 943 ThreadContext *tc) 944{ 945 // Make up a PID. There's no interprocess communication in 946 // fake_syscall mode, so there's no way for a process to know it's 947 // not getting a unique value. 948 949 tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); 950 return process->pid(); 951} 952 953 954SyscallReturn 955getuidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 956 ThreadContext *tc) 957{ 958 // Make up a UID and EUID... it shouldn't matter, and we want the 959 // simulation to be deterministic. 960 961 // EUID goes in r20. 962 tc->setIntReg(SyscallPseudoReturnReg, process->euid()); // EUID 963 return process->uid(); // UID 964} 965 966 967SyscallReturn 968getgidPseudoFunc(SyscallDesc *desc, int callnum, Process *process, 969 ThreadContext *tc) 970{ 971 // Get current group ID. EGID goes in r20. 972 tc->setIntReg(SyscallPseudoReturnReg, process->egid()); // EGID 973 return process->gid(); 974} 975 976 977SyscallReturn 978setuidFunc(SyscallDesc *desc, int callnum, Process *process, 979 ThreadContext *tc) 980{ 981 // can't fathom why a benchmark would call this. 982 int index = 0; 983 warn("Ignoring call to setuid(%d)\n", process->getSyscallArg(tc, index)); 984 return 0; 985} 986 987SyscallReturn 988getpidFunc(SyscallDesc *desc, int callnum, Process *process, 989 ThreadContext *tc) 990{ 991 return process->tgid(); 992} 993 994SyscallReturn 995gettidFunc(SyscallDesc *desc, int callnum, Process *process, 996 ThreadContext *tc) 997{ 998 return process->pid(); 999} 1000 1001SyscallReturn 1002getppidFunc(SyscallDesc *desc, int callnum, Process *process, 1003 ThreadContext *tc) 1004{ 1005 return process->ppid(); 1006} 1007 1008SyscallReturn 1009getuidFunc(SyscallDesc *desc, int callnum, Process *process, 1010 ThreadContext *tc) 1011{ 1012 return process->uid(); // UID 1013} 1014 1015SyscallReturn 1016geteuidFunc(SyscallDesc *desc, int callnum, Process *process, 1017 ThreadContext *tc) 1018{ 1019 return process->euid(); // UID 1020} 1021 1022SyscallReturn 1023getgidFunc(SyscallDesc *desc, int callnum, Process *process, 1024 ThreadContext *tc) 1025{ 1026 return process->gid(); 1027} 1028 1029SyscallReturn 1030getegidFunc(SyscallDesc *desc, int callnum, Process *process, 1031 ThreadContext *tc) 1032{ 1033 return process->egid(); 1034} 1035 1036SyscallReturn 1037fallocateFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1038{ 1039#if NO_FALLOCATE 1040 warn("Host OS cannot support calls to fallocate. Ignoring syscall"); 1041#else 1042 int index = 0; 1043 int tgt_fd = p->getSyscallArg(tc, index); 1044 int mode = p->getSyscallArg(tc, index); 1045 off_t offset = p->getSyscallArg(tc, index); 1046 off_t len = p->getSyscallArg(tc, index); 1047 1048 auto ffdp = std::dynamic_pointer_cast<FileFDEntry>((*p->fds)[tgt_fd]); 1049 if (!ffdp) 1050 return -EBADF; 1051 int sim_fd = ffdp->getSimFD(); 1052 1053 int result = fallocate(sim_fd, mode, offset, len); 1054 if (result < 0) 1055 return -errno; 1056#endif 1057 return 0; 1058} 1059 1060SyscallReturn 1061accessFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc, 1062 int index) 1063{ 1064 string path; 1065 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1066 return -EFAULT; 1067 1068 // Adjust path for cwd and redirection 1069 path = p->checkPathRedirect(path); 1070 1071 mode_t mode = p->getSyscallArg(tc, index); 1072 1073 int result = access(path.c_str(), mode); 1074 return (result == -1) ? -errno : result; 1075} 1076 1077SyscallReturn 1078accessFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1079{ 1080 return accessFunc(desc, callnum, p, tc, 0); 1081} 1082 1083SyscallReturn 1084mknodFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1085{ 1086 int index = 0; 1087 std::string path; 1088 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1089 return -EFAULT; 1090 1091 path = p->checkPathRedirect(path); 1092 mode_t mode = p->getSyscallArg(tc, index); 1093 dev_t dev = p->getSyscallArg(tc, index); 1094 1095 auto result = mknod(path.c_str(), mode, dev); 1096 return (result == -1) ? -errno : result; 1097} 1098 1099SyscallReturn 1100chdirFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1101{ 1102 int index = 0; 1103 std::string path; 1104 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1105 return -EFAULT; 1106 1107 std::string tgt_cwd; 1108 if (startswith(path, "/")) { 1109 tgt_cwd = path; 1110 } else { 1111 char buf[PATH_MAX]; 1112 tgt_cwd = realpath((p->tgtCwd + "/" + path).c_str(), buf); 1113 } 1114 std::string host_cwd = p->checkPathRedirect(tgt_cwd); 1115 1116 int result = chdir(host_cwd.c_str()); 1117 1118 if (result == -1) 1119 return -errno; 1120 1121 p->hostCwd = host_cwd; 1122 p->tgtCwd = tgt_cwd; 1123 return result; 1124} 1125 1126SyscallReturn 1127rmdirFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1128{ 1129 int index = 0; 1130 std::string path; 1131 if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index))) 1132 return -EFAULT; 1133 1134 path = p->checkPathRedirect(path); 1135 1136 auto result = rmdir(path.c_str()); 1137 return (result == -1) ? -errno : result; 1138} 1139 1140#if defined(SYS_getdents) || defined(SYS_getdents64) 1141template<typename DE, int SYS_NUM> 1142static SyscallReturn 1143getdentsImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1144{ 1145 int index = 0; 1146 int tgt_fd = p->getSyscallArg(tc, index); 1147 Addr buf_ptr = p->getSyscallArg(tc, index); 1148 unsigned count = p->getSyscallArg(tc, index); 1149 1150 auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>((*p->fds)[tgt_fd]); 1151 if (!hbfdp) 1152 return -EBADF; 1153 int sim_fd = hbfdp->getSimFD(); 1154 1155 BufferArg buf_arg(buf_ptr, count); 1156 auto status = syscall(SYS_NUM, sim_fd, buf_arg.bufferPtr(), count); 1157 1158 if (status == -1) 1159 return -errno; 1160 1161 unsigned traversed = 0; 1162 while (traversed < status) { 1163 DE *buffer = (DE*)((Addr)buf_arg.bufferPtr() + traversed); 1164 1165 auto host_reclen = buffer->d_reclen; 1166 1167 /** 1168 * Convert the byte ordering from the host to the target before 1169 * passing the data back into the target's address space to preserve 1170 * endianness. 1171 */ 1172 buffer->d_ino = htog(buffer->d_ino); 1173 buffer->d_off = htog(buffer->d_off); 1174 buffer->d_reclen = htog(buffer->d_reclen); 1175 1176 traversed += host_reclen; 1177 } 1178 1179 buf_arg.copyOut(tc->getMemProxy()); 1180 return status; 1181} 1182#endif 1183 1184#if defined(SYS_getdents) 1185SyscallReturn 1186getdentsFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1187{ 1188 typedef struct linux_dirent { 1189 unsigned long d_ino; 1190 unsigned long d_off; 1191 unsigned short d_reclen; 1192 char dname[]; 1193 } LinDent; 1194 1195 return getdentsImpl<LinDent, SYS_getdents>(desc, callnum, p, tc); 1196} 1197#endif 1198 1199#if defined(SYS_getdents64) 1200SyscallReturn 1201getdents64Func(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) 1202{ 1203 typedef struct linux_dirent64 { 1204 ino64_t d_ino; 1205 off64_t d_off; 1206 unsigned short d_reclen; 1207 char dname[]; 1208 } LinDent64; 1209 1210 return getdentsImpl<LinDent64, SYS_getdents64>(desc, callnum, p, tc); 1211} 1212#endif 1213 1214SyscallReturn 1215shutdownFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1216{ 1217 int index = 0; 1218 int tgt_fd = p->getSyscallArg(tc, index); 1219 int how = p->getSyscallArg(tc, index); 1220 1221 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1222 if (!sfdp) 1223 return -EBADF; 1224 int sim_fd = sfdp->getSimFD(); 1225 1226 int retval = shutdown(sim_fd, how); 1227 1228 return (retval == -1) ? -errno : retval; 1229} 1230 1231SyscallReturn 1232bindFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1233{ 1234 int index = 0; 1235 int tgt_fd = p->getSyscallArg(tc, index); 1236 Addr buf_ptr = p->getSyscallArg(tc, index); 1237 int addrlen = p->getSyscallArg(tc, index); 1238 1239 BufferArg bufSock(buf_ptr, addrlen); 1240 bufSock.copyIn(tc->getMemProxy()); 1241 1242 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1243 if (!sfdp) 1244 return -EBADF; 1245 int sim_fd = sfdp->getSimFD(); 1246 1247 int status = ::bind(sim_fd, 1248 (struct sockaddr *)bufSock.bufferPtr(), 1249 addrlen); 1250 1251 return (status == -1) ? -errno : status; 1252} 1253 1254SyscallReturn 1255listenFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1256{ 1257 int index = 0; 1258 int tgt_fd = p->getSyscallArg(tc, index); 1259 int backlog = p->getSyscallArg(tc, index); 1260 1261 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1262 if (!sfdp) 1263 return -EBADF; 1264 int sim_fd = sfdp->getSimFD(); 1265 1266 int status = listen(sim_fd, backlog); 1267 1268 return (status == -1) ? -errno : status; 1269} 1270 1271SyscallReturn 1272connectFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1273{ 1274 int index = 0; 1275 int tgt_fd = p->getSyscallArg(tc, index); 1276 Addr buf_ptr = p->getSyscallArg(tc, index); 1277 int addrlen = p->getSyscallArg(tc, index); 1278 1279 BufferArg addr(buf_ptr, addrlen); 1280 addr.copyIn(tc->getMemProxy()); 1281 1282 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1283 if (!sfdp) 1284 return -EBADF; 1285 int sim_fd = sfdp->getSimFD(); 1286 1287 int status = connect(sim_fd, 1288 (struct sockaddr *)addr.bufferPtr(), 1289 (socklen_t)addrlen); 1290 1291 return (status == -1) ? -errno : status; 1292} 1293 1294SyscallReturn 1295recvfromFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1296{ 1297 int index = 0; 1298 int tgt_fd = p->getSyscallArg(tc, index); 1299 Addr bufrPtr = p->getSyscallArg(tc, index); 1300 size_t bufrLen = p->getSyscallArg(tc, index); 1301 int flags = p->getSyscallArg(tc, index); 1302 Addr addrPtr = p->getSyscallArg(tc, index); 1303 Addr addrlenPtr = p->getSyscallArg(tc, index); 1304 1305 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1306 if (!sfdp) 1307 return -EBADF; 1308 int sim_fd = sfdp->getSimFD(); 1309 1310 // Reserve buffer space. 1311 BufferArg bufrBuf(bufrPtr, bufrLen); 1312 1313 // Get address length. 1314 socklen_t addrLen = 0; 1315 if (addrlenPtr != 0) { 1316 // Read address length parameter. 1317 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t)); 1318 addrlenBuf.copyIn(tc->getMemProxy()); 1319 addrLen = *((socklen_t *)addrlenBuf.bufferPtr()); 1320 } 1321 1322 struct sockaddr sa, *sap = NULL; 1323 if (addrLen != 0) { 1324 BufferArg addrBuf(addrPtr, addrLen); 1325 addrBuf.copyIn(tc->getMemProxy()); 1326 memcpy(&sa, (struct sockaddr *)addrBuf.bufferPtr(), 1327 sizeof(struct sockaddr)); 1328 sap = &sa; 1329 } 1330 1331 ssize_t recvd_size = recvfrom(sim_fd, 1332 (void *)bufrBuf.bufferPtr(), 1333 bufrLen, flags, sap, (socklen_t *)&addrLen); 1334 1335 if (recvd_size == -1) 1336 return -errno; 1337 1338 // Pass the received data out. 1339 bufrBuf.copyOut(tc->getMemProxy()); 1340 1341 // Copy address to addrPtr and pass it on. 1342 if (sap != NULL) { 1343 BufferArg addrBuf(addrPtr, addrLen); 1344 memcpy(addrBuf.bufferPtr(), sap, sizeof(sa)); 1345 addrBuf.copyOut(tc->getMemProxy()); 1346 } 1347 1348 // Copy len to addrlenPtr and pass it on. 1349 if (addrLen != 0) { 1350 BufferArg addrlenBuf(addrlenPtr, sizeof(socklen_t)); 1351 *(socklen_t *)addrlenBuf.bufferPtr() = addrLen; 1352 addrlenBuf.copyOut(tc->getMemProxy()); 1353 } 1354 1355 return recvd_size; 1356} 1357 1358SyscallReturn 1359sendtoFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1360{ 1361 int index = 0; 1362 int tgt_fd = p->getSyscallArg(tc, index); 1363 Addr bufrPtr = p->getSyscallArg(tc, index); 1364 size_t bufrLen = p->getSyscallArg(tc, index); 1365 int flags = p->getSyscallArg(tc, index); 1366 Addr addrPtr = p->getSyscallArg(tc, index); 1367 socklen_t addrLen = p->getSyscallArg(tc, index); 1368 1369 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1370 if (!sfdp) 1371 return -EBADF; 1372 int sim_fd = sfdp->getSimFD(); 1373 1374 // Reserve buffer space. 1375 BufferArg bufrBuf(bufrPtr, bufrLen); 1376 bufrBuf.copyIn(tc->getMemProxy()); 1377 1378 struct sockaddr sa, *sap = nullptr; 1379 memset(&sa, 0, sizeof(sockaddr)); 1380 if (addrLen != 0) { 1381 BufferArg addrBuf(addrPtr, addrLen); 1382 addrBuf.copyIn(tc->getMemProxy()); 1383 memcpy(&sa, (sockaddr*)addrBuf.bufferPtr(), addrLen); 1384 sap = &sa; 1385 } 1386 1387 ssize_t sent_size = sendto(sim_fd, 1388 (void *)bufrBuf.bufferPtr(), 1389 bufrLen, flags, sap, (socklen_t)addrLen); 1390 1391 return (sent_size == -1) ? -errno : sent_size; 1392} 1393 1394SyscallReturn 1395recvmsgFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1396{ 1397 int index = 0; 1398 int tgt_fd = p->getSyscallArg(tc, index); 1399 Addr msgPtr = p->getSyscallArg(tc, index); 1400 int flags = p->getSyscallArg(tc, index); 1401 1402 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1403 if (!sfdp) 1404 return -EBADF; 1405 int sim_fd = sfdp->getSimFD(); 1406 1407 /** 1408 * struct msghdr { 1409 * void *msg_name; // optional address 1410 * socklen_t msg_namelen; // size of address 1411 * struct iovec *msg_iov; // iovec array 1412 * size_t msg_iovlen; // number entries in msg_iov 1413 * i // entries correspond to buffer 1414 * void *msg_control; // ancillary data 1415 * size_t msg_controllen; // ancillary data buffer len 1416 * int msg_flags; // flags on received message 1417 * }; 1418 * 1419 * struct iovec { 1420 * void *iov_base; // starting address 1421 * size_t iov_len; // number of bytes to transfer 1422 * }; 1423 */ 1424 1425 /** 1426 * The plan with this system call is to replace all of the pointers in the 1427 * structure and the substructure with BufferArg class pointers. We will 1428 * copy every field from the structures into our BufferArg classes. 1429 */ 1430 BufferArg msgBuf(msgPtr, sizeof(struct msghdr)); 1431 msgBuf.copyIn(tc->getMemProxy()); 1432 struct msghdr *msgHdr = (struct msghdr *)msgBuf.bufferPtr(); 1433 1434 /** 1435 * We will use these address place holders to retain the pointers which 1436 * we are going to replace with our own buffers in our simulator address 1437 * space. 1438 */ 1439 Addr msg_name_phold = 0; 1440 Addr msg_iov_phold = 0; 1441 Addr iovec_base_phold[msgHdr->msg_iovlen]; 1442 Addr msg_control_phold = 0; 1443 1444 /** 1445 * Record msg_name pointer then replace with buffer pointer. 1446 */ 1447 BufferArg *nameBuf = NULL; 1448 if (msgHdr->msg_name) { 1449 /*1*/msg_name_phold = (Addr)msgHdr->msg_name; 1450 /*2*/nameBuf = new BufferArg(msg_name_phold, msgHdr->msg_namelen); 1451 /*3*/nameBuf->copyIn(tc->getMemProxy()); 1452 /*4*/msgHdr->msg_name = nameBuf->bufferPtr(); 1453 } 1454 1455 /** 1456 * Record msg_iov pointer then replace with buffer pointer. Also, setup 1457 * an array of buffer pointers for the iovec structs record and replace 1458 * their pointers with buffer pointers. 1459 */ 1460 BufferArg *iovBuf = NULL; 1461 BufferArg *iovecBuf[msgHdr->msg_iovlen]; 1462 for (int i = 0; i < msgHdr->msg_iovlen; i++) { 1463 iovec_base_phold[i] = 0; 1464 iovecBuf[i] = NULL; 1465 } 1466 1467 if (msgHdr->msg_iov) { 1468 /*1*/msg_iov_phold = (Addr)msgHdr->msg_iov; 1469 /*2*/iovBuf = new BufferArg(msg_iov_phold, msgHdr->msg_iovlen * 1470 sizeof(struct iovec)); 1471 /*3*/iovBuf->copyIn(tc->getMemProxy()); 1472 for (int i = 0; i < msgHdr->msg_iovlen; i++) { 1473 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) { 1474 /*1*/iovec_base_phold[i] = 1475 (Addr)((struct iovec *)iovBuf->bufferPtr())[i].iov_base; 1476 /*2*/iovecBuf[i] = new BufferArg(iovec_base_phold[i], 1477 ((struct iovec *)iovBuf->bufferPtr())[i].iov_len); 1478 /*3*/iovecBuf[i]->copyIn(tc->getMemProxy()); 1479 /*4*/((struct iovec *)iovBuf->bufferPtr())[i].iov_base = 1480 iovecBuf[i]->bufferPtr(); 1481 } 1482 } 1483 /*4*/msgHdr->msg_iov = (struct iovec *)iovBuf->bufferPtr(); 1484 } 1485 1486 /** 1487 * Record msg_control pointer then replace with buffer pointer. 1488 */ 1489 BufferArg *controlBuf = NULL; 1490 if (msgHdr->msg_control) { 1491 /*1*/msg_control_phold = (Addr)msgHdr->msg_control; 1492 /*2*/controlBuf = new BufferArg(msg_control_phold, 1493 CMSG_ALIGN(msgHdr->msg_controllen)); 1494 /*3*/controlBuf->copyIn(tc->getMemProxy()); 1495 /*4*/msgHdr->msg_control = controlBuf->bufferPtr(); 1496 } 1497 1498 ssize_t recvd_size = recvmsg(sim_fd, msgHdr, flags); 1499 1500 if (recvd_size < 0) 1501 return -errno; 1502 1503 if (msgHdr->msg_name) { 1504 nameBuf->copyOut(tc->getMemProxy()); 1505 delete(nameBuf); 1506 msgHdr->msg_name = (void *)msg_name_phold; 1507 } 1508 1509 if (msgHdr->msg_iov) { 1510 for (int i = 0; i< msgHdr->msg_iovlen; i++) { 1511 if (((struct iovec *)iovBuf->bufferPtr())[i].iov_base) { 1512 iovecBuf[i]->copyOut(tc->getMemProxy()); 1513 delete iovecBuf[i]; 1514 ((struct iovec *)iovBuf->bufferPtr())[i].iov_base = 1515 (void *)iovec_base_phold[i]; 1516 } 1517 } 1518 iovBuf->copyOut(tc->getMemProxy()); 1519 delete iovBuf; 1520 msgHdr->msg_iov = (struct iovec *)msg_iov_phold; 1521 } 1522 1523 if (msgHdr->msg_control) { 1524 controlBuf->copyOut(tc->getMemProxy()); 1525 delete(controlBuf); 1526 msgHdr->msg_control = (void *)msg_control_phold; 1527 } 1528 1529 msgBuf.copyOut(tc->getMemProxy()); 1530 1531 return recvd_size; 1532} 1533 1534SyscallReturn 1535sendmsgFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1536{ 1537 int index = 0; 1538 int tgt_fd = p->getSyscallArg(tc, index); 1539 Addr msgPtr = p->getSyscallArg(tc, index); 1540 int flags = p->getSyscallArg(tc, index); 1541 1542 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1543 if (!sfdp) 1544 return -EBADF; 1545 int sim_fd = sfdp->getSimFD(); 1546 1547 /** 1548 * Reserve buffer space. 1549 */ 1550 BufferArg msgBuf(msgPtr, sizeof(struct msghdr)); 1551 msgBuf.copyIn(tc->getMemProxy()); 1552 struct msghdr msgHdr = *((struct msghdr *)msgBuf.bufferPtr()); 1553 1554 /** 1555 * Assuming msgHdr.msg_iovlen >= 1, then there is no point calling 1556 * recvmsg without a buffer. 1557 */ 1558 struct iovec *iovPtr = msgHdr.msg_iov; 1559 BufferArg iovBuf((Addr)iovPtr, sizeof(struct iovec) * msgHdr.msg_iovlen); 1560 iovBuf.copyIn(tc->getMemProxy()); 1561 struct iovec *iov = (struct iovec *)iovBuf.bufferPtr(); 1562 msgHdr.msg_iov = iov; 1563 1564 /** 1565 * Cannot instantiate buffers till inside the loop. 1566 * Create array to hold buffer addresses, to be used during copyIn of 1567 * send data. 1568 */ 1569 BufferArg **bufferArray = (BufferArg **)malloc(msgHdr.msg_iovlen 1570 * sizeof(BufferArg *)); 1571 1572 /** 1573 * Iterate through the iovec structures: 1574 * Get the base buffer addreses, reserve iov_len amount of space for each. 1575 * Put the buf address into the bufferArray for later retrieval. 1576 */ 1577 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) { 1578 Addr basePtr = (Addr) iov[iovIndex].iov_base; 1579 bufferArray[iovIndex] = new BufferArg(basePtr, iov[iovIndex].iov_len); 1580 bufferArray[iovIndex]->copyIn(tc->getMemProxy()); 1581 iov[iovIndex].iov_base = bufferArray[iovIndex]->bufferPtr(); 1582 } 1583 1584 ssize_t sent_size = sendmsg(sim_fd, &msgHdr, flags); 1585 int local_errno = errno; 1586 1587 /** 1588 * Free dynamically allocated memory. 1589 */ 1590 for (int iovIndex = 0 ; iovIndex < msgHdr.msg_iovlen; iovIndex++) { 1591 BufferArg *baseBuf = ( BufferArg *)bufferArray[iovIndex]; 1592 delete(baseBuf); 1593 } 1594 1595 /** 1596 * Malloced above. 1597 */ 1598 free(bufferArray); 1599 1600 return (sent_size < 0) ? -local_errno : sent_size; 1601} 1602 1603SyscallReturn 1604getsockoptFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1605{ 1606 // union of all possible return value types from getsockopt 1607 union val { 1608 int i_val; 1609 long l_val; 1610 struct linger linger_val; 1611 struct timeval timeval_val; 1612 } val; 1613 1614 int index = 0; 1615 int tgt_fd = p->getSyscallArg(tc, index); 1616 int level = p->getSyscallArg(tc, index); 1617 int optname = p->getSyscallArg(tc, index); 1618 Addr valPtr = p->getSyscallArg(tc, index); 1619 Addr lenPtr = p->getSyscallArg(tc, index); 1620 1621 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1622 if (!sfdp) 1623 return -EBADF; 1624 int sim_fd = sfdp->getSimFD(); 1625 1626 socklen_t len = sizeof(val); 1627 int status = getsockopt(sim_fd, level, optname, &val, &len); 1628 1629 if (status == -1) 1630 return -errno; 1631 1632 // copy val to valPtr and pass it on 1633 BufferArg valBuf(valPtr, sizeof(val)); 1634 memcpy(valBuf.bufferPtr(), &val, sizeof(val)); 1635 valBuf.copyOut(tc->getMemProxy()); 1636 1637 // copy len to lenPtr and pass it on 1638 BufferArg lenBuf(lenPtr, sizeof(len)); 1639 memcpy(lenBuf.bufferPtr(), &len, sizeof(len)); 1640 lenBuf.copyOut(tc->getMemProxy()); 1641 1642 return status; 1643} 1644 1645SyscallReturn 1646getsocknameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1647{ 1648 int index = 0; 1649 int tgt_fd = p->getSyscallArg(tc, index); 1650 Addr addrPtr = p->getSyscallArg(tc, index); 1651 Addr lenPtr = p->getSyscallArg(tc, index); 1652 1653 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1654 if (!sfdp) 1655 return -EBADF; 1656 int sim_fd = sfdp->getSimFD(); 1657 1658 // lenPtr is an in-out paramenter: 1659 // sending the address length in, conveying the final length out 1660 1661 // Read in the value of len from the passed pointer. 1662 BufferArg lenBuf(lenPtr, sizeof(socklen_t)); 1663 lenBuf.copyIn(tc->getMemProxy()); 1664 socklen_t len = *(socklen_t *)lenBuf.bufferPtr(); 1665 1666 struct sockaddr sa; 1667 int status = getsockname(sim_fd, &sa, &len); 1668 1669 if (status == -1) 1670 return -errno; 1671 1672 // Copy address to addrPtr and pass it on. 1673 BufferArg addrBuf(addrPtr, sizeof(sa)); 1674 memcpy(addrBuf.bufferPtr(), &sa, sizeof(sa)); 1675 addrBuf.copyOut(tc->getMemProxy()); 1676 1677 // Copy len to lenPtr and pass it on. 1678 *(socklen_t *)lenBuf.bufferPtr() = len; 1679 lenBuf.copyOut(tc->getMemProxy()); 1680 1681 return status; 1682} 1683 1684SyscallReturn 1685getpeernameFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1686{ 1687 int index = 0; 1688 int tgt_fd = p->getSyscallArg(tc, index); 1689 Addr sockAddrPtr = p->getSyscallArg(tc, index); 1690 Addr addrlenPtr = p->getSyscallArg(tc, index); 1691 1692 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1693 if (!sfdp) 1694 return -EBADF; 1695 int sim_fd = sfdp->getSimFD(); 1696 1697 BufferArg bufAddrlen(addrlenPtr, sizeof(unsigned)); 1698 bufAddrlen.copyIn(tc->getMemProxy()); 1699 BufferArg bufSock(sockAddrPtr, *(unsigned *)bufAddrlen.bufferPtr()); 1700 1701 int retval = getpeername(sim_fd, 1702 (struct sockaddr *)bufSock.bufferPtr(), 1703 (unsigned *)bufAddrlen.bufferPtr()); 1704 1705 if (retval != -1) { 1706 bufSock.copyOut(tc->getMemProxy()); 1707 bufAddrlen.copyOut(tc->getMemProxy()); 1708 } 1709 1710 return (retval == -1) ? -errno : retval; 1711} 1712 1713SyscallReturn 1714setsockoptFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) 1715{ 1716 int index = 0; 1717 int tgt_fd = p->getSyscallArg(tc, index); 1718 int level = p->getSyscallArg(tc, index); 1719 int optname = p->getSyscallArg(tc, index); 1720 Addr valPtr = p->getSyscallArg(tc, index); 1721 socklen_t len = p->getSyscallArg(tc, index); 1722 1723 BufferArg valBuf(valPtr, len); 1724 valBuf.copyIn(tc->getMemProxy()); 1725 1726 auto sfdp = std::dynamic_pointer_cast<SocketFDEntry>((*p->fds)[tgt_fd]); 1727 if (!sfdp) 1728 return -EBADF; 1729 int sim_fd = sfdp->getSimFD(); 1730 1731 int status = setsockopt(sim_fd, level, optname, 1732 (struct sockaddr *)valBuf.bufferPtr(), len); 1733 1734 return (status == -1) ? -errno : status; 1735} 1736 1737