Unix fork from a Winelib DLL

Questions about Wine on Linux
Post Reply
abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Unix fork from a Winelib DLL

Post by abcdefg » Mon Aug 02, 2021 11:28 am

I am having trouble calling a Linux shared library function that uses fork() from an application running under Wine.

Here is the basic setup:
  • I have a Win32 program called callfork_demo.exe that loads the function call_fork() from the DLL callfork.dll. It then prints some output, calls call_fork(), then prints some more output, and terminates.
  • The DLL being loaded, callfork.dll, is a Winelib DLL. It exposes just one function, call_fork(), which is just a wrapper around the unistd.h fork() function with some additional printf's for debugging.
I've attached the code below for more details.

Here is the basic behavior I am trying to create with any interleaving of the last six lines:

[target, pre-fork] fork check!
[proxy lib, pre-fork] calling call_fork()!
[linux lib, pre-fork] pid = 24
<--- non-determinism starts here --->
[linux lib, post-fork] I'm the parent
[proxy lib, post-fork] I'm the parent coming back
[target, post-fork] I'm the parent in the target, finishing up!
[linux lib, post-fork] I'm the child
[proxy lib, post-fork] I'm the child coming back
[target, post-fork] I'm the child in the target, finishing up!

Here is the behavior I am currently getting, however:

[target, pre-fork] fork check!
[proxy lib, pre-fork] calling call_fork()!
[linux lib, pre-fork] pid = 24
<--- non-determinism starts here --->
[linux lib, post-fork] I'm the parent
[proxy lib, post-fork] I'm the parent coming back
[target, post-fork] I'm the parent in the target, finishing up!
[linux lib, post-fork] I'm the child
[proxy lib, post-fork] I'm the child coming back

Note that the difference here is the parent returns to the target just fine and finishes execution, but the child never makes it back to the target.

Basically, I just want to be able to fork a Windows process running under Wine, and my current method to do so is by wrapping the unistd.h library into a Winelib DLL and calling fork from it. I am sure there is something I am misunderstanding about why my current approach isn't working, so if someone can point out my error and explain why, it would be much appreciated.

I'll attach the full code and build script below. Thank you!

callfork_demo.c

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

typedef int(*call_fork_t)();

#pragma clang optimize off
#pragma GCC            optimize("O0")

int main(int argc, char **argv) {
  HINSTANCE HInst = LoadLibrary("callfork.dll");
  if(HInst == NULL) {
	  printf("ERROR: Could not load library\n");
	  return 1;
  }
  call_fork_t call_fork = (call_fork_t)GetProcAddress(HInst,"call_fork");
  if(call_fork == NULL) {
	  printf("ERROR: Could not load functions\n");
	  return 1;
  }

  printf("[target, pre-fork] fork check!\n");

  int pid = call_fork();
  printf("[target, post-fork] mypid = %d\n",pid);

  printf("[target, post-fork] Returning!\n");
  return 0;
}
proxy_callfork.c

Code: Select all

#include "callfork.h"

int proxy_call_fork() {
	printf("[proxy lib, pre-fork] calling call_fork()!\n");
	int pid = call_fork();
	printf("[proxy lib, post-fork] coming back, ret val = %d, pid = %d\n",pid,getpid());
	return pid;
}
callfork.c

Code: Select all

#include "callfork.h"

int call_fork() {
	printf("[linux lib, pre-fork] pid = %d\n",getpid());
	int pid = fork();
	printf("[linux lib, post-fork] ret val = %d, pid = %d\n",pid,getpid());
	return pid;
}
callfork.h

Code: Select all

#ifndef CALLFORK_H
#define CALLFORK_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int call_fork();

#endif

build.sh

Code: Select all

#!/bin/bash

MINGWCC=i686-w64-mingw32-gcc-win32
CC=clang
WCC=winegcc

# Build libraries
$CC -m32 -fPIC -c callfork.c
$CC -m32 -fPIC -shared -o libcallfork.so callfork.o
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`

# Build proxy DLL
$CC -m32 -fPIC -c proxy_callfork.c
$WCC -m32 -fPIC -shared -L. -B/home/osboxes/wine/tools/winebuild --sysroot=/home/osboxes/wine ./proxy_callfork.dll.spec -o proxy_callfork.dll.so proxy_callfork.o /home/osboxes/wine/libs/port/libwine_port.a -lcallfork

cp proxy_callfork.dll.so callfork.dll

# Build target
$MINGWCC -m32 -fPIC -o callfork_demo.exe callfork_demo.c

# Clean
rm -f *.o

madewokherd
Level 4
Level 4
Posts: 105
Joined: Mon Jun 02, 2008 5:03 pm
Contact:

Re: Unix fork from a Winelib DLL

Post by madewokherd » Mon Aug 02, 2021 10:43 pm

I don't think this can work correctly while using nearly any win32 API (even indirectly through printf) after calling fork, because wineserver isn't set up to account for it. That includes the printf in your main executable.

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Tue Aug 03, 2021 8:24 am

Thank you for taking the time to reply! Can you explain what specifically wineserver doesn't account for here? Are you saying that if the wine process forks then just Win32 API calls can't get processed? And can you explain why as well? My thought was that by forking a process under wine, the wine server would just behave as if two applications were running on wine at once. Am I incorrect in thinking that the wine server can handle calls from multiple applications running under wine at once? And is there a difference between that and two processes being managed by wine at once? Thank you, again

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Tue Aug 03, 2021 9:40 am

As a follow up, I was able to get the behavior I wanted by pre-starting the wine server and having the child sleep for three seconds after the call to fork.

callfork.c becomes:

Code: Select all

#include "callfork.h"

int call_fork() {
	printf("[linux lib, pre-fork] pid = %d\n",getpid());
	int pid = fork();
	if(pid == 0) sleep(3);
	printf("[linux lib, post-fork] ret val = %d, pid = %d\n",pid,getpid());
	return pid;
}
And the output becomes:
[target, pre-fork] fork check!
[proxy lib, pre-fork] calling call_fork()!
[linux lib, pre-fork] pid = 24
[linux lib, post-fork] I'm the parent
[proxy lib, post-fork] I'm the parent coming back
[target, post-fork] I'm the parent in the target, finishing up!
[linux lib, post-fork] I'm the child
[proxy lib, post-fork] I'm the child coming back
[target, post-fork] I'm the child in the target, finishing up!

However, I still want to find a deterministic way to do this, and most of all I want to actually understanding why this worked and the original doesn't. I think this goes back to my question of whether or not the wine server can handle requests from multiple applications/processes running under wine at once. What seems to have happened here is we just forced the wineserver to stay awake at all times and then "collapsed" the calls to the wine server into just a linear, de facto deterministic sequence. Since this worked, it suggests to me that the wine server really cannot handle requests from multiple processes at once, but this would surprise me given everything I know about wine. Any clarification?

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Tue Aug 03, 2021 12:38 pm

To follow up on this again, actually I only get the desired behavior non-deterministically with the sleep-trick. Often times the output will look like this:

[target, pre-fork] fork check!
[proxy lib, pre-fork] calling call_fork()!
[linux lib, pre-fork] pid = 24
[linux lib, post-fork] I'm the parent
[proxy lib, post-fork] I'm the parent coming back
[target, post-fork] I'm the parent in the target, finishing up!
[linux lib, post-fork] I'm the child
[proxy lib, post-fork] I'm the child coming back
[target, post-fork] I'm the chil

Where the child process will return to the wine code and then only execute part of the way and then freeze. Can anyone explain why this might be happening?

julliard
Level 1
Level 1
Posts: 9
Joined: Sat Mar 30, 2013 12:22 pm

Re: Unix fork from a Winelib DLL

Post by julliard » Wed Aug 04, 2021 6:00 am

Each thread needs its own connection to the wineserver. This is setup on process/thread creation.

If you fork, you have two processes sharing the same pipe to the server. This can't possibly work, since the server has no way of knowing which process the messages come from.

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Wed Aug 04, 2021 6:41 am

Ah thank you that is extremely helpful information.

What happens when an ordinary windows application creates a new process? Surely wine can handle multiple windows processes, so I think I would just have to implement the same thing that wine does to handle standard new windows process creation except for this fork. I would be willing to patch my version of Wine to enable this functionality if necessary, and I am going to start working on how to implement it. I would love if anyone has ideas to push me in the right direction.

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Wed Aug 04, 2021 6:49 am

Also, am I correct in thinking that the wineserver is only invoked (and thus that pipe is only communicated on) when a Win32 API call is made? For my real Winelib DLL, I actually only need one thread running in the Windows application at a time and the other thread always stays inside the linux shared library that is linked to the Winelib DLL. Would this latter thread never try to communicate with the wineserver if it never touches any Win32 code and only stays inside the linux shared library?

abcdefg
Level 1
Level 1
Posts: 9
Joined: Mon Aug 02, 2021 10:12 am

Re: Unix fork from a Winelib DLL

Post by abcdefg » Thu Aug 05, 2021 12:02 pm

My last post makes it clear that what I am actually trying to do isn't represented well by the example I gave in the original post so I'll make a new that is more faithful to what I'm actually trying to do.

Post Reply