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", }; 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/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(); 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); 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; } /*********************************************************************** diff --git a/dlls/kernel32/tests/module.c b/dlls/kernel32/tests/module.c index 7dd1125a0a6..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; @@ -1627,6 +1655,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; + + 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); + + 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); +} + #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 ) { @@ -1719,6 +1794,148 @@ 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 ) +{ + 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; + char *base; + + 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, + "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]; @@ -1755,5 +1972,7 @@ START_TEST(module) test_LdrGetDllFullName(); test_apisets(); test_ddag_node(); + test_tls_links(); test_known_dlls_load(); + test_base_address_index_tree(); } 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; 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; 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 3c55c42c6d0..d0712017e5a 100644 --- a/dlls/ntdll/loader.c +++ b/dlls/ntdll/loader.c @@ -145,9 +145,9 @@ 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 }; +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 = @@ -184,6 +184,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; @@ -268,6 +270,38 @@ 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; +} + +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, @@ -575,6 +609,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 * @@ -610,19 +655,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); } @@ -1327,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 * @@ -1336,9 +1406,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; - LIST_ENTRY *entry; + UINT old_module_count = tls_module_count; + PROCESS_TLS_INFORMATION *t; if (!(dir = RtlImageDirectoryEntryToData( mod->DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_TLS, &size ))) return FALSE; @@ -1359,54 +1430,74 @@ 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 */ - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) - { - TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); - void **old = teb->ThreadLocalStoragePointer; - void **new = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(*new)); - - if (!new) return FALSE; - if (old) memcpy( new, old, tls_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 */ - } - tls_dirs = new_ptr; tls_module_count = new_count; } + *(DWORD *)dir->AddressOfIndex = i; + tls_dirs[i] = *dir; + + if (!tls_thread_count) return TRUE; + t = RtlAllocateHeap( GetProcessHeap(), 0, offsetof( PROCESS_TLS_INFORMATION, ThreadData[tls_thread_count] )); + if (!t) return FALSE; - /* allocate the data block in all running threads */ - for (entry = tls_links.Flink; entry != &tls_links; entry = entry->Flink) + 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) { - TEB *teb = CONTAINING_RECORD( entry, TEB, TlsLinks ); + void **vector; - if (!(new_ptr = RtlAllocateHeap( GetProcessHeap(), 0, size + dir->SizeOfZeroFill ))) return -1; + t->ThreadData[j].Flags = 0; + + 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 ); - 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 (t->OperationType == ProcessTlsReplaceVector) + { + vector = alloc_tls_memory( TRUE, tls_module_count * sizeof(*vector) ); + if (!vector) return FALSE; + t->ThreadData[j].TlsVector = vector; + vector[i] = new_ptr; + } + 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; } - *(DWORD *)dir->AddressOfIndex = i; - tls_dirs[i] = *dir; + for (j = 0; j < tls_thread_count; ++j) + { + if (!(t->ThreadData[j].Flags & THREAD_TLS_INFORMATION_ASSIGNED) && t->OperationType == ProcessTlsReplaceVector) + { + /* 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. */ + 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. */ + free_tls_memory( t->ThreadData[j].TlsVector ); + } + } + RtlFreeHeap( GetProcessHeap(), 0, t ); return TRUE; } @@ -1584,6 +1675,9 @@ 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" ); + register_module_exception_directory( hModule ); /* wait until init is called for inserting into InInitializationOrderModuleList */ wm->ldr.InInitializationOrderLinks.Flink = NULL; @@ -1609,10 +1703,7 @@ 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) ))) + if (!(pointers = alloc_tls_memory( TRUE, tls_module_count * sizeof(*pointers) ))) return STATUS_NO_MEMORY; for (i = 0; i < tls_module_count; i++) @@ -1623,10 +1714,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 ); @@ -1634,6 +1725,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]) @@ -1910,6 +2002,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.@) * @@ -1917,21 +2020,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->InLoadOrderModuleList; - for (entry = mark->Blink; entry != mark; entry = entry->Blink) - { - mod = CONTAINING_RECORD(entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); - 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; } /****************************************************************** @@ -2347,6 +2441,8 @@ 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 ); + unregister_module_exception_directory( wm->ldr.DllBase ); /* FIXME: there are several more dangling references * left. Including dlls loaded by this dll before the @@ -3315,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; @@ -3349,6 +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 (!fullname && rb_get( &known_dlls, libname )) { @@ -3392,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 ); } } } @@ -3439,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; } @@ -3649,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 @@ -4077,11 +4179,16 @@ 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)) { - for (i = 0; i < tls_module_count; i++) RtlFreeHeap( GetProcessHeap(), 0, pointers[i] ); - RtlFreeHeap( GetProcessHeap(), 0, pointers ); + NtCurrentTeb()->ThreadLocalStoragePointer = NULL; + --tls_thread_count; +#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++) free_tls_memory( pointers[i] ); + free_tls_memory( pointers ); } RtlProcessFlsData( NtCurrentTeb()->FlsSlots, 2 ); NtCurrentTeb()->FlsSlots = NULL; @@ -4110,8 +4217,10 @@ 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); + unregister_module_exception_directory( wm->ldr.DllBase ); while ((entry = wm->ldr.DdagNode->Dependencies.Tail)) { @@ -4417,10 +4526,6 @@ static void init_wow64( CONTEXT *context ) imports_fixup_done = TRUE; } - RtlAcquirePebLock(); - InsertHeadList( &tls_links, &NtCurrentTeb()->TlsLinks ); - RtlReleasePebLock(); - RtlLeaveCriticalSection( &loader_section ); pWow64LdrpInitialize( context ); } @@ -4556,6 +4661,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] ); @@ -4600,10 +4708,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 */ diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index e33e85d5ca6..af9c1048b46 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -946,6 +946,8 @@ @ stdcall RtlRaiseStatus(long) @ 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/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/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c index 0d0b2d370f3..c08e7598344 100644 --- a/dlls/ntdll/rtl.c +++ b/dlls/ntdll/rtl.c @@ -2124,6 +2124,180 @@ 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; +} + +/********************************************************************* + * 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/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/exception.c b/dlls/ntdll/tests/exception.c index 7838391d41e..982486d0c1d 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 ); + 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 ); + 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 ); + 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; + 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; + 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; + 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 ); + 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; + 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 ); + 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" ); + 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 ); + 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; + 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; + } + 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; + 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; + } + 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; + 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; + } + 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__) diff --git a/dlls/ntdll/tests/info.c b/dlls/ntdll/tests/info.c index 881b79d3dc9..2f33681d64b 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) @@ -3963,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; @@ -4041,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/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/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(); } diff --git a/dlls/ntdll/tests/unwind.c b/dlls/ntdll/tests/unwind.c new file mode 100644 index 00000000000..a775999410a --- /dev/null +++ b/dlls/ntdll/tests/unwind.c @@ -0,0 +1,3268 @@ +/* + * 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 ); + } +} + +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) +{ + 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 + test_exception_directory(); +} + +#else /* !__i386__ */ + +START_TEST(unwind) +{ +} + +#endif /* !__i386__ */ 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) ); 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"); diff --git a/dlls/ntdll/unix/process.c b/dlls/ntdll/unix/process.c index 3f05fe9be2e..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(); @@ -1674,11 +1701,18 @@ NTSTATUS WINAPI NtSetInformationProcess( HANDLE handle, PROCESSINFOCLASS class, case ProcessInstrumentationCallback: { PROCESS_INSTRUMENTATION_CALLBACK_INFORMATION *instr = info; + void *callback; - FIXME( "ProcessInstrumentationCallback stub.\n" ); - - 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( 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..c76a08c5237 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; @@ -1433,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) { @@ -1488,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 ); } @@ -1638,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 */ /*********************************************************************** @@ -1839,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 */ @@ -2237,7 +2269,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 +2296,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); @@ -2867,10 +2901,12 @@ 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; assert( thread_data->xstate_features_size == xstate_features_size ); + thread_data->instrumentation_callback = &instrumentation_callback; #if defined __linux__ arch_prctl( ARCH_SET_GS, teb ); @@ -2956,6 +2992,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 ); @@ -3195,8 +3236,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 +3270,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 +3282,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 +3310,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/socket.c b/dlls/ntdll/unix/socket.c index 329da8cd4fd..9fb0f82042e 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; } @@ -1139,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; @@ -1148,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 ); @@ -1170,15 +1173,69 @@ 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 * 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->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; + 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, + SERVER_SOCKET_IO_FORCE_ASYNC | SERVER_SOCKET_IO_SYSTEM ); + 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) @@ -1191,7 +1248,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; @@ -1231,13 +1291,14 @@ 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; 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, @@ -1249,6 +1310,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; @@ -1258,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 ); } @@ -1422,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/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 2b62d5c7754..82fc9d2bc02 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 ); @@ -284,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..3c076689be2 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -4308,6 +4308,98 @@ 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 ); +#ifdef __x86_64__ /* macOS-specific hack */ + if (teb->Instrumentation[0]) ((TEB *)teb->Instrumentation[0])->ThreadLocalStoragePointer = ptr; +#endif + } + 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/user32/button.c b/dlls/user32/button.c index 10a111562a7..1d501cdc3d3 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 ) @@ -375,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 ); 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: diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c index e93a84242c4..795ea939691 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); + 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); + 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) 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; 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/input.c b/dlls/win32u/input.c index d67f462359c..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 */ -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, DWORD new_active_thread_id ) { HWND previous = get_active_window(); BOOL ret = FALSE; @@ -1934,10 +1934,11 @@ static 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 cf7909452ef..5076513ca62 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, lparam )) return 0; + return (LRESULT)prev; + } case WM_WINE_KEYBOARD_LL_HOOK: case WM_WINE_MOUSE_LL_HOOK: { 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/win32u/sysparams.c b/dlls/win32u/sysparams.c index e83e4b4f966..10ebf0c0c76 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; } @@ -2536,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; @@ -2574,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; } @@ -2613,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) @@ -2623,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 (((retrieve_flags == QDC_DATABASE_CURRENT) && !topology_id) || + ((retrieve_flags != QDC_DATABASE_CURRENT) && topology_id)) return ERROR_INVALID_PARAMETER; - if (((flags == QDC_DATABASE_CURRENT) && !topology_id) || - ((flags != QDC_DATABASE_CURRENT) && topology_id)) + if ((flags & ~(qdc_retrieve_flags_mask | QDC_VIRTUAL_MODE_AWARE))) + { + FIXME( "unsupported flags %#x.\n", flags ); return ERROR_INVALID_PARAMETER; + } - if (flags != QDC_ONLY_ACTIVE_PATHS) + if (retrieve_flags != QDC_ONLY_ACTIVE_PATHS) FIXME( "only returning active paths\n" ); if (topology_id) @@ -2671,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. */ @@ -2689,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++; } diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h index dc65ca858cb..95d17733dd8 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, 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 ); diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 4194df3a3dd..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.285" +VK_XML_VERSION = "1.3.296" 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 @@ -2488,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: 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index c40309ee468..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); @@ -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); 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/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..b8075aeb5a9 100644 --- a/dlls/winex11.drv/window.c +++ b/dlls/winex11.drv/window.c @@ -1927,6 +1927,8 @@ 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; @@ -1936,6 +1938,8 @@ void detach_client_window( struct x11drv_win_data *data, Window client_window, B 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 ); } @@ -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; @@ -1956,8 +1962,11 @@ void attach_client_window( struct x11drv_win_data *data, Window client_window ) XSelectInput( data->display, client_window, ExposureMask ); 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 ); + 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..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 ); @@ -530,6 +531,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..4ee4a372367 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", @@ -1398,6 +1399,78 @@ NTSTATUS X11DRV_D3DKMTOpenAdapterFromLuid( D3DKMT_OPENADAPTERFROMLUID *desc ) return status; } +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; +} + 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/dlls/winhttp/session.c b/dlls/winhttp/session.c index 978bf28bc37..a01f5df26d3 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 ); - if (buffer && *buflen > len) + 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 ) @@ -731,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; @@ -868,20 +903,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; @@ -912,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 5a29cd0a656..81bfe68e934 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,39 @@ 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)); + + /* Exact buffer size match. */ + url[0] = 0; + size = (lstrlenW(expected) + 1) * sizeof(WCHAR); + 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, @@ -4358,12 +4386,24 @@ 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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 +4413,51 @@ 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); + ok(ret, "failed to query proxy username %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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 +4467,27 @@ 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); + ok(ret, "failed to query proxy password %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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 +4497,27 @@ 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); + ok(ret, "failed to query username %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + ok(size == (lstrlenW(userW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_USERNAME, NULL, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + 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 +4527,27 @@ 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); + ok(ret, "failed to query password %lu\n", GetLastError()); + 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); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + ok(*buffer == 0x1, "unexpected result %s\n", wine_dbgstr_w(buffer)); + ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "unexpected result %lu\n", size); + + size = 0; + SetLastError(0xdeadbeef); + ret = WinHttpQueryOption(req, WINHTTP_OPTION_PASSWORD, NULL, &size); + ok(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "Unexpected error %lu\n", GetLastError()); + ok(size == (lstrlenW(passW) + 1) * sizeof(WCHAR), "Unexpected size %lu\n", size); + WinHttpCloseHandle(req); req = WinHttpOpenRequest(con, NULL, NULL, NULL, NULL, NULL, 0); @@ -5532,7 +5668,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, @@ -5566,11 +5703,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); @@ -5980,9 +6129,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()); diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c index 817926d0f13..ce56677c39f 100644 --- a/dlls/wow64/process.c +++ b/dlls/wow64/process.c @@ -843,8 +843,41 @@ 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(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..28d9441a5fc 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; @@ -333,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/dlls/ws2_32/tests/sock.c b/dlls/ws2_32/tests/sock.c index c390b186956..5d598095f03 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) + { + 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); + 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) + { + 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(). 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, 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/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 *); 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 diff --git a/include/winternl.h b/include/winternl.h index d151719f898..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; @@ -4920,6 +4955,8 @@ 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 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); diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 855351ce2de..fd5d5137d3e 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" @@ -2906,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 diff --git a/server/async.c b/server/async.c index fa21285bc05..1f32d10a0f5 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; @@ -810,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 ); } } 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 );