PDP11 system calls when running Unix ==================================== File: Docs.Comp.PDP11.Unix.SYSCalls - Update 0.01 Author: J.G.Harston - Date: 24-02-2003 C system calls to the Unix kernel are translated to a SYS assembler macro. This SYS macro is assembled as a TRAP call with the parameters embedded inline in the code after the TRAP instruction. The parameters are stored left-to-right going up in memory, for instance: open(char *name, mode) which assembles as: SYS open; name; mode becomes TRAP 5 EQUW *name EQUW mode Of course, this means that for variant parameters, the code space needs to be writable. Many program deal with this by putting 'empty' SYS structures in writable dataspace, filling it with the required TRAP instruction and parameters and using the SYS ind indirection call, passing the address of the filled in structure. This also gets around the problem of seperate code and data space. The kernal is able to read the parameters of the SYS ind call and as far as the kernel is concerned it is all data space. So, as an example: open(char *name, mode) could be assembled as: MOV {mode},call+4 MOV {*name},call+2 MOV {TRAP 5},call SYS ind; call which becomes something like: MOV {mode},call+4 MOV {*name},call+2 MOV {TRAP 5},call TRAP 0 EQUW call ... call: TRAP 0 ; Overwritten with TRAP opcode EQUW 0 ; Overwritten with parameter 1 EQUW 0 ; Overwritten with parameter 2 EQUW 0 ; Overwritten with parameter 3 EQUW 0 ; etc. Registers --------- Some calls also expect an entry parameter in R0. Calls return a value in R0 and sometimes also R1. If an error occured, the Carry flag is set and R0 holds an error number, otherwise Carry is clear. Some examples ------------- putchar(a) could eventually become something like: MOV a,buffer ; put the character in a buffer MOV #1,R0 ; STDOUT=1 TRAP 4 ; SYS write; buffer; nbytes EQUW buffer ; address of buffer EQUW 1 ; one character to output BCS ErrorOccured ; Do something with any error open(char *name, mode) becomes (something like): MOV trap+5,call ; where trap=opcode of TRAP 0 instruction MOV name,call+2 MOV mode,call+4 TRAP 0 EQUW call ; point to SYS open; name; mode BCS ErrorOccured ... call: TRAP 0 EQUW 0 ; filled in with name EQUW 0 ; filled in with mode TRAP C function call Assembler ---------------------------------------------------------------------------- 0 indir(int *call) SYS indir; call 1 exit(int status) r0=status SYS exit 2 fork() SYS fork r0=new process ID 3 read(filedes, char *buffer, nbytes) r0=filedes SYS read; buffer; nbytes r0=byte count 4 write(filedes, char *buffer, nbytes) r0=filedes SYS write; buffer; nbytes r0=byte count 5 open(char *name, mode) SYS open; name; mode r0=filedes 6 close(filedes) r0=filedes SYS close 7 wait(int *status) SYS wait r0=process ID, r1=status 8 creat(char *name, mode) SYS creat; name; mode r0=filedes 9 link(char *name1, char *name2) SYS link; name1; name2 10 unlink(char *name) SYS unlink; name 11 exec(char *name, char *argv[]) SYS exec; name; argv 12 chdir(char *dirname) SYS chdir; dirname 13 long time(long tp) SYS time r0-r1=time since 1970 14 mknod(char *name, mode, addr) SYS mknod; name; mode; addr 15 chmod(char *name, mode) SYS chmod; name; mode 16 chown(char *name, owner, group) SYS chown; name; owner; group 17 char *brk(addr) SYS break; addr 18 stat(char *name, buf) SYS stat; name; buf 19 lseek(filedes, long offset, whence) r0=filedes SYS lseek; offsethi; offsetlo; whence r0=pointerhi, r1=pointerlo 20 getpid() SYS getpid r0=pid 21 mount(char *special, char *name, rwflag) SYS mount; special; name; rwflag 22 umount(char *special) SYS umount; special 23 setuid(uid) r0=uid SYS setuid 24 getuid() SYS getuid r0=real uID, r1=effective uID 25 stime(long *tp) r0-r1=time SYS time 26 ptrace(request, pid, int *addr, data) r0=data SYS ptrace; pid; addr; request r0=value 27 28 fstat(filedes, buf) r0=filedes SYS fstat; buf 29 pause() SYS pause 30 utime(char *file, timep) SYS utime; file; timep 31 stty(filedes, argp) r0=filedes SYS stty; argp 32 gtty(filedes, argp) r0=filedes SYS gtty; argp 33 34 nice(inc) r0=priority SYS nice 35 ftime(tp) SYS ftime; bufptr 36 sync() SYS sync 37 kill(pid, sig) r0=pid SYS kill; sig 38 switch() SYS switch r0=front panel switches 39 40 41 dup(int filedes, int filedes2) r0=filedes1, r1=filedes2 SYS dup r0=newfiledes 42 pipe(int filedes[2]) SYS pipe r0=read filedes r1=write filedes 43 times(buffer) SYS times; buffer 44 profil(char *buff, int bufsiz, int offset, int scale) SYS profil; buff; bufsiz; offset; scale 45 tui SYS tui 46 setgid(gid) r0=gid SYS setgid 47 getgid() SYS getgid r0=real gID, r1=effective gID 48 (*signal(sig, (*func)())() SYS signal; sig; label r0=old label (RTI or RTT to return from interrupt) 49 50 51 acct(char *file) SYS acct; file 52 phys(segreg, size, physadr) SYS phys; segreg; size; physadr 53 lock(flag) SYS lock; flag 54 ioctl(filedes, request, argp) SYS ioctl; filedes; request; argp 55 56 57 58 59 exece SYS exece; name; argv; envp 60 umask(complmode) SYS umask; complmode 61 chroot(char *dirname) SYS chroot; dirname 62 63 ----------------------------------------------------------------------------- RT-11 and EMTs -------------- Programs communicate with RT-11 with EMT calls, while Unix uses TRAPs. This allows a host OS to respond to both TRAPs and EMTs and so support calls to both systems. This technique was used widely to migrate programs from RT-11 to Unix. The programs would make EMT calls, and a minimal EMT handler would translate these to the appropriate set of Unix TRAP calls. By doing this, the program code did not need to be rewritten. Code layout ----------- When loading code Unix checks the start of the file for a header as defined in the 'a.out' man page. If there is no header the code is just entered at the first location. Otherwise, the file is loaded from after the header, and entered at the entry location. 000000 word amagic This is one of: 0407 normal - opcode for BR 000020 0410 read-only text - opcode for BR 000022 0411 separated I&D - opcode for BR 000024 0405 overlay - opcode for BR 000014 000002 word atext size of text segment 000004 word adata size of initialised data 000006 word abss size of uninitialised data 000010 word asyms size of symbol table 000012 word aentry entry point 000014 word aunused not used 000016 word aflag relocation info stripped 000020 program code length=atext, entered at aentry +atext initialised data length=adata +adata relocation information length=atext+adata +atext+adata symbol table length=asyms +asyms end of loaded file Entry to code ------------- Unix enters code with registers set up as follows: r0-r5=0 r6=initial stack at top of memory r7=entry point, usually 0 The PDPTube PDP-11 emulator and the PDPTube client interface takes advantage of this and use R5 to hold a magic number to indicate the environment it is running in. It enters code with R5=&0BBC if running on an Acorn/RISC OS environment where EMT calls can be used to access the host I/O. Notes ----- Some years ago I started writing a PDP11 emulator, so I wanted to know what system calls looked like so that I could implement some to provide the emulator with some I/O. For instance, what TRAP or EMT should the program use to output a character to the output stream? It turned out that it was increadibly difficult to find this information. Books only referred obliquily to I/O. The Unix section 2 man pages listed assembler equivalents of C system calls, but these were SYS calls. SYS isn't a PDP11 opcode. It was obviously a macro, but I couldn't find what it expanded to. Eventually, I managed to track down the information. References ---------- Mahoney, Bill, "Indirect system call", news:comp.unix.wizards, 1989-11-10 http://mdfs.net/Docs/Comp/Unix/pdp11/SysCalls2 http://groups.google.com/etc. Unix authors, Unix manual section 2, 1979 http://mdfs.net/System/Unix/v7/usr/man/man2 http://mdfs.net/System/Unix/v7/usr/man/man2/intro.2 da Silva, Peter, "Running RT/RSTS code under Unix", news:comp.sys.dec, 1999-06-02 http://mdfs.net/Docs/Comp/pdp11/EMTs1.htm Lions, J., "Unix Operating System Source Code Level Six", 1977-Jun. Unix Manual Section 2 Summary ----------------------------- ACCT acct(char *file) r0=*file SYS 51 Returns -1 on error BRK char *brk(addr) char *sbrk(incr) Returns 0=ok, -1=not enough memory SYS 17, addr CHDIR chdir (char *dirname) chroot (char *dirname) Returns 0=ok SYS 12 - chdir SYS 61 - chroot CHMOD chmod (*name, mode) Returns 0=ok, -1=not found/not owner SYS 15,name,mode CHOWN chown(char *name,owner,group) Returns 0=ok, -1=not owner SYS 16,name,owner,group CLOSE close(fildes) r0=filedrescriptor SYS 6 CREATE creat(char *name,mode) SYS 8,name,mode returns r0=file descriptor DUP dup(int fildes) dup2(int filedes, filedes2) r0=filedes r1=filedes2 SYS 41 returns r0=filedes EXEC execcl(char *name, char *arg0, char *arg1, ... *argn, 0) execv(char *name, char *argv[]) exec - SYS 11,name,argv exece - SYS 59,name,argv,envp EXIT exit(int status) _exit(int status) r0=status SYS 1 FORK fork() SYS 2 r0=new process ID GETPID getpid() SYS 20 r0=pid GETUID getuid() geteuid() getgid() getegid() getuid - SYS 24 r0=real user ID r1=effective user ID getgid - SYS 47 r0=real user ID r1=effective user ID INDIR=0 SYS 0, call INTRO ->SYS.s - assembly language interface for SYStem calls error returns C set, error number in r0 return values in r0 and r1 IOCTRL ioctl(filedes, request, argp) struct sgttyb *argp; ioctl=54 SYS 54,filedes,request,argp stty=31 r0=file descriptor SYS stty,argp r0=filedes gtty=32 SYS gtty,argp KILL kill(pid, sig) kill=37 r0=pid SYS kill,sig LINK link(char *name1, char *name2) link=9 SYS link,name1,name2 LOCK lock(flag) lock=53 SYS lock; flag LSEEK long lseek(filedes, long offset, whence) long tell(filedes) lseek=19 SYS lseek; offset1; offset2; whence offset1=high, offset2=low returns r0,r1=pointer MKNOD mknod(char *name, mode, addr) mknod=14 SYS mknod; name; mode; addr MOUNT mount(char *special, char *name, rwflag) umount(char *special) mount=21 SYS mount; special; name; rwflag umount=22 SYS umount; special NICE nice(inc) nice=34 r0=priority SYS nice OPEN open(char *name, mode) open=5 SYS open; name; mode r0=file descriptor PAUSE pause() pause=29 SYS pause PHYS phys(segreg, size, physadr) phys=52 SYS phys; segreg; size; physadr PIPE pipe(int filedes[2]) pipe=42 SYS pipe r0=read file descriptor r1=write file descriptor PROFIL profil(char *buff, int bufsiz, int offset, int scale) profil=44 SYS profil; buff; bufsiz; offset; scale PTRACE ptrace(request, pid, int *addr, data) ptrace=26 r0=data SYS ptrace; pid; addr; request r0=value READ read(filedes, char *buffer, nbytes) read=3 r0=filedes SYS read; buffer; nbytes r0=byte count SETUID setuid(uid) setuid=23 r0=uiser ID SYS setuid setgid=46 r0=group ID SYS setgid SIGNAL (*signal(sig, (*func)())() signal=48 SYS signal; sig; label r0=old label RTI or RTT returns from the interrupt STAT stat (char *name, buf) struct stat *buf; fstat(filedes, buf) struct stat *buf stat=18 SYS stat; name; buf fstat=28 r0=filedes SYS fstat; buf STIME stime(long *tp) stime=25 r0-r1=time SYS time SYNC sync() sync=36 SYS sync TIME long time(0) long time(long *tloc) ftime(tp) struct timeb *tp; ftime=35 SYS ftime; bufptr time=13 SYS time r0-r1=time since 1970 TIMES times(buffer) struct tbuffer *buffer times=43 SYS times; buffer UMASK umask(complmode) umask=60 SYS umask; complmode UNLINK unlink(char *name) unlink=10 SYS unlink; name UTIME utime(char *file, timep) utime=30 SYS utime; file; timep WAIT wait(int *status) wait=7 SYS wait r0=process ID r1=status WRITE write(filedes, char*buffer, nbytes) write=4 r0=filedes SYS write; buffer; nbytes r0=byte count