CS140 Lab 8 - Machine Level Representation of Programs

Objective

  • Experiment machine level representations of programs.
  • Use gcc, gdb, and objdump tools

Background

This material is from Chapter 3 in CS:APP textbook.

Required Tasks

    Experiment with code examples



  1. Log into your Linux account on one of the CS machines.
  2. Create a directory for your lab08 experiments.
  3. Create a text or word document for your notes (turn in at the end!)
  4. Create a C program code.c containing just the following code:
    int accum = 0;
    
    int sum(int x, int y)
    {
        int t = x + y;
        accum += t;
        return t;
    }
    
  5. Generate assembly language code from this program using the C compiler. The -S option means to only generate assembly code and stop. We will also use the -Og (capital O, not zero) to tell the compiler to use only basic optimizations.

    gcc -m64 -Og -S code.c
  6. Examine the resulting code.s to and try to figure out what it does.
  7. Repeat this experiment, leaving out the -m64 (or try with -m32 if 64-bit is your default).
  8. Repeat this experiment, leaving out the -Og. Notice that the generated code is more complex (but potentially more efficient). Try -O0, -O1, -O2 instead of -Og. See the bottom of this lab for an explanation.
  9. Use the -c compiler option to both compile and assemble the code:

    gcc -m64 -Og -c code.c
  10. Use objdump to disassemble the resulting object code:

    objdump -d code.o

  11. The output of disassembly is the actual bytes in the object code in the left column, and the equivalent assembly language in the right column.
  12. Type objdump alone to see the help. Experiment with other options that might be useful.
  13. We can see the exact byte representation of our sum function using the debugger:

    gdb code.o
    (gdb) x/17xb sum
    (gdb) quit

    x/17xb means to examine 17 hex-formatted bytes.

    Executable Code


    Generating actual executable code requires running a linker.

  14. Add the following code to a file named main.c

    int main()
    {
        return sum(1, 3);
    }
    

  15. Generated the executable program as follows:

    gcc -m64 -Og -o prog code.o main.c
  16. What size in bytes is the resulting file? Why?
  17. Disassembly the resulting file:

    objdump -d prog

    A related example


    Look at the resulting assembly code for the following function:

    int simple(int *xp, int y) 
    {
        int t = *xp + y;
        *xp = t;
        return t;
    }
    

    ATT vs Intel Assembly Code Formats


    There are some differences in AT&T formatting and Intel formatting, even for the same assembly language. To see the Intel assembly for the original code.c:

    gcc -m64 -Og -S -masm=intel code.c

  18. What do you notice that's different?

Experiment with Memory and Arithmatic Instructions



Keep detailed notes of each experiment!
long test(long x, long y) {

// body
}



return:

x + y
x - y
x >> y
x * y
x * 13
x | y
~x

Trickier ones:
!x
x || y,  x && y

Try x >> y with unsigned long for return value and arguments
Try a couple of the above experiments with int, short, or char datatypes instead of long


BE CAREFUL - the compiler is too smart!!
e.g.
   x++
   y++ 
   return x+y;


Questions:
  - What register holds the first argument?
  - What register holds the second argument?
  - What register holds the third argument?
  - What register holds 4, 5, 6 arguments?
  - Try a method with 8 or 9 arguments - now what happens?

  - What register holds the return value?



Memory:
Use pointers to load something from memory.
Use pointers to save something to memory.
Create an array and access its elements
Create a struct and access its elements



If you have time:

do while loop
while loop
for loop
if without an else
if/else

Appendix: gcc Optimization flags:

From the man page:

  • -O (Same as -O1)
  • -O0 (do no optimization, the default if no optimization level is specified)
  • -O1 (optimize minimally)
  • -O2 (optimize more)
  • -O3 (optimize even more)
  • -Ofast (optimize very aggressively to the point of breaking standard compliance)
  • -Og (Optimize debugging experience. -Og enables optimizations that do not interfere with debugging. It should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience.)
  • -Os (Optimize for size. -Os enables all -O2 optimizations that do not typically increase code size. It also performs further optimizations designed to reduce code size. -Os disables the following optimization flags: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-and-partition -fprefetch-loop-arrays -ftree-vect-loop-version)

Check out



When you are done with the lab exercises, turn in your notes on Gradescope.