`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. We encourage you to please work in groups of 2 or 3 people on each studio (and the groups are allowed to change from studio to studio) though if you would prefer to complete any studio by yourself that is allowed.
As you work through these exercises, please record your answers, and when you finish them each and every person who worked on them should please log into Canvas, select this course in this semester, and then upload a file containing them and also upload any other files the assignment asks for, and submit those files for this studio assignment (there should be a separate submission from each person who worked together on them).
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.
man 2 syscalls.
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 if you have put a copy of it 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.
man 2 getuid and
man 2 setuid man pages 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 appropriate (section 3) man pages to determine which header files to include
for those additional library functions:
man 3 printf (not
man printf or
man 1 printf),
man 3 strerror, and
man 3 errno (not
man errno or
man 1 errno).
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 2 syscall(singular - this is a different page than
man 2 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
and in that new file replace the calls to
setuid with calls to
To do so, you will have to determine their ARM
architecture specific system call numbers by looking at the linux source file
at the generated header file
arch/arm/include/generated/uapi/asm/unistd-common.h in your kernel source directory.
Note that it is good programming practice, and also makes your code more portable
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. To do that, you may need include the header file
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, ssh into
shell.cec.wustl.edu using your WUSTL Key id and password,
then log into the Linux Lab cluster by running
qlogin -q all.q
Then, navigate into your linux source directory,
(which should be in
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
int argument, at the
include/linux/syscalls.h (make sure to put them before the closing
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.
arch/arm/tools/syscall.tblto tell the kernel to install our new system calls into the hardware's system call table.
Before modifying this file, make a copy of it called
Then, at the bottom of the original
syscall.tbl. file, create two new lines, following the scheme
used throughout the file. You will need to increment the value in the first column to allocate unique numbers for each of your new system calls. Furthermore, note the format of
the last two columns: the system call name, followed by the actual function
definition, which by convention is the name prefixed with
As the answer to this exercise, show the changes you added to the syscall.tbl file.
linuxsource directory (the one with subdirectories
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
(starting with step 6, after you had already unpacked the Linux distribution, set up the configuration, etc.)
and studio 2
(starting with step 7 after you had already successfully set up and booted into your Raspberry Pi).
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 your Pi.
You are now ready invoke your new system calls.
First, use sftp to retrieve
arch/arm/include/generated/uapi/asm/unistd-common.h from your kernel source directory.
Open this file on your Raspberry Pi.
You should notice two new
#define __NR_ statements at the end of the file
corresponding to your new system calls.
Next, copy this file to the appropriate include directory for GCC:
sudo cp unistd-common.h /usr/include/arm-linux-gnueabihf/asm
to a new file called
new_call.c, and in that new file replace the native calls that invoke the
setuid syscalls with the appropriate native calls to the new
functions you wrote (i.e., replace the syscall number for
__NR_ manifest constant for the new syscall that takes no
arguments and replace the syscall number for
with the one for the new syscall you added that takes one argument).
Please note that you probably will need to declare and initialize an
int variable in your
new_call.c code to pass to your syscall that takes one argument - the syscall interface may not correctly invoke your implementation of that syscall if you just pass in a (constant) numeric value.
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.
diff -up unistd.h unistd.h.MMDDYY > unistd.h.diff
For this studio, you will submit copies of the following files:
syscall.tblfiles you modified.
If you didn't keep 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).
syscallfunction), implement a third interface that directly utilizes the assembly routines necessary to invoke a system call. See the slides from today's lecture to see the steps that need to be taken to invoke a system call on ARM. You will need to make use of the gcc inline assembly function via the GNU
If you want to know more about the machine-level actions that occur
during a syscall, you can start with the detailed explanation given in the
manual to the native system call interface,
man 2 syscall.
The kernel code that contains the entry point for system calls is found
We will later discuss several methods that Linux provides for timekeeping and benchmarking program execution. One method, which is not specific to the Linux kernel, but is instead provided directly by the Raspberry Pi hardware platform, involves reading from the Cycle Count Register (CCNT) on the Raspberry Pi's ARM CPU. This register is not accessible directly by user-space programs. However, since it is accessible by the kernel, we can write a system call that allows a program to request the value from the kernel.
First, you will need to download a header that defines architecture-specific functions
for interracting with the Raspberry Pi's performance monitors.
Please download this file,
and place it in the
directory in your Linux kernel source tree.
As you did in Step (5) above,
to declare another function prototype,
this time taking a single
unsigned long long * argument.
We named the function
Like in Step (6), create a new file for your syscall's implementation in
You may use the provided file sys_read_ccnt.c.
Similar to Step (7), add your new file to the end of the object file list
As in Step (8), add your new system call to
Proceed with Exercise (9) above to compile your kernel with your new system calls, and install the kernel on your Raspberry Pi.
Proceed with Exercise (10) to write a program that implements your new system calls. In particular, call your system call twice to read two cycle counts in a row. As the answer to this exercise, please say how many cycles it takes to run the system call.
read_ccnt system call that you just implemented is unsafe.
It allows the caller to pass a pointer to any address,
to which the system call will then write the current cycle count.
Because an arbitrary address may be passed,
including one that may not be writable by the userspace process itself,
it provides a means for circumventing the kernel's memory protection mechanisms,
which can lead to memory corruption.
Modify the system call to avoid this issue.
You will need to use the
copy_to_user() function described on
page 76 of Robert Love's Linux Kernel Development.
Have the system call return the
-EFAULT error code if the copy fails.
As the answer to this exercise, please provide the code for your new implementation of the system call.
Page updated Sunday October 11, 2020, by Marion Sudvarg and Chris Gill.