/* * There is a problem here. The sigset_t API defines no way to tell * what the maximum signal/bit number supported by a sigset_t is. At * least one implementation doesn't check bit number arguments, just * accessing outside of the sigset_t if the number is out of range, so * we can't do something like sigfillset and then counting the set * bits. * * We blindly assume that all implementations support at least 128 * bits, and that, for the os2em conversion, we can ignore bits above * 128. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * As of 1.4T, which is what we emulate, filesystem info comes from * statfs(), for which we need to include . As of 4.0.1 * and 5.2, we need to call statvfs(), from . Neither * has compatability with the other. Hence this dance. :-þ * * Similar remarks apply to getfsstat()/getvfsstat(). */ #include #if __NetBSD_Version__ >= 400000003 // 4.0.1 and later #define STATFS_VIA_STATVFS #define GETFSSTAT_VIA_GETVFSSTAT #elif __NetBSD_Version__ <= 104200000 // 1.4T and earlier #undef STATFS_VIA_STATVFS #undef GETFSSTAT_VIA_GETVFSSTAT #else #error "Figure out whether STATFS_VIS_STATVFS and/or GETFSSTAT_VIA_GETVFSSTAT should be defined" #endif #ifdef STATFS_VIA_STATVFS #include #else #include #endif #ifdef GETFSSTAT_VIA_GETVFSSTAT #include #else #include #endif /* * The revs I use that don't have WNOREAP have WNOWAIT, with the same * functionality. It's not documented, but it's there, apparently for * compatability with other OSes but usable natively. */ #if defined(WNOWAIT) && !defined(WNOREAP) #define WNOREAP WNOWAIT #endif extern const char *__progname; #include "em-const.h" // PAGE_SIZE must be a power of two #define PAGE_SIZE 4096 #define USRSTACK 0xf0000000 #define MAXSSIZE (1U<<24) #define MAXDSIZE 0x04000000 #define MAXFDS 4096 #define STACKGAPLEN 400 // Second arg must be a power of two #define ROUND_UP(a,b) (((a)+(b)-1) & ~((b)-1)) #define ROUND_DOWN(a,b) ((a) & ~((b)-1)) #define REP4(x) x, x, x, x #define REP8(x) REP4(x), REP4(x) #define REP16(x) REP8(x), REP8(x) #define REP32(x) REP16(x), REP16(x) #define REP64(x) REP32(x), REP32(x) #define REP128(x) REP64(x), REP64(x) #define REP256(x) REP128(x), REP128(x) #define REP2b(x) REP4(x) #define REP3b(x) REP8(x) #define REP4b(x) REP16(x) #define REP5b(x) REP32(x) #define REP6b(x) REP64(x) #define REP7b(x) REP128(x) #define REP8b(x) REP256(x) #define OPC(opc) (((opc)>>30)&3) /* format select bits */ #define OP2(opc) (((opc)>>22)&7) /* opcode bits, format 2 */ #define DREG(opc) (((opc)>>25)&31) /* dest reg bits, formats 2 and 3 */ #define A(opc) (((opc)>>29)&1) /* annul bit, format 2 */ #define COND(opc) (((opc)>>25)&15) /* condition code bits, format 2 */ #define IMM22(opc) ((opc)&0x003fffff) /* immediate data, format 2 */ #define DISP22(opc) signextend(IMM22(opc),22) /* displacement, format 2 */ #define OP3(opc) (((opc)>>19)&0x3f) /* opcode bits, format 3 */ #define SREG1(opc) (((opc)>>14)&31) /* source reg 1 bits, format 3 */ #define SREG2(opc) ((opc)&31) /* source reg 2 bits, format 3 */ #define I(opc) (((opc)>>13)&1) /* immediate bit, format 3 */ #define ASI(opc) (((opc)>>5)&0xff) /* alternative space bits, format 3 */ #define SIMM13(opc) signextend((opc)&0x1fff,13) /* immediate data, format 3 */ #define OPF(opc) (((opc)>>5)&0x1ff) /* FPU opcode bits, format 3 */ #define P_R 0x01 #define P_W 0x02 #define P_X 0x04 #define SYSCALL_IMPL(fn) void fn(SCARGS *args __attribute__((__unused__)), SCRV *rv __attribute__((__unused__))) #define SYSCALL_SETERR(e) do { rv->err = (e); } while (0) #define SYSCALL_ERR(e) do { SYSCALL_SETERR((e)); return; } while (0) #define SYSCALL_SETRET(v) do { rv->rv = (v); rv->flags |= SCRV_RVSET; } while (0) #define SYSCALL_RET(v) do { SYSCALL_SETRET((v)); return; } while (0) #define SYSCALL_RET2(v1,v2) do { rv->rv = (v1); rv->rv2 = (v2); rv->flags |= SCRV_RVSET | SCRV_RV2SET; return; } while (0) /* * Possible actions we can take when something depends on an unset * value: error out, print a warning but carry on, and silently carry * on. (When we carry on, the value used is 0.) */ typedef enum { UNSET_ERROR = 1, UNSET_WARN, UNSET_IGNORE, } UNSET_ACTION; /* * Kinds of operation we may need to back out (or otherwise fixup) * during a vfork. See the comments on sc___vfork14 and struct * vforkbackout for more. */ typedef enum { VFB_OPEN, VFB_CLOSE, VFB_DUP2, } VFBKIND; /* * The stages of a vfork operation. See the comment on sc___vfork14 * for more. */ typedef enum { VFORK_NONE = 1, VFORK_START, VFORK_FAIL, VFORK_SUCCESS, } VFORKSTAGE; typedef struct memseg MEMSEG; typedef struct memsegops MEMSEGOPS; typedef struct state STATE; typedef struct scargs SCARGS; typedef struct fd FD; typedef struct memseg_priv_malloc MEMSEG_PRIV_MALLOC; typedef struct memseg_priv_mmap MEMSEG_PRIV_MMAP; typedef struct trc TRC; typedef struct trcfile TRCFILE; typedef struct memacc MEMACC; typedef struct mfblk MFBLK; typedef struct scrv SCRV; typedef struct sig SIG; typedef struct nulterm_status NULTERM_STATUS; typedef struct sysent SYSENT; typedef struct vforkbackout VFORKBACKOUT; typedef struct emsigset EMSIGSET; typedef struct statestack STATESTACK; typedef struct elf_ctx ELF_CTX; typedef struct psect_ops PSECT_OPS; typedef struct pidpref_priv PIDPREF_PRIV; /* * Private data for a FILE * created by fwrap_pidprefix. */ struct pidpref_priv { FILE *inner; int atbol; int closeinner; } ; /* * Common data for "read an ELF file" operations. We want to do one of * these while doing another (for PT_INTERP dynamic-linker loading * when execing dynamically-linked executables), necessitating * something at least a bit like this. */ struct elf_ctx { // Path of file being read const char *path; // (Underlying) OS file descriptor open onto path. int fd; // The Elf32_Ehdr of the file, as read off disk. Elf32_Ehdr eh; // PT_INTERP sectino contents, a zero-length string if none (yet). char interp[em_MAXPATHLEN+1]; // VA of the beginning of the text segment. uint32_t taddr; // VA of the beginning of the data segment. uint32_t daddr; // Offset to relocate all loaded sections by. uint32_t loadbase; // End of data section. uint32_t dend; // Value for AT_PHDR Aux32Info struct. uint32_t dli_pha; // Value for AT_PHENT Aux32Info struct. uint32_t dli_phes; // Value for AT_PHNUM Aux32Info struct. uint32_t dli_phn; // Value for AT_BASE Aux32Info struct. uint32_t dli_interp; // Value for AT_ENTRY Aux32Info struct. uint32_t dli_entry; // PT_PHDR value, sometimes copied to dli_pha. uint32_t phdr; } ; /* * There are three program headers we care about. When scanning * different files' headers, we want different operations. This * collects them together. */ struct psect_ops { void (*pt_load)(ELF_CTX *, Elf32_Phdr *, void (*)(void)); void (*pt_interp)(ELF_CTX *, Elf32_Phdr *, void (*)(void)); void (*pt_phdr)(ELF_CTX *, Elf32_Phdr *, void (*)(void)); } ; #define PSECT_OPS_INIT(name) {\ &psect_pt_load_##name, \ &psect_pt_interp_##name, \ &psect_pt_phdr_##name, \ } /* * An emulated-OS sigset_t. */ struct emsigset { uint32_t bits[4]; } ; /* * A possible syscall. * * In the args/rv strings: * * d 32-bit value, printed as signed decimal. * p 32-bit pointer, printed as hex. * b 32-bit value, printed in hex and decimal. * s (Pointer to) a string, printed as a string. * o 32-bit value, printed as unsigned octal. * D Two 32-bit values, printed in 64-bit signed decimal. * x A 32-bit pointer to a nil-terminated list of string * pointers (execve's argv and envp). * m Two 32-bit values, a pointer-and-length describing a * list of 32-bit values (sysctl's MIB args). * - The argument is ignored. * O Special-case hack for open(): if the second arg has * O_CREAT set, this turns into o, otherwise it is not * referenced and prints as "...". * f(...) Flags bitfield. The string in the parens is broken at * semicolons to describe the bits: * n:STRING * n in decimal, STRING terminated by ; or ) * Bit 1<> (n)) & 1) #define NEED_REG(n,what) do { if ((unset_action != UNSET_IGNORE) && !IS_REG_SET(n)) { fprintf(stderr,"%s isn't set\n",what); if (unset_action == UNSET_ERROR) top(); } } while (0) // The general-purpose hardware registers. uint32_t regs[32]; #define R_G0 0 #define R_G1 1 #define R_G2 2 #define R_G3 3 #define R_G4 4 #define R_G5 5 #define R_G6 6 #define R_G7 7 #define R_O0 8 #define R_O1 9 #define R_O2 10 #define R_O3 11 #define R_O4 12 #define R_O5 13 #define R_O6 14 #define R_O7 15 #define R_L0 16 #define R_L1 17 #define R_L2 18 #define R_L3 19 #define R_L4 20 #define R_L5 21 #define R_L6 22 #define R_L7 23 #define R_I0 24 #define R_I1 25 #define R_I2 26 #define R_I3 27 #define R_I4 28 #define R_I5 29 #define R_I6 30 #define R_I7 31 #define R_SP R_O6 #define R_FP R_I6 // Count of instructions executed. unsigned long long int instrs; // Register-window stack depth. unsigned int wdepth; // Signal mask (in the sigprocmask sense). uint64_t sigmask; // Signal handling settings. SIG sigh[em__NSIG]; // True iff cannot interact with the user. int noninteractive; // sigpend[i] true iff signal i is awaiting delivery. volatile sig_atomic_t sigpend[em__NSIG]; // True iff we're currently executing on the signal stack. // (We don't currently implement signal stacks.) int onsigstack; // Is delivery on the signal stack enabled? int sigstack_enabled; // The signal stack base and size. uint32_t sigstack_base; uint32_t sigstack_size; // Where the break (the top of data space) is at. uint32_t dbrk; } ; /* * Saved machine states are kept in a stack during vfork()s. The stack * rarely gets very deep - there's only one saved state unless a * vforked child itself vforks - but the generality is cheap. */ struct statestack { STATESTACK *link; STATE state; } ; typedef unsigned long int ULI; typedef long int LI; typedef unsigned long long int ULLI; // Command-line args. static const char *exe = 0; static char **args = 0; static int nargs = 0; // Live machine staet. static STATE s; // The VM environment. static MEMSEG *vm; // File descriptors. static FD **fds; static int nfds; // The PID of this process. static int mypid; // Call this to throw out on error. static void (*err_jmp)(void) = 0; // Types of tracing. static TRC trace[] = { { "instr" }, #define TRC_INSTR 0 { "chg" }, #define TRC_CHG 1 { "mem" }, #define TRC_MEM 2 { "syscall" }, #define TRC_SYSCALL 3 { "stack" }, #define TRC_STACK 4 { "vfork" }, #define TRC_VFORK 5 { "signal" }, #define TRC_SIGNAL 6 { "exec" }, #define TRC_EXEC 7 { 0 } }; #define TRC__N 8 // A TRCFILE for tracing to stdout. Never freed. static TRCFILE trcfile_stdout; // Elastic array holding memory accesses. static MEMACC *memacc; static int amemacc; static int nmemacc; /* * If this is set, memory accesses aren't recorded even when they * normally would be (ie, when TRC_MEM tracing is on). This is used * when, for example, printing syscall arguments in the syscall entry * code. */ static int nomemacc; /* * Address of the signal-handling trampoline. Conceptually, this * should be part of STATE, but it's always at the same place, so * there's no point. */ static uint32_t sigtramp; // All signals we have values for. #define SIG_ALLMASK ((uint64_t)(\ (1ULL << em_SIGHUP) | \ (1ULL << em_SIGINT) | \ (1ULL << em_SIGQUIT) | \ (1ULL << em_SIGILL) | \ (1ULL << em_SIGTRAP) | \ (1ULL << em_SIGABRT) | \ (1ULL << em_SIGEMT) | \ (1ULL << em_SIGFPE) | \ (1ULL << em_SIGKILL) | \ (1ULL << em_SIGBUS) | \ (1ULL << em_SIGSEGV) | \ (1ULL << em_SIGSYS) | \ (1ULL << em_SIGPIPE) | \ (1ULL << em_SIGALRM) | \ (1ULL << em_SIGTERM) | \ (1ULL << em_SIGURG) | \ (1ULL << em_SIGSTOP) | \ (1ULL << em_SIGTSTP) | \ (1ULL << em_SIGCONT) | \ (1ULL << em_SIGCHLD) | \ (1ULL << em_SIGTTIN) | \ (1ULL << em_SIGTTOU) | \ (1ULL << em_SIGIO) | \ (1ULL << em_SIGXCPU) | \ (1ULL << em_SIGXFSZ) | \ (1ULL << em_SIGVTALRM) | \ (1ULL << em_SIGPROF) | \ (1ULL << em_SIGWINCH) | \ (1ULL << em_SIGINFO) | \ (1ULL << em_SIGUSR1) | \ (1ULL << em_SIGUSR2) | \ (1ULL << em_SIGPWR) )) // All signals userland can block. #define SIG_CANBLOCK (SIG_ALLMASK & ~(uint64_t)((1ULL << em_SIGKILL) | (1ULL << em_SIGSTOP))) /* * Default signal actions, that is, the actions taken when the handler * is set to SIG_DFL. The SIGDEF_* values must all be nonzero, so * that holes in sigdef[] can be detected by noticing zero values. * There are four possible default actions: kill the process (eg, * SIGTERM), kill the process with a coredump (eg, SIGABRT), ignore * the signal (sg, SIGCONT), and stop the process (eg, SIGTTIN). * * Because _we_ can't catch SIGKILL and SIGSTOP, their entries here * never matter; they're here for completeness more than correctness. */ #define SIGDEF_KILL 1 #define SIGDEF_CORE 2 #define SIGDEF_IGNORE 3 #define SIGDEF_STOP 4 static const unsigned int sigdef[] = { [em_SIGHUP] = SIGDEF_KILL, [em_SIGINT] = SIGDEF_KILL, [em_SIGQUIT] = SIGDEF_CORE, [em_SIGILL] = SIGDEF_CORE, [em_SIGTRAP] = SIGDEF_CORE, [em_SIGABRT] = SIGDEF_CORE, [em_SIGEMT] = SIGDEF_CORE, [em_SIGFPE] = SIGDEF_CORE, [em_SIGKILL] = SIGDEF_KILL, // never matters [em_SIGBUS] = SIGDEF_CORE, [em_SIGSEGV] = SIGDEF_CORE, [em_SIGSYS] = SIGDEF_CORE, [em_SIGPIPE] = SIGDEF_KILL, [em_SIGALRM] = SIGDEF_KILL, [em_SIGTERM] = SIGDEF_KILL, [em_SIGURG] = SIGDEF_IGNORE, [em_SIGSTOP] = SIGDEF_STOP, // never matters [em_SIGTSTP] = SIGDEF_STOP, [em_SIGCONT] = SIGDEF_IGNORE, [em_SIGCHLD] = SIGDEF_IGNORE, [em_SIGTTIN] = SIGDEF_STOP, [em_SIGTTOU] = SIGDEF_STOP, [em_SIGIO] = SIGDEF_IGNORE, [em_SIGXCPU] = SIGDEF_KILL, [em_SIGXFSZ] = SIGDEF_KILL, [em_SIGVTALRM] = SIGDEF_KILL, [em_SIGPROF] = SIGDEF_KILL, [em_SIGWINCH] = SIGDEF_IGNORE, [em_SIGINFO] = SIGDEF_IGNORE, [em_SIGUSR1] = SIGDEF_KILL, [em_SIGUSR2] = SIGDEF_KILL, [em_SIGPWR] = SIGDEF_IGNORE }; /* * When set nonzero, anysigpend indicates there is probably a signal * pending delivery. Basically, it means "it's worth checking". */ static volatile sig_atomic_t anysigpend; /* * Indicates run() should check for rare events. There are various * things that happen rarely and asynchronously but that require run() * to handle them. Rather than have a bunch of variables that run() * checks each time around its loop, we have this, which reduces the * overhead on most loops to one check. */ static volatile sig_atomic_t alert_run; /* * A bunch of 0x00 octets. This is useful mostly for padding syscall * output buffers; for internal things, we can just bzero(), but for * userland, it's easier to copyout() from here than to write a * bzeroout(). */ static const char nulbuf[PAGE_SIZE] = { 0 }; // Indicates run should do just-post-execve() actions, like TRC_STACK. static int postexec; /* * The emulator can track unset values. These arise when a save * instruction is done; on real hardware, the new register window has * some values, but they are not specified. We can treat them as * undefined and track them, to some extent - not as thorough as what * valgrind does, but enough to be helpful. This specifise the action * to take when an unset value is used in any way other than just * copying it. */ static UNSET_ACTION unset_action = UNSET_IGNORE; /* * When set, we do a debugger-assist loop after forking. With vfork, * this happens when exec breaks the vfork link (we do it right after * vforkbreak()). */ static int forkwait; /* * vfork() requires some careful attention to control flow, to ensure * we don't return from the stack frame in which _we_ vfork until the * vfork sharing-and-wait assocation is broken. We also have to be * careful to deal with the effects of our underlying OS's memory * sharing on vfork - there are a number of things that a real OS * keeps separate between parent and child, but which we keep in our * VM and thus need to fix up after vfork(). Most of these have been * moved into STATE and thus are dealt with the same way (emulated) * machine registers are, by restoring pre-vfork state once the parent * resumes, but some require special attention. * * vfork_stage simply says where we are in the control flow, which * bouncse around a bit more than we might wish (the underlying * vfork() has to happen in run(), but that's something like three * call frames above the point where we discover the emulated machine * is vforking). It is VFORK_NONE during ordinary execution. When * the emulator vforks, the syscall implementation sets it to * VFORK_START. run() then vforks in the emulator and sets * vfork_stage to VFORK_FAIL (if the vfork failed) or VFORK_SUCCESS * (if it worked) and arranges for sc___vfork14 to be re-entered. * This last is not strictly necessary; run() could set up the * emulator state directly - but doing it this way lets us leverage * the existing syscall-return code, rather than having to duplicate * it in run() or factor it out. * * vfork_states is used to restore machine state (and some emulated-OS * state) in the parent, as sketched above. It's a stack, not just a * single saved state, to deal with the case where a vforked child * vforks again while it's still borrowing the parent's resources. * * vfork_dropvm is used to deal with execve(). Normally, execve just * drops the old VM, replacing it with the new. But it's not that * simple for execve in a vforked child, because that would leave us, * in the parent, with the child's VM. So when the implemetation of * emulated execve breaks the underlying-OS vfork association, it * makes sure the parent's VM is in the global vm variable. After * vforkbraek()ing, it then drops the parent VM and switches to the * new one - but the parent is then stuck with having both VM spaces * set up. The execve() code sticks the child's VM in vfork_dropvm * before vforkbreak()ing; when the parent resumes post-vfork, it * discards any VM it finds in vfork_dropvm. * * vfork_value is used to communicate the errno (if vfork_stage is * VFORK_FAIL) or return PID (if vfork_stage is VFORK_SUCCESS) to * sc___vfork14 (see the above discussion of vfork_stage). * * during_vfork is zero during normal operation; it is nonzero if the * emulator is currently emulating a vforked child. To handle vfork * from a vfork child, it is a counter, not just a boolean, * incremented when vfork() succeeds and decremented when a vfork * child execs. * * vfb is a list of things done by a vforked child that need fixups in * the parent. For example, if a vforked child closes a file * descriptor, in a real OS the file descriptor in the parent is * unaffected. For us, the underlying descriptor is unaffected, but * we have state in VM as well (see fds, above), so we need to fix * things up a bit. vfb holds these records. */ static VFORKSTAGE vfork_stage; static STATESTACK *vfork_states; static MEMSEG *vfork_dropvm; static uint32_t vfork_value; static int during_vfork; static VFORKBACKOUT *vfb; /* * Byteswap values from ELF files. These are needed because loading * values from ELF files is done with read() (or moral equivalent), * not mem_get_*(), so byte-sex differences between the emulated SPARC * and the emulator CPU, if any, become visible. We are perhaps * fortunate that the machine we're emulating uses network byte order, * letting us (ab)use the ntoh*() macros; if we were emulating a * little-endian machine, we'd need to write some kind of letoh*() * operations. */ #define ELF_HALF_TO_NATIVE(x) ntohs((x)) #define ELF_WORD_TO_NATIVE(x) ntohl((x)) #define ELF_ADDR_TO_NATIVE(x) ntohl((x)) #define ELF_OFFSET_TO_NATIVE(x) ntohl((x)) /* * Names of machine registers. The extra four at the end are mostly * for the benefit of print_regs(). For the main 32 registers, this * is indexed by the R_* values defined in the STATE struct, above; * the extra values have PRINT_REGS_* definitions for their indices. * The other PRINT_REGS* values are for the convenience of * print_regs() and related code. */ static const char * const regnames[] = { "%g0", "%g1", "%g2", "%g3", "%g4", "%g5", "%g6", "%g7", "%o0", "%o1", "%o2", "%o3", "%o4", "%o5", "%sp", "%o7", "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", "%i0", "%i1", "%i2", "%i3", "%i4", "%i5", "%fp", "%i7", "y", "pc", "npc", "cc" }; #define PRINT_REGS_Y 32 #define PRINT_REGS_PC 33 #define PRINT_REGS_NPC 34 #define PRINT_REGS_CC 35 #define PRINT_REGS__N (sizeof(regnames)/sizeof(regnames[0])) #define PRINT_REGS_ALL ((1ULL << PRINT_REGS__N) - 1ULL) #define PRINT_REGS_BAD 63 // Printed forms of integer condition code conditions. static const char *icc[] = { "n", "e", "le", "l", "leu", "lu/cs", "neg", "vs", "a", "ne", "g", "ge", "gu", "geu/cc", "pos", "vc" }; // Printed forms of FPU condition code conditions. static const char *fcc[] = { "n", "ne", "lg", "ul", "l", "ug", "g", "u", "a", "e", "ue", "ge", "uge", "le", "ule", "o" }; // Printed forms of coprocessor condition code conditions. static const char *ccc[] = { "n", "123", "12", "13", "1", "23", "2", "3", "a", "0", "03", "02", "023", "01", "013", "012" }; // The signal trampoline code. static const uint32_t sigcode[] = { 0x9de3bf18, 0xa4100002, 0xa6100003, 0xa8100004, 0xaa100005, 0xac100006, 0xae100007, 0xe007a064, 0x23000004, 0xa08c0011, 0x02800013, 0xa3400000, 0xc12ba060, 0xc13ba068, 0xc53ba070, 0xc93ba078, 0xcd3ba080, 0xd13ba088, 0xd53ba090, 0xd93ba098, 0xdd3ba0a0, 0xe13ba0a8, 0xe53ba0b0, 0xe93ba0b8, 0xed3ba0c0, 0xf13ba0c8, 0xf53ba0d0, 0xf93ba0d8, 0xfd3ba0e0, 0xd01fa040, 0xd607a04c, 0x9fc04000, 0x9407a050, 0x80940000, 0x02800013, 0x81844000, 0xc10ba060, 0xc11ba068, 0xc51ba070, 0xc91ba078, 0xcd1ba080, 0xd11ba088, 0xd51ba090, 0xd91ba098, 0xdd1ba0a0, 0xe11ba0a8, 0xe51ba0b0, 0xe91ba0b8, 0xed1ba0c0, 0xf11ba0c8, 0xf51ba0d0, 0xf91ba0d8, 0xfd1ba0e0, 0x84100012, 0x86100013, 0x88100014, 0x8a100015, 0x8c100016, 0x8e100017, 0x83e82127, 0x9003a050, 0x91d02000, 0x82102001, 0x91d02000 }; #define SZSIGCODE sizeof(sigcode) /* * conds[] is here to automate condition-code testing. It is indexed * by the condition value from a branch instruction; the resulting * value is, conceptually, an array of 16 bits indexed by the four-bit * number formed by concatenating the condition-code bits. The * resulting bit says whether the condition passes or not. */ /* The CMASK_* defines assume these */ #if (CC_N != 8) || (CC_Z != 4) || (CC_V != 2) || (CC_C != 1) #error "conds[] assumptions invalid" #endif // Would _like_ to somehow generate these from CC_* // But I think the preprocessor isn't that smart #define CMASK_N 0xff00 #define CMASK_Z 0xf0f0 #define CMASK_V 0xcccc #define CMASK_C 0xaaaa static const uint16_t conds[16] = { 0, // never CMASK_Z, // eq CMASK_Z | (CMASK_N ^ CMASK_V), // le CMASK_N ^ CMASK_V, // lt CMASK_C | CMASK_Z, // leu CMASK_C, // ltu, cs CMASK_N, // neg CMASK_V, // vs 0xffff, // always 0xffff ^ CMASK_Z, // ne 0xffff ^ (CMASK_Z | (CMASK_N ^ CMASK_V)), // gt 0xffff ^ CMASK_N ^ CMASK_V, // ge 0xffff ^ (CMASK_C | CMASK_Z), // gtu 0xffff ^ CMASK_C, // geu, cc 0xffff ^ CMASK_N, // pos 0xffff ^ CMASK_V }; // vc #undef CMASK_N #undef CMASK_Z #undef CMASK_V #undef CMASK_C // Versions of tests we can pass plain char to. #define Cisspace(x) isspace((unsigned char)(x)) #define Cisdigit(x) isdigit((unsigned char)(x)) /* * This is called upon the emulator bugchecking. This actually getting * called indicates there is a bug somewhere. */ static void panic(int, const char *, ...) __attribute__((__format__(__printf__,2,3),__noreturn__)); static void panic(int lno, const char *fmt, ...) #define panic(...) (panic)(__LINE__, __VA_ARGS__) { va_list ap; fprintf(stderr,"%d: panic (line %d): ",mypid,lno); va_start(ap,fmt); vfprintf(stderr,fmt,ap); va_end(ap); fprintf(stderr,"\n"); fflush(0); signal(SIGSEGV,SIG_DFL); signal(SIGBUS,SIG_DFL); (void)*(volatile char *)0; abort(); exit(1); } /* * Write callback for streams opened by fwrap_pidprefix(). */ static int pidprefix_w(void *pv, const char *data, int len) { PIDPREF_PRIV *p; void *nl; int o; p = pv; o = 0; while (o < len) { if (p->atbol) { fprintf(p->inner,"%d: ",mypid); p->atbol = 0; } nl = memchr(data+o,'\n',len-o); if (nl) { fwrite(data+o,1,1+((const char *)nl)-(data+o),p->inner); o = (((char *)nl)+1) - data; p->atbol = 1; } else { fwrite(data+o,1,len-o,p->inner); p->atbol = 0; break; } } return(len); } /* * Close callback for streams opened by fwrap_pidprefix(). */ static int pidprefix_c(void *pv) { PIDPREF_PRIV *p; p = pv; if (p->closeinner) fclose(p->inner); free(p); return(0); } /* * Create a FILE * that wraps another to add PID prefixes to lines. * That is, after g = fwrap_pidprefix(f,...), then writing to g will * take the data written, prefix mypid to each line a la "%d: ", and * write the resulting lines to f. * * The second arg is a flags word: * * FWPP_CLOSE * Causes closing the returned stream to automatically * close the inner stream. If not specified, closing the * outer stream does nothing in particular to the inner * stream. */ static FILE *fwrap_pidprefix(FILE *inner, unsigned int flags) #define FWPP_CLOSE 0x00000001 { FILE *rv; PIDPREF_PRIV *p; p = malloc(sizeof(PIDPREF_PRIV)); if (! p) return(0); rv = funopen(p,0,&pidprefix_w,0,&pidprefix_c); if (! rv) { free(p); return(0); } p->inner = inner; p->atbol = 1; p->closeinner = (flags & FWPP_CLOSE) ? 1 : 0; return(rv); } /* * This is called to return to the top-level loop. The test is to * handle the case where it's called before the top-level loop is * entered; I'm not sure this can happen, but it's a cheap check. */ static void top(void) __attribute__((__noreturn__)); static void top(void) { if (! err_jmp) exit(1); (*err_jmp)(); panic("err_jmp returned"); } /* * Most tracing is generated with trc(TRC_*,...) calls. But sometimes * it's more convenient to write to a FILE * (as, for example, when * calling a print-something function that takes a FILE * for the * destination). This returns the FILE * output for the given tracing * kind should be sent to, or nil if that tracing is turned off. * * The returned FILE * should never be closed by the caller. */ static FILE *trc_f(int which) { if ((which < 0) || (which >= TRC__N)) abort(); return(trace[which].out?trace[which].out->f:0); } /* * Usually, tracing should just call trc(). But, sometimes, tracing * does something complicated or expensive enough that it should be * skipped if tracing is off. This performs that test. */ static int trc_if(int which) { if ((which < 0) || (which >= TRC__N)) abort(); return(!!trace[which].out); } /* * Generate trace output. Conceptually, this is semantically * equivalent to calling trc_f() and, if the returned value is * non-nil, fprintf()ing to it - but this is more convenient in msot * cases. */ static void trc(int, const char *, ...) __attribute__((__format__(__printf__,2,3))); static void trc(int which, const char *fmt, ...) { FILE *f; va_list ap; f = trc_f(which); if (f == 0) return; va_start(ap,fmt); vfprintf(f,fmt,ap); va_end(ap); } /* * Record a memory access. This is not called unless TRC_MEM tracing * is turned on. This handles collapsing successive adjacent accesses * into a single MEMACC - though it does so only when the later access * is after the earlier; some memory accesses are done upwards isntead * of downwards specifically so that they will collapse nicely here. * (Arguably we should arrange to handle accesses immedaitely below, * as well as immediately above, existing MEMACCs, though the * realloc() interface makes that a bit annoying.) */ static void mem_rw(char rw, uint32_t a, uint8_t v) { MEMACC *m; if (nomemacc) return; if (nmemacc >= amemacc) { int i; i = amemacc; memacc = realloc(memacc,(amemacc=nmemacc+16)*sizeof(*memacc)); for (;ivp = malloc((m->a=8)*sizeof(uint8_t)); } } if ( (nmemacc > 0) && (a == (m=&memacc[nmemacc-1])->a2) && (rw == m->rw) ) { if (m->n >= m->a) m->vp = realloc(m->vp,(m->a=m->n+8)*sizeof(uint8_t)); m->vp[m->n++] = v; m->a2 ++; return; } m = &memacc[nmemacc++]; m->a1 = a; m->a2 = a + 1; m->n = 1; m->vp[0] = v; m->rw = rw; } /* * Find the MEMSEG that maps a given address. This also centralizes * alignment checks. * * XXX Arguably we should do better than linear search, even with the * optimization that accesses will tend to stay nearby and thus moving * the accessed MEMSEG to the head of the list will cut down on * searches. Maybe an array indexed by address/PAGE_SIZE? */ static MEMSEG *memseg_find(uint32_t addr, uint32_t align, const char *op) { MEMSEG *ms; MEMSEG **msp; if (addr & align) { printf("%s %08lx: not aligned\n",op,(ULI)addr); top(); } msp = &vm; while ((ms = *msp)) { if ((addr >= ms->base) && (addr < ms->end)) { *msp = ms->link; ms->link = vm; vm = ms; return(ms); } else { msp = &ms->link; } } printf("%s %08lx: not mapped\n",op,(ULI)addr); top(); } /* * Return a uint8_t pointer to the memory at emulated virtual address * addr. align is the alignment mask (0 for no alignment, 1 for * 2-byte, 3 for 4-byte, 7 for 8-byte, etc). op is a text name for * the operation, for error messages. prot is the type of access * contemplated. * * This is like memseg_find except that it does protection checks and * the return value is a poitner to the memory rather than the * relevant MEMSEG pointer. */ static uint8_t *mem_find(uint32_t addr, uint32_t align, const char *op, unsigned int prot) { MEMSEG *ms; ms = memseg_find(addr,align,op); if (ms->prot & prot) return(ms->data+(addr-ms->base)); printf("%s %08lx: not accessible\n",op,(ULI)addr); top(); } /* * Set and get memory values. Addresses must be aligned correctly for * the data type in question, and these handle the way the emulated * machine is big-endian regardless of the emulating CPU's endianness. */ // Get a 4-byte data value. static uint32_t mem_get_4(uint32_t addr) { uint8_t *p; p = mem_find(addr,3,"get_4",P_R); if (trc_if(TRC_MEM)) { mem_rw('r',addr,p[0]); mem_rw('r',addr+1,p[1]); mem_rw('r',addr+2,p[2]); mem_rw('r',addr+3,p[3]); } return((p[0]*0x01000000)|(p[1]*0x00010000)|(p[2]*0x00000100)|p[3]); } // Get a 4-byte code value. // Just like mem_get_4 except that it's P_X instead of P_R. static uint32_t mem_exe_4(uint32_t addr) { uint8_t *p; p = mem_find(addr,3,"exe_4",P_X); if (trc_if(TRC_MEM)) { mem_rw('x',addr,p[0]); mem_rw('x',addr+1,p[1]); mem_rw('x',addr+2,p[2]); mem_rw('x',addr+3,p[3]); } return((p[0]*0x01000000)|(p[1]*0x00010000)|(p[2]*0x00000100)|p[3]); } // Set a 4-byte value. static void mem_set_4(uint32_t addr, uint32_t v) { uint8_t *p; p = mem_find(addr,3,"set_4",P_W); p[0] = v >> 24; p[1] = v >> 16; p[2] = v >> 8; p[3] = v; if (trc_if(TRC_MEM)) { mem_rw('w',addr,p[0]); mem_rw('w',addr+1,p[1]); mem_rw('w',addr+2,p[2]); mem_rw('w',addr+3,p[3]); } } // Get a 2-byte data value. static uint16_t mem_get_2(uint32_t addr) { uint8_t *p; p = mem_find(addr,1,"get_2",P_R); if (trc_if(TRC_MEM)) { mem_rw('r',addr,p[0]); mem_rw('r',addr+1,p[1]); } return((p[0]*0x0100)|p[1]); } // Set a 2-byte data value. static void mem_set_2(uint32_t addr, uint16_t v) { uint8_t *p; p = mem_find(addr,1,"set_2",P_W); p[0] = v >> 8; p[1] = v; if (trc_if(TRC_MEM)) { mem_rw('w',addr,p[0]); mem_rw('w',addr+1,p[1]); } } // Get a 1-byte data value. static uint8_t mem_get_1(uint32_t addr) { uint8_t *p; p = mem_find(addr,0,"get_1",P_R); if (trc_if(TRC_MEM)) mem_rw('r',addr,*p); return(*p); } // Set a 1-byte data value. static void mem_set_1(uint32_t addr, uint8_t v) { uint8_t *p; p = mem_find(addr,0,"set_1",P_W); if (trc_if(TRC_MEM)) mem_rw('w',addr,v); *p = v; } /* * Convert an underlying OS errno to an emulated-OS errno. * * We do not assume the underlying OS supports all the errnos the * emulator does. */ static uint32_t os2em_errno(int err) { switch (errno) { #ifdef EPERM case EPERM: return(em_EPERM); break; #endif #ifdef ENOENT case ENOENT: return(em_ENOENT); break; #endif #ifdef ESRCH case ESRCH: return(em_ESRCH); break; #endif #ifdef EINTR case EINTR: return(em_EINTR); break; #endif #ifdef EIO case EIO: return(em_EIO); break; #endif #ifdef ENXIO case ENXIO: return(em_ENXIO); break; #endif #ifdef E2BIG case E2BIG: return(em_E2BIG); break; #endif #ifdef ENOEXEC case ENOEXEC: return(em_ENOEXEC); break; #endif #ifdef EBADF case EBADF: return(em_EBADF); break; #endif #ifdef ECHILD case ECHILD: return(em_ECHILD); break; #endif #ifdef EDEADLK case EDEADLK: return(em_EDEADLK); break; #endif #ifdef ENOMEM case ENOMEM: return(em_ENOMEM); break; #endif #ifdef EACCES case EACCES: return(em_EACCES); break; #endif #ifdef EFAULT case EFAULT: return(em_EFAULT); break; #endif #ifdef ENOTBLK case ENOTBLK: return(em_ENOTBLK); break; #endif #ifdef EBUSY case EBUSY: return(em_EBUSY); break; #endif #ifdef EEXIST case EEXIST: return(em_EEXIST); break; #endif #ifdef EXDEV case EXDEV: return(em_EXDEV); break; #endif #ifdef ENODEV case ENODEV: return(em_ENODEV); break; #endif #ifdef ENOTDIR case ENOTDIR: return(em_ENOTDIR); break; #endif #ifdef EISDIR case EISDIR: return(em_EISDIR); break; #endif #ifdef EINVAL case EINVAL: return(em_EINVAL); break; #endif #ifdef ENFILE case ENFILE: return(em_ENFILE); break; #endif #ifdef EMFILE case EMFILE: return(em_EMFILE); break; #endif #ifdef ENOTTY case ENOTTY: return(em_ENOTTY); break; #endif #ifdef ETXTBSY case ETXTBSY: return(em_ETXTBSY); break; #endif #ifdef EFBIG case EFBIG: return(em_EFBIG); break; #endif #ifdef ENOSPC case ENOSPC: return(em_ENOSPC); break; #endif #ifdef ESPIPE case ESPIPE: return(em_ESPIPE); break; #endif #ifdef EROFS case EROFS: return(em_EROFS); break; #endif #ifdef EMLINK case EMLINK: return(em_EMLINK); break; #endif #ifdef EPIPE case EPIPE: return(em_EPIPE); break; #endif #ifdef EDOM case EDOM: return(em_EDOM); break; #endif #ifdef ERANGE case ERANGE: return(em_ERANGE); break; #endif #ifdef EAGAIN case EAGAIN: return(em_EAGAIN); break; #endif #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EAGAIN != EWOULDBLOCK)) case EWOUDBLOCK: return(em_EWOULDBLOCK); break; #endif #ifdef EINPROGRESS case EINPROGRESS: return(em_EINPROGRESS); break; #endif #ifdef EALREADY case EALREADY: return(em_EALREADY); break; #endif #ifdef ENOTSOCK case ENOTSOCK: return(em_ENOTSOCK); break; #endif #ifdef EDESTADDRREQ case EDESTADDRREQ: return(em_EDESTADDRREQ); break; #endif #ifdef EMSGSIZE case EMSGSIZE: return(em_EMSGSIZE); break; #endif #ifdef EPROTOTYPE case EPROTOTYPE: return(em_EPROTOTYPE); break; #endif #ifdef ENOPROTOOPT case ENOPROTOOPT: return(em_ENOPROTOOPT); break; #endif #ifdef EPROTONOSUPPORT case EPROTONOSUPPORT: return(em_EPROTONOSUPPORT); break; #endif #ifdef ESOCKTNOSUPPORT case ESOCKTNOSUPPORT: return(em_ESOCKTNOSUPPORT); break; #endif #ifdef EOPNOTSUPP case EOPNOTSUPP: return(em_EOPNOTSUPP); break; #endif #ifdef EPFNOSUPPORT case EPFNOSUPPORT: return(em_EPFNOSUPPORT); break; #endif #ifdef EAFNOSUPPORT case EAFNOSUPPORT: return(em_EAFNOSUPPORT); break; #endif #ifdef EADDRINUSE case EADDRINUSE: return(em_EADDRINUSE); break; #endif #ifdef EADDRNOTAVAIL case EADDRNOTAVAIL: return(em_EADDRNOTAVAIL); break; #endif #ifdef ENETDOWN case ENETDOWN: return(em_ENETDOWN); break; #endif #ifdef ENETUNREACH case ENETUNREACH: return(em_ENETUNREACH); break; #endif #ifdef ENETRESET case ENETRESET: return(em_ENETRESET); break; #endif #ifdef ECONNABORTED case ECONNABORTED: return(em_ECONNABORTED); break; #endif #ifdef ECONNRESET case ECONNRESET: return(em_ECONNRESET); break; #endif #ifdef ENOBUFS case ENOBUFS: return(em_ENOBUFS); break; #endif #ifdef EISCONN case EISCONN: return(em_EISCONN); break; #endif #ifdef ENOTCONN case ENOTCONN: return(em_ENOTCONN); break; #endif #ifdef ESHUTDOWN case ESHUTDOWN: return(em_ESHUTDOWN); break; #endif #ifdef ETOOMANYREFS case ETOOMANYREFS: return(em_ETOOMANYREFS); break; #endif #ifdef ETIMEDOUT case ETIMEDOUT: return(em_ETIMEDOUT); break; #endif #ifdef ECONNREFUSED case ECONNREFUSED: return(em_ECONNREFUSED); break; #endif #ifdef ELOOP case ELOOP: return(em_ELOOP); break; #endif #ifdef ENAMETOOLONG case ENAMETOOLONG: return(em_ENAMETOOLONG); break; #endif #ifdef EHOSTDOWN case EHOSTDOWN: return(em_EHOSTDOWN); break; #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: return(em_EHOSTUNREACH); break; #endif #ifdef ENOTEMPTY case ENOTEMPTY: return(em_ENOTEMPTY); break; #endif #ifdef EPROCLIM case EPROCLIM: return(em_EPROCLIM); break; #endif #ifdef EUSERS case EUSERS: return(em_EUSERS); break; #endif #ifdef EDQUOT case EDQUOT: return(em_EDQUOT); break; #endif #ifdef ESTALE case ESTALE: return(em_ESTALE); break; #endif #ifdef EREMOTE case EREMOTE: return(em_EREMOTE); break; #endif #ifdef EBADRPC case EBADRPC: return(em_EBADRPC); break; #endif #ifdef ERPCMISMATCH case ERPCMISMATCH: return(em_ERPCMISMATCH); break; #endif #ifdef EPROGUNAVAIL case EPROGUNAVAIL: return(em_EPROGUNAVAIL); break; #endif #ifdef EPROGMISMATCH case EPROGMISMATCH: return(em_EPROGMISMATCH); break; #endif #ifdef EPROCUNAVAIL case EPROCUNAVAIL: return(em_EPROCUNAVAIL); break; #endif #ifdef ENOLCK case ENOLCK: return(em_ENOLCK); break; #endif #ifdef ENOSYS case ENOSYS: return(em_ENOSYS); break; #endif #ifdef EFTYPE case EFTYPE: return(em_EFTYPE); break; #endif #ifdef EAUTH case EAUTH: return(em_EAUTH); break; #endif #ifdef ENEEDAUTH case ENEEDAUTH: return(em_ENEEDAUTH); break; #endif #ifdef EIDRM case EIDRM: return(em_EIDRM); break; #endif #ifdef ENOMSG case ENOMSG: return(em_ENOMSG); break; #endif #ifdef EOVERFLOW case EOVERFLOW: return(em_EOVERFLOW); break; #endif #ifdef ENOTPLAIN case ENOTPLAIN: return(em_ENOTPLAIN); break; #endif } printf("Unmappable errno %d\n",err); top(); } /* * Convert an emulated-OS signal number to an underlying-OS signal * number. */ static int em2os_signal(uint32_t sig) { switch (sig) { case em_SIGHUP: return(SIGHUP); break; case em_SIGINT: return(SIGINT); break; case em_SIGQUIT: return(SIGQUIT); break; case em_SIGILL: return(SIGILL); break; case em_SIGTRAP: return(SIGTRAP); break; case em_SIGABRT: return(SIGABRT); break; case em_SIGEMT: return(SIGEMT); break; case em_SIGFPE: return(SIGFPE); break; case em_SIGKILL: return(SIGKILL); break; case em_SIGBUS: return(SIGBUS); break; case em_SIGSEGV: return(SIGSEGV); break; case em_SIGSYS: return(SIGSYS); break; case em_SIGPIPE: return(SIGPIPE); break; case em_SIGALRM: return(SIGALRM); break; case em_SIGTERM: return(SIGTERM); break; case em_SIGURG: return(SIGURG); break; case em_SIGSTOP: return(SIGSTOP); break; case em_SIGTSTP: return(SIGTSTP); break; case em_SIGCONT: return(SIGCONT); break; case em_SIGCHLD: return(SIGCHLD); break; case em_SIGTTIN: return(SIGTTIN); break; case em_SIGTTOU: return(SIGTTOU); break; case em_SIGIO: return(SIGIO); break; case em_SIGXCPU: return(SIGXCPU); break; case em_SIGXFSZ: return(SIGXFSZ); break; case em_SIGVTALRM: return(SIGVTALRM); break; case em_SIGPROF: return(SIGPROF); break; case em_SIGWINCH: return(SIGWINCH); break; case em_SIGINFO: return(SIGINFO); break; case em_SIGUSR1: return(SIGUSR1); break; case em_SIGUSR2: return(SIGUSR2); break; case em_SIGPWR: return(SIGPWR); break; } return(0); } /* * Convert an underlying-OS signal number to an emulated-OS signal * number. */ static int os2em_signal(uint32_t sig) { switch (sig) { case SIGHUP: return(em_SIGHUP); break; case SIGINT: return(em_SIGINT); break; case SIGQUIT: return(em_SIGQUIT); break; case SIGILL: return(em_SIGILL); break; case SIGTRAP: return(em_SIGTRAP); break; case SIGABRT: return(em_SIGABRT); break; case SIGEMT: return(em_SIGEMT); break; case SIGFPE: return(em_SIGFPE); break; case SIGKILL: return(em_SIGKILL); break; case SIGBUS: return(em_SIGBUS); break; case SIGSEGV: return(em_SIGSEGV); break; case SIGSYS: return(em_SIGSYS); break; case SIGPIPE: return(em_SIGPIPE); break; case SIGALRM: return(em_SIGALRM); break; case SIGTERM: return(em_SIGTERM); break; case SIGURG: return(em_SIGURG); break; case SIGSTOP: return(em_SIGSTOP); break; case SIGTSTP: return(em_SIGTSTP); break; case SIGCONT: return(em_SIGCONT); break; case SIGCHLD: return(em_SIGCHLD); break; case SIGTTIN: return(em_SIGTTIN); break; case SIGTTOU: return(em_SIGTTOU); break; case SIGIO: return(em_SIGIO); break; case SIGXCPU: return(em_SIGXCPU); break; case SIGXFSZ: return(em_SIGXFSZ); break; case SIGVTALRM: return(em_SIGVTALRM); break; case SIGPROF: return(em_SIGPROF); break; case SIGWINCH: return(em_SIGWINCH); break; case SIGINFO: return(em_SIGINFO); break; case SIGUSR1: return(em_SIGUSR1); break; case SIGUSR2: return(em_SIGUSR2); break; case SIGPWR: return(em_SIGPWR); break; } return(0); } /* * Convert an emulated-OS struct termios to an underlying-OS struct * termios. */ static void em2os_termios(uint32_t em, struct termios *os) { uint32_t v; int i; v = mem_get_4(em); os->c_iflag = ((v & em_IGNBRK) ? IGNBRK : 0) | ((v & em_BRKINT) ? BRKINT : 0) | ((v & em_IGNPAR) ? IGNPAR : 0) | ((v & em_PARMRK) ? PARMRK : 0) | ((v & em_INPCK) ? INPCK : 0) | ((v & em_ISTRIP) ? ISTRIP : 0) | ((v & em_INLCR) ? INLCR : 0) | ((v & em_IGNCR) ? IGNCR : 0) | ((v & em_ICRNL) ? ICRNL : 0) | ((v & em_IXON) ? IXON : 0) | ((v & em_IXOFF) ? IXOFF : 0) | ((v & em_IXANY) ? IXANY : 0) | ((v & em_IMAXBEL) ? IMAXBEL : 0); v = mem_get_4(em+4); os->c_oflag = ((v & em_OPOST) ? OPOST : 0) | ((v & em_ONLCR) ? ONLCR : 0) | ((v & em_OXTABS) ? OXTABS : 0) | ((v & em_ONOEOT) ? ONOEOT : 0) | ((v & em_OCRNL) ? OCRNL : 0) | ((v & em_ONOCR) ? ONOCR : 0) | ((v & em_ONLRET) ? ONLRET : 0); v = mem_get_4(em+8); os->c_cflag = ((v & em_CIGNORE) ? CIGNORE : 0) | (((v & em_CSIZE) == em_CS5) ? CS5 : 0) | (((v & em_CSIZE) == em_CS6) ? CS6 : 0) | (((v & em_CSIZE) == em_CS7) ? CS7 : 0) | (((v & em_CSIZE) == em_CS8) ? CS8 : 0) | ((v & em_CSTOPB) ? CSTOPB : 0) | ((v & em_CREAD) ? CREAD : 0) | ((v & em_PARENB) ? PARENB : 0) | ((v & em_PARODD) ? PARODD : 0) | ((v & em_HUPCL) ? HUPCL : 0) | ((v & em_CLOCAL) ? CLOCAL : 0) | ((v & em_CRTSCTS) ? CRTSCTS : 0) | ((v & em_CDTRCTS) ? CDTRCTS : 0) | ((v & em_MDMBUF) ? MDMBUF : 0); v = mem_get_4(em+12); os->c_lflag = ((v & em_ECHOKE) ? ECHOKE : 0) | ((v & em_ECHOE) ? ECHOE : 0) | ((v & em_ECHOK) ? ECHOK : 0) | ((v & em_ECHO) ? ECHO : 0) | ((v & em_ECHONL) ? ECHONL : 0) | ((v & em_ECHOPRT) ? ECHOPRT : 0) | ((v & em_ECHOCTL) ? ECHOCTL : 0) | ((v & em_ISIG) ? ISIG : 0) | ((v & em_ALTWERASE) ? ALTWERASE : 0) | ((v & em_IEXTEN) ? IEXTEN : 0) | ((v & em_EXTPROC) ? EXTPROC : 0) | ((v & em_TOSTOP) ? TOSTOP : 0) | ((v & em_FLUSHO) ? FLUSHO : 0) | ((v & em_NOKERNINFO) ? NOKERNINFO : 0) | ((v & em_PENDIN) ? PENDIN : 0) | ((v & em_NOFLSH) ? NOFLSH : 0); for (i=(sizeof(os->c_cc)/sizeof(os->c_cc[0]))-1;i>=0;i--) os->c_cc[i] = _POSIX_VDISABLE; os->c_cc[VEOF] = mem_get_1(em+16+em_VEOF); os->c_cc[VEOL] = mem_get_1(em+16+em_VEOL); os->c_cc[VEOL2] = mem_get_1(em+16+em_VEOL2); os->c_cc[VERASE] = mem_get_1(em+16+em_VERASE); os->c_cc[VWERASE] = mem_get_1(em+16+em_VWERASE); os->c_cc[VKILL] = mem_get_1(em+16+em_VKILL); os->c_cc[VREPRINT] = mem_get_1(em+16+em_VREPRINT); os->c_cc[VINTR] = mem_get_1(em+16+em_VINTR); os->c_cc[VQUIT] = mem_get_1(em+16+em_VQUIT); os->c_cc[VSUSP] = mem_get_1(em+16+em_VSUSP); os->c_cc[VDSUSP] = mem_get_1(em+16+em_VDSUSP); os->c_cc[VSTART] = mem_get_1(em+16+em_VSTART); os->c_cc[VSTOP] = mem_get_1(em+16+em_VSTOP); os->c_cc[VLNEXT] = mem_get_1(em+16+em_VLNEXT); os->c_cc[VDISCARD] = mem_get_1(em+16+em_VDISCARD); os->c_cc[VMIN] = mem_get_1(em+16+em_VMIN); os->c_cc[VTIME] = mem_get_1(em+16+em_VTIME); os->c_cc[VSTATUS] = mem_get_1(em+16+em_VSTATUS); os->c_ispeed = mem_get_4(em+36); os->c_ospeed = mem_get_4(em+40); } /* * Convert an underlying-OS struct termios to an emulated-OS struct * termios. */ static void os2em_termios(struct termios *os, uint32_t em) { mem_set_4(em, ((os->c_iflag & IGNBRK) ? em_IGNBRK : 0) | ((os->c_iflag & BRKINT) ? em_BRKINT : 0) | ((os->c_iflag & IGNPAR) ? em_IGNPAR : 0) | ((os->c_iflag & PARMRK) ? em_PARMRK : 0) | ((os->c_iflag & INPCK) ? em_INPCK : 0) | ((os->c_iflag & ISTRIP) ? em_ISTRIP : 0) | ((os->c_iflag & INLCR) ? em_INLCR : 0) | ((os->c_iflag & IGNCR) ? em_IGNCR : 0) | ((os->c_iflag & ICRNL) ? em_ICRNL : 0) | ((os->c_iflag & IXON) ? em_IXON : 0) | ((os->c_iflag & IXOFF) ? em_IXOFF : 0) | ((os->c_iflag & IXANY) ? em_IXANY : 0) | ((os->c_iflag & IMAXBEL) ? em_IMAXBEL : 0) ); mem_set_4(em+4, ((os->c_oflag & OPOST) ? em_OPOST : 0) | ((os->c_oflag & ONLCR) ? em_ONLCR : 0) | ((os->c_oflag & OXTABS) ? em_OXTABS : 0) | ((os->c_oflag & ONOEOT) ? em_ONOEOT : 0) | ((os->c_oflag & OCRNL) ? em_OCRNL : 0) | ((os->c_oflag & ONOCR) ? em_ONOCR : 0) | ((os->c_oflag & ONLRET) ? em_ONLRET : 0) ); mem_set_4(em+8, ((os->c_cflag & CIGNORE) ? em_CIGNORE : 0) | (((os->c_cflag & CSIZE) == CS5) ? em_CS5 : 0) | (((os->c_cflag & CSIZE) == CS6) ? em_CS6 : 0) | (((os->c_cflag & CSIZE) == CS7) ? em_CS7 : 0) | (((os->c_cflag & CSIZE) == CS8) ? em_CS8 : 0) | ((os->c_cflag & CSTOPB) ? em_CSTOPB : 0) | ((os->c_cflag & CREAD) ? em_CREAD : 0) | ((os->c_cflag & PARENB) ? em_PARENB : 0) | ((os->c_cflag & PARODD) ? em_PARODD : 0) | ((os->c_cflag & HUPCL) ? em_HUPCL : 0) | ((os->c_cflag & CLOCAL) ? em_CLOCAL : 0) | ((os->c_cflag & CRTSCTS) ? em_CRTSCTS : 0) | ((os->c_cflag & CDTRCTS) ? em_CDTRCTS : 0) | ((os->c_cflag & MDMBUF) ? em_MDMBUF : 0) ); mem_set_4(em+12, ((os->c_lflag & ECHOKE) ? em_ECHOKE : 0) | ((os->c_lflag & ECHOE) ? em_ECHOE : 0) | ((os->c_lflag & ECHOK) ? em_ECHOK : 0) | ((os->c_lflag & ECHO) ? em_ECHO : 0) | ((os->c_lflag & ECHONL) ? em_ECHONL : 0) | ((os->c_lflag & ECHOPRT) ? em_ECHOPRT : 0) | ((os->c_lflag & ECHOCTL) ? em_ECHOCTL : 0) | ((os->c_lflag & ISIG) ? em_ISIG : 0) | ((os->c_lflag & ALTWERASE) ? em_ALTWERASE : 0) | ((os->c_lflag & IEXTEN) ? em_IEXTEN : 0) | ((os->c_lflag & EXTPROC) ? em_EXTPROC : 0) | ((os->c_lflag & TOSTOP) ? em_TOSTOP : 0) | ((os->c_lflag & FLUSHO) ? em_FLUSHO : 0) | ((os->c_lflag & NOKERNINFO) ? em_NOKERNINFO : 0) | ((os->c_lflag & PENDIN) ? em_PENDIN : 0) | ((os->c_lflag & NOFLSH) ? em_NOFLSH : 0) ); mem_set_1(em+16+em_VEOF,os->c_cc[VEOF]); mem_set_1(em+16+em_VEOL,os->c_cc[VEOL]); mem_set_1(em+16+em_VEOL2,os->c_cc[VEOL2]); mem_set_1(em+16+em_VERASE,os->c_cc[VERASE]); mem_set_1(em+16+em_VWERASE,os->c_cc[VWERASE]); mem_set_1(em+16+em_VKILL,os->c_cc[VKILL]); mem_set_1(em+16+em_VREPRINT,os->c_cc[VREPRINT]); mem_set_1(em+16+em_VINTR,os->c_cc[VINTR]); mem_set_1(em+16+em_VQUIT,os->c_cc[VQUIT]); mem_set_1(em+16+em_VSUSP,os->c_cc[VSUSP]); mem_set_1(em+16+em_VDSUSP,os->c_cc[VDSUSP]); mem_set_1(em+16+em_VSTART,os->c_cc[VSTART]); mem_set_1(em+16+em_VSTOP,os->c_cc[VSTOP]); mem_set_1(em+16+em_VLNEXT,os->c_cc[VLNEXT]); mem_set_1(em+16+em_VDISCARD,os->c_cc[VDISCARD]); mem_set_1(em+16+em_VMIN,os->c_cc[VMIN]); mem_set_1(em+16+em_VTIME,os->c_cc[VTIME]); mem_set_1(em+16+em_VSTATUS,os->c_cc[VSTATUS]); mem_set_4(em+36,os->c_ispeed); mem_set_4(em+40,os->c_ospeed); } /* * strerror(), except it takse an emulated-OS errno (and the returned * strings include the short names as well as the messages). */ static const char *em_strerror(uint32_t e) { switch (e) { case em_EPERM: return("EPERM, Operation not permitted"); break; case em_ENOENT: return("ENOENT, No such file or directory"); break; case em_ESRCH: return("ESRCH, No such process"); break; case em_EINTR: return("EINTR, Interrupted system call"); break; case em_EIO: return("EIO, Input/output error"); break; case em_ENXIO: return("ENXIO, Device not configured"); break; case em_E2BIG: return("E2BIG, Argument list too long"); break; case em_ENOEXEC: return("ENOEXEC, Exec format error"); break; case em_EBADF: return("EBADF, Bad file descriptor"); break; case em_ECHILD: return("ECHILD, No child processes"); break; case em_EDEADLK: return("EDEADLK, Resource deadlock avoided"); break; case em_ENOMEM: return("ENOMEM, Cannot allocate memory"); break; case em_EACCES: return("EACCES, Permission denied"); break; case em_EFAULT: return("EFAULT, Bad address"); break; case em_ENOTBLK: return("ENOTBLK, Block device required"); break; case em_EBUSY: return("EBUSY, Device busy"); break; case em_EEXIST: return("EEXIST, File exists"); break; case em_EXDEV: return("EXDEV, Cross-device link"); break; case em_ENODEV: return("ENODEV, Operation not supported by device"); break; case em_ENOTDIR: return("ENOTDIR, Not a directory"); break; case em_EISDIR: return("EISDIR, Is a directory"); break; case em_EINVAL: return("EINVAL, Invalid argument"); break; case em_ENFILE: return("ENFILE, Too many open files in system"); break; case em_EMFILE: return("EMFILE, Too many open files"); break; case em_ENOTTY: return("ENOTTY, Inappropriate ioctl for device"); break; case em_ETXTBSY: return("ETXTBSY, Text file busy"); break; case em_EFBIG: return("EFBIG, File too large"); break; case em_ENOSPC: return("ENOSPC, No space left on device"); break; case em_ESPIPE: return("ESPIPE, Illegal seek"); break; case em_EROFS: return("EROFS, Read-only file system"); break; case em_EMLINK: return("EMLINK, Too many links"); break; case em_EPIPE: return("EPIPE, Broken pipe"); break; case em_EDOM: return("EDOM, Numerical argument out of domain"); break; case em_ERANGE: return("ERANGE, Result too large"); break; case em_EAGAIN: return("EAGAIN/EWOULDBLOCK, Resource temporarily unavailable"); break; case em_EINPROGRESS: return("EINPROGRESS, Operation now in progress"); break; case em_EALREADY: return("EALREADY, Operation already in progress"); break; case em_ENOTSOCK: return("ENOTSOCK, Socket operation on non-socket"); break; case em_EDESTADDRREQ: return("EDESTADDRREQ, Destination address required"); break; case em_EMSGSIZE: return("EMSGSIZE, Message too long"); break; case em_EPROTOTYPE: return("EPROTOTYPE, Protocol wrong type for socket"); break; case em_ENOPROTOOPT: return("ENOPROTOOPT, Protocol not available"); break; case em_EPROTONOSUPPORT: return("EPROTONOSUPPORT, Protocol not supported"); break; case em_ESOCKTNOSUPPORT: return("ESOCKTNOSUPPORT, Socket type not supported"); break; case em_EOPNOTSUPP: return("EOPNOTSUPP, Operation not supported"); break; case em_EPFNOSUPPORT: return("EPFNOSUPPORT, Protocol family not supported"); break; case em_EAFNOSUPPORT: return("EAFNOSUPPORT, Address family not supported by protocol family"); break; case em_EADDRINUSE: return("EADDRINUSE, Address already in use"); break; case em_EADDRNOTAVAIL: return("EADDRNOTAVAIL, Can't assign requested address"); break; case em_ENETDOWN: return("ENETDOWN, Network is down"); break; case em_ENETUNREACH: return("ENETUNREACH, Network is unreachable"); break; case em_ENETRESET: return("ENETRESET, Network dropped connection on reset"); break; case em_ECONNABORTED: return("ECONNABORTED, Software caused connection abort"); break; case em_ECONNRESET: return("ECONNRESET, Connection reset by peer"); break; case em_ENOBUFS: return("ENOBUFS, No buffer space available"); break; case em_EISCONN: return("EISCONN, Socket is already connected"); break; case em_ENOTCONN: return("ENOTCONN, Socket is not connected"); break; case em_ESHUTDOWN: return("ESHUTDOWN, Can't send after socket shutdown"); break; case em_ETOOMANYREFS: return("ETOOMANYREFS, Too many references: can't splice"); break; case em_ETIMEDOUT: return("ETIMEDOUT, Connection timed out"); break; case em_ECONNREFUSED: return("ECONNREFUSED, Connection refused"); break; case em_ELOOP: return("ELOOP, Too many levels of symbolic links"); break; case em_ENAMETOOLONG: return("ENAMETOOLONG, File name too long"); break; case em_EHOSTDOWN: return("EHOSTDOWN, Host is down"); break; case em_EHOSTUNREACH: return("EHOSTUNREACH, No route to host"); break; case em_ENOTEMPTY: return("ENOTEMPTY, Directory not empty"); break; case em_EPROCLIM: return("EPROCLIM, Too many processes"); break; case em_EUSERS: return("EUSERS, Too many users"); break; case em_EDQUOT: return("EDQUOT, Disc quota exceeded"); break; case em_ESTALE: return("ESTALE, Stale NFS file handle"); break; case em_EREMOTE: return("EREMOTE, Too many levels of remote in path"); break; case em_EBADRPC: return("EBADRPC, RPC struct is bad"); break; case em_ERPCMISMATCH: return("ERPCMISMATCH, RPC version wrong"); break; case em_EPROGUNAVAIL: return("EPROGUNAVAIL, RPC prog. not avail"); break; case em_EPROGMISMATCH: return("EPROGMISMATCH, Program version wrong"); break; case em_EPROCUNAVAIL: return("EPROCUNAVAIL, Bad procedure for program"); break; case em_ENOLCK: return("ENOLCK, No locks available"); break; case em_ENOSYS: return("ENOSYS, Function not implemented"); break; case em_EFTYPE: return("EFTYPE, Inappropriate file type or format"); break; case em_EAUTH: return("EAUTH, Authentication error"); break; case em_ENEEDAUTH: return("ENEEDAUTH, Need authenticator"); break; case em_EIDRM: return("EIDRM, Identifier removed"); break; case em_ENOMSG: return("ENOMSG, No message of desired type"); break; case em_EOVERFLOW: return("EOVERFLOW, Value too large to be stored in data type"); break; case em_ENOTPLAIN: return("ENOTPLAIN, Not a plain file"); break; } return("unknown"); } /* * Given a VFBKIND, return a short string for human consumption * describing it. */ static const char *vfb_kind_str(VFBKIND k) { switch (k) { case VFB_OPEN: return("OPEN"); break; case VFB_CLOSE: return("CLOSE"); break; case VFB_DUP2: return("DUP2"); break; } return("unknown"); } /* * Given a VFORKSTAGE, return a short string for human consumption * describing it. */ static const char *vfork_stage_str(VFORKSTAGE s) { switch (s) { case VFORK_NONE: return("NONE"); break; case VFORK_START: return("START"); break; case VFORK_FAIL: return("FAIL"); break; case VFORK_SUCCESS: return("SUCCESS"); break; } return("unknown"); } /* * Given an emulated-OS signal number, return its short string name. */ static const char *em_signame(uint32_t s, const char *unk) { switch (s) { case em_SIGHUP: return("SIGHUP"); break; case em_SIGINT: return("SIGINT"); break; case em_SIGQUIT: return("SIGQUIT"); break; case em_SIGILL: return("SIGILL"); break; case em_SIGTRAP: return("SIGTRAP"); break; case em_SIGABRT: return("SIGABRT"); break; case em_SIGEMT: return("SIGEMT"); break; case em_SIGFPE: return("SIGFPE"); break; case em_SIGKILL: return("SIGKILL"); break; case em_SIGBUS: return("SIGBUS"); break; case em_SIGSEGV: return("SIGSEGV"); break; case em_SIGSYS: return("SIGSYS"); break; case em_SIGPIPE: return("SIGPIPE"); break; case em_SIGALRM: return("SIGALRM"); break; case em_SIGTERM: return("SIGTERM"); break; case em_SIGURG: return("SIGURG"); break; case em_SIGSTOP: return("SIGSTOP"); break; case em_SIGTSTP: return("SIGTSTP"); break; case em_SIGCONT: return("SIGCONT"); break; case em_SIGCHLD: return("SIGCHLD"); break; case em_SIGTTIN: return("SIGTTIN"); break; case em_SIGTTOU: return("SIGTTOU"); break; case em_SIGIO: return("SIGIO"); break; case em_SIGXCPU: return("SIGXCPU"); break; case em_SIGXFSZ: return("SIGXFSZ"); break; case em_SIGVTALRM: return("SIGVTALRM"); break; case em_SIGPROF: return("SIGPROF"); break; case em_SIGWINCH: return("SIGWINCH"); break; case em_SIGINFO: return("SIGINFO"); break; case em_SIGUSR1: return("SIGUSR1"); break; case em_SIGUSR2: return("SIGUSR2"); break; case em_SIGPWR: return("SIGPWR"); break; } return(unk); } /* * Set the unset-value action, based on a string name. This is * suitable for use with command-line arguments. */ static int set_unset_action(const char *s) { if (! strcmp(s,"ignore")) { unset_action = UNSET_IGNORE; return(0); } if (! strcmp(s,"warn")) { unset_action = UNSET_WARN; return(0); } if (! strcmp(s,"error")) { unset_action = UNSET_ERROR; return(0); } fprintf(stderr,"%s: unrecognized unset action `%s'\n",__progname,s); return(1); } /* * Handle the command line. */ static void handleargs(int ac, char **av) { int skip; int errs; skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { exe = *av; break; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs = 1; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-unset")) { WANTARG(); errs |= set_unset_action(av[skip]); continue; } if (!strcmp(*av,"-forkwait")) { forkwait = 1; continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs = 1; } if (! exe) { fprintf(stderr,"%s: need executable filename\n",__progname); errs = 1; } nargs = ac - 1; args = av + 1; if (errs) exit(1); } static MEMSEGOPS memseg_ops_malloc; // forward /* * Create and return a new malloc()-backed MEMSEG, given base, size, * and protection. This does not deal with maintaining the * no-overlapping invariant; the caller must have already dealt with * that. This does link the new MEMSEG into vm, though. * * XXX Maybe collapse with any adjacent malloc MEMSEGs with identical * protection? */ static MEMSEG *memseg_new_malloc(uint32_t base, uint32_t size, unsigned char prot) { MEMSEG *n; MEMSEG_PRIV_MALLOC *p; n = malloc(sizeof(MEMSEG)); p = malloc(sizeof(MEMSEG_PRIV_MALLOC)); n->base = base; n->size = size; n->end = base + size; n->prot = prot; p->tofree = malloc(size); n->data = p->tofree; n->ops = &memseg_ops_malloc; n->priv = p; n->link = vm; vm = n; return(n); } /* * The done method for malloc memsegs. Free the underlying memory and * the private pointer. */ static void memseg_done_malloc(MEMSEG *ms) { free(((MEMSEG_PRIV_MALLOC *)ms->priv)->tofree); free(ms->priv); } /* * The curtail method for malloc MEMSEGs. Just adjust size and end; * don't worry about freeing partial memory blocks - the code * simplicity wins over the memory resource saving. */ static void memseg_curtail_malloc(MEMSEG *ms, uint32_t by) { if (by >= ms->size) panic("impossible malloc curtail"); ms->size -= by; ms->end -= by; } /* * The behead method for malloc MEMSEGs. Just adjust base, size, and * data; as for curtail, above, it's not worth trying to economize on * memory. */ static void memseg_behead_malloc(MEMSEG *ms, uint32_t by) { if (by >= ms->size) panic("impossible malloc behead"); ms->size -= by; ms->base += by; ms->data += by; } /* * The split method for malloc MEMSEGs. Just allocate a new memory * MEMSEG, copy, and adjust the old one. * * XXX Arguably should copy the smaller piece. * * XXX Arguably should refcount the backing memory and have both * MEMSEGs refer to it. */ static MEMSEG *memseg_split_malloc(MEMSEG *ms, uint32_t part1, uint32_t part2) { MEMSEG *n; if ((part1 > ms->size) || (part2 > ms->size) || (part1+part2 > ms->size)) panic("impossible malloc split"); n = memseg_new_malloc(ms->end-part2,part2,ms->prot); bcopy(ms->data+(ms->size-part2),n->data,part2); ms->size = part1; ms->end = ms->base + part1; return(n); } /* * The postexec method for malloc MEMSEGs. This is boring; malloc * MEMSEGs never survive exec()s. */ static int memseg_postexec_malloc(MEMSEG *ms __attribute__((__unused__))) { return(0); } /* * The MEMSEGOPS for malloc MEMSEGs. */ static MEMSEGOPS memseg_ops_malloc = MEMSEGOPS_INIT(malloc); static MEMSEGOPS memseg_ops_mmap; // forward /* * Create and return a new mmap()-backed MEMSEG, given the usual base, * size, and protection, and mmap-specific values for the mmap flags * and the void * returned by the underlying-OS mmap(). */ static MEMSEG *memseg_new_mmap(uint32_t base, uint32_t size, unsigned char prot, uint32_t mapflags, void *mapped) { MEMSEG *n; MEMSEG_PRIV_MMAP *p; n = malloc(sizeof(MEMSEG)); p = malloc(sizeof(MEMSEG_PRIV_MMAP)); n->base = base; n->size = size; n->end = base + size; n->prot = prot; p->refcnt = 1; p->mapped = mapped; p->size = size; p->mapflags = mapflags; n->data = mapped; n->ops = &memseg_ops_mmap; n->priv = p; n->link = vm; vm = n; return(n); } /* * The done method for mmap()-backed MEMSEGs. Drop a reference, and, * if it was the last erference, munmap() the underlying memory and * free the private data struct. */ static void memseg_done_mmap(MEMSEG *ms) { MEMSEG_PRIV_MMAP *p; p = ms->priv; p->refcnt --; if (p->refcnt < 1) { munmap(p->mapped,p->size); free(p); } } /* * The curtail method for mmap()-backed MEMSEGs. Just leave the * underlying mmap()ped memory alone and adjust the descriptive * values. */ static void memseg_curtail_mmap(MEMSEG *ms, uint32_t by) { if (by >= ms->size) panic("impossible mmap curtail"); ms->size -= by; ms->end -= by; } /* * The behead method for mmap()-backed MEMSEGs. Just leave the * underlying mmap()ped memory alone and adjust the descriptive * values. */ static void memseg_behead_mmap(MEMSEG *ms, uint32_t by) { if (by >= ms->size) panic("impossible mmap behead"); ms->size -= by; ms->base += by; ms->data += by; } /* * The split method for mmap()-backed MEMSEGs. This is why * MEMSEG_PRIV_MMAPs have refcounts, so that we can, here, generate * two MEMSEGs backed by the same underlying mmap(). */ static MEMSEG *memseg_split_mmap(MEMSEG *ms, uint32_t part1, uint32_t part2) { MEMSEG *n; MEMSEG_PRIV_MMAP *p; if ((part1 > ms->size) || (part2 > ms->size) || (part1+part2 > ms->size)) panic("impossible mmap split"); p = ms->priv; n = malloc(sizeof(MEMSEG)); n->base = ms->base + ms->size - part2; n->size = part2; n->end = ms->end; n->prot = ms->prot; p->refcnt ++; n->data = ms->data + (ms->size - part2); n->ops = ms->ops; n->priv = p; n->link = vm; vm = n; return(n); } /* * The postexec method for mmap()-backed MEMSEGs. Generally, these go * away on exec() just as malloc MEMSEGs do, but if mapped with * MAP_INHERIT they stay around. */ static int memseg_postexec_mmap(MEMSEG *ms) { return((((MEMSEG_PRIV_MMAP *)ms->priv)->mapflags & em_MAP_INHERIT) ? 1 : 0); } /* * The MEMSEGOPS for mmap MEMSEGs. */ static MEMSEGOPS memseg_ops_mmap = MEMSEGOPS_INIT(mmap); /* * Go through vm and remove anything that overlaps with the area * described by newbase and newsize, except that ignore, if non-nil, * is a MEMSEG that is to be left untouched even if it does overlap. * This is used to punch a hole in the VM space, if necessary, to * accommodate a new MEMSEG. (This is why ignore exists, so that this * can be called after the new MEMSEG is created and linked in.) */ static void memseg_clear_conflict(uint32_t newbase, uint32_t newsize, MEMSEG *ignore) { uint32_t newend; MEMSEG *ms; MEMSEG **msp; newend = newbase + newsize; if (newsize < 1) panic("empty memseg"); if ((newbase & (PAGE_SIZE-1)) || (newsize & (PAGE_SIZE-1))) panic("misaligned memseg"); if (newend < newbase) panic("va wraparound"); msp = &vm; while ((ms = *msp)) { if (ms != ignore) { /* * Each existing memseg may relate to the new memseg in one of * 13 ways (nnn=new, ooo=old, ***=both): * * (a) ...ooo...nnn... * (b) ...ooonnn... * (c) ...ooo***nnn... * (d) ...ooo***... * (e) ...ooo***ooo... * (f) ...***nnn... * (g) ...******... * (h) ...******ooo... * (i) ...nnn***nnn... * (j) ...nnn***... * (k) ...nnn***ooo... * (l) ...nnnooo... * (m) ...nnn...ooo... */ if ((ms->base >= newbase) && (ms->end <= newend)) { // Cases f, g, i, j: destroy old entirely *msp = ms->link; (*ms->ops->done)(ms); free(ms); continue; } else if ((ms->end <= newbase) || (ms->base >= newend)) { // Cases a, b, l, m: do nothing } else if (ms->end < newend) { // Cases c, d: curtail old (*ms->ops->curtail)(ms,ms->end-newbase); } else if (ms->base >= newbase) { // Cases h, k: behead old (*ms->ops->behead)(ms,newend-ms->base); } else { // Case e: split old (*ms->ops->split)(ms,newbase-ms->base,ms->end-newend); } } msp = &ms->link; } } /* * Destroy a memory space. This is used upon a successful exec() to * dispose of the old VM, and, during vfork, by the parent to dispose * of its copy of the child's VM. */ static void vm_destroy(MEMSEG *vm) { MEMSEG *ms; while ((ms = vm)) { vm = ms->link; (*ms->ops->done)(ms); free(ms); } } /* * Used after a successful exec() to replace the old VM with the new. * The old VM is in vm; the new VM is passed in. First we go through * the old VM and throw out everything that shouldn't remain (which in * most cases means all of it, but mmap() with MAP_INHERIT can create * segments which survive). Then we drop the new VM on top of it. * * In case of a conflict between a surviving piece of the old space and * a piece of the new space, the new space wins. I'm not sure what * real NetBSD/sparc 1.4T does in this case; it matters only if mmap() * was given an address it must map at which overlaps part of the new * text, data, or stack. (System-selected mmap locations can't * overlap any of those.) */ static void vm_postexec(MEMSEG *newvm) { MEMSEG *list; MEMSEG **tail; MEMSEG *s; list = vm; tail = &vm; while (vm) { s = vm; vm = s->link; if ((*s->ops->postexec)(s)) { *tail = s; tail = &s->link; } else { (*s->ops->done)(s); free(s); } } *tail = 0; while (newvm) { s = newvm; newvm = s->link; memseg_clear_conflict(s->base,s->size,0); s->link = vm; vm = s; } } /* * Create and return a new malloc MEMSEG for a given base address, * size, and protection. This is suitable for use when setting up the * address space for exec() and is currently just memseg_new_malloc() * plus memseg_clear_conflict(). */ static MEMSEG *memseg_mem(uint32_t va, uint32_t size, unsigned int prot) { MEMSEG *n; n = memseg_new_malloc(va,size,prot); memseg_clear_conflict(n->base,n->size,n); return(n); } /* * Sort vm in order of increasing addresses. We sort by base address, * but the no-overlap invariant means that the order induced by this * is equivalent to the one induced by sorting by end address instead * (or (base+end)/2, or pretty much anything else even vaguely * sensible). * * This is used when we're looking for holes in the address space, such * as for an mmap() with no address specified. */ static void sort_vm(void) { MEMSEG *sort(MEMSEG *list) { MEMSEG *a; MEMSEG *b; MEMSEG *t; MEMSEG **lp; if (!list || !list->link) return(list); a = 0; b = 0; while (list) { t = list; list = t->link; t->link = b; b = a; a = t; } a = sort(a); b = sort(b); lp = &list; while (a || b) { if (a && (!b || (a->base < b->base))) { t = a; a = a->link; } else { t = b; b = b->link; } *lp = t; lp = &t->link; } *lp = 0; return(list); } vm = sort(vm); } /* * Look for a hole at least size bytes large in the VM space, whose * base address is at least base and whose end is no highe than max. * Return the base of the located space. If no such space can be * found, we panic (arguably we should complain and top()). */ static uint32_t find_space(uint32_t base, uint32_t size, uint32_t max) { MEMSEG *ms; uint32_t lastend; uint32_t rv; if (size & 0x80000000) panic("impossible find_space (size)"); if ((uint32_t)(base+size) < base) panic("impossible find_space (wrap)"); if (base+size > max) panic("impossible find_space (max)"); do <"found"> { sort_vm(); lastend = PAGE_SIZE; for (ms=vm;ms;ms=ms->link) { if ((ms->base >= base+size) && (ms->base-lastend >= size)) { rv = (lastend < base) ? base : lastend; break <"found">; } lastend = ms->end; if (lastend+size > max) break; } if (lastend+size <= max) { rv = (lastend < base) ? base : lastend; break <"found">; } panic("can't find space: base %08lx size %08lx max %08lx",(ULI)base,(ULI)size,(ULI)max); } while( 0); return(rv); } /* * This is used during exec() to read something out of the file being * loaded into the address space. fd is our (underlying-OS) fd onto * the executable file, buf is where to read the data into, len is the * amount to read, off is the offset into the file (suitable for * passing to pread(2)), and path and what are the path to the file * and an indication of what's being read, the latter two for error * messages, if generated. * * On success this returns 0. On failure, it returns -1, with, if * TRC_EXEC tracing is turned on, an indication of what went wrong. */ static int read_exe(int fd, void *buf, int len, off_t off, const char *path, const char *what) { int rv; rv = pread(fd,buf,len,off); if (rv < 0) { trc(TRC_EXEC,"%d: %s: %s: read: %s\n",mypid,path,what,strerror(errno)); return(-1); } if (rv == 0) { trc(TRC_EXEC,"%d: %s: %s: read EOF\n",mypid,path,what); return(-1); } if (rv != len) { trc(TRC_EXEC,"%d: %s: %s: read wanted %d, got %d\n",mypid,path,what,len,rv); return(-1); } return(0); } /* * Print the condition code bits to a FILE *. This is used when, for * example, printing (emulated) machine registers. */ /* The output generated doesn't really make sense otherwise... */ #if (CC_N != 8) || (CC_Z != 4) || (CC_V != 2) || (CC_C != 1) #error "print_cc assumptions invalid" #endif static void print_cc(FILE *to, unsigned int cc) { fprintf(to,"%c%c%c%c", (cc & CC_N) ? 'N' : '.', (cc & CC_Z) ? 'Z' : '.', (cc & CC_V) ? 'V' : '.', (cc & CC_C) ? 'C' : '.' ); } /* * Print some or all emulated machine registers. to is the FILE * to * print them to and mask is a bitmask indicating which registers to * print; the low 32 bits indicate the general registers, %g0 through * %i7, and bits above that indicate other registers, per regnames[] * and the PRINT_REGS_* defines above. */ static void print_regs(FILE *to, uint64_t mask) { int nix; int ixv[PRINT_REGS__N]; int nr; int r; int n; int i; mask &= PRINT_REGS_ALL; nix = 0; for (i=0;mask;i++) { if (mask & 1) ixv[nix++] = i; mask >>= 1; } if (nix < 1) return; nr = (nix + 3) / 4; for (r=0;r= nr) fprintf(to," "); fprintf(to,"%-3s = ",regnames[i]); switch (i) { case PRINT_REGS_Y: fprintf(to,"%08lx",(ULI)s.y); break; case PRINT_REGS_PC: fprintf(to,"%08lx",(ULI)s.pc); break; case PRINT_REGS_NPC: fprintf(to,"%08lx",(ULI)s.npc); break; case PRINT_REGS_CC: print_cc(to,s.cc); break; default: if (IS_REG_SET(i)) { fprintf(to,"%08lx",(ULI)s.regs[i]); } else { fprintf(to,"(not set)"); } break; } } fprintf(to,"\n"); } } /* * Initialize emulator state on startup. */ static void setup(void) { int i; for (i=32-1;i>=0;i--) s.regs[i] = 0; s.regset = ~(uint32_t)0; s.cc = 0; s.flags = 0; s.pc = 0; s.npc = s.pc + 4; vm = 0; s.wdepth = 0; s.instrs = 0; s.noninteractive = 0; mypid = getpid(); } /* * Copy data from the emulator into emulated memory. (The name was * inspired by the copyout() kernel routine, which performs the * analogous operation.) osbuf is the buffer to copy from, embuf is * the address in emulated VM to copy to, n is the number of bytes to * copy, what is what's being copied (for errors), and prefail is used * to handle cleanup in error cases: it is called if a memory * protection fault occurs, after the fault is detected and before the * error is printed and top() is called. prefail is designed to * permit things such as freeing malloc()ed temporaries. * * XXX prefail should go away in favour of a more general way of * wrapping err_jmp. */ static void copyout(const void *osbuf, uint32_t embuf, uint32_t n, const char *what, void (*prefail)(void)) { uint32_t part; MEMSEG *ms; int left; uint32_t bp; int i; left = n; bp = embuf; while (left > 0) { ms = memseg_find(bp,0,"read"); if (! (ms->prot & P_W)) { if (prefail) (*prefail)(); printf("%s: %08lx: not accessible\n",what,(ULI)bp); top(); } part = ms->end - bp; if (part > left) part = left; bcopy((bp-embuf)+(const char *)osbuf,ms->data+(bp-ms->base),part); if (trc_if(TRC_MEM)) for (i=0;idata[i+bp-ms->base]); bp += part; left -= part; } } /* * Open an ELF file and check that it's suitable for our use (eg, that * it's for 32-bit SPARC). This is used by exec(), factored out * because it's used both for the file being exec()ed and for the * dynamic loader used by dynamically linked executables. */ static uint32_t elf_start_load(ELF_CTX *elf, int exptype, const char *exptypestr) { __label__ enoexec_; void enoexec(void) { goto enoexec_; } elf->fd = open(elf->path,O_RDONLY,0); if (elf->fd < 0) return(os2em_errno(errno)); if (0) { enoexec_:; close(elf->fd); return(em_ENOEXEC); } if (read_exe(elf->fd,&elf->eh,sizeof(elf->eh),0,elf->path,"bad ELF file (can't read header)") < 0) enoexec(); if ( (elf->eh.e_ident[EI_MAG0] != ELFMAG0) || (elf->eh.e_ident[EI_MAG1] != ELFMAG1) || (elf->eh.e_ident[EI_MAG2] != ELFMAG2) || (elf->eh.e_ident[EI_MAG3] != ELFMAG3) ) { trc(TRC_EXEC,"%d: %s: bad ELF file (bad magic number)\n",mypid,elf->path); enoexec(); } if (elf->eh.e_ident[EI_CLASS] != ELFCLASS32) { trc(TRC_EXEC,"%d: %s: bad ELF file (class isn't 32-bit)\n",mypid,elf->path); enoexec(); } if (ELF_HALF_TO_NATIVE(elf->eh.e_machine) != EM_SPARC) { trc(TRC_EXEC,"%d: %s: bad ELF file (machine isn't SPARC)\n",mypid,elf->path); enoexec(); } if (ELF_HALF_TO_NATIVE(elf->eh.e_type) != exptype) { trc(TRC_EXEC,"%d: %s: bad ELF file (type isn't %s)\n",mypid,elf->path,exptypestr); enoexec(); } if (ELF_HALF_TO_NATIVE(elf->eh.e_phentsize) != sizeof(Elf32_Phdr)) { trc(TRC_EXEC,"%d: %s: bad ELF file (phentsize isn't sizeof(Elf32_Phdr))\n",mypid,elf->path); enoexec(); } return(0); } /* * Read the program headers from an ELF file and iterate over them, * calling the appropriate methods from ops for the entries of * interest. enoexec is called if something is found which should * provoke an ENOEXEC failure from exec(). */ static void map_psect(ELF_CTX *elf, const PSECT_OPS *ops, void (*enoexec)(void)) { int nph; Elf32_Phdr *ph; int i; void noexec(void) { free(ph); (*enoexec)(); } nph = ELF_HALF_TO_NATIVE(elf->eh.e_phnum); ph = malloc(nph*sizeof(Elf32_Phdr)); if (read_exe(elf->fd,ph,nph*sizeof(Elf32_Phdr),ELF_ADDR_TO_NATIVE(elf->eh.e_phoff),elf->path,"bad ELF file (can't read program headers)") < 0) noexec(); for (i=nph-1;i>=0;i--) { unsigned int t; t = ELF_WORD_TO_NATIVE(ph[i].p_type); switch (t) { case PT_INTERP: (*ops->pt_interp)(elf,&ph[i],&noexec); break; case PT_LOAD: (*ops->pt_load)(elf,&ph[i],&noexec); break; } } } /* * The PT_INTERP handler for the main executable: read and record the * "interpreter" (really, dynamic linker) pathname in the ELF_CTX for * later. */ static void psect_pt_interp_main(ELF_CTX *elf, Elf32_Phdr *ph, void (*err)(void)) { uint32_t fsz; fsz = ELF_WORD_TO_NATIVE(ph->p_filesz); if (fsz > em_MAXPATHLEN) { trc(TRC_EXEC,"%d: %s: bad ELF file (PT_INTERP section length %lu > max %d)\n",mypid,elf->path,(unsigned long int)fsz,em_MAXPATHLEN); (*err)(); } if (read_exe(elf->fd,&elf->interp[0],fsz,ELF_WORD_TO_NATIVE(ph->p_offset),elf->path,"PT_INTERP") < 0) err(); elf->interp[fsz] = '\0'; trc(TRC_EXEC,"%d: %s saved interp = %s\n",mypid,__func__,&elf->interp[0]); } /* * The PT_INTERP handler when reading the "interpreter" specified by * the main executable. Since we don't handle cascaded * "interpreter"s, this always just errors. */ static void psect_pt_interp_interp(ELF_CTX *elf, Elf32_Phdr *ph __attribute__((__unused__)), void (*err)(void)) { trc(TRC_EXEC,"%d: %s: bad PT_INTERP file (has its own PT_INTERP)\n",mypid,elf->path); (*err)(); } /* * The PT_LOAD handler. This is used for both the main executable and * the "interpreter", since it turns out they both need the same thing * here - hence the _common naming. */ static void psect_pt_load_common(ELF_CTX *elf, Elf32_Phdr *ph, void (*err)(void)) { uint32_t align; uint32_t va; uint32_t fa; uint32_t diff; uint32_t fo; uint32_t fsz; uint32_t msz; uint32_t psz; uint32_t flags; MEMSEG *ms; uint32_t filesz; trc(TRC_EXEC,"%d: %s entry, loadbase %08lx\n",mypid,__func__,(unsigned long int)elf->loadbase); flags = ELF_WORD_TO_NATIVE(ph->p_flags); fa = ELF_ADDR_TO_NATIVE(ph->p_vaddr); align = ELF_WORD_TO_NATIVE(ph->p_align); trc(TRC_EXEC,"%d: flags %08lx fa %08lx align %08lx\n",mypid, (unsigned long int)flags, (unsigned long int)fa, (unsigned long int)align); if (!align || (align & (align-1))) { trc(TRC_EXEC,"%d: %s: p_align (%#lx) isn't a power of two\n",mypid,elf->path,(ULI)align); (*err)(); } filesz = ELF_WORD_TO_NATIVE(ph->p_filesz); if (align > 1) elf->loadbase = ROUND_UP(elf->loadbase,align); va = fa; if (align > 1) va = ROUND_DOWN(va,align); diff = fa - va; trc(TRC_EXEC,"%d: fa %08lx va %08lx diff %08lx\n",mypid, (unsigned long int)fa, (unsigned long int)va, (unsigned long int)diff); fo = ELF_OFFSET_TO_NATIVE(ph->p_offset) - diff; fsz = filesz + diff; msz = ELF_WORD_TO_NATIVE(ph->p_memsz) + diff; psz = ROUND_UP(msz,PAGE_SIZE); trc(TRC_EXEC,"%d: fo %08lx fsz %08lx msz %08lx psz %08lx\n",mypid, (unsigned long int)fo, (unsigned long int)fsz, (unsigned long int)msz, (unsigned long int)psz); va += elf->loadbase; trc(TRC_EXEC,"%d: calling memseg_mem, va %08lx psz %08lx\n",mypid, (unsigned long int)va, (unsigned long int)psz); ms = memseg_mem(va,psz,((flags&PF_R)?P_R:0)|((flags&PF_W)?P_W:0)|((flags&PF_X)?P_X:0)); if (read_exe(elf->fd,ms->data,fsz,fo,elf->path,"bad ELF file (can't read program segment)") < 0) (*err)(); if (fsz < psz) bzero(ms->data+fsz,psz-fsz); if (va+psz > elf->dend) elf->dend = va + psz; if ((s.pc >= fa) && (s.pc <= fa+filesz)) { elf->taddr = fa; if (elf->daddr == ~(uint32_t)0) elf->daddr = elf->taddr; } else { elf->daddr = fa; } } // Use psect_pt_load_common for PT_LOAD for both _main and _interp. #define psect_pt_load_main psect_pt_load_common #define psect_pt_load_interp psect_pt_load_common /* * The PT_PHDR handler for the main executable. Just record the value * for possible later use. */ static void psect_pt_phdr_main(ELF_CTX *elf, Elf32_Phdr *ph, void (*err)(void) __attribute__((__unused__))) { elf->phdr = ELF_WORD_TO_NATIVE(ph->p_vaddr); trc(TRC_EXEC,"%d: %s saved phdr = %08lx\n",mypid,__func__,(unsigned long int)elf->phdr); } /* * The PT_PHDR handler for the main executable. We do nothing here, * since it's only the main executable that PT_PHDR matters for. */ static void psect_pt_phdr_interp(ELF_CTX *elf __attribute__((__unused__)), Elf32_Phdr *ph __attribute__((__unused__)), void (*err)(void) __attribute__((__unused__))) { } /* * The PSECT_OPS structs for the main executable (main) and the * PT_INTERP "interpreter" (interp). */ static const PSECT_OPS psect_ops_main = PSECT_OPS_INIT(main); static const PSECT_OPS psect_ops_interp = PSECT_OPS_INIT(interp); /* * Try to exec xpath as an ELF executable. argvstrs and envpstrs, with * narg and nenv as their respective counts, are the argument and * environment vectors, already read out of emulated memory. */ static int try_exec_elf(const char *xpath, const char **argvstrs, int narg, const char **envpstrs, int nenv) { __label__ enoexec_; int i; int l; MEMSEG *ms; uint32_t argv; uint32_t envp; uint32_t *argvv; uint32_t *envpv; uint32_t ps_strings; int stacklen; uint32_t sfp; ELF_CTX ctx; uint32_t ee; void enoexec(void) { goto enoexec_; } void memfail(void) { free(argvv); free(envpv); } trc(TRC_EXEC,"%d: %s %s\n",mypid,__func__,xpath); trc(TRC_EXEC,"%d: narg %d\n",mypid,narg); for (i=0;i<=nargs;i++) trc(TRC_EXEC,"%d: argvstrs[%d] = %s\n",mypid,i,argvstrs[i]); trc(TRC_EXEC,"%d: nenv %d\n",mypid,nenv); for (i=0;i<=nenv;i++) trc(TRC_EXEC,"%d: envpstrs[%d] = %s\n",mypid,i,envpstrs[i]); ctx.path = xpath; ee = elf_start_load(&ctx,em_ET_EXEC,"EXEC"); if (ee) return(ee); if (0) { enoexec_:; close(ctx.fd); return(em_ENOEXEC); } s.pc = ELF_ADDR_TO_NATIVE(ctx.eh.e_entry); ctx.interp[0] = 0; ctx.taddr = ~(uint32_t)0; ctx.daddr = ~(uint32_t)0; ctx.dend = 0; ctx.loadbase = 0; ctx.dli_pha = 0; ctx.dli_phes = 0; ctx.dli_phn = 0; ctx.dli_interp = 0; ctx.dli_entry = 0; ctx.phdr = 0; trc(TRC_EXEC,"%d: %s doing map_psect for %s\n",mypid,__func__,ctx.path); map_psect(&ctx,&psect_ops_main,&enoexec); trc(TRC_EXEC,"%d: %s map_psect done for %s\n",mypid,__func__,ctx.path); if (ctx.interp[0]) { ELF_CTX ictx; void interp_err(void) { close(ictx.fd); enoexec(); } trc(TRC_EXEC,"%d: %s doing elf_start_load for interp %s\n",mypid,__func__,&ctx.interp[0]); ictx.path = &ctx.interp[0]; ee = elf_start_load(&ictx,em_ET_DYN,"DYN"); if (ee) { trc(TRC_EXEC,"%d: %s: can't load PT_INTERP file %s\n",mypid,ctx.path,ictx.path); enoexec(); } ictx.loadbase = ROUND_UP(ctx.daddr+MAXDSIZE,PAGE_SIZE); trc(TRC_EXEC,"%d: %s doing map_psect for interp %s\n",mypid,__func__,ictx.path); map_psect(&ictx,&psect_ops_interp,&interp_err); trc(TRC_EXEC,"%d: %s map_psect done for %s\n",mypid,__func__,ictx.path); ctx.dli_pha = ctx.phdr; ctx.dli_phes = ELF_ADDR_TO_NATIVE(ctx.eh.e_phentsize); ctx.dli_phn = ELF_ADDR_TO_NATIVE(ctx.eh.e_phnum); ctx.dli_entry = ELF_ADDR_TO_NATIVE(ctx.eh.e_entry); s.pc = ELF_ADDR_TO_NATIVE(ictx.eh.e_entry) + ictx.loadbase; close(ictx.fd); } close(ctx.fd); s.npc = s.pc + 4; ms = memseg_mem(USRSTACK-MAXSSIZE,MAXSSIZE,P_R|P_W|P_X); bzero(ms->data,MAXSSIZE); /* * For dynamically-linked executables, we need Aux32Info structs as * well. The stack has to be laid out with sp pointing to argc, * followed by argc+1 pointers to arg strings (and a trailing nil), * then envp pointers (terminated by a trailing nil), then Aux32Info * structs (terminated by one with a_type set to AT_NULL). After * that comes whatever else - which here means the argv and envp * strings, the stack gap, the signal-delivery trampoline, and * ps_strings. * * For the sake of comparisons, we want our stack layout to exactly * match the kernel's. In aid of this, we do things in a slightly * strange order, so as to exactly match the kernel's computations. */ s.dbrk = ctx.dend; stacklen = 0; for (i=nenv-1;i>=0;i--) stacklen += strlen(envpstrs[i]) + 1; for (i=narg-1;i>=0;i--) stacklen += strlen(argvstrs[i]) + 1; stacklen = ROUND_UP(stacklen,8); stacklen = ((narg+nenv+2)*4) + // argv/envp pointers (16 * 4) + // Aux32Info structs 4 + // not sure stacklen + // argv/envp strings STACKGAPLEN + // stack gap SZSIGCODE + // signal trampoline 16; // ps_strings stacklen = ROUND_UP(stacklen,8); SET_REG(R_SP); sfp = USRSTACK - stacklen; s.regs[R_SP] = sfp; // argc mem_set_4(sfp,narg); // argv/envp strings argvv = malloc(narg*sizeof(uint32_t)); argvv[narg] = 0; envpv = malloc(nenv*sizeof(uint32_t)); envpv[nenv] = 0; sfp = s.regs[R_SP] + (1 + narg + nenv + 2 + 16) * 4; for (i=0;i= nl) { trc(TRC_EXEC,"%d: %s: no shell name present on #! line\n",mypid,path); enoexec(); } for (sh1=sh0;(sh1=0;i--) free(newargv[i]); free(newargv); return(e); } /* * Try to exec a program. We first try to handle it as an ELF * executable; if that fails, we then try it as a #! script. If that * fails too, we return failure. * * Returns 0 on success or an (emulated-OS) errno on failure. */ static int do_execve(const char *path, const char **argvstrs, const char **envpstrs) { int e; int narg; int nenv; for (narg=0;argvstrs[narg];narg++) ; for (nenv=0;envpstrs[nenv];nenv++) ; e = try_exec_elf(path,argvstrs,narg,envpstrs,nenv); if (e == 0) return(0); e = try_exec_script(path,argvstrs,narg,envpstrs,nenv); return(e); } /* * Do the initial exec in accordance with the command-line args. */ static void initial_exec(void) { const char **argv; int i; const char *envp; int e; argv = malloc((nargs+1)*sizeof(const char *)); argv[nargs] = 0; for (i=nargs-1;i>=0;i--) argv[i] = args[i]; envp = 0; e = do_execve(exe,argv,&envp); postexec = 1; if (e) { printf("Initial exec failed %d\n",e); free(argv); top(); } free(argv); } /* * This is called when an operatino that potentially needs vfork fixup * is done from a vforked child process. This adds a VFORKBACKOUT * record, so the parent can do the fixup. */ static void add_vfork_backout(VFBKIND k, uint32_t emfd, FD fd) { VFORKBACKOUT *b; if (during_vfork < 1) return; trc(TRC_VFORK,"%d: adding vfork backout: kind=%d(%s) level=%d emfd=%lu fd fd=%d prot=%u flags=%u\n",mypid,(int)k,vfb_kind_str(k),during_vfork,(unsigned long int)emfd,fd.fd,fd.prot,fd.flags); b = malloc(sizeof(VFORKBACKOUT)); b->kind = k; b->emfd = emfd; b->fd = fd; b->link = vfb; b->level = during_vfork; vfb = b; } /* * Used in a vfork()ed child after exec, to clean up backout records * made between vforking and execing. during_vfork is cleared * elsewhere. */ static void flush_vfork_backout(void) { VFORKBACKOUT *b; while ((b = vfb)) { vfb = b->link; free(b); } } /* * Do post-vfork cleanup in the parent process. Because * add_vfork_backout pushes things onto vfb, doing things this way * does them last-done first-restored, which is exactly what we want. */ static void vfork_cleanup(void) { VFORKBACKOUT *b; FD *fd; while (vfb && (vfb->level > during_vfork)) { b = vfb; vfb = b->link; switch (b->kind) { default: panic("impossible kind %d in %s",(int)b->kind,__func__); break; case VFB_OPEN: /* * The file was opened during the vfork. The OS's file * descriptor went away with the switch back to the parent, * but the FD is still around. Fix that. */ trc(TRC_VFORK,"%d: backing out OPEN: emfd=%lu\n",mypid,(unsigned long int)b->emfd); if (b->emfd >= nfds) panic("impossible VFB_OPEN backout 1"); fd = fds[b->emfd]; if (! fd) panic("impossible VFB_OPEN backout 2"); fds[b->emfd] = 0; free(fd); break; case VFB_CLOSE: /* * The file was closed during the vfork. The switch back to * the parent has resurrected the file descriptor, but the * FD is still nonexistent. Fix that. */ trc(TRC_VFORK,"%d: backing out CLOSE: emfd=%lu\n",mypid,(unsigned long int)b->emfd); if (b->emfd >= nfds) panic("impossible VFB_CLOSE backout 1"); fd = fds[b->emfd]; if (fd) panic("impossible VFB_CLOSE backout 2"); fd = malloc(sizeof(FD)); *fd = b->fd; fds[b->emfd] = fd; free(fd); break; case VFB_DUP2: /* * A dup2() was done during the vfork. The file descriptors * have been restored, but the FD needs updating - its fd is * correct, but its prot needs restoring. */ trc(TRC_VFORK,"%d: backing out DUP2: emfd=%lu\n",mypid,(unsigned long int)b->emfd); if (b->emfd >= nfds) panic("impossible VFB_DUP2 backout 1"); fd = fds[b->emfd]; if (! fd) panic("impossible VFB_DUP2 backout 2"); fd->prot = b->fd.prot; break; } free(b); } } /* * Open a new (emulated) file descriptor. osfd is the underlying OS * file desciprtor. minfd is the point in the open file table that * the search for an available fd should start. rw is P_R and/or P_W, * indicating how the descriptor should be opened. * * This makes the add_vfork_backout() call for the new fd. */ static int new_fd(int osfd, int minfd, unsigned int rw) { int d; FD *fd; int i; for (d=minfd;(d MAXFDS) { printf("Out of fds\n"); top(); } if (d >= nfds) { i = nfds; nfds = d + 8; fds = realloc(fds,nfds*sizeof(*fds)); for (;ifd = osfd; fd->prot = rw & (P_R | P_W); add_vfork_backout(VFB_OPEN,d,*fd); return(d); } /* * Set up the initial file descriptors during startup. */ static void init_fds(void) { int i; int d; struct stat stb; nfds = 0; fds = 0; during_vfork = 0; vfb = 0; for (i=2;i>=0;i--) { if (fstat(i,&stb) >= 0) { d = dup(i); new_fd(d,i,(i==0)?P_R:P_W); } } } /* * This is the signal handler routine for all signals we catch. We * install this handler for all signals we can; it just records them * in s.sigpend[] and then sets anysigpend and alert_run so they'll be * noticed at the next emulated instruction boundary. */ static void catch_signal(int sig) { uint32_t emsig; emsig = os2em_signal(sig); if (emsig == 0) return; if (emsig >= em__NSIG) panic("handling impossible emsig %lu",(unsigned long int)emsig); s.sigpend[emsig] = 1; anysigpend = 1; alert_run = 1; } /* * Set up signals on initial startup. The only thing of note here is * that we skip trying to install handlers for not just SIGKILL and * SIGSTOP (which we're not allowed to do anything with) but also * SIGSEGV. This is because the sort of memory fault that leads to * SIGSEGV on real hardware is, here, instead noticed by the memory * access code. */ static void init_signals(void) { int i; int ossig; struct sigaction sa; s.sigmask = 0; anysigpend = 0; for (i=em__NSIG-1;i>=0;i--) { s.sigh[i].handler = em_SIG_DFL; s.sigh[i].flags = 0; s.sigpend[i] = 0; switch (i) { case em_SIGKILL: case em_SIGSTOP: case em_SIGSEGV: continue; break; } ossig = em2os_signal(i); sa.sa_handler = &catch_signal; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(ossig,&sa,0); } s.onsigstack = 0; s.sigstack_enabled = 0; s.sigstack_base = 0; s.sigstack_size = 0; } /* * Sign-extend a value. v is the value and bits is the number of * significant bits in it, including the sign bit. For example, * signextend(9,4) will return 0xfffffff9, while signextend(9,5) will * return 0x00000009. * * This assumes two's-copmplement representation; this is correct for * the emulated machine, and this is for emulator use. */ static uint32_t signextend(uint32_t v, int bits) { if ((v >> (bits-1)) & 1) { v |= (~(uint32_t)0) << bits; } else { v &= ~((~(uint32_t)0) << bits); } return(v); } /* * Report an unimplemented opcode. Just print the details and throw * out. */ static void unimp(uint32_t xa, uint32_t inst) { printf("Unimplemented: at %08lx inst=%08lx\n",(ULI)xa,(ULI)inst); printf(" OPC=%d OP2=%d DREG=%d A=%d COND=%d IMM22=%d, DISP22=%d\n", (int)OPC(inst), (int)OP2(inst), (int)DREG(inst), (int)A(inst), (int)COND(inst), (int)IMM22(inst), (int)DISP22(inst)); printf(" OP3=%d SREG1=%d SREG2=%d I=%d ASI=%d SIMM13=%d OPF=%d\n", (int)OP3(inst), (int)SREG1(inst), (int)SREG2(inst), (int)I(inst), (int)ASI(inst), (int)SIMM13(inst), OPF(inst)); top(); } /* * Do a window save. On real hardware, there are multiple register * windows, with window spills and loads happening only when the * window ring over- or under-flows. We operate, loosely speaking, as * if there were only two register windows (one valid and one * invalid), with spills and loads happening on every save and * restore. */ static void window_save(void) { uint32_t sp; int i; if (unset_action != UNSET_IGNORE) { if (! IS_REG_SET(R_SP)) { fprintf(stderr,"window save with undefined stack pointer"); if (unset_action == UNSET_ERROR) top(); } for (i=8-1;i>=0;i--) { if (! IS_REG_SET(R_L0+i)) { fprintf(stderr,"warning: window save with undefined %s\n",regnames[R_L0+i]); trc(TRC_INSTR,"warning: window save with undefined %s\n",regnames[R_L0+i]); } } for (i=8-1;i>=0;i--) { if (! IS_REG_SET(R_I0+i)) { fprintf(stderr,"warning: window save with undefined %s\n",regnames[R_I0+i]); trc(TRC_INSTR,"warning: window save with undefined %s\n",regnames[R_I0+i]); } } } sp = s.regs[R_SP]; for (i=0;i<8;i++) mem_set_4(sp+(i*4),s.regs[R_L0+i]); for (i=0;i<8;i++) mem_set_4(sp+32+(i*4),s.regs[R_I0+i]); for (i=8-1;i>=0;i--) { if (IS_REG_SET(R_O0+i)) SET_REG(R_I0+i); else UNSET_REG(R_I0+i); s.regs[R_I0+i] = s.regs[R_O0+i]; s.regs[R_O0+i] = 0; s.regs[R_L0+i] = 0; } s.regset &= ~( REG_SET_MASK(R_O0) | REG_SET_MASK(R_O1) | REG_SET_MASK(R_O2) | REG_SET_MASK(R_O3) | REG_SET_MASK(R_O4) | REG_SET_MASK(R_O5) | REG_SET_MASK(R_O6) | REG_SET_MASK(R_O7) | REG_SET_MASK(R_L0) | REG_SET_MASK(R_L1) | REG_SET_MASK(R_L2) | REG_SET_MASK(R_L3) | REG_SET_MASK(R_L4) | REG_SET_MASK(R_L5) | REG_SET_MASK(R_L6) | REG_SET_MASK(R_L7) ); s.wdepth ++; } /* * Do a window restore. See window_save for further comments. */ static void window_restore(void) { uint32_t fp; int i; if ((unset_action != UNSET_IGNORE) && !IS_REG_SET(R_FP)) { fprintf(stderr,"window restore with undefined frame pointer"); if (unset_action == UNSET_ERROR) top(); } fp = s.regs[R_FP]; for (i=8-1;i>=0;i--) { if (IS_REG_SET(R_I0+i)) SET_REG(R_O0+i); else UNSET_REG(R_O0+i); s.regs[R_O0+i] = s.regs[R_I0+i]; s.regs[R_L0+i] = mem_get_4(fp+(i*4)); s.regs[R_I0+i] = mem_get_4(fp+32+(i*4)); } s.regset |= REG_SET_MASK(R_I0) | REG_SET_MASK(R_I1) | REG_SET_MASK(R_I2) | REG_SET_MASK(R_I3) | REG_SET_MASK(R_I4) | REG_SET_MASK(R_I5) | REG_SET_MASK(R_I6) | REG_SET_MASK(R_I7) | REG_SET_MASK(R_L0) | REG_SET_MASK(R_L1) | REG_SET_MASK(R_L2) | REG_SET_MASK(R_L3) | REG_SET_MASK(R_L4) | REG_SET_MASK(R_L5) | REG_SET_MASK(R_L6) | REG_SET_MASK(R_L7); s.wdepth --; } /* * Implement addcc: compute a+b and return the result, affecting the * condition codes correspondingly. */ static uint32_t addcc(uint32_t a, uint32_t b) { uint64_t v; v = (uint64_t)a + (uint64_t)b; s.cc = ((v & 0x80000000) ? CC_N : 0) | (((uint32_t)v == 0) ? CC_Z : 0) | ((0x80000000&(a^v)&~(a^b)) ? CC_V : 0) | ((v & 0x100000000ULL) ? CC_C : 0); return(v); } /* * Implement addcc: compute a-b and return the result, affecting the * condition codes correspondingly. */ static uint32_t subcc(uint32_t a, uint32_t b) { uint64_t v; v = (uint64_t)a - (uint64_t)b; s.cc = ((v & 0x80000000) ? CC_N : 0) | (((uint32_t)v == 0) ? CC_Z : 0) | (((a^b)&(v^a)&0x80000000) ? CC_V : 0) | ((v & 0x100000000ULL) ? CC_C : 0); return(v); } /* * Implement sra: compute v>>n, where the shift is arithmetic, and * return the result. */ static uint32_t sra(uint32_t v, uint32_t n) { n &= 31; if (v & 0x80000000) { v = ~((~v) >> n); } else { v >>= n; } return(v); } /* * Implement andcc: compute a&b and return the result, affecting the * condition codes correspndingly. */ static uint32_t andcc(uint32_t a, uint32_t b) { uint32_t v; v = a & b; s.cc = ((v & 0x80000000) ? CC_N : 0) | ((v == 0) ? CC_Z : 0); return(v); } /* * Implement orcc: compute a|b and return the result, affecting the * condition codes correspndingly. */ static uint32_t orcc(uint32_t a, uint32_t b) { uint32_t v; v = a | b; s.cc = ((v & 0x80000000) ? CC_N : 0) | ((v == 0) ? CC_Z : 0); return(v); } /* * Implement andncc: compute a&~b and return the result, affecting the * condition codes correspndingly. */ static uint32_t andncc(uint32_t a, uint32_t b) { uint32_t v; v = a & ~b; s.cc = ((v & 0x80000000) ? CC_N : 0) | ((v == 0) ? CC_Z : 0); return(v); } /* * Implement conditional branch instructions. Except for branch-always * (8) and branch-never (0), which are special-cased, just look up the * condition in conds[] and pick out the bit corresponding to the * current state of the condition codes, using that to either branch * or not. We special-case 0 and 8 because the handling of the annul * bit is backwards for those two. */ static void cbranch(int cond, int annul, uint32_t to) { switch (cond) { case 8: s.npc = to; /* fall through */ case 0: if (annul) s.flags |= SF_ANNUL; break; default: if ((conds[cond&15] >> (s.cc & 15)) & 1) { s.npc = to; } else { if (annul) s.flags |= SF_ANNUL; } break; } } /* * Load a NUL-terminated string out of emulated memory (at ptr) and * return a malloc()ed copy of it in emulator memory. This also sets * up the NULTERM_STATUS for later cleanup when the string is no * longer needed. * * We from nomemacc to ensure we read the memory exactly once as far as * TRC_MEM is concerned. */ static const char *nulterm_scarg(uint32_t ptr, NULTERM_STATUS *nts) { int l; unsigned char *s; int i; l = 0; while (mem_get_1(ptr+l)) l ++; s = malloc(l+1); nomemacc ++; for (i=0;i<=l;i++) s[i] = mem_get_1(ptr+i); nomemacc --; nts->tofree = s; return(s); } /* * Do any appropriate cleanup for a NUL-terminated string copied out of * emulator memory once it's no longer needed. */ static void nulterm_done(NULTERM_STATUS *nts) { free(nts->tofree); } /* * Fetch a 32-bit syscall argument. Narrower arguments are padded; * wider arguments are treated as multiple 32-bit arguments. This * fetches out of register shadows or memory depending on the argument * number and how many arguments are in registers, as described by the * SCARGS. */ static uint32_t scarg(SCARGS *args, int n) { if (n < 0) panic("impossible scarg"); if (n < args->nreg) { if ((unset_action == UNSET_IGNORE) || ((args->setmask >> n) & 1)) return(args->regs[n]); fprintf(stderr,"syscall arg %d has undefined value\n",n); if (unset_action == UNSET_ERROR) top(); } if ((unset_action != UNSET_IGNORE) && !(args->setmask & (1U << 6))) { fprintf(stderr,"syscall arg %d depends on undefined %s\n",n,regnames[R_SP]); if (unset_action == UNSET_ERROR) top(); } if (! args->sp) panic("scarg overrun"); return(mem_get_4(args->sp+((23+n-args->nreg)*4))); } /* * Print out an ioctl command using the _IO* macros. */ static void print_decoded_ioctl(FILE *to, uint32_t ioc) { switch (ioc & em_IOC_DIRMASK) { case em_IOC_VOID: if (em_IOC_LEN(ioc) == 0) { fprintf(to,"_IO('%c',%d)",em_IOC_GROUP(ioc),em_IOC_NUM(ioc)); } else { fprintf(to,"_IOC(IOC_VOID,'%c',%d,%d)",em_IOC_GROUP(ioc),em_IOC_NUM(ioc),em_IOC_LEN(ioc)); } break; case em_IOC_OUT: fprintf(to,"_IOR('%c',%d,%d)",em_IOC_GROUP(ioc),em_IOC_NUM(ioc),em_IOC_LEN(ioc)); break; case em_IOC_IN: fprintf(to,"_IOW('%c',%d,%d)",em_IOC_GROUP(ioc),em_IOC_NUM(ioc),em_IOC_LEN(ioc)); break; case em_IOC_INOUT: fprintf(to,"_IOWR('%c',%d,%d)",em_IOC_GROUP(ioc),em_IOC_NUM(ioc),em_IOC_LEN(ioc)); break; default: fprintf(to,"unknown"); break; } } /* * Copy from/fromlen, in emulator memory, to to/tolen, in emulated * memory. If fromlen is longer, this truncates; if tolen is longer, * this NUL-pads. what is a text description of what's being copied, * in case an error message is generated. prefail is called on any * failure, before throwing out, as described in the comment on * copyout(). */ static void copy_or_nulpad(const void *from, int fromlen, uint32_t to, int tolen, const char *what, void (*prefail)(void)) { int o; int n; if (fromlen < tolen) { copyout(from,to,fromlen,what,prefail); o = fromlen; while (o < tolen) { n = sizeof(nulbuf); if (n > tolen-o) n = tolen - o; copyout(&nulbuf[0],to+o,n,what,prefail); o += n; } } else { copyout(from,to,tolen,what,prefail); } } /* * Convert our representation of the condition codes to a SPARC PSR * value. The other bits of the PSR are not emulated. This is used * when building a sigcontext structure for signal delivery. */ static uint32_t cc_to_psr(unsigned int cc) { #if (CC_N == CC_Z*2) && (em_PSR_N == em_PSR_Z*2) &&\ (CC_Z == CC_V*2) && (em_PSR_Z == em_PSR_V*2) &&\ (CC_V == CC_C*2) && (em_PSR_V == em_PSR_C*2) return((cc/CC_C)*em_PSR_C); #else return( ((cc & CC_N) ? em_PSR_N : 0) | ((cc & CC_Z) ? em_PSR_Z : 0) | ((cc & CC_V) ? em_PSR_V : 0) | ((cc & CC_C) ? em_PSR_C : 0) ); #endif } /* * Convert a SPARC PSR value's condition codes to the cc representation * we use. The other bits of the PSR are ignored. This is used * during signal return. */ static unsigned int psr_to_cc(uint32_t psr) { #if (CC_N == CC_Z*2) && (em_PSR_N == em_PSR_Z*2) &&\ (CC_Z == CC_V*2) && (em_PSR_Z == em_PSR_V*2) &&\ (CC_V == CC_C*2) && (em_PSR_V == em_PSR_C*2) return(((psr&(em_PSR_N|em_PSR_Z|em_PSR_V|em_PSR_C))/em_PSR_C)*CC_C); #else return( ((psr & em_PSR_N) ? CC_N : 0) | ((psr & em_PSR_Z) ? CC_Z : 0) | ((psr & em_PSR_V) ? CC_V : 0) | ((psr & em_PSR_C) ? CC_C : 0) ); #endif } /* * Deliver a signal. The signal number is am emulated-OS signal * number. Our caller must have made sure the signal is not blocked * or ignored; we handle SIG_DFL actions and user-provided handlers. */ static void deliver_signal(uint32_t sig) { int onstack; SIG *sh; uint32_t fp; int how; if ((sig < 1) || (sig >= em__NSIG)) panic("delivery of impossible signal %lu",(unsigned long int)sig); sh = &s.sigh[sig]; onstack = 0; // Signal stack support not yet implemented trc(TRC_SIGNAL,"%d: delivering signal %lu, handler %lu\n",mypid,(unsigned long int)sig,(unsigned long int)sh->handler); if (sh->handler == em_SIG_DFL) { if ((sig < 1) || (sig >= (sizeof(sigdef)/sizeof(sigdef[0])))) { trc(TRC_SIGNAL,"%d: SIG_DFL handler for out-of-range signal %ld\n",mypid,(LI)(int32_t)sig); how = SIGDEF_KILL; } else { how = sigdef[sig]; if (how == 0) { trc(TRC_SIGNAL,"%d: SIG_DFL handler for unknown signal %ld\n",mypid,(LI)(int32_t)sig); how = SIGDEF_KILL; } } switch (how) { case SIGDEF_KILL: trc(TRC_SIGNAL,"%d: signal %ld (%s) SIG_DFL: killing process\n",mypid,(LI)(int32_t)sig,em_signame(sig,"unknown")); exit(0); break; case SIGDEF_CORE: trc(TRC_SIGNAL,"%d: signal %ld (%s) SIG_DFL: core dump\n",mypid,(LI)(int32_t)sig,em_signame(sig,"unknown")); exit(0); break; case SIGDEF_IGNORE: trc(TRC_SIGNAL,"%d: signal %ld (%s) SIG_DFL: ignore\n",mypid,(LI)(int32_t)sig,em_signame(sig,"unknown")); return; break; case SIGDEF_STOP: trc(TRC_SIGNAL,"%d: signal %ld (%s) SIG_DFL: stop\n",mypid,(LI)(int32_t)sig,em_signame(sig,"unknown")); exit(0); break; default: panic("SIG_DFL finds unknown sigdef %d",how); break; } } fp = onstack ? s.sigstack_base + s.sigstack_size : s.regs[R_SP]; fp -= 64; fp &= ~(uint32_t)7; // Build the signal frame. mem_set_4(fp,sig); // sf.sf_signo; mem_set_4(fp+4,0); // sf.sf_code mem_set_4(fp+8,0); // sf.sf_scp mem_set_4(fp+12,0); // sf.sf_addr // Build the context to be used by sigreturn. mem_set_4(fp+16,0); // sf.sf_sc.sc_onstack (on-stack not implemented) mem_set_4(fp+20,s.sigmask); // sf.sf_sc.__sc_mask13 mem_set_4(fp+24,s.regs[R_SP]); // sf.sf_sc.sc_sp mem_set_4(fp+28,s.pc); // sf.sf_sc.sc_pc mem_set_4(fp+32,s.npc); // sf.sf_sc.sc_npc mem_set_4(fp+36,cc_to_psr(s.cc)); // sf.sf_sc.sc_psr mem_set_4(fp+40,s.regs[R_G1]); // sf.sf_sc.sc_g1 mem_set_4(fp+44,s.regs[R_O0]); // sf.sf_sc.sc_o0 mem_set_4(fp+48,s.sigmask); // sf.sf_sc.sc_mask, first word mem_set_4(fp+52,s.sigmask>>32); // sf.sf_sc.sc_mask, second word mem_set_4(fp+56,0); // sf.sf_sc.sc_mask, third word mem_set_4(fp+60,0); // sf.sf_sc.sc_mask, fourth word s.regs[R_G1] = sh->handler; s.pc = sigtramp; s.npc = sigtramp + 4; s.regs[R_SP] = fp - 64; s.sigmask = (s.sigmask | sh->mask.bits[0] | (((uint64_t)sh->mask.bits[1]) << 32)) & SIG_CANBLOCK; // any other flags? if (sh->flags & em_SA_RESETHAND) { sh->handler = em_SIG_DFL; bzero(&sh->mask.bits[0],sizeof(sh->mask.bits)); sh->flags = 0; } // if (onstack) record that we're on the signal stack } /* * Deliver pending signals, if any. If any pending signals remain * undelivered, leave anysigpend set true; otherwise, false. The * return value is a count of signals delivered. * * We deliver all signals with code set to 0. Signal delivery with * code set to anything else occurs only for signals reflecting * hardware traps, which we currently never generate. If we make * segfaults and illegal instructions and the like into signals, this * will need to change. */ static int deliver_signals(void) { int sig; int any; int n; any = 0; n = 0; anysigpend = 0; for (sig=em__NSIG-1;sig>1;sig--) { if (s.sigpend[sig]) { if (s.sigh[sig].handler == em_SIG_IGN) { s.sigpend[sig] = 0; } else if (! (s.sigmask & (1ULL << sig))) { s.sigpend[sig] = 0; deliver_signal(sig); n ++; } else { any = 1; } } } if (any) anysigpend = 1; return(n); } /* * Emulate an integer sysctl() value. valp and lenp are the * emulated-memory locations where we should write the value and * length. val is the value. rv is the SCRV for the syscall. */ static int em_sc_int(uint32_t valp, uint32_t lenp, uint32_t val, SCRV *rv) { uint32_t len; int i; if (valp == 0) { mem_set_4(lenp,4); SYSCALL_SETRET(4); return(1); } len = mem_get_4(lenp); if (len < 4) { for (i=0;i>24); val <<= 8; } SYSCALL_SETERR(em_ENOMEM); } else { for (i=0;i<4;i++) { mem_set_1(valp+i,val>>24); val <<= 8; } mem_set_4(lenp,4); SYSCALL_SETRET(4); } return(1); } /* * Handle a hw.* sysctl. mib/miblen are the MIB (already copied out of * emulated memory), valp and lenp are the places (addresses in * emulated memory) to write the value and length, and rv is the SCRV * for the syscall. */ static int em_sysctl_hw(uint32_t *mib, int miblen, uint32_t valp, uint32_t lenp, SCRV *rv) { if (miblen != 1) return(0); switch (mib[0]) { case em_HW_PAGESIZE: return(em_sc_int(valp,lenp,PAGE_SIZE,rv)); break; } return(0); } /* * Store an underlying-OS struct rusage (at ru) into an emulated struct * rusage (at buf). */ static void store_rusage(uint32_t buf, const struct rusage *ru) { mem_set_4(buf,ru->ru_utime.tv_sec); mem_set_4(buf+4,ru->ru_utime.tv_usec); mem_set_4(buf+8,ru->ru_stime.tv_sec); mem_set_4(buf+12,ru->ru_stime.tv_usec); mem_set_4(buf+16,ru->ru_maxrss); mem_set_4(buf+20,ru->ru_ixrss); mem_set_4(buf+24,ru->ru_idrss); mem_set_4(buf+28,ru->ru_isrss); mem_set_4(buf+32,ru->ru_minflt); mem_set_4(buf+36,ru->ru_majflt); mem_set_4(buf+40,ru->ru_nswap); mem_set_4(buf+44,ru->ru_inblock); mem_set_4(buf+48,ru->ru_oublock); mem_set_4(buf+52,ru->ru_msgsnd); mem_set_4(buf+56,ru->ru_msgrcv); mem_set_4(buf+60,ru->ru_nsignals); mem_set_4(buf+64,ru->ru_nvcsw); mem_set_4(buf+68,ru->ru_nivcsw); } /* * Store an underlying-OS struct stat (at stb) into an emulated struct * stat (at stp). */ static void store_stat(uint32_t stp, const struct stat *stb) { mem_set_4(stp,stb->st_dev); mem_set_4(stp+4,stb->st_ino); mem_set_4(stp+8,stb->st_mode); mem_set_4(stp+12,stb->st_nlink); mem_set_4(stp+16,stb->st_uid); mem_set_4(stp+20,stb->st_gid); mem_set_4(stp+24,stb->st_rdev); mem_set_4(stp+28,stb->st_atimespec.tv_sec); mem_set_4(stp+32,stb->st_atimespec.tv_nsec); mem_set_4(stp+36,stb->st_mtimespec.tv_sec); mem_set_4(stp+40,stb->st_mtimespec.tv_nsec); mem_set_4(stp+44,stb->st_ctimespec.tv_sec); mem_set_4(stp+48,stb->st_ctimespec.tv_nsec); mem_set_4(stp+52,0); // struct padding mem_set_4(stp+56,(stb->st_size>>16)>>16); mem_set_4(stp+60,stb->st_size); mem_set_4(stp+64,(stb->st_blocks>>16)>>16); mem_set_4(stp+68,stb->st_blocks); mem_set_4(stp+72,stb->st_blksize); mem_set_4(stp+76,0); // XXX should be st_flags mem_set_4(stp+80,stb->st_gen); mem_set_4(stp+84,0); // struct padding mem_set_4(stp+88,0); // st_qspare, word 0 mem_set_4(stp+92,0); // st_qspare, word 1 mem_set_4(stp+96,0); // st_qspare, word 2 mem_set_4(stp+100,0); // st_qspare, word 3 } #if defined(STATFS_VIA_STATVFS) || defined(GETFSSTAT_VIA_GETVFSSTAT) /* * Store an underlying-OS struct statvfs (at sf) into an emulated * struct statfs (at buf). what and prefail are passed on to * copy_or_nulpad for the strings. */ static void store_statvfs_as_statfs(uint32_t buf, const struct statvfs *sf, const char *what, void (*prefail)(void)) { uint32_t emflags; emflags = ((sf->f_flag & MNT_RDONLY) ? em_MNT_RDONLY : 0) | ((sf->f_flag & MNT_SYNCHRONOUS) ? em_MNT_SYNCHRONOUS : 0) | ((sf->f_flag & MNT_NOEXEC) ? em_MNT_NOEXEC : 0) | ((sf->f_flag & MNT_NOSUID) ? em_MNT_NOSUID : 0) | ((sf->f_flag & MNT_NODEV) ? em_MNT_NODEV : 0) | ((sf->f_flag & MNT_UNION) ? em_MNT_UNION : 0) | ((sf->f_flag & MNT_ASYNC) ? em_MNT_ASYNC : 0) | ((sf->f_flag & MNT_EXRDONLY) ? em_MNT_EXRDONLY : 0) | ((sf->f_flag & MNT_EXPORTED) ? em_MNT_EXPORTED : 0) | ((sf->f_flag & MNT_DEFEXPORTED) ? em_MNT_DEFEXPORTED : 0) | ((sf->f_flag & MNT_EXPORTANON) ? em_MNT_EXPORTANON : 0) | ((sf->f_flag & MNT_EXKERB) ? em_MNT_EXKERB : 0) | ((sf->f_flag & MNT_LOCAL) ? em_MNT_LOCAL : 0) | ((sf->f_flag & MNT_QUOTA) ? em_MNT_QUOTA : 0) | ((sf->f_flag & MNT_ROOTFS) ? em_MNT_ROOTFS : 0) | ((sf->f_flag & MNT_NOCOREDUMP) ? em_MNT_NOCOREDUMP : 0) | ((sf->f_flag & MNT_NOATIME) ? em_MNT_NOATIME : 0) | ((sf->f_flag & MNT_EXNORESPORT) ? em_MNT_EXNORESPORT : 0) | ((sf->f_flag & MNT_EXPUBLIC) ? em_MNT_EXPUBLIC : 0) | ((sf->f_flag & MNT_SYMPERM) ? em_MNT_SYMPERM : 0) | ((sf->f_flag & MNT_NODEVMTIME) ? em_MNT_NODEVMTIME : 0) | ((sf->f_flag & MNT_SOFTDEP) ? em_MNT_SOFTDEP : 0); mem_set_2(buf,0); mem_set_2(buf+2,emflags&0xffff); mem_set_4(buf+4,sf->f_bsize); mem_set_4(buf+8,sf->f_iosize); mem_set_4(buf+12,sf->f_blocks); mem_set_4(buf+16,sf->f_bfree); mem_set_4(buf+20,sf->f_bavail); mem_set_4(buf+24,sf->f_files); mem_set_4(buf+28,sf->f_ffree); mem_set_4(buf+32,sf->f_fsidx.__fsid_val[0]); mem_set_4(buf+36,sf->f_fsidx.__fsid_val[1]); mem_set_4(buf+40,sf->f_owner); mem_set_4(buf+44,emflags); mem_set_4(buf+48,sf->f_syncwrites); mem_set_4(buf+52,sf->f_asyncwrites); mem_set_4(buf+56,0); copy_or_nulpad(&sf->f_fstypename[0],sizeof(sf->f_fstypename),buf+60,em_MFSNAMELEN,what,prefail); copy_or_nulpad(&sf->f_mntonname[0],sizeof(sf->f_mntonname),buf+76,em_MNAMELEN,what,prefail); copy_or_nulpad(&sf->f_mntfromname[0],sizeof(sf->f_mntfromname),buf+166,em_MNAMELEN,what,prefail); } #endif #if !defined(STATFS_VIA_STATVFS) || !defined(GETFSSTAT_VIA_GETVFSSTAT) /* * Store an underlying-OS struct statfs (at sf) into an emulated struct * statfs (at buf). what and prefail are passed on to copy_or_nulpad * for the strings. */ static void store_statfs(uint32_t buf, const struct statfs *sf, const char *what, void (*prefail)(void)) { uint32_t emflags; emflags = ((sf->f_flags & MNT_RDONLY) ? em_MNT_RDONLY : 0) | ((sf->f_flags & MNT_SYNCHRONOUS) ? em_MNT_SYNCHRONOUS : 0) | ((sf->f_flags & MNT_NOEXEC) ? em_MNT_NOEXEC : 0) | ((sf->f_flags & MNT_NOSUID) ? em_MNT_NOSUID : 0) | ((sf->f_flags & MNT_NODEV) ? em_MNT_NODEV : 0) | ((sf->f_flags & MNT_UNION) ? em_MNT_UNION : 0) | ((sf->f_flags & MNT_ASYNC) ? em_MNT_ASYNC : 0) | ((sf->f_flags & MNT_EXRDONLY) ? em_MNT_EXRDONLY : 0) | ((sf->f_flags & MNT_EXPORTED) ? em_MNT_EXPORTED : 0) | ((sf->f_flags & MNT_DEFEXPORTED) ? em_MNT_DEFEXPORTED : 0) | ((sf->f_flags & MNT_EXPORTANON) ? em_MNT_EXPORTANON : 0) | ((sf->f_flags & MNT_EXKERB) ? em_MNT_EXKERB : 0) | ((sf->f_flags & MNT_LOCAL) ? em_MNT_LOCAL : 0) | ((sf->f_flags & MNT_QUOTA) ? em_MNT_QUOTA : 0) | ((sf->f_flags & MNT_ROOTFS) ? em_MNT_ROOTFS : 0) | ((sf->f_flags & MNT_NOCOREDUMP) ? em_MNT_NOCOREDUMP : 0) | ((sf->f_flags & MNT_NOATIME) ? em_MNT_NOATIME : 0) | ((sf->f_flags & MNT_EXNORESPORT) ? em_MNT_EXNORESPORT : 0) | ((sf->f_flags & MNT_EXPUBLIC) ? em_MNT_EXPUBLIC : 0) | ((sf->f_flags & MNT_SYMPERM) ? em_MNT_SYMPERM : 0) | ((sf->f_flags & MNT_NODEVMTIME) ? em_MNT_NODEVMTIME : 0) | ((sf->f_flags & MNT_SOFTDEP) ? em_MNT_SOFTDEP : 0); mem_set_2(buf,0); mem_set_2(buf+2,emflags&0xffff); mem_set_4(buf+4,sf->f_bsize); mem_set_4(buf+8,sf->f_iosize); mem_set_4(buf+12,sf->f_blocks); mem_set_4(buf+16,sf->f_bfree); mem_set_4(buf+20,sf->f_bavail); mem_set_4(buf+24,sf->f_files); mem_set_4(buf+28,sf->f_ffree); mem_set_4(buf+32,sf->f_fsid.val[0]); mem_set_4(buf+36,sf->f_fsid.val[1]); mem_set_4(buf+40,sf->f_owner); mem_set_4(buf+44,emflags); mem_set_4(buf+48,sf->f_syncwrites); mem_set_4(buf+52,sf->f_asyncwrites); mem_set_4(buf+56,0); copy_or_nulpad(&sf->f_fstypename[0],sizeof(sf->f_fstypename),buf+60,em_MFSNAMELEN,what,prefail); copy_or_nulpad(&sf->f_mntonname[0],sizeof(sf->f_mntonname),buf+76,em_MNAMELEN,what,prefail); copy_or_nulpad(&sf->f_mntfromname[0],sizeof(sf->f_mntfromname),buf+166,em_MNAMELEN,what,prefail); } #endif /* * Handle common code for file descriptor syscall arguments. arg is * the argument value, as returned by (eg) scarg(). prot is either 0, * meaning that no protectino check should be done, or P_R or P_W, * meaning that the descriptor has to be readable or writable. call * is the text name of the syscall, for potential error messages. */ static FD *descriptor_arg(uint32_t arg, unsigned int prot, const char *call) { FD *fd; if (arg >= nfds) { trc(TRC_SYSCALL,"%d: %s fd %lu out of range -> EBADF\n",mypid,call,(ULI)arg); return(0); } fd = fds[arg]; if (! fd) { trc(TRC_SYSCALL,"%d: %s fd %lu not open -> EBADF\n",mypid,call,(ULI)arg); return(0); } if (prot && !(fd->prot & prot)) { const char *pkind; switch (prot) { case P_R: pkind = "readable"; break; case P_W: pkind = "writable"; break; default: panic("bad prot to descriptor_arg"); break; } trc(TRC_SYSCALL,"%d: %s fd %lu not %s -> EBADF\n",mypid,call,(ULI)arg,pkind); return(0); } return(fd); } /* * Do a forkwait-style loop. * * We could leave the body of the loop empty, but that would burn CPU * unnecessarily. 1/10 second per iteration is long enough that we're * not hogging CPU but short enough that humans don't get impatient. */ static void do_forkwait(void) { volatile sig_atomic_t v; v = 1; while (v) poll(0,0,100); } /* * Do post-exec() signal-handling stuff. This mostly means resetting * caught signals to SIG_DFL. */ static void sig_postexec(void) { int i; for (i=em__NSIG-1;i>=1;i--) { switch (s.sigh[i].handler) { case em_SIG_IGN: case em_SIG_DFL: break; default: trc(TRC_SIGNAL,"%d: postexec resetting signal %d (%s) to SIG_DFL\n",mypid,i,em_signame(i,"unknown")); s.sigh[i].handler = em_SIG_DFL; bzero(&s.sigh[i].mask,sizeof(s.sigh[i].mask)); s.sigh[i].flags = 0; break; } s.sigstack_enabled = 0; s.sigstack_base = 0; s.sigstack_size = 0; } } /* * Implement exit(2). */ static SYSCALL_IMPL(sc_exit) { uint32_t ec; ec = scarg(args,0); trc(TRC_SYSCALL,"%d: exit %lu\n",mypid,(ULI)ec); exit(ec); } /* * Implement fork(2). * * The return semantics of fork()-the-syscall are undocumented. * UTSLing reveals the below: child returns <0,1>; parent, , * with the libc wrapper translating to the familiar semantics. */ static SYSCALL_IMPL(sc_fork) { pid_t kid; fflush(0); kid = fork(); if (kid < 0) SYSCALL_ERR(os2em_errno(errno)); if (kid == 0) { s.noninteractive = 1; if (forkwait) do_forkwait(); mypid = getpid(); SYSCALL_RET2(0,1); } else { SYSCALL_RET2(kid,0); } } /* * Implement read(2). * * The hardest part here is that we have to allocate a temporary buffer * to read into. We could, in theory, inspect the VM setup and * construct an iovec for readv, but that's complicated and introduces * error conditions like overruning IOV_MAX. */ static SYSCALL_IMPL(sc_read) { uint32_t d; uint32_t buf; uint32_t len; char *osbuf; FD *fd; int n; void fail(void) { free(osbuf); } d = scarg(args,0); buf = scarg(args,1); len = scarg(args,2); fd = descriptor_arg(d,P_R,"read"); if (! fd) SYSCALL_ERR(em_EBADF); trc(TRC_SYSCALL,"%d: read %ld, %08lx, %08lx\n",mypid,(LI)(int32_t)d,(ULI)buf,(ULI)len); if (len < 1) { trc(TRC_SYSCALL,"%d: read -> 0\n",mypid); SYSCALL_RET(0); } osbuf = malloc(len); if (! osbuf) { printf("Out of memory allocating read() buffer\n"); top(); } n = read(fd->fd,osbuf,len); if (n < 0) { n = os2em_errno(errno); free(osbuf); trc(TRC_SYSCALL,"%d: read -> error %d (%s)\n",mypid,n,em_strerror(n)); SYSCALL_ERR(n); } copyout(osbuf,buf,n,"read",&fail); free(osbuf); trc(TRC_SYSCALL,"%d: read -> %d\n",mypid,n); SYSCALL_RET(n); } /* * Implement write(2). * * We could copy everything into a temporary buffer, but in this case * it seems cleaner to me to convert it into writev(). */ static SYSCALL_IMPL(sc_write) { uint32_t d; uint32_t buf; uint32_t len; uint32_t part; FD *fd; int niov; static int aiov = 0; static struct iovec *iov = 0; MEMSEG *ms; int n; d = scarg(args,0); buf = scarg(args,1); len = scarg(args,2); fd = descriptor_arg(d,P_W,"write"); if (! fd) SYSCALL_ERR(em_EBADF); trc(TRC_SYSCALL,"%d: write %ld, %08lx, %08lx\n",mypid,(LI)(int32_t)d,(ULI)buf,(ULI)len); if (len < 1) SYSCALL_RET(0); niov = 0; while (len > 0) { ms = memseg_find(buf,0,"write"); if (! (ms->prot & P_R)) { printf("%d: write %08lx: not accessible\n",mypid,(ULI)buf); top(); } part = ms->end - buf; if (part > len) part = len; if (niov >= aiov) iov = realloc(iov,(aiov=niov+8)*sizeof(*iov)); iov[niov++] = (struct iovec){.iov_base=ms->data+(buf-ms->base),.iov_len=part}; buf += part; len -= part; } n = writev(fd->fd,iov,niov); if (n < 0) { n = os2em_errno(errno); trc(TRC_SYSCALL,"%d: write err %d (%s)\n",mypid,n,em_strerror(n)); SYSCALL_ERR(n); } trc(TRC_SYSCALL,"%d: write -> %d\n",mypid,n); SYSCALL_RET(n); } /* * Implement open(2). * * This is an interesting case, because, when O_CREAT is not set, the * third arg is not actually used. We load it only when we have to, * for the sake of unset-value tracking, and pass 0 to the underlying * open() if O_CREAT is not used. */ static SYSCALL_IMPL(sc_open) { const char *path; uint32_t how; uint32_t perm; int oshow; int osfd; int e; int fdp; NULTERM_STATUS nts; uint32_t d; path = nulterm_scarg(scarg(args,0),&nts); how = scarg(args,1); switch (how & em_O_ACCMODE) { case em_O_RDONLY: oshow = O_RDONLY; fdp = P_R; break; case em_O_WRONLY: oshow = O_WRONLY; fdp = P_W; break; case em_O_RDWR: oshow = O_RDWR; fdp = P_R|P_W; break; case em_O_NOACCESS: oshow = O_NOACCESS; fdp = 0; break; } #define F(x) do { if (how & em_##x) oshow |= x; } while (0) F(O_NONBLOCK); F(O_APPEND); F(O_SHLOCK); F(O_EXLOCK); F(O_ASYNC); F(O_SYNC); F(O_CREAT); F(O_TRUNC); F(O_EXCL); F(O_DSYNC); F(O_RSYNC); F(O_ALT_IO); F(O_NOCTTY); F(O_DIRECTORY); F(O_PLAIN); #undef F perm = (how & em_O_CREAT) ? scarg(args,2) : 0; osfd = open(path,oshow,perm); if (osfd < 0) { e = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(e)); } d = new_fd(osfd,0,fdp); nulterm_done(&nts); SYSCALL_RET(d); } /* * Implement close(2). */ static SYSCALL_IMPL(sc_close) { uint32_t d; FD *fd; int e; d = scarg(args,0); fd = descriptor_arg(d,0,"close"); if (! fd) SYSCALL_ERR(em_EBADF); add_vfork_backout(VFB_CLOSE,d,*fd); e = close(fd->fd); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); fds[d] = 0; free(fd); SYSCALL_RET(0); } /* * Implement wait4(2). * * The most complicated part here is converting the status value. */ static SYSCALL_IMPL(sc_wait4) { uint32_t wpid; uint32_t statusp; uint32_t options; uint32_t rusagep; int e; int st; struct rusage ru; wpid = scarg(args,0); statusp = scarg(args,1); options = scarg(args,2); rusagep = scarg(args,3); e = wait4( (wpid & 0x80000000) ? -(int)(int32_t)(uint32_t)-wpid : (int)wpid, &st, ((options & em_WUNTRACED) ? WUNTRACED : 0) | ((options & em_WNOHANG) ? WNOHANG : 0) | ((options & em_WALTSIG) ? WALTSIG : 0) | ((options & em_WNOREAP) ? WNOREAP : 0), &ru ); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); if (e == 0) SYSCALL_RET(0); if (statusp) { if (em_WIFEXITED(st)) { mem_set_4(statusp,em_W_EXITCODE(WEXITSTATUS(st),0)); } else if (em_WIFSIGNALED(st)) { mem_set_4(statusp,em_W_DEADSIG(WTERMSIG(st),WCOREDUMP(st))); } else if (em_WIFSTOPPED(st)) { mem_set_4(statusp,em_W_STOPCODE(WSTOPSIG(st))); } else { printf("Undecipherable wait4 status %#x\n",st); top(); } } SYSCALL_RET(e); } /* * Implement unlink(2). */ static SYSCALL_IMPL(sc_unlink) { const char *path; NULTERM_STATUS nts; int e; path = nulterm_scarg(scarg(args,0),&nts); if (unlink(path) < 0) { e = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(e)); } nulterm_done(&nts); SYSCALL_RET(0); } /* * Implement chdir(2). */ static SYSCALL_IMPL(sc_chdir) { const char *path; NULTERM_STATUS nts; int e; path = nulterm_scarg(scarg(args,0),&nts); if (chdir(path) < 0) { e = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(e)); } nulterm_done(&nts); SYSCALL_RET(0); } /* * Implement fchdir(2). */ static SYSCALL_IMPL(sc_fchdir) { uint32_t d; FD *fd; d = scarg(args,0); fd = descriptor_arg(d,0,"fchdir"); if (! fd) SYSCALL_ERR(em_EBADF); if (fchdir(fd->fd) < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET(0); } /* * Implement break(2). This is the actual syscall underlying brk() and * sbrk(), with the differences being dealt with by libc. */ static SYSCALL_IMPL(sc_break) { uint32_t newbrk; MEMSEG *ms; newbrk = scarg(args,0); newbrk = ROUND_UP(newbrk,PAGE_SIZE); if (newbrk > MAXDSIZE) { printf("break: %08lx exceeds data size limit\n",(ULI)newbrk); top(); } if (s.dbrk == newbrk) { trc(TRC_SYSCALL,"%d: break unchanged at %08lx\n",mypid,(ULI)s.dbrk); } else { trc(TRC_SYSCALL,"%d: break %08lx -> %08lx\n",mypid,(ULI)s.dbrk,(ULI)newbrk); } if (newbrk > s.dbrk) { ms = memseg_new_malloc(s.dbrk,newbrk-s.dbrk,P_R|P_W); bzero(ms->data,ms->size); memseg_clear_conflict(ms->base,ms->size,ms); } else if (newbrk < s.dbrk) { memseg_clear_conflict(newbrk,s.dbrk-newbrk,0); } s.dbrk = newbrk; SYSCALL_RET(0); } /* * Implement getfsstat(2). * * There is a complication here. For some underlying OS versions, we * have to use getvfsstat() instead of getfsstat(); see the comment * near the head of this file, where STATFS_VIA_STATVFS and * GETFSSTAT_VIA_GETVFSSTAT are potentially set. */ static SYSCALL_IMPL(sc_getfsstat) { #ifdef GETFSSTAT_VIA_GETVFSSTAT struct statvfs *osbuf; #define osWAIT ST_WAIT #define osNOWAIT ST_NOWAIT #define osCALL getvfsstat #define osSTORE store_statvfs_as_statfs #else struct statfs *osbuf; #define osWAIT MNT_WAIT #define osNOWAIT MNT_NOWAIT #define osCALL getfsstat #define osSTORE store_statfs #endif uint32_t embuf; uint32_t n; uint32_t emflags; int osflags; int e; int i; void fail(void) { free(osbuf); } embuf = scarg(args,0); n = scarg(args,1); emflags = scarg(args,2); osflags = ((emflags & em_MNT_WAIT) ? osWAIT : 0) | ((emflags & em_MNT_NOWAIT) ? osNOWAIT : 0); n /= 256; if (embuf == 0) { e = osCALL(0,n,osflags); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET(e); } else { osbuf = malloc(n*sizeof(*osbuf)); if (! osbuf) { printf("Out of memory allocating getfsstat() buffer\n"); top(); } e = osCALL(osbuf,n*sizeof(*osbuf),osflags); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); for (i=0;i%d\n",mypid,(unsigned long int)pid,(unsigned long int)emsig,ossig); e = kill(pid,ossig); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET(0); } /* * Implement dup(2). */ static SYSCALL_IMPL(sc_dup) { uint32_t od; FD *ofd; int osnew; uint32_t emnew; od = scarg(args,0); ofd = descriptor_arg(od,0,"dup"); if (! ofd) SYSCALL_ERR(em_EBADF); osnew = dup(ofd->fd); if (osnew < 0) SYSCALL_ERR(os2em_errno(errno)); emnew = new_fd(osnew,0,ofd->prot); SYSCALL_RET(emnew); } /* * Implement getegid(2). */ static SYSCALL_IMPL(sc_getegid) { SYSCALL_RET(getegid()); } /* * Implement getgid(2). */ static SYSCALL_IMPL(sc_getgid) { SYSCALL_RET(getgid()); } /* * Implement ioctl(2). * * We implement only a handful of ioctls. */ static SYSCALL_IMPL(sc_ioctl) { uint32_t d; uint32_t ioc; uint32_t arg; FD *fd; int e; d = scarg(args,0); ioc = scarg(args,1); fd = descriptor_arg(d,0,"ioctl"); if (! fd) SYSCALL_ERR(em_EBADF); switch (ioc) { case em_TIOCGETA: { struct termios tio; arg = scarg(args,2); e = ioctl(fd->fd,TIOCGETA,&tio); if (e < 0) SYSCALL_ERR(os2em_errno(e)); os2em_termios(&tio,arg); SYSCALL_RET(0); } break; case em_TIOCGPGRP: { int iv; arg = scarg(args,2); e = ioctl(fd->fd,TIOCGPGRP,&iv); if (e < 0) SYSCALL_ERR(os2em_errno(e)); mem_set_4(arg,iv); SYSCALL_RET(0); } break; case em_TIOCSPGRP: { int iv; arg = scarg(args,2); iv = mem_get_4(arg); e = ioctl(fd->fd,TIOCSPGRP,&iv); if (e < 0) SYSCALL_ERR(os2em_errno(e)); SYSCALL_RET(0); } break; case em_TIOCGWINSZ: { struct winsize wsz; arg = scarg(args,2); e = ioctl(fd->fd,TIOCGWINSZ,&wsz); if (e < 0) SYSCALL_ERR(os2em_errno(e)); mem_set_2(arg,wsz.ws_row); mem_set_2(arg+2,wsz.ws_col); mem_set_2(arg+4,wsz.ws_xpixel); mem_set_2(arg+6,wsz.ws_ypixel); SYSCALL_RET(0); } break; case em_FIOCLEX: fd->flags |= FDF_CLEX; SYSCALL_RET(0); break; case em_TIOCSETAW: { struct termios tio; arg = scarg(args,2); em2os_termios(arg,&tio); e = ioctl(fd->fd,TIOCSETAW,&tio); if (e < 0) SYSCALL_ERR(os2em_errno(e)); SYSCALL_RET(0); } break; case em_FIONCLEX: fd->flags &= ~FDF_CLEX; SYSCALL_RET(0); break; // When adding cases to this switch, add them to print_v_value too } printf("Unimplemented ioctl %08lx = ",(ULI)ioc); print_decoded_ioctl(stdout,ioc); printf("\n"); top(); } /* * Implement readlink(2). */ static SYSCALL_IMPL(sc_readlink) { unsigned char buf[65536]; uint32_t l; int n; const char *path; int i; uint32_t bufp; NULTERM_STATUS nts; path = nulterm_scarg(scarg(args,0),&nts); bufp = scarg(args,1); l = scarg(args,2); if (l > 65536) l = 65536; n = readlink(path,&buf[0],l); if (n < 0) { i = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(i)); } nulterm_done(&nts); trc(TRC_SYSCALL,"%d: readlink result %.*s\n",mypid,n,&buf[0]); for (i=0;i=0;i--) { argv[i] = nulterm_scarg(mem_get_4(emargv+(i<<2)),&nts_argv[i]); trc(TRC_EXEC,"%d: argv[%d] = %s\n",mypid,i,argv[i]); } envp[nenvp] = 0; for (i=nenvp-1;i>=0;i--) { envp[i] = nulterm_scarg(mem_get_4(emenvp+(i<<2)),&nts_envp[i]); trc(TRC_EXEC,"%d: envp[%d] = %s\n",mypid,i,envp[i]); } oldstate = s; oldvm = vm; vm = 0; e = do_execve(path,argv,envp); if (! e) { for (i=nargv-1;i>=0;i--) nulterm_done(&nts_argv[i]); for (i=nenvp-1;i>=0;i--) nulterm_done(&nts_envp[i]); nulterm_done(&nts_path); fflush(0); /* * As of vforkbreak(), vm must point to the parent's old VM and * vfork_dropvm to the child's new VM. */ vfork_dropvm = vm; vm = oldvm; vforkbreak(); if (during_vfork && forkwait) do_forkwait(); flush_vfork_backout(); during_vfork = 0; postexec = 1; rv->flags = 0; for (i=nfds-1;i>=0;i--) { FD *fd; fd = fds[i]; if (fd && (fd->flags & FDF_CLEX)) { add_vfork_backout(VFB_CLOSE,i,*fd); close(fd->fd); fds[i] = 0; free(fd); } } vm_postexec(vfork_dropvm); vfork_dropvm = 0; sig_postexec(); // We don't handle set-ID SYSCALL_RET(0); } else { for (i=nargv-1;i>=0;i--) nulterm_done(&nts_argv[i]); for (i=nenvp-1;i>=0;i--) nulterm_done(&nts_envp[i]); nulterm_done(&nts_path); vm_destroy(vm); vm = oldvm; s = oldstate; SYSCALL_ERR(e); } } /* * Implement madvise(2). */ static SYSCALL_IMPL(sc_madvise) { // madvise() is advisory; we always ignore it SYSCALL_RET(0); } /* * Implement getpgrp(2). */ static SYSCALL_IMPL(sc_getpgrp) { SYSCALL_RET(getpgrp()); } /* * Implement setpgid(2). */ static SYSCALL_IMPL(sc_setpgid) { int e; e = setpgid(scarg(args,0),scarg(args,1)); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET(e); } /* * Implement dup2(2). */ static SYSCALL_IMPL(sc_dup2) { uint32_t d1; FD *fd1; uint32_t d2; FD *fd2; int e; int new; d1 = scarg(args,0); fd1 = descriptor_arg(d1,0,"dup2"); if (! fd1) SYSCALL_ERR(em_EBADF); d2 = scarg(args,1); fd2 = (d2 >= nfds) ? 0 : fds[d2]; if (fd2) { add_vfork_backout(VFB_DUP2,d2,*fd2); e = dup2(fd1->fd,fd2->fd); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); fd2->prot = fd1->prot; } else { e = dup(fd1->fd); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); new = new_fd(e,d2,fd1->prot); if (new != d2) panic("impossible dup2: wanted %d, got %d",d2,new); } SYSCALL_RET(d2); } /* * Implement fcntl(2). * * We implement only a very few fcntls. */ static SYSCALL_IMPL(sc_fcntl) { uint32_t d; uint32_t cmd; FD *fd; int e; d = scarg(args,0); cmd = scarg(args,1); switch (cmd) { case em_F_DUPFD: case em_F_SETFD: fd = descriptor_arg(d,0,"fcntl"); if (! fd) SYSCALL_ERR(em_EBADF); break; default: printf("Unrecognized fcntl %lu\n",(ULI)cmd); top(); break; } switch (cmd) { case em_F_DUPFD: { int newos; newos = dup(fd->fd); if (newos < 0) SYSCALL_ERR(os2em_errno(errno)); e = new_fd(newos,scarg(args,2),fd->prot); } break; case em_F_SETFD: if (scarg(args,2) & 1) { fd->flags |= FDF_CLEX; } else { fd->flags &= ~FDF_CLEX; } break; default: panic("impossible fcntl"); break; } if (e < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET(e); } /* * Implement gettimeofday(2). */ static SYSCALL_IMPL(sc_gettimeofday) { uint32_t tvp; uint32_t tzp; struct timeval tv; tvp = scarg(args,0); tzp = scarg(args,1); trc(TRC_SYSCALL,"%d: gettimeofday %08lx, %08lx\n",mypid,(ULI)tvp,(ULI)tzp); gettimeofday(&tv,0); if (tvp) { mem_set_4(tvp,tv.tv_sec); mem_set_4(tvp+4,tv.tv_usec); } if (tzp) { mem_set_4(tzp,0); mem_set_4(tzp+4,0); } SYSCALL_RET(0); } /* * Implement getrusage(2). */ static SYSCALL_IMPL(sc_getrusage) { uint32_t who; uint32_t buf; struct rusage ru; int oswho; who = scarg(args,0); buf = scarg(args,1); trc(TRC_SYSCALL,"%d: getrusage %ld, %08lx\n",mypid,(LI)(int32_t)who,(ULI)buf); switch (who) { default: SYSCALL_ERR(em_EINVAL); break; case em_RUSAGE_SELF: oswho = RUSAGE_SELF; break; case em_RUSAGE_CHILDREN: oswho = RUSAGE_CHILDREN; break; } if (getrusage(oswho,&ru) < 0) panic("impossible getrusage failure"); store_rusage(buf,&ru); SYSCALL_RET(0); } /* * Implement fstatfs(2). * * There is a complication here. For some underlying OS versions, we * have to use fstatvfs() instead of fstatfs(); see the comment near * the head of this file, where STATFS_VIA_STATVFS and * GETFSSTAT_VIA_GETVFSSTAT are potentially set. */ static SYSCALL_IMPL(sc_fstatfs) { #ifdef STATFS_VIA_STATVFS struct statvfs sf; #define osCALL fstatvfs #define osCOPY store_statvfs_as_statfs #else struct statfs sf; #define osCALL fstatfs #define osCOPY store_statfs #endif uint32_t d; uint32_t bufp; FD *fd; int e; d = scarg(args,0); bufp = scarg(args,1); fd = descriptor_arg(d,0,"fstatfs"); if (! fd) SYSCALL_ERR(em_EBADF); e = osCALL(fd->fd,&sf); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); osCOPY(bufp,&sf,"statfs",0); SYSCALL_RET(0); #undef osCALL #undef osCOPY } /* * Implement mmap(2). */ static SYSCALL_IMPL(sc_mmap) { uint32_t addr; uint32_t len; uint32_t prot; uint32_t flags; uint32_t fd; uint64_t offset; unsigned int bwp; uint32_t end; addr = scarg(args,0); len = scarg(args,1); prot = scarg(args,2); flags = scarg(args,3); fd = scarg(args,4); offset = (scarg(args,5) * 0x100000000ULL) | scarg(args,6); if (flags & em_MAP_COPY) flags = (flags & ~em_MAP_COPY) | em_MAP_PRIVATE; if ((flags & (em_MAP_SHARED|em_MAP_PRIVATE)) == (em_MAP_SHARED|em_MAP_PRIVATE)) SYSCALL_ERR(em_EINVAL); if (len & 0x80000000) SYSCALL_ERR(em_EINVAL); bwp = offset & (PAGE_SIZE-1); offset -= bwp; len += bwp; len = ROUND_UP(len,PAGE_SIZE); if (len & 0x80000000) SYSCALL_ERR(em_EINVAL); if (flags & em_MAP_FIXED) { addr -= bwp; if (addr & (PAGE_SIZE-1)) SYSCALL_ERR(em_EINVAL); if (addr >= USRSTACK) SYSCALL_ERR(em_EINVAL); end = addr + len; if (end < addr) SYSCALL_ERR(em_EINVAL); printf("MAP_FIXED mmap not yet implemented\n"); top(); } else { addr = find_space(MAXDSIZE,len,USRSTACK); } if (flags & em_MAP_ANON) { void *mmrv; if (fd != (uint32_t)-1) SYSCALL_ERR(em_EINVAL); mmrv = mmap( 0, len, ((prot&em_PROT_READ)?PROT_READ:0) | ((prot&em_PROT_WRITE)?PROT_WRITE:0) | ((prot&em_PROT_EXEC)?PROT_EXEC:0), MAP_ANON | ((flags & em_MAP_SHARED) ? MAP_SHARED : 0) | ((flags & em_MAP_PRIVATE) ? MAP_PRIVATE : 0), -1, 0 ); if (mmrv == MAP_FAILED) { printf("Anonymous mmap failed: %s\n",strerror(errno)); top(); } memseg_new_mmap(addr,len,((prot&em_PROT_READ)?P_R:0)|((prot&em_PROT_WRITE)?P_W:0)|((prot&em_PROT_EXEC)?P_X:0),flags,mmrv); SYSCALL_RET(addr); } else { printf("Non-anonymous mmap not yet implemented\n"); top(); } } /* * Implement lseek(2). */ static SYSCALL_IMPL(sc_lseek) { uint32_t d; uint64_t off; uint32_t whence; FD *fd; off_t e; int oswhence; d = scarg(args,0); // args[1] is unused padding off = (((uint64_t)scarg(args,2)) << 32) | scarg(args,3); whence = scarg(args,4); fd = descriptor_arg(d,0,"lseek"); if (! fd) SYSCALL_ERR(em_EBADF); switch (whence) { case em_SEEK_SET: oswhence = SEEK_SET; break; case em_SEEK_CUR: oswhence = SEEK_CUR; break; case em_SEEK_END: oswhence = SEEK_END; break; default: SYSCALL_ERR(em_EINVAL); break; } e = lseek(fd->fd,off,oswhence); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); SYSCALL_RET2((e>>16)>>16,e); } /* * Implement __sysctl, the syscall behind sysctl(3). */ static SYSCALL_IMPL(sc___sysctl) { uint32_t mibp; uint32_t v; int nmib; uint32_t mib[16]; int i; // MIB length in range? v = scarg(args,1); if ((v < 2) || (v > 16)) { trc(TRC_SYSCALL,"%d: sysctl: MIB length %lu not in range 2..16 -> EINVAL\n",mypid,(ULI)v); SYSCALL_ERR(em_EINVAL); } nmib = v; // We don't support setting (yet) if (scarg(args,4)) { trc(TRC_SYSCALL,"%d: sysctl: setting not yet supported -> EPERM\n",mypid); SYSCALL_ERR(em_EPERM); } // Read MIB mibp = scarg(args,0); for (i=nmib-1;i>=0;i--) mib[i] = mem_get_4(mibp+(i<<2)); // Do it! switch (mib[0]) { case em_CTL_HW: if (em_sysctl_hw(mib+1,nmib-1,scarg(args,2),scarg(args,3),rv)) return; break; } printf("Unsupported sysctl "); for (i=0;ifd,osbuf,size); if (e < 0) SYSCALL_ERR(os2em_errno(errno)); oso = 0; emo = 0; left = size; while (oso < e) { if (oso+_DIRENT_MINSIZE(osde) > e) panic("getdents() partial (min)"); osde = (void *)(osbuf+oso); if (oso+osde->d_namlen > e) panic("getdents() partial (actual)"); if (osde->d_namlen > 255) { printf("getdents: d_namlen %d > 255, skipping entry\n",(int)osde->d_namlen); } else { l = 8 + ROUND_UP(osde->d_namlen+1,4); if (left < l) panic("getdents() overrun"); mem_set_4(buf+emo,osde->d_fileno); mem_set_2(buf+emo+4,l); switch (osde->d_type) { case DT_UNKNOWN: dt = em_DT_UNKNOWN; break; case DT_FIFO: dt = em_DT_FIFO; break; case DT_CHR: dt = em_DT_CHR; break; case DT_DIR: dt = em_DT_DIR; break; case DT_BLK: dt = em_DT_BLK; break; case DT_REG: dt = em_DT_REG; break; case DT_LNK: dt = em_DT_LNK; break; case DT_SOCK: dt = em_DT_SOCK; break; case DT_WHT: dt = em_DT_WHT; break; default: dt = em_DT_UNKNOWN; break; } mem_set_1(buf+emo+6,dt); mem_set_1(buf+emo+7,osde->d_namlen); copyout(&osde->d_name[0],buf+emo+8,osde->d_namlen,"getdents",&fail); copyout(&nulbuf[0],buf+emo+8+osde->d_namlen,l-(8+osde->d_namlen),"getdents",&fail); emo += l; } oso += osde->d_reclen; } SYSCALL_RET(emo); } /* * Implement __stat13, versioned stat(2). */ static SYSCALL_IMPL(sc___stat13) { const char *path; uint32_t stp; struct stat stb; int e; NULTERM_STATUS nts; path = nulterm_scarg(scarg(args,0),&nts); stp = scarg(args,1); e = stat(path,&stb); if (e < 0) { e = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(e)); } nulterm_done(&nts); store_stat(stp,&stb); SYSCALL_RET(0); } /* * Implement __fstat13, versioned fstat(2). */ static SYSCALL_IMPL(sc___fstat13) { uint32_t d; uint32_t stp; struct stat stb; FD *fd; d = scarg(args,0); stp = scarg(args,1); fd = descriptor_arg(d,0,"__fstat13"); if (! fd) SYSCALL_ERR(em_EBADF); if (fstat(fd->fd,&stb) < 0) panic("impossible fstat failure"); store_stat(stp,&stb); SYSCALL_RET(0); } /* * Implement __lstat13, versioned lstat(2). */ static SYSCALL_IMPL(sc___lstat13) { const char *path; uint32_t stp; struct stat stb; int e; NULTERM_STATUS nts; path = nulterm_scarg(scarg(args,0),&nts); stp = scarg(args,1); e = lstat(path,&stb); if (e < 0) { e = errno; nulterm_done(&nts); SYSCALL_ERR(os2em_errno(e)); } nulterm_done(&nts); store_stat(stp,&stb); SYSCALL_RET(0); } /* * Implement __sigaltstack14, versioned sigaltstack(2). * * We don't yet support on-signal-stack signal delivery. */ static SYSCALL_IMPL(sc___sigaltstack14) { uint32_t ssp; uint32_t ossp; uint32_t ss_base; uint32_t ss_size; uint32_t ss_flags; ssp = scarg(args,0); ossp = scarg(args,1); if (ssp) { ss_base = mem_get_4(ssp); ss_size = mem_get_4(ssp+4); ss_flags = mem_get_4(ssp+8); } if (ossp) { mem_set_4(ossp,s.sigstack_base); mem_set_4(ossp+4,s.sigstack_size); mem_set_4(ossp+8,(s.onsigstack?em_SS_ONSTACK:0)|(s.sigstack_enabled?0:em_SS_DISABLE)); } if (ss_flags & ~(uint32_t)em_SS_ALLBITS) SYSCALL_ERR(em_EINVAL); if (ss_flags & em_SS_DISABLE) { if (s.onsigstack) SYSCALL_ERR(em_EINVAL); } else { if (ss_size < em_MINSIGSTKSZ) SYSCALL_ERR(em_ENOMEM); } s.sigstack_enabled = ! (ss_flags & em_SS_DISABLE); if (ss_flags & em_SS_ONSTACK) { printf("Signal stack support incomplete\n"); top(); } s.sigstack_base = ss_base; s.sigstack_size = ss_size; SYSCALL_RET(0); } /* * Implement __vfork14, versioned vfork(2). * * We have to help the OS a bit. Most of the semantics of vfork are * taken care of by the underlying OS. But there are some cases where * OS state can be changed and, under real 1.4T, would be handled * entirely by the kernel, but in our case has traces in userland * memory (which thus survive the return to the parent). The only * example at this writing is file descriptors, which correspond to * not only OS file descriptors but small pieces of memory as well. * This is why VFORKBACKOUT exists. * * Furthermore, we can't return from here in a vforked child, or we'll * trash the parent's stack (our stack, not the emulated machine's * stack). So the actual OS-level vfork has to happen all the way up * in run(). * * We could, maybe, arrange to share emulated VM with an OS child * created with fork() rather than vfork(), but then we have the * problem of breaking that association when doing an emulated fork(). * * In order to leverage dosyscall()'s logic, though, run() arranges to * reenter us to handle syscall return. Keeping track of the control * flow is what vfork_stage is for. It is VFORK_NONE during normal * operation. We set it to VFORK_START before returning the first * time. run() then sets it to VFORK_FAIL or VFORK_SUCCESS before we * are reentered, with vfork_value set to the errno (VFORK_FAIL) or * PID (VFORK_SUCCESS) to return. */ static SYSCALL_IMPL(sc___vfork14) { trc(TRC_VFORK,"%d: %s entered, stage = %d (%s)\n",mypid,__func__,(int)vfork_stage,vfork_stage_str(vfork_stage)); switch (vfork_stage) { case VFORK_NONE: vfork_stage = VFORK_START; alert_run = 1; s.npc = s.pc; s.pc = s.xa; rv->flags |= SCRV_VFORK; return; break; case VFORK_FAIL: vfork_stage = VFORK_NONE; SYSCALL_ERR(vfork_value); break; case VFORK_SUCCESS: if (vfork_value == 0) { during_vfork ++; trc(TRC_VFORK,"%d: vfork level now %d\n",mypid,during_vfork); } else { during_vfork --; trc(TRC_VFORK,"%d: vfork level now %d\n",mypid,during_vfork); vfork_cleanup(); } vfork_stage = VFORK_NONE; SYSCALL_RET(vfork_value); break; default: panic("invalid vfork stage %d in %s",(int)vfork_stage,__func__); break; } } /* * Implement __sigaction14, versioned sigaction(2). */ static SYSCALL_IMPL(sc___sigaction14) { uint32_t sig; uint32_t act; uint32_t oact; uint32_t flags; uint32_t handler; int i; EMSIGSET mask; sig = scarg(args,0); act = scarg(args,1); oact = scarg(args,2); if ((sig < 1) || (sig >= em__NSIG)) SYSCALL_ERR(em_EINVAL); if (act) { handler = mem_get_4(act); for (i=0;i<4;i++) mask.bits[i] = mem_get_4(act+4+(i<<2)); flags = mem_get_4(act+20); if (flags & em_SA_ONSTACK) { printf("Signal stack support incomplete\n"); top(); } } if (oact) { mem_set_4(oact,s.sigh[sig].handler); for (i=0;i<4;i++) mem_set_4(oact+4+(i*4),s.sigh[sig].mask.bits[i]); mem_set_4(oact+20,s.sigh[sig].flags); } if (act) { switch (sig) { case em_SIGKILL: case em_SIGSTOP: if (handler != em_SIG_DFL) SYSCALL_ERR(em_EINVAL); break; } if (trc_if(TRC_SIGNAL)) { FILE *f; int j; const char *pref; f = trc_f(TRC_SIGNAL); fprintf(f,"%d: installing handler %08lx for %lu, mask ",mypid,(unsigned long int)handler,(unsigned long int)sig); pref = ""; for (j=1;j>5] >> (j & 31)) & 1) { const char *n; n = em_signame(j,0); if (n) fprintf(f,"%s%s",pref,n); else fprintf(f,"%s?%d",pref,j); pref = "|"; } } if (! pref[0]) fprintf(f,"0"); fprintf(f," flags %08lx\n",(unsigned long int)flags); } s.sigh[sig].handler = handler; s.sigh[sig].mask = mask; s.sigh[sig].flags = flags; if ( (handler == em_SIG_IGN) || ((handler == em_SIG_DFL) && (sigdef[sig] == SIGDEF_IGNORE)) ) s.sigpend[sig] = 0; } SYSCALL_RET(0); } /* * Implement __sigprocmask14, versioned sigprocmask(2). */ static SYSCALL_IMPL(sc___sigprocmask14) { uint32_t how; uint32_t set; uint32_t oset; uint64_t mask; how = scarg(args,0); set = scarg(args,1); oset = scarg(args,2); if (oset) { mem_set_4(oset,s.sigmask); mem_set_4(oset+4,s.sigmask>>32); mem_set_4(oset+8,0); mem_set_4(oset+12,0); } if (set) { switch (how) { default: SYSCALL_ERR(em_EINVAL); break; case em_SIG_BLOCK: case em_SIG_UNBLOCK: case em_SIG_SETMASK: break; } mask = mem_get_4(set); mask |= mem_get_4(set+4) * 0x100000000ULL; mask &= SIG_ALLMASK; switch (how) { case em_SIG_BLOCK: s.sigmask |= mask; break; case em_SIG_UNBLOCK: s.sigmask &= ~mask; break; case em_SIG_SETMASK: s.sigmask = mask; break; } s.sigmask &= SIG_CANBLOCK; if (trc_if(TRC_SIGNAL)) { FILE *f; f = trc_f(TRC_SIGNAL); fprintf(f,"%d: signal mask now ",mypid); if (s.sigmask == 0) { fprintf(f,"empty"); } else { int i; const char *pref; pref = ""; for (i=1;i> i) & 1) { const char *n; n = em_signame(i,0); if (n) fprintf(f,"%s%s",pref,n); else fprintf(f,"%s?%d",pref,i); pref = "|"; } } } fprintf(f,"\n"); } alert_run = 1; } SYSCALL_RET(0); } /* * Implement __sigsuspend14, versioned sigsuspend(2). * * We never block any signals in the underlying-OS sense, but in order * to pause waiting for a signal to arrive we have to pass _something_ * to sigsuspend. */ static SYSCALL_IMPL(sc___sigsuspend14) { uint32_t emset; uint64_t oldmask; sigset_t osmask; emset = scarg(args,0); oldmask = s.sigmask; s.sigmask = mem_get_4(emset); s.sigmask |= ((uint64_t)mem_get_4(emset+4)) << 32; s.sigmask &= SIG_CANBLOCK; while (1) { if (anysigpend && deliver_signals()) break; sigemptyset(&osmask); sigsuspend(&osmask); } s.sigmask = oldmask; SYSCALL_ERR(em_EINTR); } /* * Implement __sigreturn14, versioned sigreturn(2). */ static SYSCALL_IMPL(sc___sigreturn14) { uint32_t ctx; uint32_t pc; uint32_t npc; uint32_t psr; uint32_t g1; uint32_t o0; uint32_t sp; uint32_t maskl; uint32_t maskh; ctx = scarg(args,0); sp = mem_get_4(ctx+8); pc = mem_get_4(ctx+12); npc = mem_get_4(ctx+16); psr = mem_get_4(ctx+20); g1 = mem_get_4(ctx+24); o0 = mem_get_4(ctx+28); maskl = mem_get_4(ctx+32); maskh = mem_get_4(ctx+36); if ((pc | npc) & 3) SYSCALL_ERR(em_EINVAL); s.cc = psr_to_cc(psr); s.pc = pc; s.npc = npc; s.regs[R_G1] = g1; s.regs[R_O0] = o0; s.regs[R_SP] = sp; // no sigstack; on-stack delivery not implemented s.sigmask = ((((uint64_t)maskh) << 32) | maskl) & SIG_CANBLOCK; } /* * Implement __getcwd, the syscall behind getcwd(3). * * __getcwd's API is not documented (I got it by UTSLing); I see no way * to implement it in terms of getcwd(3), so we call the underlying * OS's __getcwd(). Ugh. * * __getcwd has no declaration visible outside libc. So, we declare it * ourselves. Double ugh. */ extern int __getcwd(char *, size_t); static SYSCALL_IMPL(sc___getcwd) { uint32_t bufp; uint32_t len; char *osbuf; int e; void fail(void) { free(osbuf); } bufp = scarg(args,0); len = scarg(args,1); osbuf = malloc(len?:1); if (! osbuf) { printf("Out of memory allocating __getcwd() buffer\n"); top(); } e = __getcwd(osbuf,len); if (e < 0) { e = errno; free(osbuf); SYSCALL_ERR(os2em_errno(e)); } if (e > len) panic("impossible __getcwd return"); copyout(osbuf,bufp,e,"__getcwd",&fail); free(osbuf); SYSCALL_RET(e); } /* * The table of syscalls. This is a relatively sparse table. They * appear here sorted by syscall number. */ static SYSENT sysent[] = { SYSINIT(exit,"V","d"), // 1 SYSINIT(fork,"d",""), // 2 SYSINIT(read,"d","dpd"), // 3 SYSINIT(write,"d","dpd"), // 4 SYSINIT(open,"d","sf(" // 5 "2@0:0=O_RDONLY,1=O_WRONLY,2=O_RDWR,3=O_NOACCESS;" "2:O_NONBLOCK;" "3:O_APPEND;" "4:O_SHLOCK;" "5:O_EXLOCK;" "6:O_ASYNC;" "7:O_SYNC;" "9:O_CREAT;" "10:O_TRUNC;" "11:O_EXCL;" "15:O_NOCTTY;" "16:O_DSYNC;" "18:O_ALT_IO;" "20:O_DIRECTORY;" "21:O_PLAIN)" "O"), SYSINIT(close,"V","d"), // 6 SYSINIT(wait4,"d","dpf(" // 7 "0:WNOHANG;" "1:WUNTRACED;" "2:WALTSIG;" "3:WNOREAP)" "p"), SYSINIT(unlink,"d","s"), // 10 SYSINIT(chdir,"V","s"), // 12 SYSINIT(fchdir,"V","d"), // 13 SYSINIT(break,"V","p"), // 17 SYSINIT(getfsstat,"d","pdf(" // 18 "0:MNT_WAIT;" "1:MNT_NOWAIT)"), SYSINIT(getpid,"dd",""), // 20 SYSINIT(getuid,"d",""), // 24 SYSINIT(geteuid,"d",""), // 25 SYSINIT(access,"V","sf(" // 33 "0:X_OK;" "1:W_OK;" "2:R_OK)"), SYSINIT(kill,"V","dv(SIG)"), // 37 SYSINIT(dup,"d","d"), // 41 SYSINIT(getegid,"d",""), // 43 SYSINIT(getgid,"d",""), // 47 SYSINIT(ioctl,"d","dv(IOCTL)p"), // 54 SYSINIT(readlink,"d","spd"), // 58 SYSINIT(execve,"V","sxx"), // 59 SYSINIT(madvise,"V","pdv(MADV)"), // 75 SYSINIT(getpgrp,"d",""), // 81 SYSINIT(setpgid,"V","dd"), // 82 SYSINIT(dup2,"d","dd"), // 90 SYSINIT(fcntl,"d","dv(FCNTL)b"), // 92 SYSINIT(gettimeofday,"V","pp"), // 116 SYSINIT(getrusage,"V","v(RUSAGE)p"), // 117 SYSINIT(fstatfs,"V","dp"), // 158 SYSINIT(mmap,"p","pdf(" // 197 "0:PROT_READ;" "1:PROT_WRITE;" "2:PROT_EXEC)" "f(" "0:MAP_SHARED;" "1:MAP_PRIVATE;" "2:MAP_COPY;" "4:MAP_FIXED;" "5:MAP_RENAME;" "6:MAP_NORESERVE;" "7:MAP_INHERIT;" "8:MAP_NOEXTEND;" "9:MAP_HASSEMAPHORE;" "1@12:0=MAP_FILE,1=MAP_ANON)" "dD"), SYSINIT(lseek,"D","d-Dv(LSEEK)"), // 199 SYSINIT(__sysctl,"d","mpppd"), // 202 SYSINIT(nanosleep,"V","pp"), // 240 SYSINIT(getdents,"d","dpd"), // 272 SYSINIT(__stat13,"V","sp"), // 278 SYSINIT(__fstat13,"V","dp"), // 279 SYSINIT(__lstat13,"V","sp"), // 280 SYSINIT(__sigaltstack14,"V","pp"), // 281 SYSINIT(__vfork14,"d",""), // 282 SYSINIT(__sigaction14,"V","v(SIG)pp"), // 291 SYSINIT(__sigprocmask14,"V","v(SIGPROCMASK)pp"), // 293 SYSINIT(__sigsuspend14,"V","p"), // 294 SYSINIT(__sigreturn14,"V","p"), // 295 SYSINIT(__getcwd,"d","pd"), // 296 [0] = { 0, 0 } }; static const int nsysent = sizeof(sysent) / sizeof(sysent[0]); /* * Implement the f syntax described in the comment on struct sysent. * Most of the hair here is string parsing. */ static const char *print_f_bits(FILE *f, const char *str, uint32_t val) { char *ep; long int n; long int m; uint32_t c; uint32_t mask; unsigned long int v; int i; int any; int found; if (*str++ != '(') panic("print_syscall_values: invalid f, no ("); any = 0; while (1) { n = strtol(str,&ep,10); if (ep == str) panic("print_syscall_values: invalid f, bad first #"); str = ep; switch (*str++) { default: panic("print_syscall_values: invalid f, bad number terminator"); break; case ':': if ((n < 0) || (n > 31)) panic("print_syscall_values: invalid f, bit # out of range"); for (i=0;str[i]&&(str[i]!=';')&&(str[i]!=')');i++) ; if (! str[i]) panic("print_syscall_values: invalid f, bad string terminator"); if (val & (((uint32_t)1) << n)) { fprintf(f,"%s%.*s",any?"|":"",i,str); val &= ~(((uint32_t)1) << n); any = 1; } str += i; break; case '@': m = strtol(str,&ep,10); if (ep == str) panic("print_syscall_values: invalid f, bad second #"); str = ep; if (*str++ != ':') panic("print_syscall_values: invalid f, bad second terminator"); if ((n < 1) || (m < 0) || (n > 32) || (m > 31) || (n+m > 32)) panic("print_syscall_values: invalid f, bad n@m"); c = val >> m; mask = (n < 32) ? (((uint32_t)1) << n) - 1 : ~(uint32_t)0; c &= mask; val &= ~(mask << m); found = 0; while (1) { v = strtoul(str,&ep,0); if (ep == str) panic("print_syscall_values: invalid f, bad test value"); if (*ep != '=') panic("print_syscall_values: invalid f, bad test terminator"); str = ep + 1; if (v & ~(unsigned long int)mask) panic("print_syscall_values: invalid f, impossible test value"); for (i=0;str[i]&&(str[i]!=',')&&(str[i]!=';')&&(str[i]!=')');i++) ; if (! str[i]) panic("print_syscall_values: invalid f, bad test string terminator"); if (c == v) { if (found) panic("print_syscall_values: invalid f, value %lu found twice",(ULI)c); fprintf(f,"%s%.*s",any?"|":"",i,str); any = 1; found = 1; } str += i; if (*str != ',') break; str ++; } if (! found) panic("print_syscall_values: invalid f, value %lu not found",(ULI)c); break; } if (*str != ';') break; str ++; } if (*str != ')') panic("print_syscall_values: invalid f, no )"); if (val) fprintf(f,"%s%08lx",any?"|":"",(ULI)val); return(str+1); } /* * Implement the v syntax described in the comment on struct sysent. * The only part even slightly complicated here is string parsing; * most of the line count is tables of values for the various keywords * supported. */ static const char *print_v_value(FILE *f, const char *str, uint32_t val) { const char *close; const char *pv; if (*str++ != '(') panic("print_syscall_values: invalid v, no ("); close = index(str,')'); if (! close) panic("print_syscall_values: invalid v, no )"); pv = 0; do <"printed"> { do <"good"> { switch (close-str) { case 3: if (!bcmp(str,"SIG",3)) { switch (val) { case em_SIGHUP: pv = "SIGHUP"; break; case em_SIGINT: pv = "SIGINT"; break; case em_SIGQUIT: pv = "SIGQUIT"; break; case em_SIGILL: pv = "SIGILL"; break; case em_SIGTRAP: pv = "SIGTRAP"; break; case em_SIGABRT: pv = "SIGABRT"; break; case em_SIGEMT: pv = "SIGEMT"; break; case em_SIGFPE: pv = "SIGFPE"; break; case em_SIGKILL: pv = "SIGKILL"; break; case em_SIGBUS: pv = "SIGBUS"; break; case em_SIGSEGV: pv = "SIGSEGV"; break; case em_SIGSYS: pv = "SIGSYS"; break; case em_SIGPIPE: pv = "SIGPIPE"; break; case em_SIGALRM: pv = "SIGALRM"; break; case em_SIGTERM: pv = "SIGTERM"; break; case em_SIGURG: pv = "SIGURG"; break; case em_SIGSTOP: pv = "SIGSTOP"; break; case em_SIGTSTP: pv = "SIGTSTP"; break; case em_SIGCONT: pv = "SIGCONT"; break; case em_SIGCHLD: pv = "SIGCHLD"; break; case em_SIGTTIN: pv = "SIGTTIN"; break; case em_SIGTTOU: pv = "SIGTTOU"; break; case em_SIGIO: pv = "SIGIO"; break; case em_SIGXCPU: pv = "SIGXCPU"; break; case em_SIGXFSZ: pv = "SIGXFSZ"; break; case em_SIGVTALRM: pv = "SIGVTALRM"; break; case em_SIGPROF: pv = "SIGPROF"; break; case em_SIGWINCH: pv = "SIGWINCH"; break; case em_SIGINFO: pv = "SIGINFO"; break; case em_SIGUSR1: pv = "SIGUSR1"; break; case em_SIGUSR2: pv = "SIGUSR2"; break; case em_SIGPWR: pv = "SIGPWR"; break; } break <"good">; } break; case 4: if (!bcmp(str,"MADV",4)) { switch (val) { case em_MADV_NORMAL: pv = "NORMAL"; break; case em_MADV_RANDOM: pv = "RANDOM"; break; case em_MADV_SEQUENTIAL: pv = "SEQUENTIAL"; break; case em_MADV_WILLNEED: pv = "WILLNEED"; break; case em_MADV_DONTNEED: pv = "DONTNEED"; break; case em_MADV_SPACEAVAIL: pv = "SPACEAVAIL"; break; case em_MADV_FREE: pv = "FREE"; break; } break <"good">; } break; case 5: switch (str[0]) { case 'I': if (!bcmp(str,"IOCTL",5)) { switch (val) { case em_TIOCGETA: pv = "TIOCGETA"; break; case em_TIOCGPGRP: pv = "TIOCGPGRP"; break; case em_TIOCSPGRP: pv = "TIOCSPGRP"; break; case em_TIOCGWINSZ: pv = "TIOCGWINSZ"; break; case em_FIOCLEX: pv = "FIOCLEX"; break; case em_TIOCSETAW: pv = "TIOCSETAW"; break; case em_FIONCLEX: pv = "FIONCLEX"; break; // When adding cases to this switch, consider implementing them (sc_ioctl) default: fprintf(f,"%08lx=",(ULI)val); print_decoded_ioctl(f,val); break <"printed">; } break <"good">; } break; case 'F': if (!bcmp(str,"FCNTL",5)) { switch (val) { case em_F_DUPFD: pv = "F_DUPFD"; break; case em_F_GETFD: pv = "F_GETFD"; break; case em_F_SETFD: pv = "F_SETFD"; break; case em_F_GETFL: pv = "F_GETFL"; break; case em_F_SETFL: pv = "F_SETFL"; break; case em_F_GETOWN: pv = "F_GETOWN"; break; case em_F_SETOWN: pv = "F_SETOWN"; break; case em_F_GETLK: pv = "F_GETLK"; break; case em_F_SETLK: pv = "F_SETLK"; break; case em_F_SETLKW: pv = "F_SETLKW"; break; case em_F_CLOSEM: pv = "F_CLOSEM"; break; } break <"good">; } break; case 'L': if (!bcmp(str,"LSEEK",5)) { switch (val) { case em_SEEK_SET: pv = "SEEK_SET"; break; case em_SEEK_CUR: pv = "SEEK_CUR"; break; case em_SEEK_END: pv = "SEEK_END"; break; } break <"good">; } break; } break; case 6: if (!bcmp(str,"RUSAGE",6)) { switch (val) { case em_RUSAGE_SELF: pv = "RUSAGE_SELF"; break; case em_RUSAGE_CHILDREN: pv = "RUSAGE_CHILDREN"; break; } break <"good">; } break; case 11: if (!bcmp(str,"SIGPROCMASK",11)) { switch (val) { case em_SIG_BLOCK: pv = "SIG_BLOCK"; break; case em_SIG_UNBLOCK: pv = "SIG_UNBLOCK"; break; case em_SIG_SETMASK: pv = "SIG_SETMASK"; break; } break <"good">; } break; } panic("print_syscall_values: invalid v, string unrecognized"); } while (0); if (pv) { fprintf(f,"%s",pv); } else { fprintf(f,"%lu=unrecognized",(ULI)val); } } while (0); return(close+1); } /* * Print syscall argument/return values. f is the FILE * to send the * resulting text to. str is the string describing the values, as * described in the comment on struct sysent. vals is the SCARGS * holding (and maybe pointing to) the values to print. * * We use nomemacc to ensure TRC_MEM doesn't see any memory accesses we * might do. */ static void print_syscall_values(FILE *f, const char *str, SCARGS *vals) { int vx; uint32_t v; if (! str) return; vx = 0; nomemacc ++; while <"loop"> (*str) { if (vx) fprintf(f,", "); switch (*str++) { case 'd': fprintf(f,"%ld",(LI)(int32_t)scarg(vals,vx++)); break; case 'p': case 'x': // same as p for the moment v = scarg(vals,vx++); if (v) fprintf(f,"%08lx",(ULI)v); else fprintf(f,"0"); break; case 'b': v = scarg(vals,vx++); fprintf(f,"%#lx=%ld",(ULI)v,(LI)(int32_t)v); break; case 's': { uint8_t c; int defer; v = scarg(vals,vx++); fprintf(f,"%08lx=\"",(ULI)v); defer = -1; while (1) { c = mem_get_1(v++); if (defer >= 0) { if ((c >= '0') && (c <= '9')) { fprintf(f,"\\%03o",defer); } else { fprintf(f,"\\%o",defer); } defer = -1; } if (! c) break; switch (c) { case '\a': fprintf(f,"\\a"); break; case '\b': fprintf(f,"\\b"); break; case '\e': fprintf(f,"\\e"); break; case '\f': fprintf(f,"\\f"); break; case '\n': fprintf(f,"\\n"); break; case '\r': fprintf(f,"\\r"); break; case '\t': fprintf(f,"\\t"); break; case '\v': fprintf(f,"\\v"); break; case '\\': fprintf(f,"\\\\"); break; default: if (c < 31) { defer = c; } else if ((c >= 127) && (c < 160)) { fprintf(f,"\\%o",c); } else { fprintf(f,"%c",c); } break; } } fprintf(f,"\""); } break; case 'o': fprintf(f,"%#lo",(ULI)scarg(vals,vx++)); break; case '-': fprintf(f,"pad"); vx ++; break; case 'O': if (scarg(vals,1) & em_O_CREAT) { fprintf(f,"%#lo",(ULI)scarg(vals,vx++)); } else { fprintf(f,"..."); vx ++; } break; case 'D': { uint64_t v64; v64 = scarg(vals,vx++); v64 = (v64 << 32) | scarg(vals,vx++); fprintf(f,"%lld",(ULLI)(int64_t)v64); } break; case 'm': { uint32_t v2; uint32_t elt; int first; v = scarg(vals,vx++); v2 = scarg(vals,vx++); fprintf(f,"%08lx/%ld=",(ULI)v,(ULI)v2); first = 1; while (v2) { elt = mem_get_4(v); v += 4; v2 --; fprintf(f,"%s%ld",first?"":".",(LI)(int32_t)elt); first = 0; } } break; case 'f': str = print_f_bits(f,str,scarg(vals,vx++)); break; case 'v': str = print_v_value(f,str,scarg(vals,vx++)); break; default: panic("print_syscall_values: invalid key char %02x",(unsigned char)str[-1]); break; } } nomemacc --; } /* * Perform a syscall. This is called when a trap instruction specifies * the code that means "do a syscall". We set up an SCARGS for any * arguments, extract the call number, handle __syscall here (we don't * cascade __syscall references), trace the arguments, perform the * call, trace the return values or error, and return by whichever * method is appropriate. */ static void dosyscall(uint32_t id) { SCARGS args; uint32_t callno; SCRV rv; uint32_t g2; uint32_t g7; void (*fn)(SCARGS *, SCRV *); FILE *f; uint32_t set; set = s.regset; g2 = s.regs[R_G2]; g7 = s.regs[R_G7]; args.setmask = (IS_REG_SET(R_O0) ? (1U << 0) : 0) | (IS_REG_SET(R_O1) ? (1U << 1) : 0) | (IS_REG_SET(R_O2) ? (1U << 2) : 0) | (IS_REG_SET(R_O3) ? (1U << 3) : 0) | (IS_REG_SET(R_O4) ? (1U << 4) : 0) | (IS_REG_SET(R_O5) ? (1U << 5) : 0) | (IS_REG_SET(R_SP) ? (1U << 6) : 0); args.nreg = 6; args.regs[0] = s.regs[R_O0]; args.regs[1] = s.regs[R_O1]; args.regs[2] = s.regs[R_O2]; args.regs[3] = s.regs[R_O3]; args.regs[4] = s.regs[R_O4]; args.regs[5] = s.regs[R_O5]; args.sp = s.regs[R_SP]; callno = id & ~(em_SYSCALL_G2RFLAG | em_SYSCALL_G7RFLAG); trc(TRC_SYSCALL,"%d: (%llu) syscall CALL %d (",mypid,s.instrs,callno); if (callno == em_SYS___syscall) { if ((unset_action != UNSET_IGNORE) && ((args.setmask & 3) != 3)) { fprintf(stderr,"__syscall call number depends on unset value\n"); if (unset_action == UNSET_ERROR) top(); } callno = args.regs[1]; args.setmask = (args.setmask & (1U << 6)) | ((args.setmask >> 2) & 0x0f); args.regs[0] = args.regs[2]; args.regs[1] = args.regs[3]; args.regs[2] = args.regs[4]; args.regs[3] = args.regs[5]; args.nreg = 4; trc(TRC_SYSCALL,"%d: __syscall -> %d (",mypid,callno); if ((callno >= nsysent) || !sysent[callno].name) { trc(TRC_SYSCALL,"?)"); } else { trc(TRC_SYSCALL,"%s)",sysent[callno].name); } } else { if ((callno >= nsysent) || !sysent[callno].name) { trc(TRC_SYSCALL,"?"); } else { trc(TRC_SYSCALL,"%s",sysent[callno].name); } } f = trc_f(TRC_SYSCALL); if (f) { fprintf(f,") ("); print_syscall_values(f,sysent[callno].args,&args); fprintf(f,")\n"); } rv.err = 0; rv.flags = ((id & em_SYSCALL_G2RFLAG) ? SCRV_G2R : 0) | ((id & em_SYSCALL_G7RFLAG) ? SCRV_G7R : 0); if (callno >= nsysent) { printf("Unknown syscall %08lx\n",(ULI)callno); top(); } fn = sysent[callno].impl; if (! fn) { printf("Unknown syscall %08lx\n",(ULI)callno); top(); } (*fn)(&args,&rv); if (rv.flags & SCRV_VFORK) return; trc(TRC_SYSCALL,"%d: (%llu) syscall RET ",mypid,s.instrs); if (rv.err == 0) { f = trc_f(TRC_SYSCALL); if (f) { fprintf(f,"success"); if (sysent[callno].rv[0] != 'V') { SCARGS a; a.regs[0] = rv.rv; a.regs[1] = rv.rv2; a.sp = 0; a.setmask = ((rv.flags & SCRV_RVSET) ? 1 : 0) | ((rv.flags & SCRV_RV2SET) ? 2 : 0) | (1U << 6); a.nreg = 2; fprintf(f," "); print_syscall_values(f,sysent[callno].rv,&a); } fprintf(f,", returning to "); } if (rv.flags & SCRV_G2R) { s.pc = g2; s.npc = s.pc + 4; trc(TRC_SYSCALL,"%%g2\n"); } else if (rv.flags & SCRV_G7R) { s.pc = g7; s.npc = s.pc + 4; trc(TRC_SYSCALL,"%%g7\n"); } else { s.cc &= ~CC_C; trc(TRC_SYSCALL,"pc/npc\n"); } if (rv.flags & SCRV_RVSET) { s.regs[R_O0] = rv.rv; SET_REG(R_O0); } if (rv.flags & SCRV_RV2SET) { s.regs[R_O1] = rv.rv2; SET_REG(R_O1); } } else { s.regs[R_O0] = rv.err; SET_REG(R_O0); trc(TRC_SYSCALL,"error %lu (%s)\n",(ULI)rv.err,em_strerror(rv.err)); s.cc |= CC_C; } } /* * Implement a trap: either an unconditional trap or a taken * conditional trap. */ static void trap(uint32_t arg) { switch (arg) { case 0: // syscall if (! IS_REG_SET(R_G1)) { fprintf(stderr,"syscall number is not set\n"); top(); } dosyscall(s.regs[R_G1]); break; case 3: // flush windows // we always spill all windows to the stack // so nothing to do here break; default: printf("Unknown trap code %08lx\n",(ULI)arg); top(); break; } } /* * Implement a trap instruction. */ static void ctrap(int cond, uint32_t arg) { int doit; doit = (conds[cond&15] >> (s.cc & 15)) & 1; if (doit) trap(arg); } /* * Implement mulscc. The arguments are the DREG number, the SREG1 * number, and the value from I, SIMM13, and/or regs[SREG2]. */ static void mulscc(int dr, int sr1, uint32_t v) { uint32_t t; uint32_t t2; NEED_REG(sr1,"mulscc SREG1"); NEED_REG(dr,"mulscc DREG"); t2 = s.regs[sr1] & 1; t = s.regs[sr1] >> 1; switch (s.cc & (CC_N | CC_V)) { case 0: case CC_N | CC_V: break; case CC_N: case CC_V: t |= 0x80000000; break; } s.regs[dr] = addcc(t,(s.y&1)?v:0); s.y = (s.y >> 1) | (t2 << 31); } /* * Emulate one instruction. This is where instruction fetch and decode * happens. Most instructions are emulated inline here; the more * complex call out to separate implementation functions. */ static void instr_(void) { uint32_t inst; uint32_t a; uint32_t v; uint32_t v2; s.xa = s.pc; if (s.xa & 3) { printf("Misaligned instruction at %08lx\n",(ULI)s.xa); top(); } s.pc = s.npc; s.npc += 4; if (s.flags & SF_ANNUL) { s.flags &= ~SF_ANNUL; return; } s.instrs ++; inst = mem_exe_4(s.xa); #define UNIMP() unimp(s.xa,inst) switch (OPC(inst)) { case 1: // format 1 // 01oo oooo oooo oooo oooo oooo oooo oooo // o = offset, in longword units s.regs[R_O7] = s.xa; SET_REG(R_O7); s.npc = s.xa + (inst << 2); break; case 0: // format 2 // 00rr rrro ooii iiii iiii iiii iiii iiii // 00ac ccco oodd dddd dddd dddd dddd dddd // r = dreg, a = annul, c = cond, o = opcode // i = immediate data, d = displacement switch (OP2(inst)) { case 0: UNIMP(); break; case 1: UNIMP(); break; case 2: // bCC cbranch(COND(inst),A(inst),s.xa+(DISP22(inst)<<2)); break; case 3: UNIMP(); break; case 4: // sethi s.regs[DREG(inst)] = IMM22(inst) << 10; SET_REG(DREG(inst)); break; case 5: UNIMP(); break; case 6: UNIMP(); break; case 7: UNIMP(); break; } break; case 2: // format 3 switch (OP3(inst)) { case 0: // add NEED_REG(SREG1(inst),"add SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"add SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] + (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 1: // and NEED_REG(SREG1(inst),"and SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"and SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] & (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 2: // or NEED_REG(SREG1(inst),"or SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"or SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] | (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 3: // xor NEED_REG(SREG1(inst),"xor SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"xor SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] ^ (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 4: // sub NEED_REG(SREG1(inst),"sub SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"sub SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] - (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 5: // andn NEED_REG(SREG1(inst),"andn SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"andn SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] & ~(I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 7: // xnor NEED_REG(SREG1(inst),"xnor SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"xnor SREG2"); s.regs[DREG(inst)] = ~(s.regs[SREG1(inst)] ^ (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)])); SET_REG(DREG(inst)); break; case 8: // addx NEED_REG(SREG1(inst),"addx SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"addx SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] + (I(inst)?SIMM13(inst):s.regs[SREG2(inst)]) + ((s.cc&CC_C)?1:0); SET_REG(DREG(inst)); break; case 12: // subx NEED_REG(SREG1(inst),"subx SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"subx SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] - (I(inst)?SIMM13(inst):s.regs[SREG2(inst)]) - ((s.cc&CC_C)?1:0); SET_REG(DREG(inst)); break; case 16: // addcc NEED_REG(SREG1(inst),"addcc SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"addcc SREG2"); s.regs[DREG(inst)] = addcc(s.regs[SREG1(inst)],I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 17: // andcc NEED_REG(SREG1(inst),"andcc SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"andcc SREG2"); s.regs[DREG(inst)] = andcc(s.regs[SREG1(inst)],I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 18: // orcc NEED_REG(SREG1(inst),"orcc SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"orcc SREG2"); s.regs[DREG(inst)] = orcc(s.regs[SREG1(inst)],I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 20: // subcc NEED_REG(SREG1(inst),"subcc SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"subcc SREG2"); s.regs[DREG(inst)] = subcc(s.regs[SREG1(inst)],I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 21: // andncc NEED_REG(SREG1(inst),"andncc SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"andncc SREG2"); s.regs[DREG(inst)] = andncc(s.regs[SREG1(inst)],I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); SET_REG(DREG(inst)); break; case 36: // mulscc mulscc(DREG(inst),SREG1(inst),I(inst)?SIMM13(inst):s.regs[SREG2(inst)]); break; case 37: // sll NEED_REG(SREG1(inst),"sll SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"sll SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] << ((I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) & 31); SET_REG(DREG(inst)); break; case 38: // srl NEED_REG(SREG1(inst),"srl SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"srl SREG2"); s.regs[DREG(inst)] = s.regs[SREG1(inst)] >> ((I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) & 31); SET_REG(DREG(inst)); break; case 39: // sra NEED_REG(SREG1(inst),"sra SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"sra SREG2"); s.regs[DREG(inst)] = sra(s.regs[SREG1(inst)],(I(inst)?SIMM13(inst):s.regs[SREG2(inst)])&31); SET_REG(DREG(inst)); break; case 40: // rd %y s.regs[DREG(inst)] = s.y; SET_REG(DREG(inst)); break; case 48: // wr %y NEED_REG(SREG1(inst),"wr %y SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"wr %y SREG2"); s.y = s.regs[SREG1(inst)] ^ (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); break; case 56: // jmpl NEED_REG(SREG1(inst),"jmpl SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"jmpl SREG2"); s.npc = s.regs[SREG1(inst)] + (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); s.regs[DREG(inst)] = s.xa; SET_REG(DREG(inst)); break; case 58: // tCC NEED_REG(SREG1(inst),"tCC SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"tCC SREG2"); ctrap(DREG(inst)&15,s.regs[SREG1(inst)]+(I(inst)?SIMM13(inst):s.regs[SREG2(inst)])); break; case 60: // save NEED_REG(SREG1(inst),"save SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"save SREG2"); v = s.regs[SREG1(inst)] + (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); window_save(); s.regs[DREG(inst)] = v; SET_REG(DREG(inst)); break; case 61: // restore NEED_REG(SREG1(inst),"restore SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"restore SREG2"); v = s.regs[SREG1(inst)] + (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]); window_restore(); s.regs[DREG(inst)] = v; SET_REG(DREG(inst)); break; default: UNIMP(); break; } break; case 3: // format 3 switch (OP3(inst)) { case 0: // ld NEED_REG(SREG1(inst),"ld SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"ld SREG2"); s.regs[DREG(inst)] = mem_get_4((I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)]); SET_REG(DREG(inst)); break; case 1: // ldub NEED_REG(SREG1(inst),"ldub SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"ldub SREG2"); s.regs[DREG(inst)] = mem_get_1((I(inst)?SIMM13(inst):s.regs[SREG2(inst)])+s.regs[SREG1(inst)]); SET_REG(DREG(inst)); break; case 2: // lduh NEED_REG(SREG1(inst),"lduh SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"lduh SREG2"); s.regs[DREG(inst)] = mem_get_2((I(inst)?SIMM13(inst):s.regs[SREG2(inst)])+s.regs[SREG1(inst)]); SET_REG(DREG(inst)); break; case 3: // ldd NEED_REG(SREG1(inst),"ldd SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"ldd SREG2"); a = (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)]; if (a & 7) { printf("ldd: %08lx: not aligned\n",(ULI)a); top(); } v = mem_get_4(a); v2 = mem_get_4(a+4); s.regs[DREG(inst)] = v; s.regs[DREG(inst)+1] = v2; SET_REG(DREG(inst)); SET_REG(DREG(inst)+1); break; case 4: // st NEED_REG(SREG1(inst),"st SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"st SREG2"); NEED_REG(DREG(inst),"st DREG"); mem_set_4( (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)], s.regs[DREG(inst)] ); break; case 5: // stb NEED_REG(SREG1(inst),"stb SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"stb SREG2"); NEED_REG(DREG(inst),"stb DREG"); mem_set_1( (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)], s.regs[DREG(inst)] ); break; case 6: // sth NEED_REG(SREG1(inst),"sth SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"sth SREG2"); NEED_REG(DREG(inst),"sth DREG"); mem_set_2( (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)], s.regs[DREG(inst)] ); break; case 7: // std NEED_REG(SREG1(inst),"std SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"std SREG2"); a = (I(inst) ? SIMM13(inst) : s.regs[SREG2(inst)]) + s.regs[SREG1(inst)]; if (a & 7) { printf("std: %08lx: not aligned\n",(ULI)a); top(); } NEED_REG(DREG(inst),"std DREG"); NEED_REG(DREG(inst)+1,"std DREG+1"); mem_set_4(a,s.regs[DREG(inst)]); mem_set_4(a+4,s.regs[DREG(inst)+1]); break; case 9: // ldsb NEED_REG(SREG1(inst),"ldsb SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"ldsb SREG2"); s.regs[DREG(inst)] = signextend(mem_get_1((I(inst)?SIMM13(inst):s.regs[SREG2(inst)])+s.regs[SREG1(inst)]),8); SET_REG(DREG(inst)); break; case 10: // ldsh NEED_REG(SREG1(inst),"ldsh SREG1"); if (! I(inst)) NEED_REG(SREG2(inst),"ldsh SREG2"); s.regs[DREG(inst)] = signextend(mem_get_2((I(inst)?SIMM13(inst):s.regs[SREG2(inst)])+s.regs[SREG1(inst)]),16); SET_REG(DREG(inst)); break; default: UNIMP(); break; } break; } } /* * Print regs[SREG1]+(I?SIMM13:regs[SREG2]), used as an address by the * memory reference instructions. */ static void print_address(FILE *mf, uint32_t opc) { int anything; anything = 0; if (SREG1(opc) != 0) { fprintf(mf,"%s",regnames[SREG1(opc)]); anything = 1; } if (I(opc)) { int32_t off; off = SIMM13(opc); if (off || !anything) { if (off < 0) fprintf(mf,"-%#lx",(ULI)-off); else fprintf(mf,"%s%#lx",anything?"+":"",(ULI)off); } } else { if (SREG2(opc) || !anything) { fprintf(mf,"%s%s",anything?"+":"",regnames[SREG2(opc)]); } } } /* * Do TRC_INSTR tracing for one instruction. Our caller has already * verified that TRC_INSTR tracing is turned on; this just * disassembles the instruction at s.pc. */ static void do_instr_trc(void) { uint32_t inst; FILE *mf; inst = mem_get_4(s.pc); mf = trc_f(TRC_INSTR); fprintf(mf,"%d: (%llu) pc %08lx, code %08lx",mypid,s.instrs,(ULI)s.pc,(ULI)inst); if (s.flags & SF_ANNUL) fprintf(mf," (annulled)"); fprintf(mf,": "); switch (OPC(inst)) { case 1: /* format 1 */ /* 01oo oooo oooo oooo oooo oooo oooo oooo */ /* o = offset, in longword units */ fprintf(mf,"call %#lx",(ULI)(s.pc+((inst&0x3fffffff)<<2))); break; case 0: /* format 2 */ /* 00rr rrro ooii iiii iiii iiii iiii iiii */ /* 00ac ccco oodd dddd dddd dddd dddd dddd */ /* r = dreg, a = annul, c = cond, o = opcode */ /* i = immediate data, d = displacement */ switch (OP2(inst)) { case 0: fprintf(mf,"unimp 0x%lx",(ULI)IMM22(inst)); break; case 4: if (inst == 0x01000000) { fprintf(mf,"nop"); } else { fprintf(mf,"sethi %%hi(0x%08lx), %s",(ULI)(IMM22(inst)<<10),regnames[DREG(inst)]); } break; { int n; const char **ccvec; const char *pref; case 2: ccvec = &icc[0]; pref = "b"; if (0) { case 6: ccvec = &fcc[0]; pref = "fb"; } if (0) { case 7: ccvec = &ccc[0]; pref = "cb"; } fprintf(mf,"%s%s",pref,ccvec[COND(inst)]); n = strlen(pref) + strlen(ccvec[COND(inst)]); if (A(inst)) { fprintf(mf,",a"); n += 2; } fprintf(mf,"%*s%#lx",12-n,"",(ULI)(s.pc+(DISP22(inst)<<2))); } break; case 1: case 3: case 5: fprintf(mf,"",(ULI)OP2(inst),(ULI)DREG(inst),(ULI)IMM22(inst)); break; } break; case 2: /* format 3 */ { static const char *rs1_ri_rd_opc[] = { "Dadd", "Xand", "Xor", "Xxor", /* 0000xx */ "Dsub", "Xandn", "Xorn", "Xxnor", /* 0001xx */ "Daddx", 0, 0, 0, "Dsubx", 0, 0, 0, /* 001xxx */ "Daddcc", "Xandcc", "Xorcc", "Xxorcc", /* 0100xx */ "Dsubcc", "Xandncc", "Xorncc", "Xxnorcc", /* 0101xx */ "Daddxcc", 0, 0, 0, "Dsubxcc", 0, 0, 0, /* 011xxx */ "Dtaddcc", "Dtsubcc", "Dtaddcctv", "Dtsubcctv", /* 1000xx */ "Dmulscc", "5sll", "5srl", "5sra", /* 1001xx */ REP3b(0), /* 101xxx */ REP3b(0), /* 110xxx */ 0, 0, 0, 0, "Dsave", "Drestore", 0, 0 }; /* 111xxx */ static const char *rdreg[] = { REP5b(0), /* 0xxxxx */ REP3b(0), /* 100xxx */ "y", "psr", "wim", "tbr", 0, 0, 0, 0, /* 101xxx */ REP4b(0) }; /* 11xxxx */ static const char *wrreg[] = { REP5b(0), /* 0xxxxx */ REP4b(0), /* 10xxxx */ "y", "psr", "wim", "tbr", 0, 0, 0, 0, /* 110xxx */ REP3b(0) }; /* 111xxx */ const char *os; int op3; int opf; op3 = OP3(inst); if ((os = rs1_ri_rd_opc[op3])) { if (inst == 0x81e80000) { /* restore %g0, %g0, %g0 -> restore */ fprintf(mf,"restore"); } else if (inst == 0x81e00000) { /* save %g0, %g0, %g0 -> save */ fprintf(mf,"save"); } else if ((op3 == 20) && (DREG(inst) == 0)) { /* subcc x, y, %g0 -> cmp x, y */ fprintf(mf,"cmp %s, ",regnames[SREG1(inst)]); if (I(inst)) fprintf(mf,"%ld",(LI)(int32_t)SIMM13(inst)); else fprintf(mf,"%s",regnames[SREG2(inst)]); } else if ( (op3 == 18) && !I(inst) && (SREG2(inst) == 0) && (DREG(inst) == 0) ) { /* orcc x, %g0, %g0 -> tst x */ fprintf(mf,"tst %s",regnames[SREG1(inst)]); } else if ( (op3 == 18) && !I(inst) && (SREG1(inst) == 0) && (DREG(inst) == 0) ) { /* orcc %g0, x, %g0 -> tst x */ fprintf(mf,"tst %s",regnames[SREG2(inst)]); } else if ((op3 == 7) && !I(inst) && (SREG2(inst) == 0)) { /* xnor x, %g0, y -> not x, y */ /* xnor x, %g0, x -> not x */ fprintf(mf,"not %s",regnames[SREG1(inst)]); if (DREG(inst) != SREG1(inst)) fprintf(mf,", %s",regnames[DREG(inst)]); } else if ((op3 == 4) && !I(inst) && (SREG1(inst) == 0)) { /* sub %g0, x, y -> neg x, y */ /* sub %g0, x, x -> neg x */ fprintf(mf,"neg %s",regnames[SREG2(inst)]); if (DREG(inst) != SREG2(inst)) fprintf(mf,", %s",regnames[DREG(inst)]); } else if ((op3 == 0) && I(inst) && (SREG1(inst) == DREG(inst))) { /* add x, y, x -> inc y, x */ /* add x, 1, x -> inc x */ fprintf(mf,"inc "); if (SIMM13(inst) != 1) fprintf(mf,"%ld, ",(LI)(int32_t)SIMM13(inst)); fprintf(mf,"%s",regnames[SREG1(inst)]); } else if ((op3 == 4) && I(inst) && (SREG1(inst) == DREG(inst))) { /* sub x, y, x -> dec y, x */ /* sub x, 1, x -> dec x */ fprintf(mf,"dec "); if (SIMM13(inst) != 1) fprintf(mf,"%ld, ",(LI)(int32_t)SIMM13(inst)); fprintf(mf,"%s",regnames[SREG1(inst)]); } else if ( (op3 == 16) && I(inst) && (SREG1(inst) == DREG(inst)) && (SIMM13(inst) == 1) ) { /* addcc x, 1, x -> inccc x */ fprintf(mf,"inccc %s",regnames[SREG1(inst)]); } else if ( (op3 == 20) && I(inst) && (SREG1(inst) == DREG(inst)) && (SIMM13(inst) == 1) ) { /* subcc x, 1, x -> deccc x */ fprintf(mf,"deccc %s",regnames[SREG1(inst)]); } else if ((op3 == 17) && (DREG(inst) == 0)) { /* andcc x, y, %g0 -> btst y, x */ fprintf(mf,"btst "); if (I(inst)) fprintf(mf,"%#lx",(ULI)SIMM13(inst)); else fprintf(mf,"%s",regnames[SREG2(inst)]); fprintf(mf,", %s",regnames[SREG1(inst)]); } else if ( (op3 == 2) && !I(inst) && (SREG1(inst) == 0) && (SREG2(inst) == 0) ) { /* or %g0, %g0, x -> clr x */ fprintf(mf,"clr %s",regnames[DREG(inst)]); } else if ( (op3 == 2) && (SREG1(inst) == 0) ) { /* or %g0, x, y -> mov x, y */ fprintf(mf,"mov "); if (I(inst)) fprintf(mf,"%#lx",(ULI)SIMM13(inst)); else fprintf(mf,"%s",regnames[SREG2(inst)]); fprintf(mf,", %s",regnames[DREG(inst)]); } else { fprintf(mf,"%s%*s%s, ",os+1,12-(int)strlen(os+1),"",regnames[SREG1(inst)]); if (I(inst)) { switch (os[0]) { case 'D': fprintf(mf,"%ld",(LI)(int32_t)SIMM13(inst)); break; case 'X': fprintf(mf,"0x%lx",(ULI)SIMM13(inst)); break; case '5': fprintf(mf,"%d",(int)(SIMM13(inst)&31)); break; } } else { fprintf(mf,"%s",regnames[SREG2(inst)]); } fprintf(mf,", %s",regnames[DREG(inst)]); } } else if ((os = rdreg[op3])) { fprintf(mf,"rd %%%s, %s",os,regnames[DREG(inst)]); } else if ((os = wrreg[op3])) { fprintf(mf,"wr %s, ",regnames[SREG1(inst)]); if (I(inst)) fprintf(mf,"0x%lx",(ULI)SIMM13(inst)); else fprintf(mf,"%s",regnames[SREG2(inst)]); fprintf(mf,", %%%s",os); } else { switch (op3) { case 52: /* 110100 - fpop1 */ { static const char *fs1_fs2_fd[] = { REP6b(0), /*000xxxxxx*/ REP3b(0), /*001000xxx*/ 0, "fmuls", "fmuld", "fmulx", /*0010010xx*/ 0, "fdivs", "fdivd", "fdivx", /*0010011xx*/ REP4b(0), /*00101xxxx*/ REP5b(0), /*0011xxxxx*/ REP7b(0), /*01xxxxxxx*/ REP8b(0) }; /*1xxxxxxxx*/ static const char *fs2_fd[] = { 0, "fmovs", 0, 0, /*0000000xx*/ 0, "fnegs", 0, 0, /*0000001xx*/ 0, "fabss", 0, 0, /*0000010xx*/ REP2b(0), /*0000011xx*/ REP4b(0), /*00001xxxx*/ REP3b(0), /*000100xxx*/ 0, "fsqrts", "fsqrtd", "fsqrtx", /*0001010xx*/ REP2b(0), /*0001011xx*/ REP4b(0), /*00011xxxx*/ REP6b(0), /*001xxxxxx*/ REP6b(0), /*010xxxxxx*/ 0, "fstoir", "fdtoir", "fxtoir", /*0110000xx*/ "fitos", 0, "fdtos", "fxtos", /*0110001xx*/ "fitod", "fstod", 0, "fxtod", /*0110010xx*/ "fitox", "fstox", "fdtox", 0, /*0110011xx*/ 0, "fstoi", "fdtoi", "fxtoi", /*0110100xx*/ REP2b(0), /*0110101xx*/ REP3b(0), /*011011xxx*/ REP5b(0), /*0111xxxxx*/ REP8b(0) }; /*1xxxxxxxx*/ opf = OPF(inst); if ((os = fs1_fs2_fd[opf])) { fprintf(mf,"%s%*s%%f%d, %%f%d, %%f%d",os,12-(int)strlen(os),"",(int)SREG1(inst),(int)SREG2(inst),(int)DREG(inst)); } else if ((os = fs2_fd[opf])) { fprintf(mf,"%s%*s%%f%d, %%f%d",os,12-(int)strlen(os),"",(int)SREG2(inst),(int)DREG(inst)); } else { fprintf(mf,"",(int)OPF(inst),(int)SREG1(inst),(int)SREG2(inst),(int)DREG(inst)); } } break; case 53: /* 110101 - fpop2 */ { static const char *cmps[] = { REP6b(0), /*000xxxxxx*/ REP4b(0), /*00100xxxx*/ 0, "fcmps", "fcmpd", "fcmpx", /*0010100xx*/ 0, "fcmpes", "fcmped", "fcmpex", /*0010101xx*/ REP3b(0), /*001011xxx*/ REP5b(0), /*0011xxxxx*/ REP7b(0), /*01xxxxxxx*/ REP8b(0) }; /*1xxxxxxxx*/ opf = OPF(inst); if ((os = cmps[opf])) { fprintf(mf,"%s%*s%%f%d, %%f%d",os,12-(int)strlen(os),"",(int)SREG1(inst),(int)SREG2(inst)); } else { fprintf(mf,"",(int)OPF(inst),(int)SREG1(inst),(int)SREG2(inst),(int)DREG(inst)); } } break; case 54: /* 110110 - cpop1 */ fprintf(mf,"",(int)OPF(inst),(int)SREG1(inst),(int)SREG2(inst),(int)DREG(inst)); break; case 55: /* 110111 - cpop2 */ fprintf(mf,"",(int)OPF(inst),(int)SREG1(inst),(int)SREG2(inst),(int)DREG(inst)); break; case 56: /* 111000 */ if (inst == 0x81c7e008) /* jmpl %i7+8, %g0 */ { fprintf(mf,"ret"); } else if (inst == 0x81c3e008) /* jmpl %o7+8, %g0 */ { fprintf(mf,"retl"); } else if (DREG(inst) == 0) /* jmpl x, %g0 */ { fprintf(mf,"jmp "); print_address(mf,inst); } else if (DREG(inst) == 15) /* jmpl x, %o7 */ { fprintf(mf,"call "); print_address(mf,inst); } else { fprintf(mf,"jmpl "); print_address(mf,inst); fprintf(mf,", %s",regnames[DREG(inst)]); } break; case 57: /* 111001 */ fprintf(mf,"rett "); print_address(mf,inst); break; case 58: /* 111010 */ os = icc[DREG(inst)&15]; fprintf(mf,"t%s%*s",os,11-(int)strlen(os),""); print_address(mf,inst); break; case 59: /* 111011 */ fprintf(mf,"iflush "); print_address(mf,inst); break; default: fprintf(mf,"",(int)OP3(inst),(int)DREG(inst),(int)SREG1(inst),(int)I(inst),(int)ASI(inst),(int)OPF(inst),(int)SIMM13(inst),(int)SREG2(inst)); break; } } } break; case 3: /* format 3 */ { static const char *addr_rd[] = { "ld", "ldub", "lduh", "ldd", 0, 0, 0, 0, /* 000xxx */ 0, "ldsb", "ldsh", 0, 0, "ldstub", 0, "swap", /* 001xxx */ REP4b(0), /* 01xxxx */ REP5b(0) }; /* 1xxxxx */ static const char *addr_asi_rd[] = { REP4b(0), /* 00xxxx */ "lda", "lduba", "lduha", "ldda", 0, 0, 0, 0, /* 010xxx */ 0, "ldsba", "ldsha", 0, 0, "ldstuba", 0, "swapa", /* 011xxx */ REP5b(0) }; /* 1xxxxx */ static const char *rd_addr[] = { 0, 0, 0, 0, "st", "stb", "sth", "std", /* 000xxx */ REP3b(0), /* 001xxx */ REP4b(0), /* 01xxxx */ REP5b(0) }; /* 1xxxxx */ static const char *rd_addr_asi[] = { REP4b(0), /* 00xxxx */ 0, 0, 0, 0, "sta", "stba", "stha", "stda", /* 010xxx */ REP3b(0), /* 011xxx */ REP5b(0) }; /* 1xxxxx */ static const char *ld[] = { REP5b(0), /* 0xxxxx */ "frld", "fsld", 0, "frldd", 0, 0, 0, 0, /* 100xxx */ REP3b(0), /* 101xxx */ "crld", "csld", 0, "crldd", 0, 0, 0, 0, /* 110xxx */ REP3b(0) }; /* 111xxx */ static const char *st[] = { REP5b(0), /* 0xxxxx */ 0, 0, 0, 0, "frst", "fsst", "fqst", "frstd", /* 100xxx */ REP3b(0), /* 101xxx */ 0, 0, 0, 0, "crst", "csst", "cqst", "crstd", /* 110xxx */ REP3b(0) }; /* 111xxx */ int op3; const char *os; op3 = OP3(inst); if ((os = addr_rd[op3])) { fprintf(mf,"%s%*s[",os,12-(int)strlen(os),""); print_address(mf,inst); fprintf(mf,"], %s",regnames[DREG(inst)]); } else if ((os = addr_asi_rd[op3]) && !I(inst)) { fprintf(mf,"%s%*s[%s+%s], %s",os,12-(int)strlen(os),"",regnames[SREG1(inst)],regnames[SREG2(inst)],regnames[DREG(inst)]); } else if ((os = rd_addr[op3])) { if (DREG(inst) == 0) { fprintf(mf,"clr%s%*s[",os+2,9-(int)strlen(os+2),""); } else { fprintf(mf,"%s%*s%s, [",os,12-(int)strlen(os),"",regnames[DREG(inst)]); } print_address(mf,inst); fprintf(mf,"]"); } else if ((os = rd_addr_asi[op3]) && !I(inst)) { fprintf(mf,"%s%*s%s, [%s+%s]",os,12-(int)strlen(os),"",regnames[DREG(inst)],regnames[SREG1(inst)],regnames[SREG2(inst)]); } else if ((os = ld[op3])) { fprintf(mf,"%s%*s[",os+2,12-(int)strlen(os+2),""); print_address(mf,inst); switch (os[1]) { case 'r': fprintf(mf,"], %%%c%d",os[0],(int)DREG(inst)); break; case 's': fprintf(mf,"], %%%csr",os[0]); break; } } else if ((os = st[op3])) { fprintf(mf,"%s%*s",os+2,12-(int)strlen(os+2),""); switch (os[1]) { case 'r': fprintf(mf,"%%%c%d",os[0],(int)DREG(inst)); break; case 's': fprintf(mf,"%%%csr",os[0]); break; case 'q': fprintf(mf,"%%%cq",os[0]); break; } fprintf(mf,", ["); print_address(mf,inst); fprintf(mf,"]"); } else { fprintf(mf,"",(int)OP3(inst),(int)DREG(inst),(int)SREG1(inst),(int)I(inst),(int)ASI(inst),(int)OPF(inst),(int)SIMM13(inst),(int)SREG2(inst)); } } break; } fprintf(mf,"\n"); } /* * Step by one instruction. This is just instr_() wrapped in code to * handle post-exec stack dumping, TRC_INSTR tracing, and ensuring * that %g0 is stuck at 0. (If any instruction could modify a * register and then later read it, we couldn't get away with * implementing %g0 this way.) */ static void instr(void) { if (postexec) { if (trc_if(TRC_STACK)) { if (! IS_REG_SET(R_SP)) panic("post-exec stack not set"); if (s.regs[R_SP] & 3) panic("post-exec stack (%08lx) misaligned",(ULI)s.regs[R_SP]); if (trc_if(TRC_STACK)) { FILE *f; uint32_t a; uint32_t v; f = fwrap_pidprefix(trc_f(TRC_STACK),0); nomemacc ++; a = s.regs[R_SP]; fprintf(f,"sp = %08lx\n",(ULI)a); if (a & 0xf) fprintf(f,"%08lx: %*s",(ULI)(a&~(uint32_t)0xf),(int)(((a&0xf)>>2)*9),""); for (;a<(uint32_t)USRSTACK;a+=4) { v = mem_get_4(a); if ((a & 0xf) == 0) fprintf(f,"%08lx: ",(ULI)a); fprintf(f," %08lx",(ULI)v); if ((a & 0xf) == 0xc) fprintf(f,"\n"); } #if USRSTACK & 0xf fprintf(f,"\n"); #endif nomemacc --; fclose(f); } } postexec = 0; } if (trc_if(TRC_INSTR)) { nomemacc ++; do_instr_trc(); nomemacc --; } instr_(); s.regs[R_G0] = 0; SET_REG(R_G0); } /* * Dump out the current memory map in a human-readable form. * * XXX We should print some indication of how each MEMSEG's type and * maybe how it arose. */ static void dump_vm(void) { MEMSEG *ms; printf("base size end prot\n"); sort_vm(); for (ms=vm;ms;ms=ms->link) { printf("%08lx %08lx %08lx %c%c%c\n", (ULI)ms->base, (ULI)ms->size, (ULI)ms->end, (ms->prot&P_R)?'R':'-', (ms->prot&P_W)?'W':'-', (ms->prot&P_X)?'X':'-'); } } /* * Turn tracing on or off at user command. cp is the argument string, * fn is a callback to be called for each parsed tracing type, and tag * is a printable description for use in messages. */ static char *trace_onoff(char *cp, void (*fn)(int), const char *tag) { char *ep; int i; for (ep=cp;*ep&&!Cisspace(*ep);ep++) ; if ((ep-cp == 1) && (*cp == '*')) { for (i=0;ioutrefs || tf->filerefs) return; fclose(tf->f); free(tf->name); free(tf); } /* * "Close" output for a tracing type. Just drop the reference and * maybe free it, and record that it's turned off. */ static void trace_close(int kind) { if (trace[kind].out) { trace[kind].out->outrefs --; trcfile_free0(trace[kind].out); trace[kind].out = 0; } } /* * "Open" output for a tracing type. First make sure it's shut off, * then unconditionally turn it on. (We do this in case it's on but * being sent somewhere else at present.) */ static void trace_open(int kind) { trace_close(kind); trace[kind].out = trace[kind].file; trace[kind].out->outrefs ++; } /* * Set up a TRCFILE for a tracing output destination. n and nlen * describe the string given as the destination. The resulting * TRCFILE pointer is returned, or nil on error. */ static TRCFILE *setup_trcfile(char *n, int nlen) { TRCFILE *tf; int fd; if ((nlen == 1) && (n[0] == '-')) return(&trcfile_stdout); tf = malloc(sizeof(TRCFILE)); tf->filerefs = 0; tf->outrefs = 0; tf->name = ntcopy(n,nlen); fd = open(tf->name,O_WRONLY|O_APPEND|O_CREAT|O_TRUNC,0666); if (fd < 0) { tf->f = 0; } else { tf->f = fdopen(fd,"a"); if (! tf->f) close(fd); } if (! tf->f) { printf("can't open %s: %s\n",tf->name,strerror(errno)); free(tf->name); free(tf); return(0); } setlinebuf(tf->f); return(tf); } /* * Set the output file for a kind of tracing. */ static void trace_setfile(int kind, TRCFILE *tf) { int on; tf->filerefs ++; on = !!trace[kind].out; trace_close(kind); if (trace[kind].file) { trace[kind].file->filerefs --; trcfile_free0(trace[kind].file); } trace[kind].file = tf; if (on) trace_open(kind); } /* * Turn tracing on for some tracing types. */ static char *trace_on(char *cp) { return(trace_onoff(cp,&trace_open,"on")); } /* * Turn tracing off for some tracing types. */ static char *trace_off(char *cp) { return(trace_onoff(cp,&trace_close,"off")); } /* * Set tracing output, either for all types or for only some of them, * depending on the argument. */ static char *trace_out(char *cp) { char *ep; char *colon; TRCFILE *tf; int i; #if TRC__N > 32 #error Update trace_out for this many types of tracing #endif uint32_t kinds; char *kp; colon = 0; for (ep=cp;*ep&&!Cisspace(*ep);ep++) if (!colon && (*ep == ':')) colon = ep; if (! colon) { tf = setup_trcfile(cp,ep-cp); if (tf) { for (i=0;i= TRC__N) { printf("bad trace name %.*s in t > command\n",(int)(cp-kp),kp); return(0); } cp ++; if (cp[-1] == ':') break; } tf = setup_trcfile(cp,ep-cp); if (tf) { for (i=0;i>i) & 1) trace_setfile(i,tf); trcfile_free0(tf); } } return(ep); } /* * Initialize tracing during startup. */ static void init_trc(void) { int i; for (i=0;i maxl) maxl = l; } for (i=0;i %s\n",maxl,trace[i].name,trace[i].out->name); } else { printf("%*s off\n",maxl,trace[i].name); } } return; } break; case '?': printf("tracing names:"); for (i=0;i)\n",*cp); return; break; case '+': cp = trace_on(cp+1); break; case '-': cp = trace_off(cp+1); break; case '>': cp = trace_out(cp+1); break; } if (cp) while (*cp && Cisspace(*cp)) cp ++; } } /* * Do TRC_MEM (log memory references) and TRC_CHG (log machine state * changes) tracing. * * XXX Change name to reflect functionality more accurately? */ static void log_chg(STATE *prevstate) { int i; int any; void pref(void) { if (! any) trc(TRC_CHG,"%d: chg: ",mypid); any ++; } if (trc_if(TRC_MEM)) { if (nmemacc) { for (i=0;irw,(ULI)m->a1); for (j=0;jn;j++) trc(TRC_CHG," %02x",m->vp[j]); trc(TRC_CHG,"\n"); } nmemacc = 0; } } if (trc_if(TRC_CHG)) { any = 0; if (s.wdepth != prevstate->wdepth) { pref(); trc(TRC_CHG," wd=%u",s.wdepth); } if (s.regset != prevstate->regset) { pref(); trc(TRC_CHG," set=%08x",s.regset); } for (i=0;i<32;i++) { if (unset_action == UNSET_IGNORE) { if (s.regs[i] != prevstate->regs[i]) { pref(); trc(TRC_CHG," %s=%08lx",regnames[i],(ULI)s.regs[i]); } } else { if (IS_REG_SET(i)) { if ( !((prevstate->regset >> i) & 1) || (s.regs[i] != prevstate->regs[i]) ) { pref(); trc(TRC_CHG," %s=%08lx",regnames[i],(ULI)s.regs[i]); } } else { if ((prevstate->regset >> i) & 1) { pref(); trc(TRC_CHG," %s=unset",regnames[i]); } } } } if (s.y != prevstate->y) { pref(); trc(TRC_CHG," y=%08lx",(ULI)s.y); } if (s.cc != prevstate->cc) { pref(); trc(TRC_CHG," cc="); print_cc(trc_f(TRC_CHG),s.cc); } if (any) trc(TRC_CHG,"\n"); } *prevstate = s; } /* * Print out the contents of some or all of the registers, implementing * the r user-interface command. The argument string is parsed into * register names, but, if no register names at all are given, all * registers get printed. * * XXX figure out a way to avoid the goto. */ static void show_regs(const char *cp) { uint64_t mask; char *ep; const char *t; int n; int i; mask = 0; while (1) { for (cp++;*cp&&Cisspace(*cp);cp++) ; if (! *cp) break; if (Cisdigit(*cp)) { n = strtol(cp,&ep,10); if ((n < 0) || (n > 31) || (cp == ep)) goto badregname; mask |= 1ULL << n; cp = ep; } else { for (t=cp;*t&&!Cisspace(*t);t++) ; for (i=0;i<32;i++) { n = strlen(regnames[i]); if ( (t-cp == n) && !strncmp(cp,regnames[i],n) ) { mask |= 1ULL << i; cp = t; break; } } if (i >= 32) { if ((ep-cp == 3) && !strncmp(cp,"npc",3)) { mask |= 1ULL << PRINT_REGS_NPC; cp = ep; } else if ((ep-cp == 2) && !strncmp(cp,"pc",2)) { mask |= 1ULL << PRINT_REGS_PC; cp = ep; } else if ((ep-cp == 2) && !strncmp(cp,"cc",2)) { mask |= 1ULL << PRINT_REGS_CC; cp = ep; } else if ((ep-cp == 1) && !strncmp(cp,"y",1)) { mask |= 1ULL << PRINT_REGS_Y; cp = ep; } else { badregname:; printf("unknown register name `%.*s'\n",(int)(ep-cp),cp); mask |= 1ULL << PRINT_REGS_BAD; } } } } if (mask == 0) mask = PRINT_REGS_ALL; if (! (mask & (1ULL << PRINT_REGS_BAD))) print_regs(stdout,mask); } /* * Implement the m (dump memory contents) user-interface command. The * most complex part here is parsing the argument string. */ static void dump_mem(const char *cp) { unsigned long int v; uint32_t v1; uint32_t v2; uint32_t a; int len; char *ep; for (;*cp&&Cisspace(*cp);cp++) ; v = strtoul(cp,&ep,16); if (ep == cp) { printf("Bad starting address\n"); return; } v1 = v; if (v1 != v) { printf("Out-of-range starting address\n"); return; } for (cp=ep;*cp&&Cisspace(*cp);cp++) ; if (*cp == '\0') { len = 1; v2 = (v1 > 0xffffff80) ? (uint32_t)-v1 : 128; } else { if (*cp == '+') { len = 1; cp ++; } v = strtoul(cp,&ep,16); if (ep == cp) { printf("Bad %s\n",len?"size":"ending address"); return; } v2 = v; if (v2 != v) { printf("Out-of-range %s\n",len?"size":"ending address"); return; } for (cp=ep;*cp&&Cisspace(*cp);cp++) ; if (*cp) { printf("Junk after arguments\n"); return; } } if (len) { v2 += v1; if ((v2 > 0) && (v2 < v1)) { printf("Address range overflow\n"); return; } } else { if (v2 < v1) { printf("(Note: swapping arguments)\n"); v = v1; v1 = v2; v2 = v; } } if (v1 == v2) return; if (v1 & 15) printf("%08lx:%*s",(ULI)(v1&~(uint32_t)15),(3*(v1&15))+(((v1&15)>8)?3:2),""); for (a=v1;a!=v2;a++) { switch (a & 15) { case 0: printf("%08lx: ",(ULI)a); break; case 8: printf(" "); break; } printf(" %02x",mem_get_1(a)); if ((a & 15) == 15) printf("\n"); } if ((a & 15) != 0) printf("\n"); nmemacc = 0; } /* * Prompt for, accept, and execute one user-interface action. The * return value is the number of instructions that should be executed * on return: zero to just call here again, a positive number to step * that many instructions, or a negative number to step until * something throws back to the top level. */ static int ui(void) { char *cp; int i; int n; char cmd[512]; printf("sparc [pc=%08lx npc=%08lx ic=%llu]> ",(ULI)s.pc,(ULI)s.npc,s.instrs); fflush(0); if (s.noninteractive) { printf("\n(exiting, non-interactive)\n"); fflush(0); exit(1); } if (fgets(&cmd[0],sizeof(cmd),stdin) == 0) exit(0); cp =&cmd[0]; i = strlen(cp); if ((i > 0) && (cp[i-1] == '\n')) cp[i-1] = '\0'; while (*cp && Cisspace(*cp)) cp ++; switch (*cp) { default: printf("`%c' not recognized (? for help)\n",*cp); break; case '\0': break; case '?': printf( "sN step N instructions\n" //"yN step N syscalls\n" "t show tracing state\n" "t +kind\n" " turn `kind' tracing on\n" "t -kind\n" " turn `kind' tracing off\n" "t >kind1,kind2,...:file\n" " send tracing output for kind1, kind2, etc to file\n" " note: does not also turn tracing on\n" "t >file\n" " like t >kind,kind,...:file for all possible kinds\n" "g go (like s with infinity as argument)\n" "r show all registers\n" "r reg [reg ...]\n" " show listed registers (number or name)\n" "M dump memory map\n" //"F dump file descriptor table\n" "m start\n" "m start end\n" "m start +bytes\n" " show memory contents (with one arg defaults to 128 bytes)\n" "Q quit\n" ); break; case 'g': return(-1); break; case 'M': dump_vm(); break; case 'm': dump_mem(cp+1); break; case 'Q': exit(0); break; case 'r': show_regs(cp); break; case 's': n = atoi(cp+1); if (n < 1) n = 1; return(n); break; case 't': do_tracing(cp+1); break; } return(0); } /* * The main loop. main() calls here once startup is complete. This is * where control returns to when a throw is done on error. It is also * where underlying-OS vfork is called on an emulated vfork, since * it's the topmost stack frame execution reaches in normal operation. * And this is where rare things that happen on instruction * boundaries, like signal delivery, are checked for. */ static void run(void) { __label__ err_throw; int i; uint64_t n; STATE savestate; void throw_out(void) { goto err_throw; } vfork_stage = VFORK_NONE; vfork_states = 0; alert_run = 0; err_jmp = &throw_out; if (0) { err_throw:; s = savestate; } n = 0; while (1) { if (n < 1) { i = ui(); if (i < 0) { n = ~(uint64_t)0; continue; } n = (unsigned int)i; continue; } savestate = s; while (n) { instr(); log_chg(&savestate); n --; if (alert_run) { alert_run = 0; // See the comment on sc___vfork14. switch (vfork_stage) { case VFORK_NONE: break; case VFORK_START: { pid_t kid; trc(TRC_VFORK,"%d: vfork START, %s actually vforking\n",mypid,__func__); kid = vfork(); if (kid < 0) { vfork_value = os2em_errno(errno); vfork_stage = VFORK_FAIL; trc(TRC_VFORK,"%d: vfork FAIL, emulator errno %lu (%s)\n",mypid,(unsigned long int)vfork_value,em_strerror(vfork_value)); continue; } else { mypid = getpid(); if (kid == 0) { STATESTACK *ss; ss = malloc(sizeof(STATESTACK)); ss->state = s; ss->link = vfork_states; vfork_states = ss; s.noninteractive = 1; } else { STATESTACK *ss; if (vfork_dropvm) { vm_destroy(vfork_dropvm); vfork_dropvm = 0; } ss = vfork_states; if (! ss) panic("vfork parent: no pending state"); vfork_states = ss->link; s = ss->state; free(ss); } vfork_value = kid; vfork_stage = VFORK_SUCCESS; trc(TRC_VFORK,"%d: vfork SUCCESS, return value %lu\n",mypid,(unsigned long int)vfork_value); continue; } } break; default: panic("invalid vfork stage %d in %s",(int)vfork_stage,__func__); break; } if (anysigpend) { deliver_signals(); log_chg(&savestate); } } } } } /* * main() is pretty simple: crack the command line, set stuff up, and * drop into run(). */ int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init_trc(); init_fds(); init_signals(); setup(); initial_exec(); run(); return(0); }