Понадобилось форкнуть процесс на дейвайсе с андроидом без использования либсишной обертки и тут началось...
Первое разочарование - fork в ядре не реализован, вместо него только clone, который вовсе не тот клон, что в libc, а совсем другой, с другими аргументами, причем порядок двух последних аргументов на некоторых архитектурах отличается. Но, как оказалось, достаточно все забить нулями и дернуть за сиськол. Так и сделал. Накидал тестовый шкриптец:
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
int main(void) {
long pid = syscall(__NR_clone, 0, NULL, NULL, NULL, NULL);
if(pid == 0) {
puts("child"); fflush(stdout);
while(1);
}
puts("parent");
return 0;
}
Родитель умирает, дите продолжает жить, все как и ожидалось. Код одинаково работает как на десктопе с интелом, так и на андроиде с армом. Ок, идем дальше.
Полностью избавляемся от libc.
SYS_WRITE = 64
SYS_EXIT = 93
SYS_CLONE = 220
add x9, sp, (8 * 2)
ldr x0, =0x000a746e65726150 // Parent
ldr x1, =0x00000a646c696843 // Child
stp x0, x1, [x9]
mov x8, SYS_CLONE
mov x0, xzr
mov x1, xzr
mov x2, xzr
mov x3, xzr
mov x4, xzr
svc 0
cbz x0, child // if(x0 == 0) goto child;
mov x8, SYS_WRITE
mov x0, 1
mov x1, x9
mov x2, 7
svc 0
exit:
mov x8, SYS_EXIT
mov x0, xzr
svc 0
child:
mov x8, SYS_WRITE
mov x0, 1
add x1, x9, 8
mov x2, 6
svc 0
loop:
b loop
Результат: выводится строка Parent, затем родитель мрет, дите мрет вместе с ним, даже не успевая вывести строку Child.
Напрашивается ответ - юзать wait, но в том то и дело, что ждать мне не нужно, задача стоит именно форкнуть и выйти. К тому же сишный код с оберткой syscall() работает так, как мне нужно, поэтому я полез смотреть кишки и вот че там было:
// syscall(__NR_clone, 0, NULL, NULL, NULL, NULL);
778: d2801b80 mov x0, #0xdc // #220
77c: 52800001 mov w1, #0x0 // #0
780: d2800002 mov x2, #0x0 // #0
784: d2800003 mov x3, #0x0 // #0
788: d2800004 mov x4, #0x0 // #0
78c: d2800005 mov x5, #0x0 // #0
790: 97ffffe0 bl 710 <syscall@plt>
......
// libc.so
00000000000179b0 <syscall@plt>:
179b0: f00005f0 adrp x16, d6000 <_DYNAMIC+0x390>
179b4: f9400a11 ldr x17, [x16,#16]
179b8: 91004210 add x16, x16, #0x10
179bc: d61f0220 br x17
......
000000000001c100 <syscall>:
1c100: aa0003e8 mov x8, x0
1c104: aa0103e0 mov x0, x1
1c108: aa0203e1 mov x1, x2
1c10c: aa0303e2 mov x2, x3
1c110: aa0403e3 mov x3, x4
1c114: aa0503e4 mov x4, x5
1c118: aa0603e5 mov x5, x6
1c11c: d4000001 svc #0x0
1c120: b140041f cmn x0, #0x1, lsl #12
1c124: da809400 cneg x0, x0, hi
1c128: 5403fb08 b.hi 24088 <__set_errno_internal>
1c12c: d65f03c0 ret
Т.е. код полностью идентичен, в x8 кладется номер сискола и все аргументы забиваются нулями, но дите не мрет вслед за родителем.
Для чистоты эксперимента накидал еще один шкриптец под x86-64:
SYS_EXIT = 60
SYS_CLONE = 56
SYS_WRITE = 1
mov $0x000a746e65726150, %rax # Parent
push %rax
mov $0x00000a646c696843, %rax # Child
push %rax
mov $SYS_CLONE, %rax
xor %rdi, %rdi
xor %rsi, %rsi
xor %rdx, %rdx
xor %r10, %r10
xor %r8, %r8
syscall
test %rax, %rax
jz child
mov $SYS_WRITE, %rax
mov $1, %rdi
lea 8(%rsp), %rsi
mov $7, %rdx
syscall
mov $SYS_EXIT, %rax
xor %rdi, %rdi
syscall
child:
mov $SYS_WRITE, %rax
mov $1, %rdi
mov %rsp, %rsi
mov $6, %rdx
syscall
loop:
jmp loop
Все работает как надо, родитель мрет, дите живет.
Такие дела.