We’ll begin with simple assignment of integer types. In particular, we consider:
= 3;
ia = 3;
uia = 3;
ca = 3;
uca = 3;
la = 3;
ula
= 4;
ib = 4;
uib = 4;
cb = 4;
ucb = 4;
lb = 4; ulb
The equivalent assembly is shown in the following table:
C | .s file | gdb |
---|---|---|
ia = 3; |
||
movl $3, -8(%rbp) |
movl $0x3,-0x8(%rbp) |
|
uia = 3; |
||
movl $3, -20(%rbp) |
movl $0x3,-0x14(%rbp) |
|
ca = 3; |
||
movb $3, -29(%rbp) |
movb $0x3,-0x1d(%rbp) |
|
uca = 3; |
||
movb $3, -32(%rbp) |
movb $0x3,-0x20(%rbp) |
|
la = 3; |
||
movq $3, -48(%rbp) |
movq $0x3,-0x30(%rbp) |
|
ula = 3; |
||
movq $3, -72(%rbp) |
movq $0x3,-0x48(%rbp) |
|
ib = 4; |
||
movl $4, -12(%rbp) |
movl $0x4,-0xc(%rbp) |
|
uib = 4; |
||
movl $4, -24(%rbp) |
movl $0x4,-0x18(%rbp) |
|
cb = 4; |
||
movb $4, -30(%rbp) |
movb $0x4,-0x1e(%rbp) |
|
ucb = 4; |
||
movb $4, -33(%rbp) |
movb $0x4,-0x21(%rbp) |
|
lb = 4; |
||
movq $4, -56(%rbp) |
movq $0x4,-0x38(%rbp) |
|
ulb = 4; |
||
movq $4, -80(%rbp) |
movq $0x4,-0x50(%rbp) |
Here we see that an integer assignment is a simple movl
(for 4-byte values), movb
(for 1-byte values), or
movq
(for 8-byte values), regardless of whether the
variable is signed or unsigned. The literal value is preceded by a
$
, and registers are preceded by %
. The syntax
-8(%rbp)
means offset by -8 bytes from the value of the
register rbp
. The literals and offsets are written (by
default) in decimal in the assembly file, but hexadecimal in gdb.
Otherwise, there is no difference.
The equivalent assembly is shown in the following table:
C | .s file | gdb |
---|---|---|
ia = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
str r3, [fp, #-8] |
str r3, [r11, #-8] |
|
uia = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
str r3, [fp, #-12] |
str r3, [r11, #-12] |
|
ca = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
strb r3, [fp, #-13] |
strb r3, [r11, #-13] |
|
uca = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
strb r3, [fp, #-14] |
strb r3, [r11, #-14] |
|
la = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
str r3, [fp, #-20] |
str r3, [r11, #-20] |
|
ula = 3; |
||
mov r3, #3 |
mov r3, #3 |
|
str r3, [fp, #-24] |
str r3, [r11, #-24] |
|
ib = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
str r3, [fp, #-28] |
str r3, [r11, #-28] |
|
uib = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
str r3, [fp, #-32] |
str r3, [r11, #-32] |
|
cb = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
strb r3, [fp, #-33] |
strb r3, [r11, #-33] |
|
ucb = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
strb r3, [fp, #-34] |
strb r3, [r11, #-34] |
|
lb = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
str r3, [fp, #-40] |
str r3, [r11, #-40] |
|
ulb = 4; |
||
mov r3, #4 |
mov r3, #4 |
|
str r3, [fp, #-44] |
str r3, [r11, #-44] |
The instructions on the ARM platform are more complex. Where before
we had a direct write to the variables’ stack locations, we now begin
with a mov
instruction to write the value to register
r3
. We then store this register on the stack using
str
or strb
(for 1-byte values). The frame
pointer is given by register fp
, and we use the syntax
[fp, #-8]
to indicate an offset of -8 bytes from the value
of the register fp
.
In gdb, we see the same pattern of mov
and
str
(or strb
), again with arguments in the
same order. The register fp
from the .s
file
is now shown as r11
instead, though they refer to the same
thing. gdb will recognize fp
as the name of a register, but
it is identical to r11
. You can try this yourself with the
command info reg fp r11
.