`What does it mean by speak, friend, and enter?' asked Merry.
'That is plain enough,' said Gimli. `If you are a friend, speak the password, and the doors will open, and you can enter.'
—The Fellowship of the Ring, Book 2, Chapter 4
System calls are the fundamental, most stable interface that is provided by the operating system. They are how user programs request the vast majority of kernel actions: creating, reading, and destroying files; allocating and freeing dynamic memory; executing new programs, etc.
In this studio, you will:
Please complete the required exercises below, as well as any optional enrichment exercises that you wish to complete.
As you work through these exercises, please record your answers, and when finished email your results to firstname.lastname@example.org with the phrase System Calls Studio in the subject line.
Make sure that the name of each person who worked on these exercises is listed in the first answer, and make sure you number each of your responses so it is easy to match your responses with each exercise.
Boot up your Raspberry Pi, open up a terminal window, and outside of the linux source directory in which you are keeping built versions of the Linux kernel make a new directory for your userspace programs (you will use
sftp to transfer files from shell.cec.wustl.edu into this directory).
shell.cec.wustl.edu, and (outside the linux source directory there as well) also create a new directory for your user space programs. In that directory create a new file called
lib_call.c. In Linux, all users have an associated user ID number, and the Linux library functions
setuid can be used to get and set that id, respectively. In that file, write a short C program that reads the user ID and prints it out, attempts to set it to 0 (the root uid) and indicates whether or not that attempt was successful, and then again reads and prints out the user ID.
Use the man pages at
man getuid and
man setuid to understand (1) what syntax is used to call those functions and what types of values they return, and (2) what header files you need to include in order for your program to compile.
Since the call to
setuid may fail, please do proper unix
style error checking: store the return value from setuid into a variable,
and follow it up with a test like:
if( return_val != 0 ) printf("Error: setuid failed! Reason: %s\n", strerror(errno));
Again, you can use the man pages to determine which header files to include
for those additional functions:
man strerror, and
Save your program, and try to compile it on
shell.cec.wustl.edu, as in:
gcc lib_call.c -o lib_call
which if successful will produce a binary file called
lib_call that you can run in a terminal window.
Fix any coding mistakes (or missing header files, etc.) in your program, until it compiles successfully with no errors or warnings, and then run your program on
shell.cec.wustl.edu, as in:
As the answer to this exercise, copy and paste the output from running your program on
shell.cec.wustl.edu, navigate to the appropriate directory, and get the
sftpand compile and run your program. As the answer to this exercise, please copy and paste the output of running the program on your Raspberry Pi, along with a brief description of any differences you noticed in the output of the program (or the compilation, etc.) on your Pi vs. on
man syscall(singular - this is a different page than
man syscalls- plural). You are strongly encouraged to continue to develop and store your code on
shell.cec.wustl.eduso that if your Pi freezes up you don't lose your work, and then when it's time to move things over to the Pi, use
sftpto do so.
lib_call.c file into a new file called
native_call.c, and in that new file replace the calls to
setuid with calls to
syscall. To do so, you will have to determine their ARM
cd architecture specific system call numbers by looking at the linux source file
arch/arm/include/uapi/asm/unistd.h. Note that it is good programming
practice, and also makes your code more portable (e.g., between
shell.cec.wustl.edu and your Raspberry Pi) if you use the manifest constants
for them (e.g.,
__NR_setuid) instead of hard-coding
the numbers for them directly in the code you write.
Compile your program, fixing any problems and recompiling as needed, and run it on your Raspberry Pi. As the answer to this exercise, copy and paste the output from running your new (native call) program on your Raspberry Pi.
Note:When making changes to the linux source
code, add a comment like
//CSE422 MMDDYY before each section (where MMDDYY is the month, day, and year on which the change is being made). This, along with generating
file diffs, will make it easier for you to keep track of the changes made to the kernel, and to revert them if needed.
There are five distinct tasks we need to accomplish to do this:
First, navigate into your linux source directory, then into the linux directory under it
(or whatever you renamed it to, from the long name produced by the linux
.tar.gz file) then into
include and then into the
Before modifying any files, at least make a backup
of any file you will be changing, as in:
cp syscalls.h syscalls.h.MMDDYY
where MMDDYY is the current month, day, and year (or, use svn or git or another version control system, and commit whenever you have a working version).
Declare two new function prototypes, one that take no arguments and one that
takes a single integer, at the
include/linux/syscalls.h (make sure to put them before the closing #endif).
You can use the prototype for
sys_getuid as a template for doing this. Make sure that you use
void in the argument list to indicate no arguments, since in some versions of C a function declared
f(void) (which takes no arguments)
is not the same as a function declared
f() (which inidicates that the function may take any number of parameters of unknown type).
As the answer to this exercise, show the two function prototypes you've added.
arch/arm/kernel/. The naming convention for a file that only implements a system call is to call the file by the syscall name, e.g., if our function that takes no arguments is called
sys_noargs, you would create the file
sys_noargs.c(additionally, there are other places we could have put this file, but since we're only adding this call for the (ARM architecture) Raspberry Pi, this is an appropriate place to put this code). Make a second file for your second system call in the same directory, using the same naming convention.
For the implementation of the function that takes no arguments, copy and paste the
contents of the file found here. Take a moment
to look through this file. Notice that the function declaration isn't a normal
declaration, but actually is made through the macro
(the zero in the name of the macro comes from the fact that this syscall takes
zero arguments, and is defined in
For the implementation of your second syscall, use the code in
sys_noargs.c as a
template. You'll need to change the
SYSCALL_DEFINE0 macro to
reference the function name you came up with, as well as the fact that this function
accepts an integer parameter. You do this by passing the type and the name of
the parameter to the macro, as in:
SYSCALL_DEFINE1( your_name, int, param_name )
In the body of this syscall, use the kernel function
to print a message that contains the value of the parameter that was passed to it.
You can use
printk much as you would use
printf. Be sure to return
a proper return value from this function.
As the answer to this exercise, show your implementation of the second function.
Makefile(after, of course first making a copy of it named
Makefile.MMDDYY) in the same directory as your source code files (which should be
arch/arm/kernel/) and add our two new files to the end of the object file list, which starts on the line with
obj-y(make sure you do not add your files after a
\character, as this specifies the start of a new line). Then, change the file extensions for your files from
.o(this implicitly tells the build system to generate the object (
.o) file it needs by compiling the source (
.c) file that has the same name except for the file extension). Be sure to add only the names of the files, with space characters in between them - as you may guess from the instructions above about where to put the names, formatting rules for how a
Makefileis laid out can be a bit picky.
As the answer to this exercise, show the newly modified line of the
that lists the new
.o files to generate.
Before modifying this file, make a copy into
Then, open the file and write system call numbers 391 and 392 for
your new system calls near the bottom of the file. Use the
other system calls as a template for how to do so.
Next we will modify
arch/arm/kernel/calls.S. This is actually
an ARM assembly language file that defines the system call table. As
before, first make a backup of
calls.S and then
add two new invocations of the
CALL macro near the end
of the file for your new system call functions.
Finally, we need to update the symbol that tells the kernel how many system
calls that it has. Go to
arch/arm/include/asm/unistd.h, make a
backup copy, and update the value of
__NR_syscalls from 392 to 396. This
may seem strange, since we increased the number by 4 but are only adding 2 new syscalls,
but on the ARM architecture the system call table can only be
certain sizes. You might want to check out the enrichment exercises, for more info
As the answer to this exercise, show the new lines you added to
drivers, etc.) you should first issue the command
make cleanto remove the artifacts from your previous build.
Then, to differentiate your new kernel from the one you built in the last
studio, we'll modify the kernel LOCALVERSION. Rather than using the
menuconfig interface like we did last time, this time we will modify
the configuration directly. In the base directory, edit the file
.config (the leading period means that this is a hidden
file that is not normally displayed) and modify the
CONFIG_LOCALVERSION string to reflect the fact that this new
kernel implements the extensions from this syscall studio.
You should now build and install your new kernel following the steps outlined in
the previous studio (starting with step 4, after you had already unpacked the Linux
distribution, set up the configuration, etc.).
Once your new kernel is booted and running on your Raspberry Pi,
open up a terminal window and run the command
uname -a to confirm the new
version is running (you should see the new LOCALVERSION string you used). As the answer to this exercise please give the output that was produced by running
uname -a on
native_call.cto a new file called
new_call.c, and in that new file replace the native calls that invoke the
setuidsyscalls with the appropriate native calls to the new functions you wrote (i.e., replace the syscall number for
getuidwith the one for the new syscall that takes no arguments and replace the syscall number for
setuidwith the one for the new syscall you added that takes one argument).
On your Raspberry Pi, compile and run this new program, and after running it check
the system log by issuing the command
dmesg. As the answer to
this exercise, copy and paste the lines of the system message log that show the
messages that were output to it by your new syscall functions.
To submit this studio, include the answers to exercises given above, and attach the userspace programs you wrote, as well as diffs of the kernel source code files you modified. To create a diff, use the diff program with the backup versions of the source code you modified.
diff -up unistd.h unistd.h.MMDDYY > unistd.h.diff
For this studio, you will submit copies of the following files:
If you don't have an original copy for any of these files, you can find them in the original source code package.
As the answer to any of these optional exercies that you would like to try, please briefly describe what you learned (and give answers to any questions the exercise contains).
The kernel code that contains the entry point for system calls is found