Accessing Windows Shared Memory Mapped File from Linux

Questions about Wine on Linux
Locked
Zeebo
Level 1
Level 1
Posts: 6
Joined: Thu Aug 25, 2022 5:51 pm

Accessing Windows Shared Memory Mapped File from Linux

Post by Zeebo »

I found a project that shows a workaround to access a Windows Memory Mapped File from Linux.
https://github.com/LeonB/wineshm-go
Unfortuantely I don't know Go, but I have a pretty good understanding of the C wrappers in the project.

Even without looking at the code in the project it should be possible to figure out what I'm not understanding. I'll provide more background with code snippets that contain code that I added where I'm just trying anything I can think of, regardless of how much sense it makes.

snippet one compiles a windows exe:

Code: Select all

// +build none

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

#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "acdata.h"

int main(int argc, char** argv) {
    char *access_mode;
    DWORD access = 0;
    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    HANDLE maph;

    if (argc < 4) {
        fprintf(stderr, "not enough arguments\n");
        return 1;
    }
    access_mode = argv[2];
    while (*access_mode) {
        switch(*access_mode) {
            case 'r': access |= FILE_MAP_READ; break;
            case 'w': access |= FILE_MAP_WRITE; break;
        }
        access_mode++;
    }

    maph = OpenFileMapping(access, TRUE, argv[1]);
    /* printf("maph: %p\n", maph); */
    if (maph == NULL) {
        fprintf(stderr, "failed to open mapping %s: %s\n", argv[1], strerror(GetLastError()));
        return 1;
    }
    SetStdHandle(STD_INPUT_HANDLE, maph);
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    ZeroMemory(&pi, sizeof(pi));

    fprintf(stderr, "mmf handle: %p\n", maph);
    /*
    freopen(NULL, "fb", maph);
    */
    struct SPageFilePhysics* b = malloc(sizeof(struct SPageFilePhysics));
    read(maph, b, sizeof(struct SPageFilePhysics));
    //fread(b, sizeof(struct SPageFilePhysics), 1, maph);

    fprintf(stderr, "buf contains: %i\n", b->packetId);
    fprintf(stderr, "buf contains: %f\n", b->fuel);
    fprintf(stderr, "buf contains: %i\n", b->gear);

    /* We can't wait for the helper to exit, since waiting for a spawned unix
     * process does not work:
     * https://bugs.winehq.org/show_bug.cgi?id=22338 */

    if (!CreateProcess(argv[3], NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
        fprintf(stderr, "failed to launch second helper process: %s\n", strerror(GetLastError()));
    }
    return 0;
}
snippet two compiles a linux program which is spawned by the windows exe

Code: Select all

// +build none

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "acdata.h"

#define SOCKET_FD 1
#define SEND_FD 0

int main(int argc, char** argv) {
    /* Send FD 0 over the open socket in FD 1 */
    struct msghdr msg = {0};
    struct cmsghdr *cmsg;
    char buf[CMSG_SPACE(sizeof(int))];
    int *fdptr;
    msg.msg_control = buf;
    msg.msg_controllen = sizeof(buf);
    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = SOL_SOCKET;
    cmsg->cmsg_type = SCM_RIGHTS;
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    fdptr = (int *) CMSG_DATA(cmsg);
    fdptr[0] = SEND_FD;
    msg.msg_controllen = cmsg->cmsg_len;
    //if (sendmsg(SOCKET_FD, &msg, 0) < 0)
        //perror("sendmsg");

    int duplicated_stdin = dup(0);
    fprintf(stderr, "duplicated to: %i\n", duplicated_stdin);
    
    freopen(NULL, "fb", stdin);
    
    char e[25];
    char h[25];
    readlink("/proc/self/fd/0", &e, sizeof(e));
    readlink("/proc/self/fd/3", &e, sizeof(h));
    fprintf(stderr, "fd 0 link: %s\n", e);
    fprintf(stderr, "fd 3 link: %s\n", h);
    
    struct SPageFilePhysics* b = malloc(sizeof(struct SPageFilePhysics));
    fread(b, sizeof(struct SPageFilePhysics), 1, stdin);
    fprintf(stderr, "buf contains: %i\n", b->packetId);
    fprintf(stderr, "buf contains: %f\n", b->fuel);
    fprintf(stderr, "buf contains: %i\n", b->gear);
    
    read(duplicated_stdin, b, sizeof(struct SPageFilePhysics));
    fprintf(stderr, "buf contains: %i\n", b->packetId);
    fprintf(stderr, "buf contains: %f\n", b->fuel);
    fprintf(stderr, "buf contains: %i\n", b->gear);
    
    int g = 0;
    //lseek(stdin, sizeof(int)*4, SEEK_SET);
    fread(&g, sizeof(int), 1, stdin);
    fprintf(stderr, "read: %i\n", g);
    
    return 0;
}
then I'm running it like this and getting the output shown:

[code

protontricks --no-runtime --background-wineserver -c "wine /home/racedev/git/wineshm-go/assets/shmwrapper1.exe 'acpmf_physics' rw /home/racedev/git/wineshm-go/assets/shmwrapper2.bin" 244210
mmf handle: 00000040
buf contains: 0
buf contains: 0.000000
buf contains: 0
duplicated to: 3
fd 0 link: /dev/null
fd 3 link:
buf contains: 0
buf contains: 0.000000
buf contains: 0
buf contains: 0
buf contains: 0.000000
buf contains: 0
read: 0
05d8:fixme:ver:GetCurrentPackageId (000000000073FDB0 0000000000000000): stub
/home/racedev/.cache/protontricks/proton/Proton 6.3/bin/wineserver-keepalive: line 29: DEBUG warning: wait_for: recursively setting old_sigint_handler to wait_sigint_handler: running_trap = 1

[/code]

I feel like it must not be working since readlink shows that stdin is /dev/null. It should somehow point me to the memory mapped file from the window exe.

With that in mind i'm going to research why either SetStdHandle(STD_INPUT_HANDLE, maph); isn't working or why CreateProcess isn't sending over stdin correctly. This was written 7 years ago, so there could be new security bits that need to be enabled in wine, etc that weren't there before.

but if there's something else i'm not seeing, i don't know nearly enough about the Windows or Wine internals.
Zeebo
Level 1
Level 1
Posts: 6
Joined: Thu Aug 25, 2022 5:51 pm

Re: Accessing Windows Shared Memory Mapped File from Linux

Post by Zeebo »

I managed to get this to work:
https://github.com/Spacefreak18/wine-linux-shm-adapter

however, i tried to (with my shmwrapper3.c code) make a linux shared memory file available in a wine prefix. It works in my test code, but in practice, e.g. with CrewChief it does not.

If anyone has any ideas why that code does not work as intended, please feel free to reach out. Otherwise, i think the initial intent of this thread is resolved.
Locked