NSO: Difference between revisions

No edit summary
No edit summary
 
(22 intermediate revisions by 9 users not shown)
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.