By the time you have completed this lab, you should:
Before completing this lab, you should:
If you've forgotten how to create the directory, consult steps 1 and 2 from lab01.
The files for this weeks lab can be found here:
And here:
~pconrad/public_html/cs16/09F/labs/lab07a/files/*
You can use the same techniques described in lab04 to copy those into your ~/cs16/lab06 directory. Consult the instructions for lab04 if you don't remember how to do this.
So far this quarter, when we've worked with a C program such as the cmdlinedemo.c program in this week's files, we've used the make command to create an "executable file", like this. (Try each of these commands for yourself as you follow along.)
-bash-3.2$ ls
cmdlinedemo.c demoTypes.c
-bash-3.2$ make cmdlinedemo
cc cmdlinedemo.c -o cmdlinedemo
-bash-3.2$
After doing this, as we know, we can use the command ./cmdlinedemo to run the program, with or without command line arguments:
-bash-3.2$ ./cmdlinedemo
argc=1
argv[0]=./cmdlinedemo
-bash-3.2$ ./cmdlinedemo foo bar
argc=3
argv[0]=./cmdlinedemo
argv[1]=foo
argv[2]=bar
-bash-3.2$
This tells the operating system: "look in the current directory (the ./ stands for current directory) for a file called cmdlinedemo and start executing the instructions you find inside.
Now we want to look a little more closely at what is happening here.
What you may already realize, but we want to highlight this week, is that the reason this works is because there is an executable file called cmdlinedemo in your current directory, as we can show with the ls command:
-bash-3.2$ ls
cmdlinedemo cmdlinedemo.c root.c
-bash-3.2$
If we use the ls -l
command instead (-l
stands for the "long" form of the directory listing), we see more information about the files:
-bash-3.2$ ls -l
total 16
-rwxr-xr-x 1 pconrad faculty 4955 2009-11-12 07:49 cmdlinedemo
-rwxr-xr-x 1 pconrad faculty 459 2009-11-12 07:49 cmdlinedemo.c
-rwxr--r-- 1 pconrad faculty 430 2009-11-12 07:49 root.c
-bash-3.2$
What we want to focus on here is the leftmost x that appears in the row for the file cmdlinedemo, namely the one underlined and bold in this listing below:
-bash-3.2$ ls -l
total 16
-rwxr-xr-x 1 pconrad faculty 4955 2009-11-12 07:49 cmdlinedemo
-rwxr-xr-x 1 pconrad faculty 459 2009-11-12 07:49 cmdlinedemo.c
-rwxr--r-- 1 pconrad faculty 430 2009-11-12 07:49 root.c
-bash-3.2$
That particular x indicates that the owner of the file "has permission to execute the file", i.e. to ask the Unix operating system to start carrying out the instructions in the file.
In this case, those instructions happen to "machine language instructions in binary". We can see that if we use the special command file cmdlinedemo, which tells what kind of file cmdlinedemo is:
-bash-3.2$ file cmdlinedemo
cmdlinedemo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 2.6.9, not stripped
-bash-3.2$
The output is long and detailed, but focus on this part that's underlined and bold in the listing below:
-bash-3.2$ file cmdlinedemo
cmdlinedemo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 2.6.9, not stripped
-bash-3.2$
That part of the output indicates that the file is in machine language that is targeted for the Intel 80386 family of processors. Contrast that with the output of the command file cmdlinedemo.c:
-bash-3.2$ file cmdlinedemo.c
cmdlinedemo.c: ASCII C++ program text
-bash-3.2$
Here we see that the file is ASCII text of a C program (which we already knew—but this shows that the "file" command is able to distinguish between the two.)
Now comes the fun part.
We are going to show that even though the file cmdlinedemo contains executable instructions, we can take away our permission to "execute" the file—i.e. make it non-executable—by changing the permission modes with the chmod command.
Then, we can bring back that permission again.
To proceed, change the permissions on the file cmdlinedemo to rw-r--r--, which corresponds to 644, like this:
-bash-3.2$ ls -l cmdlinedemo
-rwxr-xr-x 1 pconrad faculty 4955 2009-11-12 07:49 cmdlinedemo
-bash-3.2$ chmod 644 cmdlinedemo
-bash-3.2$ ls -l cmdlinedemo
-rw-r--r-- 1 pconrad faculty 4955 2009-11-12 07:49 cmdlinedemo
-bash-3.2$
Then, try to execute the file. Notice that you get an error:
-bash-3.2$ ./cmdlinedemo
-bash: ./cmdlinedemo: Permission denied
-bash-3.2$
Then, try to change it back to rwxr-xr-x (i.e. 755) as shown here:
-bash-3.2$ chmod 755 cmdlinedemo
-bash-3.2$
Try running it again. Success!
-bash-3.2$ ./cmdlinedemo
argc=1
argv[0]=./cmdlinedemo
-bash-3.2$
Now, you see that even though a file might be in the executable format—that's not enough for us to be able to run the file:
As it turns out, we can set the execute permission on certain files other than those that contain binary machine language instructions. One example is something called a shell script.
As an example of when we might use a shell script: suppose we have a file that uses the sqrt function—this weeks file contain a little example program called root.c that prints the square root of its command line argument.
As you may recall, if we try to compile a program that uses math functions such as sqrt, sin, cos, etc. with our usual trick of just typing "make progname", we run into a problem, demonstrated here with trying that on root.c:
-bash-3.2$ make root
cc root.c -o root
/tmp/ccwG7MLC.o: In function `main':
root.c:(.text+0x6e): undefined reference to `sqrt'
collect2: ld returned 1 exit status
make: *** [root] Error 1
-bash-3.2$
The problem is that the math library has to be specifically requested, so there is a long version of the compile step:
-bash-3.2$ gcc -lm root.c -o root
-bash-3.2$ ./root
Usage: ./root floating-pt-number
-bash-3.2$ ./root 4.0
2.000000
-bash-3.2$
Suppose we want to avoid typing this every time. We can create a special file called a shell script.
Type the following command to start a new file in emacs called makeroot
-bash-3.2$ emacs makeroot
Inside the file makeroot, put the following:
#!/bin/sh -vThe first line,
cc -lm root.c -o root
#!/bin/sh -v
is a special line that indicates "this file is a shell script". The -v part indicates that you want each line in the script to be echoed as it is executed.
The remaining lines in the file are unix commands—just like the ones you type in at the command line. In this case, we have just one more line—the command we want to run.
So, now, all that remains is to try to run our shell script. We don't have to compile it—shell scripts are a kind of file that can be executed by Unix without having to be turned into machine languages instructions first. We run them just like programs—by typing, for example
./makeroot
There is just one catch, as we find out when we try this:
-bash-3.2$ ./makeroot
-bash: ./makeroot: Permission denied
-bash-3.2$
The problem is that makeroot must have the execute permission set! If we do an ls -l, we can see that it doesn't have that set yet:
-bash-3.2$ ls -l makeroot
-rw-r--r-- 1 pconrad faculty 35 2009-11-12 08:00 makeroot
-bash-3.2$
So, all that is needed is to use the chmod command to set that—a permission mode of 700 should do the trick:
-bash-3.2$ chmod 700 makeroot
-bash-3.2$ ls -l
total 28
-rwxr-xr-x 1 pconrad faculty 4955 2009-11-12 07:49 cmdlinedemo
-rwxr-xr-x 1 pconrad faculty 459 2009-11-12 07:49 cmdlinedemo.c
-rwx------ 1 pconrad faculty 35 2009-11-12 08:00 makeroot
-rwxr-xr-x 1 pconrad faculty 5232 2009-11-12 07:58 root
-rwxr--r-- 1 pconrad faculty 430 2009-11-12 07:49 root.c
-bash-3.2$ ./makeroot
#!/bin/sh -v
cc -lm root.c -o root
-bash-3.2$
So, we now see that a file that isn't an "executable file"—in the sense that it doesn't contain machine language instructions—can still be executable—in the sense that it can have the execute permission set.
Of course, it isn't as simple as just setting the execute permission. Some files, like C program files, can't be directly executed by the Unix operating system. For example, if we try to run a C program directly, we'll get an error:
-bash-3.2$ ./root.c
-bash: ./root.c: Permission denied
-bash-3.2$
We can try to get around this by setting the execute permission, but that doesn't seem to help. Try it:
-bash-3.2$ chmod 755 root.c
-bash-3.2$ ./root.c
./root.c: line 1: //: is a directory
./root.c: line 2: //: is a directory
./root.c: line 8: syntax error near unexpected token `('
./root.c: line 8: `int main(int argc, char *argv[])'
-bash-3.2$
What is wrong here is that Unix is trying to interpret the C program language file as if it were a shell script—i.e. a sequence of Unix commands. That obviously is not going to get us very far.
If only there were a way to run a .c file directly, without having to compile.
Ah, but there is! Ch to the rescue...
It turns out that we can use the Ch program to execute C programs directly—as long as the execute permission is set on that file.
Try this:
-bash-3.2$ ch
Ch
Professional edition, version 6.1.0.13751
(C) Copyright 2001-2009 SoftIntegration, Inc.
http://www.softintegration.com
/cs/faculty/pconrad/cs16/lab07a> _prompt="Ch> "
Ch>
Ch> ls -l root.c
-rw-r--r-- 1 pconrad faculty 430 2009-11-12 07:49 root.c
Ch> chmod 755 root.c
Ch> ls -l root.c
-rwxr-xr-x 1 pconrad faculty 430 2009-11-12 07:49 root.c
Ch> ./root.c
Usage: /cs/faculty/pconrad/cs16/lab07a/root.c floating-pt-number
Ch> ./root.c 4.0
2.000000
Ch> ./root.c 0.25
0.500000
Ch> ./root.c -9.0
nan
Ch> ./root.c 9.0
3.000000
Ch> exit
-bash-3.2$
As you can see, we can use Unix commands such as ls -l
and chmod
from inside Ch. And, if we set the execute permission on our C source files (e.g. cmdlinedemo.c and root.c) we can execute them directly inside of Ch, as if they were executable files.
This is because the Ch program is a special kind of "Unix Shell"—one that understands the C programming language directly. Hence,
There are several reasons you might want to run C programs directly inside of Ch instead of compiling them:
-lm
What are the downsides of doing this?
The main drawback of using Ch for running complete programs—rather than the traditional compiler—is the possibility of getting confused about what is legal in C, and what is not.
Another drawback is that it is cumbersome to share pieces of code among different C programs when using Ch. So, this way of using Ch is mainly useful for small standalone programs, like the ones we've written up until now in the course.
We think you may find it helpful to know that Ch can be used for running complete programs—not just for little bits of code. But, whether or not you use it for that purpose is entirely up to you.
This lab has highlighted the point that when we speak of an executable file, there are two things we might mean:
As we've seen these two are independent of each other:
Before the next midterm go back over these steps and be prepared to give an example of each of these situations, drawing on your experiences from this lab.
Copyright 2009, Phillip T. Conrad, CS Dept, UC Santa Barbara. Permission to copy for non-commercial, non-profit, educational purposes granted, provided appropriate credit is given; all other rights reserved.