Assembler: Learning to Read
Learning Objectives
- Convert C programs into assembly
- Use
gdb
andobjdump
to examine the assembly underlying a C function/program (Note:objdump
is a GNU tool, so you need to usegcc
instead ofclang
to compile files if you want to useobjdump
. - Read simple assembly
- Become comfortable with the typeless nature of assembly
Getting Started
Pull today's exercise code from the cs61-exercises
repository. We
strongly encourage you to use the appliance today -- if you use your
laptop, you are likely to get assembly code that looks quite different
from what we expect and the observations we ask you to make and
questions we ask you may be difficult to answer.
Varieties of functions that do nothing
In the asm-intro video, we introduced the function func
that took no
parameters, did nothing, and returned nothing. That function is in the
1.c
file in today's directory. Create a few variants of this function
that take parameters and/or return a value and compare the assembly
produced for the different variants (we recommend you name these
variants 1a.c, 1b.c, etc (for consistency with some other examples we'll
be doing). You can convert c
files to assembly without modifying the
Makefile just by typing make 1a.s
. Alternatively you can add these to
the all
target in the Makefile and just type make
.
Include at least one variant that returns 0. Notice the assembly
produced. Can you think of another way the compiler might have chosen to
implement the return value? Why do you suppose the compiler chose the
instruction it did? Build a .o
file and use objump to examine the code
produced -- does that change your hypothesis.
The Sum Function
In the asm-operators video, we introduced the simple sum function, which
you will find in the file 2.c
. In addition, you will find three other
files 2a.c, 2b.c, and 2c.c. Note that each of these differs slightly
from 2.c in C, but produces identical assembly code. These examples
highlight the difference between the C abstract machine, in which the
programs are different, and the actual machine, where they are executed
identically. Your task is to come up with as many other variants of the
sum function that will produce the same assembly. Experiment with
different parameter types and different ways of instructing the compiler
to produce code that adds two numbers together. Spend at least five
minutes trying to come up with some variants, but no more than 10
minutes.
Next, examine 3.c, 4.c, 5.c, and the s files they produce. Subtraction (3.c) and Multiplication (4.c) are pretty predictable, but the assembly for 5.c has a couple of interesting features. Use google or any other resource to figure out the answer to two questions:
- What does cltd do?
- Why does idivl take only one operand?
Operands of Different Sizes
Now create versions of the functions in 2.c-5.c with a variety of different types of different sizes, e.g., char, unsigned char, short, unsigned short). Pay particular attention to the following issues:
- How are signed and unsigned values treated differently?
- How are operands of different sizes treated differently?
Bit Operations
In the video on operators you saw that there are logical operators as well as arithmetic ones. Let's experiment with some other related operations.
Examine the program 6.c. What is it doing?
Now build and examine the file 6.s. There are two interesting things about it:
- Note that although the second parameter is unsigned, the assembly code moves only a single byte in %cl. Why?
- Note the
shll
instruction. What do you suppose it does?
Next take a look at 7.c -- can you predict what code it will generate?
Build and examine 7.s to see if you were right. Why do you suppose I
implemented mul4
and not just mul2
? If you can't figure it out,
write mul2
and see what happens.
Next take a look at 8.c. Build and examine 8.s. Is it what you expect? Predict any difference you might see if you change the parameter to an int instead of an unsigned. Then test it.
What happened? (Use Google or any other resource easily available to explain any instructions you don't understand.)
Write a function that just divides its parameter by 4. What do you expect to see in the assembly? Check your answer.
Moving things around in Memory
Write a simple C function, swap.c, that takes two pointers to integers and swaps their contents. Then produce the assembler output for that function. Try reading the assembly output and see if you can figure out what it's doing. Use scrap paper or a whiteboard to draw what's happening.
Examine what happens if you change the parameters and return values to be pointers to differently sized things. What happens?
Summing Up
- You can read assembly for simple programs!
- You have probably figured out how/where parameters are passed to functions
- You know how assembly instructions express arithmetic and logical and shift operators
- You have discovered just how smart the compiler is some times!
Please complete this short survey.