Think In Geek

In geek we trust

ARM assembler in Raspberry Pi – Chapter 20

Today we will see how to make indirect calls.

Labels

One of the distinguishing features of assemblers is the shortage of symbolic information. The only symbolic support available at this (low) level are labels. We already know that labels are just addresses to the memory of the program (both data and code).

When we define a function in assembler, we define a label for it.

fun: /* label 'fun' */
  push {r4, r5}
  ...
  pop {r4, r5}
  bx lr

Later (or before, assemblers usually do not care) we use the label. So a call like

  bl fun

Is saying to the assembler, I’m using fun here, but you have to put the appropiate address there when generating machine code, ok?.

In reality, calling a function is usually much more involved but at the end there is a label that brings us to the function.

Our first indirect call

What if rather than using the label of a function, we were able to keep the addres of a function (or several of them) somewhere and call a function indirectly? Let’s try that. First, we will start with a basic Hello world that uses a label. We will call this a direct call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
.data     /* data section */
.align 4  /* ensure the next label is 4-byte aligned */
message: .asciz "Hello world\n"
 
.text     /* text section (= code) */
 
.align 4  /* ensure the next label is 4-byte aligned */
say_hello:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    /* Prepare the call to printf */
    ldr r0, addr_of_message  /* r0 ← &message */
    bl printf                /* call printf */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */
 
.align 4  /* ensure the next label is 4-byte aligned */
addr_of_message: .word message
 
.globl main /* state that 'main' label is global */
.align 4  /* ensure the next label is 4-byte aligned */
main:
    push {r4, lr}            /* keep lr because we call say_hello, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    bl say_hello             /* call say_hello, directly, using the label */
 
    mov r0, #0               /* return from the program, set error code */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller (the system) */

Now let’s add some storage in the data section to keep the address of say_hello.

.data     /* data section */
...
.align 4  /* ensure the next label is 4-byte aligned */
ptr_of_fun: .word 0   /* we set its initial value zero */

Now we will add a new function make_indirect_call that does the indirect call using the value stored in ptr_of_fun.

.align 4
make_indirect_call:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    ldr r0, addr_ptr_of_fun  /* r0 ← &ptr_of_fun */
    ldr r0, [r0]             /* r0 ← *r0 */
    blx r0                   /* indirect call to r0 */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */
 
addr_ptr_of_fun: .word ptr_of_fun

Doing an indirect call is done using the instruction blx. It behaves like bl but expects a register rather than a label.

Yoy may be wondering whether we could have used bx rather than blx. We cannot. The instruction bx does not set the lr register to the next instruction, like bl and blx do. Thus, we would call the function but it would not be able to return: it would jump back to the wrong place! (try to think which one).

Now in the main we will keep the address of say_hello in ptr_of_fun and call make_indirect_call.

main:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
 
    ldr r1, addr_say_hello   /* r1 ← &say_hello */
    ldr r0, addr_ptr_of_fun  /* r0 ← &addr_ptr_of_fun */
    str r1, [r0]             /* *r0 ← r1
                                this is
                                ptr_of_fun ← &say_hello */
 
    bl make_indirect_call    /* call make_indirect_call */
 
    mov r0, #0               /* return from the program, set error code */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller (the system) */
 
addr_ptr_of_fun: .word ptr_of_fun
addr_say_hello : .word say_hello

Note that, in the function make_indirect_call we did

    ldr r0, addr_ptr_of_fun  /* r0 ← &ptr_of_fun */
    ldr r0, [r0]             /* r0 ← *r0 */

while in the main we do

    ldr r1, addr_say_hello   /* r1 ← &say_hello */

This is a similar case like arrays: when we load an array address, we do not need to load again (as it happens when we load simple scalars). This is because if we did that, we would be loading the first element of the array. With functions a similar thing happens: the function itself, its label, is already an address. If we did another load we would be loading an instruction into the register!! Not quite what we want 🙂

In the function make_indirect_call we are not loading a function but a pointer to a function (addr_ptr_of_fun), so we have to do the typical double load we do for scalars (because at the end, a pointer is just an integer that happens to be an address of the memory of our program).

Feel the power

The last example does not look very interesting, but being able to call a function indirectly is a very powerful thing. It allows us to keep the address of a function somewhere and call it. It allows us to pass the address of a function to another function. Why would we want to do that? Well, it is a rudimentary, yet effective, way of passing code to another function.

As an example, let’s make a generic greeter function which receives a greeting function as a parameter. This way the exact greeting is actually deferred to another function.

.data     /* data section */
.align 4  /* ensure the next label is 4-byte aligned */
message_1: .asciz "Hello\n"
.align 4  /* ensure the next label is 4-byte aligned */
message_2: .asciz "Bonjour\n"
 
.text     /* text section (= code) */
 
.align 4  /* ensure the next label is 4-byte aligned */
say_hello:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    /* Prepare the call to printf */
    ldr r0, addr_of_message_1 /* r0 ← &message */
    bl printf                 /* call printf */
    pop {r4, lr}              /* restore r4 and lr */
    bx lr                     /* return to the caller */
 
.align 4  /* ensure the next label is 4-byte aligned */
addr_of_message_1: .word message_1
 
.align 4  /* ensure the next label is 4-byte aligned */
say_bonjour:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    /* Prepare the call to printf */
    ldr r0, addr_of_message_2 /* r0 ← &message */
    bl printf                 /* call printf */
    pop {r4, lr}              /* restore r4 and lr */
    bx lr                     /* return to the caller */
 
.align 4  /* ensure the next label is 4-byte aligned */
addr_of_message_2: .word message_2
 
.align 4
greeter:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    blx r0                   /* indirect call to r0 */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */
 
.globl main /* state that 'main' label is global */
.align 4  /* ensure the next label is 4-byte aligned */
main:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
 
    ldr r0, addr_say_hello   /* r0 ← &say_hello */
    bl greeter               /* call greeter */
 
    ldr r0, addr_say_bonjour /* r0 ← &say_bonjour */
    bl greeter               /* call greeter */
 
    mov r0, #0               /* return from the program, set error code */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller (the system) */
 
addr_say_hello : .word say_hello
addr_say_bonjour : .word say_bonjour

If we run it

$ ./greeter_01 
Hello
Bonjour

You are probably not impressed by the output of this previous program. So let’s try to make it more interesting: we will greet people generically, some people will be greeted in English and some other will be greeted in French.

Let’s start defining a bunch of data that we will require for this example. First greeting messages in English and French. Note that we will greet the person by name, so we will use a printf format string.

1
2
3
4
5
6
.data     /* data section */
 
.align 4  /* ensure the next label is 4-byte aligned */
message_hello: .asciz "Hello %s\n"
.align 4  /* ensure the next label is 4-byte aligned */
message_bonjour: .asciz "Bonjour %s\n"

Next we will define some tags that we will use to tag people as English or French. This tag will contain the address to the specific greeting function. The English tag will have the address of say_hello and the French tag will have the address of say_bonjour.

7
8
9
10
11
12
13
14
15
/* tags of kind of people */
.align 4  /* ensure the next label is 4-byte aligned */
person_english : .word say_hello /* tag for people
                                     that will be greeted 
                                     in English */
.align 4  /* ensure the next label is 4-byte aligned */
person_french : .word say_bonjour /* tag for people
                                     that will be greeted 
                                     in French */

Let’s define some names that we will use later, when defining people.

18
19
20
21
22
23
24
25
26
/* several names to be used in the people definition */
.align 4
name_pierre: .asciz "Pierre"
.align 4
name_john: .asciz "John"
.align 4
name_sally: .asciz "Sally"
.align 4
name_bernadette: .asciz "Bernadette"

And now define some people. Every person is actually a pair formed by an address to their name and an address to their tag.

28
29
30
31
32
33
34
35
.align 4
person_john: .word name_john, person_english
.align 4
person_pierre: .word name_pierre, person_french
.align 4
person_sally: .word name_sally, person_english
.align 4
person_bernadette: .word name_bernadette, person_french

Finally let’s group every person in an array. The array contains addresses to each people (not the people themselves).

38
39
/* array of people */
people : .word person_john, person_pierre, person_sally, person_bernadette

Now let’s define the code. These are the two specific functions for each language (English and French). Note that we already named their labels in the tags above.

41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
.text     /* text section (= code) */
 
.align 4  /* ensure the next label is 4-byte aligned */
say_hello:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    /* Prepare the call to printf */
    mov r1, r0               /* r1 ← r0 */
    ldr r0, addr_of_message_hello
                             /* r0 ← &message_hello */
    bl printf                /* call printf */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */
 
.align 4  /* ensure the next label is 4-byte aligned */
addr_of_message_hello: .word message_hello
 
.align 4  /* ensure the next label is 4-byte aligned */
say_bonjour:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
    /* Prepare the call to printf */
    mov r1, r0               /* r1 ← r0 */
    ldr r0, addr_of_message_bonjour
                             /* r0 ← &message_bonjour */
    bl printf                /* call printf */
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */
 
.align 4  /* ensure the next label is 4-byte aligned */
addr_of_message_bonjour: .word message_bonjour

Before we go to the interesting function, let’s define the main function.

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
.globl main /* state that 'main' label is global */
.align 4  /* ensure the next label is 4-byte aligned */
main:
    push {r4, r5, r6, lr}    /* keep callee saved registers that we will modify */
 
    ldr r4, addr_of_people   /* r4 ← &people */
    /* recall that people is an array of addresses (pointers) to people */
 
    /* now we loop from 0 to 4 */
    mov r5, #0               /* r5 ← 0 */
    b check_loop             /* branch to the loop check */
 
    loop:
      /* prepare the call to greet_person */
      ldr r0, [r4, r5, LSL #2]  /* r0 ← *(r4 + r5 << 2)   this is
                                   r0 ← *(r4 + r5 * 4)
                                   recall, people is an array of addresses,
                                   so this is
                                   r0 ← people[r5]
                                */
      bl greet_person           /* call greet_person */
      add r5, r5, #1            /* r5 ← r5 + 1 */
    check_loop:
      cmp r5, #4                /* compute r5 - 4 and update cpsr */
      bne loop                  /* if r5 != 4 branch to loop */
 
    mov r0, #0               /* return from the program, set error code */
    pop {r4, r5, r6, lr}     /* callee saved registers */
    bx lr                    /* return to the caller (the system) */
 
addr_of_people : .word people

As you can see, what we do here is to load elements 0 to 3 of the people array and call the function greet_person. Every element in people array is a pointer, so we can put them in a register, in this case r0 because it will be the first parameter of greet_person.

Let’s see now the code for the function greet_person.

75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* This function receives an address to a person */
.align 4
greet_person:
    push {r4, lr}            /* keep lr because we call printf, 
                                we keep r4 to keep the stack 8-byte
                                aligned, as per AAPCS requirements */
 
    /* prepare indirect function call */
    mov r4, r0               /* r0 ← r4, keep the first parameter in r4 */
    ldr r0, [r4]             /* r0 ← *r4, this is the address to the name
                                of the person and the first parameter
                                of the indirect called function*/
 
    ldr r1, [r4, #4]         /* r1 ← *(r4 + 4) this is the address
                                to the person tag */
    ldr r1, [r1]             /* r1 ← *r1, the address of the
                                specific greeting function */
 
    blx r1                   /* indirect call to r1, this is
                                the specific greeting function */
 
    pop {r4, lr}             /* restore r4 and lr */
    bx lr                    /* return to the caller */

In register r0 we have the address of a person. We move it to r4 for convenience as r0 will be used for the indirectly called function. Then we load the name of the person, found in [r4], this is [r4, #0] (this is *(r4 + 0), so *r4) into r0. Then we load the person tag, found 4 bytes after the name (remember that the name of the person is an address, so it takes 4 bytes in ARM). The tag itself is not very useful except because it allows us to get the specific greeting function (either say_hello or say_bonjour). So we load [r4, #4], the address of the tag, in r1. Ok, now r1 contains the address of the tag and we know that the first 4 bytes of a tag contain the specific greeting function.

If we run this program the output is:

$ ./greeter_02 
Hello John
Bonjour Pierre
Hello Sally
Bonjour Bernadette

Late binding and object orientation

In the last example we have implemented, in a very simple way, a feature of the object-oriented programming (OOP) called late binding, which means that one does not know which function is called for a given object.

In our example the objects are of kind Person. Every Person can be greeted, this is what greet_person does. We do not have objects of kind Person really, but EnglishPerson and FrenchPerson. When you greet an EnglishPerson you expect to greet him/her with Hello, when you greet a FrenchPerson you expect to greet him/her with Bonjour.

If you know C++ (or Java), you’ll quickly realize that our last example actually implements something like this.

struct Person
{
  const char* name;
  virtual void greet() = 0;
};
 
struct EnglishPerson : Person
{
  virtual void greet()
  {
    printf("Hello %s\n", this->name);
  }
};
 
struct FrenchPerson : Person
{
  virtual void greet()
  {
    printf("Bonjour %s\n", this->name);
  }
};

In the snippet above, this is the Person we passed to our function greet_person. That parameter allowed us to retrieve the name of the person (this->name) and the specific version of greet we wanted.

I hope that this last example, albeit a bit long, actually shows you the power of indirect calls.

This is all for today.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn

5 thoughts on “ARM assembler in Raspberry Pi – Chapter 20

  • Fernando says:

    Nice, as always!

  • Bill says:

    Hi,
    where can I find addr_of_people? Suppose it should be people : .word person_john, person_pierre, person_sally, person_bernadette

    • Roger Ferrer Ibáñez says:

      Hi Bill,

      you are right it is missing. I fixed the post.

      It should be addr_of_people: .word people.

      Thank you!

  • Grant says:

    Creating a struct in the data area is great but if you wanted to have a struct not accessible like that are you forced to use the stack (so essentially a local variable)? So you would have to recreate the struct format every time you wanted to create one in a new function right?

    • Roger Ferrer Ibáñez says:

      Hi Grant,

      basically yes. This is how C++ and any other OOP programming language that allows objects be stored in the stack work. A special function is usually invoked as part of the language semantics to set everything up. It is commonly called a constructor.

      Note though, that the example in this post is extremely naive. OOP programming languages do not keep every pointer of every virtual function they have in the object directly. Instead, they keep those pointers in another structure, usually called the virtual table, in a global variable. There is one virtual table per class (i.e. Person, EnglishPerson and FrenchPerson in the example). The objects (i.e. john, pierre, sally and bernadette) only need a pointer to the virtual table. So the initialization process for this part is as easy as setting up the proper pointer.

      Hope this answers your question.

Leave a Reply

Your email address will not be published. Required fields are marked *