CS644 hw1 Linux Syscalls and Warmup on Xinu
due Sunday midnight, Feb. 14, 20 points.
Useful gdb commands, beyond the basics, for low-level access.
For basic commands, see the gdb link from www.cs.umb.edu/ulab.
disas main disassembly
i reg register display
x/x 0x12345 hex display of location, like md
x/s 0x12345 string display
x/10i $pc display 10 instructions starting from current pc
si single-step by one instruction
b *0x12345 set breakpoint at address
0. Post a test message in thread "hw1 test" in the cs644 Google group,
that is, reply to the posting of that name.
1. UNIX (Linux) Syscalls
See the class handout of a script of a run on Solaris that found the
"ta" instruction that is the system call instruction for the Sparc
processor. Now we want to find the "int" instruction used for system
calls with the x86 processor (for all OS's running on x86.)
Similarly run gdb on a.out produced on our linux system sf06.cs.umb.edu
by compiling the following hi.c with -g for debugging:
hi.c:
main()
{
write(1,"hi\n",3);
write(1,"hi2\n",4);
return 6; /* should result in syscall exit(6) */
}
Here we are requesting output to stdout (fd=1) via the write system call.
Produce a script of gdb showing:
a. Setting a breakpoint in main and running the program to it.
b. The assembly language in main (disas main). How are the 3 args
of write being communicated to write here? Show the string "hi\n"
by finding the string address in the assembly code of main
and using "x/s 0x...".
(Note that on Sun Sparc the function arguments are passed in
registers but in x86 they are passed on the stack, because
the x86 processor has so few general registers.)
c. After execution has reached main, set a breakpoint at the
*second call* to write, and proceed to it. Then single-step
with si until you see that you're in a "write" routine
within the C library (i.e. above 0x40000000).
Use disas
to display the code and see the system call.
The reason to use a second write call here is that the first
call to write requires a lengthy lookup taking about 500 instructions,
(resolving the DLL function location) whereas the subsequent calls
are fast and thus easy to trace. You can prove to yourself that
the system call you found is used the first time too by breakpointing
it once you have its address.
d. Use si to proceed across the system call. Note how the whole
system call execution appears at user level to be due to this one
instruction. Is it int $0x80 or sysenter or what?
At what (virtual) address is the syscall
instruction? Does gdb display a name for this area of code?
e. Set a breakpoint on the return instruction at the end of main
and proceed to it. Then si back into the startup module (this
is one way to find it.)
Edit in labels for the parts a, b, etc., and the 3 args to write.
Leave this as linux_syscalls.script in your hw1 dir.
2. Run "strace a.out" for the Linux program from 1. above.
Do "man strace" on Linux to find out about this tool. Solaris
has a similar tool called truss. See the C library being located
and mmap'd in. The last few lines of the strace output describe
the execution of main. Leave this in strace.script.
3. Use ps to find out the pid of your shell process. Then
use "cat /proc//maps" as shown on Love, pg. 260
to see the memory areas of that user program. Determine
the range of addresses for its code, data, heap (malloc'd data),
C library DLL, any other DLLs, and the user stack, in
file pmap.txt.
4. Write a little user program for Linux, memtest.c, that accepts a
number on the command line, and does a single malloc of that number
of MB of memory, and then uses memset to set all that memory to 0.
After these operations, do a sleep(30) to make the program
sleep for 30 seconds, giving you time to get its pmap.
Run the program when noone else is running it, and see how
large a contiguous memory area you can get. (It is safe to
run it when others are, but you might get a different answer.)
Also get the pmap information for the largest case and
explain it, in memtest.txt. You might want to try strace on
this program too.
NOTE: as of Tuesday, Feb. 9, the SAPCs are still not reliably
available because of a network problem. I'll post to the
Google group when they are working properly.
5. Warmup on Xinu, and using remote gdb
See $pcex/gdb.script for getting started with remote gdb.
Before starting the script, build and download prodcons.lnx,
starting from $xuex/prodcons.c and $xuex/Makefile.
Do the "gdb" command in Tutor to turn debugging
over to remote gdb. Then open another window and run i386-gdb.
Recall that prodcons has a top-level user process and two other
processes, prod and cons. In addition, Xinu has a "null process",
process 0--read Comer, p. 61. This program does no user input,
so it is easy to work with under remote gdb.
Make a script (in xinu.script) of running remote gdb to show
how you did this:
a. Run i386-gdb on ulab, and use "tar rem /dev/remgdb11" or whatever
sys # you are using. (Systems 11-14 have faster download.)
b. Set breakpoints at create and main.
c. Do "c" to start the program (or "set $eip = 0x100100" followed
by "c" to restart)
d. It should hit your create breakpoint as it creates the top-level
user process. The special null process is specially set up, not
by the create syscall, so you won't see a breakpoint hit for that.
Continue from there.
e. It should hit your main breakpoint as it starts the user
process. Continue. It should hit create again, as process "cons"
is created. Continue.
f. It should hit create again, as process "prod" is created.
Use "finish" to let it do the create call and deliver you back
to the caller in main.
g. Now the system has 4 processes, prnull and 3 user processes.
The one just created shows you the return value from create: 47.
Why 47? There are 50 process slots in this version of Xinu,
up from 10 in the original. See p. 54 and correct NPROC from
10 to 50. See $xuker/../h/proc.h for the current version.
Good idea to print this out.
The null process gets slot 0, while all the other processes are
allocated from the bottom of the table, so 49 for main, 48 for
the first worker and 47 for the second.
Verify this by printing out proctab[0], the pentry for the
null process, and proctab[49], proctab[48], and proctab[47].
You can see the pnames for each process. Note that gdb
can print whole structs at once, or even arrays of structs.
h. Looking more carefully at these pentries, print out their
entry points in the code area (paddr), in hex:
gdb) p/x proctab[0].paddr etc.
Also print out pbase and plimit in hex for each.
6. Draw a picture of memory, showing the code areas and 4 stacks,
in xinu.txt, or on paper.
Files to deliver:
1. linux_syscalls.script
2. strace.script
3. pmap.txt
4. memtest.c, memtest.txt
5. xinu.script
6. xinu.txt (or paper)