Given:
struct Foo {
short a;
char b;
int c;
};
struct Foo Bar = { 0xaaaa, 0xbb, 0xcccccccc };
Here is one way of defining and accessing the struct:
.global main // 1
.text // 2
.align 2 // 3
// 4
main: // 5
str x30, [sp, 16]! // 6
// 7
ldr x0, =fmt // 8
ldr x1, =Bar // 9
ldrh w2, [x1] // 10
ldrb w3, [x1, 2] // 11
ldr w4, [x1, 4] // 12
bl printf // 13
// 14
ldr x30, [sp], 16 // 15
mov w0, wzr // 16
ret // 17
// 18
.data // 19
// 20
fmt: .asciz "%p a: 0x%x b: 0x%x c: 0x%x\n" // 21
It would be understandable if you don't see where the struct
is being
defined. That's because it isn't. Rather, the implied +0 on line 10
and
the 2 and 4 on lines 11
and 12
are the hard coded offsets into the
struct
.
Here is a second way to define a struct
.
.global main // 1
.text // 2
.align 2 // 3
// 4
.equ foo_a, 0 # like #define // 5
.equ foo_b, 2 # like #define // 6
.equ foo_c, 4 # like #define // 7
// 8
main: // 9
str x30, [sp, 16]! // 10
// 11
ldr x0, =fmt // 12
ldr x1, =Bar // 13
ldrh w2, [x1, foo_a] // 14
ldrb w3, [x1, foo_b] // 15
ldr w4, [x1, foo_c] // 16
bl printf // 17
// 18
ldr x30, [sp], 16 // 19
mov w0, wzr // 20
ret // 21
// 22
.data // 23
// 24
fmt: .asciz "%p a: 0x%x b: 0x%x c: 0x%x\n" // 25
This method uses .equ
to make the offsets into symbolic constants.
This is just like using #define
in C and C++. That is, the above is
equivalent to the following in C or C++:
#define foo_a 0
#define foo_b 2
#define foo_c 4
Finally, here is a third way of defining structs
.
.global main // 1
.text // 2
.align 2 // 3
// 4
main: // 5
str x30, [sp, 16]! // 6
// 7
ldr x0, =fmt // 8
ldr x1, =Bar // 9
ldrh w2, [x1, Foo.a] // 10
ldrb w3, [x1, Foo.b] // 11
ldr w4, [x1, Foo.c] // 12
bl printf // 13
// 14
ldr x30, [sp], 16 // 15
mov w0, wzr // 16
ret // 17
// 18
.section Foo // 19
.struct 0 // a starts at 0 and goes for 2 // 20
Foo.a: .struct Foo.a + 2 // b starts at 2 and goes for 2 // 21
Foo.b: .struct Foo.b + 2 // c starts at 4 // 22
Foo.c: // 23
// 24
.data // 25
// 26
fmt: .asciz "%p a: 0x%x b: 0x%x c: 0x%x\n" // 27
This method has a substantial benefit over the previous methods. Imagine
you need to insert a new field between Foo.a
and Foo.b
. Simply do so.
If you're using this third method, which is based on relative offsets, the
assembler will do the work of adjusting the following offsets for you.