# Coding examples
This chapter presents examples showing how the following operations
can be coded in Hexagon assembly code:
- Function prologs and epilogs
- Function calls (direct and indirect)
- Branches (direct and indirect)
- Data access
- Thread-local storage
Most of the examples include separate coding solutions for two cases:
- Absolute code (which must be loaded at a specific address)
- Position-independent code (which can be loaded at an arbitrary
address)
Position-independent code is used in shared objects, which typically
execute in the context of another object. Pay attention to the
differences between the coding solutions for absolute and
position-independent code.
Note
The examples are not intended as definitive coding
solutions – they merely demonstrate how certain basic operations can
be implemented. Optimal coding solutions can be derived from the
examples.
## Register use
The examples in this chapter use the following register conventions:
- The absolute address of the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#global-offset-table)) is stored in R24
- The absolute address of the application TLS area ([Thread-local storage access](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#thread-local-storage-access)) is stored in R25
Both R24 and R25 are initialized in the function prolog ([Function prologs and epilogs](https://docs.qualcomm.com/doc/80-N2040-23/topic/coding-examples.html#function-prologs-and-epilogs)).
Note
These register choices are arbitrary, and do not imply any
standard coding convention for Hexagon assembly code.
## Assembler symbols
**Special assembler symbols used in examples**
| **Name** | **Description** |
| --- | --- |
| \_GLOBAL\_OFFSET\_TABLE\_ | Reference symbol in the global offset table ([Global offset table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#global-offset-table)). Implicit address of the
zero’th (index = 0) GOT entry. |
| name@PCREL | Offset to position of the symbol name from the current location. |
| name@GOT | Offset of GOT entry for the symbol name from
\_GLOBAL\_OFFSET\_TABLE\_. Instructs linker to create GOT entry
for the symbol name. |
| name@GOTREL | Offset to location of the symbol name from
\_GLOBAL\_OFFSET\_TABLE\_. |
| name@PLT | Offset to PLT entry for the symbol name from the current code
location. Instructs linker to create a PLT entry for the symbol
name. |
| name@GDPLT | Offset to PLT entry for the function that returns the address of
a TLS symbol name from the current code location, using the GD method ([General dynamic (GD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#general-dynamic)).
This function takes the address of a TLS\_index structure as its
argument ([Interface](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#interface)). |
| name@GDGOT | Offset of first GOT entry for the TLS\_index structure for the
symbol name from \_GLOBAL\_OFFSET\_TABLE\_, using the GD method ([General dynamic (GD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#general-dynamic)).
Instructs linker to create GOT entries for TLS\_index structure members ti\_moduleid and ti\_tlsoffset. |
| name@LDPLT | Offset to PLT entry for the function that returns the address of
a TLS symbol name from the current code location, using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#local-dynamic)).
This function takes the address of a TLS\_index structure as its
argument ([Interface](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#interface)). |
| name@LDGOT | Offset of first GOT entry for the TLS\_index structure for the
symbol name from \_GLOBAL\_OFFSET\_TABLE\_, using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#local-dynamic)).
Instructs linker to create GOT entries for TLS\_index structure
members ti\_moduleid and ti\_tlsoffset, with the latter zeroed. |
| name@DTPREL | Offset to position of the symbol name from base of TLS template,
using the LD method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#local-dynamic)). |
| name@IE | Address of the GOT entry for the offset into the TLS area for
the symbol name, using the IE method ([Local dynamic (LD)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#local-dynamic)).
Instructs linker to create a GOT entry for the offset into the
TLS area for the symbol name. |
| name@IEGOT | Offset of the GOT entry for the offset into the TLS area for the
symbol name from
\_GLOBAL\_OFFSET\_TABLE\_, using the IE method ([Initial executable (IE)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#initial-executable)).
Instructs linker to create a GOT entry for the offset into the
TLS area for the symbol name. |
| name@TPREL | Offset to position of the symbol name from base of the TLS area,
using the LE method ([Local executable (LE)](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#local-executable)). |
## Addressing constraints
The Hexagon processor architecture has certain addressing constraints
which affect how data accesses are coded in Hexagon assembly
language. The constraints depend on the instruction and data size:
- Some branching instructions use a 7- to 22-bit operand as a signed
offset from the current program-counter (PC).
- When accessing data in the small data area (using GP-relative
addressing), a 16-bit value is used as an unsigned offset from the GP
register. This value is shifted left before it is added to the GP
register – the shift amount is determined by the size of the accessed
data:
- 0 for bytes
- 1 for half-words
- 2 for words
- 3 for double-words
This limits the range that GP-relative addressing can access: 64K for
bytes, 128K for half- words, 256K for words and 512K for
double-words.
- When indexing an entry in the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#global-offset-table)), instead of using a larger and slower
32-bit index, a program can restrict itself to just 16 bits for
indexing a GOT entry. If the GOT size grows larger than 64KB, then
32-bit indexes must be used.
## Function prologs and epilogs
Function *prologs* and *epilogs* are standard code sections that
appear at the beginning and end of a function body. Their purpose is
to manage the use of certain Hexagon processor registers within the
function ([Register use](https://docs.qualcomm.com/doc/80-N2040-23/topic/coding-examples.html#register-use)).
This section presents examples showing how function prologs and
epilogs can be coded in Hexagon assembly language.
### Absolute
If a function calls another function, it is defined as a *non-leaf*
function. In this case, in order to save the link register, a stack
frame must be allocated even if no local variables are allocated from
the stack.
If a function does not call another function, no stack frame is
necessary (though it might be useful for traversing previous stack
frames during debugging).
**Function prolog and epilog (absolute)**
| **C** | **Hexagon assembly language** |
| --- | --- |
| void foo (void)
{
...
}
Copy to clipboard | foo:
allocframe (#...)
...
dealloc_return
Copy to clipboard |
When using the GD or LD method for accessing TLS ([Thread-local storage access](https://docs.qualcomm.com/doc/80-N2040-23/topic/thread-local-storage.html#thread-local-storage-access)), the absolute address of the
GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#global-offset-table)) can be stored in a
register for convenience.
**Function prolog and epilog (absolute – GD or LD access)**
| **C** | **Hexagon assembly language** |
| --- | --- |
| void foo ()
{
...
}
Copy to clipboard | foo:
allocframe (#8)
memw(r29+#4) = r24
r24 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)
...
dealloc_return
Copy to clipboard |
When using the IE or LE method for accessing TLS, the UGP register
([Special registers](https://docs.qualcomm.com/doc/80-N2040-23/topic/process-initialization.html#special-registers)) can be copied to a
general-purpose register for convenience.
**Function prolog and epilog (absolute – IE or LE access)**
| **C** | **Hexagon assembly language** |
| --- | --- |
| void foo (void)
{
...
}
Copy to clipboard | foo:
allocframe (#...)
r25 = ugp
...
dealloc_return
Copy to clipboard |
## Direct function call
This section presents examples showing how direct function calls can
be coded in Hexagon assembly language.
### Absolute
Direct function calls are performed with the CALL instruction, which
uses the PC-relative addressing mode to specify the function address.
**Direct function call (absolute)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
foo (NULL);
Copy to clipboard | .extern foo
r0 = #0
call foo
Copy to clipboard | R_HEX_B22_PCREL
Copy to clipboard |
### Position-independent
In external function calls, the function address is not known at
build time. In this case the address must be stored in a specific
data structure at runtime ([Dynamic linking](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#dynamic-linking)).
Assuming that the relevant data structures have been initialized,
external functions are normally called through stubs contained in the
procedure linkage table.
**Direct function call (position-independent)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
foo (NULL);
Copy to clipboard | .extern foo
r0 = #0
call foo@PLT a
Copy to clipboard | R_HEX_PLT_B22_PCREL
Copy to clipboard |
^a^ The @PLT suffix is optional in this symbol reference.
Note
Local function calls use the same code sequence for both
absolute and position- independent code.
## Indirect function call
This section presents examples showing how indirect function calls
can be coded in Hexagon assembly language.
### Absolute
Indirect function calls are performed with the CALLR instruction.
**Indirect function call (absolute)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);
fun = foo;
fun (bar);
Copy to clipboard | .extern foo
.local bar
r1 = ##foo
memw(#fun) = r1
r0 = ##bar
callr r1
Copy to clipboard | R_HEX_32_6_X + R_HEX_16_X
R_HEX_GPREL_2
R_HEX_32_6_X + R_HEX_16_X
Copy to clipboard |
If the calling function does not have access to a small data area
([Special registers](https://docs.qualcomm.com/doc/80-N2040-23/topic/process-initialization.html#special-registers)), the function addresses must
be loaded from regular memory, which requires a less-efficient code
sequence.
**Indirect function call (absolute – no small data)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);
fun = foo;
fun (bar);
Copy to clipboard | .extern foo
.local bar
r0 = ##fun
r1 = ##foo
memw(r0+#0) = r1
r0 = ##fun
r1 = memw(r0+#0)
r0 = ##bar
callr r1
Copy to clipboard | R_HEX_32_6_X | R_HEX_16_X
R_HEX_32_6_X | R_HEX_16_X
R_HEX_32_6_X | R_HEX_16_X
R_HEX_32_6_X | R_HEX_16_X
Copy to clipboard |
### Position-independent
In position-independent code, indirect function calls are also
performed with the CALLR instruction. However, it is necessary to
calculate at runtime the absolute address of the function.
**Indirect function call (position-independent)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);
fun = foo;
fun (bar);
Copy to clipboard | .extern foo
.local bar
r20 = memw (r24 + ##foo@GOT) a
r0 = add (pc, ##bar@PCREL)
callr r20
Copy to clipboard | R_HEX_GOT_32_6_X b
R_HEX_GOT_11_X
R_HEX_6_PCREL_X
Copy to clipboard |
^a^ If GOT is known to be smaller than 64 KB, the immediate extension
can be omitted with the consequent changes to the appropriate
relocation type.
^b^ Relocation created for immediate constant extender.
If the GOT ([Global offset table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#global-offset-table)) is known to be
smaller than 64 KB, then a missing small data area requires fewer
loads from memory because the callee function address value fits in
16 bits.
**Indirect function call (position-independent, no small data)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern void foo (void *);
static void bar (void);
auto void (*fun) (void *);
fun = foo;
fun (bar);
Copy to clipboard | .extern foo
.local bar
r24 = add(pc,##_GLOBAL_OFFSET_TABLE_@PCREL)
r1 = add(r24, #foo@GOT)
r1 = memw(r1)
r0 = add(pc,##bar@PCREL)
callr r1
Copy to clipboard | R_HEX_GOT_16
R_HEX_6_PCREL_X
Copy to clipboard |
Note
The absolute address of the GOT is assumed to be stored in
R24 ([Register use](https://docs.qualcomm.com/doc/80-N2040-23/topic/coding-examples.html#register-use)).
Because the address of the function is referenced as a data
object, it is resolved along with the other data object when an
object file is loaded, effectively bypassing the lazy-binding
mechanism ([Procedure linkage table](https://docs.qualcomm.com/doc/80-N2040-23/topic/dynamic-linking.html#linkage-table)).
## Direct branch
This section presents an example showing how direct branches can be
coded in Hexagon assembly language.
### Absolute and position-independent
Direct branches are performed with the JUMP instruction, which uses
the PC-relative addressing mode to specify the branch address.
**Direct branch (absolute and position-independent)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| bar:
goto bar;
Copy to clipboard | .Lbar:
jump .lbar
Copy to clipboard | R_HEX_B22_PCREL
Copy to clipboard |
## Indirect branch
This section presents examples showing how indirect branches can be
coded in Hexagon assembly language.
### Absolute
Indirect branches are performed with the JUMPR instruction. For
instance, the following example uses an indirect branch to implement
a jump table.
r22 = const32 (#.Lbar)
r23 = addasl (r22, r21, #2)
r23 = memw (r23)
jumpr r23
...
.L1:
...
.L2:
...
.section .rodata, “a”, @progbits
.Lbar:
.word .L1
.word .L2
...
Copy to clipboard
If the code performing the branch does not have access to a small
data area ([Special registers](https://docs.qualcomm.com/doc/80-N2040-23/topic/process-initialization.html#special-registers)), the branch address
must be loaded from regular memory, which requires a less-efficient
code sequence.
r22 = ##.Lbar
r23 = addasl (r22, r21, #2)
r23 = memw (r23)
jumpr r23
...
.L1:
...
.L2:
...
.section .rodata, “a”, @progbits
.Lbar:
.word .L1
.word .L2
...
Copy to clipboard
### Position-independent
In position-independent code, indirect branches are also performed
with the JUMPR instruction. However, it is necessary to calculate at
runtime the absolute address of the branch destination.
For instance, the following example loads the branch address from
regular memory.
r22 = add (pc, ##.Lbar@PCREL)
r23 = addasl (r22, r21, #2)
r23 = memw (r23)
r23 = add (r23, r24)
jumpr r23
...
.L1:
...
.L2:
...
.section .data, “aw”, @progbits
.Lbar:
.word .L1
.word .L2
...
Copy to clipboard
## Data access
This section presents examples showing how data accesses can be coded
in Hexagon assembly language.
### Absolute
Data accesses are performed using the Hexagon load and store
instructions.
**Data access (absolute)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern int src;
static int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4
r23 = const32 (#dst)
r22 = memw (#src)
memw (r23) = r22
Copy to clipboard | R_HEX_GPREL16_2 + R_HEX_32 a
R_HEX_GPREL16_2
Copy to clipboard |
^a^ Relocation created in small data area.
If the code performing the data access does not have access to a
small data area ([Special registers](https://docs.qualcomm.com/doc/80-N2040-23/topic/process-initialization.html#special-registers)), the data
address must be loaded from regular memory, which requires a
less-efficient code sequence.
**Data access (absolute – no small data)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern int src;
static int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4
r23 = ##dst
r21 = memw (##src)
memw (r23) = r21
Copy to clipboard | R_HEX_HI16
R_HEX_LO16
R_HEX_HI16
R_HEX_LO16
Copy to clipboard |
### Position-independent
In position-independent code, data accesses are also performed with
the Hexagon load and store instructions. However, it is necessary to
calculate at runtime the absolute address of the data.
**Data access (position-independent – no small data)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern int src;
static int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.lcomm dst, 4
r24 = add(pc, ##_GLOBAL_OFFSET_TABLE_@PCREL)
r1 = memw(r24 + ##ptr@GOT)
memw(r1+#0) = r0
r1 = memw(r24 + ##src@GOT)
r16 = memw(r1+#0)
r0 = add(pc, ##dst@PCREL)
memw(r0+#0) = r16
Copy to clipboard | R_HEX_GOTREL_HI16
R_HEX_GOTREL_LO16
R_HEX_GOT_HI16
R_HEX_GOT_LO16
Copy to clipboard |
## Thread-local storage
This section presents examples showing how data accesses to
thread-local storage (TLS) can be coded in version-specific Hexagon
assembly language.
Note
The following examples for Hexagon V4 also apply to Hexagon
V5x and V6x.
### Absolute
This section presents code examples using the following TLS access
methods:
- GD
- LD
- IE
- LE
GD TLS data accesses in V4 use 32-bit constant extenders to make the
code sequence more efficient.
**Thread-local storage (V4 absolute – GD access)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern __thread int src;
extern __thread int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object
.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4
.text
r0 = add (r24, ##dst@GDGOT) a
call dst@GDPLT
r23 = r0
r0 = add (r24, ##src@GDGOT) a
call src@GDPLT
r22 = memw (r0)
memw (r23) = r22
Copy to clipboard | R_HEX_GD_GOT_32_6_X b +
R_HEX_GD_GOT_16_X +
R_HEX_DTPMOD_32 c +
R_HEX_DTPREL_32 c
R_HEX_GD_PLT_B22_PCREL
R_HEX_GD_GOT_32_6_X b +
R_HEX_GD_GOT_16_X +
R_HEX_DTPMOD_32 c +
R_HEX_DTPREL_32 c
R_HEX_GD_PLT_B22_PCREL
Copy to clipboard |
^a^ If GOT is known to be smaller than 64 KB, the immediate extension
can be omitted with the consequent changes to the appropriate
relocation type.
^b^ Relocation created for immediate constant extender.
^c^ Relocation created in GOT.
LD TLS data accesses in the V4 processor use 32-bit constant
extenders to make the code sequence more efficient.
**Thread-local storage (V4 absolute – LD access)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern __thread int src;
extern __thread int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .section .tbss, “awT”, @nobits
.type src, @tls_object
src: .skip 4
.type src, @tls_object
dst: .skip 4
.text
r0 = add (r24, ##dst@LDGOT) a
call dst@LDPLT
r23 = add (r0, ##dst@DTPREL) d
r22 = add (r0, ##src@DTPREL) d
memw (r23) = r22
Copy to clipboard | R_HEX_LD_GOT_32_6_X b +
R_HEX_LD_GOT_16_X +
R_HEX_DTPMOD_32 c
R_HEX_LD_PLT_B22_PCREL
R_HEX_DTPREL_32_6_X b +
R_HEX_DTPREL_16_X
R_HEX_DTPREL_32_6_X b +
R_HEX_DTPREL_16_X
Copy to clipboard |
^a^ If GOT is known to be smaller than 64 KB, the immediate extension
can be omitted with the consequent changes to the appropriate
relocation type.
^b^ Relocation created for immediate constant extender.
^c^ Relocation created in GOT.
- ^d^ If TLS template is known to be smaller than 64 KB, the immediate
- extension can be omitted with consequent changes to the appropriate
relocation type.
IE TLS data accesses in the V4 processor use
indirect-with-register-offset addressing mode and 32-bit constant
extenders to make the code sequence more efficient.
**Thread-local storage (V4 absolute – IE access)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern __thread int src;
extern __thread int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object
.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4
.text
r23 = memw (##dst@IE)
r22 = memw (##src@IE)
r22 = memw (r25 + r22 << #1)
memw (r25 + r23 << #1) = r22
Copy to clipboard | R_HEX_IE_32_6_X a +
R_HEX_IE_16_X +
R_HEX_TPREL_32 b
R_HEX_IE_32_6_X a+
R_HEX_IE_16_X +
R_HEX_TPREL_32 b
Copy to clipboard |
^a^ Relocation created for immediate constant extender.
^b^ Relocation created in GOT.
LE TLS data accesses in the V4 processor use 32-bit constant
extenders to make the code sequence more efficient.
**Thread-local storage (V4 absolute – LE access)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern __thread int src;
extern __thread int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object
.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4
.text
r23 = add (r25, ##dst@TPREL) a
r22 = memw (r25 + ##src@TPREL)
memw (r23) = r22
Copy to clipboard | R_HEX_TPREL_32_6_X b +
R_HEX_TPREL_16_X
R_HEX_TPREL_32_6_X b +
R_HEX_TPREL_11_X
Copy to clipboard |
^a^ If TLS area is known to be smaller than 64 KB, the immediate
extension can be omitted with consequent changes to the appropriate
relocation type.
^b^ Relocation created for immediate constant extender.
### Position-independent
This section presents code examples using the following TLS access
methods:
- IE
Position-independent IE TLS data accesses in the V4 processor use
indirect-with-register-offset addressing mode and 32-bit constant
extenders to make the code sequence more efficient.
**Thread-local storage (V4 position-independent – IE access)**
| **C** | **Hexagon assembly language** | **Relocation** |
| --- | --- | --- |
| extern __thread int src;
extern __thread int dst;
auto int *ptr;
ptr = &dst;
*ptr = src;
Copy to clipboard | .extern src
.type src, @tls_object
.section .tbss, “awT”, @nobits
.global dst
.type dst, @tls_object
dst: .skip 4
.text
r23 = memw (r24 + ##dst@IEGOT) a
r22 = memw (r24 + ##src@IEGOT) a
r22 = memw (r25 + r22 << #1)
memw (r25 + r23 << #1) = r22
Copy to clipboard | R_HEX_IE_GOT_32_6_X b +
R_HEX_IE_GOT_11_X +
R_HEX_TPREL_32 c
R_HEX_IE_GOT_32_6_X b+
R_HEX_IE_GOT_11_X +
R_HEX_TPREL_32 b
Copy to clipboard |
^a^ If GOT is known to be smaller than 64 KB, the immediate extension
can be omitted with consequent changes to the appropriate relocation
type.
^b^ Relocation created for immediate constant extender.
^c^ Relocation created in GOT.
Last Published: Oct 06, 2026
[Previous Topic
Local executable (LE)](https://docs.qualcomm.com/bundle/publicresource/80-N2040-23/topics/thread-local-storage.md)