/* * Acquire ticket lock. Increment the tail of the queue and use the original * value as the ticket value. Wait until the head of the queue equals the * ticket value. The futex used to wait depends on the ticket value in order * to avoid that all threads get woken up every time a ticket lock is * released. That last effect is sometimes called the "thundering herd" * effect. * * See also Nick Piggin, x86: FIFO ticket spinlocks, Linux kernel mailing list * (http://lkml.org/lkml/2007/11/1/125) for more info. */ static void acquire_sched_lock(struct sched_lock *p) { unsigned ticket, futex_value; volatile unsigned *futex; SysRes sres; ticket = __sync_fetch_and_add(&p->tail, 1); futex = &p->futex[ticket & TL_FUTEX_MASK]; if (s_debug) VG_(printf)("[%d/%d] acquire: ticket %u\n", VG_(getpid)(), VG_(gettid)(), ticket); for (;;) { futex_value = *futex; __sync_synchronize(); if (ticket == p->head) break; if (s_debug) VG_(printf)("[%d/%d] acquire: ticket %u - waiting until" " futex[%ld] != %u\n", VG_(getpid)(), VG_(gettid)(), ticket, (long)(futex - p->futex), futex_value); sres = VG_(do_syscall3)(__NR_futex, (UWord)futex, VKI_FUTEX_WAIT | VKI_FUTEX_PRIVATE_FLAG, futex_value); if (sr_isError(sres) && sr_Err(sres) != VKI_EAGAIN) { VG_(printf)("futex_wait() returned error code %lu\n", sr_Err(sres)); vg_assert(False); } } __sync_synchronize(); INNER_REQUEST(ANNOTATE_RWLOCK_ACQUIRED(p, /*is_w*/1)); vg_assert(p->owner == 0); p->owner = VG_(gettid)(); }
static void check_mmap(SysRes res, Addr base, SizeT len) { if (sr_isError(res)) { VG_(printf)("valgrind: mmap(0x%llx, %lld) failed in UME " "with error %lu (%s).\n", (ULong)base, (Long)len, sr_Err(res), VG_(strerror)(sr_Err(res)) ); if (sr_Err(res) == VKI_EINVAL) { VG_(printf)("valgrind: this can be caused by executables with " "very large text, data or bss segments.\n"); } VG_(exit)(1); } }
static Region_map *vrm_determine_local_rm_address() { /* * Determine the address of the RE kernel's local RM. */ /* * 1. Try to find symbol in ELF image file */ if (dbg_elf) VG_(printf)("opening rom/l4re\n"); SysRes res = VG_(open)((const Char*)"rom/l4re", VKI_O_RDONLY, 0); if (dbg_elf) VG_(printf)("opened: %ld\n", sr_Res(res)); if (sr_isError(res)) { VG_(printf)("Error opening file: %ld\n", sr_Err(res)); enter_kdebug(); } int fd = sr_Res(res); struct vg_stat statbuf; int err = VG_(fstat)(fd, &statbuf); if (dbg_elf) VG_(printf)("stat: %d, size %d\n", err, (int)statbuf.size); if (err) { VG_(printf)("error on fstat(): %d\n", err); enter_kdebug(); } void *a = 0; a = mmap(a, statbuf.size, VKI_PROT_READ, VKI_MAP_PRIVATE, fd, 0); if (dbg_elf) VG_(printf)("mmap to %p\n", a); melf_global_elf_info i; err = melf_parse_elf(a, &i); if (dbg_elf) VG_(printf)("parsed: %d\n", err); char *rm_addr = melf_find_symbol_by_name(&i, (char*)"__local_rm"); if (dbg_elf) VG_(printf)("Found symbol %s @ %p\n", (char*)"__local_rm", rm_addr); munmap(a, VG_PGROUNDUP(statbuf.size)); VG_(close)(fd); if (rm_addr) return reinterpret_cast<Region_map*>(rm_addr); /* * 2. If not successful parsing binary, try hard-coded value */ VG_(printf)("Did not find local-rm, aborting\n"); VG_(exit)(1); /* Not found? */ }
// returns: 0 = success, non-0 is failure // // We can execute only binaries (ELF, etc) or scripts that begin with "#!". // (Not, for example, scripts that don't begin with "#!"; see the // VG_(do_exec)() invocation from m_main.c for how that's handled.) Int VG_(do_exec_inner)(const HChar* exe, ExeInfo* info) { SysRes res; Int fd; Int ret; res = VG_(pre_exec_check)(exe, &fd, False/*allow_setuid*/); if (sr_isError(res)) return sr_Err(res); vg_assert2(sr_Res(res) >= 0 && sr_Res(res) < EXE_HANDLER_COUNT, "invalid VG_(pre_exec_check) result"); ret = (*exe_handlers[sr_Res(res)].load_fn)(fd, exe, info); VG_(close)(fd); return ret; }
static struct elfinfo *readelf(Int fd, const char *filename) { SysRes sres; struct elfinfo *e = VG_(malloc)("ume.re.1", sizeof(*e)); Int phsz; vg_assert(e); e->fd = fd; sres = VG_(pread)(fd, &e->e, sizeof(e->e), 0); if (sr_isError(sres) || sr_Res(sres) != sizeof(e->e)) { VG_(printf)("valgrind: %s: can't read ELF header: %s\n", filename, VG_(strerror)(sr_Err(sres))); goto bad; } if (VG_(memcmp)(&e->e.e_ident[0], ELFMAG, SELFMAG) != 0) { VG_(printf)("valgrind: %s: bad ELF magic number\n", filename); goto bad; } if (e->e.e_ident[EI_CLASS] != VG_ELF_CLASS) { VG_(printf)("valgrind: wrong ELF executable class " "(eg. 32-bit instead of 64-bit)\n"); goto bad; } if (e->e.e_ident[EI_DATA] != VG_ELF_DATA2XXX) { VG_(printf)("valgrind: executable has wrong endian-ness\n"); goto bad; } if (!(e->e.e_type == ET_EXEC || e->e.e_type == ET_DYN)) { VG_(printf)("valgrind: this is not an executable\n"); goto bad; } if (e->e.e_machine != VG_ELF_MACHINE) { VG_(printf)("valgrind: executable is not for " "this architecture\n"); goto bad; } if (e->e.e_phentsize != sizeof(ESZ(Phdr))) { VG_(printf)("valgrind: sizeof ELF Phdr wrong\n"); goto bad; } phsz = sizeof(ESZ(Phdr)) * e->e.e_phnum; e->p = VG_(malloc)("ume.re.2", phsz); vg_assert(e->p); sres = VG_(pread)(fd, e->p, phsz, e->e.e_phoff); if (sr_isError(sres) || sr_Res(sres) != phsz) { VG_(printf)("valgrind: can't read phdr: %s\n", VG_(strerror)(sr_Err(sres))); VG_(free)(e->p); goto bad; } return e; bad: VG_(free)(e); return NULL; }
// If the do_exec fails we try to emulate what the shell does (I used // bash as a guide). It's worth noting that the shell can execute some // things that VG_(do_exec)() (which subsitutes for the kernel's exec()) // will refuse to (eg. scripts lacking a "#!" prefix). static Int do_exec_shell_followup(Int ret, const HChar* exe_name, ExeInfo* info) { # if defined(VGPV_arm_linux_android) \ || defined(VGPV_x86_linux_android) \ || defined(VGPV_mips32_linux_android) \ || defined(VGPV_arm64_linux_android) const HChar* default_interp_name = "/system/bin/sh"; # else const HChar* default_interp_name = "/bin/sh"; # endif SysRes res; struct vg_stat st; if (VKI_ENOEXEC == ret) { // It was an executable file, but in an unacceptable format. Probably // is a shell script lacking the "#!" prefix; try to execute it so. // Is it a binary file? if (is_binary_file(exe_name)) { VG_(fmsg)("%s: cannot execute binary file\n", exe_name); VG_(exit)(126); // 126 == NOEXEC } // Looks like a script. Run it with /bin/sh. This includes // zero-length files. VG_(free)(info->interp_name); info->interp_name = VG_(strdup)("ume.desf.1", default_interp_name); VG_(free)(info->interp_args); info->interp_args = NULL; if (info->argv && info->argv[0] != NULL) info->argv[0] = exe_name; ret = VG_(do_exec_inner)(info->interp_name, info); if (0 != ret) { // Something went wrong with executing the default interpreter VG_(fmsg)("%s: bad interpreter (%s): %s\n", exe_name, info->interp_name, VG_(strerror)(ret)); VG_(exit)(126); // 126 == NOEXEC } } else if (0 != ret) { // Something else went wrong. Try to make the error more specific, // and then print a message and abort. Int exit_code = 126; // 126 == NOEXEC (bash) res = VG_(stat)(exe_name, &st); // Does the file exist ? if (sr_isError(res) && sr_Err(res) == VKI_ENOENT) { VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); exit_code = 127; // 127 == NOTFOUND (bash) // Was it a directory? } else if (!sr_isError(res) && VKI_S_ISDIR(st.mode)) { VG_(fmsg)("%s: is a directory\n", exe_name); // Was it not executable? } else if (0 != VG_(check_executable)(NULL, exe_name, False/*allow_setuid*/)) { VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); // Did it start with "#!"? If so, it must have been a bad interpreter. } else if (is_hash_bang_file(exe_name)) { VG_(fmsg)("%s: bad interpreter: %s\n", exe_name, VG_(strerror)(ret)); // Otherwise it was something else. } else { VG_(fmsg)("%s: %s\n", exe_name, VG_(strerror)(ret)); } VG_(exit)(exit_code); } return ret; }