From 508a7286343dfd517463d83db226d9f14589a677 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Sep 2024 14:00:40 -0600 Subject: [PATCH 01/67] ws2_32/Tests: Add tests for send buffering. (cherry picked from commit 8c02af2bbf8c5fd39a27b678e6fc9bea50cb7a85) CW-Bug-Id: #24269 --- dlls/ws2_32/tests/sock.c | 117 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 3 deletions(-) diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index c390b186956..e260afa9144 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -6856,9 +6856,9 @@ static void test_write_events(struct event_test_ctx *ctx) if (!broken(1)) { - /* Windows will never send less than buffer_size bytes here, but Linux - * may do a short write. */ - while ((ret = send(server, buffer, buffer_size, 0)) > 0); + /* Windows will never send less than buffer_size bytes here. */ + while ((ret = send(server, buffer, buffer_size, 0)) > 0) + ok(ret == buffer_size, "got %d.\n", ret); ok(ret == -1, "got %d\n", ret); ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u\n", WSAGetLastError()); @@ -14241,6 +14241,116 @@ static void test_broadcast(void) closesocket(s); } +struct test_send_buffering_data +{ + int buffer_size; + int sent_size; + SOCKET server; + char *buffer; +}; + +static DWORD WINAPI test_send_buffering_thread(void *arg) +{ + struct test_send_buffering_data *d = arg; + int ret; + + d->sent_size = 0; + while ((ret = send(d->server, d->buffer, d->buffer_size, 0)) > 0) + { + todo_wine ok(ret == d->buffer_size, "got %d.\n", ret); + d->sent_size += ret; + } + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u.\n", WSAGetLastError()); + ok(d->sent_size, "got 0.\n"); + ret = CancelIoEx((HANDLE)d->server, NULL); + todo_wine ok(!ret && GetLastError() == ERROR_NOT_FOUND, "got ret %d, error %lu.\n", ret, GetLastError()); + ret = CancelIo((HANDLE)d->server); + ok(ret, "got error %lu.\n", GetLastError()); + shutdown(d->server, SD_BOTH); + closesocket(d->server); + return 0; +} + +static void test_send_buffering(void) +{ + static const char test_data[] = "abcdefg01234567"; + + struct test_send_buffering_data d; + int ret, recv_size, i; + SOCKET client; + HANDLE thread; + + d.buffer_size = 1024 * 1024 * 50; + d.buffer = malloc(d.buffer_size); + + for (i = 0; i < d.buffer_size; ++i) + d.buffer[i] = test_data[i % sizeof(test_data)]; + + tcp_socketpair(&client, &d.server); + set_blocking(client, FALSE); + set_blocking(d.server, FALSE); + + d.sent_size = 0; + while ((ret = send(d.server, d.buffer, d.buffer_size, 0)) > 0) + { + todo_wine ok(ret == d.buffer_size, "got %d.\n", ret); + d.sent_size += ret; + } + ok(ret == -1, "got %d\n", ret); + ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u.\n", WSAGetLastError()); + ok(d.sent_size, "got 0.\n"); + closesocket(d.server); + + recv_size = 0; + while ((ret = recv(client, d.buffer, d.buffer_size, 0)) > 0 || WSAGetLastError() == WSAEWOULDBLOCK) + { + if (ret < 0) + continue; + for (i = 0; i < ret; ++i) + { + if (d.buffer[i] != test_data[(recv_size + i) % sizeof(test_data)]) + break; + } + ok(i == ret, "data mismatch.\n"); + recv_size += ret; + ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); + } + ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); + ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + closesocket(client); + + /* Test with the other thread which terminates before the data is actually sent. */ + for (i = 0; i < d.buffer_size; ++i) + d.buffer[i] = test_data[i % sizeof(test_data)]; + + tcp_socketpair(&client, &d.server); + set_blocking(client, FALSE); + set_blocking(d.server, FALSE); + + thread = CreateThread(NULL, 0, test_send_buffering_thread, &d, 0, NULL); + WaitForSingleObject(thread, INFINITE); + CloseHandle(thread); + + recv_size = 0; + while ((ret = recv(client, d.buffer, d.buffer_size, 0)) > 0 || WSAGetLastError() == WSAEWOULDBLOCK) + { + if (ret < 0) + continue; + for (i = 0; i < ret; ++i) + { + if (d.buffer[i] != test_data[(recv_size + i) % sizeof(test_data)]) + break; + } + ok(i == ret, "data mismatch.\n"); + recv_size += ret; + ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); + } + ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); + ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + closesocket(client); +} + START_TEST( sock ) { int i; @@ -14324,6 +14434,7 @@ START_TEST( sock ) test_connect_udp(); test_tcp_sendto_recvfrom(); test_broadcast(); + test_send_buffering(); /* There is apparently an obscure interaction between this test and * test_WSAGetOverlappedResult(). From 832a61ee317d5fb94de3f9f84e919ef43dc9e73c Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Sep 2024 14:04:31 -0600 Subject: [PATCH 02/67] ntdll: Avoid short writes on nonblocking sockets. (cherry picked from commit 535c8a84c1ec4391786d8be6f87d683e20223b12) CW-Bug-Id: #24269 --- dlls/ntdll/unix/socket.c | 60 +++++++++++++++++++++++++++++++++++++--- dlls/ws2_32/tests/sock.c | 8 +++--- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 329da8cd4fd..7603177e2e4 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1175,10 +1175,62 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi /* If we had a short write and the socket is nonblocking (and we are * not trying to force the operation to be asynchronous), return - * success. Windows actually refuses to send any data in this case, - * and returns EWOULDBLOCK, but we have no way of doing that. */ - if (status == STATUS_DEVICE_NOT_READY && async->sent_len) - status = STATUS_SUCCESS; + * success, pretened we've written everything to the socket and queue writing + * remaining data. Windows never reports partial write in this case and queues + * virtually unlimited amount of data for background write in this case. */ + if (status == STATUS_DEVICE_NOT_READY && async->sent_len && async->iov_cursor < async->count) + { + struct iovec *iov = async->iov + async->iov_cursor; + SIZE_T data_size, async_size, addr_size; + struct async_send_ioctl *rem_async; + unsigned int i, iov_count; + IO_STATUS_BLOCK *rem_io; + char *p; + + TRACE( "Short write, queueing remaining data.\n" ); + data_size = 0; + iov_count = async->count - async->iov_cursor; + for (i = 0; i < iov_count; ++i) + data_size += iov[i].iov_len; + + addr_size = max( 0, async->addr_len ); + async_size = offsetof( struct async_send_ioctl, iov[1] ) + data_size + addr_size + + sizeof(IO_STATUS_BLOCK); + if (!(rem_async = (struct async_send_ioctl *)alloc_fileio( async_size, async_send_proc, handle ))) + { + status = STATUS_NO_MEMORY; + } + else + { + /* Use a local copy of socket fd so the async send works after socket handle is closed. */ + rem_async->count = 1; + p = (char *)rem_async + offsetof( struct async_send_ioctl, iov[1] ); + rem_async->iov[0].iov_base = p; + rem_async->iov[0].iov_len = data_size; + for (i = 0; i < iov_count; ++i) + { + memcpy( p, iov[i].iov_base, iov[i].iov_len ); + p += iov[i].iov_len; + } + rem_async->unix_flags = async->unix_flags; + memcpy( p, async->addr, addr_size ); + rem_async->addr = (const struct WS_sockaddr *)p; + p += addr_size; + rem_async->addr_len = async->addr_len; + rem_async->iov_cursor = 0; + rem_async->sent_len = 0; + rem_io = (IO_STATUS_BLOCK *)p; + p += sizeof(IO_STATUS_BLOCK); + status = sock_send( handle, NULL, NULL, NULL, rem_io, fd, rem_async, TRUE ); + if (status == STATUS_PENDING) status = STATUS_SUCCESS; + if (!status) + { + async->sent_len += data_size; + async->iov_cursor = async->count; + } + else ERR( "Remaining write queue failed, status %#x.\n", status ); + } + } information = async->sent_len; if (!NT_ERROR(status) && status != STATUS_PENDING) diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index e260afa9144..826000613e5 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14257,7 +14257,7 @@ static DWORD WINAPI test_send_buffering_thread(void *arg) d->sent_size = 0; while ((ret = send(d->server, d->buffer, d->buffer_size, 0)) > 0) { - todo_wine ok(ret == d->buffer_size, "got %d.\n", ret); + ok(ret == d->buffer_size, "got %d.\n", ret); d->sent_size += ret; } ok(ret == -1, "got %d\n", ret); @@ -14294,7 +14294,7 @@ static void test_send_buffering(void) d.sent_size = 0; while ((ret = send(d.server, d.buffer, d.buffer_size, 0)) > 0) { - todo_wine ok(ret == d.buffer_size, "got %d.\n", ret); + ok(ret == d.buffer_size, "got %d.\n", ret); d.sent_size += ret; } ok(ret == -1, "got %d\n", ret); @@ -14317,7 +14317,7 @@ static void test_send_buffering(void) ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); } ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); - ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + todo_wine ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); closesocket(client); /* Test with the other thread which terminates before the data is actually sent. */ @@ -14347,7 +14347,7 @@ static void test_send_buffering(void) ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); } ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); - ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + todo_wine ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); closesocket(client); } From ce15d367049f2b256171bd79318d50b9720e389b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Sep 2024 14:13:21 -0600 Subject: [PATCH 03/67] ntdll: Locally duplicated socket fd for background send. (cherry picked from commit d9a123b6cf556a19d68c3a50cb595acdcf994923) CW-Bug-Id: #24269 --- dlls/ntdll/unix/socket.c | 11 ++++++++++- dlls/ws2_32/tests/sock.c | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index 7603177e2e4..c1d2a55a6bb 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -130,6 +130,7 @@ struct async_send_ioctl unsigned int sent_len; unsigned int count; unsigned int iov_cursor; + int fd; struct iovec iov[1]; }; @@ -1096,7 +1097,8 @@ static BOOL async_send_proc( void *user, ULONG_PTR *info, unsigned int *status ) if (*status == STATUS_ALERTED) { - if ((*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL ))) + needs_close = FALSE; + if ((fd = async->fd) == -1 && (*status = server_get_unix_fd( async->io.handle, 0, &fd, &needs_close, NULL, NULL ))) return TRUE; *status = try_send( fd, async ); @@ -1109,6 +1111,7 @@ static BOOL async_send_proc( void *user, ULONG_PTR *info, unsigned int *status ) return FALSE; } *info = async->sent_len; + if (async->fd != -1) close( async->fd ); release_fileio( &async->io ); return TRUE; } @@ -1203,6 +1206,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi else { /* Use a local copy of socket fd so the async send works after socket handle is closed. */ + rem_async->fd = dup( fd ); rem_async->count = 1; p = (char *)rem_async + offsetof( struct async_send_ioctl, iov[1] ); rem_async->iov[0].iov_base = p; @@ -1243,7 +1247,10 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi } if (status != STATUS_PENDING) + { + if (async->fd != -1) close( async->fd ); release_fileio( &async->io ); + } if (wait_handle) status = wait_async( wait_handle, options & FILE_SYNCHRONOUS_IO_ALERT ); return status; @@ -1283,6 +1290,7 @@ static NTSTATUS sock_ioctl_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap async->iov[i].iov_len = buffers[i].len; } } + async->fd = -1; async->unix_flags = unix_flags; async->addr = addr; async->addr_len = addr_len; @@ -1301,6 +1309,7 @@ NTSTATUS sock_write( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, if (!(async = (struct async_send_ioctl *)alloc_fileio( async_size, async_send_proc, handle ))) return STATUS_NO_MEMORY; + async->fd = -1; async->count = 1; async->iov[0].iov_base = (void *)buffer; async->iov[0].iov_len = length; diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 826000613e5..7d07b93533d 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14317,7 +14317,7 @@ static void test_send_buffering(void) ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); } ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); - todo_wine ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); closesocket(client); /* Test with the other thread which terminates before the data is actually sent. */ From 1eea23186075bf6cd4d3bc2e6d110e69894468c2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 12 Sep 2024 20:22:52 -0600 Subject: [PATCH 04/67] ntdll: Don't cancel background socket sends. (cherry picked from commit 3d1c2b0ede4616efab703c24c3e96572d900b73b) CW-Bug-Id: #24269 --- dlls/ntdll/unix/socket.c | 15 ++++++++------- dlls/ws2_32/tests/sock.c | 2 +- server/async.c | 23 ++++++++++++++++++----- server/fd.c | 10 +++++----- server/file.h | 3 ++- server/protocol.def | 4 +++- server/sock.c | 12 +++++++----- 7 files changed, 44 insertions(+), 25 deletions(-) diff --git a/dlls/ntdll/unix/socket.c b/dlls/ntdll/unix/socket.c index c1d2a55a6bb..9fb0f82042e 100644 --- a/dlls/ntdll/unix/socket.c +++ b/dlls/ntdll/unix/socket.c @@ -1142,7 +1142,7 @@ static void sock_save_icmp_id( struct async_send_ioctl *async ) } static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, - IO_STATUS_BLOCK *io, int fd, struct async_send_ioctl *async, int force_async ) + IO_STATUS_BLOCK *io, int fd, struct async_send_ioctl *async, unsigned int server_flags ) { HANDLE wait_handle; BOOL nonblocking; @@ -1151,7 +1151,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi SERVER_START_REQ( send_socket ) { - req->force_async = force_async; + req->flags = server_flags; req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); status = wine_server_call( req ); wait_handle = wine_server_ptr_handle( reply->wait ); @@ -1173,7 +1173,7 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi status = try_send( fd, async ); hack_update_status( handle, &status ); - if (status == STATUS_DEVICE_NOT_READY && (force_async || !nonblocking)) + if (status == STATUS_DEVICE_NOT_READY && ((server_flags & SERVER_SOCKET_IO_FORCE_ASYNC) || !nonblocking)) status = STATUS_PENDING; /* If we had a short write and the socket is nonblocking (and we are @@ -1225,7 +1225,8 @@ static NTSTATUS sock_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi rem_async->sent_len = 0; rem_io = (IO_STATUS_BLOCK *)p; p += sizeof(IO_STATUS_BLOCK); - status = sock_send( handle, NULL, NULL, NULL, rem_io, fd, rem_async, TRUE ); + status = sock_send( handle, NULL, NULL, NULL, rem_io, fd, rem_async, + SERVER_SOCKET_IO_FORCE_ASYNC | SERVER_SOCKET_IO_SYSTEM ); if (status == STATUS_PENDING) status = STATUS_SUCCESS; if (!status) { @@ -1297,7 +1298,7 @@ static NTSTATUS sock_ioctl_send( HANDLE handle, HANDLE event, PIO_APC_ROUTINE ap async->iov_cursor = 0; async->sent_len = 0; - return sock_send( handle, event, apc, apc_user, io, fd, async, force_async ); + return sock_send( handle, event, apc, apc_user, io, fd, async, force_async ? SERVER_SOCKET_IO_FORCE_ASYNC : 0 ); } NTSTATUS sock_write( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, @@ -1319,7 +1320,7 @@ NTSTATUS sock_write( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, async->iov_cursor = 0; async->sent_len = 0; - return sock_send( handle, event, apc, apc_user, io, fd, async, 1 ); + return sock_send( handle, event, apc, apc_user, io, fd, async, SERVER_SOCKET_IO_FORCE_ASYNC ); } @@ -1483,7 +1484,7 @@ static NTSTATUS sock_transmit( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, SERVER_START_REQ( send_socket ) { - req->force_async = 1; + req->flags = SERVER_SOCKET_IO_FORCE_ASYNC; req->async = server_async( handle, &async->io, event, apc, apc_user, iosb_client_ptr(io) ); status = wine_server_call( req ); wait_handle = wine_server_ptr_handle( reply->wait ); diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 7d07b93533d..99e81ae88b7 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14347,7 +14347,7 @@ static void test_send_buffering(void) ok(recv_size <= d.sent_size, "got ret %d, recv_size %d, sent_size %d.\n", ret, recv_size, d.sent_size); } ok(!ret && !WSAGetLastError(), "got ret %d, error %u.\n", ret, WSAGetLastError()); - todo_wine ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); + ok(recv_size == d.sent_size, "got %d, expected %d.\n", recv_size, d.sent_size); closesocket(client); } diff --git a/server/async.c b/server/async.c index fa21285bc05..01e536c6a6f 100644 --- a/server/async.c +++ b/server/async.c @@ -57,6 +57,7 @@ struct async unsigned int canceled :1; /* have we already queued cancellation for this async? */ unsigned int unknown_status :1; /* initial status is not known yet */ unsigned int blocking :1; /* async is blocking */ + unsigned int is_system :1; /* background system operation not affecting userspace visible state. */ struct completion *completion; /* completion associated with fd */ apc_param_t comp_key; /* completion key associated with fd */ unsigned int comp_flags; /* completion flags */ @@ -245,7 +246,7 @@ void queue_async( struct async_queue *queue, struct async *async ) grab_object( async ); list_add_tail( &queue->queue, &async->queue_entry ); - set_fd_signaled( async->fd, 0 ); + if (!async->is_system) set_fd_signaled( async->fd, 0 ); } /* create an async on a given queue of a fd */ @@ -279,6 +280,7 @@ struct async *create_async( struct fd *fd, struct thread *thread, const async_da async->canceled = 0; async->unknown_status = 0; async->blocking = !is_fd_overlapped( fd ); + async->is_system = 0; async->completion = fd_get_completion( fd, &async->comp_key ); async->comp_flags = 0; async->completion_callback = NULL; @@ -531,7 +533,7 @@ void async_set_result( struct object *obj, unsigned int status, apc_param_t tota } if (async->event) set_event( async->event ); - else if (async->fd) set_fd_signaled( async->fd, 1 ); + else if (async->fd && !async->is_system) set_fd_signaled( async->fd, 1 ); } if (!async->signaled) @@ -586,7 +588,7 @@ static int cancel_async( struct process *process, struct object *obj, struct thr restart: LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) { - if (async->terminated || async->canceled) continue; + if (async->terminated || async->canceled || async->is_system) continue; if ((!obj || (get_fd_user( async->fd ) == obj)) && (!thread || async->thread == thread) && (!iosb || async->data.iosb == iosb)) @@ -623,7 +625,16 @@ static int cancel_blocking( struct process *process, struct thread *thread, clie void cancel_process_asyncs( struct process *process ) { - cancel_async( process, NULL, NULL, 0 ); + struct async *async; + +restart: + LIST_FOR_EACH_ENTRY( async, &process->asyncs, struct async, process_entry ) + { + if (async->terminated || async->canceled) continue; + async->canceled = 1; + fd_cancel_async( async->fd, async ); + goto restart; + } } int async_close_obj_handle( struct object *obj, struct process *process, obj_handle_t handle ) @@ -657,6 +668,7 @@ void cancel_terminating_thread_asyncs( struct thread *thread ) { if (async->thread != thread || async->terminated || async->canceled) continue; if (async->completion && async->data.apc_context && !async->event) continue; + if (async->is_system) continue; async->canceled = 1; fd_cancel_async( async->fd, async ); @@ -745,7 +757,7 @@ static struct iosb *create_iosb( const void *in_data, data_size_t in_size, data_ /* create an async associated with iosb for async-based requests * returned async must be passed to async_handoff */ -struct async *create_request_async( struct fd *fd, unsigned int comp_flags, const async_data_t *data ) +struct async *create_request_async( struct fd *fd, unsigned int comp_flags, const async_data_t *data, int is_system ) { struct async *async; struct iosb *iosb; @@ -764,6 +776,7 @@ struct async *create_request_async( struct fd *fd, unsigned int comp_flags, cons } async->pending = 0; async->direct_result = 1; + async->is_system = !!is_system; async->comp_flags = comp_flags; } return async; diff --git a/server/fd.c b/server/fd.c index 04d5f9bbe10..85676910e3f 100644 --- a/server/fd.c +++ b/server/fd.c @@ -2850,7 +2850,7 @@ DECL_HANDLER(flush) if (!fd) return; - if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + if ((async = create_request_async( fd, fd->comp_flags, &req->async, 0 ))) { fd->fd_ops->flush( fd, async ); reply->event = async_handoff( async, NULL, 1 ); @@ -2879,7 +2879,7 @@ DECL_HANDLER(get_volume_info) if (!fd) return; - if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + if ((async = create_request_async( fd, fd->comp_flags, &req->async, 0 ))) { fd->fd_ops->get_volume_info( fd, async, req->info_class ); reply->wait = async_handoff( async, NULL, 1 ); @@ -2955,7 +2955,7 @@ DECL_HANDLER(read) if (!fd) return; - if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + if ((async = create_request_async( fd, fd->comp_flags, &req->async, 0 ))) { fd->fd_ops->read( fd, async, req->pos ); reply->wait = async_handoff( async, NULL, 0 ); @@ -2973,7 +2973,7 @@ DECL_HANDLER(write) if (!fd) return; - if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + if ((async = create_request_async( fd, fd->comp_flags, &req->async, 0 ))) { fd->fd_ops->write( fd, async, req->pos ); reply->wait = async_handoff( async, &reply->size, 0 ); @@ -2992,7 +2992,7 @@ DECL_HANDLER(ioctl) if (!fd) return; - if ((async = create_request_async( fd, fd->comp_flags, &req->async ))) + if ((async = create_request_async( fd, fd->comp_flags, &req->async, 0 ))) { fd->fd_ops->ioctl( fd, req->code, async ); reply->wait = async_handoff( async, NULL, 0 ); diff --git a/server/file.h b/server/file.h index f5f1ed357ae..91b68ecb8ac 100644 --- a/server/file.h +++ b/server/file.h @@ -242,7 +242,8 @@ typedef void (*async_completion_callback)( void *private ); extern void free_async_queue( struct async_queue *queue ); extern struct async *create_async( struct fd *fd, struct thread *thread, const async_data_t *data, struct iosb *iosb ); -extern struct async *create_request_async( struct fd *fd, unsigned int comp_flags, const async_data_t *data ); +extern struct async *create_request_async( struct fd *fd, unsigned int comp_flags, const async_data_t *data, + int is_system ); extern obj_handle_t async_handoff( struct async *async, data_size_t *result, int force_blocking ); extern void queue_async( struct async_queue *queue, struct async *async ); extern void async_set_timeout( struct async *async, timeout_t timeout, unsigned int status ); diff --git a/server/protocol.def b/server/protocol.def index 248a9c6385b..0236a0c87d9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -1590,14 +1590,16 @@ enum server_fd_type /* Perform a send on a socket */ @REQ(send_socket) + unsigned int flags; /* SERVER_SOCKET_IO_* flags */ async_data_t async; /* async I/O parameters */ - int force_async; /* Force asynchronous mode? */ @REPLY obj_handle_t wait; /* handle to wait on for blocking send */ unsigned int options; /* device open options */ int nonblocking; /* is socket non-blocking? */ @END +#define SERVER_SOCKET_IO_FORCE_ASYNC 0x01 +#define SERVER_SOCKET_IO_SYSTEM 0x02 /* Get socket event flags */ @REQ(socket_get_events) diff --git a/server/sock.c b/server/sock.c index b4346bbcde1..beb6b2921b3 100644 --- a/server/sock.c +++ b/server/sock.c @@ -3924,7 +3924,7 @@ DECL_HANDLER(recv_socket) sock->pending_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); sock->reported_events &= ~(req->oob ? AFD_POLL_OOB : AFD_POLL_READ); - if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) + if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async, 0 ))) { set_error( status ); @@ -3972,6 +3972,7 @@ DECL_HANDLER(send_socket) struct async *async; struct fd *fd; int bind_errno = 0; + BOOL force_async = req->flags & SERVER_SOCKET_IO_FORCE_ASYNC; if (!sock) return; fd = sock->fd; @@ -3994,7 +3995,7 @@ DECL_HANDLER(send_socket) else if (!bind_errno) bind_errno = errno; } - if (!req->force_async && !sock->nonblocking && is_fd_overlapped( fd )) + if (!force_async && !sock->nonblocking && is_fd_overlapped( fd )) timeout = (timeout_t)sock->sndtimeo * -10000; if (bind_errno) status = sock_get_ntstatus( bind_errno ); @@ -4005,7 +4006,7 @@ DECL_HANDLER(send_socket) * asyncs will not consume all available space; if there's no space * available, the current request won't be immediately satiable. */ - if ((!req->force_async && sock->nonblocking) || check_fd_events( sock->fd, POLLOUT )) + if ((!force_async && sock->nonblocking) || check_fd_events( sock->fd, POLLOUT )) { /* Give the client opportunity to complete synchronously. * If it turns out that the I/O request is not actually immediately satiable, @@ -4030,10 +4031,11 @@ DECL_HANDLER(send_socket) } } - if (status == STATUS_PENDING && !req->force_async && sock->nonblocking) + if (status == STATUS_PENDING && !force_async && sock->nonblocking) status = STATUS_DEVICE_NOT_READY; - if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async ))) + if ((async = create_request_async( fd, get_fd_comp_flags( fd ), &req->async, + req->flags & SERVER_SOCKET_IO_SYSTEM ))) { struct send_req *send_req; struct iosb *iosb = async_get_iosb( async ); From ea84f2f520cd7c69cfa62c5b454f3d9e84febe1b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 13 Sep 2024 14:24:39 -0600 Subject: [PATCH 05/67] server: Correct STATUS_NOT_FOUND return from (cancel_async). (cherry picked from commit 4e43a7891fa419136376b3233a2192394b8d450f) CW-Bug-Id: #24269 --- dlls/ntdll/tests/pipe.c | 9 +++++++++ dlls/ws2_32/tests/sock.c | 2 +- server/async.c | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/tests/pipe.c b/dlls/ntdll/tests/pipe.c index 54e3c38b4b0..1d468c08662 100644 --- a/dlls/ntdll/tests/pipe.c +++ b/dlls/ntdll/tests/pipe.c @@ -614,6 +614,10 @@ static void test_cancelio(void) ok(ioapc_called, "IOAPC didn't run\n"); + res = pNtCancelIoFile(hPipe, &cancel_sb); + ok(!res, "NtCancelIoFile returned %lx\n", res); + ok(iosb.Status == STATUS_CANCELLED, "Wrong iostatus %lx\n", iosb.Status); + CloseHandle(hPipe); if (pNtCancelIoFileEx) @@ -631,6 +635,11 @@ static void test_cancelio(void) ok(iosb.Status == STATUS_CANCELLED, "Wrong iostatus %lx\n", iosb.Status); ok(WaitForSingleObject(hEvent, 0) == 0, "hEvent not signaled\n"); + iosb.Status = 0xdeadbeef; + res = pNtCancelIoFileEx(hPipe, NULL, &cancel_sb); + ok(res == STATUS_NOT_FOUND, "NtCancelIoFileEx returned %lx\n", res); + ok(iosb.Status == 0xdeadbeef, "Wrong iostatus %lx\n", iosb.Status); + CloseHandle(hPipe); } else diff --git a/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index 99e81ae88b7..5d598095f03 100644 --- a/dlls/ws2_32/tests/sock.c +++ b/dlls/ws2_32/tests/sock.c @@ -14264,7 +14264,7 @@ static DWORD WINAPI test_send_buffering_thread(void *arg) ok(WSAGetLastError() == WSAEWOULDBLOCK, "got error %u.\n", WSAGetLastError()); ok(d->sent_size, "got 0.\n"); ret = CancelIoEx((HANDLE)d->server, NULL); - todo_wine ok(!ret && GetLastError() == ERROR_NOT_FOUND, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(!ret && GetLastError() == ERROR_NOT_FOUND, "got ret %d, error %lu.\n", ret, GetLastError()); ret = CancelIo((HANDLE)d->server); ok(ret, "got error %lu.\n", GetLastError()); shutdown(d->server, SD_BOTH); diff --git a/server/async.c b/server/async.c index 01e536c6a6f..1f32d10a0f5 100644 --- a/server/async.c +++ b/server/async.c @@ -823,7 +823,7 @@ DECL_HANDLER(cancel_async) if (obj) { int count = cancel_async( current->process, obj, thread, req->iosb ); - if (!count && req->iosb) set_error( STATUS_NOT_FOUND ); + if (!count && !thread) set_error( STATUS_NOT_FOUND ); release_object( obj ); } } From ac4be53339fd93ffd983f7f3e15bf0171aadf31d Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 19 Sep 2024 20:28:09 -0600 Subject: [PATCH 06/67] fixup! winevulkan: Share keyed mutex data. --- dlls/winevulkan/vulkan.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index c40309ee468..e5542156a21 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2972,12 +2972,13 @@ static void import_keyed_mutex(struct wine_device *device, struct wine_device_me } vr = device->funcs.p_vkImportSemaphoreFdKHR(device->host_device, &fd_info); - close(fd_info.fd); if (vr != VK_SUCCESS) { ERR("vkImportSemaphoreFdKHR failed, vr %d.\n", vr); + close(fd_info.fd); goto error; } + /* Not closing fd on successful import, the driver now owns it. */ memory->keyed_mutex_instance_id = InterlockedIncrement64((LONGLONG *)&memory->keyed_mutex_shm->instance_id_counter); TRACE("memory %p, imported keyed mutex.\n", memory); From fad64fd7b480da6282d2cfd7bef526594973d689 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 19 Sep 2024 17:45:28 -0600 Subject: [PATCH 07/67] winex11.drv: Set X window property to indicate if the presentation may flip. CW-Bug-Id: #24273 --- dlls/winex11.drv/opengl.c | 4 ++++ dlls/winex11.drv/window.c | 13 +++++++++++++ dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 1 + 4 files changed, 19 insertions(+) diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 6748912a3fb..370bf0c75be 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -1546,6 +1546,8 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel else if(usexcomposite) { struct x11drv_win_data *data; + unsigned int allow_flip = 0; + gl->type = DC_GL_CHILD_WIN; gl->colormap = XCreateColormap( gdi_display, get_dummy_parent(), visual->visual, (visual->class == PseudoColor || visual->class == GrayScale || @@ -1556,6 +1558,8 @@ static struct gl_drawable *create_gl_drawable( HWND hwnd, const struct wgl_pixel { gl->drawable = pglXCreateWindow( gdi_display, gl->format->fbconfig, gl->window, NULL ); pXCompositeRedirectWindow( gdi_display, gl->window, CompositeRedirectManual ); + XChangeProperty( gdi_display, gl->window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); } if ((data = get_win_data( hwnd ))) { diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index a95aa97e4fe..1ad38ae76b3 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1927,12 +1927,16 @@ Window get_dummy_parent(void) */ void detach_client_window( struct x11drv_win_data *data, Window client_window, BOOL reparent ) { + unsigned int allow_flip = 0; + if (data->client_window != client_window || !client_window) return; data->client_window = 0; if (!data->whole_window) return; XSelectInput( data->display, client_window, 0 ); + XChangeProperty( data->display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); XFlush( data->display ); /* make sure XSelectInput is disabled for client_window after this point */ XDeleteContext( data->display, client_window, winContext ); @@ -1946,6 +1950,8 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window, B */ void attach_client_window( struct x11drv_win_data *data, Window client_window ) { + unsigned int allow_flip = 1; + if (data->client_window == client_window || !client_window) return; detach_client_window( data, data->client_window, TRUE ); data->client_window = client_window; @@ -1954,10 +1960,13 @@ void attach_client_window( struct x11drv_win_data *data, Window client_window ) XSaveContext( data->display, client_window, winContext, (char *)data->hwnd ); XSelectInput( data->display, client_window, ExposureMask ); + XChangeProperty( data->display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); XFlush( data->display ); /* make sure XSelectInput is enabled for client_window after this point */ XReparentWindow( gdi_display, client_window, data->whole_window, data->client_rect.left - data->whole_rect.left, data->client_rect.top - data->whole_rect.top ); + TRACE( "%p/%lx attached client window %lx\n", data->hwnd, data->whole_window, client_window ); } @@ -2108,6 +2117,7 @@ void set_gamescope_overlay_prop( Display *display, Window window, HWND hwnd ) */ static void create_whole_window( struct x11drv_win_data *data ) { + unsigned int allow_flip = 0; int cx, cy, mask; XSetWindowAttributes attr; WCHAR text[1024]; @@ -2162,6 +2172,9 @@ static void create_whole_window( struct x11drv_win_data *data ) data->vis.visual, mask, &attr ); if (!data->whole_window) goto done; + XChangeProperty( data->display, data->whole_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); + X11DRV_XInput2_Enable( data->display, data->whole_window, attr.event_mask ); set_initial_wm_hints( data->display, data->whole_window ); set_wm_hints( data ); diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index ea842e57bab..4ebc9f5ca59 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -530,6 +530,7 @@ enum x11drv_atoms XATOM__GTK_WORKAREAS_D0, XATOM__XEMBED, XATOM__XEMBED_INFO, + XATOM__WINE_ALLOW_FLIP, XATOM__WINE_HWND_STYLE, XATOM__WINE_HWND_EXSTYLE, XATOM_XdndAware, diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 5f6f0773d92..6a878cbf98c 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -197,6 +197,7 @@ static const char * const atom_names[NB_XATOMS - FIRST_XATOM] = "_GTK_WORKAREAS_D0", "_XEMBED", "_XEMBED_INFO", + "_WINE_ALLOW_FLIP", "_WINE_HWND_STYLE", "_WINE_HWND_EXSTYLE", "XdndAware", From b6eba4d248cc9f45b9f5c295edb5fc17c0a83668 Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Mon, 23 Sep 2024 10:42:34 -0700 Subject: [PATCH 08/67] nsiproxy: Only set the connection count from tcp_conns_enumerate_all when appropriate. Fixes heap corruption downstream in NsiEnumerateObjectsAllParametersEx. Fixes a regression from 9085bc7b87f. (cherry picked from commit b81a18949c5b04108986cff5ad4b5733116cad32) --- dlls/nsiproxy.sys/tcp.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/dlls/nsiproxy.sys/tcp.c b/dlls/nsiproxy.sys/tcp.c index 7a3004b1a97..e472d8600a2 100644 --- a/dlls/nsiproxy.sys/tcp.c +++ b/dlls/nsiproxy.sys/tcp.c @@ -322,13 +322,17 @@ static NTSTATUS tcp_conns_enumerate_all( UINT filter, struct nsi_tcp_conn_key *k *count = reply->count; else if (ret == STATUS_BUFFER_TOO_SMALL) { - *count = reply->count; - if (want_data) + if (!want_data) { - free( connections ); - return STATUS_BUFFER_OVERFLOW; + /* If we were given buffers, the outgoing count must never be + greater than the incoming one. If we weren't, the count + should be set to the actual count. */ + *count = reply->count; + return STATUS_SUCCESS; } - return STATUS_SUCCESS; + + free( connections ); + return STATUS_BUFFER_OVERFLOW; } } SERVER_END_REQ; From 3638eaceb3d93b4fe852384814942806a5d1e2cf Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Mon, 23 Sep 2024 10:44:11 -0700 Subject: [PATCH 09/67] nsiproxy: Only set the endpoint count from udp_endpoint_enumerate_all when appropriate. Fixes heap corruption downstream in NsiEnumerateObjectsAllParametersEx. Fixes a regression from a3f737f6143. (cherry picked from commit f84f2703b5345a23c2aa39e1bf3b1f4eacfa1c9e) --- dlls/nsiproxy.sys/udp.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/dlls/nsiproxy.sys/udp.c b/dlls/nsiproxy.sys/udp.c index 1d790484d8f..897c90c131b 100644 --- a/dlls/nsiproxy.sys/udp.c +++ b/dlls/nsiproxy.sys/udp.c @@ -231,13 +231,17 @@ static NTSTATUS udp_endpoint_enumerate_all( void *key_data, UINT key_size, void *count = reply->count; else if (ret == STATUS_BUFFER_TOO_SMALL) { - *count = reply->count; - if (want_data) + if (!want_data) { - free( endpoints ); - return STATUS_BUFFER_OVERFLOW; + /* If we were given buffers, the outgoing count must never be + greater than the incoming one. If we weren't, the count + should be set to the actual count. */ + *count = reply->count; + return STATUS_SUCCESS; } - else return STATUS_SUCCESS; + + free( endpoints ); + return STATUS_BUFFER_OVERFLOW; } } SERVER_END_REQ; From db96f88963b6ebf96ea22ecc5aa0b4981015efdb Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Aug 2024 12:24:12 -0600 Subject: [PATCH 10/67] kernel32/tests: Add a test for TLS links. (cherry picked from commit ae2443ac11786b0a77f827dce6e7f0097484c769) CW-Bug-Id: #24258 --- dlls/kernel32/tests/module.c | 48 ++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index 7dd1125a0a6..fbced30aeeb 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -1627,6 +1627,53 @@ static void test_ddag_node(void) ok( se == node->Dependencies.Tail, "Expected end of the list.\n" ); } +static HANDLE test_tls_links_started, test_tls_links_done; + +static DWORD WINAPI test_tls_links_thread(void* tlsidx_v) +{ + SetEvent(test_tls_links_started); + WaitForSingleObject(test_tls_links_done, INFINITE); + return 0; +} + +static void test_tls_links(void) +{ + TEB *teb = NtCurrentTeb(), *thread_teb; + THREAD_BASIC_INFORMATION tbi; + NTSTATUS status; + HANDLE thread; + + todo_wine ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n"); + + test_tls_links_started = CreateEventW(NULL, FALSE, FALSE, NULL); + test_tls_links_done = CreateEventW(NULL, FALSE, FALSE, NULL); + + thread = CreateThread(NULL, 0, test_tls_links_thread, NULL, CREATE_SUSPENDED, NULL); + do + { + /* workaround currently present Wine bug when thread teb may be not available immediately + * after creating a thread before it is initialized on the Unix side. */ + Sleep(1); + status = NtQueryInformationThread(thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL); + ok(!status, "got %#lx.\n", status ); + } while (!(thread_teb = tbi.TebBaseAddress)); + ok(!thread_teb->ThreadLocalStoragePointer, "got %p.\n", thread_teb->ThreadLocalStoragePointer); + ResumeThread(thread); + WaitForSingleObject(test_tls_links_started, INFINITE); + + todo_wine ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); + todo_wine ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); + todo_wine ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); + todo_wine ok(!thread_teb->TlsLinks.Flink, "got %p.\n", thread_teb->TlsLinks.Flink); + todo_wine ok(!thread_teb->TlsLinks.Blink, "got %p.\n", thread_teb->TlsLinks.Blink); + SetEvent(test_tls_links_done); + WaitForSingleObject(thread, INFINITE); + + CloseHandle(thread); + CloseHandle(test_tls_links_started); + CloseHandle(test_tls_links_done); +} + #define check_dll_path(a, b) check_dll_path_( __LINE__, a, b ) static void check_dll_path_( unsigned int line, HMODULE h, const char *expected ) { @@ -1755,5 +1802,6 @@ START_TEST(module) test_LdrGetDllFullName(); test_apisets(); test_ddag_node(); + test_tls_links(); test_known_dlls_load(); } From fff0fab52c3f238b9938f1e51b26facef0787425 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Aug 2024 12:25:08 -0600 Subject: [PATCH 11/67] ntdll: Reserve space for some TLS directories at once. (cherry picked from commit 3b94e543853acd621a2cfd3a72c099df14dd14bb) CW-Bug-Id: #24258 --- dlls/kernel32/tests/module.c | 4 ++-- dlls/ntdll/loader.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index fbced30aeeb..a80a839697d 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -1643,7 +1643,7 @@ static void test_tls_links(void) NTSTATUS status; HANDLE thread; - todo_wine ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n"); + ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n"); test_tls_links_started = CreateEventW(NULL, FALSE, FALSE, NULL); test_tls_links_done = CreateEventW(NULL, FALSE, FALSE, NULL); @@ -1661,7 +1661,7 @@ static void test_tls_links(void) ResumeThread(thread); WaitForSingleObject(test_tls_links_started, INFINITE); - todo_wine ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); + ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); todo_wine ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); todo_wine ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); todo_wine ok(!thread_teb->TlsLinks.Flink, "got %p.\n", thread_teb->TlsLinks.Flink); diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 3c55c42c6d0..eee3b67e17b 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -145,7 +145,7 @@ typedef struct _wine_modref BOOL system; } WINE_MODREF; -static UINT tls_module_count; /* number of modules with TLS directory */ +static UINT tls_module_count = 32; /* number of modules with TLS directory */ static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */ LIST_ENTRY tls_links = { &tls_links, &tls_links }; @@ -1359,13 +1359,10 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) if (i == tls_module_count) { - UINT new_count = max( 32, tls_module_count * 2 ); + UINT new_count = tls_module_count * 2; - if (!tls_dirs) - new_ptr = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*tls_dirs) ); - else - new_ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_dirs, - new_count * sizeof(*tls_dirs) ); + new_ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_dirs, + new_count * sizeof(*tls_dirs) ); if (!new_ptr) return FALSE; /* resize the pointer block in all running threads */ @@ -1609,8 +1606,6 @@ static NTSTATUS alloc_thread_tls(void) void **pointers; UINT i, size; - if (!tls_module_count) return STATUS_SUCCESS; - if (!(pointers = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*pointers) ))) return STATUS_NO_MEMORY; @@ -4556,6 +4551,9 @@ void loader_init( CONTEXT *context, void **entry ) RtlSetBits( peb->TlsBitmap, 0, NtCurrentTeb()->WowTebOffset ? WOW64_TLS_MAX_NUMBER : 1 ); RtlSetBits( peb->TlsBitmap, NTDLL_TLS_ERRNO, 1 ); + if (!(tls_dirs = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*tls_dirs) ))) + NtTerminateProcess( GetCurrentProcess(), STATUS_NO_MEMORY ); + /* initialize hash table */ for (i = 0; i < HASH_MAP_SIZE; i++) InitializeListHead( &hash_table[i] ); From ab6879318b9888c53189064845afeb9f532e2c45 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Aug 2024 12:53:12 -0600 Subject: [PATCH 12/67] ntdll: Iterate TEBs only once in alloc_tls_slot(). (cherry picked from commit 68fe97501cabb9b04393a77a28a91c2d2257dff4) CW-Bug-Id: #24258 --- dlls/ntdll/loader.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index eee3b67e17b..e967ead3ed1 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1339,6 +1339,7 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) ULONG i, size; void *new_ptr; LIST_ENTRY *entry; + UINT old_module_count = tls_module_count; if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size ))) return FALSE; @@ -1364,16 +1365,22 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) new_ptr = RtlReAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_dirs, new_count * sizeof(*tls_dirs) ); if (!new_ptr) return FALSE; + tls_dirs = new_ptr; + tls_module_count = new_count; + } - /* resize the pointer block in all running threads */ - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + /* allocate the data block in all running threads */ + for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + { + TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); + + if (old_module_count < tls_module_count) { - TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); void **old = teb->ThreadLocalStoragePointer; - void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*new)); + void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*new)); if (!new) return FALSE; - if (old) memcpy( new, old, tls_module_count * sizeof(*new) ); + if (old) memcpy( new, old, old_module_count * sizeof(*new) ); teb->ThreadLocalStoragePointer = new; #ifdef __x86_64__ /* macOS-specific hack */ if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = new; @@ -1382,15 +1389,6 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) /* FIXME: can't free old block here, should be freed at thread exit */ } - tls_dirs = new_ptr; - tls_module_count = new_count; - } - - /* allocate the data block in all running threads */ - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) - { - TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); - if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return -1; memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); From 3d50128c5d6976c1f465eed5c49b5097aace9d41 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 27 Aug 2024 13:25:32 -0600 Subject: [PATCH 13/67] ntdll: Do not use TlsLinks for enumerating TEBs. (cherry picked from commit de23dfc5b1eac6e111cd66540ad9a97b61a3fe28) CW-Bug-Id: #24258 --- dlls/kernel32/tests/module.c | 8 ++++---- dlls/ntdll/loader.c | 38 ++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index a80a839697d..89accdcf483 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -1662,10 +1662,10 @@ static void test_tls_links(void) WaitForSingleObject(test_tls_links_started, INFINITE); ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); - todo_wine ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); - todo_wine ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); - todo_wine ok(!thread_teb->TlsLinks.Flink, "got %p.\n", thread_teb->TlsLinks.Flink); - todo_wine ok(!thread_teb->TlsLinks.Blink, "got %p.\n", thread_teb->TlsLinks.Blink); + ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); + ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); + ok(!thread_teb->TlsLinks.Flink, "got %p.\n", thread_teb->TlsLinks.Flink); + ok(!thread_teb->TlsLinks.Blink, "got %p.\n", thread_teb->TlsLinks.Blink); SetEvent(test_tls_links_done); WaitForSingleObject(thread, INFINITE); diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index e967ead3ed1..b88f9ec8acb 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -147,7 +147,6 @@ typedef struct _wine_modref static UINT tls_module_count = 32; /* number of modules with TLS directory */ static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */ -LIST_ENTRY tls_links = { &tls_links, &tls_links }; static RTL_CRITICAL_SECTION loader_section; static RTL_CRITICAL_SECTION_DEBUG critsect_debug = @@ -1338,8 +1337,8 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) const IMAGE_TLS_DIRECTORY *dir; ULONG i, size; void *new_ptr; - LIST_ENTRY *entry; UINT old_module_count = tls_module_count; + HANDLE thread = NULL, next; if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size ))) return FALSE; @@ -1370,9 +1369,25 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) } /* allocate the data block in all running threads */ - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + while (!NtGetNextThread( GetCurrentProcess(), thread, THREAD_QUERY_LIMITED_INFORMATION, 0, 0, &next )) { - TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); + THREAD_BASIC_INFORMATION tbi; + TEB *teb; + + if (thread) NtClose( thread ); + thread = next; + if (NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ) || !tbi.TebBaseAddress) + { + ERR( "NtQueryInformationThread failed.\n" ); + continue; + } + teb = tbi.TebBaseAddress; + if (!teb->ThreadLocalStoragePointer) + { + /* Thread is not initialized by loader yet or already teared down. */ + TRACE( "thread %04lx NULL tls block.\n", HandleToULong(tbi.ClientId.UniqueThread) ); + continue; + } if (old_module_count < tls_module_count) { @@ -1399,6 +1414,7 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) RtlFreeHeap( GetProcessHeap(), 0, InterlockedExchangePointer( (void **)teb->ThreadLocalStoragePointer + i, new_ptr )); } + if (thread) NtClose( thread ); *(DWORD *)dir->AddressOfIndex = i; tls_dirs[i] = *dir; @@ -4070,9 +4086,13 @@ void WINAPI LdrShutdownThread(void) if (wm->ldr.TlsIndex == -1) call_tls_callbacks( wm->ldr.DllBase, DLL_THREAD_DETACH ); RtlAcquirePebLock(); - if (NtCurrentTeb()->TlsLinks.Flink) RemoveEntryList( &NtCurrentTeb()->TlsLinks ); if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) { + NtCurrentTeb()->ThreadLocalStoragePointer = NULL; +#ifdef __x86_64__ /* macOS-specific hack */ + if (NtCurrentTeb()->Instrumentation[0]) + ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL; +#endif for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); RtlFreeHeap( GetProcessHeap(), 0, pointers ); } @@ -4410,10 +4430,6 @@ static void init_wow64( CONTEXT *context ) imports_fixup_done = TRUE; } - RtlAcquirePebLock(); - InsertHeadList( &tls_links, &NtCurrentTeb()->TlsLinks ); - RtlReleasePebLock(); - RtlLeaveCriticalSection( &loader_section ); pWow64LdrpInitialize( context ); } @@ -4596,10 +4612,6 @@ void loader_init( CONTEXT *context, void **entry ) if (NtCurrentTeb()->WowTebOffset) init_wow64( context ); #endif - RtlAcquirePebLock(); - InsertHeadList( &tls_links, &NtCurrentTeb()->TlsLinks ); - RtlReleasePebLock(); - NtCurrentTeb()->FlsSlots = fls_alloc_data(); if (!attach_done) /* first time around */ From e7da5a10e4c08b2fcdb3d4f1f2c868ba3598fbe3 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 3 Sep 2024 14:27:04 -0600 Subject: [PATCH 14/67] ntdll/tests: Add more tests for process instrumentation callback. (cherry picked from commit e37896280e022c08ce3d7d88542a55e06859e1f0) CW-Bug-Id: #24198 --- dlls/ntdll/tests/exception.c | 283 ++++++++++++++++++++++++++++++++++- 1 file changed, 280 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 7838391d41e..66d591e4666 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -2299,6 +2299,46 @@ static void test_KiUserCallbackDispatcher(void) VirtualProtect( pKiUserCallbackDispatcher, sizeof(saved_code), old_protect, &old_protect ); } +static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0xff, 0x05, /* inc instrumentation_call_count */ + /* &instrumentation_call_count, offset 2 */ 0x00, 0x00, 0x00, 0x00, + 0xff, 0xe1, /* jmp *ecx */ + }; + + unsigned int instrumentation_call_count; + NTSTATUS status; + + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(volatile void **)((char *)code_mem + 2) = &instrumentation_call_count; + + memset(&info, 0, sizeof(info)); + /* On 32 bit the structure is never used and just a callback pointer is expected. */ + info.Version = (ULONG_PTR)code_mem; + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_NOT_SUPPORTED + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + DestroyWindow( CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 )); + todo_wine ok( instrumentation_call_count, "got %u.\n", instrumentation_call_count ); + + memset(&info, 0, sizeof(info)); + instrumentation_call_count = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !instrumentation_call_count, "got %u.\n", instrumentation_call_count ); +} + #elif defined(__x86_64__) #define UNW_FLAG_NHANDLER 0 @@ -5496,6 +5536,7 @@ static void test_syscall_clobbered_regs(void) struct regs { UINT64 rcx; + UINT64 r10; UINT64 r11; UINT32 eflags; }; @@ -5513,16 +5554,19 @@ static void test_syscall_clobbered_regs(void) 0x41, 0x50, /* push %r8 */ 0x53, 0x55, 0x57, 0x56, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, /* push %rbx, %rbp, %rdi, %rsi, %r12, %r13, %r14, %r15 */ + 0x49, 0xba, 0xef, 0xbe, 0xad, 0xde, 0x00, 0x00, 0x00, 0x00, + /* movabs $0xdeadbeef,%r10 */ 0x41, 0xff, 0xd1, /* callq *r9 */ 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5e, 0x5f, 0x5d, 0x5b, /* pop %r15, %r14, %r13, %r12, %rsi, %rdi, %rbp, %rbx */ 0x41, 0x58, /* pop %r8 */ 0x49, 0x89, 0x48, 0x00, /* mov %rcx,(%r8) */ - 0x4d, 0x89, 0x58, 0x08, /* mov %r11,0x8(%r8) */ + 0x4d, 0x89, 0x50, 0x08, /* mov %r10,0x8(%r8) */ + 0x4d, 0x89, 0x58, 0x10, /* mov %r11,0x10(%r8) */ 0x9c, /* pushfq */ 0x59, /* pop %rcx */ 0xfc, /* cld */ - 0x41, 0x89, 0x48, 0x10, /* mov %ecx,0x10(%r8) */ + 0x41, 0x89, 0x48, 0x18, /* mov %ecx,0x18(%r8) */ 0x5c, /* pop %rsp */ 0xc3, /* ret */ }; @@ -5541,6 +5585,7 @@ static void test_syscall_clobbered_regs(void) status = func((HANDLE)0xdeadbeef, NULL, ®s, pNtCancelTimer); ok(status == STATUS_INVALID_HANDLE, "Got unexpected status %#lx.\n", status); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); /* After the syscall instruction rcx contains the address of the instruction next after syscall. */ ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, @@ -5551,6 +5596,7 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtCancelTimer && (BYTE *)regs.rcx < (BYTE *)pNtCancelTimer + 0x20, "Got unexpected rcx %s, pNtCancelTimer %p.\n", wine_dbgstr_longlong(regs.rcx), pNtCancelTimer); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); context.ContextFlags = CONTEXT_CONTROL; status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); @@ -5558,12 +5604,14 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok((regs.r11 | 0x2) == regs.eflags, "Expected r11 (%#I64x) | 0x2 to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); context.ContextFlags = CONTEXT_INTEGER; status = func(GetCurrentThread(), &context, ®s, pNtGetContextThread); @@ -5571,13 +5619,14 @@ static void test_syscall_clobbered_regs(void) ok((BYTE *)regs.rcx > (BYTE *)pNtGetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtGetContextThread + 0x20, "Got unexpected rcx %s, pNtGetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtGetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); status = func(GetCurrentThread(), &context, ®s, pNtSetContextThread); ok(status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status); ok((BYTE *)regs.rcx > (BYTE *)pNtSetContextThread && (BYTE *)regs.rcx < (BYTE *)pNtSetContextThread + 0x20, "Got unexpected rcx %s, pNtSetContextThread %p.\n", wine_dbgstr_longlong(regs.rcx), pNtSetContextThread); ok(regs.r11 == regs.eflags, "Expected r11 (%#I64x) to equal EFLAGS (%#x).\n", regs.r11, regs.eflags); - + ok(regs.r10 != regs.rcx, "got %#I64x.\n", regs.r10); } static CONTEXT test_raiseexception_regs_context; @@ -5677,6 +5726,232 @@ static void test_raiseexception_regs(void) RemoveVectoredExceptionHandler(vectored_handler); } +static LONG CALLBACK test_instrumentation_callback_handler( EXCEPTION_POINTERS *exception_info ) +{ + EXCEPTION_RECORD *rec = exception_info->ExceptionRecord; + CONTEXT *c = exception_info->ContextRecord; + + if (rec->ExceptionCode == EXCEPTION_BREAKPOINT) ++c->Rip; + return EXCEPTION_CONTINUE_EXECUTION; +} + +static HANDLE instrumentation_callback_thread_ready, instrumentation_callback_thread_wait; + +static DWORD WINAPI test_instrumentation_callback_thread( void *arg ) +{ + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + + SetEvent( instrumentation_callback_thread_ready ); + NtWaitForSingleObject( instrumentation_callback_thread_wait, FALSE, NULL ); + return 0; +} + +struct instrumentation_callback_data +{ + unsigned int call_count; + struct + { + char *r10; + char *rcx; + } + call_data[256]; +}; + +static void init_instrumentation_data(struct instrumentation_callback_data *d) +{ + memset( d, 0xcc, sizeof(*d) ); + d->call_count = 0; +} + +static void test_instrumentation_callback(void) +{ + static const BYTE instrumentation_callback[] = + { + 0x50, 0x52, /* push %rax, %rdx */ + + 0x48, 0xba, /* movabs instrumentation_call_count, %rdx */ + /* &instrumentation_call_count, offset 4 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb8, 0x01, 0x00, 0x00, 0x00, /* mov $0x1,%eax */ + 0xf0, 0x0f, 0xc1, 0x02, /* lock xadd %eax,(%rdx) */ + 0x0f, 0xb6, 0xc0, /* movzx %al,%eax */ + 0x48, 0xba, /* movabs instrumentation_call_data, %rdx */ + /* instrumentation_call_data, offset 26 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x01, 0xc0, /* add %rax,%rax */ + 0x48, 0x8d, 0x14, 0xc2, /* lea (%rdx,%rax,8),%rdx */ + 0x4c, 0x89, 0x12, /* mov %r10,(%rdx) */ + 0x48, 0x89, 0x4a, 0x08, /* mov %rcx,0x8(%rdx) */ + + 0x5a, 0x58, /* pop %rdx, %rax */ + 0x41, 0xff, 0xe2, /* jmp *r10 */ + }; + + struct instrumentation_callback_data curr_data, data; + PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION info; + HMODULE ntdll = GetModuleHandleA( "ntdll.dll" ); + void *pLdrInitializeThunk; + EXCEPTION_RECORD record; + void *vectored_handler; + unsigned int i, count; + NTSTATUS status; + HANDLE thread; + CONTEXT ctx; + HWND hwnd; + LONG pass; + + if (is_arm64ec) return; + + memcpy( code_mem, instrumentation_callback, sizeof(instrumentation_callback) ); + *(void **)((char *)code_mem + 4) = &curr_data.call_count; + *(void **)((char *)code_mem + 26) = curr_data.call_data; + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH + || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, + "got %#lx.\n", status ); + /* If instrumentation callback is not yet set during syscall entry it won't be called on exit. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + if (status) + { + win_skip( "Failed setting instrumenation callback.\n" ); + return; + } + + init_instrumentation_data( &curr_data ); + memset( &info, 0xcc, sizeof(info) ); + status = NtQueryInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info), NULL ); + data = curr_data; + ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess + && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20, + "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess ); + todo_wine ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); + + memset(&info, 0, sizeof(info)); + info.Callback = code_mem; + init_instrumentation_data( &curr_data ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + + vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler ); + ok( !!vectored_handler, "failed.\n" ); + init_instrumentation_data( &curr_data ); + DbgBreakPoint(); + data = curr_data; + todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + + pass = 0; + InterlockedIncrement( &pass ); + pRtlCaptureContext( &ctx ); + if (InterlockedIncrement( &pass ) == 2) /* interlocked to prevent compiler from moving before capture */ + { + record.ExceptionCode = 0xceadbeef; + record.NumberParameters = 0; + init_instrumentation_data( &curr_data ); + status = pNtRaiseException( &record, &ctx, TRUE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 3) + { + data = curr_data; + todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + pKiUserExceptionDispatcher ); + init_instrumentation_data( &curr_data ); + NtContinue( &ctx, FALSE ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 4) + { + data = curr_data; + /* Not called for NtContinue. */ + ok( !data.call_count, "got %u.\n", data.call_count ); + init_instrumentation_data( &curr_data ); + NtSetContextThread( GetCurrentThread(), &ctx ); + ok( 0, "Shouldn't be reached.\n" ); + } + else if (pass == 5) + { + data = curr_data; + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); + init_instrumentation_data( &curr_data ); + } + ok( pass == 5, "got %ld.\n", pass ); + RemoveVectoredExceptionHandler( vectored_handler ); + + status = pNtQueueApcThread( GetCurrentThread(), apc_func, 0x1234, 0x5678, 0xdeadbeef ); + ok( !status, "got %#lx.\n", status ); + init_instrumentation_data( &curr_data ); + apc_called = FALSE; + SleepEx( 0, TRUE ); + data = curr_data; + ok( apc_called, "APC was not called.\n" ); + todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + todo_wine ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); + + instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL ); + instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL ); + init_instrumentation_data( &curr_data ); + thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pLdrInitializeThunk) break; + } + todo_wine ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); + + init_instrumentation_data( &curr_data ); + SetEvent( instrumentation_callback_thread_wait ); + NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + count = 0; + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20) + ++count; + } + todo_wine ok( count == 2, "got %u.\n", count ); + + SetEvent( instrumentation_callback_thread_wait ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); + CloseHandle( instrumentation_callback_thread_ready ); + CloseHandle( instrumentation_callback_thread_wait ); + + hwnd = CreateWindowA( "Static", "test", 0, 0, 0, 0, 0, 0, 0, 0, 0 ); + init_instrumentation_data( &curr_data ); + DestroyWindow( hwnd ); + data = curr_data; + todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + for (i = 0; i < data.call_count; ++i) + { + if (data.call_data[i].r10 == pKiUserCallbackDispatcher) + break; + } + todo_wine ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); + + init_instrumentation_data( &curr_data ); + memset(&info, 0, sizeof(info)); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); + data = curr_data; + ok( !status, "got %#lx.\n", status ); + ok( !data.call_count, "got %u.\n", data.call_count ); +} + #elif defined(__arm__) #define UNW_FLAG_NHANDLER 0 @@ -12932,6 +13207,7 @@ START_TEST(exception) test_copy_context(); test_set_live_context(); test_hwbpt_in_syscall(); + test_instrumentation_callback(); #elif defined(__x86_64__) @@ -12979,6 +13255,7 @@ START_TEST(exception) test_syscall_clobbered_regs(); test_raiseexception_regs(); test_hwbpt_in_syscall(); + test_instrumentation_callback(); #elif defined(__aarch64__) From a91e43f87b999c0ffb4d5e69adddf0797644dd05 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 4 Sep 2024 16:16:00 -0600 Subject: [PATCH 15/67] ntdll: Call instrumentation callback from wine_syscall_dispatcher on x64. (cherry picked from commit 390d2d2160e54e1ba40042c473154f5ac53e295f) CW-Bug-Id: #24198 --- dlls/ntdll/tests/exception.c | 28 +++++++-------- dlls/ntdll/unix/process.c | 8 +++-- dlls/ntdll/unix/signal_arm.c | 6 ++++ dlls/ntdll/unix/signal_arm64.c | 6 ++++ dlls/ntdll/unix/signal_i386.c | 6 ++++ dlls/ntdll/unix/signal_x86_64.c | 63 +++++++++++++++++++++++++++++++-- dlls/ntdll/unix/unix_private.h | 2 ++ dlls/wow64/process.c | 2 +- 8 files changed, 102 insertions(+), 19 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 66d591e4666..a358c19fab2 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5826,11 +5826,11 @@ static void test_instrumentation_callback(void) status = NtQueryInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info), NULL ); data = curr_data; ok( status == STATUS_INVALID_INFO_CLASS, "got %#lx.\n", status ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 >= (char *)NtQueryInformationProcess && data.call_data[0].r10 < (char *)NtQueryInformationProcess + 0x20, "got %p, NtQueryInformationProcess %p.\n", data.call_data[0].r10, NtQueryInformationProcess ); - todo_wine ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); + ok( data.call_data[0].rcx != data.call_data[0].r10, "got %p.\n", data.call_data[0].rcx ); memset(&info, 0, sizeof(info)); info.Callback = code_mem; @@ -5838,7 +5838,7 @@ static void test_instrumentation_callback(void) status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, &info, sizeof(info) ); data = curr_data; ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); vectored_handler = AddVectoredExceptionHandler( TRUE, test_instrumentation_callback_handler ); ok( !!vectored_handler, "failed.\n" ); @@ -5863,8 +5863,8 @@ static void test_instrumentation_callback(void) else if (pass == 3) { data = curr_data; - todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserExceptionDispatcher ); init_instrumentation_data( &curr_data ); NtContinue( &ctx, FALSE ); @@ -5882,8 +5882,8 @@ static void test_instrumentation_callback(void) else if (pass == 5) { data = curr_data; - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == (void *)ctx.Rip, "got %p, expected %p.\n", data.call_data[0].r10, (void *)ctx.Rip ); init_instrumentation_data( &curr_data ); } ok( pass == 5, "got %ld.\n", pass ); @@ -5896,8 +5896,8 @@ static void test_instrumentation_callback(void) SleepEx( 0, TRUE ); data = curr_data; ok( apc_called, "APC was not called.\n" ); - todo_wine ok( data.call_count == 1, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); + ok( data.call_count == 1, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserApcDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserApcDispatcher ); instrumentation_callback_thread_ready = CreateEventW( NULL, FALSE, FALSE, NULL ); instrumentation_callback_thread_wait = CreateEventW( NULL, FALSE, FALSE, NULL ); @@ -5905,7 +5905,7 @@ static void test_instrumentation_callback(void) thread = CreateThread( NULL, 0, test_instrumentation_callback_thread, 0, 0, NULL ); NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); pLdrInitializeThunk = GetProcAddress( ntdll, "LdrInitializeThunk" ); for (i = 0; i < data.call_count; ++i) { @@ -5917,14 +5917,14 @@ static void test_instrumentation_callback(void) SetEvent( instrumentation_callback_thread_wait ); NtWaitForSingleObject( instrumentation_callback_thread_ready, FALSE, NULL ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); count = 0; for (i = 0; i < data.call_count; ++i) { if (data.call_data[i].r10 >= (char *)NtWaitForSingleObject && data.call_data[i].r10 < (char *)NtWaitForSingleObject + 0x20) ++count; } - todo_wine ok( count == 2, "got %u.\n", count ); + ok( count == 2, "got %u.\n", count ); SetEvent( instrumentation_callback_thread_wait ); WaitForSingleObject( thread, INFINITE ); @@ -5936,7 +5936,7 @@ static void test_instrumentation_callback(void) init_instrumentation_data( &curr_data ); DestroyWindow( hwnd ); data = curr_data; - todo_wine ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); + ok( data.call_count && data.call_count <= 256, "got %u.\n", data.call_count ); for (i = 0; i < data.call_count; ++i) { if (data.call_data[i].r10 == pKiUserCallbackDispatcher) diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 3f05fe9be2e..23e26077e0c 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1675,10 +1675,14 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info; - FIXME( "ProcessInstrumentationCallback stub.\n" ); - if (size < sizeof(*instr)) return STATUS_INFO_LENGTH_MISMATCH; ret = STATUS_SUCCESS; + if (handle != GetCurrentProcess()) + { + FIXME( "Setting ProcessInstrumentationCallback is not yet supported for other process.\n" ); + break; + } + set_process_instrumentation_callback( instr->Callback ); break; } diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index dbad562c6d3..b8659222d19 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -760,6 +760,12 @@ static NTSTATUS libunwind_virtual_unwind( DWORD ip, DWORD *frame, CONTEXT *conte } #endif +void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not supported.\n" ); +} + + /*********************************************************************** * unwind_builtin_dll */ diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index a4bae5563a8..ffc08e21def 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -206,6 +206,12 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) (char *)SP_sig(sigcontext) <= (char *)arm64_thread_data()->syscall_frame); } +void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not supported.\n" ); +} + + /*********************************************************************** * dwarf_virtual_unwind * diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index cdecda2c646..23c01c6b64c 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -603,6 +603,12 @@ static BOOL is_inside_syscall( ucontext_t *sigcontext ) } +void set_process_instrumentation_callback( void *callback ) +{ + if (callback) FIXME( "Not supported.\n" ); +} + + struct xcontext { CONTEXT c; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 9b05ff2c97a..cf8adbe4dbc 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -406,6 +406,8 @@ C_ASSERT( sizeof(struct callback_stack_layout) == 0x58 ); static unsigned int syscall_flags; +#define RESTORE_FLAGS_INSTRUMENTATION CONTEXT_i386 + struct syscall_frame { ULONG64 rax; /* 0000 */ @@ -455,6 +457,7 @@ struct amd64_thread_data DWORD fs; /* 0338 WOW TEB selector */ DWORD xstate_features_size; /* 033c */ UINT64 xstate_features_mask; /* 0340 */ + void **instrumentation_callback; /* 0348 */ }; C_ASSERT( sizeof(struct amd64_thread_data) <= sizeof(((struct ntdll_thread_data *)0)->cpu_data) ); @@ -486,6 +489,23 @@ static BOOL is_inside_syscall( const ucontext_t *sigcontext ) } +extern void __wine_syscall_dispatcher_instrumentation(void); +static void *instrumentation_callback; +static pthread_mutex_t instrumentation_callback_mutex = PTHREAD_MUTEX_INITIALIZER; + +void set_process_instrumentation_callback( void *callback ) +{ + void *ptr = (char *)user_shared_data + page_size; + sigset_t sigset; + void *old; + + server_enter_uninterrupted_section( &instrumentation_callback_mutex, &sigset ); + old = InterlockedExchangePointer( &instrumentation_callback, callback ); + if (!old && callback) InterlockedExchangePointer( ptr, __wine_syscall_dispatcher_instrumentation ); + else if (old && !callback) InterlockedExchangePointer( ptr, __wine_syscall_dispatcher ); + server_leave_uninterrupted_section( &instrumentation_callback_mutex, &sigset ); +} + struct xcontext { CONTEXT c; @@ -2237,7 +2257,8 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo ) /* disallow single-stepping through a syscall */ - if ((void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher) + if ((void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher + || (void *)RIP_sig( sigcontext ) == __wine_syscall_dispatcher_instrumentation) { extern const void *__wine_syscall_dispatcher_prolog_end_ptr; @@ -2263,6 +2284,7 @@ static BOOL handle_syscall_trap( ucontext_t *sigcontext, siginfo_t *siginfo ) frame->rip = *(ULONG64 *)RSP_sig( sigcontext ); frame->eflags = EFL_sig(sigcontext); frame->restore_flags = CONTEXT_CONTROL; + if (instrumentation_callback) frame->restore_flags |= RESTORE_FLAGS_INSTRUMENTATION; RCX_sig( sigcontext ) = (ULONG64)frame; RSP_sig( sigcontext ) += sizeof(ULONG64); @@ -2871,6 +2893,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; assert( thread_data->xstate_features_size == xstate_features_size ); + thread_data->instrumentation_callback = &instrumentation_callback; #if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); @@ -3195,8 +3218,10 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movw %gs:0x338,%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif + "testl $0x10000,%edx\n\t" /* RESTORE_FLAGS_INSTRUMENTATION */ "movq 0x60(%rcx),%r14\n\t" - "testl $0x3,%edx\n\t" /* CONTEXT_CONTROL | CONTEXT_INTEGER */ + "jnz 2f\n\t" + "3:\ttestl $0x3,%edx\n\t" /* CONTEXT_CONTROL | CONTEXT_INTEGER */ "jnz 1f\n\t" /* switch to user stack */ @@ -3227,8 +3252,10 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "1:\ttestl $0x2,%edx\n\t" /* CONTEXT_INTEGER */ "jnz 1f\n\t" + /* CONTEXT_CONTROL */ "movq (%rsp),%rcx\n\t" /* frame->rip */ "iretq\n" + /* CONTEXT_INTEGER */ "1:\tmovq 0x00(%rcx),%rax\n\t" "movq 0x18(%rcx),%rdx\n\t" "movq 0x30(%rcx),%r8\n\t" @@ -3237,6 +3264,20 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "movq 0x48(%rcx),%r11\n\t" "movq 0x10(%rcx),%rcx\n\t" "iretq\n" + /* RESTORE_FLAGS_INSTRUMENTATION */ +#ifdef __APPLE__ + "2:\tmovq %gs:0x30,%r10\n\t" + "movq 0x348(%r10),%r10\n\t" +#else + "2:\tmovq %gs:0x348,%r10\n\t" /* amd64_thread_data()->instrumentation_callback */ +#endif + "movq (%r10),%r10\n\t" + "test %r10,%r10\n\t" + "jz 3b\n\t" + "testl $0x2,%edx\n\t" /* CONTEXT_INTEGER */ + "jnz 1b\n\t" + "xchgq %r10,(%rsp)\n\t" + "iretq\n\t" /* pop rbp-based kernel stack cfi */ __ASM_CFI("\t.cfi_restore_state\n") @@ -3251,6 +3292,24 @@ __ASM_GLOBAL_FUNC( __wine_syscall_dispatcher, "jmp " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_return") ) +__ASM_GLOBAL_FUNC( __wine_syscall_dispatcher_instrumentation, +#ifdef __APPLE__ + "movq %gs:0x30,%rcx\n\t" + "movq 0x328(%rcx),%rcx\n\t" +#else + "movq %gs:0x328,%rcx\n\t" /* amd64_thread_data()->syscall_frame */ +#endif + "popq 0x70(%rcx)\n\t" /* frame->rip */ + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + __ASM_CFI_REG_IS_AT2(rip, rcx, 0xf0,0x00) + "pushfq\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset 8\n\t") + "popq 0x80(%rcx)\n\t" + __ASM_CFI(".cfi_adjust_cfa_offset -8\n\t") + "movl $0x10000,0xb4(%rcx)\n\t" /* frame->restore_flags <- RESTORE_FLAGS_INSTRUMENTATION */ + "jmp " __ASM_LOCAL_LABEL("__wine_syscall_dispatcher_prolog_end") ) + + /*********************************************************************** * __wine_unix_call_dispatcher */ diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 2b62d5c7754..23a5b49ef62 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -250,6 +250,8 @@ static inline UINT64 xstate_extended_features(void) return xstate_supported_features_mask & ~(UINT64)3; } +extern void set_process_instrumentation_callback( void *callback ); + extern void *get_cpu_area( USHORT machine ); extern void set_thread_id( TEB *teb, DWORD pid, DWORD tid ); extern NTSTATUS init_thread_stack( TEB *teb, ULONG_PTR limit, SIZE_T reserve_size, SIZE_T commit_size ); diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 817926d0f13..842a812ba35 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -844,7 +844,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) else return STATUS_INVALID_PARAMETER; case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ - if (len == sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) + if (len >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) { FIXME( "ProcessInstrumentationCallback stub\n" ); return STATUS_SUCCESS; From db90ca1be7a3f2b47100e1c2cb2dd94a8b356868 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 4 Sep 2024 16:20:28 -0600 Subject: [PATCH 16/67] ntdll: Call instrumentation callback for KiUserExceptionDispatcher on x64. (cherry picked from commit 15bf582a8f44efb07a100172c144b4eabe26a336) CW-Bug-Id: #24198 --- dlls/ntdll/tests/exception.c | 4 ++-- dlls/ntdll/unix/signal_x86_64.c | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index a358c19fab2..e2ca3016315 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5845,8 +5845,8 @@ static void test_instrumentation_callback(void) init_instrumentation_data( &curr_data ); DbgBreakPoint(); data = curr_data; - todo_wine ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); - todo_wine ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, + ok( data.call_count == 1 || broken( data.call_count == 2 ) /* before Win10 1809 */, "got %u.\n", data.call_count ); + ok( data.call_data[0].r10 == pKiUserExceptionDispatcher, "got %p, expected %p.\n", data.call_data[0].r10, pKiUserExceptionDispatcher ); pass = 0; diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index cf8adbe4dbc..926c9d791b7 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1453,6 +1453,7 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec NTSTATUS status; XSAVE_AREA_HEADER *src_xs; unsigned int xstate_size; + void *callback; if (rec->ExceptionCode == EXCEPTION_SINGLE_STEP) { @@ -1508,6 +1509,11 @@ static void setup_raise_exception( ucontext_t *sigcontext, EXCEPTION_RECORD *rec RSP_sig(sigcontext) = (ULONG_PTR)stack; /* clear single-step, direction, and align check flag */ EFL_sig(sigcontext) &= ~(0x100|0x400|0x40000); + if ((callback = instrumentation_callback)) + { + R10_sig(sigcontext) = RIP_sig(sigcontext); + RIP_sig(sigcontext) = (ULONG64)callback; + } leave_handler( sigcontext ); } From cabc00e156733e957b281672421563ee04403700 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 4 Sep 2024 16:21:12 -0600 Subject: [PATCH 17/67] ntdll: Call instrumentation callback for LdrInitializeThunk on x64. (cherry picked from commit d06d0580d487f91835b6a52c6fba7b23e38ee072) CW-Bug-Id: #24198 --- dlls/ntdll/tests/exception.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index e2ca3016315..1676f820130 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5911,7 +5911,7 @@ static void test_instrumentation_callback(void) { if (data.call_data[i].r10 == pLdrInitializeThunk) break; } - todo_wine ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); + ok( i < data.call_count, "LdrInitializeThunk not found.\n" ); init_instrumentation_data( &curr_data ); SetEvent( instrumentation_callback_thread_wait ); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 926c9d791b7..da60870c913 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2895,6 +2895,7 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB struct amd64_thread_data *thread_data = (struct amd64_thread_data *)&teb->GdiTebBatch; CONTEXT *ctx, context = { 0 }; I386_CONTEXT *wow_context; + void *callback; thread_data->syscall_table = KeServiceDescriptorTable; thread_data->xstate_features_mask = xstate_supported_features_mask; @@ -2985,6 +2986,11 @@ void call_init_thunk( LPTHREAD_START_ROUTINE entry, void *arg, BOOL suspend, TEB frame->restore_flags |= CONTEXT_INTEGER; frame->syscall_flags = syscall_flags; frame->syscall_cfa = syscall_cfa; + if ((callback = instrumentation_callback)) + { + frame->r10 = frame->rip; + frame->rip = (ULONG64)callback; + } pthread_sigmask( SIG_UNBLOCK, &server_block_set, NULL ); __wine_syscall_dispatcher_return( frame, 0 ); From dbd334bca5a9f3f5d4d9a14c797748a6dcd0d66f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 4 Sep 2024 15:50:34 -0600 Subject: [PATCH 18/67] ntdll: Call instrumentation callback for KiUserModeCallback on x64. (cherry picked from commit 73de94bbfa45797404b33706f809c195700f0a0f) CW-Bug-Id: #24198 --- dlls/ntdll/tests/exception.c | 2 +- dlls/ntdll/unix/signal_x86_64.c | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index 1676f820130..982486d0c1d 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -5942,7 +5942,7 @@ static void test_instrumentation_callback(void) if (data.call_data[i].r10 == pKiUserCallbackDispatcher) break; } - todo_wine ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); + ok( i < data.call_count, "KiUserCallbackDispatcher not found.\n" ); init_instrumentation_data( &curr_data ); memset(&info, 0, sizeof(info)); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index da60870c913..9955d144bb5 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1664,7 +1664,12 @@ __ASM_GLOBAL_FUNC( call_user_mode_callback, "movw 0x338(%r8),%fs\n" /* amd64_thread_data()->fs */ "1:\n\t" #endif - "jmpq *%rcx" ) /* func */ + "movq 0x348(%r8),%r10\n\t" /* amd64_thread_data()->instrumentation_callback */ + "movq (%r10),%r10\n\t" + "test %r10,%r10\n\t" + "jz 1f\n\t" + "xchgq %rcx,%r10\n\t" + "1\t:jmpq *%rcx" ) /* func */ /*********************************************************************** From 139fd552efbbce8df0dc0d2237211c6706d354c2 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Thu, 5 Sep 2024 12:55:07 -0600 Subject: [PATCH 19/67] ntdll: Support old parameter layout for NtSetInformationProcess( ProcessInstrumentationCallback ). (cherry picked from commit 20c8c55519cd3f26485b20e2484af1aaa430cdc2) CW-Bug-Id: #24198 --- dlls/ntdll/tests/info.c | 16 ++++++++++++++++ dlls/ntdll/unix/process.c | 7 +++++-- dlls/wow64/process.c | 2 +- dlls/wow64/struct32.h | 7 ------- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 881b79d3dc9..bd2ef32ead0 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -3695,6 +3695,22 @@ static void test_process_instrumentation_callback(void) ok( status == STATUS_SUCCESS || status == STATUS_INFO_LENGTH_MISMATCH || broken( status == STATUS_PRIVILEGE_NOT_HELD ) /* some versions and machines before Win10 */, "Got unexpected status %#lx.\n", status ); + + if (status) + { + win_skip( "NtSetInformationProcess failed, skipping further tests.\n" ); + return; + } + + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) + 4 ); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + status = NtSetInformationProcess( GetCurrentProcess(), ProcessInstrumentationCallback, + &info.Callback, sizeof(info.Callback) / 2 ); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); } static void test_debuggee_dbgport(int argc, char **argv) diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 23e26077e0c..772413348ea 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1674,15 +1674,18 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, case ProcessInstrumentationCallback: { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info; + void *callback; - if (size < sizeof(*instr)) return STATUS_INFO_LENGTH_MISMATCH; + if (size < sizeof(callback)) return STATUS_INFO_LENGTH_MISMATCH; + if (size >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION)) callback = instr->Callback; + else callback = *(void **)info; ret = STATUS_SUCCESS; if (handle != GetCurrentProcess()) { FIXME( "Setting ProcessInstrumentationCallback is not yet supported for other process.\n" ); break; } - set_process_instrumentation_callback( instr->Callback ); + set_process_instrumentation_callback( callback ); break; } diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 842a812ba35..0d22c7d01cb 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -844,7 +844,7 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) else return STATUS_INVALID_PARAMETER; case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ - if (len >= sizeof(PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32)) + if (len >= sizeof(void *)) { FIXME( "ProcessInstrumentationCallback stub\n" ); return STATUS_SUCCESS; diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index daa89b84dfc..01f1bac1885 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -228,13 +228,6 @@ typedef struct ULONG InheritedFromUniqueProcessId; } PROCESS_BASIC_INFORMATION32; -typedef struct -{ - ULONG Version; - ULONG Reserved; - ULONG Callback; -} PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION32; - typedef struct { ULONG ReserveSize; From 95fab2341bdb590a7cc0c1ffa47a02f38aa4dbc9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Sep 2024 17:21:20 -0600 Subject: [PATCH 20/67] fixup! ntdll: Support x86_64 syscall emulation. CW-Bug-Id: #24198 --- dlls/ntdll/unix/signal_x86_64.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 9955d144bb5..c76a08c5237 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -1870,6 +1870,7 @@ static void sigsys_handler( int signal, siginfo_t *siginfo, void *sigcontext ) frame->rcx = ctx->uc_mcontext.gregs[REG_RIP]; frame->eflags = ctx->uc_mcontext.gregs[REG_EFL]; frame->restore_flags = 0; + if (instrumentation_callback) frame->restore_flags |= RESTORE_FLAGS_INSTRUMENTATION; ctx->uc_mcontext.gregs[REG_RCX] = (ULONG_PTR)frame; ctx->uc_mcontext.gregs[REG_R11] = frame->eflags; ctx->uc_mcontext.gregs[REG_EFL] &= ~0x100; /* clear single-step flag */ From 2382761de620888c13f77b6eb101d31172d4d200 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Sep 2024 17:24:21 -0600 Subject: [PATCH 21/67] Revert "ntdll: Change module search order in LdrFindEntryForAddress()." This reverts commit dc6bd5820f5f3d2fba351a9d1ad0fdb2be9ad5c0. CW-Bug-Id: #24258 --- dlls/ntdll/loader.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index b88f9ec8acb..e74c0fd5497 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1929,10 +1929,10 @@ NTSTATUS WINAPI LdrFindEntryForAddress( const void *addr, PLDR_DATA_TABLE_ENTRY PLIST_ENTRY mark, entry; PLDR_DATA_TABLE_ENTRY mod; - mark = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList; - for (entry = mark->Blink; entry != mark; entry = entry->Blink) + mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList; + for (entry = mark->Flink; entry != mark; entry = entry->Flink) { - mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); if (mod->DllBase <= addr && (const char *)addr < (char*)mod->DllBase + mod->SizeOfImage) { From 6ea3cc3d07f3bf3d96a70ff6fe342fb6d49030af Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Sep 2024 15:28:58 -0600 Subject: [PATCH 22/67] ntdll: Implement RtlRbInsertNodeEx(). Based on Wine RB tree implementation in include/wine/rbtree.h. (cherry picked from commit 9a9bc22e11cfc0e69f4cac78a2b6dfe096b2df9d) CW-Bug-Id: #24258 --- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/rtl.c | 91 +++++++++++++++++++++++++++++++++++++++++++ include/ntdef.h | 6 +++ include/winternl.h | 1 + 4 files changed, 99 insertions(+) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index e33e85d5ca6..adbc5ad5e54 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -946,6 +946,7 @@ @ stdcall RtlRaiseStatus(long) @ stdcall RtlRandom(ptr) @ stdcall RtlRandomEx(ptr) +@ stdcall RtlRbInsertNodeEx(ptr ptr long ptr) @ stdcall RtlReAllocateHeap(long long ptr long) @ stub RtlReadMemoryStream @ stub RtlReadOutOfProcessMemoryStream diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c index 0d0b2d370f3..6fc331c31a7 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -2124,6 +2124,97 @@ void WINAPI RtlGetCurrentProcessorNumberEx(PROCESSOR_NUMBER *processor) processor->Reserved = 0; } +static RTL_BALANCED_NODE *rtl_node_parent( RTL_BALANCED_NODE *node ) +{ + return (RTL_BALANCED_NODE *)(node->ParentValue & ~(ULONG_PTR)RTL_BALANCED_NODE_RESERVED_PARENT_MASK); +} + +static void rtl_set_node_parent( RTL_BALANCED_NODE *node, RTL_BALANCED_NODE *parent ) +{ + node->ParentValue = (ULONG_PTR)parent | (node->ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK); +} + +static void rtl_rotate( RTL_RB_TREE *tree, RTL_BALANCED_NODE *n, int right ) +{ + RTL_BALANCED_NODE *child = n->Children[!right]; + RTL_BALANCED_NODE *parent = rtl_node_parent( n ); + + if (!parent) tree->root = child; + else if (parent->Left == n) parent->Left = child; + else parent->Right = child; + + n->Children[!right] = child->Children[right]; + if (n->Children[!right]) rtl_set_node_parent( n->Children[!right], n ); + child->Children[right] = n; + rtl_set_node_parent( child, parent ); + rtl_set_node_parent( n, child ); +} + +static void rtl_flip_color( RTL_BALANCED_NODE *node ) +{ + node->Red = !node->Red; + node->Left->Red = !node->Left->Red; + node->Right->Red = !node->Right->Red; +} + +/********************************************************************* + * RtlRbInsertNodeEx [NTDLL.@] + */ +void WINAPI RtlRbInsertNodeEx( RTL_RB_TREE *tree, RTL_BALANCED_NODE *parent, BOOLEAN right, RTL_BALANCED_NODE *node ) +{ + RTL_BALANCED_NODE *grandparent; + + TRACE( "tree %p, parent %p, right %d, node %p.\n", tree, parent, right, node ); + + node->ParentValue = (ULONG_PTR)parent; + node->Left = NULL; + node->Right = NULL; + + if (!parent) + { + tree->root = tree->min = node; + return; + } + if (right > 1) + { + ERR( "right %d.\n", right ); + return; + } + if (parent->Children[right]) + { + ERR( "parent %p, right %d, child %p.\n", parent, right, parent->Children[right] ); + return; + } + + node->Red = 1; + parent->Children[right] = node; + if (tree->min == parent && parent->Left == node) tree->min = node; + grandparent = rtl_node_parent( parent ); + while (parent->Red) + { + right = (parent == grandparent->Right); + if (grandparent->Children[!right] && grandparent->Children[!right]->Red) + { + node = grandparent; + rtl_flip_color( node ); + if (!(parent = rtl_node_parent( node ))) break; + grandparent = rtl_node_parent( parent ); + continue; + } + if (node == parent->Children[!right]) + { + node = parent; + rtl_rotate( tree, node, right ); + parent = rtl_node_parent( node ); + grandparent = rtl_node_parent( parent ); + } + parent->Red = 0; + grandparent->Red = 1; + rtl_rotate( tree, grandparent, !right ); + } + tree->root->Red = 0; +} + /*********************************************************************** * RtlIsProcessorFeaturePresent [NTDLL.@] */ diff --git a/include/ntdef.h b/include/ntdef.h index 8d04f83da5d..e840bd3f9fe 100644 --- a/include/ntdef.h +++ b/include/ntdef.h @@ -84,6 +84,12 @@ typedef struct _RTL_BALANCED_NODE #define RTL_BALANCED_NODE_RESERVED_PARENT_MASK 3 +typedef struct _RTL_RB_TREE +{ + RTL_BALANCED_NODE *root; + RTL_BALANCED_NODE *min; +} RTL_RB_TREE, *PRTL_RB_TREE; + #define RTL_CONSTANT_STRING(s) { sizeof(s) - sizeof(s[0]), sizeof(s), (void*)s } #endif /* _NTDEF_ */ diff --git a/include/winternl.h b/include/winternl.h index d151719f898..07706fa9ad9 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4920,6 +4920,7 @@ NTSYSAPI NTSTATUS WINAPI RtlQueueWorkItem(PRTL_WORK_ITEM_ROUTINE,PVOID,ULONG); NTSYSAPI void DECLSPEC_NORETURN WINAPI RtlRaiseStatus(NTSTATUS); NTSYSAPI ULONG WINAPI RtlRandom(PULONG); NTSYSAPI ULONG WINAPI RtlRandomEx(PULONG); +NTSYSAPI void WINAPI RtlRbInsertNodeEx(RTL_RB_TREE*,RTL_BALANCED_NODE*,BOOLEAN,RTL_BALANCED_NODE*); NTSYSAPI PVOID WINAPI RtlReAllocateHeap(HANDLE,ULONG,PVOID,SIZE_T) __WINE_ALLOC_SIZE(4) __WINE_DEALLOC(RtlFreeHeap,3); NTSYSAPI NTSTATUS WINAPI RtlRegisterWait(PHANDLE,HANDLE,RTL_WAITORTIMERCALLBACKFUNC,PVOID,ULONG,ULONG); NTSYSAPI void WINAPI RtlReleaseActivationContext(HANDLE); From 17b61f5cbdbd054e741cb06602e457fe7e3a8c6b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Sep 2024 15:26:48 -0600 Subject: [PATCH 23/67] ntdll: Implement RtlRbRemoveNode(). Based on Wine RB tree implementation in include/wine/rbtree.h. (cherry picked from commit 067b6a8c234f86fa64af5cb11efc65e43fce65f7) CW-Bug-Id: #24258 --- dlls/ntdll/ntdll.spec | 1 + dlls/ntdll/rtl.c | 83 +++++++++++++++++++++++++++++++++++++++++++ include/winternl.h | 1 + 3 files changed, 85 insertions(+) diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index adbc5ad5e54..af9c1048b46 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -947,6 +947,7 @@ @ stdcall RtlRandom(ptr) @ stdcall RtlRandomEx(ptr) @ stdcall RtlRbInsertNodeEx(ptr ptr long ptr) +@ stdcall RtlRbRemoveNode(ptr ptr) @ stdcall RtlReAllocateHeap(long long ptr long) @ stub RtlReadMemoryStream @ stub RtlReadOutOfProcessMemoryStream diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c index 6fc331c31a7..c08e7598344 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -2215,6 +2215,89 @@ void WINAPI RtlRbInsertNodeEx( RTL_RB_TREE *tree, RTL_BALANCED_NODE *parent, BOO tree->root->Red = 0; } +/********************************************************************* + * RtlRbRemoveNode [NTDLL.@] + */ +void WINAPI RtlRbRemoveNode( RTL_RB_TREE *tree, RTL_BALANCED_NODE *node ) +{ + RTL_BALANCED_NODE *iter = NULL, *child, *parent, *w; + BOOL need_fixup; + int right; + + TRACE( "tree %p, node %p.\n", tree, node ); + + if (node->Right && (node->Left || tree->min == node)) + { + for (iter = node->Right; iter->Left; iter = iter->Left) + ; + if (tree->min == node) tree->min = iter; + } + else if (tree->min == node) tree->min = rtl_node_parent( node ); + if (!iter || !node->Left) iter = node; + + child = iter->Left ? iter->Left : iter->Right; + + if (!(parent = rtl_node_parent( iter ))) tree->root = child; + else if (iter == parent->Left) parent->Left = child; + else parent->Right = child; + + if (child) rtl_set_node_parent( child, parent ); + + need_fixup = !iter->Red; + + if (node != iter) + { + *iter = *node; + if (!(w = rtl_node_parent( iter ))) tree->root = iter; + else if (node == w->Left) w->Left = iter; + else w->Right = iter; + + if (iter->Right) rtl_set_node_parent( iter->Right, iter ); + if (iter->Left) rtl_set_node_parent( iter->Left, iter ); + if (parent == node) parent = iter; + } + + if (!need_fixup) + { + if (tree->root) tree->root->Red = 0; + return; + } + + while (parent && !(child && child->Red)) + { + right = (child == parent->Right); + w = parent->Children[!right]; + if (w->Red) + { + w->Red = 0; + parent->Red = 1; + rtl_rotate( tree, parent, right ); + w = parent->Children[!right]; + } + if ((w->Left && w->Left->Red) || (w->Right && w->Right->Red)) + { + if (!(w->Children[!right] && w->Children[!right]->Red)) + { + w->Children[right]->Red = 0; + w->Red = 1; + rtl_rotate( tree, w, !right ); + w = parent->Children[!right]; + } + w->Red = parent->Red; + parent->Red = 0; + if (w->Children[!right]) w->Children[!right]->Red = 0; + rtl_rotate( tree, parent, right ); + child = NULL; + break; + } + w->Red = 1; + child = parent; + parent = rtl_node_parent( child ); + } + if (child) child->Red = 0; + if (tree->root) tree->root->Red = 0; +} + /*********************************************************************** * RtlIsProcessorFeaturePresent [NTDLL.@] */ diff --git a/include/winternl.h b/include/winternl.h index 07706fa9ad9..51fc92afae3 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -4921,6 +4921,7 @@ NTSYSAPI void DECLSPEC_NORETURN WINAPI RtlRaiseStatus(NTSTATUS); NTSYSAPI ULONG WINAPI RtlRandom(PULONG); NTSYSAPI ULONG WINAPI RtlRandomEx(PULONG); NTSYSAPI void WINAPI RtlRbInsertNodeEx(RTL_RB_TREE*,RTL_BALANCED_NODE*,BOOLEAN,RTL_BALANCED_NODE*); +NTSYSAPI void WINAPI RtlRbRemoveNode(RTL_RB_TREE*,RTL_BALANCED_NODE*); NTSYSAPI PVOID WINAPI RtlReAllocateHeap(HANDLE,ULONG,PVOID,SIZE_T) __WINE_ALLOC_SIZE(4) __WINE_DEALLOC(RtlFreeHeap,3); NTSYSAPI NTSTATUS WINAPI RtlRegisterWait(PHANDLE,HANDLE,RTL_WAITORTIMERCALLBACKFUNC,PVOID,ULONG,ULONG); NTSYSAPI void WINAPI RtlReleaseActivationContext(HANDLE); From 2b697f085c22c506facb4046c0bda0a4879d11a9 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 18 Sep 2024 20:13:40 -0600 Subject: [PATCH 24/67] ntdll/tests: Add tests for RTL RB tree. (cherry picked from commit 677209101b24fcdc148657b958ec721396c5881b) CW-Bug-Id: #24258 --- dlls/ntdll/tests/rtl.c | 148 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c index 34a8837b3cd..1d1ed2d9975 100644 --- a/dlls/ntdll/tests/rtl.c +++ b/dlls/ntdll/tests/rtl.c @@ -36,6 +36,7 @@ #include "ddk/ntifs.h" #include "wine/test.h" #include "wine/asm.h" +#include "wine/rbtree.h" #ifndef __WINE_WINTERNL_H @@ -106,6 +107,9 @@ static void * (WINAPI *pRtlFindExportedRoutineByName)(HMODULE,const char *); static NTSTATUS (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *); static NTSTATUS (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **); static NTSTATUS (WINAPI *pLdrUnregisterDllNotification)(void *); +static void (WINAPI *pRtlRbInsertNodeEx)(RTL_RB_TREE *, RTL_BALANCED_NODE *, BOOLEAN, RTL_BALANCED_NODE *); +static void (WINAPI *pRtlRbRemoveNode)(RTL_RB_TREE *, RTL_BALANCED_NODE *); + static HMODULE hkernel32 = 0; static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); @@ -149,6 +153,8 @@ static void InitFunctionPtrs(void) pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules"); pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification"); pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification"); + pRtlRbInsertNodeEx = (void *)GetProcAddress(hntdll, "RtlRbInsertNodeEx"); + pRtlRbRemoveNode = (void *)GetProcAddress(hntdll, "RtlRbRemoveNode"); } hkernel32 = LoadLibraryA("kernel32.dll"); ok(hkernel32 != 0, "LoadLibrary failed\n"); @@ -3675,6 +3681,147 @@ static void test_RtlFindExportedRoutineByName(void) ok( proc == NULL, "Shouldn't find forwarded function\n" ); } +struct test_rb_tree_entry +{ + int value; + struct rb_entry wine_rb_entry; + RTL_BALANCED_NODE rtl_entry; +}; + +static int test_rb_tree_entry_compare( const void *key, const struct wine_rb_entry *entry ) +{ + const struct test_rb_tree_entry *t = WINE_RB_ENTRY_VALUE(entry, struct test_rb_tree_entry, wine_rb_entry); + const int *value = key; + + return *value - t->value; +} + +static int test_rtl_rb_tree_entry_compare( const void *key, const RTL_BALANCED_NODE *entry ) +{ + const struct test_rb_tree_entry *t = CONTAINING_RECORD(entry, struct test_rb_tree_entry, rtl_entry); + const int *value = key; + + return *value - t->value; +} + +static int rtl_rb_tree_put( RTL_RB_TREE *tree, const void *key, RTL_BALANCED_NODE *entry, + int (*compare_func)( const void *key, const RTL_BALANCED_NODE *entry )) +{ + RTL_BALANCED_NODE *parent = tree->root; + BOOLEAN right = 0; + int c; + + while (parent) + { + if (!(c = compare_func( key, parent ))) return -1; + right = c > 0; + if (!parent->Children[right]) break; + parent = parent->Children[right]; + } + pRtlRbInsertNodeEx( tree, parent, right, entry ); + return 0; +} + +static struct test_rb_tree_entry *test_rb_tree_entry_from_wine_rb( struct rb_entry *entry ) +{ + if (!entry) return NULL; + return CONTAINING_RECORD(entry, struct test_rb_tree_entry, wine_rb_entry); +} + +static struct test_rb_tree_entry *test_rb_tree_entry_from_rtl_rb( RTL_BALANCED_NODE *entry ) +{ + if (!entry) return NULL; + return CONTAINING_RECORD(entry, struct test_rb_tree_entry, rtl_entry); +} + +static struct test_rb_tree_entry *test_rb_tree_entry_rtl_parent( struct test_rb_tree_entry *node ) +{ + return test_rb_tree_entry_from_rtl_rb( (void *)(node->rtl_entry.ParentValue + & ~(ULONG_PTR)RTL_BALANCED_NODE_RESERVED_PARENT_MASK) ); +} + +static void test_rb_tree(void) +{ + static int test_values[] = { 44, 51, 6, 66, 69, 20, 87, 80, 72, 86, 90, 16, 54, 61, 62, 14, 27, 39, 42, 41 }; + static const unsigned int count = ARRAY_SIZE(test_values); + + struct test_rb_tree_entry *nodes, *parent, *parent2; + RTL_BALANCED_NODE *prev_min_entry = NULL; + int ret, is_red, min_val; + struct rb_tree wine_tree; + RTL_RB_TREE rtl_tree; + unsigned int i; + + if (!pRtlRbInsertNodeEx) + { + win_skip( "RtlRbInsertNodeEx is not present.\n" ); + return; + } + + memset( &rtl_tree, 0, sizeof(rtl_tree) ); + nodes = malloc( count * sizeof(*nodes) ); + memset( nodes, 0xcc, count * sizeof(*nodes) ); + + min_val = test_values[0]; + rb_init( &wine_tree, test_rb_tree_entry_compare ); + for (i = 0; i < count; ++i) + { + winetest_push_context( "i %u", i ); + nodes[i].value = test_values[i]; + ret = rb_put( &wine_tree, &nodes[i].value, &nodes[i].wine_rb_entry ); + ok( !ret, "got %d.\n", ret ); + parent = test_rb_tree_entry_from_wine_rb( nodes[i].wine_rb_entry.parent ); + ret = rtl_rb_tree_put( &rtl_tree, &nodes[i].value, &nodes[i].rtl_entry, test_rtl_rb_tree_entry_compare ); + ok( !ret, "got %d.\n", ret ); + parent2 = test_rb_tree_entry_rtl_parent( &nodes[i] ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + is_red = nodes[i].rtl_entry.ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK; + ok( is_red == rb_is_red( &nodes[i].wine_rb_entry ), "got %d, expected %d.\n", is_red, + rb_is_red( &nodes[i].wine_rb_entry )); + + parent = test_rb_tree_entry_from_wine_rb( wine_tree.root ); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.root ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + if (nodes[i].value <= min_val) + { + min_val = nodes[i].value; + prev_min_entry = &nodes[i].rtl_entry; + } + ok( rtl_tree.min == prev_min_entry, "unexpected min tree entry.\n" ); + winetest_pop_context(); + } + + for (i = 0; i < count; ++i) + { + struct test_rb_tree_entry *node; + + winetest_push_context( "i %u", i ); + rb_remove( &wine_tree, &nodes[i].wine_rb_entry ); + pRtlRbRemoveNode( &rtl_tree, &nodes[i].rtl_entry ); + + parent = test_rb_tree_entry_from_wine_rb( wine_tree.root ); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.root ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + + parent = test_rb_tree_entry_from_wine_rb( rb_head( wine_tree.root )); + parent2 = test_rb_tree_entry_from_rtl_rb( rtl_tree.min ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + + RB_FOR_EACH_ENTRY(node, &wine_tree, struct test_rb_tree_entry, wine_rb_entry) + { + is_red = node->rtl_entry.ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK; + ok( is_red == rb_is_red( &node->wine_rb_entry ), "got %d, expected %d.\n", is_red, rb_is_red( &node->wine_rb_entry )); + parent = test_rb_tree_entry_from_wine_rb( node->wine_rb_entry.parent ); + parent2 = test_rb_tree_entry_rtl_parent( node ); + ok( parent == parent2, "got %p, %p.\n", parent, parent2 ); + } + winetest_pop_context(); + } + ok( !rtl_tree.root, "got %p.\n", rtl_tree.root ); + ok( !rtl_tree.min, "got %p.\n", rtl_tree.min ); + free(nodes); +} + START_TEST(rtl) { InitFunctionPtrs(); @@ -3722,4 +3869,5 @@ START_TEST(rtl) test_RtlInitializeSid(); test_RtlValidSecurityDescriptor(); test_RtlFindExportedRoutineByName(); + test_rb_tree(); } From efadc74fc3cc52b17d9a871b34e62018096df9ea Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Sep 2024 16:55:53 -0600 Subject: [PATCH 25/67] ntdll: Fill LDR_DATA_TABLE_ENTRY.BaseAddressIndexNode. (cherry picked from commit 4f3c7ef9fa556bd09f0001238fad8aa99ed760b7) CW-Bug-Id: #24258 --- dlls/kernel32/tests/module.c | 93 ++++++++++++++++++++++++++++++++++++ dlls/ntdll/loader.c | 35 ++++++++++++++ 2 files changed, 128 insertions(+) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index 89accdcf483..0262e44a3ee 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -1766,6 +1766,98 @@ static void test_known_dlls_load(void) DeleteFileA( dll ); } + +static RTL_BALANCED_NODE *rtl_node_parent( RTL_BALANCED_NODE *node ) +{ + return (RTL_BALANCED_NODE *)(node->ParentValue & ~(ULONG_PTR)RTL_BALANCED_NODE_RESERVED_PARENT_MASK); +} + +static unsigned int check_address_index_tree( RTL_BALANCED_NODE *node ) +{ + LDR_DATA_TABLE_ENTRY *mod; + unsigned int count; + char *base; + + if (!node) return 0; + ok( (node->ParentValue & RTL_BALANCED_NODE_RESERVED_PARENT_MASK) <= 1, "got ParentValue %#Ix.\n", + node->ParentValue ); + + mod = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + base = mod->DllBase; + if (node->Left) + { + mod = CONTAINING_RECORD(node->Left, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + ok( (char *)mod->DllBase < base, "wrong ordering.\n" ); + } + if (node->Right) + { + mod = CONTAINING_RECORD(node->Right, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + ok( (char *)mod->DllBase > base, "wrong ordering.\n" ); + } + + count = check_address_index_tree( node->Left ); + count += check_address_index_tree( node->Right ); + return count + 1; +} + +static void test_base_address_index_tree(void) +{ + LIST_ENTRY *first = &NtCurrentTeb()->Peb->LdrData->InLoadOrderModuleList; + unsigned int tree_count, list_count = 0; + LDR_DATA_TABLE_ENTRY *mod, *mod2; + RTL_BALANCED_NODE *root, *node; + LDR_DDAG_NODE *ddag_node; + NTSTATUS status; + HMODULE hexe; + char *base; + + /* Check for old LDR data strcuture. */ + hexe = GetModuleHandleW( NULL ); + ok( !!hexe, "Got NULL exe handle.\n" ); + status = LdrFindEntryForAddress( hexe, &mod ); + ok( !status, "got %#lx.\n", status ); + if (!(ddag_node = mod->DdagNode)) + { + win_skip( "DdagNode is NULL, skipping tests.\n" ); + return; + } + ok( !!ddag_node->Modules.Flink, "Got NULL module link.\n" ); + mod2 = CONTAINING_RECORD(ddag_node->Modules.Flink, LDR_DATA_TABLE_ENTRY, NodeModuleLink); + ok( mod2 == mod || broken( (void **)mod2 == (void **)mod - 1 ), "got %p, expected %p.\n", mod2, mod ); + if (mod2 != mod) + { + win_skip( "Old LDR_DATA_TABLE_ENTRY structure, skipping tests.\n" ); + return; + } + + mod = CONTAINING_RECORD(first->Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + ok( mod->BaseAddressIndexNode.ParentValue || mod->BaseAddressIndexNode.Left || mod->BaseAddressIndexNode.Right, + "got zero BaseAddressIndexNode.\n" ); + root = &mod->BaseAddressIndexNode; + while (rtl_node_parent( root )) + root = rtl_node_parent( root ); + tree_count = check_address_index_tree( root ); + for (LIST_ENTRY *entry = first->Flink; entry != first; entry = entry->Flink) + { + ++list_count; + mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); + base = mod->DllBase; + node = root; + mod2 = NULL; + while (1) + { + ok( !!node, "got NULL.\n" ); + if (!node) break; + mod2 = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + if (base == (char *)mod2->DllBase) break; + if (base < (char *)mod2->DllBase) node = node->Left; + else node = node->Right; + } + ok( base == (char *)mod2->DllBase, "module %s not found.\n", debugstr_w(mod->BaseDllName.Buffer) ); + } + ok( tree_count == list_count, "count mismatch %u, %u.\n", tree_count, list_count ); +} + START_TEST(module) { WCHAR filenameW[MAX_PATH]; @@ -1804,4 +1896,5 @@ START_TEST(module) test_ddag_node(); test_tls_links(); test_known_dlls_load(); + test_base_address_index_tree(); } diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index e74c0fd5497..055129d06db 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -183,6 +183,8 @@ static PEB_LDR_DATA ldr = { &ldr.InInitializationOrderModuleList, &ldr.InInitializationOrderModuleList } }; +static RTL_RB_TREE base_address_index_tree; + static RTL_BITMAP tls_bitmap; static RTL_BITMAP tls_expansion_bitmap; @@ -267,6 +269,24 @@ static void module_push_unload_trace( const WINE_MODREF *wm ) unload_trace_ptr = unload_traces; } +static int rtl_rb_tree_put( RTL_RB_TREE *tree, const void *key, RTL_BALANCED_NODE *entry, + int (*compare_func)( const void *key, const RTL_BALANCED_NODE *entry )) +{ + RTL_BALANCED_NODE *parent = tree->root; + BOOLEAN right = 0; + int c; + + while (parent) + { + if (!(c = compare_func( key, parent ))) return -1; + right = c > 0; + if (!parent->Children[right]) break; + parent = parent->Children[right]; + } + RtlRbInsertNodeEx( tree, parent, right, entry ); + return 0; +} + #ifdef __arm64ec__ static void update_hybrid_metadata( void *module, IMAGE_NT_HEADERS *nt, @@ -574,6 +594,17 @@ static void call_ldr_notifications( ULONG reason, LDR_DATA_TABLE_ENTRY *module ) } } +/* compare base address */ +static int base_address_compare( const void *key, const RTL_BALANCED_NODE *entry ) +{ + const LDR_DATA_TABLE_ENTRY *mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + const char *base = key; + + if (base < (char *)mod->DllBase) return -1; + if (base > (char *)mod->DllBase) return 1; + return 0; +} + /************************************************************************* * hash_basename * @@ -1595,6 +1626,8 @@ static WINE_MODREF *alloc_module( HMODULE hModule, const UNICODE_STRING *nt_name &wm->ldr.InMemoryOrderLinks); InsertTailList(&hash_table[hash_basename(wm->ldr.BaseDllName.Buffer)], &wm->ldr.HashLinks); + if (rtl_rb_tree_put( &base_address_index_tree, wm->ldr.DllBase, &wm->ldr.BaseAddressIndexNode, base_address_compare )) + ERR( "rtl_rb_tree_put failed.\n" ); /* wait until init is called for inserting into InInitializationOrderModuleList */ wm->ldr.InInitializationOrderLinks.Flink = NULL; @@ -2356,6 +2389,7 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, RemoveEntryList(&wm->ldr.InLoadOrderLinks); RemoveEntryList(&wm->ldr.InMemoryOrderLinks); RemoveEntryList(&wm->ldr.HashLinks); + RtlRbRemoveNode( &base_address_index_tree, &wm->ldr.BaseAddressIndexNode ); /* FIXME: there are several more dangling references * left. Including dlls loaded by this dll before the @@ -4123,6 +4157,7 @@ static void free_modref( WINE_MODREF *wm ) RemoveEntryList(&wm->ldr.InLoadOrderLinks); RemoveEntryList(&wm->ldr.InMemoryOrderLinks); RemoveEntryList(&wm->ldr.HashLinks); + RtlRbRemoveNode( &base_address_index_tree, &wm->ldr.BaseAddressIndexNode ); if (wm->ldr.InInitializationOrderLinks.Flink) RemoveEntryList(&wm->ldr.InInitializationOrderLinks); From 713554ec9314306426c96f1b67603eac39dba964 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Sep 2024 17:06:12 -0600 Subject: [PATCH 26/67] ntdll: Use base address tree in get_modref(). (cherry picked from commit f7867d552464a36a3b43414949ee5676dcb38d56) CW-Bug-Id: #24258 --- dlls/ntdll/loader.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 055129d06db..9923091b74a 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -287,6 +287,20 @@ static int rtl_rb_tree_put( RTL_RB_TREE *tree, const void *key, RTL_BALANCED_NOD return 0; } +static RTL_BALANCED_NODE *rtl_rb_tree_get( RTL_RB_TREE *tree, const void *key, + int (*compare_func)( const void *key, const RTL_BALANCED_NODE *entry )) +{ + RTL_BALANCED_NODE *parent = tree->root; + int c; + + while (parent) + { + if (!(c = compare_func( key, parent ))) return parent; + parent = parent->Children[c > 0]; + } + return NULL; +} + #ifdef __arm64ec__ static void update_hybrid_metadata( void *module, IMAGE_NT_HEADERS *nt, @@ -640,19 +654,14 @@ static ULONG hash_basename(const WCHAR *basename) */ static WINE_MODREF *get_modref( HMODULE hmod ) { - PLIST_ENTRY mark, entry; PLDR_DATA_TABLE_ENTRY mod; + RTL_BALANCED_NODE *node; if (cached_modref && cached_modref->ldr.DllBase == hmod) return cached_modref; - mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList; - for (entry = mark->Flink; entry != mark; entry = entry->Flink) - { - mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); - if (mod->DllBase == hmod) - return cached_modref = CONTAINING_RECORD(mod, WINE_MODREF, ldr); - } - return NULL; + if (!(node = rtl_rb_tree_get( &base_address_index_tree, hmod, base_address_compare ))) return NULL; + mod = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + return cached_modref = CONTAINING_RECORD(mod, WINE_MODREF, ldr); } From 5df0fa57953b673de7123baf9d369e5fb0c7b253 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 20 Sep 2024 17:22:43 -0600 Subject: [PATCH 27/67] ntdll: Use base address tree in LdrFindEntryForAddress(). (cherry picked from commit 30cf3c477766f48dac5233eeb666c70c42f16692) CW-Bug-Id: #24258 --- dlls/ntdll/loader.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 9923091b74a..d7fc458dde1 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1961,6 +1961,17 @@ NTSTATUS WINAPI LdrDisableThreadCalloutsForDll(HMODULE hModule) return ret; } +/* compare base address */ +static int module_address_search_compare( const void *key, const RTL_BALANCED_NODE *entry ) +{ + const LDR_DATA_TABLE_ENTRY *mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + const char *addr = key; + + if (addr < (char *)mod->DllBase) return -1; + if (addr >= (char *)mod->DllBase + mod->SizeOfImage) return 1; + return 0; +} + /****************************************************************** * LdrFindEntryForAddress (NTDLL.@) * @@ -1968,21 +1979,12 @@ NTSTATUS WINAPI LdrDisableThreadCalloutsForDll(HMODULE hModule) */ NTSTATUS WINAPI LdrFindEntryForAddress( const void *addr, PLDR_DATA_TABLE_ENTRY *pmod ) { - PLIST_ENTRY mark, entry; - PLDR_DATA_TABLE_ENTRY mod; + RTL_BALANCED_NODE *node; - mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList; - for (entry = mark->Flink; entry != mark; entry = entry->Flink) - { - mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); - if (mod->DllBase <= addr && - (const char *)addr < (char*)mod->DllBase + mod->SizeOfImage) - { - *pmod = mod; - return STATUS_SUCCESS; - } - } - return STATUS_NO_MORE_ENTRIES; + if (!(node = rtl_rb_tree_get( &base_address_index_tree, addr, module_address_search_compare ))) + return STATUS_NO_MORE_ENTRIES; + *pmod = CONTAINING_RECORD(node, LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode); + return STATUS_SUCCESS; } /****************************************************************** From a3c850fa9be658fdfa90b89d9f2912538b12a00b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 17 Sep 2024 15:01:54 -0600 Subject: [PATCH 28/67] ntdll: Implement NtSetInformationProcess( ProcessTlsInformation ). CW-Bug-Id: #24258 --- dlls/ntdll/tests/info.c | 265 +++++++++++++++++++++++++++++++++ dlls/ntdll/unix/process.c | 27 ++++ dlls/ntdll/unix/unix_private.h | 1 + dlls/ntdll/unix/virtual.c | 89 +++++++++++ dlls/wow64/process.c | 33 ++++ dlls/wow64/struct32.h | 24 +++ include/winternl.h | 35 +++++ 7 files changed, 474 insertions(+) diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index bd2ef32ead0..2f33681d64b 100644 --- a/dlls/ntdll/tests/info.c +++ b/dlls/ntdll/tests/info.c @@ -3979,6 +3979,270 @@ static void test_processor_idle_cycle_time(void) ok( size == cpu_count * sizeof(*buffer), "got %#lx.\n", size ); } +static DWORD WINAPI test_set_process_tls_info_thread(void *param) +{ + return 0; +} + +static void test_set_process_tls_info(void) +{ + THREAD_BASIC_INFORMATION tbi; + TEB *teb = NtCurrentTeb(), *thread_teb; + char buffer[1024]; + PROCESS_TLS_INFORMATION *tlsinfo = (PROCESS_TLS_INFORMATION *)buffer; + void **save_tls_pointers[2]; + void *tls_pointer[4], *new_tls_pointer[4], *tls_pointer2[4], *new_tls_pointer2[4]; + unsigned int i; + DWORD thread_id, curr_thread_id = GetCurrentThreadId(); + NTSTATUS status; + HANDLE thread; + BOOL wow = is_wow64 && !old_wow64; + + thread = CreateThread( NULL, 0, test_set_process_tls_info_thread, NULL, CREATE_SUSPENDED, &thread_id ); + do + { + /* workaround currently present Wine bug when thread teb may be not available immediately + * after creating a thread before it is initialized on the Unix side. */ + Sleep( 1 ); + status = NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ); + ok( !status, "got %#lx.\n", status ); + } while (!(thread_teb = tbi.TebBaseAddress)); + ok( !thread_teb->ThreadLocalStoragePointer, "got %p.\n", thread_teb->ThreadLocalStoragePointer ); + + save_tls_pointers[0] = teb->ThreadLocalStoragePointer; + save_tls_pointers[1] = thread_teb->ThreadLocalStoragePointer; + + for (i = 0; i < ARRAY_SIZE(tls_pointer); ++i) + { + tls_pointer[i] = (void *)(ULONG_PTR)(i + 1); + new_tls_pointer[i] = (void *)(ULONG_PTR)(i + 20); + } + teb->ThreadLocalStoragePointer = tls_pointer; + + /* This flag probably requests WOW64 teb update. */ + tlsinfo->Flags = 1; + tlsinfo->ThreadDataCount = 1; + tlsinfo->OperationType = ProcessTlsReplaceVector; + tlsinfo->TlsVectorLength = 2; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[0].ThreadId = thread_id; + tlsinfo->ThreadData[0].TlsVector = new_tls_pointer; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + if (wow) + { + ok( !status, "got %#lx.\n", status ); + ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + } + else + { + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( tlsinfo->Flags == 1, "got %#lx.\n", tlsinfo->Flags ); + ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + } + + /* Other PROCESS_TLS_INFORMATION flags are invalid. STATUS_INFO_LENGTH_MISMATCH is weird but that's for any flag + * besides 1. */ + tlsinfo->Flags = 2; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[0].ThreadId = thread_id; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_INFO_LENGTH_MISMATCH, "got %#lx.\n", status ); + ok( tlsinfo->Flags == 2, "got %#lx.\n", tlsinfo->Flags ); + ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + + /* Nonzero THREAD_TLS_INFORMATION flags on input are invalid. */ + tlsinfo->Flags = 0; + tlsinfo->ThreadData[0].Flags = 1; + tlsinfo->ThreadData[0].ThreadId = thread_id; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadData[0].Flags == 1, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->OperationType = MaxProcessTlsOperation; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + /* Unknown operation type. */ + ok( status == STATUS_INFO_LENGTH_MISMATCH || status == STATUS_INVALID_PARAMETER, "got %#lx.\n", status ); + + tlsinfo->OperationType = ProcessTlsReplaceVector; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount]) + 8); + /* Larger data size. */ + ok( (!wow && status == STATUS_INFO_LENGTH_MISMATCH) || (wow && !status), "got %#lx.\n", status ); + + tlsinfo->ThreadData[0].Flags = 0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + /* ThreadId is output parameter, ignored on input and contains the thread where the data were assigned on + * output. */ + ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + /* TlsVector contains the repaced vector on output. */ + ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + for (i = 0; i < ARRAY_SIZE(tls_pointer); ++i) + { + ok( tls_pointer[i] == (void *)(ULONG_PTR)(i + 1), "got %p.\n", tls_pointer ); + /* TlsVectorLength pointers are copied from the old vector to the new one. */ + if (i < 2) + ok( new_tls_pointer[i] == (void *)(ULONG_PTR)(i + 1), "got %p.\n", tls_pointer ); + else + ok( new_tls_pointer[i] == (void *)(ULONG_PTR)(i + 20), "got %p.\n", tls_pointer ); + } + + teb->ThreadLocalStoragePointer = NULL; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[0].TlsVector = new_tls_pointer; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + /* Threads with NULL ThreadLocalStoragePointer are ignored. */ + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].TlsVector == new_tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + + memcpy( tls_pointer2, tls_pointer, sizeof(tls_pointer2) ); + thread_teb->ThreadLocalStoragePointer = tls_pointer2; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( thread_teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( tlsinfo->ThreadData[0].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer2, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + + /* Two eligible threads, data for only one are provided, that succeeds. */ + teb->ThreadLocalStoragePointer = tls_pointer; + thread_teb->ThreadLocalStoragePointer = tls_pointer2; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[0].TlsVector = new_tls_pointer; + thread_teb->ThreadLocalStoragePointer = tls_pointer2; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( tlsinfo->ThreadDataCount == 1, "got %#lx.\n", tlsinfo->ThreadDataCount ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + ok( thread_teb->ThreadLocalStoragePointer == tls_pointer2, "wrong vector.\n" ); + + /* Set for both threads at once as probably intended. Provide an extra data for the missing third thread + * which won't be used. */ + teb->ThreadLocalStoragePointer = tls_pointer; + thread_teb->ThreadLocalStoragePointer = tls_pointer2; + memcpy( new_tls_pointer2, new_tls_pointer, sizeof(new_tls_pointer2) ); + tlsinfo->ThreadDataCount = 3; + tlsinfo->ThreadData[0].TlsVector = new_tls_pointer; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[1].TlsVector = new_tls_pointer2; + tlsinfo->ThreadData[1].Flags = 0; + tlsinfo->ThreadData[2].TlsVector = (void *)0xdeadbeef; + tlsinfo->ThreadData[2].Flags = 0; + tlsinfo->ThreadData[2].ThreadId = 0xdeadbeef; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + ok( tlsinfo->ThreadData[1].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[1].Flags ); + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( tlsinfo->ThreadData[1].ThreadId == thread_id, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId ); + ok( tlsinfo->ThreadData[1].TlsVector == tls_pointer2, "got %p.\n", tlsinfo->ThreadData[1].TlsVector ); + ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags ); + ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId ); + ok( tlsinfo->ThreadData[2].TlsVector == (void *)0xdeadbeef, "got %p.\n", tlsinfo->ThreadData[2].TlsVector ); + + /* Test with unaccessible data. */ + tlsinfo->ThreadData[0].TlsVector = new_tls_pointer; + tlsinfo->ThreadData[0].ThreadId = 0; + tlsinfo->ThreadData[0].Flags = 0; + tlsinfo->ThreadData[1].TlsVector = new_tls_pointer2; + tlsinfo->ThreadData[1].ThreadId = 0; + tlsinfo->ThreadData[1].Flags = 0; + teb->ThreadLocalStoragePointer = tls_pointer; + thread_teb->ThreadLocalStoragePointer = (void *)0xdeadbee0; + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_ACCESS_VIOLATION, "got %#lx.\n", status ); + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount ); + if (wow) + { + ok( !tlsinfo->ThreadData[0].Flags, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( !tlsinfo->ThreadData[0].ThreadId, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( tlsinfo->ThreadData[0].TlsVector == new_tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + } + else + { + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( tlsinfo->ThreadData[0].ThreadId == curr_thread_id, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( tlsinfo->ThreadData[0].TlsVector == tls_pointer, "got %p.\n", tlsinfo->ThreadData[0].TlsVector ); + } + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( !tlsinfo->ThreadData[1].Flags, "got %#lx.\n", tlsinfo->ThreadData[1].Flags ); + ok( teb->ThreadLocalStoragePointer == new_tls_pointer, "wrong vector.\n" ); + ok( !tlsinfo->ThreadData[1].ThreadId, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId ); + ok( tlsinfo->ThreadData[1].TlsVector == new_tls_pointer2, "got %p.\n", tlsinfo->ThreadData[1].TlsVector ); + ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags ); + ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId ); + ok( tlsinfo->ThreadData[2].TlsVector == (void *)0xdeadbeef, "got %p.\n", tlsinfo->ThreadData[2].TlsVector ); + + /* Test replacing TLS index. */ + teb->ThreadLocalStoragePointer = new_tls_pointer; + thread_teb->ThreadLocalStoragePointer = new_tls_pointer2; + new_tls_pointer[1] = (void *)0xcccccccc; + new_tls_pointer2[1] = (void *)0xdddddddd; + tlsinfo->ThreadDataCount = 3; + tlsinfo->OperationType = ProcessTlsReplaceIndex; + tlsinfo->TlsIndex = 1; + for (i = 0; i < 3; ++i) + { + tlsinfo->ThreadData[i].Flags = 0; + tlsinfo->ThreadData[i].ThreadId = 0xdeadbeef; + tlsinfo->ThreadData[i].TlsModulePointer = (void *)((ULONG_PTR)i + 1); + } + status = NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, tlsinfo, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[tlsinfo->ThreadDataCount])); + ok( status == STATUS_SUCCESS, "got %#lx.\n", status ); + ok( !tlsinfo->Flags, "got %#lx.\n", tlsinfo->Flags ); + ok( tlsinfo->ThreadDataCount == 3, "got %#lx.\n", tlsinfo->ThreadDataCount ); + ok( tlsinfo->ThreadData[0].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[0].Flags ); + ok( (ULONG_PTR)new_tls_pointer[1] == 1, "got %p.\n", new_tls_pointer[1] ); + ok( tlsinfo->ThreadData[0].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[0].ThreadId ); + ok( (ULONG_PTR)tlsinfo->ThreadData[0].TlsModulePointer == 0xcccccccc, "got %p.\n", tlsinfo->ThreadData[0].TlsModulePointer ); + ok( tlsinfo->ThreadData[1].Flags == THREAD_TLS_INFORMATION_ASSIGNED, "got %#lx.\n", tlsinfo->ThreadData[1].Flags ); + ok( (ULONG_PTR)new_tls_pointer2[1] == 2, "got %p.\n", new_tls_pointer2[1] ); + ok( tlsinfo->ThreadData[1].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[1].ThreadId ); + ok( (ULONG_PTR)tlsinfo->ThreadData[1].TlsModulePointer == 0xdddddddd, "got %p.\n", tlsinfo->ThreadData[1].TlsModulePointer ); + ok( !tlsinfo->ThreadData[2].Flags, "got %#lx.\n", tlsinfo->ThreadData[2].Flags ); + ok( tlsinfo->ThreadData[2].ThreadId == 0xdeadbeef, "got %#Ix.\n", tlsinfo->ThreadData[2].ThreadId ); + ok( (ULONG_PTR)tlsinfo->ThreadData[2].TlsModulePointer == 3, "got %p.\n", tlsinfo->ThreadData[2].TlsModulePointer ); + + /* Restore original TLS data. */ + teb->ThreadLocalStoragePointer = save_tls_pointers[0]; + thread_teb->ThreadLocalStoragePointer = save_tls_pointers[1]; + ResumeThread( thread ); + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); +} + START_TEST(info) { char **argv; @@ -4057,4 +4321,5 @@ START_TEST(info) test_system_debug_control(); test_process_id(); test_processor_idle_cycle_time(); + test_set_process_tls_info(); } diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 772413348ea..3085510243f 100644 --- a/dlls/ntdll/unix/process.c +++ b/dlls/ntdll/unix/process.c @@ -1612,6 +1612,33 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, process_error_mode = *(UINT *)info; break; + case ProcessTlsInformation: + { + PROCESS_TLS_INFORMATION *t = info; + unsigned int i; + + if (handle != NtCurrentProcess()) + { + FIXME( "ProcessTlsInformation is not supported for the other process yet, handle %p.\n", handle ); + return STATUS_INVALID_HANDLE; + } + + if (size < sizeof(*t) || size != offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount])) + return STATUS_INFO_LENGTH_MISMATCH; + if (t->Flags & ~PROCESS_TLS_INFORMATION_WOW64) + { + WARN( "ProcessTlsInformation: unknown flags %#x.\n", (int)t->Flags ); + return STATUS_INFO_LENGTH_MISMATCH; + } + if (t->Flags & PROCESS_TLS_INFORMATION_WOW64 && !(is_win64 && is_wow64())) + return STATUS_INVALID_PARAMETER; + if (t->OperationType >= MaxProcessTlsOperation) return STATUS_INFO_LENGTH_MISMATCH; + for (i = 0; i < t->ThreadDataCount; ++i) + if (t->ThreadData[i].Flags) return STATUS_INVALID_PARAMETER; + ret = virtual_set_tls_information( t ); + break; + } + case ProcessAffinityMask: { const ULONG_PTR system_mask = get_system_affinity_mask(); diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 23a5b49ef62..82fc9d2bc02 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -286,6 +286,7 @@ extern TEB *virtual_alloc_first_teb(void); extern NTSTATUS virtual_alloc_teb( TEB **ret_teb ); extern void virtual_free_teb( TEB *teb ); extern NTSTATUS virtual_clear_tls_index( ULONG index ); +extern NTSTATUS virtual_set_tls_information( PROCESS_TLS_INFORMATION *t ); extern NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR limit_low, ULONG_PTR limit_high, SIZE_T reserve_size, SIZE_T commit_size, BOOL guard_page ); extern void virtual_map_user_shared_data(void); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 0ee452bc4fc..2063bdbaa4c 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4308,6 +4308,95 @@ NTSTATUS virtual_clear_tls_index( ULONG index ) } +/*********************************************************************** + * virtual_set_tls_information_teb + */ +static NTSTATUS virtual_set_tls_information_teb( PROCESS_TLS_INFORMATION *t, unsigned int *idx, TEB *teb ) +{ + __TRY + { +#ifdef _WIN64 + if (t->Flags & PROCESS_TLS_INFORMATION_WOW64) + { + WOW_TEB *wow_teb = get_wow_teb( teb ); + ULONG *ptr; + + if (wow_teb && wow_teb->ThreadLocalStoragePointer) + { + if (t->OperationType == ProcessTlsReplaceVector) + { + ptr = t->ThreadData[*idx].TlsVector; + memcpy( ptr, ULongToPtr( wow_teb->ThreadLocalStoragePointer ), sizeof(*ptr) * t->TlsVectorLength ); + t->ThreadData[*idx].TlsVector = ULongToPtr( InterlockedExchange( (LONG *)&wow_teb->ThreadLocalStoragePointer, PtrToLong( ptr ))); + t->ThreadData[*idx].ThreadId = wow_teb->ClientId.UniqueThread; + } + else + { + ptr = ULongToPtr( wow_teb->ThreadLocalStoragePointer ); + t->ThreadData[*idx].TlsModulePointer = + ULongToPtr( InterlockedExchange( (LONG *)&ptr[t->TlsIndex], + PtrToLong( t->ThreadData[*idx].TlsModulePointer ))); + } + t->ThreadData[*idx].Flags = THREAD_TLS_INFORMATION_ASSIGNED; + ++*idx; + } + } + else +#endif + if (teb->ThreadLocalStoragePointer) + { + void **ptr; + + if (t->OperationType == ProcessTlsReplaceVector) + { + ptr = t->ThreadData[*idx].TlsVector; + memcpy( ptr, teb->ThreadLocalStoragePointer, sizeof(*ptr) * t->TlsVectorLength ); + t->ThreadData[*idx].TlsVector = InterlockedExchangePointer( &teb->ThreadLocalStoragePointer, ptr ); + t->ThreadData[*idx].ThreadId = HandleToULong( teb->ClientId.UniqueThread ); + } + else + { + ptr = teb->ThreadLocalStoragePointer; + t->ThreadData[*idx].TlsModulePointer = InterlockedExchangePointer( &ptr[t->TlsIndex], + t->ThreadData[*idx].TlsModulePointer ); + } + t->ThreadData[*idx].Flags = THREAD_TLS_INFORMATION_ASSIGNED; + ++*idx; + } + } + __EXCEPT + { + return STATUS_ACCESS_VIOLATION; + } + __ENDTRY + + return STATUS_SUCCESS; +} + + +/*********************************************************************** + * virtual_set_tls_information + */ +NTSTATUS virtual_set_tls_information( PROCESS_TLS_INFORMATION *t ) +{ + struct ntdll_thread_data *thread_data; + NTSTATUS ret = STATUS_SUCCESS; + unsigned int idx = 0; + sigset_t sigset; + + server_enter_uninterrupted_section( &virtual_mutex, &sigset ); + LIST_FOR_EACH_ENTRY_REV( thread_data, &teb_list, struct ntdll_thread_data, entry ) + { + TEB *teb = CONTAINING_RECORD( thread_data, TEB, GdiTebBatch ); + + if (idx == t->ThreadDataCount) break; + if ((ret = virtual_set_tls_information_teb( t, &idx, teb ))) break; + } + server_leave_uninterrupted_section( &virtual_mutex, &sigset ); + return ret; +} + + /*********************************************************************** * virtual_alloc_thread_stack */ diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 0d22c7d01cb..ce56677c39f 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -843,6 +843,39 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args ) } else return STATUS_INVALID_PARAMETER; + case ProcessTlsInformation: + { + PROCESS_TLS_INFORMATION32 *t32 = ptr; + PROCESS_TLS_INFORMATION *t; + ULONG i; + + if (len >= sizeof(*t32) && len >= offsetof(PROCESS_TLS_INFORMATION32, ThreadData[t32->ThreadDataCount])) + { + t = Wow64AllocateTemp( offsetof(PROCESS_TLS_INFORMATION, ThreadData[t32->ThreadDataCount]) ); + t->Flags = t32->Flags ? t32->Flags : PROCESS_TLS_INFORMATION_WOW64; + t->OperationType = t32->OperationType; + t->ThreadDataCount = t32->ThreadDataCount; + t->TlsIndex = t32->TlsIndex; + for (i = 0; i < t->ThreadDataCount; ++i) + { + t->ThreadData[i].Flags = t32->ThreadData[i].Flags; + t->ThreadData[i].ThreadId = t32->ThreadData[i].ThreadId; + t->ThreadData[i].TlsVector = ULongToPtr( t32->ThreadData[i].TlsVector ); + } + if (!(status = NtSetInformationProcess( handle, class, t, offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount]) ))) + { + for (i = 0; i < t->ThreadDataCount; ++i) + { + t32->ThreadData[i].Flags = t->ThreadData[i].Flags; + t32->ThreadData[i].ThreadId = t->ThreadData[i].ThreadId; + t32->ThreadData[i].TlsVector = PtrToUlong( t->ThreadData[i].TlsVector ); + } + } + return status; + } + else return STATUS_INFO_LENGTH_MISMATCH; + } + case ProcessInstrumentationCallback: /* PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION */ if (len >= sizeof(void *)) { diff --git a/dlls/wow64/struct32.h b/dlls/wow64/struct32.h index 01f1bac1885..28d9441a5fc 100644 --- a/dlls/wow64/struct32.h +++ b/dlls/wow64/struct32.h @@ -326,6 +326,30 @@ typedef struct ULONG DefaultBase; } RTL_PROCESS_MODULE_INFORMATION_EX32; +typedef struct +{ + ULONG Flags; + union + { + ULONG TlsVector; + ULONG TlsModulePointer; + }; + ULONG ThreadId; +} THREAD_TLS_INFORMATION32; + +typedef struct +{ + ULONG Flags; + ULONG OperationType; + ULONG ThreadDataCount; + union + { + ULONG TlsIndex; + ULONG TlsVectorLength; + }; + THREAD_TLS_INFORMATION32 ThreadData[1]; +} PROCESS_TLS_INFORMATION32; + typedef struct { ULONG BaseAddress; diff --git a/include/winternl.h b/include/winternl.h index 51fc92afae3..aff1e75c07f 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2561,6 +2561,41 @@ typedef struct _PROCESS_CYCLE_TIME_INFORMATION { ULONGLONG CurrentCycleCount; } PROCESS_CYCLE_TIME_INFORMATION, *PPROCESS_CYCLE_TIME_INFORMATION; +typedef struct _THREAD_TLS_INFORMATION +{ + ULONG Flags; + union + { + void *TlsVector; + void *TlsModulePointer; + }; + ULONG_PTR ThreadId; +} THREAD_TLS_INFORMATION, * PTHREAD_TLS_INFORMATION; + +#define THREAD_TLS_INFORMATION_ASSIGNED 0x2 + +typedef enum _PROCESS_TLS_INFORMATION_TYPE +{ + ProcessTlsReplaceIndex, + ProcessTlsReplaceVector, + MaxProcessTlsOperation +} PROCESS_TLS_INFORMATION_TYPE, *PPROCESS_TLS_INFORMATION_TYPE; + +typedef struct _PROCESS_TLS_INFORMATION +{ + ULONG Flags; + ULONG OperationType; + ULONG ThreadDataCount; + union + { + ULONG TlsIndex; + ULONG TlsVectorLength; + }; + THREAD_TLS_INFORMATION ThreadData[1]; +} PROCESS_TLS_INFORMATION, *PPROCESS_TLS_INFORMATION; + +#define PROCESS_TLS_INFORMATION_WOW64 1 + typedef struct _PROCESS_STACK_ALLOCATION_INFORMATION { SIZE_T ReserveSize; From 1bd85e459e919baae6c92d57249bc2e9d12ab120 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 23 Sep 2024 19:38:54 -0600 Subject: [PATCH 29/67] ntdll: Use NtSetInformationProcess( ProcessTlsInformation ) in alloc_tls_slot(). CW-Bug-Id: #24258 --- dlls/ntdll/loader.c | 98 ++++++++++++++++++++++----------------- dlls/ntdll/unix/virtual.c | 3 ++ 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index d7fc458dde1..ea9afcf993e 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -147,6 +147,7 @@ typedef struct _wine_modref static UINT tls_module_count = 32; /* number of modules with TLS directory */ static IMAGE_TLS_DIRECTORY *tls_dirs; /* array of TLS directories */ +static ULONG tls_thread_count; /* number of threads for which ThreadLocalStoragePointer is allocated in TEB. */ static RTL_CRITICAL_SECTION loader_section; static RTL_CRITICAL_SECTION_DEBUG critsect_debug = @@ -1375,10 +1376,10 @@ static BOOL is_dll_native_subsystem( LDR_DATA_TABLE_ENTRY *mod, const IMAGE_NT_H static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) { const IMAGE_TLS_DIRECTORY *dir; - ULONG i, size; + ULONG i, j, size; void *new_ptr; UINT old_module_count = tls_module_count; - HANDLE thread = NULL, next; + PROCESS_TLS_INFORMATION *t; if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size ))) return FALSE; @@ -1407,57 +1408,66 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) tls_dirs = new_ptr; tls_module_count = new_count; } + *(DWORD *)dir->AddressOfIndex = i; + tls_dirs[i] = *dir; - /* allocate the data block in all running threads */ - while (!NtGetNextThread( GetCurrentProcess(), thread, THREAD_QUERY_LIMITED_INFORMATION, 0, 0, &next )) + if (!tls_thread_count) return TRUE; + t = RtlAllocateHeap( GetProcessHeap(), 0, offsetof( PROCESS_TLS_INFORMATION, ThreadData[tls_thread_count] )); + if (!t) return FALSE; + + t->Flags = 0; + t->ThreadDataCount = tls_thread_count; + if (old_module_count < tls_module_count) + { + t->OperationType = ProcessTlsReplaceVector; + t->TlsVectorLength = old_module_count; + } + else + { + t->OperationType = ProcessTlsReplaceIndex; + t->TlsIndex = i; + } + for (j = 0; j < tls_thread_count; ++j) { - THREAD_BASIC_INFORMATION tbi; - TEB *teb; + void **vector; - if (thread) NtClose( thread ); - thread = next; - if (NtQueryInformationThread( thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL ) || !tbi.TebBaseAddress) + t->ThreadData[j].Flags = 0; + + if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return FALSE; + memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); + memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); + + if (t->OperationType == ProcessTlsReplaceVector) { - ERR( "NtQueryInformationThread failed.\n" ); - continue; + vector = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*vector) ); + if (!vector) return FALSE; + t->ThreadData[j].TlsVector = vector; + vector[i] = new_ptr; } - teb = tbi.TebBaseAddress; - if (!teb->ThreadLocalStoragePointer) + else t->ThreadData[j].TlsModulePointer = new_ptr; + } + if (NtSetInformationProcess( GetCurrentProcess(), ProcessTlsInformation, t, + offsetof(PROCESS_TLS_INFORMATION, ThreadData[t->ThreadDataCount]))) + { + ERR( "ProcessTlsInformation failed.\n" ); + return FALSE; + } + + for (j = 0; j < tls_thread_count; ++j) + { + if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) && t->OperationType == ProcessTlsReplaceVector) { - /* Thread is not initialized by loader yet or already teared down. */ - TRACE( "thread %04lx NULL tls block.\n", HandleToULong(tbi.ClientId.UniqueThread) ); - continue; + /* There could be fewer active threads than we counted here due to force terminated threads, first + * free extra TLS directory data set in the new TLS vector. */ + RtlFreeHeap( GetProcessHeap(), 0, ((void **)t->ThreadData[j].TlsVector)[i] ); } - - if (old_module_count < tls_module_count) + if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) || t->OperationType == ProcessTlsReplaceIndex) { - void **old = teb->ThreadLocalStoragePointer; - void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*new)); - - if (!new) return FALSE; - if (old) memcpy( new, old, old_module_count * sizeof(*new) ); - teb->ThreadLocalStoragePointer = new; -#ifdef __x86_64__ /* macOS-specific hack */ - if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = new; -#endif - TRACE( "thread %04lx tls block %p -> %p\n", HandleToULong(teb->ClientId.UniqueThread), old, new ); - /* FIXME: can't free old block here, should be freed at thread exit */ + /* FIXME: can't free old Tls vector here, should be freed at thread exit. */ + RtlFreeHeap( GetProcessHeap(), 0, t->ThreadData[j].TlsVector ); } - - if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return -1; - memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); - memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); - - TRACE( "thread %04lx slot %lu: %lu/%lu bytes at %p\n", - HandleToULong(teb->ClientId.UniqueThread), i, size, dir->SizeOfZeroFill, new_ptr ); - - RtlFreeHeap( GetProcessHeap(), 0, - InterlockedExchangePointer( (void **)teb->ThreadLocalStoragePointer + i, new_ptr )); } - if (thread) NtClose( thread ); - - *(DWORD *)dir->AddressOfIndex = i; - tls_dirs[i] = *dir; + RtlFreeHeap( GetProcessHeap(), 0, t ); return TRUE; } @@ -1685,6 +1695,7 @@ static NTSTATUS alloc_thread_tls(void) TRACE( "slot %u: %u/%lu bytes at %p\n", i, size, dir->SizeOfZeroFill, pointers[i] ); } + ++tls_thread_count; NtCurrentTeb()->ThreadLocalStoragePointer = pointers; #ifdef __x86_64__ /* macOS-specific hack */ if (NtCurrentTeb()->Instrumentation[0]) @@ -4134,6 +4145,7 @@ void WINAPI LdrShutdownThread(void) if ((pointers = NtCurrentTeb()->ThreadLocalStoragePointer)) { NtCurrentTeb()->ThreadLocalStoragePointer = NULL; + --tls_thread_count; #ifdef __x86_64__ /* macOS-specific hack */ if (NtCurrentTeb()->Instrumentation[0]) ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL; diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index 2063bdbaa4c..3c076689be2 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4353,6 +4353,9 @@ static NTSTATUS virtual_set_tls_information_teb( PROCESS_TLS_INFORMATION *t, uns memcpy( ptr, teb->ThreadLocalStoragePointer, sizeof(*ptr) * t->TlsVectorLength ); t->ThreadData[*idx].TlsVector = InterlockedExchangePointer( &teb->ThreadLocalStoragePointer, ptr ); t->ThreadData[*idx].ThreadId = HandleToULong( teb->ClientId.UniqueThread ); +#ifdef __x86_64__ /* macOS-specific hack */ + if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = ptr; +#endif } else { From 7ebedd3873222164107f92876ee1e57a10b4842e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 23 Sep 2024 20:51:44 -0600 Subject: [PATCH 30/67] ntdll: Match memory allocation layout for ThreadLocalStoragePointer. CW-Bug-Id: #24258 --- dlls/kernel32/tests/module.c | 120 +++++++++++++++++++++++++++++------ dlls/ntdll/loader.c | 51 +++++++++++---- 2 files changed, 139 insertions(+), 32 deletions(-) diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index 0262e44a3ee..e7e27bb6b83 100644 --- a/dlls/kernel32/tests/module.c +++ b/dlls/kernel32/tests/module.c @@ -141,6 +141,34 @@ static void create_test_dll( const char *name ) CloseHandle( handle ); } +static BOOL is_old_loader_struct(void) +{ + LDR_DATA_TABLE_ENTRY *mod, *mod2; + LDR_DDAG_NODE *ddag_node; + NTSTATUS status; + HMODULE hexe; + + /* Check for old LDR data strcuture. */ + hexe = GetModuleHandleW( NULL ); + ok( !!hexe, "Got NULL exe handle.\n" ); + status = LdrFindEntryForAddress( hexe, &mod ); + ok( !status, "got %#lx.\n", status ); + if (!(ddag_node = mod->DdagNode)) + { + win_skip( "DdagNode is NULL, skipping tests.\n" ); + return TRUE; + } + ok( !!ddag_node->Modules.Flink, "Got NULL module link.\n" ); + mod2 = CONTAINING_RECORD(ddag_node->Modules.Flink, LDR_DATA_TABLE_ENTRY, NodeModuleLink); + ok( mod2 == mod || broken( (void **)mod2 == (void **)mod - 1 ), "got %p, expected %p.\n", mod2, mod ); + if (mod2 != mod) + { + win_skip( "Old LDR_DATA_TABLE_ENTRY structure, skipping tests.\n" ); + return TRUE; + } + return FALSE; +} + static void testGetModuleFileName(const char* name) { HMODULE hMod; @@ -1766,6 +1794,76 @@ static void test_known_dlls_load(void) DeleteFileA( dll ); } +static HANDLE test_tls_links_started, test_tls_links_done; + +static DWORD WINAPI test_tls_links_thread(void* tlsidx_v) +{ + SetEvent(test_tls_links_started); + WaitForSingleObject(test_tls_links_done, INFINITE); + return 0; +} + +static void test_tls_links(void) +{ + TEB *teb = NtCurrentTeb(), *thread_teb; + THREAD_BASIC_INFORMATION tbi; + NTSTATUS status; + ULONG i, count; + HANDLE thread; + SIZE_T size; + void **ptr; + + ok(!!teb->ThreadLocalStoragePointer, "got NULL.\n"); + + test_tls_links_started = CreateEventW(NULL, FALSE, FALSE, NULL); + test_tls_links_done = CreateEventW(NULL, FALSE, FALSE, NULL); + + thread = CreateThread(NULL, 0, test_tls_links_thread, NULL, CREATE_SUSPENDED, NULL); + do + { + /* workaround currently present Wine bug when thread teb may be not available immediately + * after creating a thread before it is initialized on the Unix side. */ + Sleep(1); + status = NtQueryInformationThread(thread, ThreadBasicInformation, &tbi, sizeof(tbi), NULL); + ok(!status, "got %#lx.\n", status ); + } while (!(thread_teb = tbi.TebBaseAddress)); + ok(!thread_teb->ThreadLocalStoragePointer, "got %p.\n", thread_teb->ThreadLocalStoragePointer); + ResumeThread(thread); + WaitForSingleObject(test_tls_links_started, INFINITE); + + if (!is_old_loader_struct()) + { + ptr = teb->ThreadLocalStoragePointer; + count = (ULONG_PTR)ptr[-2]; + size = HeapSize(GetProcessHeap(), 0, ptr - 2); + ok(size == (count + 2) * sizeof(void *), "got count %lu, size %Iu.\n", count, size); + + for (i = 0; i < count; ++i) + { + if (!ptr[i]) continue; + size = HeapSize(GetProcessHeap(), 0, (void **)ptr[i] - 2); + ok(size && size < 100000, "got %Iu.\n", size); + } + + ptr = thread_teb->ThreadLocalStoragePointer; + count = (ULONG_PTR)ptr[-2]; + size = HeapSize(GetProcessHeap(), 0, ptr - 2); + ok(size == (count + 2) * sizeof(void *), "got count %lu, size %Iu.\n", count, size); + } + + ok(!!thread_teb->ThreadLocalStoragePointer, "got NULL.\n"); + ok(!teb->TlsLinks.Flink, "got %p.\n", teb->TlsLinks.Flink); + ok(!teb->TlsLinks.Blink, "got %p.\n", teb->TlsLinks.Blink); + ok(!thread_teb->TlsLinks.Flink, "got %p.\n", thread_teb->TlsLinks.Flink); + ok(!thread_teb->TlsLinks.Blink, "got %p.\n", thread_teb->TlsLinks.Blink); + SetEvent(test_tls_links_done); + WaitForSingleObject(thread, INFINITE); + + CloseHandle(thread); + CloseHandle(test_tls_links_started); + CloseHandle(test_tls_links_done); +} + static RTL_BALANCED_NODE *rtl_node_parent( RTL_BALANCED_NODE *node ) { @@ -1806,29 +1904,9 @@ static void test_base_address_index_tree(void) unsigned int tree_count, list_count = 0; LDR_DATA_TABLE_ENTRY *mod, *mod2; RTL_BALANCED_NODE *root, *node; - LDR_DDAG_NODE *ddag_node; - NTSTATUS status; - HMODULE hexe; char *base; - /* Check for old LDR data strcuture. */ - hexe = GetModuleHandleW( NULL ); - ok( !!hexe, "Got NULL exe handle.\n" ); - status = LdrFindEntryForAddress( hexe, &mod ); - ok( !status, "got %#lx.\n", status ); - if (!(ddag_node = mod->DdagNode)) - { - win_skip( "DdagNode is NULL, skipping tests.\n" ); - return; - } - ok( !!ddag_node->Modules.Flink, "Got NULL module link.\n" ); - mod2 = CONTAINING_RECORD(ddag_node->Modules.Flink, LDR_DATA_TABLE_ENTRY, NodeModuleLink); - ok( mod2 == mod || broken( (void **)mod2 == (void **)mod - 1 ), "got %p, expected %p.\n", mod2, mod ); - if (mod2 != mod) - { - win_skip( "Old LDR_DATA_TABLE_ENTRY structure, skipping tests.\n" ); - return; - } + if (is_old_loader_struct()) return; mod = CONTAINING_RECORD(first->Flink, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); ok( mod->BaseAddressIndexNode.ParentValue || mod->BaseAddressIndexNode.Left || mod->BaseAddressIndexNode.Right, diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index ea9afcf993e..c67f6e7adc6 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1367,6 +1367,36 @@ static BOOL is_dll_native_subsystem( LDR_DATA_TABLE_ENTRY *mod, const IMAGE_NT_H return TRUE; } + +/************************************************************************* + * alloc_tls_memory + * + * Allocate memory for TLS vector or index with an extra data. + */ +static void *alloc_tls_memory( BOOL vector, ULONG_PTR size ) +{ + ULONG_PTR *ptr; + + if (!(ptr = RtlAllocateHeap( GetProcessHeap(), vector ? HEAP_ZERO_MEMORY : 0, size + sizeof(void *) * 2 ))) return NULL; + ptr += 2; + if (vector) ptr[-2] = size / sizeof(void *); + else ptr[-2] = ptr[-1] = 0; + return ptr; +} + + +/************************************************************************* + * free_tls_memory + * + * Free TLS vector or index memory. + */ +static void free_tls_memory( void *ptr ) +{ + if (!ptr) return; + RtlFreeHeap( GetProcessHeap(), 0, (void **)ptr - 2 ); +} + + /************************************************************************* * alloc_tls_slot * @@ -1433,13 +1463,13 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) t->ThreadData[j].Flags = 0; - if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return FALSE; + if (!(new_ptr = alloc_tls_memory( FALSE, size + dir->SizeOfZeroFill ))) return FALSE; memcpy( new_ptr, (void *)dir->StartAddressOfRawData, size ); memset( (char *)new_ptr + size, 0, dir->SizeOfZeroFill ); if (t->OperationType == ProcessTlsReplaceVector) { - vector = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, tls_module_count * sizeof(*vector) ); + vector = alloc_tls_memory( TRUE, tls_module_count * sizeof(*vector) ); if (!vector) return FALSE; t->ThreadData[j].TlsVector = vector; vector[i] = new_ptr; @@ -1459,12 +1489,12 @@ static BOOL alloc_tls_slot( LDR_DATA_TABLE_ENTRY *mod ) { /* There could be fewer active threads than we counted here due to force terminated threads, first * free extra TLS directory data set in the new TLS vector. */ - RtlFreeHeap( GetProcessHeap(), 0, ((void **)t->ThreadData[j].TlsVector)[i] ); + free_tls_memory( ((void **)t->ThreadData[j].TlsVector)[i] ); } if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) || t->OperationType == ProcessTlsReplaceIndex) { /* FIXME: can't free old Tls vector here, should be freed at thread exit. */ - RtlFreeHeap( GetProcessHeap(), 0, t->ThreadData[j].TlsVector ); + free_tls_memory( t->ThreadData[j].TlsVector ); } } RtlFreeHeap( GetProcessHeap(), 0, t ); @@ -1672,8 +1702,7 @@ static NTSTATUS alloc_thread_tls(void) void **pointers; UINT i, size; - if (!(pointers = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, - tls_module_count * sizeof(*pointers) ))) + if (!(pointers = alloc_tls_memory( TRUE, tls_module_count * sizeof(*pointers) ))) return STATUS_NO_MEMORY; for (i = 0; i < tls_module_count; i++) @@ -1684,10 +1713,10 @@ static NTSTATUS alloc_thread_tls(void) size = dir->EndAddressOfRawData - dir->StartAddressOfRawData; if (!size && !dir->SizeOfZeroFill) continue; - if (!(pointers[i] = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) + if (!(pointers[i] = alloc_tls_memory( FALSE, size + dir->SizeOfZeroFill ))) { - while (i) RtlFreeHeap( GetProcessHeap(), 0, pointers[--i] ); - RtlFreeHeap( GetProcessHeap(), 0, pointers ); + while (i) free_tls_memory( pointers[--i] ); + free_tls_memory( pointers ); return STATUS_NO_MEMORY; } memcpy( pointers[i], (void *)dir->StartAddressOfRawData, size ); @@ -4150,8 +4179,8 @@ void WINAPI LdrShutdownThread(void) if (NtCurrentTeb()->Instrumentation[0]) ((TEB *)NtCurrentTeb()->Instrumentation[0])->ThreadLocalStoragePointer = NULL; #endif - for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); - RtlFreeHeap( GetProcessHeap(), 0, pointers ); + for (i = 0; i < tls_module_count; i++) free_tls_memory( pointers[i] ); + free_tls_memory( pointers ); } RtlProcessFlsData( NtCurrentTeb()->FlsSlots, 2 ); NtCurrentTeb()->FlsSlots = NULL; From d037b81984923156ffea4eeb26262d3bc7b9a7f8 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Mon, 19 Feb 2024 12:47:01 +0100 Subject: [PATCH 31/67] ntdll/tests: Move unwinding tests to a separate file. (cherry picked from commit 510fc64140de80037a121cfda2528e911ef1c1e1) CW-Bug-Id: #24258 --- dlls/ntdll/tests/Makefile.in | 1 + dlls/ntdll/tests/unwind.c | 3220 ++++++++++++++++++++++++++++++++++ 2 files changed, 3221 insertions(+) create mode 100644 dlls/ntdll/tests/unwind.c diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in index 3ca77a03053..3742968c415 100644 --- a/dlls/ntdll/tests/Makefile.in +++ b/dlls/ntdll/tests/Makefile.in @@ -25,5 +25,6 @@ SOURCES = \ thread.c \ threadpool.c \ time.c \ + unwind.c \ virtual.c \ wow64.c diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c new file mode 100644 index 00000000000..e146b1674df --- /dev/null +++ b/dlls/ntdll/tests/unwind.c @@ -0,0 +1,3220 @@ +/* + * Unit test suite for exception unwinding + * + * Copyright 2009, 2024 Alexandre Julliard + * Copyright 2020, 2021 Martin Storsjö + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winnt.h" +#include "winreg.h" +#include "winnt.h" +#include "winternl.h" +#include "wine/test.h" + +#ifndef __i386__ + +static void *code_mem; +static HMODULE ntdll; + +static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionEntry)(ULONG64, ULONG64*, UNWIND_HISTORY_TABLE*); +static PRUNTIME_FUNCTION (WINAPI *pRtlLookupFunctionTable)(ULONG64, ULONG64*, ULONG*); +static BOOLEAN (CDECL *pRtlInstallFunctionTableCallback)(DWORD64, DWORD64, DWORD, PGET_RUNTIME_FUNCTION_CALLBACK, PVOID, PCWSTR); +static BOOLEAN (CDECL *pRtlAddFunctionTable)(RUNTIME_FUNCTION*, DWORD, DWORD64); +static BOOLEAN (CDECL *pRtlDeleteFunctionTable)(RUNTIME_FUNCTION*); +static DWORD (WINAPI *pRtlAddGrowableFunctionTable)(void**, RUNTIME_FUNCTION*, DWORD, DWORD, ULONG_PTR, ULONG_PTR); +static void (WINAPI *pRtlGrowFunctionTable)(void*, DWORD); +static void (WINAPI *pRtlDeleteGrowableFunctionTable)(void*); +static NTSTATUS (WINAPI *pRtlGetNativeSystemInformation)(SYSTEM_INFORMATION_CLASS,void*,ULONG,ULONG*); +static NTSTATUS (WINAPI *pNtAllocateVirtualMemoryEx)(HANDLE,PVOID*,SIZE_T*,ULONG,ULONG,MEM_EXTENDED_PARAMETER*,ULONG); + +#ifdef __arm__ + +#define UWOP_TWOBYTES(x) (((x) >> 8) & 0xff), ((x) & 0xff) +#define UWOP_THREEBYTES(x) (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff) +#define UWOP_FOURBYTES(x) (((x) >> 24) & 0xff), (((x) >> 16) & 0xff), (((x) >> 8) & 0xff), ((x) & 0xff) + +#define UWOP_ALLOC_SMALL(size) (0x00 | (size/4)) /* Max 0x7f * 4 */ +#define UWOP_SAVE_REGSW(regmask) UWOP_TWOBYTES((0x80 << 8) | (regmask)) +#define UWOP_SET_FP(reg) (0xC0 | reg) +#define UWOP_SAVE_RANGE_4_7_LR(reg,lr) (0xD0 | (reg - 4) | ((lr) ? 0x04 : 0)) +#define UWOP_SAVE_RANGE_4_11_LR(reg,lr)(0xD8 | (reg - 8) | ((lr) ? 0x04 : 0)) +#define UWOP_SAVE_D8_RANGE(reg) (0xE0 | (reg - 8)) +#define UWOP_ALLOC_MEDIUMW(size) UWOP_TWOBYTES((0xE8 << 8) | (size/4)) /* Max 0x3ff * 4 */ +#define UWOP_SAVE_REGS(regmask) UWOP_TWOBYTES((0xEC << 8) | ((regmask) & 0xFF) | (((regmask) & (1<results[i].regs); + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size ); + + runtime_func.BeginAddress = code_offset; + if (test->unwind_size) + runtime_func.UnwindData = unwind_offset; + else + memcpy(&runtime_func.UnwindData, test->unwind_info, 4); + + trace( "code: %p stack: %p\n", code_mem, fake_stack ); + + for (i = 0; i < test->nb_results; i++) + { + memset( &ctx_ptr, 0, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + memset( &unset_reg64, 0x55, sizeof(unset_reg64) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 4; + + context.Sp = (ULONG_PTR)fake_stack; + context.Lr = (ULONG_PTR)ORIG_LR; + context.R11 = (ULONG_PTR)fake_stack + test->results[i].fp_offset; + orig_fp = context.R11; + orig_pc = (ULONG_PTR)code_mem + code_offset + test->results[i].pc_offset; + + trace( "%u/%u: pc=%p (%02x) fp=%p sp=%p\n", testnum, i, + (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp ); + + data = (void *)0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG)code_mem, orig_pc, + &runtime_func, &context, &data, &frame, &ctx_ptr ); + if (test->results[i].handler > 0) + { + ok( (char *)handler == (char *)code_mem + 0x200, + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + } + else + { + ok( handler == NULL, "%u/%u: handler %p instead of NULL\n", testnum, i, handler ); + ok( data == (test->results[i].handler < 0 ? + (void *)0xdeadbeef : NULL), + "%u/%u: handler data set to %p/%p\n", testnum, i, data, + (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) ); + } + + ok( context.Pc == test->results[i].pc, "%u/%u: wrong pc %p/%p\n", + testnum, i, (void *)context.Pc, (void*)test->results[i].pc ); + ok( frame == (test->results[i].frame_offset ? (ULONG)fake_stack : 0) + test->results[i].frame, "%u/%u: wrong frame %x/%x\n", + testnum, i, (int)((char *)frame - (char *)(test->results[i].frame_offset ? fake_stack : NULL)), test->results[i].frame ); + + for (j = 0; j < 47; j++) + { + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (j >= 4 && j <= 11 && (&ctx_ptr.R4)[j - 4]) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", + testnum, i, reg_names[j], (&context.R0)[j] ); + if (k < nb_regs) + ok( (&context.R0)[j] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] ); + } + else if (j == lr && ctx_ptr.Lr) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %lx\n", + testnum, i, reg_names[j], context.Lr ); + if (k < nb_regs) + ok( context.Lr == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.Lr, (int)test->results[i].regs[k][1] ); + } + else if (j == sp) + { + if (k < nb_regs) + ok( context.Sp == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.Sp, (int)test->results[i].regs[k][1] ); + else + ok( context.Sp == frame, "%u/%u: wrong sp %p/%p\n", + testnum, i, (void *)context.Sp, (void *)frame); + } + else if (j >= d8 && j <= d15 && (&ctx_ptr.D8)[j - d8]) + { + ok( k < nb_regs, "%u/%u: register %s should not be set to %llx\n", + testnum, i, reg_names[j], context.D[j - d0] ); + if (k < nb_regs) + ok( context.D[j - d0] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %llx/%llx\n", + testnum, i, reg_names[j], context.D[j - d0], test->results[i].regs[k][1] ); + } + else if (k < nb_regs) + { + if (j <= r12) + ok( (&context.R0)[j] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)(&context.R0)[j], (int)test->results[i].regs[k][1] ); + else if (j == lr) + ok( context.Lr == test->results[i].regs[k][1], + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)context.Lr, (int)test->results[i].regs[k][1] ); + else + ok( context.D[j - d0] == test->results[i].regs[k][1], + "%u/%u: register %s wrong %llx/%llx\n", + testnum, i, reg_names[j], context.D[j - d0], test->results[i].regs[k][1] ); + } + else + { + ok( k == nb_regs, "%u/%u: register %s should be set\n", testnum, i, reg_names[j] ); + if (j == lr) + ok( context.Lr == ORIG_LR, "%u/%u: register lr wrong %p/unset\n", + testnum, i, (void *)context.Lr ); + else if (j == r11) + ok( context.R11 == orig_fp, "%u/%u: register fp wrong %p/unset\n", + testnum, i, (void *)context.R11 ); + else if (j < d0) + ok( (&context.R0)[j] == unset_reg, + "%u/%u: register %s wrong %p/unset\n", + testnum, i, reg_names[j], (void *)(&context.R0)[j]); + else + ok( context.D[j - d0] == unset_reg64, + "%u/%u: register %s wrong %llx/unset\n", + testnum, i, reg_names[j], context.D[j - d0]); + } + } + } +} + +#define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff) + +static void test_virtual_unwind(void) +{ + + static const BYTE function_0[] = + { + 0x70, 0xb5, /* 00: push {r4-r6, lr} */ + 0x88, 0xb0, /* 02: sub sp, sp, #32 */ + 0x2d, 0xed, 0x06, 0x8b, /* 04: vpush {d8-d10} */ + 0x00, 0xbf, /* 08: nop */ + 0x2d, 0xed, 0x06, 0x3b, /* 0a: vpush {d3-d5} */ + 0xaf, 0x3f, 0x00, 0x80, /* 0e: nop.w */ + 0x6d, 0xed, 0x06, 0x1b, /* 12: vpush {d17-d19} */ + 0x2d, 0xe9, 0x00, 0x15, /* 16: push.w {r8, r10, r12} */ + 0xeb, 0x46, /* 1a: mov r11, sp */ + 0x00, 0xbf, /* 1c: nop */ + 0xbd, 0xec, 0x06, 0x8b, /* 1e: vpop {d8-d10} */ + 0xdd, 0x46, /* 22: mov sp, r11 */ + 0x08, 0xb0, /* 24: add sp, sp, #32 */ + 0x70, 0xbd, /* 26: pop {r4-r6, pc} */ + }; + + static const DWORD unwind_info_0_header = + (sizeof(function_0)/2) | /* function length */ + (1 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (1 << 23) | /* epilog */ + (5 << 28); /* codes, (sizeof(unwind_info_0)-headers+3)/4 */ + static const DWORD unwind_info_0_epilog0 = + (15 << 0) | /* offset = 0x1e / 2 = 15 */ + (0xE << 20) | /* condition, 0xE = always */ + (13 << 24); /* index, byte offset to epilog opcodes */ + + static const BYTE unwind_info_0[] = + { + DW(unwind_info_0_header), + DW(unwind_info_0_epilog0), + + UWOP_SET_FP(11), /* mov r11, sp */ + UWOP_SAVE_REGSW((1<0 seems to get incorrect handling of the epilogue */ + { 0x0c, 0x00, 0, ORIG_LR, 0x008, TRUE, { {d8,0x400000000}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, +#endif + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_10[] = + { + 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */ + 0xeb, 0x46, /* 04: mov r11, sp */ + 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */ + 0x84, 0xb0, /* 0a: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0c: nop */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */ + 0xbd, 0xe8, 0x00, 0x48, /* 14: pop.w {r11, lr} */ + 0x70, 0x47, /* 18: bx lr */ + }; + + static const DWORD unwind_info_10_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_10)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) }; + + static const struct results results_10[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, +#if 0 + /* L=1, R=1, Ret>0 seems to get incorrect handling of the epilogue */ + { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, +#endif + { 0x18, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_11[] = + { + 0x2d, 0xe9, 0x00, 0x48, /* 00: push.w {r11, lr} */ + 0xeb, 0x46, /* 04: mov r11, sp */ + 0x2d, 0xed, 0x04, 0x8b, /* 06: vpush {d8-d9} */ + 0x84, 0xb0, /* 0a: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0c: nop */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0xbd, 0xec, 0x04, 0x8b, /* 10: vpop {d8-d9} */ + 0xbd, 0xe8, 0x00, 0x88, /* 14: pop.w {r11, pc} */ + }; + + static const DWORD unwind_info_11_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_11)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (1 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) }; + + static const struct results results_11[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x06, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x0e, 0x00, 0, 0x24, 0x028, TRUE, { {r11,0x20}, {lr,0x24}, {d8,0x1400000010}, {d9,0x1c00000018}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x14, 0x018, TRUE, { {r11,0x10}, {lr,0x14}, {d8,0x400000000}, {d9,0xc00000008}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x04, 0x008, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + }; + + static const BYTE function_12[] = + { + 0x2d, 0xed, 0x0e, 0x8b, /* 00: vpush {d8-d14} */ + 0x84, 0xb0, /* 04: sub sp, sp, #16 */ + 0x00, 0xbf, /* 06: nop */ + 0x04, 0xb0, /* 08: add sp, sp, #16 */ + 0xbd, 0xec, 0x0e, 0x8b, /* 0a: vpop {d8-d14} */ + 0x00, 0xf0, 0x00, 0xb8, /* 0e: b tailcall */ + }; + + static const DWORD unwind_info_12_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_12)/2 << 2) | /* FunctionLength */ + (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) }; + + static const struct results results_12[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }}, + { 0x06, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x048, TRUE, { {d8,0x1400000010}, {d9,0x1c00000018}, {d10,0x2400000020}, {d11,0x2c00000028}, {d12,0x3400000030}, {d13,0x3c00000038}, {d14,0x4400000040}, {-1,-1} }}, + { 0x0a, 0x00, 0, ORIG_LR, 0x038, TRUE, { {d8,0x400000000}, {d9,0xc00000008}, {d10,0x1400000010}, {d11,0x1c00000018}, {d12,0x2400000020}, {d13,0x2c00000028}, {d14,0x3400000030}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_13[] = + { + 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */ + 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */ + 0x85, 0xb0, /* 08: sub sp, sp, #20 */ + 0x00, 0xbf, /* 0a: nop */ + 0x05, 0xb0, /* 0c: add sp, sp, #20 */ + 0x2d, 0xe8, 0xf0, 0x8f, /* 0e: pop.w {r4-r11, lr} */ + }; + + static const DWORD unwind_info_13_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_13)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (6 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) }; + + static const struct results results_13[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0e, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + }; + + static const BYTE function_14[] = + { + 0x2d, 0xe9, 0xf0, 0x4f, /* 00: push.w {r4-r11, lr} */ + 0x85, 0xb0, /* 04: sub sp, sp, #20 */ + 0x00, 0xbf, /* 06: nop */ + 0x05, 0xb0, /* 08: add sp, sp, #20 */ + 0x2d, 0xe8, 0xf0, 0x8f, /* 0a: pop.w {r4-r11, lr} */ + }; + + static const DWORD unwind_info_14_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_14)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (5 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_14[] = { DW(unwind_info_14_packed) }; + + static const struct results results_14[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + { 0x06, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x34, 0x038, TRUE, { {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {lr,0x34}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x20, 0x024, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r8,0x10}, {r9,0x14}, {r10,0x18}, {r11,0x1c}, {lr,0x20}, {-1,-1} }}, + }; + + static const BYTE function_15[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb5, /* 02: push {r4,lr} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */ + 0x10, 0xb5, /* 0e: pop {r4} */ + 0x5d, 0xf8, 0x14, 0xfb, /* 10: ldr pc, [sp], #20 */ + }; + + static const DWORD unwind_info_15_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_15)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_15[] = { DW(unwind_info_15_packed) }; + + static const struct results results_15[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0e, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }}, + }; + + static const BYTE function_16[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x2d, 0xe9, 0x00, 0x48, /* 02: push.w {r11,lr} */ + 0xeb, 0x46, /* 06: mov r11, sp */ + 0x00, 0xbf, /* 08: nop */ + 0xbd, 0xe8, 0x10, 0x40, /* 0a: pop.w {r11,lr} */ + 0x04, 0xb0, /* 0e: add sp, sp, #16 */ + 0x00, 0xf0, 0x00, 0xb8, /* 10: b tailcall */ + }; + + static const DWORD unwind_info_16_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_16)/2 << 2) | /* FunctionLength */ + (2 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_16[] = { DW(unwind_info_16_packed) }; + + static const struct results results_16[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x04, 0x018, TRUE, { {r11,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x10, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_17[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb4, /* 02: push {r4} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 0a: add sp, sp, #512 */ + 0x10, 0xbc, /* 0e: pop {r4} */ + 0x04, 0xb0, /* 10: add sp, sp, #16 */ + 0x70, 0x47, /* 12: bx lr */ + }; + + static const DWORD unwind_info_17_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_17)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_17[] = { DW(unwind_info_17_packed) }; + + static const struct results results_17[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x214, TRUE, { {r4,0x200}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x10, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_18[] = + { + 0x08, 0xb5, /* 00: push {r3,lr} */ + 0x00, 0xbf, /* 02: nop */ + 0x08, 0xbd, /* 04: pop {r3,pc} */ + }; + + static const DWORD unwind_info_18_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_18)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fcu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_18[] = { DW(unwind_info_18_packed) }; + + static const struct results results_18[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x008, TRUE, { {lr,0x04}, {-1,-1} }}, + }; + + static const BYTE function_19[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x14, 0xb4, /* 02: push {r0-r4} */ + 0x00, 0xbf, /* 04: nop */ + 0x1f, 0xbc, /* 06: pop {r0-r4} */ + 0x04, 0xb0, /* 08: add sp, sp, #16 */ + 0x70, 0x47, /* 0a: bx lr */ + }; + + static const DWORD unwind_info_19_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_19)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3ffu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_19[] = { DW(unwind_info_19_packed) }; + + static const struct results results_19[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_20[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x14, 0xb4, /* 02: push {r0-r4} */ + 0x00, 0xbf, /* 04: nop */ + 0x04, 0xb0, /* 06: add sp, sp, #16 */ + 0x10, 0xbc, /* 08: pop {r4} */ + 0x04, 0xb0, /* 0a: add sp, sp, #16 */ + 0x70, 0x47, /* 0c: bx lr */ + }; + + static const DWORD unwind_info_20_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_20)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3f7u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_20[] = { DW(unwind_info_20_packed) }; + + static const struct results results_20[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_21[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb4, /* 02: push {r4} */ + 0x84, 0xb0, /* 04: sub sp, sp, #16 */ + 0x00, 0xbf, /* 06: nop */ + 0x1f, 0xbc, /* 08: pop {r0-r4} */ + 0x04, 0xb0, /* 0a: add sp, sp, #16 */ + 0x70, 0x47, /* 0c: bx lr */ + }; + + static const DWORD unwind_info_21_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_21)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fbu << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_21[] = { DW(unwind_info_21_packed) }; + + static const struct results results_21[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x014, TRUE, { {r4,0x00}, {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x024, TRUE, { {r4,0x10}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_22[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + 0x0d, 0xf5, 0x00, 0x7d, /* 04: add sp, sp, #512 */ + 0x10, 0xb5, /* 08: pop {r4} */ + 0x5d, 0xf8, 0x14, 0xfb, /* 0a: ldr pc, [sp], #20 */ + }; + + static const DWORD unwind_info_22_packed = + (2 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_22)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_22[] = { DW(unwind_info_22_packed) }; + + static const struct results results_22[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x02, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x04, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x00, 0x014, TRUE, { {lr,0x00}, {-1,-1} }}, + }; + + static const BYTE function_23[] = + { + 0x0f, 0xb4, /* 00: push {r0-r3} */ + 0x10, 0xb5, /* 02: push {r4,lr} */ + 0xad, 0xf5, 0x00, 0x7d, /* 04: sub sp, sp, #512 */ + 0x00, 0xbf, /* 08: nop */ + 0x00, 0xbf, /* 0a: nop */ + }; + + static const DWORD unwind_info_23_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_23)/2 << 2) | /* FunctionLength */ + (3 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (1 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (128 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_23[] = { DW(unwind_info_23_packed) }; + + static const struct results results_23[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x04, 0x018, TRUE, { {r4,0x00}, {lr,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x204, 0x218, TRUE, { {r4,0x200}, {lr,0x204}, {-1,-1} }}, + }; + + static const BYTE function_24[] = + { + 0x2d, 0xe9, 0xfc, 0x48, /* 00: push.w {r2-r7,r11,lr} */ + 0x0d, 0xf1, 0x18, 0x0b, /* 04: add r11, sp, #24 */ + 0x00, 0xbf, /* 08: nop */ + 0x02, 0xb0, /* 0a: add sp, sp, #8 */ + 0xbd, 0xe8, 0x10, 0x48, /* 0c: pop.w {r4-r7,r11,pc} */ + }; + + static const DWORD unwind_info_24_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_24)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0x3f5u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_24[] = { DW(unwind_info_24_packed) }; + + static const struct results results_24[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + }; + + static const BYTE function_25[] = + { + 0x2d, 0xe9, 0xf0, 0x48, /* 00: push.w {r4-r7,r11,lr} */ + 0x0d, 0xf1, 0x10, 0x0b, /* 04: add r11, sp, #16 */ + 0x82, 0xb0, /* 08: sub sp, sp, #8 */ + 0x00, 0xbf, /* 0a: nop */ + 0xbd, 0xe8, 0xfc, 0x48, /* 0c: pop.w {r2-r7,r11,pc} */ + }; + + static const DWORD unwind_info_25_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_25)/2 << 2) | /* FunctionLength */ + (0 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (3 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (1 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (0x3f9u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_25[] = { DW(unwind_info_25_packed) }; + + static const struct results results_25[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x14, 0x018, TRUE, { {r4,0x00}, {r5,0x04}, {r6,0x08}, {r7,0x0c}, {r11,0x10}, {lr,0x14}, {-1,-1} }}, + { 0x0a, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x1c, 0x020, TRUE, { {r4,0x08}, {r5,0x0c}, {r6,0x10}, {r7,0x14}, {r11,0x18}, {lr,0x1c}, {-1,-1} }}, + }; + + static const BYTE function_26[] = + { + 0x2d, 0xe9, 0x10, 0x08, /* 00: push.w {r4, r11} */ + 0x0d, 0xf1, 0x1c, 0x0b, /* 04: add.w r11, sp, #28 */ + 0x84, 0xb0, /* 08: sub sp, sp, #16 */ + 0x00, 0xbf, /* 0a: nop */ + 0x04, 0xb0, /* 0c: add sp, sp, #16 */ + 0xbd, 0xe8, 0x10, 0x08, /* 0e: pop.w {r4, r11} */ + 0x70, 0x47, /* 12: bx lr */ + }; + + /* C=1, L=0 is disallowed by doc */ + static const DWORD unwind_info_26_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_26)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (0 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (0 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (1 << 21) | /* C - hook up r11 */ + (4 << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_26[] = { DW(unwind_info_26_packed) }; + + static const struct results results_26[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x0a, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x018, TRUE, { {r4,0x10}, {r11,0x14}, {-1,-1} }}, + { 0x0e, 0x10, 0, ORIG_LR, 0x008, TRUE, { {r4,0x00}, {r11,0x04}, {-1,-1} }}, + { 0x12, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_27[] = + { + 0x0e, 0xb4, /* 00: push {r1-r3} */ + 0x00, 0xbf, /* 02: nop */ + 0x03, 0xb0, /* 04: add sp, sp, #12 */ + 0x70, 0x47, /* 06: bx lr */ + }; + + static const DWORD unwind_info_27_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_27)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3f6u << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_27[] = { DW(unwind_info_27_packed) }; + + static const struct results results_27[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_28[] = + { + 0x0e, 0xb4, /* 00: push {r1-r3} */ + 0x00, 0xbf, /* 02: nop */ + 0x03, 0xb0, /* 04: add sp, sp, #12 */ + 0x70, 0x47, /* 06: bx lr */ + }; + + static const DWORD unwind_info_28_packed = + (1 << 0) | /* Flag, 01 has prologue, 10 (2) fragment (no prologue) */ + (sizeof(function_28)/2 << 2) | /* FunctionLength */ + (1 << 13) | /* Ret (00 pop, 01 16 bit branch, 10 32 bit branch, 11 no epilogue) */ + (0 << 15) | /* H (homing, 16 bytes push of r0-r3 at start) */ + (7 << 16) | /* Reg r4 - r(4+N), or d8 - d(8+N) */ + (1 << 19) | /* R (0 integer registers, 1 float registers, R=1, Reg=7 no registers */ + (0 << 20) | /* L, push LR */ + (0 << 21) | /* C - hook up r11 */ + (0x3fau << 22); /* StackAdjust, stack/4. 0x3F4 special, + (0-3) stack adjustment, 4 PF (prologue folding), 8 EF (epilogue folding) */ + + static const BYTE unwind_info_28[] = { DW(unwind_info_28_packed) }; + + static const struct results results_28[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x02, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00c, TRUE, { {-1,-1} }}, + { 0x06, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_29[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + }; + + static const DWORD unwind_info_29_header = + (sizeof(function_29)/2) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (0 << 23) | /* epilog */ + (1 << 28); /* codes, (sizeof(unwind_info_29)-headers+3)/4 */ + + static const BYTE unwind_info_29[] = + { + DW(unwind_info_29_header), + UWOP_MSFT_OP_CONTEXT, + UWOP_END, + }; + + static const struct results results_29[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, 0x40, 0x38, FALSE, { {r0,0x04}, {r1,0x08}, {r2,0x0c}, {r3,0x10}, {r4,0x14}, {r5,0x18}, {r6,0x1c}, {r7,0x20}, {r8,0x24}, {r9,0x28}, {r10,0x2c}, {r11,0x30}, {r12,0x34}, {sp,0x38}, {lr,0x3c}, + {d0,0x5400000050}, {d1,0x5c00000058}, {d2,0x6400000060}, {d3,0x6c00000068}, {d4,0x7400000070}, {d5,0x7c00000078}, {d6,0x8400000080}, {d7,0x8c00000088}, + {d8,0x9400000090}, {d9,0x9c00000098}, {d10,0xa4000000a0}, {d11,0xac000000a8}, {d12,0xb4000000b0}, {d13,0xbc000000b8}, {d14,0xc4000000c0}, {d15,0xcc000000c8}, + {d16,0xd4000000d0}, {d17,0xdc000000d8}, {d18,0xe4000000e0}, {d19,0xec000000e8}, {d20,0xf4000000f0}, {d21,0xfc000000f8}, {d22,0x10400000100}, {d23,0x10c00000108}, + {d24,0x11400000110}, {d25,0x11c00000118}, {d26,0x12400000120}, {d27,0x12c00000128}, {d28,0x13400000130}, {d29,0x13c00000138}, {d30,0x14400000140}, {d31,0x14c00000148} }}, + }; + + static const BYTE function_30[] = + { + 0x00, 0xbf, /* 00: nop */ + 0x00, 0xbf, /* 02: nop */ + 0x00, 0xbf, /* 04: nop */ + 0x00, 0xbf, /* 06: nop */ + }; + + static const DWORD unwind_info_30_header = + (sizeof(function_30)/2) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* F */ + (0 << 23) | /* epilog */ + (2 << 28); /* codes, (sizeof(unwind_info_30)-headers+3)/4 */ + + static const BYTE unwind_info_30[] = + { + DW(unwind_info_30_header), + UWOP_ALLOC_SMALL(12), /* sub sp, sp, #12 */ + UWOP_SAVE_REGS((1<> 8) & 0xff), ((x) & 0xff) + +#define UWOP_ALLOC_SMALL(size) (0x00 | (size/16)) +#define UWOP_SAVE_R19R20_X(offset) (0x20 | (offset/8)) +#define UWOP_SAVE_FPLR(offset) (0x40 | (offset/8)) +#define UWOP_SAVE_FPLR_X(offset) (0x80 | (offset/8 - 1)) +#define UWOP_ALLOC_MEDIUM(size) UWOP_TWOBYTES((0xC0 << 8) | (size/16)) +#define UWOP_SAVE_REGP(reg, offset) UWOP_TWOBYTES((0xC8 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REGP_X(reg, offset) UWOP_TWOBYTES((0xCC << 8) | ((reg - 19) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_REG(reg, offset) UWOP_TWOBYTES((0xD0 << 8) | ((reg - 19) << 6) | (offset/8)) +#define UWOP_SAVE_REG_X(reg, offset) UWOP_TWOBYTES((0xD4 << 8) | ((reg - 19) << 5) | (offset/8 - 1)) +#define UWOP_SAVE_LRP(reg, offset) UWOP_TWOBYTES((0xD6 << 8) | ((reg - 19)/2 << 6) | (offset/8)) +#define UWOP_SAVE_FREGP(reg, offset) UWOP_TWOBYTES((0xD8 << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREGP_X(reg, offset) UWOP_TWOBYTES((0xDA << 8) | ((reg - 8) << 6) | (offset/8 - 1)) +#define UWOP_SAVE_FREG(reg, offset) UWOP_TWOBYTES((0xDC << 8) | ((reg - 8) << 6) | (offset/8)) +#define UWOP_SAVE_FREG_X(reg, offset) UWOP_TWOBYTES((0xDE << 8) | ((reg - 8) << 5) | (offset/8 - 1)) +#define UWOP_ALLOC_LARGE(size) UWOP_TWOBYTES((0xE0 << 8) | ((size/16) >> 16)), UWOP_TWOBYTES(size/16) +#define UWOP_SET_FP 0xE1 +#define UWOP_ADD_FP(offset) UWOP_TWOBYTES((0xE2 << 8) | (offset/8)) +#define UWOP_NOP 0xE3 +#define UWOP_END 0xE4 +#define UWOP_END_C 0xE5 +#define UWOP_SAVE_NEXT 0xE6 +#define UWOP_SAVE_ANY_REG(reg,offset) 0xE7,(reg),(offset) +#define UWOP_TRAP_FRAME 0xE8 +#define UWOP_MACHINE_FRAME 0xE9 +#define UWOP_CONTEXT 0xEA +#define UWOP_EC_CONTEXT 0xEB +#define UWOP_CLEAR_UNWOUND_TO_CALL 0xEC + +struct results +{ + int pc_offset; /* pc offset from code start */ + int fp_offset; /* fp offset from stack pointer */ + int handler; /* expect handler to be set? */ + ULONG_PTR pc; /* expected final pc value */ + int frame; /* expected frame return value */ + int frame_offset; /* whether the frame return value is an offset or an absolute value */ + ULONG_PTR regs[48][2]; /* expected values for registers */ +}; + +struct unwind_test +{ + const BYTE *function; + size_t function_size; + const BYTE *unwind_info; + size_t unwind_size; + const struct results *results; + unsigned int nb_results; + int unwound_clear; + int last_set_reg_ptr; +}; + +enum regs +{ + x0, x1, x2, x3, x4, x5, x6, x7, + x8, x9, x10, x11, x12, x13, x14, x15, + x16, x17, x18, x19, x20, x21, x22, x23, + x24, x25, x26, x27, x28, x29, lr, sp, + d0, d1, d2, d3, d4, d5, d6, d7, + d8, d9, d10, d11, d12, d13, d14, d15 +}; + +static const char * const reg_names[48] = +{ + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "lr", "sp", + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15", +}; + +#define ORIG_LR 0xCCCCCCCC + +static void call_virtual_unwind( int testnum, const struct unwind_test *test ) +{ + static const int code_offset = 1024; + static const int unwind_offset = 2048; + void *handler, *data; + CONTEXT context; + RUNTIME_FUNCTION runtime_func; + KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; + UINT i, j, k; + ULONG64 fake_stack[256]; + ULONG64 frame, orig_pc, orig_fp, unset_reg, sp_offset = 0, regval, *regptr; + static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs); + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, test->unwind_size ); + + runtime_func.BeginAddress = code_offset; + if (test->unwind_size) + runtime_func.UnwindData = unwind_offset; + else + memcpy(&runtime_func.UnwindData, test->unwind_info, 4); + + for (i = 0; i < test->nb_results; i++) + { + winetest_push_context( "%u/%u", testnum, i ); + memset( &ctx_ptr, 0x55, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 8; + + context.Sp = (ULONG_PTR)fake_stack; + context.Lr = (ULONG_PTR)ORIG_LR; + context.Fp = (ULONG_PTR)fake_stack + test->results[i].fp_offset; + context.ContextFlags = 0xcccc; + if (test->unwound_clear) context.ContextFlags |= CONTEXT_ARM64_UNWOUND_TO_CALL; + + orig_fp = context.Fp; + orig_pc = (ULONG64)code_mem + code_offset + test->results[i].pc_offset; + + trace( "pc=%p (%02x) fp=%p sp=%p\n", (void *)orig_pc, *(UINT *)orig_pc, (void *)orig_fp, (void *)context.Sp ); + + data = (void *)0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_pc, + &runtime_func, &context, &data, &frame, &ctx_ptr ); + if (test->results[i].handler > 0) + { + ok( (char *)handler == (char *)code_mem + 0x200, + "wrong handler %p/%p\n", handler, (char *)code_mem + 0x200 ); + if (handler) ok( *(DWORD *)data == 0x08070605, + "wrong handler data %lx\n", *(DWORD *)data ); + } + else + { + ok( handler == NULL, "handler %p instead of NULL\n", handler ); + ok( data == (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL), + "handler data set to %p/%p\n", data, + (test->results[i].handler < 0 ? (void *)0xdeadbeef : NULL) ); + } + + ok( context.Pc == test->results[i].pc, "wrong pc %p/%p\n", + (void *)context.Pc, (void*)test->results[i].pc ); + ok( frame == (test->results[i].frame_offset ? (ULONG64)fake_stack : 0) + test->results[i].frame, "wrong frame %p/%p\n", + (void *)frame, (char *)(test->results[i].frame_offset ? fake_stack : NULL) + test->results[i].frame ); + if (!test->unwound_clear || i < test->unwound_clear) + ok( context.ContextFlags == (0xcccc | CONTEXT_ARM64_UNWOUND_TO_CALL), + "wrong flags %lx\n", context.ContextFlags ); + else + ok( context.ContextFlags == 0xcccc, + "wrong flags %lx\n", context.ContextFlags ); + + sp_offset = 0; + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + break; + if (test->results[i].regs[k][0] == sp) { + /* If sp is part of the registers list, treat it as an offset + * between the returned frame pointer and the sp register. */ + sp_offset = test->results[i].regs[k][1]; + break; + } + } + ok( frame - sp_offset == context.Sp, "wrong sp %p/%p\n", + (void *)(frame - sp_offset), (void *)context.Sp); + + for (j = 0; j < 48; j++) + { + if (j == sp) continue; /* Handling sp separately above */ + + if (j <= 30) + { + regval = context.X[j]; + regptr = j < 19 ? NULL : (&ctx_ptr.X19)[j - 19]; + } + else + { + regval = context.V[j - d0].Low; + regptr = j < d8 ? NULL : (&ctx_ptr.D8)[j - d8]; + } + + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (k < nb_regs) + { + ok( regval == test->results[i].regs[k][1], + "register %s wrong %llx/%llx\n", reg_names[j], regval, test->results[i].regs[k][1] ); + if (regptr) + { + if (test->last_set_reg_ptr && j > test->last_set_reg_ptr && j <= 30) + ok( regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names[j] ); + else + { + ok( regptr != (void *)unset_reg, "register %s should have pointer set\n", reg_names[j] ); + if (regptr != (void *)unset_reg) + ok( *regptr == regval, "register %s should have reg pointer to %llx / %llx\n", + reg_names[j], *regptr, regval ); + } + } + } + else + { + ok( k == nb_regs, "register %s should be set\n", reg_names[j] ); + ok( !regptr || regptr == (void *)unset_reg, "register %s should not have pointer set\n", reg_names[j] ); + if (j == lr) + ok( context.Lr == ORIG_LR, "register lr wrong %llx/unset\n", context.Lr ); + else if (j == x29) + ok( context.Fp == orig_fp, "register fp wrong %llx/unset\n", context.Fp ); + else + ok( regval == unset_reg, "register %s wrong %llx/unset\n", reg_names[j], regval); + } + } + winetest_pop_context(); + } +} + +#define DW(dword) ((dword >> 0) & 0xff), ((dword >> 8) & 0xff), ((dword >> 16) & 0xff), ((dword >> 24) & 0xff) + +static void test_virtual_unwind(void) +{ + static const BYTE function_0[] = + { + 0xff, 0x83, 0x00, 0xd1, /* 00: sub sp, sp, #32 */ + 0xf3, 0x53, 0x01, 0xa9, /* 04: stp x19, x20, [sp, #16] */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xf3, 0x53, 0x41, 0xa9, /* 0c: ldp x19, x20, [sp, #16] */ + 0xff, 0x83, 0x00, 0x91, /* 10: add sp, sp, #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_0_header = + (sizeof(function_0)/4) | /* function length */ + (1 << 20) | /* X */ + (0 << 21) | /* E */ + (1 << 22) | /* epilog */ + (2 << 27); /* codes */ + static const DWORD unwind_info_0_epilog0 = + (3 << 0) | /* offset */ + (4 << 22); /* index */ + + static const BYTE unwind_info_0[] = + { + DW(unwind_info_0_header), + DW(unwind_info_0_epilog0), + + UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */ + UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */ + UWOP_END, + + UWOP_SAVE_REGP(19, 16), /* stp x19, x20, [sp, #16] */ + UWOP_ALLOC_SMALL(32), /* sub sp, sp, #32 */ + UWOP_END, + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_0[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 1, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + + static const BYTE function_1[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xfe, 0x0b, 0x00, 0xf9, /* 04: str x30, [sp, #16] */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */ + 0xfe, 0x0b, 0x40, 0xf9, /* 14: ldr x30, [sp, #16] */ + 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_1_packed = + (1 << 0) | /* Flag */ + (sizeof(function_1)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_1[] = { DW(unwind_info_1_packed) }; + + static const struct results results_1[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x20, 0x030, TRUE, { {x19,0x10}, {x20,0x18}, {lr,0x20}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x10, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {lr,0x10}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_2[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_2_header = + (sizeof(function_2)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_2[] = + { + DW(unwind_info_2_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_MACHINE_FRAME, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + /* Partial prologues with the custom frame opcodes (machine frame, + * context) behave like there's one less instruction to skip, because the + * custom frame is set up externally without an explicit instruction. */ + static const struct results results_2[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x0008, 0x010, FALSE, { {-1,-1} }}, + { 0x08, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + { 0x0c, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + { 0x10, 0x00, 0, 0x0018, 0x020, FALSE, { {-1,-1} }}, + }; + + static const BYTE function_3[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_3_header = + (sizeof(function_3)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_3[] = + { + DW(unwind_info_3_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_CONTEXT, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_3[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0 , 0x0108, 0x110, FALSE, { {x0, 0x08}, {x1, 0x10}, {x2, 0x18}, {x3, 0x20}, {x4, 0x28}, {x5, 0x30}, {x6, 0x38}, {x7, 0x40}, {x8, 0x48}, {x9, 0x50}, {x10, 0x58}, {x11, 0x60}, {x12, 0x68}, {x13, 0x70}, {x14, 0x78}, {x15, 0x80}, {x16, 0x88}, {x17, 0x90}, {x18, 0x98}, {x19, 0xA0}, {x20, 0xA8}, {x21, 0xB0}, {x22, 0xB8}, {x23, 0xC0}, {x24, 0xC8}, {x25, 0xD0}, {x26, 0xD8}, {x27, 0xE0}, {x28, 0xE8}, {x29, 0xF0}, {lr, 0xF8}, {d0, 0x110}, {d1, 0x120}, {d2, 0x130}, {d3, 0x140}, {d4, 0x150}, {d5, 0x160}, {d6, 0x170}, {d7, 0x180}, {d8, 0x190}, {d9, 0x1a0}, {d10, 0x1b0}, {d11, 0x1c0}, {d12, 0x1d0}, {d13, 0x1e0}, {d14, 0x1f0}, {d15, 0x200}, {-1,-1} }}, + { 0x08, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + { 0x0c, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + { 0x10, 0x00, 0 , 0x0118, 0x120, FALSE, { {x0, 0x18}, {x1, 0x20}, {x2, 0x28}, {x3, 0x30}, {x4, 0x38}, {x5, 0x40}, {x6, 0x48}, {x7, 0x50}, {x8, 0x58}, {x9, 0x60}, {x10, 0x68}, {x11, 0x70}, {x12, 0x78}, {x13, 0x80}, {x14, 0x88}, {x15, 0x90}, {x16, 0x98}, {x17, 0xA0}, {x18, 0xA8}, {x19, 0xB0}, {x20, 0xB8}, {x21, 0xC0}, {x22, 0xC8}, {x23, 0xD0}, {x24, 0xD8}, {x25, 0xE0}, {x26, 0xE8}, {x27, 0xF0}, {x28, 0xF8}, {x29, 0x100}, {lr, 0x108}, {d0, 0x120}, {d1, 0x130}, {d2, 0x140}, {d3, 0x150}, {d4, 0x160}, {d5, 0x170}, {d6, 0x180}, {d7, 0x190}, {d8, 0x1a0}, {d9, 0x1b0}, {d10, 0x1c0}, {d11, 0x1d0}, {d12, 0x1e0}, {d13, 0x1f0}, {d14, 0x200}, {d15, 0x210}, {-1,-1} }}, + }; + + static const BYTE function_4[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xff, 0x03, 0x08, 0xd1, /* 04: sub sp, sp, #512 */ + 0xff, 0x43, 0x40, 0xd1, /* 08: sub sp, sp, #65536 */ + 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */ + 0xf3, 0x53, 0xbe, 0xa9, /* 10: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 14: stp x21, x22, [sp, #16] */ + 0xf7, 0x0f, 0x1e, 0xf8, /* 18: str x23, [sp, #-32]! */ + 0xf8, 0x07, 0x00, 0xf9, /* 1c: str x24, [sp, #8] */ + 0xf9, 0x7b, 0x01, 0xa9, /* 20: stp x25, x30, [sp, #16] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 24: stp x29, x30, [sp, #48] */ + 0xfd, 0x7b, 0xbe, 0xa9, /* 28: stp x29, x30, [sp, #-32]! */ + 0xf3, 0x53, 0xbe, 0xa9, /* 2c: stp x19, x20, [sp, #-32]! */ + 0xe8, 0x27, 0xbe, 0x6d, /* 30: stp d8, d9, [sp, #-32]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 34: stp d10, d11, [sp, #16] */ + 0xec, 0x0f, 0x1e, 0xfc, /* 38: str d12, [sp, #-32]! */ + 0xed, 0x07, 0x00, 0xfd, /* 3c: str d13, [sp, #8] */ + 0xfd, 0x43, 0x00, 0x91, /* 40: add x29, sp, #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 44: ret */ + }; + + static const DWORD unwind_info_4_header = + (sizeof(function_4)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (8 << 27); /* codes */ + + static const BYTE unwind_info_4[] = + { + DW(unwind_info_4_header), + + UWOP_ADD_FP(16), /* 40: add x29, sp, #16 */ + UWOP_SAVE_FREG(13, 8), /* 3c: str d13, [sp, #8] */ + UWOP_SAVE_FREG_X(12, 32), /* 38: str d12, [sp, #-32]! */ + UWOP_SAVE_FREGP(10, 16), /* 34: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 32), /* 30: stp d8, d9, [sp, #-32]! */ + UWOP_SAVE_R19R20_X(32), /* 2c: stp x19, x20, [sp, #-32]! */ + UWOP_SAVE_FPLR_X(32), /* 28: stp x29, x30, [sp, #-32]! */ + UWOP_SAVE_FPLR(16), /* 24: stp x29, x30, [sp, #16] */ + UWOP_SAVE_LRP(25, 16), /* 20: stp x25, x30, [sp, #16] */ + UWOP_SAVE_REG(24, 8), /* 1c: str x24, [sp, #8] */ + UWOP_SAVE_REG_X(23, 32), /* 18: str x23, [sp, #-32]! */ + UWOP_SAVE_REGP(21, 16), /* 14: stp x21, x22, [sp, #16] */ + UWOP_SAVE_REGP_X(19, 32), /* 10: stp x19, x20, [sp, #-32]! */ + UWOP_SET_FP, /* 0c: mov x29, sp */ + UWOP_ALLOC_LARGE(65536), /* 08: sub sp, sp, #65536 */ + UWOP_ALLOC_MEDIUM(512), /* 04: sub sp, sp, #512 */ + UWOP_ALLOC_SMALL(16), /* 00: sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_4[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x00010, TRUE, { {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x00210, TRUE, { {-1,-1} }}, + { 0x0c, 0x10, 0, ORIG_LR, 0x10210, TRUE, { {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x0018, 0x10210, TRUE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x28, 0x00, 0, 0x0018, 0x10220, FALSE, { {x19, 0x20}, {x20, 0x28}, {x21, 0x30}, {x22, 0x38}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {lr, 0x18}, {x29, 0x10}, {-1,-1} }}, + { 0x2c, 0x00, 0, 0x0038, 0x10240, FALSE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x20}, {x24, 0x28}, {x25, 0x30}, {lr, 0x38}, {x29, 0x30}, {-1,-1} }}, + { 0x30, 0x00, 0, 0x0058, 0x10260, FALSE, { {x19, 0x60}, {x20, 0x68}, {x21, 0x70}, {x22, 0x78}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {lr, 0x58}, {x29, 0x50}, {-1,-1} }}, + { 0x34, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x38, 0x00, 0, 0x0078, 0x10280, FALSE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x60}, {x24, 0x68}, {x25, 0x70}, {lr, 0x78}, {x29, 0x70}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, + { 0x3c, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {-1,-1} }}, + { 0x40, 0x00, 0, 0x0098, 0x102a0, FALSE, { {x19, 0xa0}, {x20, 0xa8}, {x21, 0xb0}, {x22, 0xb8}, {x23, 0x80}, {x24, 0x88}, {x25, 0x90}, {lr, 0x98}, {x29, 0x90}, {d8, 0x20}, {d9, 0x28}, {d10, 0x30}, {d11, 0x38}, {d12, 0x00}, {d13, 0x08}, {-1,-1} }}, + { 0x44, 0x20, 0, 0x00a8, 0x102b0, FALSE, { {x19, 0xb0}, {x20, 0xb8}, {x21, 0xc0}, {x22, 0xc8}, {x23, 0x90}, {x24, 0x98}, {x25, 0xa0}, {lr, 0xa8}, {x29, 0xa0}, {d8, 0x30}, {d9, 0x38}, {d10, 0x40}, {d11, 0x48}, {d12, 0x10}, {d13, 0x18}, {-1,-1} }}, + }; + + static const BYTE function_5[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x5b, 0x01, 0xa9, /* 04: stp x21, x22, [sp, #16] */ + 0xf7, 0x63, 0xbc, 0xa9, /* 08: stp x23, x24, [sp, #-64]! */ + 0xf9, 0x6b, 0x01, 0xa9, /* 0c: stp x25, x26, [sp, #16] */ + 0xfb, 0x73, 0x02, 0xa9, /* 10: stp x27, x28, [sp, #32] */ + 0xfd, 0x7b, 0x03, 0xa9, /* 14: stp x29, x30, [sp, #48] */ + 0xe8, 0x27, 0xbc, 0x6d, /* 18: stp d8, d9, [sp, #-64]! */ + 0xea, 0x2f, 0x01, 0x6d, /* 1c: stp d10, d11, [sp, #16] */ + 0xec, 0x37, 0x02, 0x6d, /* 20: stp d12, d13, [sp, #32] */ + 0xee, 0x3f, 0x03, 0x6d, /* 24: stp d14, d15, [sp, #48] */ + 0xc0, 0x03, 0x5f, 0xd6, /* 28: ret */ + }; + + static const DWORD unwind_info_5_header = + (sizeof(function_5)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (4 << 27); /* codes */ + + static const BYTE unwind_info_5[] = + { + DW(unwind_info_5_header), + + UWOP_SAVE_NEXT, /* 24: stp d14, d15, [sp, #48] */ + UWOP_SAVE_FREGP(12, 32), /* 20: stp d12, d13, [sp, #32] */ + UWOP_SAVE_NEXT, /* 1c: stp d10, d11, [sp, #16] */ + UWOP_SAVE_FREGP_X(8, 64), /* 18: stp d8, d9, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 14: stp x29, x30, [sp, #48] */ + UWOP_SAVE_REGP(27, 32), /* 10: stp x27, x28, [sp, #32] */ + UWOP_SAVE_NEXT, /* 0c: stp x25, x26, [sp, #16] */ + UWOP_SAVE_REGP_X(23, 64), /* 08: stp x23, x24, [sp, #-64]! */ + UWOP_SAVE_NEXT, /* 04: stp x21, x22, [sp, #16] */ + UWOP_SAVE_R19R20_X(32), /* 00: stp x19, x20, [sp, #-32]! */ + UWOP_END, + UWOP_NOP /* padding */ + }; + + /* Windows seems to only save one register for UWOP_SAVE_NEXT for + * float registers, contrary to what the documentation says. The tests + * for those cases are commented out; they succeed in wine but fail + * on native windows. */ + static const struct results results_5[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x00000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x00020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {x22, 0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {-1,-1} }}, + { 0x18, 0x00, 0, 0x38, 0x00060, TRUE, { {x19, 0x40}, {x20, 0x48}, {x21, 0x50}, {x22, 0x58}, {x23, 0x00}, {x24, 0x08}, {x25, 0x10}, {x26, 0x18}, {x27, 0x20}, {x28, 0x28}, {x29, 0x30}, {lr, 0x38}, {-1,-1} }}, + { 0x1c, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, +#if 0 + { 0x20, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {-1,-1} }}, + { 0x24, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {-1,-1} }}, + { 0x28, 0x00, 0, 0x78, 0x000a0, TRUE, { {x19, 0x80}, {x20, 0x88}, {x21, 0x90}, {x22, 0x98}, {x23, 0x40}, {x24, 0x48}, {x25, 0x50}, {x26, 0x58}, {x27, 0x60}, {x28, 0x68}, {x29, 0x70}, {lr, 0x78}, {d8, 0x00}, {d9, 0x08}, {d10, 0x10}, {d11, 0x18}, {d12, 0x20}, {d13, 0x28}, {d14, 0x30}, {d15, 0x38}, {-1,-1} }}, +#endif + }; + + static const BYTE function_6[] = + { + 0xf3, 0x53, 0xbd, 0xa9, /* 00: stp x19, x20, [sp, #-48]! */ + 0xf5, 0x0b, 0x00, 0xf9, /* 04: str x21, [sp, #16] */ + 0xe8, 0xa7, 0x01, 0x6d, /* 08: stp d8, d9, [sp, #24] */ + 0xea, 0x17, 0x00, 0xfd, /* 0c: str d10, [sp, #40] */ + 0xff, 0x03, 0x00, 0xd1, /* 10: sub sp, sp, #0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 14: nop */ + 0xff, 0x03, 0x00, 0x91, /* 18: add sp, sp, #0 */ + 0xea, 0x17, 0x40, 0xfd, /* 1c: ldr d10, [sp, #40] */ + 0xe8, 0xa7, 0x41, 0x6d, /* 20: ldp d8, d9, [sp, #24] */ + 0xf5, 0x0b, 0x40, 0xf9, /* 24: ldr x21, [sp, #16] */ + 0xf3, 0x53, 0xc3, 0xa8, /* 28: ldp x19, x20, [sp], #48 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 2c: ret */ + }; + + static const DWORD unwind_info_6_packed = + (1 << 0) | /* Flag */ + (sizeof(function_6)/4 << 2) | /* FunctionLength */ + (2 << 13) | /* RegF */ + (3 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_6[] = { DW(unwind_info_6_packed) }; + + static const struct results results_6[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {d10, 0x28}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {d8, 0x18}, {d9, 0x20}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {x21, 0x10}, {-1,-1} }}, + { 0x28, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19,0x00}, {x20,0x08}, {-1,-1} }}, + { 0x2c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_7[] = + { + 0xf3, 0x0f, 0x1d, 0xf8, /* 00: str x19, [sp, #-48]! */ + 0xe8, 0xa7, 0x00, 0x6d, /* 04: stp d8, d9, [sp, #8] */ + 0xea, 0xaf, 0x01, 0x6d, /* 08: stp d10, d11, [sp, #24] */ + 0xff, 0x03, 0x00, 0xd1, /* 0c: sub sp, sp, #0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xff, 0x03, 0x00, 0x91, /* 14: add sp, sp, #0 */ + 0xea, 0xaf, 0x41, 0x6d, /* 18: ldp d10, d11, [sp, #24] */ + 0xe8, 0xa7, 0x40, 0x6d, /* 1c: ldp d8, d9, [sp, #8] */ + 0xf3, 0x07, 0x43, 0xf8, /* 20: ldr x19, [sp], #48 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */ + }; + + static const DWORD unwind_info_7_packed = + (1 << 0) | /* Flag */ + (sizeof(function_7)/4 << 2) | /* FunctionLength */ + (3 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_7[] = { DW(unwind_info_7_packed) }; + + static const struct results results_7[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {d10, 0x18}, {d11, 0x20}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {d8, 0x08}, {d9, 0x10}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x030, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_8[] = + { + 0xe8, 0x27, 0xbf, 0x6d, /* 00: stp d8, d9, [sp, #-16]! */ + 0xff, 0x83, 0x00, 0xd1, /* 04: sub sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xff, 0x83, 0x00, 0x91, /* 0c: add sp, sp, #32 */ + 0xe8, 0x27, 0xc1, 0x6c, /* 10: ldp d8, d9, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_8_packed = + (1 << 0) | /* Flag */ + (sizeof(function_8)/4 << 2) | /* FunctionLength */ + (1 << 13) | /* RegF */ + (0 << 16) | /* RegI */ + (0 << 20) | /* H */ + (0 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_8[] = { DW(unwind_info_8_packed) }; + + static const struct results results_8[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x030, TRUE, { {d8, 0x20}, {d9, 0x28}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x010, TRUE, { {d8, 0x00}, {d9, 0x08}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_9[] = + { + 0xf3, 0x0f, 0x1b, 0xf8, /* 00: str x19, [sp, #-80]! */ + 0xe0, 0x87, 0x00, 0xa9, /* 04: stp x0, x1, [sp, #8] */ + 0xe2, 0x8f, 0x01, 0xa9, /* 08: stp x2, x3, [sp, #24] */ + 0xe4, 0x97, 0x02, 0xa9, /* 0c: stp x4, x5, [sp, #40] */ + 0xe6, 0x9f, 0x03, 0xa9, /* 10: stp x6, x7, [sp, #56] */ + 0xff, 0x83, 0x00, 0xd1, /* 14: sub sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 18: nop */ + 0xff, 0x83, 0x00, 0x91, /* 1c: add sp, sp, #32 */ + 0x1f, 0x20, 0x03, 0xd5, /* 20: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 24: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 28: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 2c: nop */ + 0xf3, 0x0f, 0x1b, 0xf8, /* 30: ldr x19, [sp], #80 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 34: ret */ + }; + + static const DWORD unwind_info_9_packed = + (1 << 0) | /* Flag */ + (sizeof(function_9)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (1 << 16) | /* RegI */ + (1 << 20) | /* H */ + (0 << 21) | /* CR */ + (7 << 23); /* FrameSize */ + + static const BYTE unwind_info_9[] = { DW(unwind_info_9_packed) }; + + static const struct results results_9[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x20, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x24, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x28, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x2c, 0x00, 0, ORIG_LR, 0x070, TRUE, { {x19, 0x20}, {-1,-1} }}, + { 0x30, 0x00, 0, ORIG_LR, 0x050, TRUE, { {x19, 0x00}, {-1,-1} }}, + { 0x34, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_10[] = + { + 0xfe, 0x0f, 0x1f, 0xf8, /* 00: str lr, [sp, #-16]! */ + 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xff, 0x43, 0x00, 0x91, /* 0c: add sp, sp, #16 */ + 0xfe, 0x07, 0x41, 0xf8, /* 10: ldr lr, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_10_packed = + (1 << 0) | /* Flag */ + (sizeof(function_10)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (0 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (2 << 23); /* FrameSize */ + + static const BYTE unwind_info_10[] = { DW(unwind_info_10_packed) }; + + static const struct results results_10[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x10, 0x020, TRUE, { {lr, 0x10}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x00, 0x010, TRUE, { {lr, 0x00}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_11[] = + { + 0xf3, 0x53, 0xbe, 0xa9, /* 00: stp x19, x20, [sp, #-32]! */ + 0xf5, 0x7b, 0x01, 0xa9, /* 04: stp x21, lr, [sp, #16] */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xff, 0x43, 0x00, 0x91, /* 10: add sp, sp, #16 */ + 0xf5, 0x7b, 0x41, 0xa9, /* 14: ldp x21, lr, [sp, #16] */ + 0xf3, 0x53, 0xc2, 0xa8, /* 18: ldp x19, x20, [sp], #32 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_11_packed = + (1 << 0) | /* Flag */ + (sizeof(function_11)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (3 << 16) | /* RegI */ + (0 << 20) | /* H */ + (1 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_11[] = { DW(unwind_info_11_packed) }; + + static const struct results results_11[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x28, 0x030, TRUE, { {x19, 0x10}, {x20, 0x18}, {x21, 0x20}, {lr, 0x28}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x18, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {x21, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x18, 0x00, 0, ORIG_LR, 0x020, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x1c, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_12[] = + { + 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */ + 0xfd, 0x7b, 0xbe, 0xa9, /* 04: stp x29, lr, [sp, #-32]! */ + 0xfd, 0x03, 0x00, 0x91, /* 08: mov x29, sp */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xbf, 0x03, 0x00, 0x91, /* 10: mov sp, x29 */ + 0xfd, 0x7b, 0xc2, 0xa8, /* 14: ldp x29, lr, [sp], #32 */ + 0xf3, 0x53, 0xc1, 0xa8, /* 18: ldp x19, x20, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 1c: ret */ + }; + + static const DWORD unwind_info_12_packed = + (1 << 0) | /* Flag */ + (sizeof(function_12)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (3 << 21) | /* CR */ + (3 << 23); /* FrameSize */ + + static const BYTE unwind_info_12[] = { DW(unwind_info_12_packed) }; + + static const struct results results_12[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x18, 0x040, TRUE, { {x19, 0x30}, {x20, 0x38}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x14, 0x10, 0, 0x08, 0x030, TRUE, { {x19, 0x20}, {x20, 0x28}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x18, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x1c, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_13[] = + { + 0xf3, 0x53, 0xbf, 0xa9, /* 00: stp x19, x20, [sp, #-16]! */ + 0xff, 0x43, 0x08, 0xd1, /* 04: sub sp, sp, #528 */ + 0xfd, 0x7b, 0x00, 0xd1, /* 08: stp x29, lr, [sp] */ + 0xfd, 0x03, 0x00, 0x91, /* 0c: mov x29, sp */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xbf, 0x03, 0x00, 0x91, /* 14: mov sp, x29 */ + 0xfd, 0x7b, 0x40, 0xa9, /* 18: ldp x29, lr, [sp] */ + 0xff, 0x43, 0x08, 0x91, /* 1c: add sp, sp, #528 */ + 0xf3, 0x53, 0xc1, 0xa8, /* 20: ldp x19, x20, [sp], #16 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 24: ret */ + }; + + static const DWORD unwind_info_13_packed = + (1 << 0) | /* Flag */ + (sizeof(function_13)/4 << 2) | /* FunctionLength */ + (0 << 13) | /* RegF */ + (2 << 16) | /* RegI */ + (0 << 20) | /* H */ + (3 << 21) | /* CR */ + (34 << 23); /* FrameSize */ + + static const BYTE unwind_info_13[] = { DW(unwind_info_13_packed) }; + + static const struct results results_13[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x08, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }}, + { 0x0c, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x10, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x14, 0x10, 0, 0x18, 0x230, TRUE, { {x19, 0x220}, {x20, 0x228}, {x29, 0x10}, {lr, 0x18}, {-1,-1} }}, + { 0x18, 0x10, 0, 0x08, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {x29, 0x00}, {lr, 0x08}, {-1,-1} }}, + { 0x1c, 0x10, 0, ORIG_LR, 0x220, TRUE, { {x19, 0x210}, {x20, 0x218}, {-1,-1} }}, + { 0x20, 0x10, 0, ORIG_LR, 0x010, TRUE, { {x19, 0x00}, {x20, 0x08}, {-1,-1} }}, + { 0x24, 0x10, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + }; + + static const BYTE function_14[] = + { + 0xe6, 0x9f, 0xba, 0xad, /* 00: stp q6, q7, [sp, #-0xb0]! */ + 0xe8, 0x27, 0x01, 0xad, /* 04: stp q8, q9, [sp, #0x20] */ + 0xea, 0x2f, 0x02, 0xad, /* 08: stp q10, q11, [sp, #0x40] */ + 0xec, 0x37, 0x03, 0xad, /* 0c: stp q12, q13, [sp, #0x60] */ + 0xee, 0x3f, 0x04, 0xad, /* 10: stp q14, q15, [sp, #0x80] */ + 0xfd, 0x7b, 0x0a, 0xa9, /* 14: stp x29, x30, [sp, #0xa0] */ + 0xfd, 0x83, 0x02, 0x91, /* 18: add x29, sp, #0xa0 */ + 0x1f, 0x20, 0x03, 0xd5, /* 1c: nop */ + 0xfd, 0x7b, 0x4a, 0xa9, /* 20: ldp x29, x30, [sp, #0xa0] */ + 0xee, 0x3f, 0x44, 0xad, /* 24: ldp q14, q15, [sp, #0x80] */ + 0xec, 0x37, 0x43, 0xad, /* 28: ldp q12, q13, [sp, #0x60] */ + 0xea, 0x2f, 0x42, 0xad, /* 2c: ldp q10, q11, [sp, #0x40] */ + 0xe8, 0x27, 0x41, 0xad, /* 30: ldp q8, q9, [sp, #0x20] */ + 0xe6, 0x9f, 0xc5, 0xac, /* 34: ldp q6, q7, [sp], #0xb0 */ + 0xc0, 0x03, 0x5f, 0xd6, /* 38: ret */ + }; + + static const DWORD unwind_info_14_header = + (sizeof(function_14)/4) | /* function length */ + (0 << 20) | /* X */ + (1 << 21) | /* E */ + (2 << 22) | /* epilog */ + (5 << 27); /* codes */ + + static const BYTE unwind_info_14[] = + { + DW(unwind_info_14_header), + UWOP_ADD_FP(0xa0), /* 18: add x29, sp, #0xa0 */ + UWOP_SAVE_FPLR(0xa0), /* 14: stp x29, x30, [sp, #0xa0] */ + UWOP_SAVE_ANY_REG(0x4e,0x88), /* 10: stp q14, q15, [sp, #0x80] */ + UWOP_SAVE_NEXT, /* 0c: stp q12, q13, [sp, #0x60] */ + UWOP_SAVE_ANY_REG(0x4a,0x84), /* 08: stp q10, q11, [sp, #0x40] */ + UWOP_SAVE_ANY_REG(0x48,0x82), /* 04: stp q8, q9, [sp, #0x20] */ + UWOP_SAVE_ANY_REG(0x66,0x8a), /* 00: stp q6, q7, [sp, #-0xb0]! */ + UWOP_END, + UWOP_NOP /* padding */ + }; + + static const struct results results_14[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x000, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {-1,-1} }}, + { 0x10, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {-1,-1} }}, + { 0x14, 0x00, 0, ORIG_LR, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {-1,-1} }}, + { 0x18, 0x00, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }}, + { 0x1c, 0xa0, 0, 0xa8, 0x0b0, TRUE, { {d6, 0x00}, {d7, 0x10}, {d8, 0x20}, {d9, 0x30}, {d10, 0x40}, {d11, 0x50}, {d12, 0x60}, {d13, 0x70}, {d14, 0x80}, {d15, 0x90}, {lr, 0xa8}, {x29, 0xa0}, {-1,-1} }}, + }; + + static const BYTE function_15[] = + { + 0x1f, 0x20, 0x03, 0xd5, /* 00: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0x1f, 0x20, 0x03, 0xd5, /* 10: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 14: ret */ + }; + + static const DWORD unwind_info_15_header = + (sizeof(function_15)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (2 << 27); /* codes */ + + static const BYTE unwind_info_15[] = + { + DW(unwind_info_15_header), + UWOP_END_C, + UWOP_SET_FP, /* mov x29, sp */ + UWOP_SAVE_REGP(19, 0x10), /* stp r19, r20, [sp, #0x10] */ + UWOP_SAVE_FPLR_X(0x20), /* stp r29, lr, [sp,-#0x20]! */ + UWOP_END, + UWOP_NOP, /* padding */ + UWOP_NOP, /* padding */ + }; + + static const struct results results_15[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x04, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x08, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x0c, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x10, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + { 0x14, 0x00, 0, 0x08, 0x020, TRUE, { {x29, 0x00}, {lr, 0x08}, {x19,0x10}, {x20,0x18}, {-1,-1} }}, + }; + + static const BYTE function_16[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 04: nop */ + 0xff, 0x43, 0x00, 0xd1, /* 08: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 0c: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 10: ret */ + }; + + static const DWORD unwind_info_16_header = + (sizeof(function_16)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_16[] = + { + DW(unwind_info_16_header), + + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_EC_CONTEXT, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_16[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0 , 0x00f8, 0x0a8, FALSE, { {x0, 0x80}, {x1, 0x88}, {x2, 0xb8}, {x3, 0xc0}, {x4, 0xc8}, {x5, 0xd0}, {x6, 0x130}, {x7, 0x140}, {x8, 0x78}, {x9, 0x150}, {x10, 0x160}, {x11, 0x170}, {x12, 0x180}, {x13, 0}, {x14, 0}, {x15, 0x190}, {x16, 0x0158014801380128}, {x17, 0x0198018801780168}, {x18, 0}, {x19, 0xd8}, {x20, 0xe0}, {x21, 0xe8}, {x22, 0x0f0}, {x23, 0}, {x24, 0}, {x25, 0xa8}, {x26, 0xb0}, {x27, 0x90}, {x28, 0}, {x29, 0xa0}, {lr, 0x120}, {d0, 0x1a0}, {d1, 0x1b0}, {d2, 0x1c0}, {d3, 0x1d0}, {d4, 0x1e0}, {d5, 0x1f0}, {d6, 0x200}, {d7, 0x210}, {d8, 0x220}, {d9, 0x230}, {d10, 0x240}, {d11, 0x250}, {d12, 0x260}, {d13, 0x270}, {d14, 0x280}, {d15, 0x290}, {-1,-1} }}, + { 0x08, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + { 0x0c, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + { 0x10, 0x00, 0 , 0x0108, 0x0b8, FALSE, { {x0, 0x90}, {x1, 0x98}, {x2, 0xc8}, {x3, 0xd0}, {x4, 0xd8}, {x5, 0xe0}, {x6, 0x140}, {x7, 0x150}, {x8, 0x88}, {x9, 0x160}, {x10, 0x170}, {x11, 0x180}, {x12, 0x190}, {x13, 0}, {x14, 0}, {x15, 0x1a0}, {x16, 0x0168015801480138}, {x17, 0x01a8019801880178}, {x18, 0}, {x19, 0xe8}, {x20, 0xf0}, {x21, 0xf8}, {x22, 0x100}, {x23, 0}, {x24, 0}, {x25, 0xb8}, {x26, 0xc0}, {x27, 0xa0}, {x28, 0}, {x29, 0xb0}, {lr, 0x130}, {d0, 0x1b0}, {d1, 0x1c0}, {d2, 0x1d0}, {d3, 0x1e0}, {d4, 0x1f0}, {d5, 0x200}, {d6, 0x210}, {d7, 0x220}, {d8, 0x230}, {d9, 0x240}, {d10, 0x250}, {d11, 0x260}, {d12, 0x270}, {d13, 0x280}, {d14, 0x290}, {d15, 0x2a0}, {-1,-1} }}, + }; + + static const BYTE function_17[] = + { + 0xff, 0x43, 0x00, 0xd1, /* 00: sub sp, sp, #16 */ + 0xff, 0x43, 0x00, 0xd1, /* 04: sub sp, sp, #16 */ + 0x1f, 0x20, 0x03, 0xd5, /* 08: nop */ + 0xc0, 0x03, 0x5f, 0xd6, /* 0c: ret */ + }; + + static const DWORD unwind_info_17_header = + (sizeof(function_17)/4) | /* function length */ + (0 << 20) | /* X */ + (0 << 21) | /* E */ + (0 << 22) | /* epilog */ + (1 << 27); /* codes */ + + static const BYTE unwind_info_17[] = + { + DW(unwind_info_17_header), + + UWOP_CLEAR_UNWOUND_TO_CALL, + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_ALLOC_SMALL(16), /* sub sp, sp, #16 */ + UWOP_END, + }; + + static const struct results results_17[] = + { + /* offset fp handler pc frame offset registers */ + { 0x00, 0x00, 0, ORIG_LR, 0x010, TRUE, { {-1,-1} }}, + { 0x04, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x08, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + { 0x0c, 0x00, 0, ORIG_LR, 0x020, TRUE, { {-1,-1} }}, + }; + + static const struct unwind_test tests[] = + { +#define TEST(func, unwind, unwind_packed, results, unwound_clear, last_ptr) \ + { func, sizeof(func), unwind, unwind_packed ? 0 : sizeof(unwind), results, ARRAY_SIZE(results), unwound_clear, last_ptr } + TEST(function_0, unwind_info_0, 0, results_0, 0, 0), + TEST(function_1, unwind_info_1, 1, results_1, 0, 0), + TEST(function_2, unwind_info_2, 0, results_2, 1, 0), + TEST(function_3, unwind_info_3, 0, results_3, 1, x28), + TEST(function_4, unwind_info_4, 0, results_4, 0, 0), + TEST(function_5, unwind_info_5, 0, results_5, 0, 0), + TEST(function_6, unwind_info_6, 1, results_6, 0, 0), + TEST(function_7, unwind_info_7, 1, results_7, 0, 0), + TEST(function_8, unwind_info_8, 1, results_8, 0, 0), + TEST(function_9, unwind_info_9, 1, results_9, 0, 0), + TEST(function_10, unwind_info_10, 1, results_10, 0, 0), + TEST(function_11, unwind_info_11, 1, results_11, 0, 0), + TEST(function_12, unwind_info_12, 1, results_12, 0, 0), + TEST(function_13, unwind_info_13, 1, results_13, 0, 0), + TEST(function_14, unwind_info_14, 0, results_14, 0, 0), + TEST(function_15, unwind_info_15, 0, results_15, 0, 0), + TEST(function_16, unwind_info_16, 0, results_16, 1, x18), + TEST(function_17, unwind_info_17, 0, results_17, 2, 0), +#undef TEST + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + call_virtual_unwind( i, &tests[i] ); +} + +#elif defined(__x86_64__) + +#define UWOP_PUSH_NONVOL 0 +#define UWOP_ALLOC_LARGE 1 +#define UWOP_ALLOC_SMALL 2 +#define UWOP_SET_FPREG 3 +#define UWOP_SAVE_NONVOL 4 +#define UWOP_SAVE_NONVOL_FAR 5 +#define UWOP_SAVE_XMM128 8 +#define UWOP_SAVE_XMM128_FAR 9 +#define UWOP_PUSH_MACHFRAME 10 + +struct results +{ + int rip_offset; /* rip offset from code start */ + int rbp_offset; /* rbp offset from stack pointer */ + int handler; /* expect handler to be set? */ + int rip; /* expected final rip value */ + int frame; /* expected frame return value */ + int regs[8][2]; /* expected values for registers */ +}; + +struct unwind_test +{ + const BYTE *function; + size_t function_size; + const BYTE *unwind_info; + const struct results *results; + unsigned int nb_results; + const struct results *broken_results; +}; + +enum regs +{ + rax, rcx, rdx, rbx, rsp, rbp, rsi, rdi, + r8, r9, r10, r11, r12, r13, r14, r15 +}; + +static const char * const reg_names[16] = +{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" +}; + +#define UWOP(code,info) (UWOP_##code | ((info) << 4)) + +static void call_virtual_unwind( int testnum, const struct unwind_test *test ) +{ + static const int code_offset = 1024; + static const int unwind_offset = 2048; + void *handler, *data; + CONTEXT context; + RUNTIME_FUNCTION runtime_func; + KNONVOLATILE_CONTEXT_POINTERS ctx_ptr; + UINT i, j, k, broken_k; + ULONG64 fake_stack[256]; + ULONG64 frame, orig_rip, orig_rbp, unset_reg; + UINT unwind_size = 4 + 2 * test->unwind_info[2] + 8; + void *expected_handler, *broken_handler; + + memcpy( (char *)code_mem + code_offset, test->function, test->function_size ); + memcpy( (char *)code_mem + unwind_offset, test->unwind_info, unwind_size ); + + runtime_func.BeginAddress = code_offset; + runtime_func.EndAddress = code_offset + test->function_size; + runtime_func.UnwindData = unwind_offset; + + trace( "code: %p stack: %p\n", code_mem, fake_stack ); + + for (i = 0; i < test->nb_results; i++) + { + memset( &ctx_ptr, 0, sizeof(ctx_ptr) ); + memset( &context, 0x55, sizeof(context) ); + memset( &unset_reg, 0x55, sizeof(unset_reg) ); + for (j = 0; j < 256; j++) fake_stack[j] = j * 8; + + context.Rsp = (ULONG_PTR)fake_stack; + context.Rbp = (ULONG_PTR)fake_stack + test->results[i].rbp_offset; + orig_rbp = context.Rbp; + orig_rip = (ULONG64)code_mem + code_offset + test->results[i].rip_offset; + + trace( "%u/%u: rip=%p (%02x) rbp=%p rsp=%p\n", testnum, i, + (void *)orig_rip, *(BYTE *)orig_rip, (void *)orig_rbp, (void *)context.Rsp ); + + data = (void *)0xdeadbeef; + handler = RtlVirtualUnwind( UNW_FLAG_EHANDLER, (ULONG64)code_mem, orig_rip, + &runtime_func, &context, &data, &frame, &ctx_ptr ); + + expected_handler = test->results[i].handler ? (char *)code_mem + 0x200 : NULL; + broken_handler = test->broken_results && test->broken_results[i].handler ? (char *)code_mem + 0x200 : NULL; + + ok( handler == expected_handler || broken( test->broken_results && handler == broken_handler ), + "%u/%u: wrong handler %p/%p\n", testnum, i, handler, expected_handler ); + if (handler) + ok( *(DWORD *)data == 0x08070605, "%u/%u: wrong handler data %lx\n", testnum, i, *(DWORD *)data ); + else + ok( data == (void *)0xdeadbeef, "%u/%u: handler data set to %p\n", testnum, i, data ); + + ok( context.Rip == test->results[i].rip + || broken( test->broken_results && context.Rip == test->broken_results[i].rip ), + "%u/%u: wrong rip %p/%x\n", testnum, i, (void *)context.Rip, test->results[i].rip ); + ok( frame == (ULONG64)fake_stack + test->results[i].frame + || broken( test->broken_results && frame == (ULONG64)fake_stack + test->broken_results[i].frame ), + "%u/%u: wrong frame %p/%p\n", + testnum, i, (void *)frame, (char *)fake_stack + test->results[i].frame ); + + for (j = 0; j < 16; j++) + { + static const UINT nb_regs = ARRAY_SIZE(test->results[i].regs); + + for (k = 0; k < nb_regs; k++) + { + if (test->results[i].regs[k][0] == -1) + { + k = nb_regs; + break; + } + if (test->results[i].regs[k][0] == j) break; + } + + if (test->broken_results) + { + for (broken_k = 0; broken_k < nb_regs; broken_k++) + { + if (test->broken_results[i].regs[broken_k][0] == -1) + { + broken_k = nb_regs; + break; + } + if (test->broken_results[i].regs[broken_k][0] == j) + break; + } + } + else + { + broken_k = k; + } + + if (j == rsp) /* rsp is special */ + { + ULONG64 expected_rsp, broken_rsp; + + ok( !ctx_ptr.IntegerContext[j], + "%u/%u: rsp should not be set in ctx_ptr\n", testnum, i ); + expected_rsp = test->results[i].regs[k][1] < 0 + ? -test->results[i].regs[k][1] : (ULONG64)fake_stack + test->results[i].regs[k][1]; + if (test->broken_results) + broken_rsp = test->broken_results[i].regs[k][1] < 0 + ? -test->broken_results[i].regs[k][1] + : (ULONG64)fake_stack + test->broken_results[i].regs[k][1]; + else + broken_rsp = expected_rsp; + + ok( context.Rsp == expected_rsp || broken( context.Rsp == broken_rsp ), + "%u/%u: register rsp wrong %p/%p\n", + testnum, i, (void *)context.Rsp, (void *)expected_rsp ); + continue; + } + + if (ctx_ptr.IntegerContext[j]) + { + ok( k < nb_regs || broken( broken_k < nb_regs ), "%u/%u: register %s should not be set to %Ix\n", + testnum, i, reg_names[j], *(&context.Rax + j) ); + ok( k == nb_regs || *(&context.Rax + j) == test->results[i].regs[k][1] + || broken( broken_k == nb_regs || *(&context.Rax + j) + == test->broken_results[i].regs[broken_k][1] ), + "%u/%u: register %s wrong %p/%x\n", + testnum, i, reg_names[j], (void *)*(&context.Rax + j), test->results[i].regs[k][1] ); + } + else + { + ok( k == nb_regs || broken( broken_k == nb_regs ), "%u/%u: register %s should be set\n", + testnum, i, reg_names[j] ); + if (j == rbp) + ok( context.Rbp == orig_rbp, "%u/%u: register rbp wrong %p/unset\n", + testnum, i, (void *)context.Rbp ); + else + ok( *(&context.Rax + j) == unset_reg, + "%u/%u: register %s wrong %p/unset\n", + testnum, i, reg_names[j], (void *)*(&context.Rax + j)); + } + } + } +} + +static void test_virtual_unwind(void) +{ + static const BYTE function_0[] = + { + 0xff, 0xf5, /* 00: push %rbp */ + 0x48, 0x81, 0xec, 0x10, 0x01, 0x00, 0x00, /* 02: sub $0x110,%rsp */ + 0x48, 0x8d, 0x6c, 0x24, 0x30, /* 09: lea 0x30(%rsp),%rbp */ + 0x48, 0x89, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 0e: mov %rbx,0xf0(%rbp) */ + 0x48, 0x89, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 15: mov %rsi,0xf8(%rbp) */ + 0x90, /* 1c: nop */ + 0x48, 0x8b, 0x9d, 0xf0, 0x00, 0x00, 0x00, /* 1d: mov 0xf0(%rbp),%rbx */ + 0x48, 0x8b, 0xb5, 0xf8, 0x00, 0x00, 0x00, /* 24: mov 0xf8(%rbp),%rsi */ + 0x48, 0x8d, 0xa5, 0xe0, 0x00, 0x00, 0x00, /* 2b: lea 0xe0(%rbp),%rsp */ + 0x5d, /* 32: pop %rbp */ + 0xc3 /* 33: ret */ + }; + + static const BYTE unwind_info_0[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x1c, /* prolog size */ + 8, /* opcode count */ + (0x03 << 4) | rbp, /* frame reg rbp offset 0x30 */ + + 0x1c, UWOP(SAVE_NONVOL, rsi), 0x25, 0, /* 1c: mov %rsi,0x128(%rsp) */ + 0x15, UWOP(SAVE_NONVOL, rbx), 0x24, 0, /* 15: mov %rbx,0x120(%rsp) */ + 0x0e, UWOP(SET_FPREG, rbp), /* 0e: lea 0x30(%rsp),rbp */ + 0x09, UWOP(ALLOC_LARGE, 0), 0x22, 0, /* 09: sub $0x110,%rsp */ + 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_0[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }}, + { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }}, + { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }}, + { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x2b, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}}, + { 0x32, 0x40, FALSE, 0x008, 0x010, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}}, + { 0x33, 0x40, FALSE, 0x000, 0x010, { {rsp,0x008}, {-1,-1}}}, + }; + + static const struct results broken_results_0[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x02, 0x40, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + { 0x09, 0x40, FALSE, 0x118, 0x000, { {rsp,0x120}, {rbp,0x110}, {-1,-1} }}, + { 0x0e, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {-1,-1} }}, + { 0x15, 0x40, FALSE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {-1,-1} }}, + { 0x1c, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x1d, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + { 0x24, 0x40, TRUE, 0x128, 0x010, { {rsp,0x130}, {rbp,0x120}, {rbx,0x130}, {rsi,0x138}, {-1,-1}}}, + /* On Win11 output frame in epilogue corresponds to context->Rsp - 0x8 when fpreg is set. */ + { 0x2b, 0x40, FALSE, 0x128, 0x128, { {rsp,0x130}, {rbp,0x120}, {-1,-1}}}, + { 0x32, 0x40, FALSE, 0x008, 0x008, { {rsp,0x010}, {rbp,0x000}, {-1,-1}}}, + { 0x33, 0x40, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1}}}, + }; + + static const BYTE function_1[] = + { + 0x53, /* 00: push %rbx */ + 0x55, /* 01: push %rbp */ + 0x56, /* 02: push %rsi */ + 0x57, /* 03: push %rdi */ + 0x41, 0x54, /* 04: push %r12 */ + 0x48, 0x83, 0xec, 0x30, /* 06: sub $0x30,%rsp */ + 0x90, 0x90, /* 0a: nop; nop */ + 0x48, 0x83, 0xc4, 0x30, /* 0c: add $0x30,%rsp */ + 0x41, 0x5c, /* 10: pop %r12 */ + 0x5f, /* 12: pop %rdi */ + 0x5e, /* 13: pop %rsi */ + 0x5d, /* 14: pop %rbp */ + 0x5b, /* 15: pop %rbx */ + 0xc3 /* 16: ret */ + }; + + static const BYTE unwind_info_1[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0a, /* prolog size */ + 6, /* opcode count */ + 0, /* frame reg */ + + 0x0a, UWOP(ALLOC_SMALL, 5), /* 0a: sub $0x30,%rsp */ + 0x06, UWOP(PUSH_NONVOL, r12), /* 06: push %r12 */ + 0x04, UWOP(PUSH_NONVOL, rdi), /* 04: push %rdi */ + 0x03, UWOP(PUSH_NONVOL, rsi), /* 03: push %rsi */ + 0x02, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x01, UWOP(PUSH_NONVOL, rbx), /* 01: push %rbx */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_1[] = + { + /* offset rbp handler rip frame registers */ + { 0x00, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, + { 0x02, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, + { 0x03, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, + { 0x04, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, + { 0x06, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, + { 0x0a, 0x50, TRUE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, + { 0x0c, 0x50, FALSE, 0x058, 0x000, { {rsp,0x060}, {rbx,0x050}, {rbp,0x048}, {rsi,0x040}, {rdi,0x038}, {r12,0x030}, {-1,-1} }}, + { 0x10, 0x50, FALSE, 0x028, 0x000, { {rsp,0x030}, {rbx,0x020}, {rbp,0x018}, {rsi,0x010}, {rdi,0x008}, {r12,0x000}, {-1,-1} }}, + { 0x12, 0x50, FALSE, 0x020, 0x000, { {rsp,0x028}, {rbx,0x018}, {rbp,0x010}, {rsi,0x008}, {rdi,0x000}, {-1,-1} }}, + { 0x13, 0x50, FALSE, 0x018, 0x000, { {rsp,0x020}, {rbx,0x010}, {rbp,0x008}, {rsi,0x000}, {-1,-1} }}, + { 0x14, 0x50, FALSE, 0x010, 0x000, { {rsp,0x018}, {rbx,0x008}, {rbp,0x000}, {-1,-1} }}, + { 0x15, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbx,0x000}, {-1,-1} }}, + { 0x16, 0x50, FALSE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + }; + + static const BYTE function_2[] = + { + 0x55, /* 00: push %rbp */ + 0x90, 0x90, /* 01: nop; nop */ + 0x5d, /* 03: pop %rbp */ + 0xc3 /* 04: ret */ + }; + + static const BYTE unwind_info_2[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 2, /* opcode count */ + 0, /* frame reg */ + + 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x00, UWOP(PUSH_MACHFRAME, 0), /* 00 */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_2[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x008, 0x000, { {rsp,-0x020}, {rbp,0x000}, {-1,-1} }}, + }; + + static const BYTE unwind_info_3[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 2, /* opcode count */ + 0, /* frame reg */ + + 0x01, UWOP(PUSH_NONVOL, rbp), /* 02: push %rbp */ + 0x00, UWOP(PUSH_MACHFRAME, 1), /* 00 */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_3[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x010, 0x000, { {rsp,-0x028}, {rbp,0x000}, {-1,-1} }}, + }; + + static const BYTE function_4[] = + { + 0x55, /* 00: push %rbp */ + 0x5d, /* 01: pop %rbp */ + 0xc3 /* 02: ret */ + }; + + static const BYTE unwind_info_4[] = + { + 1 | (UNW_FLAG_EHANDLER << 3), /* version + flags */ + 0x0, /* prolog size */ + 0, /* opcode count */ + 0, /* frame reg */ + + 0x00, 0x02, 0x00, 0x00, /* handler */ + 0x05, 0x06, 0x07, 0x08, /* data */ + }; + + static const struct results results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, TRUE, 0x000, 0x000, { {rsp,0x008}, {-1,-1} }}, + }; + + static const struct results broken_results_4[] = + { + /* offset rbp handler rip frame registers */ + { 0x01, 0x50, FALSE, 0x008, 0x000, { {rsp,0x010}, {rbp,0x000}, {-1,-1} }}, + }; + + static const struct unwind_test tests[] = + { + { function_0, sizeof(function_0), unwind_info_0, results_0, ARRAY_SIZE(results_0), broken_results_0 }, + { function_1, sizeof(function_1), unwind_info_1, results_1, ARRAY_SIZE(results_1) }, + { function_2, sizeof(function_2), unwind_info_2, results_2, ARRAY_SIZE(results_2) }, + { function_2, sizeof(function_2), unwind_info_3, results_3, ARRAY_SIZE(results_3) }, + + /* Broken before Win10 1809. */ + { function_4, sizeof(function_4), unwind_info_4, results_4, ARRAY_SIZE(results_4), broken_results_4 }, + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); i++) + call_virtual_unwind( i, &tests[i] ); +} + +static RUNTIME_FUNCTION* CALLBACK dynamic_unwind_callback( DWORD64 pc, PVOID context ) +{ + static const int code_offset = 1024; + static RUNTIME_FUNCTION runtime_func; + (*(DWORD *)context)++; + + runtime_func.BeginAddress = code_offset + 16; + runtime_func.EndAddress = code_offset + 32; + runtime_func.UnwindData = 0; + return &runtime_func; +} + +static void test_dynamic_unwind(void) +{ + static const int code_offset = 1024; + char buf[2 * sizeof(RUNTIME_FUNCTION) + 4]; + SYSTEM_CPU_INFORMATION info; + RUNTIME_FUNCTION *runtime_func, *func; + ULONG_PTR table, base; + void *growable_table, *ptr; + NTSTATUS status; + DWORD count; + ULONG len, len2; + + if (!pRtlInstallFunctionTableCallback || !pRtlLookupFunctionEntry) + { + win_skip( "Dynamic unwind functions not found\n" ); + return; + } + + /* Test RtlAddFunctionTable with aligned RUNTIME_FUNCTION pointer */ + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->EndAddress = code_offset + 16; + runtime_func->UnwindData = 0; + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); + + /* Lookup function outside of any function table */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); + ok( !base || broken(base == 0xdeadbeef), + "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base ); + + /* Test with pointer inside of our function */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == runtime_func, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + /* Test RtlDeleteFunctionTable */ + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (aligned)\n", runtime_func ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); + + /* Unaligned RUNTIME_FUNCTION pointer */ + runtime_func = (RUNTIME_FUNCTION *)((ULONG_PTR)buf | 0x3); + runtime_func->BeginAddress = code_offset; + runtime_func->EndAddress = code_offset + 16; + runtime_func->UnwindData = 0; + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (unaligned)\n", runtime_func ); + + /* Attempt to insert the same entry twice */ + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->EndAddress = code_offset + 16; + runtime_func->UnwindData = 0; + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); + ok( pRtlAddFunctionTable( runtime_func, 1, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (first attempt)\n", runtime_func ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for runtime_func = %p (second attempt)\n", runtime_func ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable returned success for nonexistent table runtime_func = %p\n", runtime_func ); + + /* Empty table */ + ok( pRtlAddFunctionTable( runtime_func, 0, (ULONG_PTR)code_mem ), + "RtlAddFunctionTable failed for empty table\n" ); + ok( pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable failed for empty table\n" ); + ok( !pRtlDeleteFunctionTable( runtime_func ), + "RtlDeleteFunctionTable succeeded twice for empty table\n" ); + + /* Test RtlInstallFunctionTableCallback with both low bits unset */ + table = (ULONG_PTR)code_mem; + ok( !pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), + "RtlInstallFunctionTableCallback returned success for table = %Ix\n", table ); + + /* Test RtlInstallFunctionTableCallback with both low bits set */ + table = (ULONG_PTR)code_mem | 0x3; + ok( pRtlInstallFunctionTableCallback( table, (ULONG_PTR)code_mem, code_offset + 32, &dynamic_unwind_callback, (PVOID*)&count, NULL ), + "RtlInstallFunctionTableCallback failed for table = %Ix\n", table ); + + /* Lookup function outside of any function table */ + count = 0; + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry returned unexpected function, expected: NULL, got: %p\n", func ); + ok( !base || broken(base == 0xdeadbeef), + "RtlLookupFunctionEntry modified base address, expected: 0, got: %Ix\n", base ); + ok( !count, + "RtlLookupFunctionEntry issued %ld unexpected calls to dynamic_unwind_callback\n", count ); + + /* Test with pointer inside of our function */ + count = 0; + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 24, &base, NULL ); + ok( func != NULL && func->BeginAddress == code_offset + 16 && func->EndAddress == code_offset + 32, + "RtlLookupFunctionEntry didn't return expected function, got: %p\n", func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + ok( count == 1, + "RtlLookupFunctionEntry issued %ld calls to dynamic_unwind_callback, expected: 1\n", count ); + + /* Clean up again */ + ok( pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), + "RtlDeleteFunctionTable failed for table = %p\n", (PVOID)table ); + ok( !pRtlDeleteFunctionTable( (PRUNTIME_FUNCTION)table ), + "RtlDeleteFunctionTable returned success for nonexistent table = %p\n", (PVOID)table ); + + if (!pRtlAddGrowableFunctionTable) + { + win_skip("Growable function tables are not supported.\n"); + return; + } + + runtime_func = (RUNTIME_FUNCTION *)buf; + runtime_func->BeginAddress = code_offset; + runtime_func->EndAddress = code_offset + 16; + runtime_func->UnwindData = 0; + runtime_func++; + runtime_func->BeginAddress = code_offset + 16; + runtime_func->EndAddress = code_offset + 32; + runtime_func->UnwindData = 0; + runtime_func = (RUNTIME_FUNCTION *)buf; + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 1, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 2, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 1, 2, (ULONG_PTR)code_mem, (ULONG_PTR)code_mem + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + pRtlDeleteGrowableFunctionTable( growable_table ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, runtime_func, 0, 2, (ULONG_PTR)code_mem, + (ULONG_PTR)code_mem + code_offset + 64 ); + ok(!status, "RtlAddGrowableFunctionTable failed for runtime_func = %p (aligned), %#lx.\n", runtime_func, status ); + ok(growable_table != 0, "Unexpected table value.\n"); + + /* Current count is 0. */ + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 1 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 8, &base, NULL ); + ok( func == runtime_func, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + /* Second function is inaccessible yet. */ + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == NULL, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + + pRtlGrowFunctionTable( growable_table, 2 ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 16, &base, NULL ); + ok( func == runtime_func + 1, + "RtlLookupFunctionEntry didn't return expected function, expected: %p, got: %p\n", runtime_func, func ); + ok( base == (ULONG_PTR)code_mem, + "RtlLookupFunctionEntry returned invalid base, expected: %Ix, got: %Ix\n", (ULONG_PTR)code_mem, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)code_mem + code_offset + 32, &base, NULL ); + ok( func == NULL, "RtlLookupFunctionEntry got %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)code_mem + code_offset + 8, &base, &len ); + ok( func == NULL, "RtlLookupFunctionTable wrong table, got: %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionTable wrong base, got: %Ix\n", base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)pRtlLookupFunctionEntry, &base, &len ); + ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"), + "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 ); + ok( func == ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr ); + ok( len == len2, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", len, len2 ); + + pRtlDeleteGrowableFunctionTable( growable_table ); + + if (pRtlGetNativeSystemInformation && + !pRtlGetNativeSystemInformation( SystemCpuInformation, &info, sizeof(info), &len ) && + info.ProcessorArchitecture == PROCESSOR_ARCHITECTURE_ARM64) + { + static const BYTE fast_forward[] = { 0x48, 0x8b, 0xc4, 0x48, 0x89, 0x58, 0x20, 0x55, 0x5d, 0xe9 }; + IMAGE_ARM64EC_METADATA *metadata; + ARM64_RUNTIME_FUNCTION *arm64func = (ARM64_RUNTIME_FUNCTION *)buf; + MEM_EXTENDED_PARAMETER param = { 0 }; + SIZE_T size = 0x1000; + + if (!memcmp( pRtlLookupFunctionEntry, fast_forward, sizeof(fast_forward) )) + { + ptr = (char *)pRtlLookupFunctionEntry + sizeof(fast_forward); + ptr = (char *)ptr + 4 + *(int *)ptr; + base = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)ptr, &base, &len ); + ok( base == (ULONG_PTR)GetModuleHandleA("ntdll.dll"), + "RtlLookupFunctionTable wrong base, got: %Ix / %p\n", base, GetModuleHandleA("ntdll.dll") ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &len2 ); + ok( func != ptr, "RtlLookupFunctionTable wrong table, got: %p / %p\n", func, ptr ); + ptr = RtlImageDirectoryEntryToData( (void *)base, TRUE, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &len2 ); + metadata = (void *)((IMAGE_LOAD_CONFIG_DIRECTORY *)ptr)->CHPEMetadataPointer; + ok( (char *)func == (char *)base + metadata->ExtraRFETable, + "RtlLookupFunctonTable wrong table, got: %p / %p\n", func, (char *)base + metadata->ExtraRFETable ); + ok( len == metadata->ExtraRFETableSize, "RtlLookupFunctionTable wrong len, got: %lu / %lu\n", + len, metadata->ExtraRFETableSize ); + } + + arm64func->BeginAddress = code_offset; + arm64func->Flag = 1; + arm64func->FunctionLength = 4; + arm64func->RegF = 1; + arm64func->RegI = 1; + arm64func->H = 1; + arm64func->CR = 1; + arm64func->FrameSize = 1; + arm64func++; + arm64func->BeginAddress = code_offset + 16; + arm64func->Flag = 1; + arm64func->FunctionLength = 4; + arm64func->RegF = 1; + arm64func->RegI = 1; + arm64func->H = 1; + arm64func->CR = 1; + arm64func->FrameSize = 1; + + param.Type = MemExtendedParameterAttributeFlags; + param.ULong64 = MEM_EXTENDED_PARAMETER_EC_CODE; + ptr = NULL; + status = pNtAllocateVirtualMemoryEx( GetCurrentProcess(), &ptr, &size, MEM_RESERVE | MEM_COMMIT, + PAGE_EXECUTE_READWRITE, ¶m, 1 ); + ok( !status, "NtAllocateVirtualMemoryEx failed %lx\n", status ); + + growable_table = NULL; + status = pRtlAddGrowableFunctionTable( &growable_table, (RUNTIME_FUNCTION *)buf, + 2, 2, (ULONG_PTR)ptr, (ULONG_PTR)ptr + code_offset + 64 ); + ok( !status, "RtlAddGrowableFunctionTable failed %lx\n", status ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)ptr + code_offset + 8, &base, NULL ); + ok( func == (RUNTIME_FUNCTION *)buf, "RtlLookupFunctionEntry expected func: %p, got: %p\n", + buf, func ); + ok( base == (ULONG_PTR)ptr, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", + (ULONG_PTR)ptr, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)ptr + code_offset + 16, &base, NULL ); + ok( func == (RUNTIME_FUNCTION *)(buf + sizeof(*arm64func)), + "RtlLookupFunctionEntry expected func: %p, got: %p\n", buf + sizeof(*arm64func), func ); + ok( base == (ULONG_PTR)ptr, "RtlLookupFunctionEntry expected base: %Ix, got: %Ix\n", + (ULONG_PTR)ptr, base ); + + base = 0xdeadbeef; + func = pRtlLookupFunctionEntry( (ULONG_PTR)ptr + code_offset + 32, &base, NULL ); + ok( !func, "RtlLookupFunctionEntry got: %p\n", func ); + ok( base == 0xdeadbeef, "RtlLookupFunctionEntry got: %Ix\n", base ); + + pRtlDeleteGrowableFunctionTable( growable_table ); + VirtualFree( ptr, 0, MEM_RELEASE ); + } +} + +#endif + +START_TEST(unwind) +{ + ntdll = GetModuleHandleA("ntdll.dll"); + code_mem = VirtualAlloc( NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + +#define X(f) p##f = (void*)GetProcAddress(ntdll, #f) + X(NtAllocateVirtualMemoryEx); + X(RtlAddFunctionTable); + X(RtlAddGrowableFunctionTable); + X(RtlDeleteFunctionTable); + X(RtlDeleteGrowableFunctionTable); + X(RtlGetNativeSystemInformation); + X(RtlGrowFunctionTable); + X(RtlInstallFunctionTableCallback); + X(RtlLookupFunctionEntry); + X(RtlLookupFunctionTable); +#undef X + +#ifdef __arm__ + test_virtual_unwind(); +#elif defined(__aarch64__) + test_virtual_unwind(); +#elif defined(__x86_64__) + test_virtual_unwind(); + test_dynamic_unwind(); +#endif +} + +#else /* !__i386__ */ + +START_TEST(unwind) +{ +} + +#endif /* !__i386__ */ From c67cc17152add818b1818f29e04d8caf44f0a7ba Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Sep 2024 14:05:13 -0600 Subject: [PATCH 32/67] ntdll/tests: Add a test for module function table search. CW-Bug-Id: #24258 --- dlls/ntdll/tests/unwind.c | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c index e146b1674df..a775999410a 100644 --- a/dlls/ntdll/tests/unwind.c +++ b/dlls/ntdll/tests/unwind.c @@ -3181,6 +3181,53 @@ static void test_dynamic_unwind(void) } } +static void test_exception_directory(void) +{ + ULONG len, exc_dir_size, old, saved_size, saved_address; + HMODULE mod = GetModuleHandleW( NULL ); + PRUNTIME_FUNCTION func; + IMAGE_NT_HEADERS *nt; + ULONG_PTR base; + void *exc_dir; + BOOL ret; + + func = pRtlLookupFunctionTable( (ULONG_PTR)test_exception_directory, &base, &len ); + ok( !!func, "got NULL.\n" ); + + exc_dir = RtlImageDirectoryEntryToData( mod, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &exc_dir_size ); + ok( func == exc_dir, "got %p, expected %p.\n", func, exc_dir ); + ok( len == exc_dir_size, "got %lu, expected %lu.\n", len, exc_dir_size ); + ok( base == (ULONG_PTR)mod, "got %#Ix, expected %p.\n", base, mod ); + + nt = RtlImageNtHeader( mod ); + ok( !!nt, "got NULL.\n" ); + ret = VirtualProtect( &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress, + sizeof(DWORD) * 2, PAGE_READWRITE, &old); + ok( ret, "got error %lu.\n", GetLastError() ); + saved_size = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size; + saved_address = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress; + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = 0; + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = 0; + ret = VirtualProtect( &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress, + sizeof(DWORD) * 2, old, &old); + ok( ret, "got error %lu.\n", GetLastError() ); + + base = 0xdeadbeef; + len = 0xdeadbeef; + func = pRtlLookupFunctionTable( (ULONG_PTR)test_exception_directory, &base, &len ); + todo_wine ok( func == exc_dir, "got %p, expected %p.\n", func, exc_dir ); + todo_wine ok( len == exc_dir_size, "got %lu, expected %lu.\n", len, exc_dir_size ); + ok( base == (ULONG_PTR)mod, "got %#Ix, expected %p.\n", base, mod ); + ret = VirtualProtect( &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress, + sizeof(DWORD) * 2, PAGE_READWRITE, &old); + ok( ret, "got error %lu.\n", GetLastError() ); + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = saved_size; + nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = saved_address; + ret = VirtualProtect( &nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress, + sizeof(DWORD) * 2, old, &old); + ok( ret, "got error %lu.\n", GetLastError() ); +} + #endif START_TEST(unwind) @@ -3209,6 +3256,7 @@ START_TEST(unwind) test_virtual_unwind(); test_dynamic_unwind(); #endif + test_exception_directory(); } #else /* !__i386__ */ From f3cf8d1db699096322095506aac6e8f505fed488 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Sep 2024 13:38:50 -0600 Subject: [PATCH 33/67] ntdll: Maintain module exception directory table. CW-Bug-Id: #24258 --- dlls/ntdll/exception.c | 98 +++++++++++++++++++++++++++++++++++++++++ dlls/ntdll/loader.c | 3 ++ dlls/ntdll/ntdll_misc.h | 2 + include/winnt.h | 10 +++++ 4 files changed, 113 insertions(+) diff --git a/dlls/ntdll/exception.c b/dlls/ntdll/exception.c index d4b5f901e10..d4c1f9758d9 100644 --- a/dlls/ntdll/exception.c +++ b/dlls/ntdll/exception.c @@ -326,6 +326,94 @@ static RTL_CRITICAL_SECTION_DEBUG dynamic_unwind_debug = }; static RTL_CRITICAL_SECTION dynamic_unwind_section = { &dynamic_unwind_debug, -1, 0, 0, 0, 0 }; +struct module_exception_dir_entry +{ + void *exception_dir; + void *dllbase; + DWORD size_of_image; + DWORD exception_dir_size; +}; + +#define MAX_MODULE_EXCEPTION_DIR_ENTRIES 512 + +struct module_exception_dir_table +{ + DWORD count; + DWORD max_count; + DWORD unk; + DWORD unk2; + struct module_exception_dir_entry entries[MAX_MODULE_EXCEPTION_DIR_ENTRIES]; +}; +struct module_exception_dir_table DECLSPEC_ALLOCATE(".mrdata") exception_dir_table = { 1, MAX_MODULE_EXCEPTION_DIR_ENTRIES }; + +C_ASSERT( sizeof(struct module_exception_dir_entry) == 0x18 ); +C_ASSERT( offsetof(struct module_exception_dir_table, entries) == 0x10 ); + +void register_module_exception_directory( void *module ) +{ + SIZE_T size = sizeof(exception_dir_table); + struct module_exception_dir_entry e; + void *addr = &exception_dir_table; + IMAGE_NT_HEADERS *nt; + ULONG old_prot; + unsigned int i; + + if (!(nt = RtlImageNtHeader( module ))) return; + e.exception_dir = RtlImageDirectoryEntryToData( module, TRUE, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &e.exception_dir_size ); + if (!e.exception_dir) return; + e.size_of_image = nt->OptionalHeader.SizeOfImage; + e.dllbase = module; + + RtlEnterCriticalSection( &dynamic_unwind_section ); + + TRACE("count %ld, max_count %ld.\n", exception_dir_table.count, exception_dir_table.max_count); + if (exception_dir_table.count == MAX_MODULE_EXCEPTION_DIR_ENTRIES) goto done; + + NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &old_prot ); + /* First entry is reserved for ntdll regardless of base address order. */ + if (exception_dir_table.count <= 2) + { + MEMORY_BASIC_INFORMATION mbi; + + NtQueryVirtualMemory( GetCurrentProcess(), LdrInitializeThunk, MemoryBasicInformation, &mbi, sizeof(mbi), NULL ); + if (module == mbi.AllocationBase) + { + exception_dir_table.entries[0] = e; + goto done; + } + } + for (i = 1; i < exception_dir_table.count; ++i) + if ((ULONG_PTR)module < (ULONG_PTR)exception_dir_table.entries[i].dllbase) break; + memmove( &exception_dir_table.entries[i + 1], &exception_dir_table.entries[i], + sizeof(*exception_dir_table.entries) * (exception_dir_table.count - i) ); + exception_dir_table.entries[i] = e; + ++exception_dir_table.count; +done: + RtlLeaveCriticalSection( &dynamic_unwind_section ); +} + +void unregister_module_exception_directory( void *module ) +{ + SIZE_T size = sizeof(exception_dir_table); + void *addr = &exception_dir_table; + ULONG old_prot; + unsigned int i; + + RtlEnterCriticalSection( &dynamic_unwind_section ); + for (i = 1; i < exception_dir_table.count; ++i) + { + if (module == exception_dir_table.entries[i].dllbase) + { + NtProtectVirtualMemory( NtCurrentProcess(), &addr, &size, PAGE_READWRITE, &old_prot ); + memmove( &exception_dir_table.entries[i], &exception_dir_table.entries[i + 1], + sizeof(*exception_dir_table.entries) * (exception_dir_table.count - i - 1) ); + --exception_dir_table.count; + break; + } + } + RtlLeaveCriticalSection( &dynamic_unwind_section ); +} + static ULONG_PTR get_runtime_function_end( RUNTIME_FUNCTION *func, ULONG_PTR addr ) { #ifdef __x86_64__ @@ -637,6 +725,16 @@ PRUNTIME_FUNCTION WINAPI RtlLookupFunctionEntry( ULONG_PTR pc, ULONG_PTR *base, return func; } +#else + +void register_module_exception_directory( void *module ) +{ +} + +void unregister_module_exception_directory( void *module ) +{ +} + #endif /* __x86_64__ || __arm__ || __aarch64__ */ diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index c67f6e7adc6..105a7420dcc 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -1677,6 +1677,7 @@ static WINE_MODREF *alloc_module( HMODULE hModule, const UNICODE_STRING *nt_name &wm->ldr.HashLinks); if (rtl_rb_tree_put( &base_address_index_tree, wm->ldr.DllBase, &wm->ldr.BaseAddressIndexNode, base_address_compare )) ERR( "rtl_rb_tree_put failed.\n" ); + register_module_exception_directory( hModule ); /* wait until init is called for inserting into InInitializationOrderModuleList */ wm->ldr.InInitializationOrderLinks.Flink = NULL; @@ -2441,6 +2442,7 @@ static NTSTATUS build_module( LPCWSTR load_path, const UNICODE_STRING *nt_name, RemoveEntryList(&wm->ldr.InMemoryOrderLinks); RemoveEntryList(&wm->ldr.HashLinks); RtlRbRemoveNode( &base_address_index_tree, &wm->ldr.BaseAddressIndexNode ); + unregister_module_exception_directory( wm->ldr.DllBase ); /* FIXME: there are several more dangling references * left. Including dlls loaded by this dll before the @@ -4212,6 +4214,7 @@ static void free_modref( WINE_MODREF *wm ) RtlRbRemoveNode( &base_address_index_tree, &wm->ldr.BaseAddressIndexNode ); if (wm->ldr.InInitializationOrderLinks.Flink) RemoveEntryList(&wm->ldr.InInitializationOrderLinks); + unregister_module_exception_directory( wm->ldr.DllBase ); while ((entry = wm->ldr.DdagNode->Dependencies.Tail)) { diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index af4d8b57348..ed7892c4259 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -54,6 +54,8 @@ extern BOOL heap_zero_hack; extern LONG call_vectored_handlers( EXCEPTION_RECORD *rec, CONTEXT *context ); extern void DECLSPEC_NORETURN raise_status( NTSTATUS status, EXCEPTION_RECORD *rec ); extern LONG WINAPI call_unhandled_exception_filter( PEXCEPTION_POINTERS eptr ); +extern void register_module_exception_directory( void *module ); +extern void unregister_module_exception_directory( void *module ); extern void WINAPI LdrInitializeThunk(CONTEXT*,ULONG_PTR,ULONG_PTR,ULONG_PTR); extern NTSTATUS WINAPI KiUserExceptionDispatcher(EXCEPTION_RECORD*,CONTEXT*); diff --git a/include/winnt.h b/include/winnt.h index 228cdff8af9..abf02d75e33 100644 --- a/include/winnt.h +++ b/include/winnt.h @@ -197,6 +197,16 @@ extern "C" { # define __has_attribute(x) 0 #endif +#ifndef DECLSPEC_ALLOCATE +# if __has_attribute(allocate) && !defined(MIDL_PASS) +# define DECLSPEC_ALLOCATE(x) __declspec(allocate(x)) +# elif defined(__GNUC__) +# define DECLSPEC_ALLOCATE(x) __attribute__((section(x))) +# else +# define DECLSPEC_ALLOCATE(x) +# endif +#endif + #if ((defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)))) || __has_attribute(ms_hook_prologue)) && (defined(__i386__) || defined(__x86_64__)) #define DECLSPEC_HOTPATCH __attribute__((__ms_hook_prologue__)) #else From f612d6f694efffc0ddb74a2311c39207adde0c5e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 24 Sep 2024 17:48:00 -0600 Subject: [PATCH 34/67] HACK: wine.inf: Add native,builting override for TDUSC / xinput1_3. CW-Bug-Id: #24258 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 855351ce2de..dc7232cf4e7 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2887,6 +2887,7 @@ HKCU,Software\Wine\AppDefaults\rayne1.exe\DllOverrides,"d3d8",,"native" HKCU,Software\Wine\AppDefaults\rayne2.exe\DllOverrides,"d3d8",,"native" HKCU,Software\Wine\AppDefaults\RDR2.exe\DllOverrides,"vulkan-1",,"native" HKCU,Software\Wine\AppDefaults\ShadowOfWar.exe\DllOverrides,"amd_ags_x64",,"disabled" +HKCU,Software\Wine\AppDefaults\TDUSC.exe\DllOverrides,"xinput1_3",,"native,builtin" ;;App-specific overrides for atiadlxx.dll. HKCU,Software\Wine\AppDefaults\s2_sp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\s2_mp64_ship.exe\DllOverrides,"atiadlxx",,"builtin" From ae11e82f92d6d2c9b730dcd6be06e819382dde13 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 25 Sep 2024 11:38:22 -0600 Subject: [PATCH 35/67] fixup! winevulkan: Support waiting for and signalling d3d12 shared fences. Spotted by Nikolay Sivov. --- dlls/winevulkan/vulkan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e5542156a21..ba6a09aaafa 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -667,7 +667,7 @@ static VkResult wine_vk_device_convert_create_info(struct wine_phys_dev *phys_de */ static void wine_vk_device_free(struct wine_device *device) { - struct pending_d3d12_fence_op *op; + struct pending_d3d12_fence_op *op, *op_cursor; struct wine_queue *queue; if (!device) @@ -687,7 +687,7 @@ static void wine_vk_device_free(struct wine_device *device) } pthread_mutex_destroy(&device->signaller_mutex); - LIST_FOR_EACH_ENTRY(op, &device->free_fence_ops_list, struct pending_d3d12_fence_op, entry) + LIST_FOR_EACH_ENTRY_SAFE(op, op_cursor, &device->free_fence_ops_list, struct pending_d3d12_fence_op, entry) { device->funcs.p_vkDestroySemaphore(device->host_device, op->local_sem.sem, NULL); free(op); From d444e90c6e1974e3119911c1aebc7bcbaa7d5539 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Wed, 25 Sep 2024 13:14:43 -0600 Subject: [PATCH 36/67] fixup! winex11.drv: Set X window property to indicate if the presentation may flip. CW-Bug-Id: #24273 --- dlls/winex11.drv/window.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c index 1ad38ae76b3..b8075aeb5a9 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1935,11 +1935,11 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window, B if (!data->whole_window) return; XSelectInput( data->display, client_window, 0 ); - XChangeProperty( data->display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, - PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); XFlush( data->display ); /* make sure XSelectInput is disabled for client_window after this point */ XDeleteContext( data->display, client_window, winContext ); + XChangeProperty( gdi_display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); if (reparent) XReparentWindow( gdi_display, client_window, get_dummy_parent(), 0, 0 ); TRACE( "%p/%lx detached client window %lx\n", data->hwnd, data->whole_window, client_window ); } @@ -1960,10 +1960,10 @@ void attach_client_window( struct x11drv_win_data *data, Window client_window ) XSaveContext( data->display, client_window, winContext, (char *)data->hwnd ); XSelectInput( data->display, client_window, ExposureMask ); - XChangeProperty( data->display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, - PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); XFlush( data->display ); /* make sure XSelectInput is enabled for client_window after this point */ + XChangeProperty( gdi_display, client_window, x11drv_atom(_WINE_ALLOW_FLIP), XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&allow_flip, sizeof(allow_flip) / 4 ); XReparentWindow( gdi_display, client_window, data->whole_window, data->client_rect.left - data->whole_rect.left, data->client_rect.top - data->whole_rect.top ); From ae0bc6d39ba48e10b77a91975d25a934729c4d71 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 8 Jul 2024 13:51:34 +0200 Subject: [PATCH 37/67] winhttp/tests: Add some tests for querying string options with NULL buffer. Signed-off-by: Nikolay Sivov (cherry picked from commit 6ea59e9d2e85cd690cc01c0564a9099131efb400) --- dlls/winhttp/tests/winhttp.c | 104 +++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 5a29cd0a656..96334a4d5d1 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -4358,12 +4358,28 @@ static void test_credentials(void) ok(!buffer[0], "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(!size, "expected 0, got %lu\n", size); + size = 4; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == 2, "Unexpected size %lu\n", size); + size = ARRAY_SIZE(buffer); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); ok(ret, "failed to query proxy password %lu\n", GetLastError()); ok(!buffer[0], "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(!size, "expected 0, got %lu\n", size); + size = 4; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == 2, "Unexpected size %lu\n", size); + ret = WinHttpSetOption(req, WINHTTP_OPTION_PROXY_USERNAME, proxy_userW, lstrlenW(proxy_userW)); ok(ret, "failed to set username %lu\n", GetLastError()); @@ -4373,18 +4389,52 @@ static void test_credentials(void) ok(!wcscmp(buffer, proxy_userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(proxy_userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, &buffer, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); + size = ARRAY_SIZE(buffer); ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); ok(ret, "failed to query username %lu\n", GetLastError()); ok(!buffer[0], "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(!size, "expected 0, got %lu\n", size); + size = 4; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == 2, "Unexpected size %lu\n", size); + size = ARRAY_SIZE(buffer); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); ok(ret, "failed to query password %lu\n", GetLastError()); ok(!buffer[0], "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(!size, "expected 0, got %lu\n", size); + size = 4; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == 2, "Unexpected size %lu\n", size); + ret = WinHttpSetOption(req, WINHTTP_OPTION_PROXY_PASSWORD, proxy_passW, lstrlenW(proxy_passW)); ok(ret, "failed to set proxy password %lu\n", GetLastError()); @@ -4394,6 +4444,24 @@ static void test_credentials(void) ok(!wcscmp(buffer, proxy_passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(proxy_passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); + ret = WinHttpSetOption(req, WINHTTP_OPTION_USERNAME, userW, lstrlenW(userW)); ok(ret, "failed to set username %lu\n", GetLastError()); @@ -4403,6 +4471,24 @@ static void test_credentials(void) ok(!wcscmp(buffer, userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == (lstrlenW(userW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == (lstrlenW(userW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); + ret = WinHttpSetOption(req, WINHTTP_OPTION_PASSWORD, passW, lstrlenW(passW)); ok(ret, "failed to set password %lu\n", GetLastError()); @@ -4412,6 +4498,24 @@ static void test_credentials(void) ok(!wcscmp(buffer, passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, NULL, &size); + todo_wine + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + todo_wine + ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); + WinHttpCloseHandle(req); req = WinHttpOpenRequest(con, NULL, NULL, NULL, NULL, NULL, 0); From fb0369bccfec348b1ac3175d2523d030dfda1097 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Mon, 8 Jul 2024 23:38:27 +0200 Subject: [PATCH 38/67] winhttp: Fix error handling when returning string options. Signed-off-by: Nikolay Sivov (cherry picked from commit 89325c2a4a95abca1c08f01dccae27c87284c372) --- dlls/winhttp/session.c | 32 ++++++++++++++++++-------------- dlls/winhttp/tests/winhttp.c | 28 ---------------------------- 2 files changed, 18 insertions(+), 42 deletions(-) diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 978bf28bc37..48edddf1d3b 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -681,16 +681,24 @@ static void request_destroy( struct object_header *hdr ) free( request ); } -static void str_to_buffer( WCHAR *buffer, const WCHAR *str, LPDWORD buflen ) +static BOOL return_string_option( WCHAR *buffer, const WCHAR *str, LPDWORD buflen ) { - int len = 0; - if (str) len = lstrlenW( str ); + int len = sizeof(WCHAR); + if (str) len += lstrlenW( str ) * sizeof(WCHAR); if (buffer && *buflen > len) { - if (str) memcpy( buffer, str, len * sizeof(WCHAR) ); - buffer[len] = 0; + if (str) memcpy( buffer, str, len ); + len -= sizeof(WCHAR); + buffer[len / sizeof(WCHAR)] = 0; + *buflen = len; + return TRUE; + } + else + { + *buflen = len; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; } - *buflen = len * sizeof(WCHAR); } static WCHAR *blob_to_str( DWORD encoding, CERT_NAME_BLOB *blob ) @@ -868,20 +876,16 @@ static BOOL request_query_option( struct object_header *hdr, DWORD option, void return TRUE; case WINHTTP_OPTION_USERNAME: - str_to_buffer( buffer, request->connect->username, buflen ); - return TRUE; + return return_string_option( buffer, request->connect->username, buflen ); case WINHTTP_OPTION_PASSWORD: - str_to_buffer( buffer, request->connect->password, buflen ); - return TRUE; + return return_string_option( buffer, request->connect->password, buflen ); case WINHTTP_OPTION_PROXY_USERNAME: - str_to_buffer( buffer, request->connect->session->proxy_username, buflen ); - return TRUE; + return return_string_option( buffer, request->connect->session->proxy_username, buflen ); case WINHTTP_OPTION_PROXY_PASSWORD: - str_to_buffer( buffer, request->connect->session->proxy_password, buflen ); - return TRUE; + return return_string_option( buffer, request->connect->session->proxy_password, buflen ); case WINHTTP_OPTION_MAX_HTTP_AUTOMATIC_REDIRECTS: if (!validate_buffer( buffer, buflen, sizeof(DWORD) )) return FALSE; diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 96334a4d5d1..838c0275ed3 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -4361,9 +4361,7 @@ static void test_credentials(void) size = 4; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == 2, "Unexpected size %lu\n", size); size = ARRAY_SIZE(buffer); @@ -4375,9 +4373,7 @@ static void test_credentials(void) size = 4; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == 2, "Unexpected size %lu\n", size); ret = WinHttpSetOption(req, WINHTTP_OPTION_PROXY_USERNAME, proxy_userW, lstrlenW(proxy_userW)); @@ -4392,19 +4388,14 @@ static void test_credentials(void) buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, &buffer, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); size = 0; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); size = ARRAY_SIZE(buffer); @@ -4416,9 +4407,7 @@ static void test_credentials(void) size = 4; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == 2, "Unexpected size %lu\n", size); size = ARRAY_SIZE(buffer); @@ -4430,9 +4419,7 @@ static void test_credentials(void) size = 4; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == 2, "Unexpected size %lu\n", size); ret = WinHttpSetOption(req, WINHTTP_OPTION_PROXY_PASSWORD, proxy_passW, lstrlenW(proxy_passW)); @@ -4447,19 +4434,14 @@ static void test_credentials(void) buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); size = 0; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); ret = WinHttpSetOption(req, WINHTTP_OPTION_USERNAME, userW, lstrlenW(userW)); @@ -4474,19 +4456,14 @@ static void test_credentials(void) buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == (lstrlenW(userW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); size = 0; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == (lstrlenW(userW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); ret = WinHttpSetOption(req, WINHTTP_OPTION_PASSWORD, passW, lstrlenW(passW)); @@ -4501,19 +4478,14 @@ static void test_credentials(void) buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); size = 0; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, NULL, &size); - todo_wine ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); - todo_wine ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); WinHttpCloseHandle(req); From 8fc24d2f971b39d57d0331cf376917e603348236 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Tue, 9 Jul 2024 11:54:14 +0200 Subject: [PATCH 39/67] winhttp: Implement WinHttpQueryOption(WINHTTP_OPTION_URL). (cherry picked from commit 74ce6fc4ab3792b7867c2daad9f9c9f5902601aa) --- dlls/winhttp/session.c | 38 ++++++++++++++++++++++++++++++++++++ dlls/winhttp/tests/winhttp.c | 38 ++++++++++++++++++++++++++++++++++-- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 48edddf1d3b..0e75145d17a 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -739,6 +739,33 @@ static BOOL copy_sockaddr( const struct sockaddr *addr, SOCKADDR_STORAGE *addr_s } } +static WCHAR *build_url( struct request *request ) +{ + URL_COMPONENTS uc; + DWORD len = 0; + WCHAR *ret; + + memset( &uc, 0, sizeof(uc) ); + uc.dwStructSize = sizeof(uc); + uc.nScheme = (request->hdr.flags & WINHTTP_FLAG_SECURE) ? INTERNET_SCHEME_HTTPS : INTERNET_SCHEME_HTTP; + uc.lpszHostName = request->connect->hostname; + uc.dwHostNameLength = wcslen( uc.lpszHostName ); + uc.nPort = request->connect->hostport; + uc.lpszUserName = request->connect->username; + uc.dwUserNameLength = request->connect->username ? wcslen( request->connect->username ) : 0; + uc.lpszPassword = request->connect->password; + uc.dwPasswordLength = request->connect->password ? wcslen( request->connect->password ) : 0; + uc.lpszUrlPath = request->path; + uc.dwUrlPathLength = wcslen( uc.lpszUrlPath ); + + WinHttpCreateUrl( &uc, 0, NULL, &len ); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || !(ret = malloc( len * sizeof(WCHAR) ))) return NULL; + + if (WinHttpCreateUrl( &uc, 0, ret, &len )) return ret; + free( ret ); + return NULL; +} + static BOOL request_query_option( struct object_header *hdr, DWORD option, void *buffer, DWORD *buflen ) { struct request *request = (struct request *)hdr; @@ -916,6 +943,17 @@ static BOOL request_query_option( struct object_header *hdr, DWORD option, void *buflen = sizeof(DWORD); return TRUE; + case WINHTTP_OPTION_URL: + { + WCHAR *url; + BOOL ret; + + if (!(url = build_url( request ))) return FALSE; + ret = return_string_option( buffer, url, buflen ); + free( url ); + return ret; + } + default: FIXME( "unimplemented option %lu\n", option ); SetLastError( ERROR_INVALID_PARAMETER ); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 838c0275ed3..9bf32bec7ce 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3293,6 +3293,7 @@ static void test_redirect(int port) HINTERNET ses, con, req; char buf[128]; DWORD size, len, count, status; + WCHAR url[128], expected[128]; BOOL ret; ses = WinHttpOpen(L"winetest", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0); @@ -3304,12 +3305,32 @@ static void test_redirect(int port) req = WinHttpOpenRequest(con, L"POST", L"/redirect", NULL, NULL, NULL, 0); ok(req != NULL, "failed to open a request %lu\n", GetLastError()); + url[0] = 0; + size = sizeof(url); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_URL, url, &size); + ok(ret, "got %lu\n", GetLastError()); + swprintf(expected, ARRAY_SIZE(expected), L"http://localhost:%u/redirect", port); + ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); + ret = WinHttpSendRequest(req, NULL, 0, (void *)"data", sizeof("data"), sizeof("data"), 0); ok(ret, "failed to send request %lu\n", GetLastError()); + url[0] = 0; + size = sizeof(url); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_URL, url, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); + ret = WinHttpReceiveResponse(req, NULL); ok(ret, "failed to receive response %lu\n", GetLastError()); + url[0] = 0; + size = sizeof(url); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_URL, url, &size); + ok(ret, "got %lu\n", GetLastError()); + swprintf(expected, ARRAY_SIZE(expected), L"http://localhost:%u/temporary", port); + ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); + status = 0xdeadbeef; size = sizeof(status); ret = WinHttpQueryHeaders(req, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, @@ -5608,7 +5629,8 @@ static void test_chunked_read(void) static void test_max_http_automatic_redirects (void) { HINTERNET session, request, connection; - DWORD max_redirects, err; + DWORD max_redirects, err, size; + WCHAR url[128]; BOOL ret; session = WinHttpOpen(L"winetest", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, @@ -5642,11 +5664,23 @@ static void test_max_http_automatic_redirects (void) } ok(ret == TRUE, "WinHttpSendRequest failed: %lu\n", GetLastError()); + url[0] = 0; + size = sizeof(url); + ret = WinHttpQueryOption(request, WINHTTP_OPTION_URL, url, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(!wcscmp(url, L"http://test.winehq.org/tests/redirecttest.php?max=3"), "got %s\n", wine_dbgstr_w(url)); + SetLastError(0xdeadbeef); ret = WinHttpReceiveResponse(request, NULL); ok(!ret, "WinHttpReceiveResponse succeeded, expected failure\n"); ok(GetLastError() == ERROR_WINHTTP_REDIRECT_FAILED, "Expected ERROR_WINHTTP_REDIRECT_FAILED, got %lu\n", GetLastError()); + url[0] = 0; + size = sizeof(url); + ret = WinHttpQueryOption(request, WINHTTP_OPTION_URL, url, &size); + ok(ret, "got %lu\n", GetLastError()); + ok(!wcscmp(url, L"http://test.winehq.org/tests/redirecttest.php?id=2&max=3"), "got %s\n", wine_dbgstr_w(url)); + done: ret = WinHttpCloseHandle(request); ok(ret == TRUE, "WinHttpCloseHandle failed on closing request, got %d.\n", ret); @@ -6056,9 +6090,9 @@ START_TEST (winhttp) test_WinHttpGetProxyForUrl(); test_chunked_read(); test_max_http_automatic_redirects(); + si.event = CreateEventW(NULL, 0, 0, NULL); si.port = 7532; - thread = CreateThread(NULL, 0, server_thread, &si, 0, NULL); ok(thread != NULL, "failed to create thread %lu\n", GetLastError()); From b6eff700e8bc199848850e8105c8dbd600b261bd Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 16 Jul 2024 12:54:24 +0200 Subject: [PATCH 40/67] winhttp/tests: Add some more tests for string options in WinHttpQueryOption(). Signed-off-by: Nikolay Sivov (cherry picked from commit 4a2265742a07609f1d53bccfd46d60cc6f09b07f) --- dlls/winhttp/tests/winhttp.c | 53 ++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 9bf32bec7ce..34b3a3d1bb4 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3321,6 +3321,15 @@ static void test_redirect(int port) ok(ret, "got %lu\n", GetLastError()); ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); + /* Exact buffer size match. */ + url[0] = 0; + size = (lstrlenW(expected) + 1) * sizeof(WCHAR); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_URL, url, &size); + todo_wine + ok(ret, "got %lu\n", GetLastError()); + todo_wine + ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); + ret = WinHttpReceiveResponse(req, NULL); ok(ret, "failed to receive response %lu\n", GetLastError()); @@ -4406,6 +4415,17 @@ static void test_credentials(void) ok(!wcscmp(buffer, proxy_userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(proxy_userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + /* Exact buffer size match. */ + size = (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR); + buffer[0] = 0; + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, &buffer, &size); + todo_wine + ok(ret, "failed to query proxy username %lu\n", GetLastError()); + todo_wine + ok(!wcscmp(buffer, proxy_userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == lstrlenW(proxy_userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, &buffer, &size); @@ -4452,6 +4472,17 @@ static void test_credentials(void) ok(!wcscmp(buffer, proxy_passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(proxy_passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + /* Exact buffer size match. */ + size = (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR); + buffer[0] = 0; + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); + todo_wine + ok(ret, "failed to query proxy password %lu\n", GetLastError()); + todo_wine + ok(!wcscmp(buffer, proxy_passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == lstrlenW(proxy_passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); @@ -4474,6 +4505,17 @@ static void test_credentials(void) ok(!wcscmp(buffer, userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + /* Exact buffer size match. */ + size = (lstrlenW(userW) + 1) * sizeof(WCHAR); + buffer[0] = 0; + ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); + todo_wine + ok(ret, "failed to query username %lu\n", GetLastError()); + todo_wine + ok(!wcscmp(buffer, userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == lstrlenW(userW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); @@ -4496,6 +4538,17 @@ static void test_credentials(void) ok(!wcscmp(buffer, passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); ok(size == lstrlenW(passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + /* Exact buffer size match. */ + buffer[0] = 0; + size = (lstrlenW(passW) + 1) * sizeof(WCHAR); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); + todo_wine + ok(ret, "failed to query password %lu\n", GetLastError()); + todo_wine + ok(!wcscmp(buffer, passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); + todo_wine + ok(size == lstrlenW(passW) * sizeof(WCHAR), "unexpected result %lu\n", size); + buffer[0] = 0x1; SetLastError(0xdeadbeef); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); From a75392678be754afbb268ae8d7640bdfce94e3c7 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Tue, 16 Jul 2024 13:07:28 +0200 Subject: [PATCH 41/67] winhttp: Handle exact buffer length match in WinHttpQueryOption(). Signed-off-by: Nikolay Sivov (cherry picked from commit 9e9b59908ef9a06c7aeecd4298761b1b45446c04) --- dlls/winhttp/session.c | 2 +- dlls/winhttp/tests/winhttp.c | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/dlls/winhttp/session.c b/dlls/winhttp/session.c index 0e75145d17a..a01f5df26d3 100644 --- a/dlls/winhttp/session.c +++ b/dlls/winhttp/session.c @@ -685,7 +685,7 @@ static BOOL return_string_option( WCHAR *buffer, const WCHAR *str, LPDWORD bufle { int len = sizeof(WCHAR); if (str) len += lstrlenW( str ) * sizeof(WCHAR); - if (buffer && *buflen > len) + if (buffer && *buflen >= len) { if (str) memcpy( buffer, str, len ); len -= sizeof(WCHAR); diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 34b3a3d1bb4..81bfe68e934 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -3325,9 +3325,7 @@ static void test_redirect(int port) url[0] = 0; size = (lstrlenW(expected) + 1) * sizeof(WCHAR); ret = WinHttpQueryOption(req, WINHTTP_OPTION_URL, url, &size); - todo_wine ok(ret, "got %lu\n", GetLastError()); - todo_wine ok(!wcscmp(url, expected), "expected %s got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(url)); ret = WinHttpReceiveResponse(req, NULL); @@ -4419,11 +4417,8 @@ static void test_credentials(void) size = (lstrlenW(proxy_userW) + 1) * sizeof(WCHAR); buffer[0] = 0; ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_USERNAME, &buffer, &size); - todo_wine ok(ret, "failed to query proxy username %lu\n", GetLastError()); - todo_wine ok(!wcscmp(buffer, proxy_userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == lstrlenW(proxy_userW) * sizeof(WCHAR), "unexpected result %lu\n", size); buffer[0] = 0x1; @@ -4476,11 +4471,8 @@ static void test_credentials(void) size = (lstrlenW(proxy_passW) + 1) * sizeof(WCHAR); buffer[0] = 0; ret = WinHttpQueryOption(req, WINHTTP_OPTION_PROXY_PASSWORD, &buffer, &size); - todo_wine ok(ret, "failed to query proxy password %lu\n", GetLastError()); - todo_wine ok(!wcscmp(buffer, proxy_passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == lstrlenW(proxy_passW) * sizeof(WCHAR), "unexpected result %lu\n", size); buffer[0] = 0x1; @@ -4509,11 +4501,8 @@ static void test_credentials(void) size = (lstrlenW(userW) + 1) * sizeof(WCHAR); buffer[0] = 0; ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, &buffer, &size); - todo_wine ok(ret, "failed to query username %lu\n", GetLastError()); - todo_wine ok(!wcscmp(buffer, userW), "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == lstrlenW(userW) * sizeof(WCHAR), "unexpected result %lu\n", size); buffer[0] = 0x1; @@ -4542,11 +4531,8 @@ static void test_credentials(void) buffer[0] = 0; size = (lstrlenW(passW) + 1) * sizeof(WCHAR); ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, &buffer, &size); - todo_wine ok(ret, "failed to query password %lu\n", GetLastError()); - todo_wine ok(!wcscmp(buffer, passW), "unexpected result %s\n", wine_dbgstr_w(buffer)); - todo_wine ok(size == lstrlenW(passW) * sizeof(WCHAR), "unexpected result %lu\n", size); buffer[0] = 0x1; From 6dfd3c992578916d4d2fe5fe947c529dc86381e0 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Fri, 30 Aug 2024 23:14:12 +0200 Subject: [PATCH 42/67] winevulkan: Update to VK spec version 1.3.295. I have been putting this off for too long because handling VK_KHR_maintenance7 is not trivial. But by now a lot of other extensions have been released, so we should at least support those. (cherry picked from commit 9ecf19bc72106d97338222f52ea00c6bb32f8a87) --- dlls/winevulkan/make_vulkan | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 4194df3a3dd..c91b768cd9c 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.285" +VK_XML_VERSION = "1.3.295" WINE_VK_VERSION = (1, 3) # Filenames to create. @@ -100,6 +100,7 @@ UNSUPPORTED_EXTENSIONS = [ "VK_GOOGLE_display_timing", "VK_KHR_external_fence_win32", # Relates to external_semaphore and needs type conversions in bitflags. + "VK_KHR_maintenance7", # Causes infinity recursion in struct convert code "VK_KHR_shared_presentable_image", # Needs WSI work. "VK_KHR_video_encode_queue", "VK_KHR_video_queue", # TODO Video extensions use separate headers + xml From 38e2c276bcba6cdc647258b2789653cd90e6f4d8 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Thu, 26 Sep 2024 22:10:32 +0200 Subject: [PATCH 43/67] winevulkan: Avoid empty struct extension conversions. (cherry picked from commit 9ad4e02ffcc3e426fed6f7dcfd4cbe8d11dc4736) --- dlls/winevulkan/make_vulkan | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index c91b768cd9c..2b6c8569712 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -2489,6 +2489,9 @@ class StructConversionFunction(object): needs_extensions = self.operand.needs_extensions_conversion(self.conv, self.direction) + if self.direction == Direction.OUTPUT and not any([any([self.member_needs_copy(ext, m) for m in ext]) for ext in self.operand.struct_extensions]): + needs_extensions = False + body += "{\n" if needs_extensions: if self.direction == Direction.INPUT: From 8b70d565bee0a9446a9db8becfc9e89c8402e574 Mon Sep 17 00:00:00 2001 From: Georg Lehmann Date: Thu, 26 Sep 2024 22:11:51 +0200 Subject: [PATCH 44/67] winevulkan: Update to VK spec version 1.3.296. (cherry picked from commit fb256d8b950b5498bc4b2252de7b0b7a714229f7) --- dlls/winevulkan/make_vulkan | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 2b6c8569712..f5d2a5a9575 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -64,7 +64,7 @@ from enum import Enum LOGGER = logging.Logger("vulkan") LOGGER.addHandler(logging.StreamHandler()) -VK_XML_VERSION = "1.3.295" +VK_XML_VERSION = "1.3.296" WINE_VK_VERSION = (1, 3) # Filenames to create. From 1e44da35cc4cda5db101af2ab511292337f7cedc Mon Sep 17 00:00:00 2001 From: Arkadiusz Hiler Date: Fri, 27 Sep 2024 13:52:52 +0300 Subject: [PATCH 45/67] winevulkan: Update vk.xml to 1.3.296. --- dlls/winevulkan/vk.xml | 1569 +++++++++++++++++++++++++++++++++------- 1 file changed, 1313 insertions(+), 256 deletions(-) diff --git a/dlls/winevulkan/vk.xml b/dlls/winevulkan/vk.xml index aa8c8ee2869..00d97f14c1f 100644 --- a/dlls/winevulkan/vk.xml +++ b/dlls/winevulkan/vk.xml @@ -55,7 +55,7 @@ branch of the member gitlab server. - + @@ -67,7 +67,7 @@ branch of the member gitlab server. - + @@ -175,11 +175,11 @@ branch of the member gitlab server. #define VKSC_API_VERSION_1_0 VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, 0)// Patch version should always be set to 0 // Version of this file -#define VK_HEADER_VERSION 285 +#define VK_HEADER_VERSION 296 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(0, 1, 3, VK_HEADER_VERSION) // Version of this file -#define VK_HEADER_VERSION 14 +#define VK_HEADER_VERSION 15 // Complete version of this file #define VK_HEADER_VERSION_COMPLETE VK_MAKE_API_VERSION(VKSC_API_VARIANT, 1, 0, VK_HEADER_VERSION) @@ -389,6 +389,8 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkBuildMicromapFlagsEXT; typedef VkFlags VkMicromapCreateFlagsEXT; + typedef VkFlags VkIndirectCommandsLayoutUsageFlagsEXT; + typedef VkFlags VkIndirectCommandsInputModeFlagsEXT; typedef VkFlags VkDirectDriverLoadingFlagsLUNARG; typedef VkFlags64 VkPipelineCreateFlags2KHR; typedef VkFlags64 VkBufferUsageFlags2KHR; @@ -502,7 +504,7 @@ typedef void* MTLSharedEvent_id; typedef VkFlags VkVideoDecodeH264PictureLayoutFlagsKHR; Video Encode Core extension - typedef VkFlags VkVideoEncodeFlagsKHR; + typedef VkFlags VkVideoEncodeFlagsKHR; typedef VkFlags VkVideoEncodeUsageFlagsKHR; typedef VkFlags VkVideoEncodeContentFlagsKHR; typedef VkFlags VkVideoEncodeCapabilityFlagsKHR; @@ -550,7 +552,10 @@ typedef void* MTLSharedEvent_id; VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineBinaryKHR) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNV) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutEXT) + VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectExecutionSetEXT) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplate) VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversion) @@ -628,6 +633,7 @@ typedef void* MTLSharedEvent_id; + @@ -780,10 +786,15 @@ typedef void* MTLSharedEvent_id; + + + + + @@ -796,6 +807,8 @@ typedef void* MTLSharedEvent_id; + + WSI extensions @@ -874,6 +887,7 @@ typedef void* MTLSharedEvent_id; + Enumerated types in the header, but not used by the API @@ -1664,7 +1678,55 @@ typedef void* MTLSharedEvent_id; uint32_t offsetStart of the range, in bytes uint32_t sizeSize of the range, in bytes - + + VkStructureType sType + const void* pNext + const VkPipelineBinaryKeysAndDataKHR* pKeysAndDataInfo + VkPipeline pipeline + const VkPipelineCreateInfoKHR* pPipelineCreateInfo + + + VkStructureType sType + const void* pNext + uint32_t pipelineBinaryCount + VkPipelineBinaryKHR* pPipelineBinaries + + + size_t dataSize + void* pData + + + uint32_t binaryCount + const VkPipelineBinaryKeyKHR* pPipelineBinaryKeys + const VkPipelineBinaryDataKHR* pPipelineBinaryData + + + VkStructureType sType + void* pNext + uint32_t keySize + uint8_t key[VK_MAX_PIPELINE_BINARY_KEY_SIZE_KHR] + + + VkStructureType sType + const void* pNext + uint32_t binaryCount + const VkPipelineBinaryKHR* pPipelineBinaries + + + VkStructureType sType + void* pNext + VkPipeline pipeline + + + VkStructureType sType + void* pNext + VkPipelineBinaryKHR pipelineBinary + + + VkStructureType sType + void* pNext + + VkStructureType sType const void* pNext VkPipelineLayoutCreateFlags flags @@ -1731,7 +1793,7 @@ typedef void* MTLSharedEvent_id; uint32_t clearValueCount const VkClearValue* pClearValues - + float float32[4] int32_t int32[4] uint32_t uint32[4] @@ -1835,7 +1897,7 @@ typedef void* MTLSharedEvent_id; VkBool32 vertexPipelineStoresAndAtomicsstores and atomic ops on storage buffers and images are supported in vertex, tessellation, and geometry stages VkBool32 fragmentStoresAndAtomicsstores and atomic ops on storage buffers and images are supported in the fragment stage VkBool32 shaderTessellationAndGeometryPointSizetessellation and geometry stages can export point size - VkBool32 shaderImageGatherExtendedimage gather with run-time values and independent offsets + VkBool32 shaderImageGatherExtendedimage gather with runtime values and independent offsets VkBool32 shaderStorageImageExtendedFormatsthe extended set of formats can be used for storage images VkBool32 shaderStorageImageMultisamplemultisample images can be used for storage images VkBool32 shaderStorageImageReadWithoutFormatread from storage image does not require format qualifier @@ -1875,7 +1937,7 @@ typedef void* MTLSharedEvent_id; uint32_t maxImageDimension1Dmax 1D image dimension uint32_t maxImageDimension2Dmax 2D image dimension uint32_t maxImageDimension3Dmax 3D image dimension - uint32_t maxImageDimensionCubemax cubemap image dimension + uint32_t maxImageDimensionCubemax cube map image dimension uint32_t maxImageArrayLayersmax layers for image arrays uint32_t maxTexelBufferElementsmax texel buffer size (fstexels) uint32_t maxUniformBufferRangemax uniform buffer range (bytes) @@ -2097,7 +2159,7 @@ typedef void* MTLSharedEvent_id; uint32_t planeStackIndexThe z-order of the plane. VkSurfaceTransformFlagBitsKHR transformTransform to apply to the images as part of the scanout operation float globalAlphaGlobal alpha value. Must be between 0 and 1, inclusive. Ignored if alphaMode is not VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR - VkDisplayPlaneAlphaFlagBitsKHR alphaModeWhat type of alpha blending to use. Must be a bit from vkGetDisplayPlanePropertiesKHR::supportedAlpha. + VkDisplayPlaneAlphaFlagBitsKHR alphaModeThe type of alpha blending to use. Must be one of the bits from VkDisplayPlaneCapabilitiesKHR::supportedAlpha for this display plane VkExtent2D imageExtentsize of the images to use with this surface @@ -2225,7 +2287,7 @@ typedef void* MTLSharedEvent_id; const void* pNext VkDebugReportFlagsEXT flagsIndicates which events call this callback PFN_vkDebugReportCallbackEXT pfnCallbackFunction pointer of a callback function - void* pUserDataUser data provided to callback function + void* pUserDataData provided to callback function VkStructureType sTypeMust be VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT @@ -2233,7 +2295,7 @@ typedef void* MTLSharedEvent_id; uint32_t disabledValidationCheckCountNumber of validation checks to disable const VkValidationCheckEXT* pDisabledValidationChecksValidation checks to disable - + VkStructureType sTypeMust be VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT const void* pNext uint32_t enabledValidationFeatureCountNumber of validation features to enable @@ -2252,7 +2314,7 @@ typedef void* MTLSharedEvent_id; const char* pSettingName VkLayerSettingTypeEXT typeThe type of the object uint32_t valueCountNumber of values of the setting - const void* pValuesValues to pass for a setting + const void* pValuesValues to pass for a setting VkStructureType sType @@ -2966,9 +3028,9 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkBool32 multiviewMultiple views in a renderpass - VkBool32 multiviewGeometryShaderMultiple views in a renderpass w/ geometry shader - VkBool32 multiviewTessellationShaderMultiple views in a renderpass w/ tessellation shader + VkBool32 multiviewMultiple views in a render pass + VkBool32 multiviewGeometryShaderMultiple views in a render pass w/ geometry shader + VkBool32 multiviewTessellationShaderMultiple views in a render pass w/ tessellation shader @@ -3734,6 +3796,42 @@ typedef void* MTLSharedEvent_id; uint32_t maxCombinedImageSamplerDescriptorCount VkBool32 fragmentShadingRateClampCombinerInputs + + VkStructureType sType + void* pNext + VkBool32 maintenance7 + + + VkStructureType sType + void* pNext + VkBool32 robustFragmentShadingRateAttachmentAccess + VkBool32 separateDepthStencilAttachmentAccess + uint32_t maxDescriptorSetTotalUniformBuffersDynamic + uint32_t maxDescriptorSetTotalStorageBuffersDynamic + uint32_t maxDescriptorSetTotalBuffersDynamic + uint32_t maxDescriptorSetUpdateAfterBindTotalUniformBuffersDynamic + uint32_t maxDescriptorSetUpdateAfterBindTotalStorageBuffersDynamic + uint32_t maxDescriptorSetUpdateAfterBindTotalBuffersDynamic + + + VkStructureType sType + void* pNext + uint32_t layeredApiCount + VkPhysicalDeviceLayeredApiPropertiesKHR* pLayeredApisOutput list of layered implementations underneath the physical device + + + VkStructureType sType + void* pNext + uint32_t vendorID + uint32_t deviceID + VkPhysicalDeviceLayeredApiKHR layeredAPI + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE] + + + VkStructureType sType + void* pNext + VkPhysicalDeviceProperties2 properties + VkStructureType sType const void* pNext @@ -4408,12 +4506,18 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 cornerSampledImage - - VkStructureType sType + + VkStructureType sType void* pNext VkBool32 computeDerivativeGroupQuads VkBool32 computeDerivativeGroupLinear + + + VkStructureType sType + void* pNext + VkBool32 meshAndTaskShaderDerivatives + VkStructureType sType @@ -4883,7 +4987,7 @@ typedef void* MTLSharedEvent_id; VkBool32 scalarBlockLayout - + VkStructureType sType const void* pNext VkBool32 supportsProtectedRepresents if surface can be protected @@ -5078,7 +5182,7 @@ typedef void* MTLSharedEvent_id; const void* pNext HMONITOR hmonitor - + VkStructureType sType void* pNext VkBool32 fullScreenExclusiveSupported @@ -5088,7 +5192,7 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 presentBarrier - + VkStructureType sType void* pNext VkBool32 presentBarrierSupported @@ -5445,9 +5549,9 @@ typedef void* MTLSharedEvent_id; VkBool32 uniformAndStorageBuffer16BitAccess16-bit integer/floating-point variables supported in BufferBlock and Block VkBool32 storagePushConstant1616-bit integer/floating-point variables supported in PushConstant VkBool32 storageInputOutput1616-bit integer/floating-point variables supported in shader inputs and outputs - VkBool32 multiviewMultiple views in a renderpass - VkBool32 multiviewGeometryShaderMultiple views in a renderpass w/ geometry shader - VkBool32 multiviewTessellationShaderMultiple views in a renderpass w/ tessellation shader + VkBool32 multiviewMultiple views in a render pass + VkBool32 multiviewGeometryShaderMultiple views in a render pass w/ geometry shader + VkBool32 multiviewTessellationShaderMultiple views in a render pass w/ tessellation shader VkBool32 variablePointersStorageBuffer VkBool32 variablePointers VkBool32 protectedMemory @@ -6291,11 +6395,172 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 depthClipControl + + VkStructureType sType + void* pNext + VkBool32 deviceGeneratedCommands + VkBool32 dynamicGeneratedPipelineLayout + + + VkStructureType sType + void* pNext + uint32_t maxIndirectPipelineCount + uint32_t maxIndirectShaderObjectCount + uint32_t maxIndirectSequenceCount + uint32_t maxIndirectCommandsTokenCount + uint32_t maxIndirectCommandsTokenOffset + uint32_t maxIndirectCommandsIndirectStride + VkIndirectCommandsInputModeFlagsEXT supportedIndirectCommandsInputModes + VkShaderStageFlags supportedIndirectCommandsShaderStages + VkShaderStageFlags supportedIndirectCommandsShaderStagesPipelineBinding + VkShaderStageFlags supportedIndirectCommandsShaderStagesShaderBinding + VkBool32 deviceGeneratedCommandsTransformFeedback + VkBool32 deviceGeneratedCommandsMultiDrawIndirectCount + + + VkStructureType sType + void* pNext + VkPipeline pipeline + + + VkStructureType sType + void* pNext + uint32_t shaderCount + const VkShaderEXT* pShaders + + + VkStructureType sType + void* pNext + VkIndirectExecutionSetEXT indirectExecutionSet + VkIndirectCommandsLayoutEXT indirectCommandsLayout + uint32_t maxSequenceCount + uint32_t maxDrawCount + + + VkStructureType sType + const void* pNext + VkPipeline initialPipeline + uint32_t maxPipelineCount + + + VkStructureType sType + const void* pNext + uint32_t setLayoutCount + const VkDescriptorSetLayout* pSetLayouts + + + VkStructureType sType + const void* pNext + uint32_t shaderCount + const VkShaderEXT* pInitialShaders + const VkIndirectExecutionSetShaderLayoutInfoEXT* pSetLayoutInfos + uint32_t maxShaderCount + uint32_t pushConstantRangeCount + const VkPushConstantRange* pPushConstantRanges + + + const VkIndirectExecutionSetPipelineInfoEXT* pPipelineInfo + const VkIndirectExecutionSetShaderInfoEXT* pShaderInfo + + + VkStructureType sType + const void* pNext + VkIndirectExecutionSetInfoTypeEXT type + VkIndirectExecutionSetInfoEXT info + + + VkStructureType sType + const void* pNext + VkShaderStageFlags shaderStages + VkIndirectExecutionSetEXT indirectExecutionSet + VkIndirectCommandsLayoutEXT indirectCommandsLayout + VkDeviceAddress indirectAddress + VkDeviceSize indirectAddressSize + VkDeviceAddress preprocessAddress + VkDeviceSize preprocessSize + uint32_t maxSequenceCount + VkDeviceAddress sequenceCountAddress + uint32_t maxDrawCount + + + VkStructureType sType + const void* pNext + uint32_t index + VkPipeline pipeline + + + VkStructureType sType + const void* pNext + uint32_t index + VkShaderEXT shader + + + VkStructureType sType + const void* pNext + VkIndirectCommandsLayoutUsageFlagsEXT flags + VkShaderStageFlags shaderStages + uint32_t indirectStride + VkPipelineLayout pipelineLayout + uint32_t tokenCount + const VkIndirectCommandsLayoutTokenEXT* pTokens + + + VkStructureType sType + const void* pNext + VkIndirectCommandsTokenTypeEXT type + VkIndirectCommandsTokenDataEXT data + uint32_t offset + + + VkDeviceAddress bufferAddress + uint32_t stride + uint32_t commandCount + + + uint32_t vertexBindingUnit + + + VkDeviceAddress bufferAddress + uint32_t size + uint32_t stride + + + VkIndirectCommandsInputModeFlagBitsEXT mode + + + VkDeviceAddress bufferAddress + uint32_t size + VkIndexType indexType + + + VkPushConstantRange updateRange + + + VkIndirectExecutionSetInfoTypeEXT type + VkShaderStageFlags shaderStages + + + const VkIndirectCommandsPushConstantTokenEXT* pPushConstant + const VkIndirectCommandsVertexBufferTokenEXT* pVertexBuffer + const VkIndirectCommandsIndexBufferTokenEXT* pIndexBuffer + const VkIndirectCommandsExecutionSetTokenEXT* pExecutionSet + VkStructureType sType const void* pNext VkBool32 negativeOneToOne + + VkStructureType sType + void* pNext + VkBool32 depthClampControl + + + VkStructureType sType + const void* pNext + VkDepthClampModeEXT depthClampMode + const VkDepthClampRangeEXT* pDepthClampRange + VkStructureType sType void* pNext @@ -6306,6 +6571,11 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 externalMemoryRDMA + + VkStructureType sType + void* pNext + VkBool32 shaderRelaxedExtendedInstruction + VkStructureType sType void* pNext @@ -6659,12 +6929,12 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkFormat format - VkComponentMapping componentMapping - VkImageCreateFlags imageCreateFlags - VkImageType imageType - VkImageTiling imageTiling - VkImageUsageFlags imageUsageFlags + VkFormat format + VkComponentMapping componentMapping + VkImageCreateFlags imageCreateFlags + VkImageType imageType + VkImageTiling imageTiling + VkImageUsageFlags imageUsageFlags VkStructureType sType @@ -6676,16 +6946,16 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext - VkVideoCapabilityFlagsKHR flags - VkDeviceSize minBitstreamBufferOffsetAlignment - VkDeviceSize minBitstreamBufferSizeAlignment - VkExtent2D pictureAccessGranularity - VkExtent2D minCodedExtent - VkExtent2D maxCodedExtent - uint32_t maxDpbSlots - uint32_t maxActiveReferencePictures - VkExtensionProperties stdHeaderVersion + void* pNext + VkVideoCapabilityFlagsKHR flags + VkDeviceSize minBitstreamBufferOffsetAlignment + VkDeviceSize minBitstreamBufferSizeAlignment + VkExtent2D pictureAccessGranularity + VkExtent2D minCodedExtent + VkExtent2D maxCodedExtent + uint32_t maxDpbSlots + uint32_t maxActiveReferencePictures + VkExtensionProperties stdHeaderVersion VkStructureType sType @@ -6718,7 +6988,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkVideoDecodeCapabilityFlagsKHR flags + VkVideoDecodeCapabilityFlagsKHR flags VkStructureType sType @@ -6753,27 +7023,9 @@ typedef void* MTLSharedEvent_id; #include "vk_video/vulkan_video_codec_h264std.h" - - - - - - - - - - - - - - - - #include "vk_video/vulkan_video_codec_h264std_decode.h" - - VkStructureType sType const void* pNext @@ -6782,9 +7034,9 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext - StdVideoH264LevelIdc maxLevelIdc - VkOffset2D fieldOffsetGranularity + void* pNext + StdVideoH264LevelIdc maxLevelIdc + VkOffset2D fieldOffsetGranularity @@ -6820,25 +7072,10 @@ typedef void* MTLSharedEvent_id; - - - - - - - - - - - - - #include "vk_video/vulkan_video_codec_h265std_decode.h" - - VkStructureType sType const void* pNext @@ -6847,7 +7084,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - StdVideoH265LevelIdc maxLevelIdc + StdVideoH265LevelIdc maxLevelIdc VkStructureType sType @@ -6895,7 +7132,7 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - StdVideoAV1Level maxLevel + StdVideoAV1Level maxLevel VkStructureType sType @@ -7034,30 +7271,30 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkVideoEncodeCapabilityFlagsKHR flags - VkVideoEncodeRateControlModeFlagsKHR rateControlModes - uint32_t maxRateControlLayers - uint64_t maxBitrate - uint32_t maxQualityLevels - VkExtent2D encodeInputPictureGranularity - VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags + VkVideoEncodeCapabilityFlagsKHR flags + VkVideoEncodeRateControlModeFlagsKHR rateControlModes + uint32_t maxRateControlLayers + uint64_t maxBitrate + uint32_t maxQualityLevels + VkExtent2D encodeInputPictureGranularity + VkVideoEncodeFeedbackFlagsKHR supportedEncodeFeedbackFlags VkStructureType sType void* pNext - VkVideoEncodeH264CapabilityFlagsKHR flags - StdVideoH264LevelIdc maxLevelIdc - uint32_t maxSliceCount - uint32_t maxPPictureL0ReferenceCount - uint32_t maxBPictureL0ReferenceCount - uint32_t maxL1ReferenceCount - uint32_t maxTemporalLayerCount - VkBool32 expectDyadicTemporalLayerPattern - int32_t minQp - int32_t maxQp - VkBool32 prefersGopRemainingFrames - VkBool32 requiresGopRemainingFrames - VkVideoEncodeH264StdFlagsKHR stdSyntaxFlags + VkVideoEncodeH264CapabilityFlagsKHR flags + StdVideoH264LevelIdc maxLevelIdc + uint32_t maxSliceCount + uint32_t maxPPictureL0ReferenceCount + uint32_t maxBPictureL0ReferenceCount + uint32_t maxL1ReferenceCount + uint32_t maxTemporalLayerCount + VkBool32 expectDyadicTemporalLayerPattern + int32_t minQp + int32_t maxQp + VkBool32 prefersGopRemainingFrames + VkBool32 requiresGopRemainingFrames + VkVideoEncodeH264StdFlagsKHR stdSyntaxFlags VkStructureType sType @@ -7076,13 +7313,6 @@ typedef void* MTLSharedEvent_id; - - - - - - - VkStructureType sType const void* pNext @@ -7182,22 +7412,22 @@ typedef void* MTLSharedEvent_id; VkStructureType sType void* pNext - VkVideoEncodeH265CapabilityFlagsKHR flags - StdVideoH265LevelIdc maxLevelIdc - uint32_t maxSliceSegmentCount - VkExtent2D maxTiles - VkVideoEncodeH265CtbSizeFlagsKHR ctbSizes - VkVideoEncodeH265TransformBlockSizeFlagsKHR transformBlockSizes - uint32_t maxPPictureL0ReferenceCount - uint32_t maxBPictureL0ReferenceCount - uint32_t maxL1ReferenceCount - uint32_t maxSubLayerCount - VkBool32 expectDyadicTemporalSubLayerPattern - int32_t minQp - int32_t maxQp - VkBool32 prefersGopRemainingFrames - VkBool32 requiresGopRemainingFrames - VkVideoEncodeH265StdFlagsKHR stdSyntaxFlags + VkVideoEncodeH265CapabilityFlagsKHR flags + StdVideoH265LevelIdc maxLevelIdc + uint32_t maxSliceSegmentCount + VkExtent2D maxTiles + VkVideoEncodeH265CtbSizeFlagsKHR ctbSizes + VkVideoEncodeH265TransformBlockSizeFlagsKHR transformBlockSizes + uint32_t maxPPictureL0ReferenceCount + uint32_t maxBPictureL0ReferenceCount + uint32_t maxL1ReferenceCount + uint32_t maxSubLayerCount + VkBool32 expectDyadicTemporalSubLayerPattern + int32_t minQp + int32_t maxQp + VkBool32 prefersGopRemainingFrames + VkBool32 requiresGopRemainingFrames + VkVideoEncodeH265StdFlagsKHR stdSyntaxFlags VkStructureType sType @@ -7212,14 +7442,9 @@ typedef void* MTLSharedEvent_id; uint32_t preferredMaxL1ReferenceCount #include "vk_video/vulkan_video_codec_h265std_encode.h" - - - - - VkStructureType sType const void* pNext @@ -7442,13 +7667,13 @@ typedef void* MTLSharedEvent_id; VkStructureType sType - void* pNext + const void* pNext VkDeviceAddress address VkBufferUsageFlags usage VkStructureType sType - void* pNext + const void* pNext VkBuffer buffer @@ -7892,6 +8117,25 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 graphicsPipelineLibrary + + VkStructureType sType + void* pNext + VkBool32 pipelineBinaries + + + VkStructureType sType + const void* pNext + VkBool32 disableInternalCache + + + VkStructureType sType + void* pNext + VkBool32 pipelineBinaryInternalCache + VkBool32 pipelineBinaryInternalCacheControl + VkBool32 pipelineBinaryPrefersInternalCache + VkBool32 pipelineBinaryPrecompiledInternalCache + VkBool32 pipelineBinaryCompressedData + VkStructureType sType void* pNext @@ -8495,7 +8739,7 @@ typedef void* MTLSharedEvent_id; void* pNext VkPresentModeKHR presentMode - + VkStructureType sType void* pNext VkPresentScalingFlagsEXT supportedPresentScaling @@ -8789,6 +9033,24 @@ typedef void* MTLSharedEvent_id; VkDeviceOrHostAddressConstAMDX infos uint64_t stride + + VkStructureType sType + void* pNext + VkBool32 antiLag + + + VkStructureType sType + const void* pNext + VkAntiLagModeAMD mode + uint32_t maxFPS + const VkAntiLagPresentationInfoAMD* pPresentationInfo + + + VkStructureType sType + void* pNext + VkAntiLagStageAMD stage + uint64_t frameIndex + VkStructureType sType const void* pNext @@ -9123,6 +9385,11 @@ typedef void* MTLSharedEvent_id; void* pNext VkBool32 shaderRawAccessChains + + VkStructureType sType + void* pNext + VkBool32 commandBufferInheritance + VkStructureType sType void* pNext @@ -9138,12 +9405,21 @@ typedef void* MTLSharedEvent_id; const void* pNext uint32_t maximumRequestedAlignment + + VkStructureType sType + void* pNext + VkBool32 shaderReplicatedComposites + + + float minDepthClamp + float maxDepthClamp + Vulkan enumerant (token) definitions - + @@ -9176,6 +9452,7 @@ typedef void* MTLSharedEvent_id; + @@ -9727,6 +10004,15 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + Flags @@ -10326,6 +10612,7 @@ typedef void* MTLSharedEvent_id; Vendor IDs are now represented as enums instead of the old <vendorids> tag, allowing them to be included in the API headers. + @@ -10364,7 +10651,8 @@ typedef void* MTLSharedEvent_id; - + + @@ -11212,6 +11500,30 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + @@ -11253,6 +11565,13 @@ typedef void* MTLSharedEvent_id; + + + + + + + @@ -11277,6 +11596,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -11735,6 +12060,39 @@ typedef void* MTLSharedEvent_id; uint32_t srcCacheCount const VkPipelineCache* pSrcCaches + + VkResult vkCreatePipelineBinariesKHR + VkDevice device + const VkPipelineBinaryCreateInfoKHR* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkPipelineBinaryHandlesInfoKHR* pBinaries + + + void vkDestroyPipelineBinaryKHR + VkDevice device + VkPipelineBinaryKHR pipelineBinary + const VkAllocationCallbacks* pAllocator + + + VkResult vkGetPipelineKeyKHR + VkDevice device + const VkPipelineCreateInfoKHR* pPipelineCreateInfo + VkPipelineBinaryKeyKHR* pPipelineKey + + + VkResult vkGetPipelineBinaryDataKHR + VkDevice device + const VkPipelineBinaryDataInfoKHR* pInfo + VkPipelineBinaryKeyKHR* pPipelineBinaryKey + size_t* pPipelineBinaryDataSize + void* pPipelineBinaryData + + + VkResult vkReleaseCapturedPipelineDataKHR + VkDevice device + const VkReleaseCapturedPipelineDataInfoKHR* pInfo + const VkAllocationCallbacks* pAllocator + VkResult vkCreateGraphicsPipelines VkDevice device @@ -12705,6 +13063,66 @@ typedef void* MTLSharedEvent_id; VkIndirectCommandsLayoutNV indirectCommandsLayout const VkAllocationCallbacks* pAllocator + + + void vkCmdExecuteGeneratedCommandsEXT + VkCommandBuffer commandBuffer + VkBool32 isPreprocessed + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo + + + void vkCmdPreprocessGeneratedCommandsEXT + VkCommandBuffer commandBuffer + const VkGeneratedCommandsInfoEXT* pGeneratedCommandsInfo + VkCommandBuffer stateCommandBuffer + + + void vkGetGeneratedCommandsMemoryRequirementsEXT + VkDevice device + const VkGeneratedCommandsMemoryRequirementsInfoEXT* pInfo + VkMemoryRequirements2* pMemoryRequirements + + + VkResult vkCreateIndirectCommandsLayoutEXT + VkDevice device + const VkIndirectCommandsLayoutCreateInfoEXT* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkIndirectCommandsLayoutEXT* pIndirectCommandsLayout + + + void vkDestroyIndirectCommandsLayoutEXT + VkDevice device + VkIndirectCommandsLayoutEXT indirectCommandsLayout + const VkAllocationCallbacks* pAllocator + + + VkResult vkCreateIndirectExecutionSetEXT + VkDevice device + const VkIndirectExecutionSetCreateInfoEXT* pCreateInfo + const VkAllocationCallbacks* pAllocator + VkIndirectExecutionSetEXT* pIndirectExecutionSet + + + void vkDestroyIndirectExecutionSetEXT + VkDevice device + VkIndirectExecutionSetEXT indirectExecutionSet + const VkAllocationCallbacks* pAllocator + + + void vkUpdateIndirectExecutionSetPipelineEXT + VkDevice device + VkIndirectExecutionSetEXT indirectExecutionSet + uint32_t executionSetWriteCount + const VkWriteIndirectExecutionSetPipelineEXT* pExecutionSetWrites + + + void vkUpdateIndirectExecutionSetShaderEXT + VkDevice device + VkIndirectExecutionSetEXT indirectExecutionSet + uint32_t executionSetWriteCount + const VkWriteIndirectExecutionSetShaderEXT* pExecutionSetWrites + + void vkGetPhysicalDeviceFeatures2 VkPhysicalDevice physicalDevice @@ -12907,35 +13325,35 @@ typedef void* MTLSharedEvent_id; VkDevice device const VkImportFenceFdInfoKHR* pImportFenceFdInfo - + VkResult vkGetFenceSciSyncFenceNV VkDevice device const VkFenceGetSciSyncInfoNV* pGetSciSyncHandleInfo void* pHandle - + VkResult vkGetFenceSciSyncObjNV VkDevice device const VkFenceGetSciSyncInfoNV* pGetSciSyncHandleInfo void* pHandle - + VkResult vkImportFenceSciSyncFenceNV VkDevice device const VkImportFenceSciSyncInfoNV* pImportFenceSciSyncInfo - + VkResult vkImportFenceSciSyncObjNV VkDevice device const VkImportFenceSciSyncInfoNV* pImportFenceSciSyncInfo - + VkResult vkGetSemaphoreSciSyncObjNV VkDevice device const VkSemaphoreGetSciSyncInfoNV* pGetSciSyncInfo void* pHandle - + VkResult vkImportSemaphoreSciSyncObjNV VkDevice device const VkImportSemaphoreSciSyncInfoNV* pImportSemaphoreSciSyncInfo @@ -14218,6 +14636,11 @@ typedef void* MTLSharedEvent_id; VkDevice device const VkPipelineIndirectDeviceAddressInfoNV* pInfo + + void vkAntiLagUpdateAMD + VkDevice device + const VkAntiLagDataAMD* pData + void vkCmdSetCullMode VkCommandBuffer commandBuffer @@ -15398,7 +15821,13 @@ typedef void* MTLSharedEvent_id; void vkCmdSetRenderingInputAttachmentIndicesKHR VkCommandBuffer commandBuffer - const VkRenderingInputAttachmentIndexInfoKHR* pLocationInfo + const VkRenderingInputAttachmentIndexInfoKHR* pInputAttachmentIndexInfo + + + void vkCmdSetDepthClampRangeEXT + VkCommandBuffer commandBuffer + VkDepthClampModeEXT depthClampMode + const VkDepthClampRangeEXT* pDepthClampRange @@ -15524,6 +15953,7 @@ typedef void* MTLSharedEvent_id; + @@ -15917,7 +16347,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16068,6 +16498,7 @@ typedef void* MTLSharedEvent_id; + @@ -16248,8 +16679,11 @@ typedef void* MTLSharedEvent_id; + + + - + @@ -16262,6 +16696,7 @@ typedef void* MTLSharedEvent_id; + @@ -16270,10 +16705,16 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -16336,6 +16777,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -16349,6 +16793,10 @@ typedef void* MTLSharedEvent_id; + + + + @@ -16361,6 +16809,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -16375,14 +16826,17 @@ typedef void* MTLSharedEvent_id; + + + @@ -16397,11 +16851,13 @@ typedef void* MTLSharedEvent_id; + + @@ -16422,6 +16878,7 @@ typedef void* MTLSharedEvent_id; + @@ -16444,7 +16901,7 @@ typedef void* MTLSharedEvent_id; - + @@ -16467,6 +16924,7 @@ typedef void* MTLSharedEvent_id; + @@ -16478,6 +16936,7 @@ typedef void* MTLSharedEvent_id; + @@ -16495,6 +16954,7 @@ typedef void* MTLSharedEvent_id; + @@ -16503,6 +16963,7 @@ typedef void* MTLSharedEvent_id; + @@ -16538,14 +16999,17 @@ typedef void* MTLSharedEvent_id; + + + @@ -16586,6 +17050,8 @@ typedef void* MTLSharedEvent_id; + + @@ -16642,6 +17108,7 @@ typedef void* MTLSharedEvent_id; + @@ -16674,6 +17141,7 @@ typedef void* MTLSharedEvent_id; + @@ -16706,10 +17174,20 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + - + @@ -16763,6 +17241,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -16841,6 +17322,11 @@ typedef void* MTLSharedEvent_id; + + + + + @@ -17067,7 +17553,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17331,6 +17817,7 @@ typedef void* MTLSharedEvent_id; + @@ -17570,6 +18057,7 @@ typedef void* MTLSharedEvent_id; + @@ -17663,6 +18151,7 @@ typedef void* MTLSharedEvent_id; + @@ -17849,7 +18338,7 @@ typedef void* MTLSharedEvent_id; - + @@ -17869,6 +18358,7 @@ typedef void* MTLSharedEvent_id; + @@ -17893,6 +18383,7 @@ typedef void* MTLSharedEvent_id; + @@ -18080,11 +18571,7 @@ typedef void* MTLSharedEvent_id; - - - - - + @@ -18106,6 +18593,7 @@ typedef void* MTLSharedEvent_id; + @@ -18124,6 +18612,7 @@ typedef void* MTLSharedEvent_id; + @@ -18192,7 +18681,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18204,7 +18693,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18290,7 +18779,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18314,7 +18803,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18326,7 +18815,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18335,6 +18824,7 @@ typedef void* MTLSharedEvent_id; + @@ -18346,7 +18836,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18356,7 +18846,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18365,9 +18855,9 @@ typedef void* MTLSharedEvent_id; - + - + @@ -18400,6 +18890,7 @@ typedef void* MTLSharedEvent_id; + @@ -18432,6 +18923,7 @@ typedef void* MTLSharedEvent_id; + @@ -18538,6 +19030,7 @@ typedef void* MTLSharedEvent_id; + @@ -18599,6 +19092,7 @@ typedef void* MTLSharedEvent_id; + @@ -18647,14 +19141,14 @@ typedef void* MTLSharedEvent_id; - + - + @@ -18793,23 +19287,25 @@ typedef void* MTLSharedEvent_id; + - + - - - - - - + + + + + + + @@ -18837,6 +19333,10 @@ typedef void* MTLSharedEvent_id; + + + + @@ -18845,7 +19345,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18863,7 +19363,7 @@ typedef void* MTLSharedEvent_id; - + @@ -18925,7 +19425,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19077,6 +19577,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -19084,6 +19587,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -19128,6 +19634,11 @@ typedef void* MTLSharedEvent_id; + + + + + @@ -19136,6 +19647,7 @@ typedef void* MTLSharedEvent_id; + @@ -19171,7 +19683,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19254,6 +19766,7 @@ typedef void* MTLSharedEvent_id; + @@ -19273,7 +19786,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19349,6 +19862,21 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + @@ -19366,6 +19894,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -19395,7 +19926,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19532,7 +20063,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19564,7 +20095,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19580,6 +20111,7 @@ typedef void* MTLSharedEvent_id; + @@ -19594,9 +20126,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -19624,6 +20157,7 @@ typedef void* MTLSharedEvent_id; + @@ -19632,6 +20166,7 @@ typedef void* MTLSharedEvent_id; + @@ -19650,7 +20185,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19700,7 +20235,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19713,6 +20248,7 @@ typedef void* MTLSharedEvent_id; + @@ -19724,7 +20260,7 @@ typedef void* MTLSharedEvent_id; - + @@ -19735,6 +20271,7 @@ typedef void* MTLSharedEvent_id; + @@ -19841,11 +20378,11 @@ typedef void* MTLSharedEvent_id; - + - + @@ -19866,6 +20403,10 @@ typedef void* MTLSharedEvent_id; + + + + @@ -19934,6 +20475,7 @@ typedef void* MTLSharedEvent_id; + @@ -19995,9 +20537,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20033,6 +20576,7 @@ typedef void* MTLSharedEvent_id; + @@ -20071,6 +20615,7 @@ typedef void* MTLSharedEvent_id; + @@ -20095,6 +20640,7 @@ typedef void* MTLSharedEvent_id; + @@ -20117,7 +20663,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20129,6 +20675,8 @@ typedef void* MTLSharedEvent_id; + + @@ -20154,6 +20702,7 @@ typedef void* MTLSharedEvent_id; + @@ -20183,6 +20732,7 @@ typedef void* MTLSharedEvent_id; + @@ -20210,6 +20760,7 @@ typedef void* MTLSharedEvent_id; + @@ -20218,12 +20769,14 @@ typedef void* MTLSharedEvent_id; - + + + @@ -20232,6 +20785,7 @@ typedef void* MTLSharedEvent_id; + @@ -20248,7 +20802,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20256,6 +20810,7 @@ typedef void* MTLSharedEvent_id; + @@ -20288,6 +20843,7 @@ typedef void* MTLSharedEvent_id; + @@ -20367,6 +20923,7 @@ typedef void* MTLSharedEvent_id; + @@ -20422,12 +20979,13 @@ typedef void* MTLSharedEvent_id; - + + @@ -20436,9 +20994,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20449,9 +21008,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20469,10 +21029,7 @@ typedef void* MTLSharedEvent_id; - - - - + @@ -20508,9 +21065,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20535,7 +21093,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20550,6 +21108,7 @@ typedef void* MTLSharedEvent_id; + @@ -20577,6 +21136,7 @@ typedef void* MTLSharedEvent_id; + @@ -20585,7 +21145,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20615,6 +21175,7 @@ typedef void* MTLSharedEvent_id; + @@ -20657,6 +21218,7 @@ typedef void* MTLSharedEvent_id; + @@ -20691,10 +21253,10 @@ typedef void* MTLSharedEvent_id; - + @@ -20723,9 +21285,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20767,14 +21330,16 @@ typedef void* MTLSharedEvent_id; + - + + @@ -20823,6 +21388,7 @@ typedef void* MTLSharedEvent_id; + @@ -20849,9 +21415,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20859,6 +21426,7 @@ typedef void* MTLSharedEvent_id; + @@ -20884,9 +21452,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20899,9 +21468,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -20909,7 +21479,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20919,7 +21489,7 @@ typedef void* MTLSharedEvent_id; - + @@ -20931,6 +21501,7 @@ typedef void* MTLSharedEvent_id; + @@ -21005,6 +21576,7 @@ typedef void* MTLSharedEvent_id; + @@ -21021,6 +21593,7 @@ typedef void* MTLSharedEvent_id; + @@ -21040,6 +21613,7 @@ typedef void* MTLSharedEvent_id; + @@ -21061,6 +21635,7 @@ typedef void* MTLSharedEvent_id; + @@ -21348,6 +21923,7 @@ typedef void* MTLSharedEvent_id; + @@ -21364,6 +21940,11 @@ typedef void* MTLSharedEvent_id; + + + + + @@ -21469,6 +22050,7 @@ typedef void* MTLSharedEvent_id; + @@ -21512,6 +22094,7 @@ typedef void* MTLSharedEvent_id; + @@ -21520,6 +22103,7 @@ typedef void* MTLSharedEvent_id; + @@ -21538,6 +22122,7 @@ typedef void* MTLSharedEvent_id; + @@ -21552,6 +22137,7 @@ typedef void* MTLSharedEvent_id; + @@ -21611,10 +22197,16 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -21622,7 +22214,7 @@ typedef void* MTLSharedEvent_id; - + VkPhysicalDeviceYcbcr2Plane444FormatsFeaturesEXT and @@ -21637,6 +22229,7 @@ typedef void* MTLSharedEvent_id; + @@ -21670,12 +22263,13 @@ typedef void* MTLSharedEvent_id; - + + @@ -21684,9 +22278,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -21740,6 +22335,7 @@ typedef void* MTLSharedEvent_id; + @@ -21753,9 +22349,10 @@ typedef void* MTLSharedEvent_id; + - + VkPhysicalDevice4444FormatsFeaturesEXT and @@ -21768,6 +22365,7 @@ typedef void* MTLSharedEvent_id; + @@ -21786,6 +22384,7 @@ typedef void* MTLSharedEvent_id; + @@ -21816,7 +22415,7 @@ typedef void* MTLSharedEvent_id; - + @@ -21859,9 +22458,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -21873,6 +22473,7 @@ typedef void* MTLSharedEvent_id; + @@ -21897,9 +22498,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -21907,6 +22509,7 @@ typedef void* MTLSharedEvent_id; + @@ -21915,6 +22518,7 @@ typedef void* MTLSharedEvent_id; + @@ -22166,6 +22770,7 @@ typedef void* MTLSharedEvent_id; + @@ -22179,9 +22784,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -22197,6 +22803,7 @@ typedef void* MTLSharedEvent_id; + @@ -22222,7 +22829,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22232,6 +22839,7 @@ typedef void* MTLSharedEvent_id; + @@ -22241,6 +22849,7 @@ typedef void* MTLSharedEvent_id; + @@ -22269,6 +22878,7 @@ typedef void* MTLSharedEvent_id; + @@ -22281,6 +22891,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -22297,6 +22910,7 @@ typedef void* MTLSharedEvent_id; + @@ -22319,6 +22933,7 @@ typedef void* MTLSharedEvent_id; + @@ -22333,6 +22948,7 @@ typedef void* MTLSharedEvent_id; + @@ -22342,6 +22958,7 @@ typedef void* MTLSharedEvent_id; + @@ -22359,6 +22976,7 @@ typedef void* MTLSharedEvent_id; + @@ -22425,6 +23043,7 @@ typedef void* MTLSharedEvent_id; + @@ -22441,6 +23060,7 @@ typedef void* MTLSharedEvent_id; + @@ -22542,6 +23162,7 @@ typedef void* MTLSharedEvent_id; + @@ -22551,6 +23172,7 @@ typedef void* MTLSharedEvent_id; + @@ -22569,6 +23191,7 @@ typedef void* MTLSharedEvent_id; + @@ -22593,6 +23216,7 @@ typedef void* MTLSharedEvent_id; + @@ -22607,6 +23231,7 @@ typedef void* MTLSharedEvent_id; + @@ -22618,6 +23243,7 @@ typedef void* MTLSharedEvent_id; + @@ -22647,6 +23273,7 @@ typedef void* MTLSharedEvent_id; + @@ -22656,6 +23283,7 @@ typedef void* MTLSharedEvent_id; + @@ -22678,11 +23306,12 @@ typedef void* MTLSharedEvent_id; + - + @@ -22739,6 +23368,7 @@ typedef void* MTLSharedEvent_id; + @@ -22755,6 +23385,7 @@ typedef void* MTLSharedEvent_id; + @@ -22784,6 +23415,7 @@ typedef void* MTLSharedEvent_id; + @@ -22806,6 +23438,7 @@ typedef void* MTLSharedEvent_id; + @@ -22837,6 +23470,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -22913,10 +23549,11 @@ typedef void* MTLSharedEvent_id; - - + + + @@ -22930,7 +23567,7 @@ typedef void* MTLSharedEvent_id; - + @@ -22938,7 +23575,7 @@ typedef void* MTLSharedEvent_id; - + @@ -23077,6 +23714,7 @@ typedef void* MTLSharedEvent_id; + @@ -23121,6 +23759,7 @@ typedef void* MTLSharedEvent_id; + @@ -23192,6 +23831,7 @@ typedef void* MTLSharedEvent_id; + @@ -23206,6 +23846,7 @@ typedef void* MTLSharedEvent_id; + @@ -23224,6 +23865,7 @@ typedef void* MTLSharedEvent_id; + @@ -23257,6 +23899,7 @@ typedef void* MTLSharedEvent_id; + @@ -23396,10 +24039,20 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + + @@ -23434,6 +24087,7 @@ typedef void* MTLSharedEvent_id; + @@ -23494,6 +24148,7 @@ typedef void* MTLSharedEvent_id; + @@ -23561,12 +24216,48 @@ typedef void* MTLSharedEvent_id; - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -23579,6 +24270,7 @@ typedef void* MTLSharedEvent_id; + @@ -23612,6 +24304,7 @@ typedef void* MTLSharedEvent_id; + @@ -23679,6 +24372,7 @@ typedef void* MTLSharedEvent_id; + @@ -23699,9 +24393,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -23709,6 +24404,7 @@ typedef void* MTLSharedEvent_id; + @@ -23737,6 +24433,7 @@ typedef void* MTLSharedEvent_id; + @@ -23745,6 +24442,7 @@ typedef void* MTLSharedEvent_id; + @@ -23775,6 +24473,7 @@ typedef void* MTLSharedEvent_id; + @@ -23821,6 +24520,7 @@ typedef void* MTLSharedEvent_id; + @@ -23856,10 +24556,15 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + @@ -23903,6 +24608,7 @@ typedef void* MTLSharedEvent_id; + @@ -23912,6 +24618,7 @@ typedef void* MTLSharedEvent_id; + @@ -23931,6 +24638,7 @@ typedef void* MTLSharedEvent_id; + @@ -23954,6 +24662,7 @@ typedef void* MTLSharedEvent_id; + @@ -23985,9 +24694,10 @@ typedef void* MTLSharedEvent_id; + - + @@ -23998,6 +24708,7 @@ typedef void* MTLSharedEvent_id; + @@ -24020,6 +24731,7 @@ typedef void* MTLSharedEvent_id; + @@ -24061,16 +24773,17 @@ typedef void* MTLSharedEvent_id; - + + - + @@ -24133,7 +24846,7 @@ typedef void* MTLSharedEvent_id; - + @@ -24150,6 +24863,7 @@ typedef void* MTLSharedEvent_id; + @@ -24168,6 +24882,7 @@ typedef void* MTLSharedEvent_id; + @@ -24196,6 +24911,7 @@ typedef void* MTLSharedEvent_id; + @@ -24251,6 +24967,8 @@ typedef void* MTLSharedEvent_id; + + @@ -24265,6 +24983,7 @@ typedef void* MTLSharedEvent_id; + @@ -24279,16 +24998,22 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + - + - - + + + + + @@ -24303,10 +25028,24 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + + + + + + @@ -24315,12 +25054,16 @@ typedef void* MTLSharedEvent_id; + - + - - + + + + + @@ -24367,11 +25110,82 @@ typedef void* MTLSharedEvent_id; - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -24384,6 +25198,7 @@ typedef void* MTLSharedEvent_id; + @@ -24396,6 +25211,7 @@ typedef void* MTLSharedEvent_id; + @@ -24437,10 +25253,19 @@ typedef void* MTLSharedEvent_id; - + - - + + + + + + + + + + + @@ -24462,6 +25287,134 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -26111,6 +27064,15 @@ typedef void* MTLSharedEvent_id; + + + + + + + + + @@ -26464,6 +27426,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -26627,6 +27595,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -26718,6 +27689,9 @@ typedef void* MTLSharedEvent_id; + + + @@ -26858,6 +27832,12 @@ typedef void* MTLSharedEvent_id; + + + + + + @@ -26937,8 +27917,8 @@ typedef void* MTLSharedEvent_id; VK_PIPELINE_STAGE_2_SUBPASS_SHADER_BIT_HUAWEI - - VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_NV + + VK_PIPELINE_STAGE_2_COMMAND_PREPROCESS_BIT_EXT VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR @@ -26963,4 +27943,81 @@ typedef void* MTLSharedEvent_id; VK_PIPELINE_STAGE_2_OPTICAL_FLOW_BIT_NV + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 3254c833685896fc650736c2a073a2f8e7a49b9f Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Mon, 22 Jul 2024 21:00:25 +0000 Subject: [PATCH 46/67] comctl32: Implement EVENT_OBJECT_STATECHANGE for buttons. --- dlls/comctl32/button.c | 11 +++++++++++ dlls/user32/button.c | 1 + 2 files changed, 12 insertions(+) diff --git a/dlls/comctl32/button.c b/dlls/comctl32/button.c index 77eb54fbcf1..9f188e0f137 100644 --- a/dlls/comctl32/button.c +++ b/dlls/comctl32/button.c @@ -608,6 +608,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); infoPtr->state |= BUTTON_BTNPRESSED; SetCapture( hWnd ); + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); } else if (wParam == VK_UP || wParam == VK_DOWN) { @@ -637,6 +638,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L SetCapture( hWnd ); infoPtr->state |= BUTTON_BTNPRESSED; SendMessageW( hWnd, BM_SETSTATE, TRUE, 0 ); + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); break; case WM_KEYUP: @@ -649,6 +651,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L SendMessageW(hWnd, BCM_SETDROPDOWNSTATE, FALSE, 0); if (!(state & BUTTON_BTNPRESSED)) break; infoPtr->state &= BUTTON_NSTATES | BST_HOT; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); if (!(state & BST_PUSHED)) { ReleaseCapture(); @@ -688,6 +691,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L infoPtr->state &= BUTTON_NSTATES; if (infoPtr->state & BST_PUSHED) SendMessageW( hWnd, BM_SETSTATE, FALSE, 0 ); + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); } break; @@ -717,6 +721,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L { infoPtr->state |= BST_HOT; InvalidateRect( hWnd, NULL, FALSE ); + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); break; } @@ -724,6 +729,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L { infoPtr->state &= ~BST_HOT; InvalidateRect( hWnd, NULL, FALSE ); + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); break; } @@ -858,6 +864,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_SETFOCUS: TRACE("WM_SETFOCUS %p\n",hWnd); infoPtr->state |= BST_FOCUS; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); if (btn_type == BS_OWNERDRAW) paint_button( infoPtr, btn_type, ODA_FOCUS ); @@ -871,6 +878,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L case WM_KILLFOCUS: TRACE("WM_KILLFOCUS %p\n",hWnd); infoPtr->state &= ~BST_FOCUS; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); if ((infoPtr->state & BUTTON_BTNPRESSED) && GetCapture() == hWnd) ReleaseCapture(); @@ -994,6 +1002,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L if ((infoPtr->state & 3) != wParam) { infoPtr->state = (infoPtr->state & ~3) | wParam; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); InvalidateRect( hWnd, NULL, FALSE ); } if ((btn_type == BS_AUTORADIOBUTTON) && (wParam == BST_CHECKED) && (style & WS_CHILD)) @@ -1017,6 +1026,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L if (btn_type == BS_USERBUTTON) BUTTON_NOTIFY_PARENT( hWnd, (state & BST_PUSHED) ? BN_HILITE : BN_UNHILITE ); infoPtr->state = state; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); InvalidateRect( hWnd, NULL, FALSE ); } @@ -1029,6 +1039,7 @@ static LRESULT CALLBACK BUTTON_WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, L { infoPtr->state &= ~BST_DROPDOWNPUSHED; infoPtr->state |= new_state; + NotifyWinEvent( EVENT_OBJECT_STATECHANGE, hWnd, OBJID_CLIENT, 0 ); InvalidateRect(hWnd, NULL, FALSE); } break; diff --git a/dlls/user32/button.c b/dlls/user32/button.c index 10a111562a7..3d691bf03a3 100644 --- a/dlls/user32/button.c +++ b/dlls/user32/button.c @@ -119,6 +119,7 @@ static inline LONG get_button_state( HWND hwnd ) static inline void set_button_state( HWND hwnd, LONG state ) { SetWindowLongW( hwnd, STATE_GWL_OFFSET, state ); + NtUserNotifyWinEvent( EVENT_OBJECT_STATECHANGE, hwnd, OBJID_CLIENT, 0 ); } static inline HFONT get_button_font( HWND hwnd ) From 61992a546edd40d7a0f6eccb57df051f6f42f1b3 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 31 Jul 2024 18:32:18 +0000 Subject: [PATCH 47/67] user32: Handle WM_GETOBJECT in buttons. This is a hack. We should implement RealGetWindowClass instead. --- dlls/user32/button.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/user32/button.c b/dlls/user32/button.c index 3d691bf03a3..1d501cdc3d3 100644 --- a/dlls/user32/button.c +++ b/dlls/user32/button.c @@ -376,6 +376,11 @@ LRESULT ButtonWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, case WM_GETFONT: return (LRESULT)get_button_font( hWnd ); + case WM_GETOBJECT: + if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX) + return 0x10002; + break; + case WM_SETFOCUS: TRACE("WM_SETFOCUS %p\n",hWnd); set_button_state( hWnd, get_button_state(hWnd) | BST_FOCUS ); From 2b481353dd433442b936de284c357a2dd3b0cbca Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 31 Jul 2024 18:44:54 +0000 Subject: [PATCH 48/67] win32u: Implement EVENT_OBJECT_NAMECHANGE. --- dlls/win32u/defwnd.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c index f0cc8bdbf7f..e1acd055498 100644 --- a/dlls/win32u/defwnd.c +++ b/dlls/win32u/defwnd.c @@ -2640,6 +2640,8 @@ LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, case WM_SETTEXT: result = set_window_text( hwnd, (void *)lparam, ansi ); + if (result) + NtUserNotifyWinEvent( EVENT_OBJECT_NAMECHANGE, hwnd, OBJID_WINDOW, 0 ); if (result && (get_window_long( hwnd, GWL_STYLE ) & WS_CAPTION) == WS_CAPTION) handle_nc_paint( hwnd , (HRGN)1 ); /* repaint caption */ break; From 0b29c422c6c25051584fae74a17a237fc426fba8 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Wed, 24 Jul 2024 21:03:16 +0000 Subject: [PATCH 49/67] comctl32: Handle WM_GETOBJECT in tab control. (cherry picked from commit 8b1e784fa5b672633087840ff1606a7b2f68cf16) --- dlls/comctl32/tab.c | 5 +++++ dlls/comctl32/tests/tab.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/dlls/comctl32/tab.c b/dlls/comctl32/tab.c index b0d645bddd4..887682196bc 100644 --- a/dlls/comctl32/tab.c +++ b/dlls/comctl32/tab.c @@ -3368,6 +3368,11 @@ TAB_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case TCM_SETEXTENDEDSTYLE: return TAB_SetExtendedStyle (infoPtr, wParam, lParam); + case WM_GETOBJECT: + if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX) + return 0x1000f; + break; + case WM_GETFONT: return TAB_GetFont (infoPtr); diff --git a/dlls/comctl32/tests/tab.c b/dlls/comctl32/tests/tab.c index 6d38baae8a6..2e6767a06d5 100644 --- a/dlls/comctl32/tests/tab.c +++ b/dlls/comctl32/tests/tab.c @@ -1621,6 +1621,20 @@ static void test_TCM_GETROWCOUNT(void) DestroyWindow(hTab); } +static void test_WM_GETOBJECT(void) +{ + HWND hTab; + DWORD objid; + + hTab = createFilledTabControl(parent_wnd, TCS_FIXEDWIDTH, TCIF_TEXT|TCIF_IMAGE, 2); + ok(hTab != NULL, "Failed to create tab control\n"); + + objid = SendMessageA(hTab, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX); + ok(objid == 0x1000f, "Unexpected objid %lu.\n", objid); + + DestroyWindow(hTab); +} + START_TEST(tab) { LOGFONTA logfont; @@ -1660,6 +1674,7 @@ START_TEST(tab) test_create(); test_TCN_SELCHANGING(); test_TCM_GETROWCOUNT(); + test_WM_GETOBJECT(); uninit_winevent_hook(); From e753a484d192e9b05cd7502a0e14a116774af5e7 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 27 Sep 2024 18:17:34 -0600 Subject: [PATCH 50/67] win32u: Correctly fill new foreground window TID in WM_ACTIVATEAPP. CW-Bug-Id: #24294 --- dlls/user32/tests/win.c | 87 ++++++++++++++++++++++++++++++++++++++++- dlls/win32u/input.c | 7 +++- server/protocol.def | 1 + server/queue.c | 8 +++- server/user.h | 1 + server/winstation.c | 1 + 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 3be8e4969b9..50ef9026aa1 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -69,6 +69,7 @@ static HWND hwndMessage; static HWND hwndMain, hwndMain2; static HHOOK hhook; static BOOL app_activated, app_deactivated; +static LPARAM activate_app_lparam; static const char* szAWRClass = "Winsize"; static HMENU hmenu; @@ -1076,6 +1077,7 @@ static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_ACTIVATEAPP: if (wparam) app_activated = TRUE; else app_deactivated = TRUE; + activate_app_lparam = lparam; break; case WM_MOUSEACTIVATE: return MA_ACTIVATE; @@ -10893,6 +10895,7 @@ static void test_GetMessagePos(void) #define SET_FOREGROUND_STEAL_2 0x04 #define SET_FOREGROUND_SET_2 0x08 #define SET_FOREGROUND_INJECT 0x10 +#define SET_FOREGROUND_DESKTOP 0x20 struct set_foreground_thread_params { @@ -10941,6 +10944,8 @@ static DWORD WINAPI set_foreground_thread(void *params) SetForegroundWindow(p->window2); check_wnd_state(0, p->window2, 0, 0); } + if (msg.wParam & SET_FOREGROUND_DESKTOP) + SetForegroundWindow(GetDesktopWindow()); SetEvent(p->command_executed); continue; @@ -10988,6 +10993,7 @@ static void test_activateapp(HWND window1) * check_wnd_state(window1, thread_params.thread_window, window1, 0); */ ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); ok(!app_deactivated, "Received WM_ACTIVATEAPP(0), did not expect it.\n"); + activate_app_lparam = 0; while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); check_wnd_state(0, thread_params.thread_window, 0, 0); test_window = GetForegroundWindow(); @@ -10997,6 +11003,8 @@ static void test_activateapp(HWND window1) /* This message is reliable on Windows and inside a virtual desktop. * It is unreliable on KDE (50/50) and never arrives on FVWM. * ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); */ + if (app_deactivated) + ok(activate_app_lparam == tid, "got thread id %Iu, expected %lu.\n", activate_app_lparam, tid); /* Set foreground: WM_ACTIVATEAPP (1) is delivered. */ app_activated = app_deactivated = FALSE; @@ -11099,11 +11107,88 @@ static void test_activateapp(HWND window1) ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); ok(!app_deactivated, "Received WM_ACTIVATEAPP(0), did not expect it.\n"); + DestroyWindow(window2); + + if (winetest_interactive) + { + /* As soon as window looses focus to other process window there are no good and sure ways to make the process + * foreground again through WINAPI (thus blocking any further tests, so once setting, e. g., desktop window as + * foregroung the app window should be set to foreground manually. */ + ShowWindow(thread_params.thread_window, SW_HIDE); + SetActiveWindow(window1); + SetForegroundWindow(window1); + test_window = GetForegroundWindow(); + ok(test_window == window1, "Expected foreground window %p, got %p\n", + window1, test_window); + app_activated = app_deactivated = FALSE; + activate_app_lparam = 0; + trace("Now switch to any other window manually either way, like alt+tab, clicking other window, Win key + D.\n"); + while (GetForegroundWindow() == window1) + { + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + Sleep(10); + } + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); + ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); + ok(activate_app_lparam && activate_app_lparam != GetCurrentThreadId(), + "got %Iu, tid %lu.\n", activate_app_lparam, tid); + + /* Switch to desktop window from the same thread. */ + trace("Now switch to test window manually.\n"); + while (GetForegroundWindow() != window1) + { + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + Sleep(10); + } + Sleep(30); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + check_wnd_state(window1, window1, window1, 0); + app_activated = app_deactivated = FALSE; + activate_app_lparam = 0; + SetForegroundWindow(GetDesktopWindow()); + test_window = GetForegroundWindow(); + ok(test_window != window1, "Got the same foregorund window.\n"); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); + ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); + ok(activate_app_lparam && activate_app_lparam != GetCurrentThreadId() && activate_app_lparam != tid, + "got %Iu, tid %lu.\n", activate_app_lparam, tid); + + /* Switch to desktop window from the other thread. */ + trace("Now switch to test window manually, again.\n"); + while (GetForegroundWindow() != window1) + { + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + Sleep(10); + } + Sleep(30); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + check_wnd_state(window1, window1, window1, 0); + app_activated = app_deactivated = FALSE; + activate_app_lparam = 0; + PostThreadMessageA(tid, thread_params.msg_command, SET_FOREGROUND_DESKTOP, 0); + WaitForSingleObject(thread_params.command_executed, INFINITE); + test_window = GetForegroundWindow(); + ok(test_window != window2, "Got the same foregorund window.\n"); + Sleep(30); + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); + ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); + ok(activate_app_lparam && activate_app_lparam != tid, "got %Iu, tid %lu.\n", activate_app_lparam, tid); + trace("Switch to test window manually, for the last time for now.\n"); + while (GetForegroundWindow() != window1) + { + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + Sleep(10); + } + while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + } + PostThreadMessageA(tid, thread_params.msg_quit, 0, 0); WaitForSingleObject(thread, INFINITE); CloseHandle(thread_params.command_executed); - DestroyWindow(window2); } static LRESULT WINAPI winproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d67f462359c..65a52c1c864 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1876,7 +1876,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = get_active_window(); BOOL ret = FALSE; - DWORD old_thread, new_thread; + DWORD old_thread, new_thread, foreground_tid; CBTACTIVATESTRUCT cbt; if (previous == hwnd) @@ -1905,7 +1905,10 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) req->handle = wine_server_user_handle( hwnd ); req->internal_msg = WM_WINE_SETACTIVEWINDOW; if ((ret = !wine_server_call_err( req ))) + { previous = wine_server_ptr_handle( reply->previous ); + foreground_tid = reply->foreground_tid; + } } SERVER_END_REQ; if (!ret) goto done; @@ -1937,7 +1940,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) for (phwnd = list; *phwnd; phwnd++) { if (get_window_thread( *phwnd, NULL ) == old_thread) - send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); + send_message( *phwnd, WM_ACTIVATEAPP, 0, foreground_tid ); } } if (new_thread) diff --git a/server/protocol.def b/server/protocol.def index 0236a0c87d9..e6f3db85c41 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3022,6 +3022,7 @@ enum coords_relative unsigned int internal_msg; /* set active window internal message */ @REPLY user_handle_t previous; /* handle to the previous active window */ + thread_id_t foreground_tid; /* thread id of the new active window */ @END /* Set the current thread capture window */ diff --git a/server/queue.c b/server/queue.c index 0ed30b6256f..04f38b03837 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1429,7 +1429,11 @@ static void thread_input_destroy( struct object *obj ) empty_msg_list( &input->msg_list ); if ((desktop = input->desktop)) { - if (desktop->foreground_input == input) desktop->foreground_input = NULL; + if (desktop->foreground_input == input) + { + desktop->foreground_input = NULL; + desktop->foreground_tid = 0; + } release_object( desktop ); } release_object( input->shared_mapping ); @@ -3694,6 +3698,7 @@ DECL_HANDLER(set_foreground_window) thread->queue->input->desktop == desktop) { set_foreground_input( desktop, thread->queue->input ); + desktop->foreground_tid = thread->id; reply->send_msg_new = (desktop->foreground_input != queue->input); } else set_win32_error( ERROR_INVALID_WINDOW_HANDLE ); @@ -3749,6 +3754,7 @@ DECL_HANDLER(set_active_window) LIST_FOR_EACH_ENTRY_SAFE( msg, next, &queue->msg_list[POST_MESSAGE], struct message, entry ) if (msg->msg == req->internal_msg) remove_queue_message( queue, msg, POST_MESSAGE ); } + reply->foreground_tid = desktop->foreground_tid; } else set_error( STATUS_INVALID_HANDLE ); } diff --git a/server/user.h b/server/user.h index cc873ef47ae..3aacc556f61 100644 --- a/server/user.h +++ b/server/user.h @@ -66,6 +66,7 @@ struct desktop timeout_t close_timeout_val;/* timeout duration before closing desktop */ struct list touches; /* list of active touches */ struct thread_input *foreground_input; /* thread input of foreground thread */ + thread_id_t foreground_tid; /* thread id of the foreground window */ unsigned int users; /* processes and threads using this desktop */ user_handle_t cursor_win; /* window that contains the cursor */ unsigned int cursor_clip_flags;/* last cursor clip flags */ diff --git a/server/winstation.c b/server/winstation.c index 875e87ed6b2..0410582ccc9 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -280,6 +280,7 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->close_timeout = NULL; desktop->close_timeout_val = 0; desktop->foreground_input = NULL; + desktop->foreground_tid = 0; desktop->users = 0; desktop->cursor_win = 0; desktop->cursor_clip_flags = 0; From d4022ea8b251a22d7de0ffc6f5ef9d820fc4dd68 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 28 Sep 2024 17:34:29 +0000 Subject: [PATCH 51/67] user32: Respond to OBJID_QUERYCLASSNAMEIDX in combo boxes. This is a hack. We should implement RealGetWindowClass instead. CW-Bug-Id: #24314 --- dlls/user32/combo.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/user32/combo.c b/dlls/user32/combo.c index 6afd841f82a..25b6888de0e 100644 --- a/dlls/user32/combo.c +++ b/dlls/user32/combo.c @@ -1897,6 +1897,11 @@ LRESULT ComboWndProc_common( HWND hwnd, UINT message, WPARAM wParam, LPARAM lPar return SendMessageW(lphc->owner, message, wParam, lParam); break; + case WM_GETOBJECT: + if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX) + return 0x10005; + break; + /* Combo messages */ case CB_ADDSTRING: From 2cc5baf273a661d7ea423fbe74c301e0c92a22d3 Mon Sep 17 00:00:00 2001 From: Esme Povirk Date: Sat, 28 Sep 2024 17:42:26 +0000 Subject: [PATCH 52/67] comctl32: Handle OBJID_QUERYCLASSNAMEIDX in trackbars. CW-Bug-Id: #24314 --- dlls/comctl32/trackbar.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dlls/comctl32/trackbar.c b/dlls/comctl32/trackbar.c index f2cb2a07a8d..936f88c9fbe 100644 --- a/dlls/comctl32/trackbar.c +++ b/dlls/comctl32/trackbar.c @@ -2012,6 +2012,11 @@ TRACKBAR_WindowProc (HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) case WM_GETDLGCODE: return DLGC_WANTARROWS; + case WM_GETOBJECT: + if ((LONG)lParam == OBJID_QUERYCLASSNAMEIDX) + return 0x10012; + return 0; + case WM_KEYDOWN: return TRACKBAR_KeyDown (infoPtr, (INT)wParam); From 4ee0a14db7a689e42cefc3d016e6285a940626ae Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Sat, 28 Sep 2024 21:27:27 -0600 Subject: [PATCH 53/67] ntdll: Skip dll file search when getting module handle. CW-Bug-Id: #24315 --- dlls/ntdll/loader.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 105a7420dcc..8ca9ac52bd1 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -3411,7 +3411,7 @@ static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub ) */ static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname, UNICODE_STRING *nt_name, WINE_MODREF **pwm, HANDLE *mapping, SECTION_IMAGE_INFORMATION *image_info, - struct file_id *id ) + struct file_id *id, BOOL find_loaded ) { const WCHAR *known_dll_name = NULL; WCHAR *fullname = NULL; @@ -3446,6 +3446,12 @@ static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname, UNI goto done; } } + if (find_loaded) + { + TRACE( "Skipping file search for %s.\n", debugstr_w(libname) ); + status = STATUS_DLL_NOT_FOUND; + goto done; + } if (!fullname && rb_get( &known_dlls, libname )) { prepend_system_dir( libname, wcslen(libname), &fullname ); @@ -3488,7 +3494,7 @@ static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname, UNI strstriW( libname, L"mfc42" )) { WARN_(loaddll)( "Using a fake mfc42 handle\n" ); - status = find_dll_file( load_path, L"kernel32.dll", nt_name, pwm, mapping, image_info, id ); + status = find_dll_file( load_path, L"kernel32.dll", nt_name, pwm, mapping, image_info, id, find_loaded ); } } } @@ -3535,7 +3541,7 @@ static NTSTATUS load_dll( const WCHAR *load_path, const WCHAR *libname, DWORD fl if (nts) { - nts = find_dll_file( load_path, libname, &nt_name, pwm, &mapping, &image_info, &id ); + nts = find_dll_file( load_path, libname, &nt_name, pwm, &mapping, &image_info, &id, FALSE ); system = FALSE; } @@ -3745,7 +3751,7 @@ NTSTATUS WINAPI LdrGetDllHandleEx( ULONG flags, LPCWSTR load_path, ULONG *dll_ch RtlEnterCriticalSection( &loader_section ); status = find_dll_file( load_path, dllname ? dllname : name->Buffer, - &nt_name, &wm, &mapping, &image_info, &id ); + &nt_name, &wm, &mapping, &image_info, &id, TRUE ); if (wm) *base = wm->ldr.DllBase; else From be742516f1041fb9bd914f44d7d580f4e6fe5ff4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Sat, 28 Sep 2024 21:28:09 -0600 Subject: [PATCH 54/67] atiadlxx: Bump driver version to 99. CW-Bug-Id: #24315 --- dlls/atiadlxx/atiadlxx_main.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c index 8fdd2e55d02..9399025f94b 100644 --- a/dlls/atiadlxx/atiadlxx_main.c +++ b/dlls/atiadlxx/atiadlxx_main.c @@ -172,15 +172,15 @@ typedef struct ADLDisplayMap } ADLDisplayMap, *LPADLDisplayMap; static const ADLVersionsInfo version = { - "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", + "99.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", "", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; static const ADLVersionsInfoX2 version2 = { - "23.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", + "99.19.02-230831a-396538C-AMD-Software-Adrenalin-Edition", "", - "23.10.2", + "99.10.2", "http://support.amd.com/drivers/xml/driver_09_us.xml", }; From 308764a58986cd0c45a10f8df51c2731ea5ae737 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Sat, 28 Sep 2024 21:29:11 -0600 Subject: [PATCH 55/67] wine.inf: Enable builtin atiadlxx for FFXVI. CW-Bug-Id: #24315 --- loader/wine.inf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/loader/wine.inf.in b/loader/wine.inf.in index dc7232cf4e7..fd5d5137d3e 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -2907,5 +2907,6 @@ HKCU,Software\Wine\AppDefaults\starwarssquadrons.exe\DllOverrides,"atiadlxx",,"b HKCU,Software\Wine\AppDefaults\GW2.Main_Win64_Retail.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\Spider-Man.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\RiftApart.exe\DllOverrides,"atiadlxx",,"builtin" +HKCU,Software\Wine\AppDefaults\ffxvi.exe\DllOverrides,"atiadlxx",,"builtin" HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Height",0x10001,480 HKLM,Software\Wow6432Node\lucasarts entertainment company llc\Star Wars: Episode I Racer\v1.0,"Display Width",0x10001,640 From 9db402d2008bd3d539bb9aae1a1d6b865b4016bc Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Sep 2024 15:15:04 -0600 Subject: [PATCH 56/67] ntdll: HACK: Enable WINE_ALERT_SIMULATE_SCHED_QUANTUM option for Mary Skelter 2. CW-Bug-Id: #24283 --- dlls/ntdll/unix/loader.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 1e05e75375b..1e75eff1595 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2068,6 +2068,7 @@ static void hacks_init(void) else if (main_argc > 1) { alert_simulate_sched_quantum = !!strstr(main_argv[1], "GTA5.exe"); + alert_simulate_sched_quantum = alert_simulate_sched_quantum || !!strstr(main_argv[1], "MarySkelter2.exe"); } if (alert_simulate_sched_quantum) ERR("HACK: Simulating sched quantum in NtWaitForAlertByThreadId.\n"); From 38e2231a37d580de604638c424ae60e601b4e07f Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Sep 2024 10:04:46 -0600 Subject: [PATCH 57/67] Revert "win32u: Correctly fill new foreground window TID in WM_ACTIVATEAPP." This reverts commit f06ad045cab8d06573955d5f4d40f98680586d75. CW-Bug-Id: #24294 --- dlls/user32/tests/win.c | 87 +---------------------------------------- dlls/win32u/input.c | 7 +--- server/protocol.def | 1 - server/queue.c | 8 +--- server/user.h | 1 - server/winstation.c | 1 - 6 files changed, 4 insertions(+), 101 deletions(-) diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c index 50ef9026aa1..3be8e4969b9 100644 --- a/dlls/user32/tests/win.c +++ b/dlls/user32/tests/win.c @@ -69,7 +69,6 @@ static HWND hwndMessage; static HWND hwndMain, hwndMain2; static HHOOK hhook; static BOOL app_activated, app_deactivated; -static LPARAM activate_app_lparam; static const char* szAWRClass = "Winsize"; static HMENU hmenu; @@ -1077,7 +1076,6 @@ static LRESULT WINAPI main_window_procA(HWND hwnd, UINT msg, WPARAM wparam, LPAR case WM_ACTIVATEAPP: if (wparam) app_activated = TRUE; else app_deactivated = TRUE; - activate_app_lparam = lparam; break; case WM_MOUSEACTIVATE: return MA_ACTIVATE; @@ -10895,7 +10893,6 @@ static void test_GetMessagePos(void) #define SET_FOREGROUND_STEAL_2 0x04 #define SET_FOREGROUND_SET_2 0x08 #define SET_FOREGROUND_INJECT 0x10 -#define SET_FOREGROUND_DESKTOP 0x20 struct set_foreground_thread_params { @@ -10944,8 +10941,6 @@ static DWORD WINAPI set_foreground_thread(void *params) SetForegroundWindow(p->window2); check_wnd_state(0, p->window2, 0, 0); } - if (msg.wParam & SET_FOREGROUND_DESKTOP) - SetForegroundWindow(GetDesktopWindow()); SetEvent(p->command_executed); continue; @@ -10993,7 +10988,6 @@ static void test_activateapp(HWND window1) * check_wnd_state(window1, thread_params.thread_window, window1, 0); */ ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); ok(!app_deactivated, "Received WM_ACTIVATEAPP(0), did not expect it.\n"); - activate_app_lparam = 0; while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); check_wnd_state(0, thread_params.thread_window, 0, 0); test_window = GetForegroundWindow(); @@ -11003,8 +10997,6 @@ static void test_activateapp(HWND window1) /* This message is reliable on Windows and inside a virtual desktop. * It is unreliable on KDE (50/50) and never arrives on FVWM. * ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); */ - if (app_deactivated) - ok(activate_app_lparam == tid, "got thread id %Iu, expected %lu.\n", activate_app_lparam, tid); /* Set foreground: WM_ACTIVATEAPP (1) is delivered. */ app_activated = app_deactivated = FALSE; @@ -11107,88 +11099,11 @@ static void test_activateapp(HWND window1) ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); ok(!app_deactivated, "Received WM_ACTIVATEAPP(0), did not expect it.\n"); - DestroyWindow(window2); - - if (winetest_interactive) - { - /* As soon as window looses focus to other process window there are no good and sure ways to make the process - * foreground again through WINAPI (thus blocking any further tests, so once setting, e. g., desktop window as - * foregroung the app window should be set to foreground manually. */ - ShowWindow(thread_params.thread_window, SW_HIDE); - SetActiveWindow(window1); - SetForegroundWindow(window1); - test_window = GetForegroundWindow(); - ok(test_window == window1, "Expected foreground window %p, got %p\n", - window1, test_window); - app_activated = app_deactivated = FALSE; - activate_app_lparam = 0; - trace("Now switch to any other window manually either way, like alt+tab, clicking other window, Win key + D.\n"); - while (GetForegroundWindow() == window1) - { - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - Sleep(10); - } - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); - ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); - ok(activate_app_lparam && activate_app_lparam != GetCurrentThreadId(), - "got %Iu, tid %lu.\n", activate_app_lparam, tid); - - /* Switch to desktop window from the same thread. */ - trace("Now switch to test window manually.\n"); - while (GetForegroundWindow() != window1) - { - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - Sleep(10); - } - Sleep(30); - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - check_wnd_state(window1, window1, window1, 0); - app_activated = app_deactivated = FALSE; - activate_app_lparam = 0; - SetForegroundWindow(GetDesktopWindow()); - test_window = GetForegroundWindow(); - ok(test_window != window1, "Got the same foregorund window.\n"); - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); - ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); - ok(activate_app_lparam && activate_app_lparam != GetCurrentThreadId() && activate_app_lparam != tid, - "got %Iu, tid %lu.\n", activate_app_lparam, tid); - - /* Switch to desktop window from the other thread. */ - trace("Now switch to test window manually, again.\n"); - while (GetForegroundWindow() != window1) - { - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - Sleep(10); - } - Sleep(30); - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - check_wnd_state(window1, window1, window1, 0); - app_activated = app_deactivated = FALSE; - activate_app_lparam = 0; - PostThreadMessageA(tid, thread_params.msg_command, SET_FOREGROUND_DESKTOP, 0); - WaitForSingleObject(thread_params.command_executed, INFINITE); - test_window = GetForegroundWindow(); - ok(test_window != window2, "Got the same foregorund window.\n"); - Sleep(30); - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - ok(!app_activated, "Received WM_ACTIVATEAPP(1), did not expect it.\n"); - ok(app_deactivated, "Expected WM_ACTIVATEAPP(0), did not receive it.\n"); - ok(activate_app_lparam && activate_app_lparam != tid, "got %Iu, tid %lu.\n", activate_app_lparam, tid); - trace("Switch to test window manually, for the last time for now.\n"); - while (GetForegroundWindow() != window1) - { - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - Sleep(10); - } - while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); - } - PostThreadMessageA(tid, thread_params.msg_quit, 0, 0); WaitForSingleObject(thread, INFINITE); CloseHandle(thread_params.command_executed); + DestroyWindow(window2); } static LRESULT WINAPI winproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 65a52c1c864..d67f462359c 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1876,7 +1876,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = get_active_window(); BOOL ret = FALSE; - DWORD old_thread, new_thread, foreground_tid; + DWORD old_thread, new_thread; CBTACTIVATESTRUCT cbt; if (previous == hwnd) @@ -1905,10 +1905,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) req->handle = wine_server_user_handle( hwnd ); req->internal_msg = WM_WINE_SETACTIVEWINDOW; if ((ret = !wine_server_call_err( req ))) - { previous = wine_server_ptr_handle( reply->previous ); - foreground_tid = reply->foreground_tid; - } } SERVER_END_REQ; if (!ret) goto done; @@ -1940,7 +1937,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) for (phwnd = list; *phwnd; phwnd++) { if (get_window_thread( *phwnd, NULL ) == old_thread) - send_message( *phwnd, WM_ACTIVATEAPP, 0, foreground_tid ); + send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); } } if (new_thread) diff --git a/server/protocol.def b/server/protocol.def index e6f3db85c41..0236a0c87d9 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3022,7 +3022,6 @@ enum coords_relative unsigned int internal_msg; /* set active window internal message */ @REPLY user_handle_t previous; /* handle to the previous active window */ - thread_id_t foreground_tid; /* thread id of the new active window */ @END /* Set the current thread capture window */ diff --git a/server/queue.c b/server/queue.c index 04f38b03837..0ed30b6256f 100644 --- a/server/queue.c +++ b/server/queue.c @@ -1429,11 +1429,7 @@ static void thread_input_destroy( struct object *obj ) empty_msg_list( &input->msg_list ); if ((desktop = input->desktop)) { - if (desktop->foreground_input == input) - { - desktop->foreground_input = NULL; - desktop->foreground_tid = 0; - } + if (desktop->foreground_input == input) desktop->foreground_input = NULL; release_object( desktop ); } release_object( input->shared_mapping ); @@ -3698,7 +3694,6 @@ DECL_HANDLER(set_foreground_window) thread->queue->input->desktop == desktop) { set_foreground_input( desktop, thread->queue->input ); - desktop->foreground_tid = thread->id; reply->send_msg_new = (desktop->foreground_input != queue->input); } else set_win32_error( ERROR_INVALID_WINDOW_HANDLE ); @@ -3754,7 +3749,6 @@ DECL_HANDLER(set_active_window) LIST_FOR_EACH_ENTRY_SAFE( msg, next, &queue->msg_list[POST_MESSAGE], struct message, entry ) if (msg->msg == req->internal_msg) remove_queue_message( queue, msg, POST_MESSAGE ); } - reply->foreground_tid = desktop->foreground_tid; } else set_error( STATUS_INVALID_HANDLE ); } diff --git a/server/user.h b/server/user.h index 3aacc556f61..cc873ef47ae 100644 --- a/server/user.h +++ b/server/user.h @@ -66,7 +66,6 @@ struct desktop timeout_t close_timeout_val;/* timeout duration before closing desktop */ struct list touches; /* list of active touches */ struct thread_input *foreground_input; /* thread input of foreground thread */ - thread_id_t foreground_tid; /* thread id of the foreground window */ unsigned int users; /* processes and threads using this desktop */ user_handle_t cursor_win; /* window that contains the cursor */ unsigned int cursor_clip_flags;/* last cursor clip flags */ diff --git a/server/winstation.c b/server/winstation.c index 0410582ccc9..875e87ed6b2 100644 --- a/server/winstation.c +++ b/server/winstation.c @@ -280,7 +280,6 @@ static struct desktop *create_desktop( const struct unicode_str *name, unsigned desktop->close_timeout = NULL; desktop->close_timeout_val = 0; desktop->foreground_input = NULL; - desktop->foreground_tid = 0; desktop->users = 0; desktop->cursor_win = 0; desktop->cursor_clip_flags = 0; From cd414fca4bc1b12a856d8d75164f72a1f013d7c4 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Sep 2024 10:03:23 -0600 Subject: [PATCH 58/67] win32u: Call set_active_window() helper directly from handle_internal_message(). (cherry picked from commit 99bfda52ed1f9ee2df0feb4c5e6bda8d83820c29) CW-Bug-Id: #24294 --- dlls/win32u/input.c | 2 +- dlls/win32u/message.c | 7 ++++++- dlls/win32u/win32u_private.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index d67f462359c..8c841f556ea 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1872,7 +1872,7 @@ static HWND set_focus_window( HWND hwnd, BOOL from_active, BOOL force ) /******************************************************************* * set_active_window */ -static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) +BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = get_active_window(); BOOL ret = FALSE; diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index cf7909452ef..19e9f8bc98d 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2122,9 +2122,14 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR if (is_desktop_window( hwnd )) return 0; return set_window_style( hwnd, wparam, lparam ); case WM_WINE_SETACTIVEWINDOW: + { + HWND prev; + if (!wparam && NtUserGetWindowThread( NtUserGetForegroundWindow(), NULL ) == GetCurrentThreadId()) return 0; - return (LRESULT)NtUserSetActiveWindow( (HWND)wparam ); + if (!set_active_window( (HWND)wparam, &prev, FALSE, TRUE )) return 0; + return (LRESULT)prev; + } case WM_WINE_KEYBOARD_LL_HOOK: case WM_WINE_MOUSE_LL_HOOK: { diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index dc65ca858cb..576a73b02a3 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -103,6 +103,7 @@ extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ); extern BOOL set_caret_blink_time( unsigned int time ); extern BOOL set_caret_pos( int x, int y ); extern BOOL set_foreground_window( HWND hwnd, BOOL mouse ); +extern BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ); extern void toggle_caret( HWND hwnd ); extern void update_mouse_tracking_info( HWND hwnd ); extern BOOL get_clip_cursor( RECT *rect ); From e05e0ba8af04bc774a7ff107046d25bf00327e21 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Fri, 27 Sep 2024 18:17:34 -0600 Subject: [PATCH 59/67] win32u: Correctly fill new foreground window TID in WM_ACTIVATEAPP. (cherry picked from commit b84fbae1c7c409a1fe5b0784f65baea32bef148b) CW-Bug-Id: #24294 --- dlls/win32u/input.c | 17 ++++++++++------- dlls/win32u/message.c | 2 +- dlls/win32u/win32u_private.h | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c index 8c841f556ea..9284b4cc849 100644 --- a/dlls/win32u/input.c +++ b/dlls/win32u/input.c @@ -1872,7 +1872,7 @@ static HWND set_focus_window( HWND hwnd, BOOL from_active, BOOL force ) /******************************************************************* * set_active_window */ -BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) +BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new_active_thread_id ) { HWND previous = get_active_window(); BOOL ret = FALSE; @@ -1934,10 +1934,11 @@ BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { if (old_thread) { + if (!new_active_thread_id) new_active_thread_id = new_thread; for (phwnd = list; *phwnd; phwnd++) { if (get_window_thread( *phwnd, NULL ) == old_thread) - send_message( *phwnd, WM_ACTIVATEAPP, 0, new_thread ); + send_message( *phwnd, WM_ACTIVATEAPP, 0, new_active_thread_id ); } } if (new_thread) @@ -2010,7 +2011,7 @@ HWND WINAPI NtUserSetActiveWindow( HWND hwnd ) return get_active_window(); /* Windows doesn't seem to return an error here */ } - if (!set_active_window( hwnd, &prev, FALSE, TRUE )) return 0; + if (!set_active_window( hwnd, &prev, FALSE, TRUE, 0 )) return 0; return prev; } @@ -2056,7 +2057,7 @@ HWND WINAPI NtUserSetFocus( HWND hwnd ) /* activate hwndTop if needed. */ if (hwndTop != get_active_window()) { - if (!set_active_window( hwndTop, NULL, FALSE, FALSE )) return 0; + if (!set_active_window( hwndTop, NULL, FALSE, FALSE, 0 )) return 0; if (!is_window( hwnd )) return 0; /* Abort if window destroyed */ /* Do not change focus if the window is no longer active */ @@ -2079,9 +2080,11 @@ HWND WINAPI NtUserSetFocus( HWND hwnd ) BOOL set_foreground_window( HWND hwnd, BOOL mouse ) { BOOL ret, send_msg_old = FALSE, send_msg_new = FALSE; + DWORD new_thread_id; HWND previous = 0; if (mouse) hwnd = get_full_window_handle( hwnd ); + new_thread_id = get_window_thread( hwnd, NULL ); SERVER_START_REQ( set_foreground_window ) { @@ -2098,9 +2101,9 @@ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) if (ret && previous != hwnd) { if (send_msg_old) /* old window belongs to other thread */ - NtUserPostMessage( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 ); + NtUserPostMessage( previous, WM_WINE_SETACTIVEWINDOW, 0, new_thread_id ); else if (send_msg_new) /* old window belongs to us but new one to other thread */ - ret = set_active_window( 0, NULL, mouse, TRUE ); + ret = set_active_window( 0, NULL, mouse, TRUE, new_thread_id ); /* already active, set_active_window will do no nothing */ if (!send_msg_new && hwnd == get_active_window()) @@ -2112,7 +2115,7 @@ BOOL set_foreground_window( HWND hwnd, BOOL mouse ) if (send_msg_new) /* new window belongs to other thread */ NtUserPostMessage( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 ); else /* new window belongs to us */ - ret = set_active_window( hwnd, NULL, mouse, TRUE ); + ret = set_active_window( hwnd, NULL, mouse, TRUE, 0 ); } return ret; } diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c index 19e9f8bc98d..5076513ca62 100644 --- a/dlls/win32u/message.c +++ b/dlls/win32u/message.c @@ -2127,7 +2127,7 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR if (!wparam && NtUserGetWindowThread( NtUserGetForegroundWindow(), NULL ) == GetCurrentThreadId()) return 0; - if (!set_active_window( (HWND)wparam, &prev, FALSE, TRUE )) return 0; + if (!set_active_window( (HWND)wparam, &prev, FALSE, TRUE, lparam )) return 0; return (LRESULT)prev; } case WM_WINE_KEYBOARD_LL_HOOK: diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index 576a73b02a3..95d17733dd8 100644 --- a/dlls/win32u/win32u_private.h +++ b/dlls/win32u/win32u_private.h @@ -103,7 +103,7 @@ extern BOOL set_capture_window( HWND hwnd, UINT gui_flags, HWND *prev_ret ); extern BOOL set_caret_blink_time( unsigned int time ); extern BOOL set_caret_pos( int x, int y ); extern BOOL set_foreground_window( HWND hwnd, BOOL mouse ); -extern BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ); +extern BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus, DWORD new_active_thread_id ); extern void toggle_caret( HWND hwnd ); extern void update_mouse_tracking_info( HWND hwnd ); extern BOOL get_clip_cursor( RECT *rect ); From 849d4bda09fb9ca6d90b75a32c1a8c584dc6210e Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Sep 2024 16:53:02 -0600 Subject: [PATCH 60/67] fixup! ntdll: Skip dll file search when getting module handle. CW-Bug-Id: #24315 --- dlls/ntdll/loader.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 8ca9ac52bd1..d0712017e5a 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -3445,12 +3445,12 @@ static NTSTATUS find_dll_file( const WCHAR *load_path, const WCHAR *libname, UNI status = STATUS_SUCCESS; goto done; } - } - if (find_loaded) - { - TRACE( "Skipping file search for %s.\n", debugstr_w(libname) ); - status = STATUS_DLL_NOT_FOUND; - goto done; + if (find_loaded) + { + TRACE( "Skipping file search for %s.\n", debugstr_w(libname) ); + status = STATUS_DLL_NOT_FOUND; + goto done; + } } if (!fullname && rb_get( &known_dlls, libname )) { From d6e4afddaba5b34e3bec060f4bf2179c3cfeb16b Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Mon, 30 Sep 2024 20:28:39 -0600 Subject: [PATCH 61/67] ntdll: HACK: Force en-US locale for EAC launcher. CW-Bug-Id: #24326 --- dlls/ntdll/unix/env.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dlls/ntdll/unix/env.c b/dlls/ntdll/unix/env.c index d068c1a7df9..f76368ba73d 100644 --- a/dlls/ntdll/unix/env.c +++ b/dlls/ntdll/unix/env.c @@ -815,6 +815,13 @@ static void init_locale(void) if (!unix_to_win_locale( ctype, system_locale )) system_locale[0] = 0; TRACE_(nls)( "Unix LC_CTYPE is %s, setting system locale to %s\n", debugstr_a(ctype), debugstr_a(user_locale) ); + + if (main_argc > 1 && strstr(main_argv[1], "start_protected_game.exe")) + { + FIXME( "HACK setting EN locale.\n" ); + messages = "en-US"; + } + if (!unix_to_win_locale( messages, user_locale )) user_locale[0] = 0; TRACE_(nls)( "Unix LC_MESSAGES is %s, user system locale to %s\n", debugstr_a(messages), debugstr_a(user_locale) ); From 68460d3ec5c1e24d1666ffc306980217d7f2d9df Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 1 Oct 2024 17:18:00 -0600 Subject: [PATCH 62/67] user32/tests: Add tests for QueryDisplayConfig( QDC_VIRTUAL_MODE_AWARE ). CW-Bug-Id: #24333 --- dlls/user32/tests/monitor.c | 158 +++++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 39 deletions(-) diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index e93a84242c4..0a80d694be4 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -1455,6 +1455,12 @@ static void test_GetDisplayConfigBufferSizes(void) ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes); else ok(ret == ERROR_NOT_SUPPORTED, "got %ld\n", ret); + + paths = modes = 0; + ret = pGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE, &paths, &modes); + todo_wine ok(!ret || broken(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED) /* before Win10 */, "got %ld\n", ret); + if (!ret) + ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes); } static BOOL CALLBACK test_EnumDisplayMonitors_normal_cb(HMONITOR monitor, HDC hdc, LPRECT rect, @@ -1731,12 +1737,13 @@ static void check_preferred_mode(const DISPLAYCONFIG_TARGET_PREFERRED_MODE *mode static void test_QueryDisplayConfig_result(UINT32 flags, UINT32 paths, const DISPLAYCONFIG_PATH_INFO *pi, UINT32 modes, const DISPLAYCONFIG_MODE_INFO *mi) { - UINT32 i; + UINT32 i, src_mode_idx, tgt_mode_idx; LONG ret; DISPLAYCONFIG_SOURCE_DEVICE_NAME source_name; DISPLAYCONFIG_TARGET_DEVICE_NAME target_name; DISPLAYCONFIG_TARGET_PREFERRED_MODE preferred_mode; DISPLAYCONFIG_ADAPTER_NAME adapter_name; + const DISPLAYCONFIG_DESKTOP_IMAGE_INFO *di; for (i = 0; i < paths; i++) { @@ -1789,65 +1796,127 @@ static void test_QueryDisplayConfig_result(UINT32 flags, } /* Check corresponding modes */ - if (pi[i].sourceInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + if (flags & QDC_VIRTUAL_MODE_AWARE) { - skip("Path doesn't contain source modeInfoIdx\n"); - continue; + src_mode_idx = pi[i].sourceInfo.sourceModeInfoIdx; + if (src_mode_idx == DISPLAYCONFIG_PATH_SOURCE_MODE_IDX_INVALID) + { + skip("Path doesn't contain source modeInfoIdx\n"); + continue; + } + } + else + { + src_mode_idx = pi[i].sourceInfo.modeInfoIdx; + if (src_mode_idx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + { + skip("Path doesn't contain source modeInfoIdx\n"); + continue; + } } - ok(pi[i].sourceInfo.modeInfoIdx < modes, "Expected index <%d, got %d\n", modes, pi[i].sourceInfo.modeInfoIdx); - if (pi[i].sourceInfo.modeInfoIdx >= modes) + ok(src_mode_idx < modes, "Expected index <%d, got %d\n", modes, src_mode_idx); + if (src_mode_idx >= modes) continue; - ok(mi[pi[i].sourceInfo.modeInfoIdx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, "Expected infoType %d, got %d\n", - DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, mi[pi[i].sourceInfo.modeInfoIdx].infoType); - ok(pi[i].sourceInfo.id == mi[pi[i].sourceInfo.modeInfoIdx].id, "Expected id %u, got %u\n", - pi[i].sourceInfo.id, mi[pi[i].sourceInfo.modeInfoIdx].id); - ok(pi[i].sourceInfo.adapterId.HighPart == mi[pi[i].sourceInfo.modeInfoIdx].adapterId.HighPart && - pi[i].sourceInfo.adapterId.LowPart == mi[pi[i].sourceInfo.modeInfoIdx].adapterId.LowPart, + ok(mi[src_mode_idx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, "Expected infoType %d, got %d\n", + DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, mi[src_mode_idx].infoType); + ok(pi[i].sourceInfo.id == mi[src_mode_idx].id, "Expected id %u, got %u\n", + pi[i].sourceInfo.id, mi[src_mode_idx].id); + ok(pi[i].sourceInfo.adapterId.HighPart == mi[src_mode_idx].adapterId.HighPart && + pi[i].sourceInfo.adapterId.LowPart == mi[src_mode_idx].adapterId.LowPart, "Expected LUID %08lx:%08lx, got %08lx:%08lx\n", pi[i].sourceInfo.adapterId.HighPart, pi[i].sourceInfo.adapterId.LowPart, - mi[pi[i].sourceInfo.modeInfoIdx].adapterId.HighPart, mi[pi[i].sourceInfo.modeInfoIdx].adapterId.LowPart); - ok(mi[pi[i].sourceInfo.modeInfoIdx].sourceMode.width > 0 && mi[pi[i].sourceInfo.modeInfoIdx].sourceMode.height > 0, + mi[src_mode_idx].adapterId.HighPart, mi[src_mode_idx].adapterId.LowPart); + ok(mi[src_mode_idx].sourceMode.width > 0 && mi[src_mode_idx].sourceMode.height > 0, "Expected non-zero height/width, got %ux%u\n", - mi[pi[i].sourceInfo.modeInfoIdx].sourceMode.width, mi[pi[i].sourceInfo.modeInfoIdx].sourceMode.height); + mi[src_mode_idx].sourceMode.width, mi[src_mode_idx].sourceMode.height); - if (pi[i].targetInfo.modeInfoIdx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + if (flags & QDC_VIRTUAL_MODE_AWARE) { - skip("Path doesn't contain target modeInfoIdx\n"); - continue; + tgt_mode_idx = pi[i].targetInfo.targetModeInfoIdx; + if (tgt_mode_idx == DISPLAYCONFIG_PATH_TARGET_MODE_IDX_INVALID) + { + skip("Path doesn't contain target modeInfoIdx\n"); + continue; + } + } + else + { + tgt_mode_idx = pi[i].targetInfo.modeInfoIdx; + if (tgt_mode_idx == DISPLAYCONFIG_PATH_MODE_IDX_INVALID) + { + skip("Path doesn't contain target modeInfoIdx\n"); + continue; + } } - ok(pi[i].targetInfo.modeInfoIdx < modes, "Expected index <%d, got %d\n", modes, pi[i].targetInfo.modeInfoIdx); - if (pi[i].targetInfo.modeInfoIdx >= modes) + ok(tgt_mode_idx < modes, "Expected index <%d, got %d\n", modes, tgt_mode_idx); + if (tgt_mode_idx >= modes) continue; - ok(mi[pi[i].targetInfo.modeInfoIdx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET, "Expected infoType %d, got %d\n", - DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, mi[pi[i].targetInfo.modeInfoIdx].infoType); - ok(pi[i].targetInfo.id == mi[pi[i].targetInfo.modeInfoIdx].id, "Expected id %u, got %u\n", - pi[i].targetInfo.id, mi[pi[i].targetInfo.modeInfoIdx].id); - ok(pi[i].targetInfo.adapterId.HighPart == mi[pi[i].targetInfo.modeInfoIdx].adapterId.HighPart && - pi[i].targetInfo.adapterId.LowPart == mi[pi[i].targetInfo.modeInfoIdx].adapterId.LowPart, + ok(mi[tgt_mode_idx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_TARGET, "Expected infoType %d, got %d\n", + DISPLAYCONFIG_MODE_INFO_TYPE_SOURCE, mi[tgt_mode_idx].infoType); + ok(pi[i].targetInfo.id == mi[tgt_mode_idx].id, "Expected id %u, got %u\n", + pi[i].targetInfo.id, mi[tgt_mode_idx].id); + ok(pi[i].targetInfo.adapterId.HighPart == mi[tgt_mode_idx].adapterId.HighPart && + pi[i].targetInfo.adapterId.LowPart == mi[tgt_mode_idx].adapterId.LowPart, "Expected LUID %08lx:%08lx, got %08lx:%08lx\n", pi[i].targetInfo.adapterId.HighPart, pi[i].targetInfo.adapterId.LowPart, - mi[pi[i].targetInfo.modeInfoIdx].adapterId.HighPart, mi[pi[i].targetInfo.modeInfoIdx].adapterId.LowPart); - ok(mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.activeSize.cx > 0 && - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.activeSize.cy > 0, + mi[tgt_mode_idx].adapterId.HighPart, mi[tgt_mode_idx].adapterId.LowPart); + ok(mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.activeSize.cx > 0 && + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.activeSize.cy > 0, "Expected non-zero height/width, got %ux%u\n", - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.activeSize.cx, - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.activeSize.cy); + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.activeSize.cx, + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.activeSize.cy); if (flags == QDC_DATABASE_CURRENT) - ok(mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cx == 0 && - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cy == 0, + ok(mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cx == 0 && + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cy == 0, "Expected zero height/width, got %ux%u\n", - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cx, - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cy); + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cx, + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cy); else - ok(mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cx > 0 && - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cy > 0, + ok(mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cx > 0 && + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cy > 0, "Expected non-zero height/width, got %ux%u\n", - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cx, - mi[pi[i].targetInfo.modeInfoIdx].targetMode.targetVideoSignalInfo.totalSize.cy); + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cx, + mi[tgt_mode_idx].targetMode.targetVideoSignalInfo.totalSize.cy); + if (flags & QDC_VIRTUAL_MODE_AWARE) + { + tgt_mode_idx = pi[i].targetInfo.desktopModeInfoIdx; + if (tgt_mode_idx == DISPLAYCONFIG_PATH_DESKTOP_IMAGE_IDX_INVALID) + { + skip("Path doesn't contain target desktopModeInfoIdx.\n"); + continue; + } + } + else + { + continue; + } + + ok(mi[tgt_mode_idx].infoType == DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE, "Expected infoType %d, got %d\n", + DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE, mi[tgt_mode_idx].infoType); + ok(pi[i].targetInfo.id == mi[tgt_mode_idx].id, "Expected id %u, got %u\n", + pi[i].targetInfo.id, mi[tgt_mode_idx].id); + ok(pi[i].targetInfo.adapterId.HighPart == mi[tgt_mode_idx].adapterId.HighPart && + pi[i].targetInfo.adapterId.LowPart == mi[tgt_mode_idx].adapterId.LowPart, + "Expected LUID %08lx:%08lx, got %08lx:%08lx\n", + pi[i].targetInfo.adapterId.HighPart, pi[i].targetInfo.adapterId.LowPart, + mi[tgt_mode_idx].adapterId.HighPart, mi[tgt_mode_idx].adapterId.LowPart); + di = &mi[tgt_mode_idx].desktopImageInfo; + ok(di->DesktopImageRegion.right > 0 && di->DesktopImageRegion.bottom > 0, + "Expected non-zero height/width, got %lux%lu\n", + di->DesktopImageRegion.right, + di->DesktopImageRegion.bottom); + ok(di->DesktopImageClip.right > 0 && di->DesktopImageClip.bottom > 0, + "Expected non-zero height/width, got %lux%lu\n", + di->DesktopImageClip.right, + di->DesktopImageClip.bottom); + ok(di->PathSourceSize.x > 0 && di->PathSourceSize.y > 0, + "Expected non-zero x/y, got %lux%lu\n", + di->PathSourceSize.x, + di->PathSourceSize.y); } } @@ -1946,6 +2015,17 @@ static void test_QueryDisplayConfig(void) ok(topologyid != 0xFF, "expected topologyid to be set, got %d\n", topologyid); if (!ret && paths > 0 && modes > 0) test_QueryDisplayConfig_result(QDC_DATABASE_CURRENT, paths, pi, modes, mi); + + paths = ARRAY_SIZE(pi); + modes = ARRAY_SIZE(mi); + memset(pi, 0xFF, sizeof(pi)); + memset(mi, 0xFF, sizeof(mi)); + ret = pQueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE, &paths, pi, &modes, mi, NULL); + todo_wine ok(!ret || broken(ret == ERROR_INVALID_PARAMETER) /* before Win10 */, "got %ld\n", ret); + if (!ret) + ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes); + if (!ret && paths > 0 && modes > 0) + test_QueryDisplayConfig_result(QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE, paths, pi, modes, mi); } static void test_DisplayConfigGetDeviceInfo(void) From 1a1f136e41406ada73fab95f88d5c471307f0d97 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 1 Oct 2024 16:27:47 -0600 Subject: [PATCH 63/67] win32u: Support QDC_VIRTUAL_MODE_AWARE in NtUserGetDisplayConfigBufferSizes(). CW-Bug-Id: #24333 --- dlls/user32/tests/monitor.c | 2 +- dlls/win32u/sysparams.c | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 0a80d694be4..54e02075c0e 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -1458,7 +1458,7 @@ static void test_GetDisplayConfigBufferSizes(void) paths = modes = 0; ret = pGetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE, &paths, &modes); - todo_wine ok(!ret || broken(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED) /* before Win10 */, "got %ld\n", ret); + ok(!ret || broken(ret == ERROR_INVALID_PARAMETER || ret == ERROR_NOT_SUPPORTED) /* before Win10 */, "got %ld\n", ret); if (!ret) ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes); } diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index e83e4b4f966..00cc2ed43cc 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -220,6 +220,8 @@ static const WCHAR guid_devinterface_monitorW[] = {'{','E','6','F','0','7','B','5','F','-','E','E','9','7','-','4','A','9','0','-', 'B','0','7','6','-','3','3','F','5','7','B','F','4','E','A','A','7','}',0}; +static const UINT32 qdc_retrieve_flags_mask = QDC_ALL_PATHS | QDC_ONLY_ACTIVE_PATHS | QDC_DATABASE_CURRENT; + #define NEXT_DEVMODEW(mode) ((DEVMODEW *)((char *)((mode) + 1) + (mode)->dmDriverExtra)) /* Cached display device information */ @@ -2442,7 +2444,7 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in skip_update = TRUE; } - switch (flags) + switch (flags & qdc_retrieve_flags_mask) { case QDC_ALL_PATHS: case QDC_ONLY_ACTIVE_PATHS: @@ -2452,8 +2454,14 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in return ERROR_INVALID_PARAMETER; } + if ((flags & ~(qdc_retrieve_flags_mask | QDC_VIRTUAL_MODE_AWARE))) + { + FIXME( "unsupported flags %#x.\n", flags ); + return ERROR_INVALID_PARAMETER; + } + /* FIXME: semi-stub */ - if (flags != QDC_ONLY_ACTIVE_PATHS) + if ((flags & qdc_retrieve_flags_mask) != QDC_ONLY_ACTIVE_PATHS) FIXME( "only returning active paths\n" ); /* NtUserGetDisplayConfigBufferSizes() is called by display drivers to trigger display settings update. */ @@ -2473,6 +2481,8 @@ LONG WINAPI NtUserGetDisplayConfigBufferSizes( UINT32 flags, UINT32 *num_path_in *num_path_info = count; *num_mode_info = count * 2; + if (flags & QDC_VIRTUAL_MODE_AWARE) + *num_mode_info += count; TRACE( "returning %u paths %u modes\n", *num_path_info, *num_mode_info ); return ERROR_SUCCESS; } From 5416bf697ab79fe415c76a0f9e3c907ebb550d51 Mon Sep 17 00:00:00 2001 From: Paul Gofman Date: Tue, 1 Oct 2024 16:33:13 -0600 Subject: [PATCH 64/67] win32u: Support QDC_VIRTUAL_MODE_AWARE in NtUserQueryDisplayConfig(). CW-Bug-Id: #24333 --- dlls/user32/tests/monitor.c | 2 +- dlls/win32u/sysparams.c | 93 +++++++++++++++++++++++++++---------- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index 54e02075c0e..795ea939691 100644 --- a/dlls/user32/tests/monitor.c +++ b/dlls/user32/tests/monitor.c @@ -2021,7 +2021,7 @@ static void test_QueryDisplayConfig(void) memset(pi, 0xFF, sizeof(pi)); memset(mi, 0xFF, sizeof(mi)); ret = pQueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE, &paths, pi, &modes, mi, NULL); - todo_wine ok(!ret || broken(ret == ERROR_INVALID_PARAMETER) /* before Win10 */, "got %ld\n", ret); + ok(!ret || broken(ret == ERROR_INVALID_PARAMETER) /* before Win10 */, "got %ld\n", ret); if (!ret) ok(paths > 0 && modes > 0, "got %u, %u\n", paths, modes); if (!ret && paths > 0 && modes > 0) diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c index 00cc2ed43cc..10ebf0c0c76 100644 --- a/dlls/win32u/sysparams.c +++ b/dlls/win32u/sysparams.c @@ -2546,11 +2546,17 @@ static void set_mode_target_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu } static void set_path_target_info( DISPLAYCONFIG_PATH_TARGET_INFO *info, const LUID *gpu_luid, - UINT32 target_id, UINT32 mode_index, const DEVMODEW *devmode ) + UINT32 target_id, UINT32 mode_index, UINT32 desktop_mode_index, + UINT32 flags, const DEVMODEW *devmode ) { info->adapterId = *gpu_luid; info->id = target_id; - info->modeInfoIdx = mode_index; + if (flags & QDC_VIRTUAL_MODE_AWARE) + { + info->targetModeInfoIdx = mode_index; + info->desktopModeInfoIdx = desktop_mode_index; + } + else info->modeInfoIdx = mode_index; info->outputTechnology = DISPLAYCONFIG_OUTPUT_TECHNOLOGY_DISPLAYPORT_EXTERNAL; info->rotation = get_dc_rotation( devmode ); info->scaling = DISPLAYCONFIG_SCALING_IDENTITY; @@ -2584,12 +2590,34 @@ static void set_mode_source_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu } } +static void set_mode_desktop_info( DISPLAYCONFIG_MODE_INFO *info, const LUID *gpu_luid, UINT32 target_id, + const DISPLAYCONFIG_SOURCE_MODE *source_mode ) +{ + DISPLAYCONFIG_DESKTOP_IMAGE_INFO *mode = &info->desktopImageInfo; + + info->infoType = DISPLAYCONFIG_MODE_INFO_TYPE_DESKTOP_IMAGE; + info->adapterId = *gpu_luid; + info->id = target_id; + mode->PathSourceSize.x = source_mode->width; + mode->PathSourceSize.y = source_mode->height; + mode->DesktopImageRegion.left = 0; + mode->DesktopImageRegion.top = 0; + mode->DesktopImageRegion.right = source_mode->width; + mode->DesktopImageRegion.bottom = source_mode->height; + mode->DesktopImageClip = mode->DesktopImageRegion; +} + static void set_path_source_info( DISPLAYCONFIG_PATH_SOURCE_INFO *info, const LUID *gpu_luid, - UINT32 source_id, UINT32 mode_index ) + UINT32 source_id, UINT32 mode_index, UINT32 flags ) { info->adapterId = *gpu_luid; info->id = source_id; - info->modeInfoIdx = mode_index; + if (flags & QDC_VIRTUAL_MODE_AWARE) + { + info->sourceModeInfoIdx = mode_index; + info->cloneGroupId = DISPLAYCONFIG_PATH_CLONE_GROUP_INVALID; + } + else info->modeInfoIdx = mode_index; info->statusFlags = DISPLAYCONFIG_SOURCE_IN_USE; } @@ -2623,8 +2651,9 @@ LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAY const LUID *gpu_luid; DEVMODEW devmode; struct monitor *monitor; + DWORD retrieve_flags = flags & qdc_retrieve_flags_mask; - FIXME( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p semi-stub\n", + TRACE( "flags %#x, paths_count %p, paths %p, modes_count %p, modes %p, topology_id %p.\n", flags, paths_count, paths, modes_count, modes, topology_id ); if (!paths_count || !modes_count) @@ -2633,16 +2662,22 @@ LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAY if (!*paths_count || !*modes_count) return ERROR_INVALID_PARAMETER; - if (flags != QDC_ALL_PATHS && - flags != QDC_ONLY_ACTIVE_PATHS && - flags != QDC_DATABASE_CURRENT) + if (retrieve_flags != QDC_ALL_PATHS && + retrieve_flags != QDC_ONLY_ACTIVE_PATHS && + retrieve_flags != QDC_DATABASE_CURRENT) return ERROR_INVALID_PARAMETER; - if (((flags == QDC_DATABASE_CURRENT) && !topology_id) || - ((flags != QDC_DATABASE_CURRENT) && topology_id)) + if (((retrieve_flags == QDC_DATABASE_CURRENT) && !topology_id) || + ((retrieve_flags != QDC_DATABASE_CURRENT) && topology_id)) return ERROR_INVALID_PARAMETER; - if (flags != QDC_ONLY_ACTIVE_PATHS) + if ((flags & ~(qdc_retrieve_flags_mask | QDC_VIRTUAL_MODE_AWARE))) + { + FIXME( "unsupported flags %#x.\n", flags ); + return ERROR_INVALID_PARAMETER; + } + + if (retrieve_flags != QDC_ONLY_ACTIVE_PATHS) FIXME( "only returning active paths\n" ); if (topology_id) @@ -2681,17 +2716,6 @@ LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAY goto done; } - paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE; - set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode ); - set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, &devmode ); - - mode_index++; - if (mode_index == *modes_count) - { - ret = ERROR_INSUFFICIENT_BUFFER; - goto done; - } - /* Multiple targets can be driven by the same source, ensure a mode * hasn't already been added for this source. */ @@ -2699,9 +2723,30 @@ LONG WINAPI NtUserQueryDisplayConfig( UINT32 flags, UINT32 *paths_count, DISPLAY { set_mode_source_info( &modes[mode_index], gpu_luid, adapter_index, &devmode ); source_mode_index = mode_index; - mode_index++; + if (++mode_index == *modes_count) + { + ret = ERROR_INSUFFICIENT_BUFFER; + goto done; + } } - set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index ); + + paths[path_index].flags = DISPLAYCONFIG_PATH_ACTIVE; + set_mode_target_info( &modes[mode_index], gpu_luid, output_id, flags, &devmode ); + if (flags & QDC_VIRTUAL_MODE_AWARE) + { + if (++mode_index == *modes_count) + { + ret = ERROR_INSUFFICIENT_BUFFER; + goto done; + } + set_mode_desktop_info( &modes[mode_index], gpu_luid, output_id, &modes[source_mode_index].sourceMode ); + set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index - 1, mode_index, + flags, &devmode ); + } + else set_path_target_info( &paths[path_index].targetInfo, gpu_luid, output_id, mode_index, ~0u, flags, &devmode ); + ++mode_index; + + set_path_source_info( &paths[path_index].sourceInfo, gpu_luid, adapter_index, source_mode_index, flags ); path_index++; } From f25c0d02d997b7ddde259091965eb1f0f5e8d75c Mon Sep 17 00:00:00 2001 From: Liam Middlebrook Date: Sat, 12 Oct 2024 15:15:30 -0700 Subject: [PATCH 65/67] gdi32: Implement D3DKMTEnumAdapters2 Signed-off-by: Liam Middlebrook Note: This commit is specifically targeted towards ValveSoftware/wine experimental_9.0 branch. Upstream wine contains commits which already implement this, but are part of a larger series of changes inter-twined with WoW64 and unification of GDI backends. --- dlls/gdi32/objects.c | 74 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/dlls/gdi32/objects.c b/dlls/gdi32/objects.c index bddc29a3007..093039c3509 100644 --- a/dlls/gdi32/objects.c +++ b/dlls/gdi32/objects.c @@ -971,10 +971,78 @@ NTSTATUS WINAPI D3DKMTOpenAdapterFromGdiDisplayName( D3DKMT_OPENADAPTERFROMGDIDI return status; } -NTSTATUS WINAPI D3DKMTEnumAdapters2( const void *param ) +NTSTATUS WINAPI D3DKMTEnumAdapters2( D3DKMT_ENUMADAPTERS2 *enumAdapters ) { - FIXME( "param %p stub.\n", param ); - return STATUS_NOT_SUPPORTED; + NTSTATUS status = STATUS_SUCCESS; + SP_DEVINFO_DATA device_data; + DEVPROPTYPE type; + HDEVINFO devinfo; + UINT dev_count = 0; + HANDLE mutex; + + TRACE("(%p)\n", enumAdapters); + + mutex = get_display_device_init_mutex(); + devinfo = SetupDiGetClassDevsW(&GUID_DEVCLASS_DISPLAY, L"PCI", NULL, 0); + device_data.cbSize = sizeof(device_data); + + while(SetupDiEnumDeviceInfo(devinfo, dev_count++, &device_data)) + { + D3DKMT_OPENADAPTERFROMLUID luid_desc; + UINT dev_idx = dev_count - 1; + D3DKMT_ADAPTERINFO *adapter; + + TRACE("Device: %u\n", dev_idx); + + /* If nothing to write, just pass through the loop */ + if (!enumAdapters->pAdapters) + continue; + + adapter = (D3DKMT_ADAPTERINFO*)(enumAdapters->pAdapters + dev_idx); + + if (SetupDiGetDevicePropertyW(devinfo, &device_data, &DEVPROPKEY_GPU_LUID, &type, + (BYTE *)&luid_desc.AdapterLuid, sizeof(luid_desc.AdapterLuid), NULL, 0)) + { + /* NumOfSources appears to be in reference to displays. This could mean connected + * displays, maximum number of "heads", surfaces for direct scanout, or something else + * entirely. It's not clear from the MSDN page what kind of value is actually expected + * here. + * + * bPrecisePresentRegionsPreferred sounds like a scanout-level optimization. Again, MSDN + * isn't very descriptive about what this really means. Given that it's typical for + * modern GPUs to scanout an entire surface at once, leave this falsey. + */ + adapter->NumOfSources = 0; + adapter->bPrecisePresentRegionsPreferred = FALSE; + FIXME("NumOfSources and bPrecisePresentRegionsPreferred not set, need implementation.\n"); + + if ((status = NtGdiDdDDIOpenAdapterFromLuid(&luid_desc))) + break; + + adapter->AdapterLuid = luid_desc.AdapterLuid; + adapter->hAdapter = luid_desc.hAdapter; + + TRACE("hAdapter: %u AdapterLuid: %08lx:%08lx NumOfSources: %lu bPrecisePresentRegionsPreferred: %d\n", + adapter->hAdapter, + adapter->AdapterLuid.HighPart, + adapter->AdapterLuid.LowPart, + adapter->NumOfSources, + adapter->bPrecisePresentRegionsPreferred); + } + else + { + TRACE("no known adapter\n"); + } + } + /* decrement dev count to actual count */ + dev_count--; + SetupDiDestroyDeviceInfoList(devinfo); + release_display_device_init_mutex(mutex); + + TRACE("Devices enumerated: %u\n", dev_count); + enumAdapters->NumAdapters = dev_count; + + return status; } /*********************************************************************** From 23d772f57155b90ae886845f34282467c4478579 Mon Sep 17 00:00:00 2001 From: Liam Middlebrook Date: Sat, 12 Oct 2024 15:31:38 -0700 Subject: [PATCH 66/67] win32u: Move D3DKMTQueryAdapterInfo into gdi_dc_funcs Signed-off-by: Liam Middlebrook Note: This commit is specifically targeted towards ValveSoftware/wine experimental_9.0 branch. Upstream wine contains commits which already implement this, but are part of a larger series of changes inter-twined with WoW64 and unification of GDI backends. --- dlls/win32u/dibdrv/dc.c | 2 ++ dlls/win32u/driver.c | 15 ++++++++++++--- dlls/win32u/emfdrv.c | 1 + dlls/win32u/font.c | 1 + dlls/win32u/path.c | 1 + dlls/winex11.drv/init.c | 1 + dlls/winex11.drv/x11drv.h | 1 + dlls/winex11.drv/x11drv_main.c | 19 +++++++++++++++++++ dlls/winex11.drv/xrender.c | 1 + include/wine/gdi_driver.h | 1 + 10 files changed, 40 insertions(+), 3 deletions(-) diff --git a/dlls/win32u/dibdrv/dc.c b/dlls/win32u/dibdrv/dc.c index 7fe4b765a78..737c28adc8a 100644 --- a/dlls/win32u/dibdrv/dc.c +++ b/dlls/win32u/dibdrv/dc.c @@ -710,6 +710,7 @@ const struct gdi_dc_funcs dib_driver = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_DIB_DRV /* priority */ @@ -1271,6 +1272,7 @@ static const struct gdi_dc_funcs window_driver = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_DIB_DRV + 10 /* priority */ diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c index bc3409a9e34..ccfe684b28e 100644 --- a/dlls/win32u/driver.c +++ b/dlls/win32u/driver.c @@ -554,6 +554,11 @@ static NTSTATUS nulldrv_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *d return STATUS_PROCEDURE_NOT_FOUND; } +static NTSTATUS nulldrv_D3DKMTQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ) +{ + return STATUS_PROCEDURE_NOT_FOUND; +} + static NTSTATUS nulldrv_D3DKMTQueryVideoMemoryInfo( D3DKMT_QUERYVIDEOMEMORYINFO *desc ) { return STATUS_PROCEDURE_NOT_FOUND; @@ -658,6 +663,7 @@ const struct gdi_dc_funcs null_driver = nulldrv_D3DKMTCheckVidPnExclusiveOwnership, /* pD3DKMTCheckVidPnExclusiveOwnership */ nulldrv_D3DKMTCloseAdapter, /* pD3DKMTCloseAdapter */ nulldrv_D3DKMTOpenAdapterFromLuid, /* pD3DKMTOpenAdapterFromLuid */ + nulldrv_D3DKMTQueryAdapterInfo, /* pD3DKMTQueryAdapterInfo */ nulldrv_D3DKMTQueryVideoMemoryInfo, /* pD3DKMTQueryVideoMemoryInfo */ nulldrv_D3DKMTSetVidPnSourceOwner, /* pD3DKMTSetVidPnSourceOwner */ @@ -1637,11 +1643,14 @@ NTSTATUS WINAPI NtGdiDdDDIDestroyDevice( const D3DKMT_DESTROYDEVICE *desc ) */ NTSTATUS WINAPI NtGdiDdDDIQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ) { - if (!desc) + TRACE("(%p)\n", desc); + + if (!desc || !desc->hAdapter) return STATUS_INVALID_PARAMETER; - FIXME("desc %p, type %d stub\n", desc, desc->Type); - return STATUS_NOT_IMPLEMENTED; + if (!get_display_driver()->pD3DKMTQueryAdapterInfo) + return STATUS_PROCEDURE_NOT_FOUND; + return get_display_driver()->pD3DKMTQueryAdapterInfo(desc); } /****************************************************************************** diff --git a/dlls/win32u/emfdrv.c b/dlls/win32u/emfdrv.c index 069ad9d1297..28bc47772d4 100644 --- a/dlls/win32u/emfdrv.c +++ b/dlls/win32u/emfdrv.c @@ -522,6 +522,7 @@ static const struct gdi_dc_funcs emfdrv_driver = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_GRAPHICS_DRV /* priority */ diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index a7739e64da2..eebb687b935 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -4814,6 +4814,7 @@ const struct gdi_dc_funcs font_driver = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_FONT_DRV /* priority */ diff --git a/dlls/win32u/path.c b/dlls/win32u/path.c index e0c96f5ef6f..2783cd51832 100644 --- a/dlls/win32u/path.c +++ b/dlls/win32u/path.c @@ -2121,6 +2121,7 @@ const struct gdi_dc_funcs path_driver = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_PATH_DRV /* priority */ diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c index 5576fdd50e6..9bce0740fd3 100644 --- a/dlls/winex11.drv/init.c +++ b/dlls/winex11.drv/init.c @@ -389,6 +389,7 @@ static const struct user_driver_funcs x11drv_funcs = .dc_funcs.pD3DKMTCheckVidPnExclusiveOwnership = X11DRV_D3DKMTCheckVidPnExclusiveOwnership, .dc_funcs.pD3DKMTCloseAdapter = X11DRV_D3DKMTCloseAdapter, .dc_funcs.pD3DKMTOpenAdapterFromLuid = X11DRV_D3DKMTOpenAdapterFromLuid, + .dc_funcs.pD3DKMTQueryAdapterInfo = X11DRV_D3DKMTQueryAdapterInfo, .dc_funcs.pD3DKMTQueryVideoMemoryInfo = X11DRV_D3DKMTQueryVideoMemoryInfo, .dc_funcs.pD3DKMTSetVidPnSourceOwner = X11DRV_D3DKMTSetVidPnSourceOwner, .dc_funcs.priority = GDI_PRIORITY_GRAPHICS_DRV, diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h index 4ebc9f5ca59..d65e68b89ae 100644 --- a/dlls/winex11.drv/x11drv.h +++ b/dlls/winex11.drv/x11drv.h @@ -163,6 +163,7 @@ extern BOOL X11DRV_Chord( PHYSDEV dev, INT left, INT top, INT right, INT bottom, extern NTSTATUS X11DRV_D3DKMTCheckVidPnExclusiveOwnership( const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *desc ); extern NTSTATUS X11DRV_D3DKMTCloseAdapter( const D3DKMT_CLOSEADAPTER *desc ); extern NTSTATUS X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *desc ); +extern NTSTATUS X11DRV_D3DKMTQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ); extern NTSTATUS X11DRV_D3DKMTQueryVideoMemoryInfo( D3DKMT_QUERYVIDEOMEMORYINFO *desc ); extern NTSTATUS X11DRV_D3DKMTSetVidPnSourceOwner( const D3DKMT_SETVIDPNSOURCEOWNER *desc ); extern BOOL X11DRV_Ellipse( PHYSDEV dev, INT left, INT top, INT right, INT bottom ); diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index 6a878cbf98c..d5461e1124e 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -1399,6 +1399,25 @@ NTSTATUS X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *desc ) return status; } +NTSTATUS X11DRV_D3DKMTQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ) +{ + NTSTATUS status = STATUS_INVALID_PARAMETER; + struct x11_d3dkmt_adapter *adapter; + + pthread_mutex_lock(&d3dkmt_mutex); + LIST_FOR_EACH_ENTRY(adapter, &x11_d3dkmt_adapters, struct x11_d3dkmt_adapter, entry) + { + if (adapter->handle != desc->hAdapter) + continue; + + FIXME("desc %p, type %d stub\n", desc, desc->Type); + status = STATUS_NOT_IMPLEMENTED; + break; + } + pthread_mutex_unlock(&d3dkmt_mutex); + return status; +} + NTSTATUS X11DRV_D3DKMTQueryVideoMemoryInfo( D3DKMT_QUERYVIDEOMEMORYINFO *desc ) { const struct vulkan_funcs *vulkan_funcs = get_vulkan_driver(WINE_VULKAN_DRIVER_VERSION); diff --git a/dlls/winex11.drv/xrender.c b/dlls/winex11.drv/xrender.c index 7656c0637df..c69c260138f 100644 --- a/dlls/winex11.drv/xrender.c +++ b/dlls/winex11.drv/xrender.c @@ -2434,6 +2434,7 @@ static const struct gdi_dc_funcs xrender_funcs = NULL, /* pD3DKMTCheckVidPnExclusiveOwnership */ NULL, /* pD3DKMTCloseAdapter */ NULL, /* pD3DKMTOpenAdapterFromLuid */ + NULL, /* pD3DKMTQueryAdapterInfo */ NULL, /* pD3DKMTQueryVideoMemoryInfo */ NULL, /* pD3DKMTSetVidPnSourceOwner */ GDI_PRIORITY_GRAPHICS_DRV + 10 /* priority */ diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h index 26562bfef2b..bd1ae446f73 100644 --- a/include/wine/gdi_driver.h +++ b/include/wine/gdi_driver.h @@ -169,6 +169,7 @@ struct gdi_dc_funcs NTSTATUS (*pD3DKMTCheckVidPnExclusiveOwnership)(const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *); NTSTATUS (*pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *); NTSTATUS (*pD3DKMTOpenAdapterFromLuid)(D3DKMT_OPENADAPTERFROMLUID *); + NTSTATUS (*pD3DKMTQueryAdapterInfo)(D3DKMT_QUERYADAPTERINFO *); NTSTATUS (*pD3DKMTQueryVideoMemoryInfo)(D3DKMT_QUERYVIDEOMEMORYINFO *); NTSTATUS (*pD3DKMTSetVidPnSourceOwner)(const D3DKMT_SETVIDPNSOURCEOWNER *); From b239fbc3f17f4e464c634064d15037471ea0985c Mon Sep 17 00:00:00 2001 From: Liam Middlebrook Date: Sat, 12 Oct 2024 15:35:53 -0700 Subject: [PATCH 67/67] winex11.drv: Implement D3DKMTQueryAdapterInfo WDDM_2_7_CAPS Used by applications to query support for "Hardware Scheduling" for GPU workloads. This is used as a proxy to determine scheduling guarantees between userspace command submission and GPU execution. Signed-off-by: Liam Middlebrook Note: This commit is specifically targeted towards ValveSoftware/wine experimental_9.0 branch. Upstream wine contains commits which change how this would be implemented. Notably, the changes in `dlls/winex11.drv/x11drv_main.c` should be instead made in `dlls/win32u/d3dkmt.c`. --- dlls/winex11.drv/x11drv_main.c | 53 ++++++++++++++++++++++++++++++++++ include/ddk/d3dkmthk.h | 14 +++++++++ 2 files changed, 67 insertions(+) diff --git a/dlls/winex11.drv/x11drv_main.c b/dlls/winex11.drv/x11drv_main.c index d5461e1124e..4ee4a372367 100644 --- a/dlls/winex11.drv/x11drv_main.c +++ b/dlls/winex11.drv/x11drv_main.c @@ -1401,19 +1401,72 @@ NTSTATUS X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *desc ) NTSTATUS X11DRV_D3DKMTQueryAdapterInfo( D3DKMT_QUERYADAPTERINFO *desc ) { + const struct vulkan_funcs *vulkan_funcs = get_vulkan_driver(WINE_VULKAN_DRIVER_VERSION); + PFN_vkGetPhysicalDeviceProperties2KHR pvkGetPhysicalDeviceProperties2KHR; + VkPhysicalDeviceDriverPropertiesKHR driverProperties; + VkPhysicalDeviceProperties2KHR properties2; NTSTATUS status = STATUS_INVALID_PARAMETER; struct x11_d3dkmt_adapter *adapter; + if (!vulkan_funcs) + { + WARN("Vulkan is unavailable.\n"); + return STATUS_UNSUCCESSFUL; + } + pthread_mutex_lock(&d3dkmt_mutex); LIST_FOR_EACH_ENTRY(adapter, &x11_d3dkmt_adapters, struct x11_d3dkmt_adapter, entry) { if (adapter->handle != desc->hAdapter) continue; + if (!(pvkGetPhysicalDeviceProperties2KHR = (void *)vulkan_funcs->p_vkGetInstanceProcAddr(d3dkmt_vk_instance, "vkGetPhysicalDeviceProperties2KHR"))) + { + WARN("Failed to load vkGetPhysicalDeviceProperties2KHR.\n"); + status = STATUS_UNSUCCESSFUL; + goto done; + } + + memset(&driverProperties, 0, sizeof(driverProperties)); + memset(&properties2, 0, sizeof(properties2)); + driverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR; + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR; + properties2.pNext = &driverProperties; + pvkGetPhysicalDeviceProperties2KHR(adapter->vk_device, &properties2); + + if (desc->Type == KMTQAITYPE_WDDM_2_7_CAPS) + { + /* + * Advertise Hardware-Scheduling as enabled for NVIDIA Adapters. NVIDIA driver does + * userspace submission. Allow overriding this value via the + * WINE_DISABLE_HARDWARE_SCHEDULING environment variable. + */ + D3DKMT_WDDM_2_7_CAPS *data = desc->pPrivateDriverData; + const char *e = getenv("WINE_DISABLE_HARDWARE_SCHEDULING"); + if ((!e || *e == '\0' || *e == '0') && + (driverProperties.driverID == VK_DRIVER_ID_NVIDIA_PROPRIETARY)) + { + data->HwSchEnabled = 1; + data->HwSchSupported = 1; + data->HwSchEnabledByDefault = 1; + status = STATUS_SUCCESS; + goto done; + } + else + { + data->HwSchEnabled = 0; + data->HwSchSupported = 0; + data->HwSchEnabledByDefault = 0; + status = STATUS_SUCCESS; + goto done; + } + } + FIXME("desc %p, type %d stub\n", desc, desc->Type); status = STATUS_NOT_IMPLEMENTED; break; } +done: pthread_mutex_unlock(&d3dkmt_mutex); return status; } diff --git a/include/ddk/d3dkmthk.h b/include/ddk/d3dkmthk.h index 2d30bdd8777..d4d4f88a605 100644 --- a/include/ddk/d3dkmthk.h +++ b/include/ddk/d3dkmthk.h @@ -266,6 +266,20 @@ typedef struct _D3DKMT_QUERYADAPTERINFO UINT PrivateDriverDataSize; } D3DKMT_QUERYADAPTERINFO; +typedef struct _D3DKMT_WDDM_2_7_CAPS +{ + union { + struct { + UINT HwSchSupported : 1; + UINT HwSchEnabled : 1; + UINT HwSchEnabledByDefault : 1; + UINT IndependentVidPnVSyncControl : 1; + UINT Reserved : 28; + }; + UINT Value; + }; +} D3DKMT_WDDM_2_7_CAPS; + typedef enum _D3DKMT_QUERYRESULT_PREEMPTION_ATTEMPT_RESULT { D3DKMT_PreemptionAttempt = 0,