Line 3: |
Line 3: |
| 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. | | 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. |
| | | |
− | = Header = | + | = NsoHeader = |
| {| class="wikitable" border="1" | | {| class="wikitable" border="1" |
| |- | | |- |
Line 12: |
Line 12: |
| | 0x0 | | | 0x0 |
| | 4 | | | 4 |
− | | Magic "NSO0" | + | | Signature ("NSO0") |
| |- | | |- |
| | 0x4 | | | 0x4 |
| | 4 | | | 4 |
− | | Version (always 0) | + | | Version |
| |- | | |- |
| | 0x8 | | | 0x8 |
Line 27: |
Line 27: |
| |- | | |- |
| | 0x10 | | | 0x10 |
− | | 0xC | + | | 0x4 |
− | | [[#SegmentHeader|SegmentHeader]] for .text | + | | TextFileOffset |
| + | |- |
| + | | 0x14 |
| + | | 0x4 |
| + | | TextMemoryOffset |
| + | |- |
| + | | 0x18 |
| + | | 0x4 |
| + | | TextSize |
| |- | | |- |
| | 0x1C | | | 0x1C |
Line 35: |
Line 43: |
| |- | | |- |
| | 0x20 | | | 0x20 |
− | | 0xC | + | | 0x4 |
− | | [[#SegmentHeader|SegmentHeader]] for .rodata | + | | RoFileOffset |
| + | |- |
| + | | 0x24 |
| + | | 0x4 |
| + | | RoMemoryOffset |
| + | |- |
| + | | 0x28 |
| + | | 0x4 |
| + | | RoSize |
| |- | | |- |
| | 0x2C | | | 0x2C |
Line 43: |
Line 59: |
| |- | | |- |
| | 0x30 | | | 0x30 |
− | | 0xC | + | | 0x4 |
− | | [[#SegmentHeader|SegmentHeader]] for .data | + | | DataFileOffset |
| + | |- |
| + | | 0x34 |
| + | | 0x4 |
| + | | DataMemoryOffset |
| + | |- |
| + | | 0x38 |
| + | | 0x4 |
| + | | DataSize |
| |- | | |- |
| | 0x3C | | | 0x3C |
Line 71: |
Line 95: |
| |- | | |- |
| | 0x88 | | | 0x88 |
− | | 0x8 | + | | 0x4 |
− | | [[#SegmentHeaderRelative|SegmentHeaderRelative]] for .api_info | + | | EmbeddedOffset (relative to the .rodata section) |
| + | |- |
| + | | 0x8C |
| + | | 0x4 |
| + | | EmbeddedSize |
| |- | | |- |
| | 0x90 | | | 0x90 |
− | | 0x8 | + | | 0x4 |
− | | [[#SegmentHeaderRelative|SegmentHeaderRelative]] for .dynstr | + | | DynStrOffset (relative to the .rodata section) |
| + | |- |
| + | | 0x94 |
| + | | 0x4 |
| + | | DynStrSize |
| |- | | |- |
| | 0x98 | | | 0x98 |
− | | 0x8 | + | | 0x4 |
− | | [[#SegmentHeaderRelative|SegmentHeaderRelative]] for .dynsym | + | | DynSymOffset (relative to the .rodata section) |
| + | |- |
| + | | 0x9C |
| + | | 0x4 |
| + | | DynSymSize |
| |- | | |- |
| | 0xA0 | | | 0xA0 |
Line 98: |
Line 134: |
| | Compressed sections | | | Compressed sections |
| |} | | |} |
− |
| |
− | Most data in Switch binaries are standard ELF structures, however some are custom.
| |
− | For example, the MOD header is essentially a replacement for a PT_DYNAMIC program header.
| |
| | | |
| == Flags == | | == Flags == |
Line 127: |
Line 160: |
| |} | | |} |
| | | |
− | == SegmentHeader == | + | == ModuleId == |
− | {| class="wikitable" border="1"
| + | This is "nn::ro::detail::ModuleId". |
− | |-
| |
− | ! Offset
| |
− | ! Size
| |
− | ! Description
| |
− | |-
| |
− | | 0x0
| |
− | | 0x4
| |
− | | FileOffset
| |
− | |-
| |
− | | 0x4
| |
− | | 0x4
| |
− | | MemoryOffset
| |
− | |-
| |
− | | 0x8
| |
− | | 0x4
| |
− | | Size (decompressed)
| |
− | |}
| |
| | | |
− | == ModuleId ==
| |
| Value of "build id" from ELF's GNU .note section. Contains variable sized digest, up to 32bytes. | | Value of "build id" from ELF's GNU .note section. Contains variable sized digest, up to 32bytes. |
− |
| |
− | == SegmentHeaderRelative ==
| |
− | {| class="wikitable" border="1"
| |
− | |-
| |
− | ! Offset
| |
− | ! Size
| |
− | ! Description
| |
− | |-
| |
− | | 0x0
| |
− | | 4
| |
− | | Offset
| |
− | |-
| |
− | | 0x4
| |
− | | 4
| |
− | | Size
| |
− | |}
| |
− |
| |
− | Offset is relative to the .rodata section.
| |
− |
| |
− | == MOD ==
| |
− | {| class="wikitable" border="1"
| |
− | |-
| |
− | ! Offset
| |
− | ! Size
| |
− | ! Description
| |
− | |-
| |
− | | 0x00
| |
− | | 4
| |
− | | Reserved
| |
− | |-
| |
− | | 0x04
| |
− | | 4
| |
− | | MagicOffset (always 8, so it works when MOD is at image_base + 0)
| |
− | |-
| |
− | | 0x08
| |
− | | 4
| |
− | | Magic "MOD0"
| |
− | |-
| |
− | | 0x0C
| |
− | | 4
| |
− | | .dynamic offset
| |
− | |-
| |
− | | 0x10
| |
− | | 4
| |
− | | .bss start offset
| |
− | |-
| |
− | | 0x14
| |
− | | 4
| |
− | | .bss end offset
| |
− | |-
| |
− | | 0x18
| |
− | | 4
| |
− | | .eh_frame_hdr start offset
| |
− | |-
| |
− | | 0x1C
| |
− | | 4
| |
− | | .eh_frame_hdr end offset
| |
− | |-
| |
− | | 0x20
| |
− | | 4
| |
− | | Offset to runtime-generated module object (typically equal to .bss base)
| |
− | |}
| |
− |
| |
− | 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. If MOD is not located at image base, the value at offset 4 must still point to the MOD magic. In the case of .text being at image base, this implies that the first instruction can only be an unconditional branch over the offset literal.
| |
− |
| |
− | == nnSdk Version ==
| |
− | With [17.0.0+] this structure is normally located immediately after the [[#MOD|MOD0]]. This stores the nnSdk version.
| |
− |
| |
− | {| class="wikitable" border="1"
| |
− | |-
| |
− | ! Offset
| |
− | ! Size
| |
− | ! Description
| |
− | |-
| |
− | | 0x0
| |
− | | 0x4
| |
− | | Major
| |
− | |-
| |
− | | 0x4
| |
− | | 0x4
| |
− | | Minor
| |
− | |-
| |
− | | 0x8
| |
− | | 0x4
| |
− | | Micro
| |
− | |}
| |
| | | |
| = Arguments = | | = Arguments = |
− | [[Loader_services|Loader]] maps memory and writes the [[Loader_services#SetProgramArgument|arguments]] to {end of rwdata section specified by last SegmentHeader}. 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. | + | [[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: | | The structure located at argdata_addr is as follows: |