0%

【Linux技术分享】-wine下的WINE_UNIX_CALL使用

-wine下的WINE_UNIX_CALL使用

问题概述

wine的高版本比如wine10上PE文件调用unix模块函数是如何通过WINE_UNIX_CALL调用?

总体流程

总体流程概览

PE 模块 (如 dxva2.dll)
    └── WINE_UNIX_CALL(func, params)
        │
        ▼
    ntdll.dll (__wine_unix_call)
        │
        ├── 保存 Windows 上下文
        ├── 切换到 Unix 环境 (通过系统调用或 thunk)
        └── 调用 Unix 侧函数表项
            │
            ▼
    Unix 模块 (如 dxva2.so)
        └── 对应的 unix_xxx 函数

总体流程详解

  1. pe下调用__wine_init_unix_call初始化模块,该函数调用 NtQueryVirtualMemory
    1
    2
    3
    4
    5
    6
    NTSTATUS WINAPI __wine_init_unix_call(void)
    {
    return NtQueryVirtualMemory( GetCurrentProcess(), image_base(), MemoryWineUnixFuncs,
    &__wine_unixlib_handle, sizeof(__wine_unixlib_handle), NULL );
    }

  2. 在NtQueryVirtualMemory这个函数会查询当前PE模块(dxva2.dll)相关联的Unix模块(dxva2.so)的信息。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    NTSTATUS WINAPI NtQueryVirtualMemory( HANDLE process, LPCVOID addr,
    MEMORY_INFORMATION_CLASS info_class,
    PVOID buffer, SIZE_T len, SIZE_T *res_len )
    {
    ......
    case MemoryWineUnixFuncs:
    case MemoryWineUnixWow64Funcs:
    if (len != sizeof(unixlib_handle_t)) return STATUS_INFO_LENGTH_MISMATCH;
    if (process == GetCurrentProcess())
    {
    void *module = (void *)addr;
    const void *funcs = NULL;

    status = get_builtin_unix_funcs( module, info_class == MemoryWineUnixWow64Funcs, &funcs );
    if (!status) *(unixlib_handle_t *)buffer = (UINT_PTR)funcs;
    return status;
    }
    return STATUS_INVALID_HANDLE;
    ......
    }
  3. 在get_builtin_unix_funcs函数中会调用获取目标 .so 文件中的 __wine_unix_call_funcs 的函数指针数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    static NTSTATUS get_builtin_unix_funcs( void *module, BOOL wow, const void **funcs )
    {
    const char *ptr_name = wow ? "__wine_unix_call_wow64_funcs" : "__wine_unix_call_funcs";
    sigset_t sigset;
    NTSTATUS status = STATUS_DLL_NOT_FOUND;
    struct builtin_module *builtin;

    server_enter_uninterrupted_section( &virtual_mutex, &sigset );
    LIST_FOR_EACH_ENTRY( builtin, &builtin_modules, struct builtin_module, entry )
    {
    if (builtin->module != module) continue;
    if (builtin->unix_path && !builtin->unix_handle)
    {
    builtin->unix_handle = dlopen( builtin->unix_path, RTLD_NOW );
    if (!builtin->unix_handle)
    WARN_(module)( "failed to load %s: %s\n", debugstr_a(builtin->unix_path), dlerror() );
    }
    if (builtin->unix_handle)
    {
    *funcs = dlsym( builtin->unix_handle, ptr_name );
    status = *funcs ? STATUS_SUCCESS : STATUS_ENTRYPOINT_NOT_FOUND;
    }
    break;
    }
    server_leave_uninterrupted_section( &virtual_mutex, &sigset );
    return status;
    }
  4. pe下调用ntdll.dll中的__wine_unix_call函数,调用unix模块的函数表项,传入的参数第一个是数指针数组,第二个是调用函数元素的索引,第三个是函数参数
    1
    2
    3
    4
    5
    6
    7
    static inline NTSTATUS __wine_unix_call( unixlib_handle_t handle, unsigned int code, void *args )
    {
    return __wine_unix_call_dispatcher( handle, code, args );
    }
    #endif

    #define WINE_UNIX_CALL(code,args) __wine_unix_call( __wine_unixlib_handle, (code), (args) )

    具体实现

    PE模块

  5. 在pe中的模块初始化时需要加载unix模块的函数指针
    1
    2
    3
    4
    5
    6
    7
    8
    static BOOL WINAPI load_vaapi_funcs(INIT_ONCE *once, void *param, void **context)
    {
    __wine_init_unix_call();
    return TRUE;
    }
    static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
    if (!InitOnceExecuteOnce(&init_once, load_vaapi_funcs, NULL, NULL) || !__wine_unixlib_handle)
    return E_FAIL;
  6. pe中的调用unix模块函数,需要通过WINE_UNIX_CALL宏调用
    1
    2
    3
    4
    5
    6
    #define VAAPI_CALL(func, params) WINE_UNIX_CALL(unix_ ## func, params)
    struct vaapi_create_params params = {0};
    NTSTATUS status;

    TRACE("Calling Unix side to create VA-API service...\n");
    status = VAAPI_CALL(create, &params);

unix模块函数

  1. 在unix模块中函数定义,必须是NTSTATUS类型返回值,参数必须是void*类型
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    static NTSTATUS unix_vaapi_create(void *args)
    {
    struct vaapi_create_params *params = (struct vaapi_create_params *)args;

    TRACE("Unix call: creating VA-API service\n");
    params->service = vaapi_videoservice_create();
    TRACE("Unix call: service = %p\n", params->service);
    return params->service ? S_OK : S_FALSE;
    }

    const unixlib_entry_t __wine_unix_call_funcs[] =
    {
    unix_vaapi_create,
    };

    C_ASSERT(ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count);

    共用模块头文件定义

  2. 在头文件中定义参数结构体和函数索引枚举
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include "wine/unixlib.h"
    /* Parameter struct shared between PE side and Unix side */
    struct vaapi_create_params
    {
    void *service; /* OUT: IWineVideoService pointer */
    };

    /* Unix call function table index */
    enum unix_funcs
    {
    unix_create,
    unix_funcs_count
    };