-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 函数
总体流程详解
- pe下调用__wine_init_unix_call初始化模块,该函数调用 NtQueryVirtualMemory
1
2
3
4
5
6NTSTATUS WINAPI __wine_init_unix_call(void)
{
return NtQueryVirtualMemory( GetCurrentProcess(), image_base(), MemoryWineUnixFuncs,
&__wine_unixlib_handle, sizeof(__wine_unixlib_handle), NULL );
} - 在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
20NTSTATUS 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;
......
} - 在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
27static 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;
} - pe下调用ntdll.dll中的__wine_unix_call函数,调用unix模块的函数表项,传入的参数第一个是数指针数组,第二个是调用函数元素的索引,第三个是函数参数
1
2
3
4
5
6
7static inline NTSTATUS __wine_unix_call( unixlib_handle_t handle, unsigned int code, void *args )
{
return __wine_unix_call_dispatcher( handle, code, args );
}具体实现
PE模块
- 在pe中的模块初始化时需要加载unix模块的函数指针
1
2
3
4
5
6
7
8static 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; - pe中的调用unix模块函数,需要通过WINE_UNIX_CALL宏调用
1
2
3
4
5
6
struct vaapi_create_params params = {0};
NTSTATUS status;
TRACE("Calling Unix side to create VA-API service...\n");
status = VAAPI_CALL(create, ¶ms);
unix模块函数
- 在unix模块中函数定义,必须是NTSTATUS类型返回值,参数必须是void*类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static 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);共用模块头文件定义
- 在头文件中定义参数结构体和函数索引枚举
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 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
};