Changes

940 bytes added ,  21:29, 25 September 2024
no edit summary
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.