1. Goals for this week:

  1. Compilation steps and practice with ARM64 assembly

  2. Disassemble binary executables with objdump and gdb.

  3. ALU condition codes (flags) in Lab 3.

  4. Explore real computer hardware.

2. Starting Point Code

Start by creating a week04 in your cs31/weeklylab subdirectory and copying over some files:

$ cd ~/cs31/weeklylab
$ mkdir week04
$ cd week04
$ pwd
/home/you/cs31/weeklylab/week04
$ cp ~kwebb/public/cs31/week04/* ./
$ ls
Makefile conditions.c simpleops.c

3. Using gcc to generate ARM64 assembly

When working on one of the ARM machines, you will likely find it easiest to keep two terminals open:

  1. A remote terminal (text-only) session that you can use to execute commands on an ARM CPU, and

  2. A local terminal on the machine that you’re physically sitting in front of.

This way, you can open code files in your favorite graphical editor (e.g., code, atom, etc.) in terminal #2 while executing commands and testing ARM programs in terminal #1.

Let’s try out gcc to build ARM64 assembly files and .o files and look at the results. Open up simpleops.c.

We are going to look at how to use gcc to create an assembly version of this file, and how to create a object .o file, and how to examine its contents. We are also going to look at how to used gdb to see the assembly code and to step through the execution of individual instructions.

Before we build this, we need to log in to a machine that has an ARM64 CPU! You can use the ssh command to remotely log into a different computer. We have a small cluster of ARM machines set up for CS 31. You can log into one using:

ssh arm.cs.swarthmore.edu

If you open up the Makefile you can see the rules for building .s, .o and executable files from simpleops.c.

$ gcc -S simpleops.c   # just runs the assembler to create a .s text file
$ gcc -c simpleops.s   # compiles to a relocatable object binary file (.o)
$ gcc -o simpleops simpleops.o  # creates an executable file

To see the machine code and assembly code mappings in the .o file:

$ objdump -d simpleops.o

If the assembly code you’re seeing looks nothing like the ARM assembly we saw in class, you probably ran the above commands on an x86-64 CPU. Make sure you run make, objdump, and run the compiled executables after logging on to one of the ARM machines with ssh.

Currently, our ARM machines are named after planets in the solar system (e.g., mercury, venus, etc.), so ensure that your terminal prompt is showing the name of a planet to confirm that you’re connected to an ARM machine!

You can compare this to the assembly file:

$ cat simpleops.s

4. gdb disassemble

Next, let’s try disassembling code using gdb. The gdb debugger supports debugging at the assembly code level by stepping through the execution of individual ARM64 instructions, examining register values, and disassembling functions. Today, we are just going to look at the disassemble functionality of gdb. We will revisit debugging at the assembly code level over the next few weeks.

$ gdb ./simpleops
(gdb) break main
(gdb) run

In gdb you can disassemble code using the disas command:

(gdb) disas main

I’ll show you a few other gdb commands for debugging at the assembly code level, but we will revisit this in later weeks.

(gdb) break main
(gdb) run
(gdb) disas
(gdb) ni           # execute next instruction
(gdb) disas
(gdb) ni           # execute a few more
...
(gdb) disas
(gdb) print $x0   # print out the value stored in CPU register x0

You can also dump the values of all the registers at once:

(gdb) info registers

Often, it’s helpful to track the values of registers as they change while executing instructions. You can use the display command to automatically print register values after each instruction. For example:

(gdb) display $x0
(gdb) display $x1

When done, enter quit to quit the gdb session:

(gdb) quit

5. Tips for Lab 3

5.1. ALU Flags

On the ALU lab, you need to set 5 flags (the condition codes). You can get an idea of how to set these by writing some code in C and reasoning about what the values of the flags should be after those operations. For example, consider how the flags should be set in the conditions.c program.

This program includes some example arithmetic and bit-wise operations, operations that your ALU will implement. We will use it to reason about the ALU flags part of Lab 3.

Recall the flags (or condition codes) are used encode state about the result of an ALU operation. For the ALU you are building in Logisim, the flags are defined as the following:

  • EQ: Is set (1), if the two operands are equal. Otherwise, it is clear (0).

  • ZF: Is set, if the computed result is zero.

  • SF: Is set, if the computed result’s most significant bit is set (i.e., it’s negative if interpreted as signed).

  • CF: Is set, if the computed result caused an overflow, when interpreted as an unsigned operation. (add and subtract only)

  • OF: Is set, if the computed result caused an overflow, when interpreted as a signed operation. (add and subtract only)

Compile conditions.c and run the program and look at some of the example operations and think about what the flag settings should be for these examples.

$ make
$ ./conditions

ADD
---
unsigned 2147483647 + 4 is  2147483651 (0x80000003)
  signed 2147483647 + 4 is -2147483645 (0x80000003)

SUB
---
unsigned 3 - 5 is  4294967294 (0xfffffffe)
  signed 3 - 5 is          -2 (0xfffffffe)
...

The first couple examples are ADD and SUB operations. Think about the value of each of the 5 flags for each of these example operations. In other words, which flags should be set (1) and which should be clear (0) as the result of adding 2147483647 + 4? And which should be set and which should be clear as the result of 3 - 5?

Next, consider the OR and AND examples, and how the flags should be set for these examples.

When implementing the flags in your ALU, you can return to this program and try different examples to help you think about how the flags should be set.

5.2. Logisim Tips

  • You can often change the orientation of circuits in the circuit menu and sometimes some pin locations for easier wiring.

  • If connections seem to not be working properly check if a wire is shorting pins by being connected along an edge of a component. Move the component and look underneath it — if all looks good you can hit Undo and put the component back. If things look bad, try deleting the component, fix the wiring and then re-place the component.

  • Wires can occasionally pass underneath components and create invisible connections. As above, move the component to check for this if you experience problems.

6. Computer Teardown

Log out. Form a group of 4 people (including yourself). Your group is assigned a single computer that you can take apart.

The goal is to find as many parts of a computer as you can. We have tools available to remove parts from your computer, and here are a few links that may be helpful:

Try to identify some of the following:

  • the processor

  • RAM memory card(s)

  • the backplane

  • the chipset, PCI and Memory buses

  • expansion slots

  • the hard drive (what’s inside?)

  • the network interface card (NIC)

  • the graphics card

  • where do different ports go?

  • …​ then see what else you can find

Before leaving lab, please clean-up all spare parts, screws, etc. that you have removed by putting them in the boxes. You do not need to put the cases in the boxes, just all loose parts. Cases can be left on the desks. CPU thermal glue is toxic, so just to be safe I recommend washing your hands after lab today.

7. Handy References