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
#includeof 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
fgetsto return something other thanNULL, havesscanfreturn 2, and havecompare(a, b)return 1fgetswill read from the command line (stdin) and store the string ininput, up to 100 characters- only returns
NULLon failure, so we probably don't have to worry about that
- only returns
sscanfis a super useful function: it parses a string (the first parameter) according to a format string given by the second parameter%dis the format specifier for an integer, so thissscanfcall will parseinputas 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
inputwill be stored inaand the second will be stored inb
- so the first integer in
compareis actually implemented in raw assembly ingdb-activity.s
- Lets use
gdbto get a sense for how everything is fitting together. (This tutorial video goes over usinggdbif you want to review.) - Running
disas comparefrom withingdbgives
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); } }