Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[wip] syscall(linux): forkAndExecInChild #959

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

luoliwoshang
Copy link
Contributor

@luoliwoshang luoliwoshang commented Jan 14, 2025

result

before use exec.command in linux
got

panic("todo: syscall.forkAndExecInChild")

now

root@be00d9b1c2c9:~/llgo/_demo/commandrun# llgo run .
len: 62
data: total 4
-rw-r--r-- 1 root root 258 Jan 16 10:52 commandrun.go

env

root@be00d9b1c2c9:~/llgo/_demo/commandrun# uname -a
Linux be00d9b1c2c9 6.10.14-linuxkit #1 SMP Fri Nov 29 17:22:03 UTC 2024 aarch64 aarch64 aarch64 GNU/Linux

question

in ci, the demo will fail,but in my local docker env , can run success
https://github.com/goplus/llgo/actions/runs/12826009988/job/35765102523?pr=959

other question(resolved)

in linux, go use clone to fork a child process,current is temp use fork to create a child process
src/syscall/exec_linux.go

	locked = true
	if clone3 != nil {
		pid, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3))
	} else {
		flags |= uintptr(SIGCHLD)
		if runtime.GOARCH == "s390x" {
			// On Linux/s390, the first two arguments of clone(2) are swapped.
			pid, err1 = rawVforkSyscall(SYS_CLONE, 0, flags)
		} else {
			pid, err1 = rawVforkSyscall(SYS_CLONE, flags, 0)
		}
	}

in above code,only pass two param to clone, but in the signature of c,have three params

int clone (int (*__fn) (void *__arg), void *__child_stack, int __flags, void *__arg, ...) ;

and if we only pass the flags & arg through call c.clone

err1 = Errno(os.Clone(nil, 0, c.Int(flags), 0))
fmt.Println(err1)
fmt.Println(Errno(os.Errno()))
// llgo:type C
type fn func(__arg c.Pointer) c.Int

//go:linkname Clone C.clone
func Clone(__fn fn, __childStack c.Pointer, __flags c.Int, __arg uintptr, __llgo_va_list ...any) c.Int

will got

Unknown error -1
Invalid argument

its error output like use clone without child_stack , got Invalid argument

int main() {
    const int STACK_SIZE = 1024 * 1024;
    void *stack = malloc(STACK_SIZE);
    if (!stack) {
        perror("malloc");
        return 1;
    }

    int flags = SIGCHLD;
    // int pid = clone(child_function, stack + STACK_SIZE, flags, NULL);  work normally
    int pid = clone(child_function, NULL, flags, NULL); // got clone: Invalid argument
    if (pid == -1) {
        perror("clone");
        free(stack);
        return 1;
    }

    printf("Parent process PID: %d, Child PID: %d\n", getpid(), pid);
    free(stack);
    return 0;
}

in the under asm code,it have some operate

// func rawVforkSyscall(trap, a1, a2 uintptr) (r1, err uintptr)
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-40
	MOVQ	a1+8(FP), DI
	MOVQ	a2+16(FP), SI
	MOVQ	$0, DX
	MOVQ	$0, R10
	MOVQ	$0, R8
	MOVQ	$0, R9
	MOVQ	trap+0(FP), AX	// syscall entry
	POPQ	R12 // preserve return address
	SYSCALL
	PUSHQ	R12
	CMPQ	AX, $0xfffffffffffff001
	JLS	ok2
	MOVQ	$-1, r1+24(FP)
	NEGQ	AX
	MOVQ	AX, err+32(FP)
	RET
ok2:
	MOVQ	AX, r1+24(FP)
	MOVQ	$0, err+32(FP)

Copy link

codecov bot commented Jan 14, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 81.58%. Comparing base (e016e92) to head (1e89fe1).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #959   +/-   ##
=======================================
  Coverage   81.58%   81.58%           
=======================================
  Files          53       53           
  Lines        9050     9050           
=======================================
  Hits         7383     7383           
  Misses       1516     1516           
  Partials      151      151           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@luoliwoshang luoliwoshang force-pushed the sys/ExecInChild branch 2 times, most recently from 39ecf9e to 8dee005 Compare January 14, 2025 14:25
@luoliwoshang luoliwoshang marked this pull request as draft January 14, 2025 16:03
@luoliwoshang luoliwoshang marked this pull request as ready for review January 14, 2025 16:12
@luoliwoshang luoliwoshang changed the title syscall(linux): forkAndExecInChild [wip] syscall(linux): forkAndExecInChild Jan 15, 2025
@MeteorsLiu
Copy link
Contributor

MeteorsLiu commented Jan 17, 2025

The wrapper is accurate, the reason why the function is different from Go's is that libc wraps Clone in its way.

Raw Syscall may like(x86-64):

 long clone(unsigned long flags, void *stack,
                      int *parent_tid, int *child_tid,
                      unsigned long tls);

In libc, it may like:

 int clone(int (*fn)(void *_Nullable), void *stack, int flags,
                 void *_Nullable arg, ...  /* pid_t *_Nullable parent_tid,
                                              void *_Nullable tls,
                                              pid_t *_Nullable child_tid */ );

Acutally, the stack can be nullable, however, libc does a force verification for stack:

Code from gblic

ENTRY (__clone)
	cmp	r0, 0		/* @fn can't be NULL.  */
	and	r1,r1,-4	/* @child_stack be 4 bytes aligned per ABI.  */
	cmp.ne	r1, 0		/* @child_stack can't be NULL.  */
	bz	L (__sys_err)

In libc, stack cannot be nullable because of calling the fn.

When you don't need to call fn, the stack param can be zero.

@luoliwoshang
Copy link
Contributor Author

In libc, stack cannot be nullable because of calling the fn.

When you don't need to call fn, the stack param can be zero.

Good investgation! We may use the c.syscall to call the clone function from core like go vForkSyscall , to avoid libc's wrapper check.

Copy link

qiniu-x bot commented Jan 17, 2025

[Git-flow] Hi @luoliwoshang, There are some suggestions for your information:


Rebase suggestions

  • Following commits have duplicated messages

    debug

    debug

    debug

    debug

    debug

    debug

    debug

    debug

    debug

Which seems insignificant, recommend to use git rebase command to reorganize your PR.

For other git-flow instructions, recommend refer to these examples.

If you have any questions about this comment, feel free to raise an issue here:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants