Line 1: |
Line 1: |
| NSO is the main executable format. | | 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): | + | 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. |
| | | |
− | === SegmentHeader === | + | = NsoHeader = |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
Line 12: |
Line 12: |
| | 0x0 | | | 0x0 |
| | 4 | | | 4 |
− | | file offset of data | + | | Signature ("NSO0") |
| |- | | |- |
| | 0x4 | | | 0x4 |
| | 4 | | | 4 |
− | | memory offset loaded to | + | | Version |
| |- | | |- |
| | 0x8 | | | 0x8 |
| | 4 | | | 4 |
− | | size of data copied to memory offset (i.e. size after decompression) | + | | Reserved |
| |- | | |- |
| | 0xC | | | 0xC |
| | 4 | | | 4 |
− | | alignment used on memory size / size of .bss in the case of .data segment | + | | [[#Flags|Flags]] |
− | |} | + | |- |
− | | + | | 0x10 |
− | === .rodata-relative extent ===
| + | | 0x4 |
− | {| class="wikitable" border="1"
| + | | TextFileOffset |
| + | |- |
| + | | 0x14 |
| + | | 0x4 |
| + | | TextMemoryOffset |
| + | |- |
| + | | 0x18 |
| + | | 0x4 |
| + | | TextSize |
| |- | | |- |
− | ! Offset
| + | | 0x1C |
− | ! Size
| + | | 0x4 |
− | ! Description
| + | | ModuleNameOffset (calculated by sizeof(header)) |
| |- | | |- |
− | | 0x0 | + | | 0x20 |
− | | 4 | + | | 0x4 |
− | | offset (relative to .rodata) | + | | RoFileOffset |
| |- | | |- |
| + | | 0x24 |
| | 0x4 | | | 0x4 |
− | | 4 | + | | RoMemoryOffset |
− | | size of region
| |
− | |}
| |
− | | |
− | === NSO Header ===
| |
− | {| class="wikitable" border="1"
| |
| |- | | |- |
− | ! Offset
| + | | 0x28 |
− | ! Size
| + | | 0x4 |
− | ! Description
| + | | RoSize |
| |- | | |- |
− | | 0x0 | + | | 0x2C |
− | | 4 | + | | 0x4 |
− | | Magic "NSO0" | + | | ModuleNameSize |
| |- | | |- |
| + | | 0x30 |
| | 0x4 | | | 0x4 |
− | | 4 | + | | DataFileOffset |
− | |
| |
| |- | | |- |
− | | 0x8 | + | | 0x34 |
− | | 4 | + | | 0x4 |
− | | | + | | DataMemoryOffset |
| |- | | |- |
− | | 0xC | + | | 0x38 |
− | | 4 | + | | 0x4 |
− | | Always 0x3f? | + | | DataSize |
| |- | | |- |
− | | 0x10 | + | | 0x3C |
− | | 0x10 * 3 | + | | 0x4 |
− | | SegmentHeader for each segment | + | | BssSize |
| |- | | |- |
| | 0x40 | | | 0x40 |
| | 0x20 | | | 0x20 |
− | | Value of "build id" from ELF's GNU .note section. Contains variable sized digest, up to 32bytes. | + | | [[#ModuleId|ModuleId]] |
| |- | | |- |
| | 0x60 | | | 0x60 |
− | | 0x4 * 3 | + | | 0x4 |
− | | file size of each segment (i.e. LZ4-compressed size) | + | | TextFileSize (.text compressed size) |
| |- | | |- |
− | | 0x6c | + | | 0x64 |
− | | 0x24 | + | | 0x4 |
− | | Padding | + | | 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 | | | 0x90 |
− | | 8 | + | | 0x4 |
− | | .rodata-relative extents of .dynstr | + | | DynStrOffset (relative to the .rodata section) |
| + | |- |
| + | | 0x94 |
| + | | 0x4 |
| + | | DynStrSize |
| |- | | |- |
| | 0x98 | | | 0x98 |
− | | 8 | + | | 0x4 |
− | | .rodata-relative extents of .dynsym | + | | DynSymOffset (relative to the .rodata section) |
| + | |- |
| + | | 0x9C |
| + | | 0x4 |
| + | | DynSymSize |
| |- | | |- |
| | 0xA0 | | | 0xA0 |
− | | 0x20 * 3 | + | | 0x20 |
− | | SHA256 hashes over the decompressed sections using the above byte-sizes: .text, .rodata, and .data. | + | | 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 | | | 0x100 |
− | | | + | | Variable |
| | Compressed sections | | | Compressed sections |
| |} | | |} |
| | | |
− | Most data in Switch binaries are standard ELF structures, however some are custom.
| + | == Flags == |
− | For example, the MOD header is essentially a replacement for a PT_DYNAMIC program header.
| |
− | | |
− | === MOD === | |
− | All offsets are signed 32bit values relative to the magic field.
| |
− | The 32bits at image base + 4 must point to the magic field.
| |
− | The MOD structure is designed such that it can be placed at image base and point to itself.
| |
− | The 2 fields preceding the magic field get copied around with the structure, even if it is relocated to somewhere besides the image base.
| |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
− | ! Offset | + | ! Bits |
− | ! Size
| |
| ! Description | | ! Description |
| |- | | |- |
− | | 0x00 | + | | 0 |
− | | 4 | + | | TextCompress (.text section is compressed) |
− | | zero padding | + | |- |
| + | | 1 |
| + | | RoCompress (.rodata section is compressed) |
| + | |- |
| + | | 2 |
| + | | DataCompress (.data section is compressed) |
| |- | | |- |
− | | 0x04 | + | | 3 |
− | | 4 | + | | TextHash (.text hash must be checked when loading) |
− | | offset to magic. Always 8 (so it works when MOD is at image_base + 0).
| |
| |- | | |- |
− | | 0x08
| |
| | 4 | | | 4 |
− | | magic "MOD0" | + | | RoHash (.rodata hash must be checked when loading) |
| |- | | |- |
− | | 0x0C | + | | 5 |
− | | 4 | + | | DataHash (.data hash must be checked when loading) |
− | | .dynamic offset | + | |} |
| + | |
| + | == 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_services|Loader]] maps memory and writes the [[Loader_services#SetProgramArgument|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: |
| + | {| class="wikitable" border="1" |
| |- | | |- |
− | | 0x10
| + | ! Offset |
− | | 4
| + | ! Size |
− | | .bss start offset
| + | ! Description |
| |- | | |- |
− | | 0x14 | + | | 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? |
− | | 4 | |
− | | .bss end offset | |
| |- | | |- |
− | | 0x18 | + | | 0x4 || 0x4 || This is the total_bytesize of the actual argdata string. |
− | | 4 | |
− | | .eh_frame start offset | |
| |- | | |- |
− | | 0x1C | + | | 0x8 || 0x18 || Unused by official sw. |
− | | 4 | |
− | | .eh_frame end offset | |
| |- | | |- |
− | | 0x20 | + | | 0x20 || See above || Actual argdata string. |
− | | 4 | |
− | | offset to runtime-generated module object. typically equal to .bss base. | |
| |} | | |} |
| + | |
| + | * 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. |