IOS/Syscalls
Internally, IOS uses a syscall table that is stored toward the end of the binary. The exact address varies with version of IOS, but there are two methods to locate it:
ELF header:
The second-to-last program header is the syscall table. For example:
$ arm-eabi-readelf -l ~/wii/system_updates/boot2.elf | tail -2 LOAD 0x0230d5 0xffff7f60 0xffff7f60 0x00a88 0x00a88 RW 0x10 LOAD 0x023b5d 0xffff8a00 0xffff8a00 0x00000 0x071e8 RW 0x20
or
elf_header:134C01A0 elf32_phdr < 1, 0x230D5, syscall_base, syscall_base, 0xA88, 0xA88, 6, 0x10> elf_header:134C01C0 elf32_phdr < 1, 0x23B5D, current_thread_context, current_thread_context, 0, 0x71E8, 6, 0x20>
Syscall Handler: The second vector is the invalid instruction handler, which is used to implement syscalls:
kernel:FFFF0000 LDR PC, =_reset kernel:FFFF0004 LDR PC, =starlet_syscall_handler kernel:FFFF1894 starlet_syscall_handler ; CODE XREF: start�j kernel:FFFF1894 E9 CD 7F FF STMFA SP, {R0-LR}^ kernel:FFFF1898 E1 4F 80 00 MRS R8, SPSR kernel:FFFF189C E5 8D 80 00 STR R8, [SP,#arg_0] kernel:FFFF18A0 E5 8D E0 40 STR LR, [SP,#arg_40] kernel:FFFF18A4 E5 1E A0 04 LDR R10, [LR,#-4] kernel:FFFF18A8 E3 CA 9D 7F BIC R9, R10, #0x1FC0 kernel:FFFF18AC E5 9F 84 9C LDR R8, =0xE6000010 kernel:FFFF18B0 E3 C9 90 20 BIC R9, R9, #0x20 kernel:FFFF18B4 E1 59 00 08 CMP R9, R8 kernel:FFFF18B8 1A 00 00 1F BNE loc_FFFF193C kernel:FFFF18BC E1 A0 A2 CA MOV R10, R10,ASR#5 kernel:FFFF18C0 E2 0A A0 FF AND R10, R10, #0xFF kernel:FFFF18C4 E3 5A 00 75 CMP R10, #0x75 ; 'u' kernel:FFFF18C8 CA 00 00 11 BGT loc_FFFF1914 kernel:FFFF18CC E1 A0 80 0D MOV R8, SP kernel:FFFF18D0 E3 A0 B0 1F MOV R11, #0x1F kernel:FFFF18D4 E1 21 F0 0B MSR CPSR_c, R11 kernel:FFFF18D8 E5 98 80 44 LDR R8, [R8,#arg_44] kernel:FFFF18DC E5 9F B4 70 LDR R11, =syscall_flags_maybe kernel:FFFF18E0 E7 9B B1 0A LDR R11, [R11,R10,LSL#2] kernel:FFFF18E4 E0 8D D1 0B ADD SP, SP, R11,LSL#2 kernel:FFFF18E8 kernel:FFFF18E8 loc_FFFF18E8 ; CODE XREF: starlet_syscall_handler+68�j kernel:FFFF18E8 E3 5B 00 00 CMP R11, #0 kernel:FFFF18EC 0A 00 00 03 BEQ loc_FFFF1900 kernel:FFFF18F0 E5 3D 90 04 LDR R9, [SP,#var_4]! kernel:FFFF18F4 E5 28 90 04 STR R9, [R8,#-4]! kernel:FFFF18F8 E2 4B B0 01 SUB R11, R11, #1 kernel:FFFF18FC EA FF FF F9 B loc_FFFF18E8 kernel:FFFF1900 loc_FFFF1900 ; CODE XREF: starlet_syscall_handler+58�j kernel:FFFF1900 E1 A0 D0 08 MOV SP, R8 kernel:FFFF1904 E5 9F B4 4C LDR R11, =syscall_base kernel:FFFF1908 E7 9B B1 0A LDR R11, [R11,R10,LSL#2] kernel:FFFF190C E1 A0 E0 0F MOV LR, PC kernel:FFFF1910 E1 2F FF 1B BX R11 kernel:FFFF1914 loc_FFFF1914 ; CODE XREF: starlet_syscall_handler+34�j kernel:FFFF1914 E3 A0 B0 DB MOV R11, #0xDB ; '¦' kernel:FFFF1918 E1 21 F0 0B MSR CPSR_c, R11 kernel:FFFF191C E5 9D B0 00 LDR R11, [SP,#arg_0] kernel:FFFF1920 E1 6F F0 0B MSR SPSR_cxsf, R11 kernel:FFFF1924 E1 A0 E0 00 MOV LR, R0 kernel:FFFF1928 E9 DD 7F FF LDMED SP, {R0-LR}^ kernel:FFFF192C E1 A0 00 00 NOP kernel:FFFF1930 E1 A0 00 0E MOV R0, LR kernel:FFFF1934 E5 9D E0 40 LDR LR, [SP,#arg_40] kernel:FFFF1938 E1 B0 F0 0E MOVS PC, LR kernel:FFFF193C loc_FFFF193C ; CODE XREF: starlet_syscall_handler+24�j kernel:FFFF193C E5 9F D4 18 LDR SP, =current_thread_context kernel:FFFF1940 E5 9D D0 00 LDR SP, [SP,#arg_0] kernel:FFFF1944 E5 8D E0 40 STR LR, [SP,#arg_40] kernel:FFFF1948 E3 A0 E0 06 MOV LR, #6 kernel:FFFF194C E5 8D E0 50 STR LR, [SP,#arg_50] kernel:FFFF1950 E2 8D D0 04 ADD SP, SP, #4 kernel:FFFF1954 E9 4D 7F FF STMFD SP, {R0-LR}^ kernel:FFFF1958 E1 4F B0 00 MRS R11, SPSR kernel:FFFF195C E5 0D B0 04 STR R11, [SP,#-4+arg_0] kernel:FFFF1960 EA 00 00 C9 B schedule kernel:FFFF1960 ; End of function starlet_syscall_handler
Syscalls are invoked by way of the invalid instruction handler; syscalls take the form 0xE6000010 | (syscall_num << 5). (E.g. E6000010 is syscall 0, E60006D0 is syscall 0x36, etc.)
tmbinc has written an IDAPython script which can take a database that has "syscall_base" defined, and transform the references to it into more meaningful things -- it is available here: IOS/Syscall_IDAPython
(please feel free to contribute your own findings!)
ID # | Internal name | Description | Return value |
---|---|---|---|
0 | u32 thread_create( u32 (*proc)(void* arg), u8 priority, u32* stack, u32 stacksize, void* arg, BOOL autostart) | Creates a thread | Returns threadid |
1 | thread_join | ||
2 | thread_cancel | ||
3 | get_tid | ||
4 | get_pid | ||
5 | thread_continue | ||
6 | thread_stop | ||
7 | thread_yield | ||
8 | thread_get_priority | ||
9 | thread_set_priority | ||
a | message_queue_create | ||
b | message_queue_destroy | ||
c | message_queue_send | ||
d | message_queue_send_now | ||
e | message_queue_receive | ||
f | RegisterEventHandler | ||
10 | UnregisterEventHandler | ||
11 | timer_create | ||
12 | timer_restart | ||
13 | timer_stop | ||
14 | timer_destroy | ||
15 | timer_now | ||
16 | heap_create | ||
17 | heap_destroy | ||
18 | heap_alloc | ||
19 | heap_alloc_aligned | ||
1a | heap_free | ||
1b | BOOL device_register(char* device, u32 messagequeue) | Registers device to the device tree, so it can be opened (from Starlet and PPC) | Returns 0 on success, else error |
1c | u32 device_open(char* device, int mode) | Similar to IOS_Open on PPC, except now internal to the IOS system | Returns an fd |
1d | device_close | ||
1e | device_read | ||
1f | device_write | ||
20 | device_seek | ||
21 | device_ioctl | ||
22 | device_ioctlv | ||
23 | device_open_async | ||
24 | device_close_async | ||
25 | device_read_async | ||
26 | device_write_async | ||
27 | device_seek_async | ||
28 | device_ioctl_async | ||
29 | device_ioctlv_async | ||
2a | syscall_2a | seems to set the return value of a received message | |
2b | SetUID | ||
2c | get_hmac_queue_for_pid | ||
2d | SetGID | ||
2e | lookup_GID_maybe | ||
2f | cc_ahbMemFlush | ||
30 | syscall_ahbMemFlush_wrapper | ||
31 | software_IRQ_31 | seems to enable hardware interrupts for device nr 31 | |
32 | software_irq_18 | seems to enable hardware interrupts for device nr 18 | |
33 | software_IRQ_7_or_8(id) | seems to enable hardware interrupts for device nr 7 if id==0, else device nr 8 | |
34 | software_IRQ(id) | seems to enable hardware interrupts for device nr. id | |
35 | _return_0 | ||
36 | syscall_36 | ||
37 | syscall_37 | ||
38 | iobuf_log_header_info | ||
39 | iobuf_log_buffer_info | ||
3a | syscall_3a | ||
3b | syscall_3b | ||
3c | syscall_3c | ||
3d | syscall_3d | ||
3e | syscall_3e | ||
3f | void sync_before_read(u32 address, u32 size) | Invalidates dcache, and something (probably related to flushing memory) | |
40 | sync_after_write | ||
41 | ppc_boot | ||
42 | ios_boot | ||
43 | syscall_43 | ||
44 | int syscall_assert_di_reset | Clears bit 10 of 0xD800194 | Returns 0 on success, -1 on error |
45 | int syscall_deassert_di_reset | Enables bit 10 of 0xD800194 | Returns 0 on success, -1 on error |
46 | BOOL syscall_check_di_reset | Checks bit 10 of 0xD800194 | Returns 1 on reset asserted, 0 on (deasserted or error) |
47 | zero_r0_r1 | ||
48 | set_r0_1_r1_0 | ||
49 | get_boot_vector | ||
4a | syscall_4a | ||
4b | kernel_debug_print | ||
4c | kernel_set_version | ||
4d | kernel_get_version | ||
4e | poke_E0_1 | ||
4f | virt_to_phys | ||
50 | syscall_50 | ||
51 | syscall_51 | ||
52 | syscall_52 | ||
53 | syscall_53 | ||
54 | syscall_54 | ||
55 | get_bc_flag | ||
56 | poke_gpios | ||
57 | syscall_57 | ||
58 | call_poke_debug_port | ||
59 | create_key | ||
5a | destroy_key | ||
5b | es_syscall_5b | ||
5c | es_syscall_5c | ||
5d | set_public_key | ||
5e | es_syscall_5e | ||
5f | es_syscall_5f | ||
60 | es_syscall_60 | ||
61 | get_keyid | ||
62 | es_syscall_62 | ||
63 | es_syscall_63 | ||
64 | sha_async | ||
65 | sha | ||
66 | aes_async | ||
67 | aes | ||
68 | es_syscall_68 | ||
69 | es_syscall_69 | ||
6a | es_syscall_6a | ||
6b | hmac | ||
6c | hmac_async | ||
6d | es_syscall_6d | ||
6e | get_ng_cert | ||
6f | key_set_permission_mask | ||
70 | es_syscall_70 | ||
71 | es_syscall_71 | ||
72 | es_syscall_72 | ||
73 | es_syscall_73 | ||
74 | es_syscall_74 | ||
75 | ?? | ||
76 | ?? |