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