LINUX.ORG.RU

Странности с sys_clone в андроиде

 , , ,


0

1

Понадобилось форкнуть процесс на дейвайсе с андроидом без использования либсишной обертки и тут началось...

Первое разочарование - 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

Все работает как надо, родитель мрет, дите живет.

Такие дела.

Ответ на: комментарий от joy4eg

Надеялся, что кто-то сможет объяснить эзотерику со смертью потомка в коде из листинга 2.

Nietzsche
() автор топика

Боюсь предположить зачем это может понадобится. Нужен живущий в фоне процесс — делай сервис.

a1batross ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Проблема заключалась способе запуска кода и особенностях adb.

Вот кусок из Makefile:

run: $(PROGRAM)
	adb push $(PROGRAM) /data/local/tmp
	adb shell '/data/local/tmp/$(PROGRAM) ; echo -e "\n\n--------------------\n\nexit code: $$?"'

В этом случае все потомки мрут вместе с родительским процессом.

Решается так:

$ adb shell
shell@zeroflte:/ $ /data/local/tmp/sys-clone
Parent.
Child.
shell@zeroflte:/ $
shell@zeroflte:/ $ top | head

User 12%, System 0%, IOW 0%, IRQ 0%
User 317 + Nice 0 + Sys 9 + Idle 2173 + IOW 0 + IRQ 0 + SIRQ 0 = 2499

  PID PR CPU% S  #THR     VSS     RSS PCY UID      Name
11157  1  12% R     1    152K      8K  fg shell    /data/local/tmp/sys-clone
11168  7   0% R     1   7396K   1788K  fg shell    top
17385  2   0% S     1      0K      0K  fg root     kworker/u16:11

Как видим, ребенок продолжает жить.

Nietzsche
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.