CS 208 s21 — Learning Block #11
Table of Contents
1 Review
Take some time to practice translating assembly to C. There's no single correct C code for the given assembly code—multiple valid translations exist.
Exercise 1: long f(long x, long y)
1
f(long, long): subq %rsi, %rdi jne .L3 movq %rdi, %rax ret .L3: movq %rsi, %rax ret
Exercise 2: long f(long x, long y)
2
f(long, long): cmpq $2, %rdi setle %dl cmpq %rsi, %rdi sete %al testb %al, %dl je .L3 movl $1, %eax ret .L3: movl $2, %eax ret
Exercise 3: long f(long *p)
3
f(long *p): testq %rdi, %rdi je .L4 movq (%rdi), %rax leaq -10(%rax), %rdx testq %rdx, %rdx jle .L3 addq %rax, %rax ret .L3: addq $1, %rax ret .L4: movl $0, %eax ret
2 Practice
Translate this assembly to C code:4
.LC0: .string "Hello %d" main: pushq %rbx movl $0, %ebx jmp .L2 .L3: movl %ebx, %esi movl $.LC0, %edi movl $0, %eax call printf addl $1, %ebx .L2: cmpl $9, %ebx jle .L3 movl $0, %eax popq %rbx ret
CSPP practice problems 3.24 (p. 224), 3.26 (p. 228), and 3.30 (p. 236)
3 Background for Lab 2
See the slides here: ./lab2-background-slides.pdf (includes a walkthrough of the activity below)
4 gdb
activity
- Get started with these commands:
wget http://cs.carleton.edu/faculty/awb/cs208/s21/topics/gdb-activity.tar tar xvf gdb-activity.tar cd gdb-activity make
- Try running the program with
./gdb-activity
, what happens? - Open
gdb-activity.c
#include <string.h> #include <stdlib.h> #include <stdio.h> int compare(int a, int b); int main(int argc, char** argv) { int a, b, n; char input[100]; printf("enter good args: "); if (fgets(input, 100, stdin) == NULL) { printf("I said good args\n"); } n = sscanf(input, "%d %d", &a, &b); if (n == 2 && compare(a,b) == 1) { printf("good args!\n"); } else { printf("bad args, try harder!\n"); } return 0; }
Observations:
- four functions are called:
printf
,fgets
,sscanf
, andcompare
- the first three are C library functions (since they aren't declared anywhere, they must come from the
#include
of library headers) - look each library function up in the terminal with
man 3 FUNCTION
, or consult cplusplus.com/FUNCTION (the latter is often easier to understand)
- the first three are C library functions (since they aren't declared anywhere, they must come from the
- to get the program to print "good args!", we need
fgets
to return something other thanNULL
, havesscanf
return 2, and havecompare(a, b)
return 1fgets
will read from the command line (stdin
) and store the string ininput
, up to 100 characters- only returns
NULL
on failure, so we probably don't have to worry about that
- only returns
sscanf
is a super useful function: it parses a string (the first parameter) according to a format string given by the second parameter%d
is the format specifier for an integer, so thissscanf
call will parseinput
as two integers separated by a space- the parsed items (i.e., each
%d
) will be written to the corresponding pointers provided as arguments after the format string- so the first integer in
input
will be stored ina
and the second will be stored inb
- so the first integer in
compare
is actually implemented in raw assembly ingdb-activity.s
- Lets use
gdb
to get a sense for how everything is fitting together. (This tutorial video goes over usinggdb
if you want to review.) - Running
disas compare
from withingdb
gives
0x00000000004006d7 <+0>: push %rbx 0x00000000004006d8 <+1>: mov %rdi,%rbx 0x00000000004006db <+4>: add $0x5,%rbx 0x00000000004006df <+8>: add %rsi,%rbx 0x00000000004006e2 <+11>: cmp $0xd0,%rbx 0x00000000004006e9 <+18>: sete %al 0x00000000004006ec <+21>: movzbq %al,%rax 0x00000000004006f0 <+25>: pop %rbx 0x00000000004006f1 <+26>: retq
- We can reverse engineer the C code to be something like
int compare(int a, int b) { return a + b + 5 == 0xd0; // we add %rdi, %rsi, and 5 together in %rbx and then compare it to $0xd0 // sete writes 1 to the given register if the cmp indicates the operands are equal // since this is the return value, and we want compare to return 1, // we should choose inputs to make these equal }
Footnotes:
1
long f(long x, long y) { long z = x - y; if (z == 0) { return z; } return y; }
f(long, long): subq %rsi, %rdi // compute x - y, ok to overwrite x since we don't use it again jne .L3 // jump to L3 when %rdi != %rsi (i.e., x - y != 0) movq %rdi, %rax // copy z to the return value ret .L3: movq %rsi, %rax // copy y to the return value ret
2
long f(long x, long y) { if (x < 3 && x == y) { return 1; } else { return 2; } }
f(long, long): cmpq $2, %rdi // perform x - 2, set condition codes setle %dl // write 1 to the lowest byte of %rdx when x <= 2 (i.e., %dl contains 1 when x < 3) cmpq %rsi, %rdi // perform x - y, set condition codes sete %al // write 1 to the lowest byte of %rax when x == y testb %al, %dl // perform %al & %dl, set condition codes je .L3 // jump to L3 when %al & %dl == 0 (i.e., jump unless both previous set instructions // wrote 1, meaning x < 3 and x == y) movl $1, %eax // make return value 1 ret .L3: movl $2, %eax // make return value 2 ret
3
#include <stdlib.h> // to provide NULL long f(long *p) { if (p == NULL) { return 0; } if (*p - 10 > 0) { return *p + *p; } else { return *p + 1; } }
f(long *p): testq %rdi, %rdi // perform p & p, set condition codes je .L4 // jump to L4 if p & p == 0 (i.e, jump if p is NULL) movq (%rdi), %rax // put *p in the return value leaq -10(%rax), %rdx // put *p - 10 in %rdx (remember lea uses the value in the register, not memory testq %rdx, %rdx // perform (*p - 10) & (*p - 10), set condition codes jle .L3 // jump to L3 if (*p - 10) & (*p - 10) <= 0 (i.e., don't jump if *p - 10 > 0) addq %rax, %rax // return value is now *p + *p ret .L3: addq $1, %rax // add 1 to return value ret .L4: movl $0, %eax // set return value to 0 ret
4
#include <stdio.h> int main() { for (int i = 0; i < 10; i++) { printf("Hello %d", i); } }