# Position-Independent Code

Position-Independent Code (PIC) is a requirement for all Ledger device apps. Because Ledger OS loads apps into different flash memory slots at runtime, your code must not contain hard-coded addresses that reference compile-time link addresses. This page explains how PIC works in the Ledger OS environment, what limitations it introduces, and how to use the `PIC()` macro to avoid common segmentation faults.

This page is part of the memory management series for Ledger device apps. See also [persistent storage](./persistent-storage) and [memory alignment](./memory-alignment).

## PIC and model implications

PIC stands for Position-Independent Code. The Ledger OS toolchain produces PIC to allow the code **link address** to be different from the code **execution address**. For example, the `main` function is linked in the generated application at address `0xC0D00000`. However, the slot used when loaded into the Secure Element could be `0x10E40400`. Therefore, if the code makes a reference to `0xC0D00000`, even with an offset, it would be denied access as the application is locked by the Memory Protection Unit (not to mention, this is not the correct address of the `main` function at runtime).

The PIC assembly generator makes sure every dereference is relative to the Program Counter, and never to an arbitrary address resolved during the link stage. This behavior is supported by clang versions 4.0.0 and later.

Traditionally, PIC code implies the BSS segment (RAM variables) is at a constant offset of the code. For example, if code is at `0xC0D00000`, then global vars may be at `0xC2D00000`, so if loaded at `0x10E00000` then global vars would be at `0x12E00000`. However, Ledger OS uses a fixed address for global vars. The global variables start address and length are defined in the link script. Only the code is meant to be placed at different addresses (in flash memory, rather than RAM).

The model we choose has limitations, which are related to the way `const` data and code is referenced in other `const` data. Here is a simple example:

```c copy
const char array1[] = {1, 2, 3, 4};
const char array2[] = {1, 2, 3, 4};
const char *array_2d[] = {array1, array2};

void main() {
    int sum, i, j;
    sum = 0;
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 4; j++) {
            sum += array_2d[i][j]; // Segmentation Fault!
        }
    }
}
```

In the example above, when dereferencing `array_2d`, the compiler uses a link-time address (in the `0xC0D00000` space, following the previous examples). This is not where the program is loaded in memory at runtime. Therefore, when the dereference is executed, it causes a segmentation fault that effectively stalls the SE. Luckily, the solution is straightforward, thanks to a small piece of assembly provided with the SDKs which is invoked with the `PIC(...)` macro. `PIC(...)` uses the current load address to adjust the link-time address in order to acquire the correct runtime address of `const` data and code. The above example can be corrected by modifying the line where `array_2d` is dereferenced as follows:

```c copy
sum += ((const char*) PIC(array_2d[i]))[j];
```

The same mechanism must be applied when storing function pointers in `const` data. The PIC call cast is just different. Additionally, if a non-link-time address is passed to `PIC(...)`, it will be preserved. This is possible due to the wisely chosen link-time address which is beyond both real RAM and loadable addresses. For example, `PIC(...)` is used during a call to `io_seproxyhal_display_default(...)`: all display elements can hold a reference to a string to be displayed with the element, and the string could be in RAM or code, and therefore `PIC(...)` is applied to acquire the correct runtime address of the string, even if it is in RAM.

## Summary

The key rules for writing PIC-compliant code:

- Use the `PIC(...)` macro whenever you dereference a pointer stored in `const` data, including function pointers.
- Never assume that a link-time address matches the runtime address.
- `PIC(...)` passes through RAM addresses safely; you can apply it even when you are unsure whether a pointer points to RAM or flash.
- The PIC model only relocates code in flash; global variables (BSS segment) always live at a fixed address defined in the link script.

## Further reading

- [Persistent storage](./persistent-storage) — how flash memory and NVRAM are managed in Ledger OS.
- [Memory alignment](./memory-alignment) — alignment constraints for the Secure Element.
- [Application environment](../ledger-os/application-environment) — how Ledger OS isolates and loads apps into the Secure Element.
