Indirect calls
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.
TinyMCE checkbox toggler for jQuery ARM assembler in Raspberry Pi – Chapter 21
where can I find addr_of_people? Suppose it should be people : .word person_john, person_pierre, person_sally, person_bernadette</p>
you are right it is missing. I fixed the post.
It should be
addr_of_people: .word people
.Thank you!
</ul> </li>
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
andFrenchPerson
in the example). The objects (i.e.john
,pierre
,sally
andbernadette
) 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.
</ul> </li> </ul>
</p>