CRO0: Difference between revisions

From 3dbrew
Jump to navigation Jump to search
No edit summary
Kynex7510 (talk | contribs)
About hashes
 
(24 intermediate revisions by 8 users not shown)
Line 1: Line 1:
[[Category:File formats]]
[[Category:File formats]]
CRO with extension .cro is used for "DLLs". CRS with extension .crs is in the same format of CRO but storing the symbol information of the static module (the main application). The end of the file is aligned to a 0x1000-byte boundary with 0xCC bytes.
The CRO hashes before the magic are hashed themselves and verified against the [[CRR0|CRR]] hash table.
When the RO module loads the entire CRO into process memory (mapped in the 0x00100000-0x04000000 region), it relocates the mapped CRO data (eg. such that offsets can be dereferenced). The magic field is also changed to "FIXD" if fix level is not 0.
Upon loading, the RO module will look for export symbol "nnroAeabiAtexit_" to patch it to its import symbol "__aeabi_atexit".
For dumping symbols and loading a CRO into IDA, see [https://github.com/plutooo/ctr/] and [https://github.com/wwylele/IDA_plugin_CRO].
= Structure =


{| class="wikitable" border="1"
{| class="wikitable" border="1"
Line 6: Line 18:
!  Description
!  Description
|-
|-
| 0x0
| 0x00
| 0x80
| 0x20
| SHA-256 hash-table, verified by [[CRR0|CRR]]
| SHA-256 hash over the raw data from offset 0x80 to *(code offset), exclusive
|-
| 0x20
| 0x20
| SHA-256 hash over the raw data from *(code offset) to *(module name offset), exclusive
|-
| 0x40
| 0x20
| SHA-256 hash over the raw data from *(module name offset) to *(data offset), exclusive
|-
| 0x60
| 0x20
| SHA-256 hash over the raw data from *(data offset) to *(data offset + data size), exclusive (RO does NOT check this hash)
|-
|-
| 0x80
| 0x80
Line 16: Line 40:
| 0x84
| 0x84
| 0x04
| 0x04
| Code size
| Name offset
|-
|-
| 0x88
| 0x88
| 0x08
| 0x04
| Unknown
| Next loaded CRO pointer, set by RO during loading (Usually zero when the CRO is being loaded)
|-
| 0x8C
| 0x04
| Previous loaded CRO pointer, set by RO during loading
|-
|-
| 0x90
| 0x90
Line 27: Line 55:
|-
|-
| 0x94
| 0x94
| 0x1C
| 0x04
| .bss size
|-
| 0x98
| 0x04
| Fixed size, set by RO after fixing, used to keep track of the new size
|-
| 0x9C
| 0x04
| Unknown
| Unknown
|-
| 0xA0
| 0x04
| [[#Segment offset|Segment offset]] that is always the same as export symbol "nnroControlObject_". 0xFFFFFFFF in CRS
|-
| 0xA4
| 0x04
| [[#Segment offset|Segment offset]] for "OnLoad" function, which will be called when the module is initialized. Set to 0xFFFFFFFF if not exists.
|-
| 0xA8
| 0x04
| [[#Segment offset|Segment offset]] for "OnExit" function, which will be called when the module is finalized. Set to 0xFFFFFFFF if not exists.
|-
| 0xAC
| 0x04
| [[#Segment offset|Segment offset]] for "OnUnresolved" function, which will be called when an unresolved function is called. Set to 0xFFFFFFFF if not exists.
|-
|-
| 0xB0
| 0xB0
Line 40: Line 92:
| 0xB8
| 0xB8
| 0x04
| 0x04
| unk1 offset
| .data offset
|-
|-
| 0xBC
| 0xBC
| 0x04
| 0x04
| unk1 size
| .data size
|-
|-
| 0xC0
| 0xC0
Line 51: Line 103:
|-
|-
| 0xC4
| 0xC4
| 0xBC
| 0x04
| Module Name size
| Module Name size
|-
|-
| 0xC8
| 0xC8
| 0x04
| 0x04
| unk2 offset
| Segment Table offset
|-
|-
| 0xCC
| 0xCC
| 0x04
| 0x04
| unk2 size
| Segment Table num (size = num * 12)
|-
|-
| 0xD0
| 0xD0
| 0x04
| 0x04
| Export Table offset
| Named Export Table offset
|-
|-
| 0xD4
| 0xD4
| 0x04
| 0x04
| Export Table num (size = num * 8)
| Named Export Table num (size = num * 8)
|-
|-
| 0xD8
| 0xD8
| 0x04
| 0x04
| unk3 offset
| Indexed Export Table offset
|-
|-
| 0xDC
| 0xDC
| 0x04
| 0x04
| unk3 num
| Indexed Export Table num (size = num * 4)
|-
|-
| 0xE0
| 0xE0
Line 88: Line 140:
| 0xE8
| 0xE8
| 0x04
| 0x04
| Export Info offset
| Export Trie offset
|-
|-
| 0xEC
| 0xEC
| 0x04
| 0x04
| Export Info num (size = num * 8)
| Export Trie num (size = num * 8)
|-
|-
| 0xF0
| 0xF0
| 0x04
| 0x04
| unk4 offset
| Import Module Table offset
|-
|-
| 0xF4
| 0xF4
| 0x04
| 0x04
| unk4 num
| Import Module Table num (size = num * 20)
|-
|-
| 0xF8
| 0xF8
| 0x04
| 0x04
| unk5 offset
| Import Relocations offset
|-
|-
| 0xFC
| 0xFC
| 0x04
| 0x04
| unk5 num (size = num * 12)
| Import Relocations num (size = num * 12)
|-
|-
| 0x100
| 0x100
| 0x04
| 0x04
| Import Table 1 offset
| Named Import Table offset
|-
|-
| 0x104
| 0x104
| 0x04
| 0x04
| Import Table 1 num (size = num * 4)
| Named Import Table num (size = num * 8)
|-
|-
| 0x108
| 0x108
| 0x04
| 0x04
| Import Table 2 offset
| Indexed Import Table offset
|-
|-
| 0x10C
| 0x10C
| 0x04
| 0x04
| Import Table 2 num (size = num * 4)
| Indexed Import Table num (size = num * 8)
|-
|-
| 0x110
| 0x110
| 0x04
| 0x04
| Import Table 3 offset
| Anonymous Import Table offset
|-
|-
| 0x114
| 0x114
| 0x04
| 0x04
| Import Table 3 num (size = num * 4)
| Anonymous Import Table num (size = num * 8)
|-
|-
| 0x118
| 0x118
Line 144: Line 196:
| 0x120
| 0x120
| 0x04
| 0x04
| unk8 offset
| [[#Fields 0x120, 0x130|Unknown Relocations]] base parameter offset
|-
|-
| 0x124
| 0x124
| 0x04
| 0x04
| unk8 num
| [[#Fields 0x120, 0x130|Unknown Relocations]] base parameter num (size = num * 8)
|-
|-
| 0x128
| 0x128
| 0x04
| 0x04
| Import Info offset
| Internal Relocations offset
|-
|-
| 0x12C
| 0x12C
| 0x04
| 0x04
| Import Info num (size = num * 12)
| Internal Relocations num (size = num * 12)
|-
|-
| 0x130
| 0x130
| 0x04
| 0x04
| unk9 offset
| [[#Fields 0x120, 0x130|Unknown Relocations]] offset
|-
|-
| 0x134
| 0x134
| 0x04
| 0x04
| unk9 num
| [[#Fields 0x120, 0x130|Unknown Relocations]] num (size = num * 12)
|}
|}


CRO with extension .cro is used for "DLLs". CRS with extension .crs can be used for storing "DLL" symbols as well. The end of the file is aligned to a 0x1000-byte boundary with 0xCC bytes.
== Segment offset ==
CRO0 files are usually stored under "romfs:/cro/".


The first hash-table entry hashes the 0x100-byte header following the hash-table. The following hash-table entries hash the sections specified in the header.
{| class="wikitable" border="1"
!  Bits
!  Description
|-
| 0-3
| Segment index for table
|-
| 4-31
| Offset into segment
|}


When the RO module loads the entire CRO into process memory(mapped in the 0x00100000-0x04000000 region), it modifies the mapped CRO data. The magic field is also changed to "FIXD".
== Segment Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| Segment offset
|-
| 0x4
| 0x4
| Segment size
|-
| 0x8
| 0x4
| Segment id (0 = .text, 1 = .rodata, 2 = .data, 3 = .bss)
|}


Upon loading, the RO module will look for symbol "__aeabi_atexit" or "nnroAeabiAtexit_".
== Named Export Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| Name offset
|-
| 0x4
| 0x4
| [[#Segment offset (4 bytes)|Segment offset]] for export
|}
 
== Indexed Export Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| [[#Segment offset (4 bytes)|Segment offset]] for export
|}
 
== Export Trie entry ==
 
Each entry represents a node in the trie:
 
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x2
| Flags (bits 0-2: bit index, bits 3-15: char index)
|-
| 0x2
| 0x2
| Left node info (bit 0-14: node index, bit 15: is leaf)
|-
| 0x4
| 0x2
| Right node info (same as left node)
|-
| 0x6
| 0x2
| Index for the named exports table
|}
 
The root node is a dummy node, with the right child being a leaf and pointing to the empty string. Hence the search must be performed starting from the left child.
 
Search is performed by extracting the bit of the input using informations encoded in the flags. If the bit is 0 or the offsets are out of bounds the search continues on the left, else on the right.
The search always yields a result, which may not match the input string, so a final strcmp call is required.
 
== Named Import Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| Name offset
|-
| 0x4
| 0x4
| Offset of the head of a linear list that contains the patches for this import
|}
 
== Indexed Import Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| index of the export symbol
|-
| 0x4
| 0x4
| Offset of the head of a linear list that contains the patches for this import
|}
 
== Anonymous Import Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| [[#Segment offset (4 bytes)|Segment offset]] of the export symbol
|-
| 0x4
| 0x4
| Offset of the head of a linear list that contains the patches for this import
|}
 
== Import Module Table entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| Module name offset
|-
| 0x4
| 0x4
| Indexed import num
|-
| 0x8
| 0x4
| Offset of the head of a sub list in Indexed Import Table
|-
| 0xC
| 0x4
| Anonymous import num
|-
| 0x10
| 0x4
| Offset of the head of a sub list in Anonymous Import Table
|}
 
== Relocation entry ==
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| [[#Segment offset (4 bytes)|Segment offset]] for output.
|-
| 0x4
| 0x1
| Relocation type (R_ARM_NONE = 0, R_ARM_ABS32 = 2, R_ARM_REL32 = 3, R_ARM_THM_PC22 = 10, R_ARM_CALL = 28, R_ARM_JUMP24 = 29, R_ARM_TARGET1 = 38, R_ARM_PREL31 = 42)
|-
| 0x5
| 0x1
| For import relocations, non-zero if last entry; for internal relocations, this is the referred segment index
|-
| 0x6
| 0x1
| For import relocations, 1 is written to first entry if all symbols loaded successfully; unknown (padding?) for internal relocations
|-
| 0x7
| 0x1
| Unknown (padding?)
|-
| 0x8
| 0x4
| addend
|}
 
Relocation code from RO:
 
static Result writeRelocation(u32* out, u32 relocType, u32 addend, u32 base, u32 inputPtr) {
    const s32 branchOffset = inputPtr - base;
    u32 offset = base + addend - inputPtr;
    if (relocType == R_ARM_NONE)
        return 0;
    if (relocType == R_ARM_ABS32 || relocType == R_ARM_TARGET1) {
        *out = base + addend;
        return 0;
    }
    if (relocType == R_ARM_REL32) {
        *out = offset;
        return 0;
    }
    if (relocType == R_ARM_THM_PC22) {
        // +-4MB.
        if (branchOffset >= 0x400000 || branchOffset <= -0x400000)
            return 0xD9012C23;
        if (base & 1) {
            *out = (((offset >> 12) | 0xF000) << 16) | ((offset << 4) >> 5) | 0xF800;
        } else {
            if (offset & 2)
                offset += 2;
            *out = ((offset >> 12) | 0xF000) << 16 | ((offset << 4) >> 5) | 0xE800;
        }
        return 0;
    }
    if (relocType == R_ARM_CALL) {
        // +-32MB.
        if (branchOffset >= 0x2000000 || branchOffset <= -0x2000000)
            return 0xD9012C23;
        if (base & 1) {
            *out = ((offset << 23) & 0x1000000) | ((offset << 6) >> 8) | 0xFA000000;
        } else {
            *out = 0xEB000000 | ((offset << 6) >> 8);
        }
        return 0;
    }
    if (relocType == R_ARM_JUMP24) {
        // +-32MB.
        if (branchOffset >= 0x2000000 || branchOffset <= -0x2000000 || (base & 1))
            return 0xD9012C23;
        *out = (*out & 0xFF000000) | ((offset << 6) >> 8);
        return 0;
    }
    if (relocType == R_ARM_PREL31) {
        *out = addend + ((base << 1) >> 1) - inputPtr;
        return 0;
    }
    return 0xD9012C22;
}
 
== Fields 0x120, 0x130 ==
 
Field 0x130 is a list of relocations which serve an unknown purpose. Field 0x120 is a list of entries related to the previous list:
 
{| class="wikitable" border="1"
!  Offset
!  Size
!  Description
|-
| 0x0
| 0x4
| Offset to a relocation entry in field 0x130
|-
| 0x4
| 0x4
| Segment offset used for calculating the "base" parameter
|}
 
= nnroControlObject =
 
Signature:
 
Result nnroControlObject(void* arg, u32 type);
 
{| class="wikitable" border="1"
!  Type
!  Description
|-
| 0
| Write address of "nnroEitNode_" to memory pointed by arg (*(u32*)arg = &nnroEitNode_)
|-
| 1
| Set global in CRO to arg, where arg is the CRO end (g_CRO_end = (u32)arg)
|-
| 2
| Write CRO end to memory pointed by arg (*(u32*)arg = g_CRO_end)
|-
| 3
| Write begin, end of function list (C initializers?) to memory pointed by arg (*(u32*)arg = (u32)listBegin, ((u32*)arg)[1] = (u32)listEnd)
|-
| 4
| Write begin, end of function list (C++ initializers?) to memory pointed by arg (*(u32*)arg = (u32)listBegin, ((u32*)arg)[1] = (u32)listEnd)
|}
 
"EitNode" are nodes of a linked list containing informations about the main executable + loaded modules:
 
{| class="wikitable" border="1"
!  Index Word
!  Type
!  Description
|-
| 0
| EitNode*
| Pointer to the previous element of the list
|-
| 1
| EitNode*
| Pointer to the next element of the list
|-
| 2
| u32
| Module code start, for the main executable this is .text base, for CROs this is the value at CRO+0xB0
|-
| 3
| u32
| Module code end (start + size), for the main executable the size is the size of all mapped sections, for CROs it's the value at CRO+0xB4
|-
| 4
| u32*
| Unknown, seems to be an array of u32, probably holds debugging informations
|-
| 5
| u32*
| This points at the end of the previous array
|-
| 6
| ElfNode* (?)
| Unknown, this is only set for the main executable node, and it points to itself
|}

Latest revision as of 15:19, 26 December 2025


CRO with extension .cro is used for "DLLs". CRS with extension .crs is in the same format of CRO but storing the symbol information of the static module (the main application). The end of the file is aligned to a 0x1000-byte boundary with 0xCC bytes.

The CRO hashes before the magic are hashed themselves and verified against the CRR hash table.

When the RO module loads the entire CRO into process memory (mapped in the 0x00100000-0x04000000 region), it relocates the mapped CRO data (eg. such that offsets can be dereferenced). The magic field is also changed to "FIXD" if fix level is not 0.

Upon loading, the RO module will look for export symbol "nnroAeabiAtexit_" to patch it to its import symbol "__aeabi_atexit".

For dumping symbols and loading a CRO into IDA, see [1] and [2].

Structure

Offset Size Description
0x00 0x20 SHA-256 hash over the raw data from offset 0x80 to *(code offset), exclusive
0x20 0x20 SHA-256 hash over the raw data from *(code offset) to *(module name offset), exclusive
0x40 0x20 SHA-256 hash over the raw data from *(module name offset) to *(data offset), exclusive
0x60 0x20 SHA-256 hash over the raw data from *(data offset) to *(data offset + data size), exclusive (RO does NOT check this hash)
0x80 0x04 Magic "CRO0"
0x84 0x04 Name offset
0x88 0x04 Next loaded CRO pointer, set by RO during loading (Usually zero when the CRO is being loaded)
0x8C 0x04 Previous loaded CRO pointer, set by RO during loading
0x90 0x04 File size
0x94 0x04 .bss size
0x98 0x04 Fixed size, set by RO after fixing, used to keep track of the new size
0x9C 0x04 Unknown
0xA0 0x04 Segment offset that is always the same as export symbol "nnroControlObject_". 0xFFFFFFFF in CRS
0xA4 0x04 Segment offset for "OnLoad" function, which will be called when the module is initialized. Set to 0xFFFFFFFF if not exists.
0xA8 0x04 Segment offset for "OnExit" function, which will be called when the module is finalized. Set to 0xFFFFFFFF if not exists.
0xAC 0x04 Segment offset for "OnUnresolved" function, which will be called when an unresolved function is called. Set to 0xFFFFFFFF if not exists.
0xB0 0x04 Code offset
0xB4 0x04 Code size
0xB8 0x04 .data offset
0xBC 0x04 .data size
0xC0 0x04 Module Name offset
0xC4 0x04 Module Name size
0xC8 0x04 Segment Table offset
0xCC 0x04 Segment Table num (size = num * 12)
0xD0 0x04 Named Export Table offset
0xD4 0x04 Named Export Table num (size = num * 8)
0xD8 0x04 Indexed Export Table offset
0xDC 0x04 Indexed Export Table num (size = num * 4)
0xE0 0x04 Export Strings offset
0xE4 0x04 Export Strings size
0xE8 0x04 Export Trie offset
0xEC 0x04 Export Trie num (size = num * 8)
0xF0 0x04 Import Module Table offset
0xF4 0x04 Import Module Table num (size = num * 20)
0xF8 0x04 Import Relocations offset
0xFC 0x04 Import Relocations num (size = num * 12)
0x100 0x04 Named Import Table offset
0x104 0x04 Named Import Table num (size = num * 8)
0x108 0x04 Indexed Import Table offset
0x10C 0x04 Indexed Import Table num (size = num * 8)
0x110 0x04 Anonymous Import Table offset
0x114 0x04 Anonymous Import Table num (size = num * 8)
0x118 0x04 Import Strings offset
0x11C 0x04 Import Strings size
0x120 0x04 Unknown Relocations base parameter offset
0x124 0x04 Unknown Relocations base parameter num (size = num * 8)
0x128 0x04 Internal Relocations offset
0x12C 0x04 Internal Relocations num (size = num * 12)
0x130 0x04 Unknown Relocations offset
0x134 0x04 Unknown Relocations num (size = num * 12)

Segment offset

Bits Description
0-3 Segment index for table
4-31 Offset into segment

Segment Table entry

Offset Size Description
0x0 0x4 Segment offset
0x4 0x4 Segment size
0x8 0x4 Segment id (0 = .text, 1 = .rodata, 2 = .data, 3 = .bss)

Named Export Table entry

Offset Size Description
0x0 0x4 Name offset
0x4 0x4 Segment offset for export

Indexed Export Table entry

Offset Size Description
0x0 0x4 Segment offset for export

Export Trie entry

Each entry represents a node in the trie:

Offset Size Description
0x0 0x2 Flags (bits 0-2: bit index, bits 3-15: char index)
0x2 0x2 Left node info (bit 0-14: node index, bit 15: is leaf)
0x4 0x2 Right node info (same as left node)
0x6 0x2 Index for the named exports table

The root node is a dummy node, with the right child being a leaf and pointing to the empty string. Hence the search must be performed starting from the left child.

Search is performed by extracting the bit of the input using informations encoded in the flags. If the bit is 0 or the offsets are out of bounds the search continues on the left, else on the right. The search always yields a result, which may not match the input string, so a final strcmp call is required.

Named Import Table entry

Offset Size Description
0x0 0x4 Name offset
0x4 0x4 Offset of the head of a linear list that contains the patches for this import

Indexed Import Table entry

Offset Size Description
0x0 0x4 index of the export symbol
0x4 0x4 Offset of the head of a linear list that contains the patches for this import

Anonymous Import Table entry

Offset Size Description
0x0 0x4 Segment offset of the export symbol
0x4 0x4 Offset of the head of a linear list that contains the patches for this import

Import Module Table entry

Offset Size Description
0x0 0x4 Module name offset
0x4 0x4 Indexed import num
0x8 0x4 Offset of the head of a sub list in Indexed Import Table
0xC 0x4 Anonymous import num
0x10 0x4 Offset of the head of a sub list in Anonymous Import Table

Relocation entry

Offset Size Description
0x0 0x4 Segment offset for output.
0x4 0x1 Relocation type (R_ARM_NONE = 0, R_ARM_ABS32 = 2, R_ARM_REL32 = 3, R_ARM_THM_PC22 = 10, R_ARM_CALL = 28, R_ARM_JUMP24 = 29, R_ARM_TARGET1 = 38, R_ARM_PREL31 = 42)
0x5 0x1 For import relocations, non-zero if last entry; for internal relocations, this is the referred segment index
0x6 0x1 For import relocations, 1 is written to first entry if all symbols loaded successfully; unknown (padding?) for internal relocations
0x7 0x1 Unknown (padding?)
0x8 0x4 addend

Relocation code from RO:

static Result writeRelocation(u32* out, u32 relocType, u32 addend, u32 base, u32 inputPtr) {
    const s32 branchOffset = inputPtr - base;
    u32 offset = base + addend - inputPtr;

    if (relocType == R_ARM_NONE)
        return 0;

    if (relocType == R_ARM_ABS32 || relocType == R_ARM_TARGET1) {
        *out = base + addend;
        return 0;
    }

    if (relocType == R_ARM_REL32) {
        *out = offset;
        return 0;
    }

    if (relocType == R_ARM_THM_PC22) {
        // +-4MB.
        if (branchOffset >= 0x400000 || branchOffset <= -0x400000)
            return 0xD9012C23;

        if (base & 1) {
            *out = (((offset >> 12) | 0xF000) << 16) | ((offset << 4) >> 5) | 0xF800;
        } else {
            if (offset & 2)
                offset += 2;

            *out = ((offset >> 12) | 0xF000) << 16 | ((offset << 4) >> 5) | 0xE800;
        }

        return 0;
    }

    if (relocType == R_ARM_CALL) {
        // +-32MB.
        if (branchOffset >= 0x2000000 || branchOffset <= -0x2000000)
            return 0xD9012C23;

        if (base & 1) {
            *out = ((offset << 23) & 0x1000000) | ((offset << 6) >> 8) | 0xFA000000;
        } else {
            *out = 0xEB000000 | ((offset << 6) >> 8);
        }

        return 0;
    }

    if (relocType == R_ARM_JUMP24) {
        // +-32MB.
        if (branchOffset >= 0x2000000 || branchOffset <= -0x2000000 || (base & 1))
            return 0xD9012C23;

        *out = (*out & 0xFF000000) | ((offset << 6) >> 8);
        return 0;
    }

    if (relocType == R_ARM_PREL31) {
        *out = addend + ((base << 1) >> 1) - inputPtr;
        return 0;
    }

    return 0xD9012C22;
}

Fields 0x120, 0x130

Field 0x130 is a list of relocations which serve an unknown purpose. Field 0x120 is a list of entries related to the previous list:

Offset Size Description
0x0 0x4 Offset to a relocation entry in field 0x130
0x4 0x4 Segment offset used for calculating the "base" parameter

nnroControlObject

Signature:

Result nnroControlObject(void* arg, u32 type);
Type Description
0 Write address of "nnroEitNode_" to memory pointed by arg (*(u32*)arg = &nnroEitNode_)
1 Set global in CRO to arg, where arg is the CRO end (g_CRO_end = (u32)arg)
2 Write CRO end to memory pointed by arg (*(u32*)arg = g_CRO_end)
3 Write begin, end of function list (C initializers?) to memory pointed by arg (*(u32*)arg = (u32)listBegin, ((u32*)arg)[1] = (u32)listEnd)
4 Write begin, end of function list (C++ initializers?) to memory pointed by arg (*(u32*)arg = (u32)listBegin, ((u32*)arg)[1] = (u32)listEnd)

"EitNode" are nodes of a linked list containing informations about the main executable + loaded modules:

Index Word Type Description
0 EitNode* Pointer to the previous element of the list
1 EitNode* Pointer to the next element of the list
2 u32 Module code start, for the main executable this is .text base, for CROs this is the value at CRO+0xB0
3 u32 Module code end (start + size), for the main executable the size is the size of all mapped sections, for CROs it's the value at CRO+0xB4
4 u32* Unknown, seems to be an array of u32, probably holds debugging informations
5 u32* This points at the end of the previous array
6 ElfNode* (?) Unknown, this is only set for the main executable node, and it points to itself