NSO
NSO is the main executable format.
It starts with the "NSO" header and mainly describes .text, .rodata, and .data segments (like a short-form of ELF program headers). If the segments are compressed, they are compressed using LZ4.
NsoHeader
| Offset | Size | Description |
|---|---|---|
| 0x0 | 4 | Signature ("NSO0") |
| 0x4 | 4 | Version |
| 0x8 | 4 | Reserved |
| 0xC | 4 | Flags |
| 0x10 | 0x4 | TextFileOffset |
| 0x14 | 0x4 | TextMemoryOffset |
| 0x18 | 0x4 | TextSize |
| 0x1C | 0x4 | ModuleNameOffset (calculated by sizeof(header)) |
| 0x20 | 0x4 | RoFileOffset |
| 0x24 | 0x4 | RoMemoryOffset |
| 0x28 | 0x4 | RoSize |
| 0x2C | 0x4 | ModuleNameSize |
| 0x30 | 0x4 | DataFileOffset |
| 0x34 | 0x4 | DataMemoryOffset |
| 0x38 | 0x4 | DataSize |
| 0x3C | 0x4 | BssSize |
| 0x40 | 0x20 | ModuleId |
| 0x60 | 0x4 | TextFileSize (.text compressed size) |
| 0x64 | 0x4 | RoFileSize (.rodata compressed size) |
| 0x68 | 0x4 | DataFileSize (.data compressed size) |
| 0x6C | 0x1C | Reserved |
| 0x88 | 0x4 | EmbeddedOffset (relative to the .rodata section) |
| 0x8C | 0x4 | EmbeddedSize |
| 0x90 | 0x4 | DynStrOffset (relative to the .rodata section) |
| 0x94 | 0x4 | DynStrSize |
| 0x98 | 0x4 | DynSymOffset (relative to the .rodata section) |
| 0x9C | 0x4 | DynSymSize |
| 0xA0 | 0x20 | TextHash (SHA-256 hash over the decompressed .text section using the above size) |
| 0xC0 | 0x20 | RoHash (SHA-256 hash over the decompressed .rodata section using the above size) |
| 0xE0 | 0x20 | DataHash (SHA-256 hash over the decompressed .data section using the above size) |
| 0x100 | Variable | Compressed sections |
Flags
| Bits | Description |
|---|---|
| 0 | TextCompress (.text section is compressed) |
| 1 | RoCompress (.rodata section is compressed) |
| 2 | DataCompress (.data section is compressed) |
| 3 | TextHash (.text hash must be checked when loading) |
| 4 | RoHash (.rodata hash must be checked when loading) |
| 5 | DataHash (.data hash must be checked when loading) |
ModuleId
This is "nn::ro::detail::ModuleId".
Value of "build id" from ELF's GNU .note section. Contains variable sized digest, up to 32bytes.
Arguments
Loader maps memory and writes the arguments to {end of rwdata section}. Official processes use argdata_addr = {page-aligned _end}. svcQueryMemory is used by official sw to verify that argdata_addr is mapped RW, since this memory is only mapped when arguments are specified via that command. Afterwards, official sw aligns the argdata_addr to 4-bytes.
The structure located at argdata_addr is as follows:
| Offset | Size | Description |
|---|---|---|
| 0x0 | 0x4 | This is the total allocated space relative to argdata_addr, used for calculating the max size of the argv ptr array. Normally 0x9000? |
| 0x4 | 0x4 | This is the total_bytesize of the actual argdata string. |
| 0x8 | 0x18 | Unused by official sw. |
| 0x20 | See above | Actual argdata string. |
- The copy of the args used with the argv array is written by official processes to actual_argdata_string+actual_argdata_size.
- argv_ptrarray written by official processes is at (args_copy+actual_argdata_size) + 0x9 & ~0x7.