diff options
Diffstat (limited to '')
140 files changed, 6564 insertions, 3168 deletions
diff --git a/.ci/scripts/windows/docker.sh b/.ci/scripts/windows/docker.sh index 0be3613aa..45f75c874 100755 --- a/.ci/scripts/windows/docker.sh +++ b/.ci/scripts/windows/docker.sh @@ -56,7 +56,6 @@ for i in package/*.exe; do x86_64-w64-mingw32-strip "${i}" done -pip3 install pefile python3 .ci/scripts/windows/scan_dll.py package/*.exe package/imageformats/*.dll "package/" # copy FFmpeg libraries diff --git a/.ci/scripts/windows/install-vulkan-sdk.ps1 b/.ci/scripts/windows/install-vulkan-sdk.ps1 new file mode 100644 index 000000000..de218d90a --- /dev/null +++ b/.ci/scripts/windows/install-vulkan-sdk.ps1 @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: 2023 yuzu Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +$ErrorActionPreference = "Stop" + +$VulkanSDKVer = "1.3.250.1" +$ExeFile = "VulkanSDK-$VulkanSDKVer-Installer.exe" +$Uri = "https://sdk.lunarg.com/sdk/download/$VulkanSDKVer/windows/$ExeFile" +$Destination = "./$ExeFile" + +echo "Downloading Vulkan SDK $VulkanSDKVer from $Uri" +$WebClient = New-Object System.Net.WebClient +$WebClient.DownloadFile($Uri, $Destination) +echo "Finished downloading $ExeFile" + +$VULKAN_SDK = "C:/VulkanSDK/$VulkanSDKVer" +$Arguments = "--root `"$VULKAN_SDK`" --accept-licenses --default-answer --confirm-command install" + +echo "Installing Vulkan SDK $VulkanSDKVer" +$InstallProcess = Start-Process -FilePath $Destination -NoNewWindow -PassThru -Wait -ArgumentList $Arguments +$ExitCode = $InstallProcess.ExitCode + +if ($ExitCode -ne 0) { + echo "Error installing Vulkan SDK $VulkanSDKVer (Error: $ExitCode)" + Exit $ExitCode +} + +echo "Finished installing Vulkan SDK $VulkanSDKVer" + +if ("$env:GITHUB_ACTIONS" -eq "true") { + echo "VULKAN_SDK=$VULKAN_SDK" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append + echo "$VULKAN_SDK/Bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append +} diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml index ceb7e0c32..1e259df05 100644 --- a/.ci/templates/build-msvc.yml +++ b/.ci/templates/build-msvc.yml @@ -7,9 +7,12 @@ parameters: version: '' steps: -- script: choco install vulkan-sdk - displayName: 'Install vulkan-sdk' -- script: refreshenv && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw /GA /Gr /Ob2" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_ENABLE_LTO=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. +- task: Powershell@2 + displayName: 'Install Vulkan SDK' + inputs: + targetType: 'filePath' + filePath: './.ci/scripts/windows/install-vulkan-sdk.ps1' +- script: refreshenv && glslangValidator --version && mkdir build && cd build && cmake -E env CXXFLAGS="/Gw" cmake -G "Visual Studio 17 2022" -A x64 -DCMAKE_POLICY_DEFAULT_CMP0069=NEW -DYUZU_ENABLE_LTO=ON -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DYUZU_TESTS=OFF -DUSE_DISCORD_PRESENCE=ON -DENABLE_QT_TRANSLATION=ON -DDISPLAY_VERSION=${{ parameters['version'] }} -DCMAKE_BUILD_TYPE=Release -DYUZU_CRASH_DUMPS=ON .. && cd .. displayName: 'Configure CMake' - task: MSBuild@1 displayName: 'Build' diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml index e639e965a..5893f860e 100644 --- a/.github/workflows/android-build.yml +++ b/.github/workflows/android-build.yml @@ -10,6 +10,7 @@ on: jobs: android: runs-on: ubuntu-latest + if: ${{ github.repository == 'yuzu-emu/yuzu-android' }} steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index b5d338199..cbe6b0fbd 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -73,6 +73,10 @@ jobs: needs: format runs-on: windows-2022 steps: + - uses: actions/checkout@v3 + with: + submodules: recursive + fetch-depth: 0 - name: Set up cache uses: actions/cache@v3 with: @@ -81,22 +85,22 @@ jobs: restore-keys: | ${{ runner.os }}-msvc- - name: Install dependencies - # due to how chocolatey works, only cmd.exe is supported here - shell: cmd + shell: pwsh run: | - choco install vulkan-sdk wget - call refreshenv - wget https://github.com/mbitsnbites/buildcache/releases/download/v0.27.6/buildcache-windows.zip - 7z x buildcache-windows.zip - copy buildcache\bin\buildcache.exe C:\ProgramData\chocolatey\bin - rmdir buildcache - echo %PATH% >> %GITHUB_PATH% + $ErrorActionPreference = "Stop" + $BuildCacheVer = "v0.28.4" + $File = "buildcache-windows.zip" + $Uri = "https://github.com/mbitsnbites/buildcache/releases/download/$BuildCacheVer/$File" + $WebClient = New-Object System.Net.WebClient + $WebClient.DownloadFile($Uri, $File) + 7z x $File + $CurrentDir = Convert-Path . + echo "$CurrentDir/buildcache/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + - name: Install Vulkan SDK + shell: pwsh + run: .\.ci\scripts\windows\install-vulkan-sdk.ps1 - name: Set up MSVC uses: ilammy/msvc-dev-cmd@v1 - - uses: actions/checkout@v3 - with: - submodules: recursive - fetch-depth: 0 - name: Configure env: CC: cl.exe diff --git a/.gitmodules b/.gitmodules index 9f96b70be..361f4845b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -56,5 +56,5 @@ path = externals/nx_tzdb/tzdb_to_nx url = https://github.com/lat9nq/tzdb_to_nx.git [submodule "VulkanMemoryAllocator"] - path = externals/vma/VulkanMemoryAllocator + path = externals/VulkanMemoryAllocator url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 26e93b038..00d540f1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -63,6 +63,18 @@ option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF) CMAKE_DEPENDENT_OPTION(YUZU_USE_FASTER_LD "Check if a faster linker is available" ON "NOT WIN32" OFF) +set(DEFAULT_ENABLE_OPENSSL ON) +if (ANDROID OR WIN32 OR APPLE) + # - Windows defaults to the Schannel backend. + # - macOS defaults to the SecureTransport backend. + # - Android currently has no SSL backend as the NDK doesn't include any SSL + # library; a proper 'native' backend would have to go through Java. + # But you can force builds for those platforms to use OpenSSL if you have + # your own copy of it. + set(DEFAULT_ENABLE_OPENSSL OFF) +endif() +option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL}) + # On Android, fetch and compile libcxx before doing anything else if (ANDROID) set(CMAKE_SKIP_INSTALL_RULES ON) @@ -277,15 +289,16 @@ find_package(Boost 1.79.0 REQUIRED context) find_package(enet 1.3 MODULE) find_package(fmt 9 REQUIRED) find_package(inih 52 MODULE COMPONENTS INIReader) -find_package(LLVM MODULE COMPONENTS Demangle) +find_package(LLVM 17 MODULE COMPONENTS Demangle) find_package(lz4 REQUIRED) find_package(nlohmann_json 3.8 REQUIRED) find_package(Opus 1.3 MODULE) +find_package(VulkanMemoryAllocator CONFIG) find_package(ZLIB 1.2 REQUIRED) find_package(zstd 1.5 REQUIRED) if (NOT YUZU_USE_EXTERNAL_VULKAN_HEADERS) - find_package(Vulkan 1.3.246 REQUIRED) + find_package(Vulkan 1.3.256 REQUIRED) endif() if (ENABLE_LIBUSB) @@ -322,6 +335,10 @@ if (MINGW) find_library(MSWSOCK_LIBRARY mswsock REQUIRED) endif() +if(ENABLE_OPENSSL) + find_package(OpenSSL 1.1.1 REQUIRED) +endif() + # Please consider this as a stub if(ENABLE_QT6 AND Qt6_LOCATION) list(APPEND CMAKE_PREFIX_PATH "${Qt6_LOCATION}") diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 4ff588851..1f7cd598e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -144,9 +144,9 @@ endif() add_subdirectory(nx_tzdb) # VMA -add_library(vma vma/vma.cpp) -target_include_directories(vma PUBLIC ./vma/VulkanMemoryAllocator/include) -target_link_libraries(vma PRIVATE Vulkan::Headers) +if (NOT TARGET GPUOpen::VulkanMemoryAllocator) + add_subdirectory(VulkanMemoryAllocator) +endif() if (NOT TARGET LLVM::Demangle) add_library(demangle demangle/ItaniumDemangle.cpp) diff --git a/externals/Vulkan-Headers b/externals/Vulkan-Headers -Subproject 63af1cf1ee906ba4dcd5a324bdd0201d4f4bfd1 +Subproject ed857118e243fdc0f3a100f00ac9919e874cfe6 diff --git a/externals/VulkanMemoryAllocator b/externals/VulkanMemoryAllocator new file mode 160000 +Subproject 9b0fc3e7b02afe97895eb3e945fe800c3a7485a diff --git a/externals/demangle/ItaniumDemangle.cpp b/externals/demangle/ItaniumDemangle.cpp index b055a2fd7..47dd5d301 100644 --- a/externals/demangle/ItaniumDemangle.cpp +++ b/externals/demangle/ItaniumDemangle.cpp @@ -20,9 +20,7 @@ #include <cstdlib> #include <cstring> #include <functional> -#include <numeric> #include <utility> -#include <vector> using namespace llvm; using namespace llvm::itanium_demangle; @@ -81,8 +79,8 @@ struct DumpVisitor { } void printStr(const char *S) { fprintf(stderr, "%s", S); } - void print(StringView SV) { - fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.begin()); + void print(std::string_view SV) { + fprintf(stderr, "\"%.*s\"", (int)SV.size(), SV.data()); } void print(const Node *N) { if (N) @@ -90,14 +88,6 @@ struct DumpVisitor { else printStr("<null>"); } - void print(NodeOrString NS) { - if (NS.isNode()) - print(NS.asNode()); - else if (NS.isString()) - print(NS.asString()); - else - printStr("NodeOrString()"); - } void print(NodeArray A) { ++Depth; printStr("{"); @@ -116,13 +106,11 @@ struct DumpVisitor { // Overload used when T is exactly 'bool', not merely convertible to 'bool'. void print(bool B) { printStr(B ? "true" : "false"); } - template <class T> - typename std::enable_if<std::is_unsigned<T>::value>::type print(T N) { + template <class T> std::enable_if_t<std::is_unsigned<T>::value> print(T N) { fprintf(stderr, "%llu", (unsigned long long)N); } - template <class T> - typename std::enable_if<std::is_signed<T>::value>::type print(T N) { + template <class T> std::enable_if_t<std::is_signed<T>::value> print(T N) { fprintf(stderr, "%lld", (long long)N); } @@ -185,6 +173,50 @@ struct DumpVisitor { return printStr("TemplateParamKind::Template"); } } + void print(Node::Prec P) { + switch (P) { + case Node::Prec::Primary: + return printStr("Node::Prec::Primary"); + case Node::Prec::Postfix: + return printStr("Node::Prec::Postfix"); + case Node::Prec::Unary: + return printStr("Node::Prec::Unary"); + case Node::Prec::Cast: + return printStr("Node::Prec::Cast"); + case Node::Prec::PtrMem: + return printStr("Node::Prec::PtrMem"); + case Node::Prec::Multiplicative: + return printStr("Node::Prec::Multiplicative"); + case Node::Prec::Additive: + return printStr("Node::Prec::Additive"); + case Node::Prec::Shift: + return printStr("Node::Prec::Shift"); + case Node::Prec::Spaceship: + return printStr("Node::Prec::Spaceship"); + case Node::Prec::Relational: + return printStr("Node::Prec::Relational"); + case Node::Prec::Equality: + return printStr("Node::Prec::Equality"); + case Node::Prec::And: + return printStr("Node::Prec::And"); + case Node::Prec::Xor: + return printStr("Node::Prec::Xor"); + case Node::Prec::Ior: + return printStr("Node::Prec::Ior"); + case Node::Prec::AndIf: + return printStr("Node::Prec::AndIf"); + case Node::Prec::OrIf: + return printStr("Node::Prec::OrIf"); + case Node::Prec::Conditional: + return printStr("Node::Prec::Conditional"); + case Node::Prec::Assign: + return printStr("Node::Prec::Assign"); + case Node::Prec::Comma: + return printStr("Node::Prec::Comma"); + case Node::Prec::Default: + return printStr("Node::Prec::Default"); + } + } void newLine() { printStr("\n"); @@ -334,36 +366,21 @@ public: using Demangler = itanium_demangle::ManglingParser<DefaultAllocator>; -char *llvm::itaniumDemangle(const char *MangledName, char *Buf, - size_t *N, int *Status) { - if (MangledName == nullptr || (Buf != nullptr && N == nullptr)) { - if (Status) - *Status = demangle_invalid_args; +char *llvm::itaniumDemangle(std::string_view MangledName) { + if (MangledName.empty()) return nullptr; - } - - int InternalStatus = demangle_success; - Demangler Parser(MangledName, MangledName + std::strlen(MangledName)); - OutputStream S; + Demangler Parser(MangledName.data(), + MangledName.data() + MangledName.length()); Node *AST = Parser.parse(); + if (!AST) + return nullptr; - if (AST == nullptr) - InternalStatus = demangle_invalid_mangled_name; - else if (!initializeOutputStream(Buf, N, S, 1024)) - InternalStatus = demangle_memory_alloc_failure; - else { - assert(Parser.ForwardTemplateRefs.empty()); - AST->print(S); - S += '\0'; - if (N != nullptr) - *N = S.getCurrentPosition(); - Buf = S.getBuffer(); - } - - if (Status) - *Status = InternalStatus; - return InternalStatus == demangle_success ? Buf : nullptr; + OutputBuffer OB; + assert(Parser.ForwardTemplateRefs.empty()); + AST->print(OB); + OB += '\0'; + return OB.getBuffer(); } ItaniumPartialDemangler::ItaniumPartialDemangler() @@ -396,14 +413,12 @@ bool ItaniumPartialDemangler::partialDemangle(const char *MangledName) { } static char *printNode(const Node *RootNode, char *Buf, size_t *N) { - OutputStream S; - if (!initializeOutputStream(Buf, N, S, 128)) - return nullptr; - RootNode->print(S); - S += '\0'; + OutputBuffer OB(Buf, N); + RootNode->print(OB); + OB += '\0'; if (N != nullptr) - *N = S.getCurrentPosition(); - return S.getBuffer(); + *N = OB.getCurrentPosition(); + return OB.getBuffer(); } char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { @@ -417,8 +432,8 @@ char *ItaniumPartialDemangler::getFunctionBaseName(char *Buf, size_t *N) const { case Node::KAbiTagAttr: Name = static_cast<const AbiTagAttr *>(Name)->Base; continue; - case Node::KStdQualifiedName: - Name = static_cast<const StdQualifiedName *>(Name)->Child; + case Node::KModuleEntity: + Name = static_cast<const ModuleEntity *>(Name)->Name; continue; case Node::KNestedName: Name = static_cast<const NestedName *>(Name)->Name; @@ -441,9 +456,7 @@ char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf, return nullptr; const Node *Name = static_cast<const FunctionEncoding *>(RootNode)->getName(); - OutputStream S; - if (!initializeOutputStream(Buf, N, S, 128)) - return nullptr; + OutputBuffer OB(Buf, N); KeepGoingLocalFunction: while (true) { @@ -458,27 +471,27 @@ char *ItaniumPartialDemangler::getFunctionDeclContextName(char *Buf, break; } + if (Name->getKind() == Node::KModuleEntity) + Name = static_cast<const ModuleEntity *>(Name)->Name; + switch (Name->getKind()) { - case Node::KStdQualifiedName: - S += "std"; - break; case Node::KNestedName: - static_cast<const NestedName *>(Name)->Qual->print(S); + static_cast<const NestedName *>(Name)->Qual->print(OB); break; case Node::KLocalName: { auto *LN = static_cast<const LocalName *>(Name); - LN->Encoding->print(S); - S += "::"; + LN->Encoding->print(OB); + OB += "::"; Name = LN->Entity; goto KeepGoingLocalFunction; } default: break; } - S += '\0'; + OB += '\0'; if (N != nullptr) - *N = S.getCurrentPosition(); - return S.getBuffer(); + *N = OB.getCurrentPosition(); + return OB.getBuffer(); } char *ItaniumPartialDemangler::getFunctionName(char *Buf, size_t *N) const { @@ -494,17 +507,15 @@ char *ItaniumPartialDemangler::getFunctionParameters(char *Buf, return nullptr; NodeArray Params = static_cast<FunctionEncoding *>(RootNode)->getParams(); - OutputStream S; - if (!initializeOutputStream(Buf, N, S, 128)) - return nullptr; + OutputBuffer OB(Buf, N); - S += '('; - Params.printWithComma(S); - S += ')'; - S += '\0'; + OB += '('; + Params.printWithComma(OB); + OB += ')'; + OB += '\0'; if (N != nullptr) - *N = S.getCurrentPosition(); - return S.getBuffer(); + *N = OB.getCurrentPosition(); + return OB.getBuffer(); } char *ItaniumPartialDemangler::getFunctionReturnType( @@ -512,18 +523,16 @@ char *ItaniumPartialDemangler::getFunctionReturnType( if (!isFunction()) return nullptr; - OutputStream S; - if (!initializeOutputStream(Buf, N, S, 128)) - return nullptr; + OutputBuffer OB(Buf, N); if (const Node *Ret = static_cast<const FunctionEncoding *>(RootNode)->getReturnType()) - Ret->print(S); + Ret->print(OB); - S += '\0'; + OB += '\0'; if (N != nullptr) - *N = S.getCurrentPosition(); - return S.getBuffer(); + *N = OB.getCurrentPosition(); + return OB.getBuffer(); } char *ItaniumPartialDemangler::finishDemangle(char *Buf, size_t *N) const { @@ -563,8 +572,8 @@ bool ItaniumPartialDemangler::isCtorOrDtor() const { case Node::KNestedName: N = static_cast<const NestedName *>(N)->Name; break; - case Node::KStdQualifiedName: - N = static_cast<const StdQualifiedName *>(N)->Child; + case Node::KModuleEntity: + N = static_cast<const ModuleEntity *>(N)->Name; break; } } diff --git a/externals/demangle/llvm/Demangle/Demangle.h b/externals/demangle/llvm/Demangle/Demangle.h index 5b673e4e1..1552a501a 100644 --- a/externals/demangle/llvm/Demangle/Demangle.h +++ b/externals/demangle/llvm/Demangle/Demangle.h @@ -12,6 +12,7 @@ #include <cstddef> #include <string> +#include <string_view> namespace llvm { /// This is a llvm local version of __cxa_demangle. Other than the name and @@ -29,9 +30,10 @@ enum : int { demangle_success = 0, }; -char *itaniumDemangle(const char *mangled_name, char *buf, size_t *n, - int *status); - +/// Returns a non-NULL pointer to a NUL-terminated C style string +/// that should be explicitly freed, if successful. Otherwise, may return +/// nullptr if mangled_name is not a valid mangling or is nullptr. +char *itaniumDemangle(std::string_view mangled_name); enum MSDemangleFlags { MSDF_None = 0, @@ -40,10 +42,34 @@ enum MSDemangleFlags { MSDF_NoCallingConvention = 1 << 2, MSDF_NoReturnType = 1 << 3, MSDF_NoMemberType = 1 << 4, + MSDF_NoVariableType = 1 << 5, }; -char *microsoftDemangle(const char *mangled_name, char *buf, size_t *n, + +/// Demangles the Microsoft symbol pointed at by mangled_name and returns it. +/// Returns a pointer to the start of a null-terminated demangled string on +/// success, or nullptr on error. +/// If n_read is non-null and demangling was successful, it receives how many +/// bytes of the input string were consumed. +/// status receives one of the demangle_ enum entries above if it's not nullptr. +/// Flags controls various details of the demangled representation. +char *microsoftDemangle(std::string_view mangled_name, size_t *n_read, int *status, MSDemangleFlags Flags = MSDF_None); +// Demangles a Rust v0 mangled symbol. +char *rustDemangle(std::string_view MangledName); + +// Demangles a D mangled symbol. +char *dlangDemangle(std::string_view MangledName); + +/// Attempt to demangle a string using different demangling schemes. +/// The function uses heuristics to determine which demangling scheme to use. +/// \param MangledName - reference to string to demangle. +/// \returns - the demangled string, or a copy of the input string if no +/// demangling occurred. +std::string demangle(std::string_view MangledName); + +bool nonMicrosoftDemangle(std::string_view MangledName, std::string &Result); + /// "Partial" demangler. This supports demangling a string into an AST /// (typically an intermediate stage in itaniumDemangle) and querying certain /// properties or partially printing the demangled name. @@ -59,7 +85,7 @@ struct ItaniumPartialDemangler { bool partialDemangle(const char *MangledName); /// Just print the entire mangled name into Buf. Buf and N behave like the - /// second and third parameters to itaniumDemangle. + /// second and third parameters to __cxa_demangle. char *finishDemangle(char *Buf, size_t *N) const; /// Get the base name of a function. This doesn't include trailing template @@ -95,6 +121,7 @@ struct ItaniumPartialDemangler { bool isSpecialName() const; ~ItaniumPartialDemangler(); + private: void *RootNode; void *Context; diff --git a/externals/demangle/llvm/Demangle/DemangleConfig.h b/externals/demangle/llvm/Demangle/DemangleConfig.h index a8aef9df1..c7f86d766 100644 --- a/externals/demangle/llvm/Demangle/DemangleConfig.h +++ b/externals/demangle/llvm/Demangle/DemangleConfig.h @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_DEMANGLE_COMPILER_H -#define LLVM_DEMANGLE_COMPILER_H +#ifndef LLVM_DEMANGLE_DEMANGLECONFIG_H +#define LLVM_DEMANGLE_DEMANGLECONFIG_H #ifndef __has_feature #define __has_feature(x) 0 diff --git a/externals/demangle/llvm/Demangle/ItaniumDemangle.h b/externals/demangle/llvm/Demangle/ItaniumDemangle.h index 64b35c142..0dc3d7337 100644 --- a/externals/demangle/llvm/Demangle/ItaniumDemangle.h +++ b/externals/demangle/llvm/Demangle/ItaniumDemangle.h @@ -1,5 +1,5 @@ -//===------------------------- ItaniumDemangle.h ----------------*- C++ -*-===// -// +//===--- ItaniumDemangle.h -----------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-FileCopyrightText: Part of the LLVM Project @@ -7,144 +7,220 @@ // //===----------------------------------------------------------------------===// // -// Generic itanium demangler library. This file has two byte-per-byte identical -// copies in the source tree, one in libcxxabi, and the other in llvm. +// Generic itanium demangler library. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. // //===----------------------------------------------------------------------===// #ifndef DEMANGLE_ITANIUMDEMANGLE_H #define DEMANGLE_ITANIUMDEMANGLE_H -// FIXME: (possibly) incomplete list of features that clang mangles that this -// file does not yet support: -// - C++ modules TS - #include "DemangleConfig.h" -#include "StringView.h" +#include "StringViewExtras.h" #include "Utility.h" +#include <algorithm> #include <cassert> #include <cctype> #include <cstdio> #include <cstdlib> #include <cstring> -#include <numeric> +#include <limits> +#include <new> +#include <string_view> +#include <type_traits> #include <utility> -#define FOR_EACH_NODE_KIND(X) \ - X(NodeArrayNode) \ - X(DotSuffix) \ - X(VendorExtQualType) \ - X(QualType) \ - X(ConversionOperatorType) \ - X(PostfixQualifiedType) \ - X(ElaboratedTypeSpefType) \ - X(NameType) \ - X(AbiTagAttr) \ - X(EnableIfAttr) \ - X(ObjCProtoName) \ - X(PointerType) \ - X(ReferenceType) \ - X(PointerToMemberType) \ - X(ArrayType) \ - X(FunctionType) \ - X(NoexceptSpec) \ - X(DynamicExceptionSpec) \ - X(FunctionEncoding) \ - X(LiteralOperator) \ - X(SpecialName) \ - X(CtorVtableSpecialName) \ - X(QualifiedName) \ - X(NestedName) \ - X(LocalName) \ - X(VectorType) \ - X(PixelVectorType) \ - X(SyntheticTemplateParamName) \ - X(TypeTemplateParamDecl) \ - X(NonTypeTemplateParamDecl) \ - X(TemplateTemplateParamDecl) \ - X(TemplateParamPackDecl) \ - X(ParameterPack) \ - X(TemplateArgumentPack) \ - X(ParameterPackExpansion) \ - X(TemplateArgs) \ - X(ForwardTemplateReference) \ - X(NameWithTemplateArgs) \ - X(GlobalQualifiedName) \ - X(StdQualifiedName) \ - X(ExpandedSpecialSubstitution) \ - X(SpecialSubstitution) \ - X(CtorDtorName) \ - X(DtorName) \ - X(UnnamedTypeName) \ - X(ClosureTypeName) \ - X(StructuredBindingName) \ - X(BinaryExpr) \ - X(ArraySubscriptExpr) \ - X(PostfixExpr) \ - X(ConditionalExpr) \ - X(MemberExpr) \ - X(EnclosingExpr) \ - X(CastExpr) \ - X(SizeofParamPackExpr) \ - X(CallExpr) \ - X(NewExpr) \ - X(DeleteExpr) \ - X(PrefixExpr) \ - X(FunctionParam) \ - X(ConversionExpr) \ - X(InitListExpr) \ - X(FoldExpr) \ - X(ThrowExpr) \ - X(UUIDOfExpr) \ - X(BoolExpr) \ - X(StringLiteral) \ - X(LambdaExpr) \ - X(IntegerCastExpr) \ - X(IntegerLiteral) \ - X(FloatLiteral) \ - X(DoubleLiteral) \ - X(LongDoubleLiteral) \ - X(BracedExpr) \ - X(BracedRangeExpr) - DEMANGLE_NAMESPACE_BEGIN +template <class T, size_t N> class PODSmallVector { + static_assert(std::is_pod<T>::value, + "T is required to be a plain old data type"); + + T *First = nullptr; + T *Last = nullptr; + T *Cap = nullptr; + T Inline[N] = {0}; + + bool isInline() const { return First == Inline; } + + void clearInline() { + First = Inline; + Last = Inline; + Cap = Inline + N; + } + + void reserve(size_t NewCap) { + size_t S = size(); + if (isInline()) { + auto *Tmp = static_cast<T *>(std::malloc(NewCap * sizeof(T))); + if (Tmp == nullptr) + std::terminate(); + std::copy(First, Last, Tmp); + First = Tmp; + } else { + First = static_cast<T *>(std::realloc(First, NewCap * sizeof(T))); + if (First == nullptr) + std::terminate(); + } + Last = First + S; + Cap = First + NewCap; + } + +public: + PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} + + PODSmallVector(const PODSmallVector &) = delete; + PODSmallVector &operator=(const PODSmallVector &) = delete; + + PODSmallVector(PODSmallVector &&Other) : PODSmallVector() { + if (Other.isInline()) { + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return; + } + + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + } + + PODSmallVector &operator=(PODSmallVector &&Other) { + if (Other.isInline()) { + if (!isInline()) { + std::free(First); + clearInline(); + } + std::copy(Other.begin(), Other.end(), First); + Last = First + Other.size(); + Other.clear(); + return *this; + } + + if (isInline()) { + First = Other.First; + Last = Other.Last; + Cap = Other.Cap; + Other.clearInline(); + return *this; + } + + std::swap(First, Other.First); + std::swap(Last, Other.Last); + std::swap(Cap, Other.Cap); + Other.clear(); + return *this; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void push_back(const T &Elem) { + if (Last == Cap) + reserve(size() * 2); + *Last++ = Elem; + } + + // NOLINTNEXTLINE(readability-identifier-naming) + void pop_back() { + assert(Last != First && "Popping empty vector!"); + --Last; + } + + void dropBack(size_t Index) { + assert(Index <= size() && "dropBack() can't expand!"); + Last = First + Index; + } + + T *begin() { return First; } + T *end() { return Last; } + + bool empty() const { return First == Last; } + size_t size() const { return static_cast<size_t>(Last - First); } + T &back() { + assert(Last != First && "Calling back() on empty vector!"); + return *(Last - 1); + } + T &operator[](size_t Index) { + assert(Index < size() && "Invalid access!"); + return *(begin() + Index); + } + void clear() { Last = First; } + + ~PODSmallVector() { + if (!isInline()) + std::free(First); + } +}; + // Base class of all AST nodes. The AST is built by the parser, then is // traversed by the printLeft/Right functions to produce a demangled string. class Node { public: enum Kind : unsigned char { -#define ENUMERATOR(NodeKind) K ## NodeKind, - FOR_EACH_NODE_KIND(ENUMERATOR) -#undef ENUMERATOR +#define NODE(NodeKind) K##NodeKind, +#include "ItaniumNodes.def" }; /// Three-way bool to track a cached value. Unknown is possible if this node /// has an unexpanded parameter pack below it that may affect this cache. enum class Cache : unsigned char { Yes, No, Unknown, }; + /// Operator precedence for expression nodes. Used to determine required + /// parens in expression emission. + enum class Prec { + Primary, + Postfix, + Unary, + Cast, + PtrMem, + Multiplicative, + Additive, + Shift, + Spaceship, + Relational, + Equality, + And, + Xor, + Ior, + AndIf, + OrIf, + Conditional, + Assign, + Comma, + Default, + }; + private: Kind K; + Prec Precedence : 6; + // FIXME: Make these protected. public: /// Tracks if this node has a component on its right side, in which case we /// need to call printRight. - Cache RHSComponentCache; + Cache RHSComponentCache : 2; /// Track if this node is a (possibly qualified) array type. This can affect /// how we format the output string. - Cache ArrayCache; + Cache ArrayCache : 2; /// Track if this node is a (possibly qualified) function type. This can /// affect how we format the output string. - Cache FunctionCache; + Cache FunctionCache : 2; public: - Node(Kind K_, Cache RHSComponentCache_ = Cache::No, - Cache ArrayCache_ = Cache::No, Cache FunctionCache_ = Cache::No) - : K(K_), RHSComponentCache(RHSComponentCache_), ArrayCache(ArrayCache_), - FunctionCache(FunctionCache_) {} + Node(Kind K_, Prec Precedence_ = Prec::Primary, + Cache RHSComponentCache_ = Cache::No, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : K(K_), Precedence(Precedence_), RHSComponentCache(RHSComponentCache_), + ArrayCache(ArrayCache_), FunctionCache(FunctionCache_) {} + Node(Kind K_, Cache RHSComponentCache_, Cache ArrayCache_ = Cache::No, + Cache FunctionCache_ = Cache::No) + : Node(K_, Prec::Primary, RHSComponentCache_, ArrayCache_, + FunctionCache_) {} /// Visit the most-derived object corresponding to this object. template<typename Fn> void visit(Fn F) const; @@ -155,52 +231,65 @@ public: // would construct an equivalent node. //template<typename Fn> void match(Fn F) const; - bool hasRHSComponent(OutputStream &S) const { + bool hasRHSComponent(OutputBuffer &OB) const { if (RHSComponentCache != Cache::Unknown) return RHSComponentCache == Cache::Yes; - return hasRHSComponentSlow(S); + return hasRHSComponentSlow(OB); } - bool hasArray(OutputStream &S) const { + bool hasArray(OutputBuffer &OB) const { if (ArrayCache != Cache::Unknown) return ArrayCache == Cache::Yes; - return hasArraySlow(S); + return hasArraySlow(OB); } - bool hasFunction(OutputStream &S) const { + bool hasFunction(OutputBuffer &OB) const { if (FunctionCache != Cache::Unknown) return FunctionCache == Cache::Yes; - return hasFunctionSlow(S); + return hasFunctionSlow(OB); } Kind getKind() const { return K; } - virtual bool hasRHSComponentSlow(OutputStream &) const { return false; } - virtual bool hasArraySlow(OutputStream &) const { return false; } - virtual bool hasFunctionSlow(OutputStream &) const { return false; } + Prec getPrecedence() const { return Precedence; } + + virtual bool hasRHSComponentSlow(OutputBuffer &) const { return false; } + virtual bool hasArraySlow(OutputBuffer &) const { return false; } + virtual bool hasFunctionSlow(OutputBuffer &) const { return false; } // Dig through "glue" nodes like ParameterPack and ForwardTemplateReference to // get at a node that actually represents some concrete syntax. - virtual const Node *getSyntaxNode(OutputStream &) const { - return this; - } - - void print(OutputStream &S) const { - printLeft(S); + virtual const Node *getSyntaxNode(OutputBuffer &) const { return this; } + + // Print this node as an expression operand, surrounding it in parentheses if + // its precedence is [Strictly] weaker than P. + void printAsOperand(OutputBuffer &OB, Prec P = Prec::Default, + bool StrictlyWorse = false) const { + bool Paren = + unsigned(getPrecedence()) >= unsigned(P) + unsigned(StrictlyWorse); + if (Paren) + OB.printOpen(); + print(OB); + if (Paren) + OB.printClose(); + } + + void print(OutputBuffer &OB) const { + printLeft(OB); if (RHSComponentCache != Cache::No) - printRight(S); + printRight(OB); } - // Print the "left" side of this Node into OutputStream. - virtual void printLeft(OutputStream &) const = 0; + // Print the "left" side of this Node into OutputBuffer. + virtual void printLeft(OutputBuffer &) const = 0; // Print the "right". This distinction is necessary to represent C++ types // that appear on the RHS of their subtype, such as arrays or functions. // Since most types don't have such a component, provide a default // implementation. - virtual void printRight(OutputStream &) const {} + virtual void printRight(OutputBuffer &) const {} - virtual StringView getBaseName() const { return StringView(); } + virtual std::string_view getBaseName() const { return {}; } // Silence compiler warnings, this dtor will never be called. virtual ~Node() = default; @@ -227,19 +316,19 @@ public: Node *operator[](size_t Idx) const { return Elements[Idx]; } - void printWithComma(OutputStream &S) const { + void printWithComma(OutputBuffer &OB) const { bool FirstElement = true; for (size_t Idx = 0; Idx != NumElements; ++Idx) { - size_t BeforeComma = S.getCurrentPosition(); + size_t BeforeComma = OB.getCurrentPosition(); if (!FirstElement) - S += ", "; - size_t AfterComma = S.getCurrentPosition(); - Elements[Idx]->print(S); + OB += ", "; + size_t AfterComma = OB.getCurrentPosition(); + Elements[Idx]->printAsOperand(OB, Node::Prec::Comma); // Elements[Idx] is an empty parameter pack expansion, we should erase the // comma we just printed. - if (AfterComma == S.getCurrentPosition()) { - S.setCurrentPosition(BeforeComma); + if (AfterComma == OB.getCurrentPosition()) { + OB.setCurrentPosition(BeforeComma); continue; } @@ -254,43 +343,48 @@ struct NodeArrayNode : Node { template<typename Fn> void match(Fn F) const { F(Array); } - void printLeft(OutputStream &S) const override { - Array.printWithComma(S); - } + void printLeft(OutputBuffer &OB) const override { Array.printWithComma(OB); } }; class DotSuffix final : public Node { const Node *Prefix; - const StringView Suffix; + const std::string_view Suffix; public: - DotSuffix(const Node *Prefix_, StringView Suffix_) + DotSuffix(const Node *Prefix_, std::string_view Suffix_) : Node(KDotSuffix), Prefix(Prefix_), Suffix(Suffix_) {} template<typename Fn> void match(Fn F) const { F(Prefix, Suffix); } - void printLeft(OutputStream &s) const override { - Prefix->print(s); - s += " ("; - s += Suffix; - s += ")"; + void printLeft(OutputBuffer &OB) const override { + Prefix->print(OB); + OB += " ("; + OB += Suffix; + OB += ")"; } }; class VendorExtQualType final : public Node { const Node *Ty; - StringView Ext; + std::string_view Ext; + const Node *TA; public: - VendorExtQualType(const Node *Ty_, StringView Ext_) - : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_) {} + VendorExtQualType(const Node *Ty_, std::string_view Ext_, const Node *TA_) + : Node(KVendorExtQualType), Ty(Ty_), Ext(Ext_), TA(TA_) {} + + const Node *getTy() const { return Ty; } + std::string_view getExt() const { return Ext; } + const Node *getTA() const { return TA; } - template<typename Fn> void match(Fn F) const { F(Ty, Ext); } + template <typename Fn> void match(Fn F) const { F(Ty, Ext, TA); } - void printLeft(OutputStream &S) const override { - Ty->print(S); - S += " "; - S += Ext; + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += " "; + OB += Ext; + if (TA != nullptr) + TA->print(OB); } }; @@ -316,13 +410,13 @@ protected: const Qualifiers Quals; const Node *Child; - void printQuals(OutputStream &S) const { + void printQuals(OutputBuffer &OB) const { if (Quals & QualConst) - S += " const"; + OB += " const"; if (Quals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (Quals & QualRestrict) - S += " restrict"; + OB += " restrict"; } public: @@ -331,24 +425,27 @@ public: Child_->ArrayCache, Child_->FunctionCache), Quals(Quals_), Child(Child_) {} + Qualifiers getQuals() const { return Quals; } + const Node *getChild() const { return Child; } + template<typename Fn> void match(Fn F) const { F(Child, Quals); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Child->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Child->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { - return Child->hasArray(S); + bool hasArraySlow(OutputBuffer &OB) const override { + return Child->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { - return Child->hasFunction(S); + bool hasFunctionSlow(OutputBuffer &OB) const override { + return Child->hasFunction(OB); } - void printLeft(OutputStream &S) const override { - Child->printLeft(S); - printQuals(S); + void printLeft(OutputBuffer &OB) const override { + Child->printLeft(OB); + printQuals(OB); } - void printRight(OutputStream &S) const override { Child->printRight(S); } + void printRight(OutputBuffer &OB) const override { Child->printRight(OB); } }; class ConversionOperatorType final : public Node { @@ -360,74 +457,96 @@ public: template<typename Fn> void match(Fn F) const { F(Ty); } - void printLeft(OutputStream &S) const override { - S += "operator "; - Ty->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "operator "; + Ty->print(OB); } }; class PostfixQualifiedType final : public Node { const Node *Ty; - const StringView Postfix; + const std::string_view Postfix; public: - PostfixQualifiedType(Node *Ty_, StringView Postfix_) + PostfixQualifiedType(const Node *Ty_, std::string_view Postfix_) : Node(KPostfixQualifiedType), Ty(Ty_), Postfix(Postfix_) {} template<typename Fn> void match(Fn F) const { F(Ty, Postfix); } - void printLeft(OutputStream &s) const override { - Ty->printLeft(s); - s += Postfix; + void printLeft(OutputBuffer &OB) const override { + Ty->printLeft(OB); + OB += Postfix; } }; class NameType final : public Node { - const StringView Name; + const std::string_view Name; public: - NameType(StringView Name_) : Node(KNameType), Name(Name_) {} + NameType(std::string_view Name_) : Node(KNameType), Name(Name_) {} template<typename Fn> void match(Fn F) const { F(Name); } - StringView getName() const { return Name; } - StringView getBaseName() const override { return Name; } + std::string_view getName() const { return Name; } + std::string_view getBaseName() const override { return Name; } + + void printLeft(OutputBuffer &OB) const override { OB += Name; } +}; + +class BitIntType final : public Node { + const Node *Size; + bool Signed; + +public: + BitIntType(const Node *Size_, bool Signed_) + : Node(KBitIntType), Size(Size_), Signed(Signed_) {} + + template <typename Fn> void match(Fn F) const { F(Size, Signed); } - void printLeft(OutputStream &s) const override { s += Name; } + void printLeft(OutputBuffer &OB) const override { + if (!Signed) + OB += "unsigned "; + OB += "_BitInt"; + OB.printOpen(); + Size->printAsOperand(OB); + OB.printClose(); + } }; class ElaboratedTypeSpefType : public Node { - StringView Kind; + std::string_view Kind; Node *Child; public: - ElaboratedTypeSpefType(StringView Kind_, Node *Child_) + ElaboratedTypeSpefType(std::string_view Kind_, Node *Child_) : Node(KElaboratedTypeSpefType), Kind(Kind_), Child(Child_) {} template<typename Fn> void match(Fn F) const { F(Kind, Child); } - void printLeft(OutputStream &S) const override { - S += Kind; - S += ' '; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += Kind; + OB += ' '; + Child->print(OB); } }; struct AbiTagAttr : Node { Node *Base; - StringView Tag; + std::string_view Tag; - AbiTagAttr(Node* Base_, StringView Tag_) - : Node(KAbiTagAttr, Base_->RHSComponentCache, - Base_->ArrayCache, Base_->FunctionCache), + AbiTagAttr(Node *Base_, std::string_view Tag_) + : Node(KAbiTagAttr, Base_->RHSComponentCache, Base_->ArrayCache, + Base_->FunctionCache), Base(Base_), Tag(Tag_) {} template<typename Fn> void match(Fn F) const { F(Base, Tag); } - void printLeft(OutputStream &S) const override { - Base->printLeft(S); - S += "[abi:"; - S += Tag; - S += "]"; + std::string_view getBaseName() const override { return Base->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Base->printLeft(OB); + OB += "[abi:"; + OB += Tag; + OB += "]"; } }; @@ -439,21 +558,21 @@ public: template<typename Fn> void match(Fn F) const { F(Conditions); } - void printLeft(OutputStream &S) const override { - S += " [enable_if:"; - Conditions.printWithComma(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB += " [enable_if:"; + Conditions.printWithComma(OB); + OB += ']'; } }; class ObjCProtoName : public Node { const Node *Ty; - StringView Protocol; + std::string_view Protocol; friend class PointerType; public: - ObjCProtoName(const Node *Ty_, StringView Protocol_) + ObjCProtoName(const Node *Ty_, std::string_view Protocol_) : Node(KObjCProtoName), Ty(Ty_), Protocol(Protocol_) {} template<typename Fn> void match(Fn F) const { F(Ty, Protocol); } @@ -463,11 +582,11 @@ public: static_cast<const NameType *>(Ty)->getName() == "objc_object"; } - void printLeft(OutputStream &S) const override { - Ty->print(S); - S += "<"; - S += Protocol; - S += ">"; + void printLeft(OutputBuffer &OB) const override { + Ty->print(OB); + OB += "<"; + OB += Protocol; + OB += ">"; } }; @@ -479,36 +598,38 @@ public: : Node(KPointerType, Pointee_->RHSComponentCache), Pointee(Pointee_) {} + const Node *getPointee() const { return Pointee; } + template<typename Fn> void match(Fn F) const { F(Pointee); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Pointee->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { + void printLeft(OutputBuffer &OB) const override { // We rewrite objc_object<SomeProtocol>* into id<SomeProtocol>. if (Pointee->getKind() != KObjCProtoName || !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { - Pointee->printLeft(s); - if (Pointee->hasArray(s)) - s += " "; - if (Pointee->hasArray(s) || Pointee->hasFunction(s)) - s += "("; - s += "*"; + Pointee->printLeft(OB); + if (Pointee->hasArray(OB)) + OB += " "; + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += "("; + OB += "*"; } else { const auto *objcProto = static_cast<const ObjCProtoName *>(Pointee); - s += "id<"; - s += objcProto->Protocol; - s += ">"; + OB += "id<"; + OB += objcProto->Protocol; + OB += ">"; } } - void printRight(OutputStream &s) const override { + void printRight(OutputBuffer &OB) const override { if (Pointee->getKind() != KObjCProtoName || !static_cast<const ObjCProtoName *>(Pointee)->isObjCObject()) { - if (Pointee->hasArray(s) || Pointee->hasFunction(s)) - s += ")"; - Pointee->printRight(s); + if (Pointee->hasArray(OB) || Pointee->hasFunction(OB)) + OB += ")"; + Pointee->printRight(OB); } } }; @@ -528,15 +649,30 @@ class ReferenceType : public Node { // Dig through any refs to refs, collapsing the ReferenceTypes as we go. The // rule here is rvalue ref to rvalue ref collapses to a rvalue ref, and any // other combination collapses to a lvalue ref. - std::pair<ReferenceKind, const Node *> collapse(OutputStream &S) const { + // + // A combination of a TemplateForwardReference and a back-ref Substitution + // from an ill-formed string may have created a cycle; use cycle detection to + // avoid looping forever. + std::pair<ReferenceKind, const Node *> collapse(OutputBuffer &OB) const { auto SoFar = std::make_pair(RK, Pointee); + // Track the chain of nodes for the Floyd's 'tortoise and hare' + // cycle-detection algorithm, since getSyntaxNode(S) is impure + PODSmallVector<const Node *, 8> Prev; for (;;) { - const Node *SN = SoFar.second->getSyntaxNode(S); + const Node *SN = SoFar.second->getSyntaxNode(OB); if (SN->getKind() != KReferenceType) break; auto *RT = static_cast<const ReferenceType *>(SN); SoFar.second = RT->Pointee; SoFar.first = std::min(SoFar.first, RT->RK); + + // The middle of Prev is the 'slow' pointer moving at half speed + Prev.push_back(SoFar.second); + if (Prev.size() > 1 && SoFar.second == Prev[(Prev.size() - 1) / 2]) { + // Cycle detected + SoFar.second = nullptr; + break; + } } return SoFar; } @@ -548,31 +684,35 @@ public: template<typename Fn> void match(Fn F) const { F(Pointee, RK); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return Pointee->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return Pointee->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { + void printLeft(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore<bool> SavePrinting(Printing, true); - std::pair<ReferenceKind, const Node *> Collapsed = collapse(s); - Collapsed.second->printLeft(s); - if (Collapsed.second->hasArray(s)) - s += " "; - if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) - s += "("; + ScopedOverride<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB); + if (!Collapsed.second) + return; + Collapsed.second->printLeft(OB); + if (Collapsed.second->hasArray(OB)) + OB += " "; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += "("; - s += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); + OB += (Collapsed.first == ReferenceKind::LValue ? "&" : "&&"); } - void printRight(OutputStream &s) const override { + void printRight(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore<bool> SavePrinting(Printing, true); - std::pair<ReferenceKind, const Node *> Collapsed = collapse(s); - if (Collapsed.second->hasArray(s) || Collapsed.second->hasFunction(s)) - s += ")"; - Collapsed.second->printRight(s); + ScopedOverride<bool> SavePrinting(Printing, true); + std::pair<ReferenceKind, const Node *> Collapsed = collapse(OB); + if (!Collapsed.second) + return; + if (Collapsed.second->hasArray(OB) || Collapsed.second->hasFunction(OB)) + OB += ")"; + Collapsed.second->printRight(OB); } }; @@ -587,69 +727,33 @@ public: template<typename Fn> void match(Fn F) const { F(ClassType, MemberType); } - bool hasRHSComponentSlow(OutputStream &S) const override { - return MemberType->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + return MemberType->hasRHSComponent(OB); } - void printLeft(OutputStream &s) const override { - MemberType->printLeft(s); - if (MemberType->hasArray(s) || MemberType->hasFunction(s)) - s += "("; + void printLeft(OutputBuffer &OB) const override { + MemberType->printLeft(OB); + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += "("; else - s += " "; - ClassType->print(s); - s += "::*"; - } - - void printRight(OutputStream &s) const override { - if (MemberType->hasArray(s) || MemberType->hasFunction(s)) - s += ")"; - MemberType->printRight(s); - } -}; - -class NodeOrString { - const void *First; - const void *Second; - -public: - /* implicit */ NodeOrString(StringView Str) { - const char *FirstChar = Str.begin(); - const char *SecondChar = Str.end(); - if (SecondChar == nullptr) { - assert(FirstChar == SecondChar); - ++FirstChar, ++SecondChar; - } - First = static_cast<const void *>(FirstChar); - Second = static_cast<const void *>(SecondChar); + OB += " "; + ClassType->print(OB); + OB += "::*"; } - /* implicit */ NodeOrString(Node *N) - : First(static_cast<const void *>(N)), Second(nullptr) {} - NodeOrString() : First(nullptr), Second(nullptr) {} - - bool isString() const { return Second && First; } - bool isNode() const { return First && !Second; } - bool isEmpty() const { return !First && !Second; } - - StringView asString() const { - assert(isString()); - return StringView(static_cast<const char *>(First), - static_cast<const char *>(Second)); - } - - const Node *asNode() const { - assert(isNode()); - return static_cast<const Node *>(First); + void printRight(OutputBuffer &OB) const override { + if (MemberType->hasArray(OB) || MemberType->hasFunction(OB)) + OB += ")"; + MemberType->printRight(OB); } }; class ArrayType final : public Node { const Node *Base; - NodeOrString Dimension; + Node *Dimension; public: - ArrayType(const Node *Base_, NodeOrString Dimension_) + ArrayType(const Node *Base_, Node *Dimension_) : Node(KArrayType, /*RHSComponentCache=*/Cache::Yes, /*ArrayCache=*/Cache::Yes), @@ -657,21 +761,19 @@ public: template<typename Fn> void match(Fn F) const { F(Base, Dimension); } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasArraySlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasArraySlow(OutputBuffer &) const override { return true; } - void printLeft(OutputStream &S) const override { Base->printLeft(S); } + void printLeft(OutputBuffer &OB) const override { Base->printLeft(OB); } - void printRight(OutputStream &S) const override { - if (S.back() != ']') - S += " "; - S += "["; - if (Dimension.isString()) - S += Dimension.asString(); - else if (Dimension.isNode()) - Dimension.asNode()->print(S); - S += "]"; - Base->printRight(S); + void printRight(OutputBuffer &OB) const override { + if (OB.back() != ']') + OB += " "; + OB += "["; + if (Dimension) + Dimension->print(OB); + OB += "]"; + Base->printRight(OB); } }; @@ -695,8 +797,8 @@ public: F(Ret, Params, CVQuals, RefQual, ExceptionSpec); } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasFunctionSlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } // Handle C++'s ... quirky decl grammar by using the left & right // distinction. Consider: @@ -705,32 +807,32 @@ public: // that takes a char and returns an int. If we're trying to print f, start // by printing out the return types's left, then print our parameters, then // finally print right of the return type. - void printLeft(OutputStream &S) const override { - Ret->printLeft(S); - S += " "; + void printLeft(OutputBuffer &OB) const override { + Ret->printLeft(OB); + OB += " "; } - void printRight(OutputStream &S) const override { - S += "("; - Params.printWithComma(S); - S += ")"; - Ret->printRight(S); + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); + Ret->printRight(OB); if (CVQuals & QualConst) - S += " const"; + OB += " const"; if (CVQuals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (CVQuals & QualRestrict) - S += " restrict"; + OB += " restrict"; if (RefQual == FrefQualLValue) - S += " &"; + OB += " &"; else if (RefQual == FrefQualRValue) - S += " &&"; + OB += " &&"; if (ExceptionSpec != nullptr) { - S += ' '; - ExceptionSpec->print(S); + OB += ' '; + ExceptionSpec->print(OB); } } }; @@ -742,10 +844,11 @@ public: template<typename Fn> void match(Fn F) const { F(E); } - void printLeft(OutputStream &S) const override { - S += "noexcept("; - E->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "noexcept"; + OB.printOpen(); + E->printAsOperand(OB); + OB.printClose(); } }; @@ -757,10 +860,11 @@ public: template<typename Fn> void match(Fn F) const { F(Types); } - void printLeft(OutputStream &S) const override { - S += "throw("; - Types.printWithComma(S); - S += ')'; + void printLeft(OutputBuffer &OB) const override { + OB += "throw"; + OB.printOpen(); + Types.printWithComma(OB); + OB.printClose(); } }; @@ -791,41 +895,41 @@ public: NodeArray getParams() const { return Params; } const Node *getReturnType() const { return Ret; } - bool hasRHSComponentSlow(OutputStream &) const override { return true; } - bool hasFunctionSlow(OutputStream &) const override { return true; } + bool hasRHSComponentSlow(OutputBuffer &) const override { return true; } + bool hasFunctionSlow(OutputBuffer &) const override { return true; } const Node *getName() const { return Name; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Ret) { - Ret->printLeft(S); - if (!Ret->hasRHSComponent(S)) - S += " "; + Ret->printLeft(OB); + if (!Ret->hasRHSComponent(OB)) + OB += " "; } - Name->print(S); + Name->print(OB); } - void printRight(OutputStream &S) const override { - S += "("; - Params.printWithComma(S); - S += ")"; + void printRight(OutputBuffer &OB) const override { + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); if (Ret) - Ret->printRight(S); + Ret->printRight(OB); if (CVQuals & QualConst) - S += " const"; + OB += " const"; if (CVQuals & QualVolatile) - S += " volatile"; + OB += " volatile"; if (CVQuals & QualRestrict) - S += " restrict"; + OB += " restrict"; if (RefQual == FrefQualLValue) - S += " &"; + OB += " &"; else if (RefQual == FrefQualRValue) - S += " &&"; + OB += " &&"; if (Attrs != nullptr) - Attrs->print(S); + Attrs->print(OB); } }; @@ -838,25 +942,25 @@ public: template<typename Fn> void match(Fn F) const { F(OpName); } - void printLeft(OutputStream &S) const override { - S += "operator\"\" "; - OpName->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "operator\"\" "; + OpName->print(OB); } }; class SpecialName final : public Node { - const StringView Special; + const std::string_view Special; const Node *Child; public: - SpecialName(StringView Special_, const Node *Child_) + SpecialName(std::string_view Special_, const Node *Child_) : Node(KSpecialName), Special(Special_), Child(Child_) {} template<typename Fn> void match(Fn F) const { F(Special, Child); } - void printLeft(OutputStream &S) const override { - S += Special; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += Special; + Child->print(OB); } }; @@ -871,11 +975,11 @@ public: template<typename Fn> void match(Fn F) const { F(FirstType, SecondType); } - void printLeft(OutputStream &S) const override { - S += "construction vtable for "; - FirstType->print(S); - S += "-in-"; - SecondType->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "construction vtable for "; + FirstType->print(OB); + OB += "-in-"; + SecondType->print(OB); } }; @@ -888,12 +992,52 @@ struct NestedName : Node { template<typename Fn> void match(Fn F) const { F(Qual, Name); } - StringView getBaseName() const override { return Name->getBaseName(); } + std::string_view getBaseName() const override { return Name->getBaseName(); } + + void printLeft(OutputBuffer &OB) const override { + Qual->print(OB); + OB += "::"; + Name->print(OB); + } +}; + +struct ModuleName : Node { + ModuleName *Parent; + Node *Name; + bool IsPartition; + + ModuleName(ModuleName *Parent_, Node *Name_, bool IsPartition_ = false) + : Node(KModuleName), Parent(Parent_), Name(Name_), + IsPartition(IsPartition_) {} + + template <typename Fn> void match(Fn F) const { + F(Parent, Name, IsPartition); + } + + void printLeft(OutputBuffer &OB) const override { + if (Parent) + Parent->print(OB); + if (Parent || IsPartition) + OB += IsPartition ? ':' : '.'; + Name->print(OB); + } +}; + +struct ModuleEntity : Node { + ModuleName *Module; + Node *Name; + + ModuleEntity(ModuleName *Module_, Node *Name_) + : Node(KModuleEntity), Module(Module_), Name(Name_) {} + + template <typename Fn> void match(Fn F) const { F(Module, Name); } + + std::string_view getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Qual->print(S); - S += "::"; - Name->print(S); + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + OB += '@'; + Module->print(OB); } }; @@ -906,10 +1050,10 @@ struct LocalName : Node { template<typename Fn> void match(Fn F) const { F(Encoding, Entity); } - void printLeft(OutputStream &S) const override { - Encoding->print(S); - S += "::"; - Entity->print(S); + void printLeft(OutputBuffer &OB) const override { + Encoding->print(OB); + OB += "::"; + Entity->print(OB); } }; @@ -924,51 +1068,66 @@ public: template<typename Fn> void match(Fn F) const { F(Qualifier, Name); } - StringView getBaseName() const override { return Name->getBaseName(); } + std::string_view getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Qualifier->print(S); - S += "::"; - Name->print(S); + void printLeft(OutputBuffer &OB) const override { + Qualifier->print(OB); + OB += "::"; + Name->print(OB); } }; class VectorType final : public Node { const Node *BaseType; - const NodeOrString Dimension; + const Node *Dimension; public: - VectorType(const Node *BaseType_, NodeOrString Dimension_) - : Node(KVectorType), BaseType(BaseType_), - Dimension(Dimension_) {} + VectorType(const Node *BaseType_, const Node *Dimension_) + : Node(KVectorType), BaseType(BaseType_), Dimension(Dimension_) {} + + const Node *getBaseType() const { return BaseType; } + const Node *getDimension() const { return Dimension; } template<typename Fn> void match(Fn F) const { F(BaseType, Dimension); } - void printLeft(OutputStream &S) const override { - BaseType->print(S); - S += " vector["; - if (Dimension.isNode()) - Dimension.asNode()->print(S); - else if (Dimension.isString()) - S += Dimension.asString(); - S += "]"; + void printLeft(OutputBuffer &OB) const override { + BaseType->print(OB); + OB += " vector["; + if (Dimension) + Dimension->print(OB); + OB += "]"; } }; class PixelVectorType final : public Node { - const NodeOrString Dimension; + const Node *Dimension; public: - PixelVectorType(NodeOrString Dimension_) + PixelVectorType(const Node *Dimension_) : Node(KPixelVectorType), Dimension(Dimension_) {} template<typename Fn> void match(Fn F) const { F(Dimension); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { // FIXME: This should demangle as "vector pixel". - S += "pixel vector["; - S += Dimension.asString(); - S += "]"; + OB += "pixel vector["; + Dimension->print(OB); + OB += "]"; + } +}; + +class BinaryFPType final : public Node { + const Node *Dimension; + +public: + BinaryFPType(const Node *Dimension_) + : Node(KBinaryFPType), Dimension(Dimension_) {} + + template<typename Fn> void match(Fn F) const { F(Dimension); } + + void printLeft(OutputBuffer &OB) const override { + OB += "_Float"; + Dimension->print(OB); } }; @@ -990,20 +1149,20 @@ public: template<typename Fn> void match(Fn F) const { F(Kind, Index); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { switch (Kind) { case TemplateParamKind::Type: - S += "$T"; + OB += "$T"; break; case TemplateParamKind::NonType: - S += "$N"; + OB += "$N"; break; case TemplateParamKind::Template: - S += "$TT"; + OB += "$TT"; break; } if (Index > 0) - S << Index - 1; + OB << Index - 1; } }; @@ -1017,13 +1176,9 @@ public: template<typename Fn> void match(Fn F) const { F(Name); } - void printLeft(OutputStream &S) const override { - S += "typename "; - } + void printLeft(OutputBuffer &OB) const override { OB += "typename "; } - void printRight(OutputStream &S) const override { - Name->print(S); - } + void printRight(OutputBuffer &OB) const override { Name->print(OB); } }; /// A non-type template parameter declaration, 'int N'. @@ -1037,15 +1192,15 @@ public: template<typename Fn> void match(Fn F) const { F(Name, Type); } - void printLeft(OutputStream &S) const override { - Type->printLeft(S); - if (!Type->hasRHSComponent(S)) - S += " "; + void printLeft(OutputBuffer &OB) const override { + Type->printLeft(OB); + if (!Type->hasRHSComponent(OB)) + OB += " "; } - void printRight(OutputStream &S) const override { - Name->print(S); - Type->printRight(S); + void printRight(OutputBuffer &OB) const override { + Name->print(OB); + Type->printRight(OB); } }; @@ -1062,15 +1217,14 @@ public: template<typename Fn> void match(Fn F) const { F(Name, Params); } - void printLeft(OutputStream &S) const override { - S += "template<"; - Params.printWithComma(S); - S += "> typename "; + void printLeft(OutputBuffer &OB) const override { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "template<"; + Params.printWithComma(OB); + OB += "> typename "; } - void printRight(OutputStream &S) const override { - Name->print(S); - } + void printRight(OutputBuffer &OB) const override { Name->print(OB); } }; /// A template parameter pack declaration, 'typename ...T'. @@ -1083,14 +1237,12 @@ public: template<typename Fn> void match(Fn F) const { F(Param); } - void printLeft(OutputStream &S) const override { - Param->printLeft(S); - S += "..."; + void printLeft(OutputBuffer &OB) const override { + Param->printLeft(OB); + OB += "..."; } - void printRight(OutputStream &S) const override { - Param->printRight(S); - } + void printRight(OutputBuffer &OB) const override { Param->printRight(OB); } }; /// An unexpanded parameter pack (either in the expression or type context). If @@ -1104,11 +1256,12 @@ public: class ParameterPack final : public Node { NodeArray Data; - // Setup OutputStream for a pack expansion unless we're already expanding one. - void initializePackExpansion(OutputStream &S) const { - if (S.CurrentPackMax == std::numeric_limits<unsigned>::max()) { - S.CurrentPackMax = static_cast<unsigned>(Data.size()); - S.CurrentPackIndex = 0; + // Setup OutputBuffer for a pack expansion, unless we're already expanding + // one. + void initializePackExpansion(OutputBuffer &OB) const { + if (OB.CurrentPackMax == std::numeric_limits<unsigned>::max()) { + OB.CurrentPackMax = static_cast<unsigned>(Data.size()); + OB.CurrentPackIndex = 0; } } @@ -1131,38 +1284,38 @@ public: template<typename Fn> void match(Fn F) const { F(Data); } - bool hasRHSComponentSlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasRHSComponent(S); + bool hasRHSComponentSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasArray(S); + bool hasArraySlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() && Data[Idx]->hasFunction(S); + bool hasFunctionSlow(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() && Data[Idx]->hasFunction(OB); } - const Node *getSyntaxNode(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; - return Idx < Data.size() ? Data[Idx]->getSyntaxNode(S) : this; + const Node *getSyntaxNode(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; + return Idx < Data.size() ? Data[Idx]->getSyntaxNode(OB) : this; } - void printLeft(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; + void printLeft(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printLeft(S); + Data[Idx]->printLeft(OB); } - void printRight(OutputStream &S) const override { - initializePackExpansion(S); - size_t Idx = S.CurrentPackIndex; + void printRight(OutputBuffer &OB) const override { + initializePackExpansion(OB); + size_t Idx = OB.CurrentPackIndex; if (Idx < Data.size()) - Data[Idx]->printRight(S); + Data[Idx]->printRight(OB); } }; @@ -1181,8 +1334,8 @@ public: NodeArray getElements() const { return Elements; } - void printLeft(OutputStream &S) const override { - Elements.printWithComma(S); + void printLeft(OutputBuffer &OB) const override { + Elements.printWithComma(OB); } }; @@ -1199,35 +1352,35 @@ public: const Node *getChild() const { return Child; } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { constexpr unsigned Max = std::numeric_limits<unsigned>::max(); - SwapAndRestore<unsigned> SavePackIdx(S.CurrentPackIndex, Max); - SwapAndRestore<unsigned> SavePackMax(S.CurrentPackMax, Max); - size_t StreamPos = S.getCurrentPosition(); + ScopedOverride<unsigned> SavePackIdx(OB.CurrentPackIndex, Max); + ScopedOverride<unsigned> SavePackMax(OB.CurrentPackMax, Max); + size_t StreamPos = OB.getCurrentPosition(); // Print the first element in the pack. If Child contains a ParameterPack, // it will set up S.CurrentPackMax and print the first element. - Child->print(S); + Child->print(OB); // No ParameterPack was found in Child. This can occur if we've found a pack // expansion on a <function-param>. - if (S.CurrentPackMax == Max) { - S += "..."; + if (OB.CurrentPackMax == Max) { + OB += "..."; return; } // We found a ParameterPack, but it has no elements. Erase whatever we may // of printed. - if (S.CurrentPackMax == 0) { - S.setCurrentPosition(StreamPos); + if (OB.CurrentPackMax == 0) { + OB.setCurrentPosition(StreamPos); return; } // Else, iterate through the rest of the elements in the pack. - for (unsigned I = 1, E = S.CurrentPackMax; I < E; ++I) { - S += ", "; - S.CurrentPackIndex = I; - Child->print(S); + for (unsigned I = 1, E = OB.CurrentPackMax; I < E; ++I) { + OB += ", "; + OB.CurrentPackIndex = I; + Child->print(OB); } } }; @@ -1242,12 +1395,11 @@ public: NodeArray getParams() { return Params; } - void printLeft(OutputStream &S) const override { - S += "<"; - Params.printWithComma(S); - if (S.back() == '>') - S += " "; - S += ">"; + void printLeft(OutputBuffer &OB) const override { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + Params.printWithComma(OB); + OB += ">"; } }; @@ -1289,42 +1441,42 @@ struct ForwardTemplateReference : Node { // special handling. template<typename Fn> void match(Fn F) const = delete; - bool hasRHSComponentSlow(OutputStream &S) const override { + bool hasRHSComponentSlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore<bool> SavePrinting(Printing, true); - return Ref->hasRHSComponent(S); + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasRHSComponent(OB); } - bool hasArraySlow(OutputStream &S) const override { + bool hasArraySlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore<bool> SavePrinting(Printing, true); - return Ref->hasArray(S); + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasArray(OB); } - bool hasFunctionSlow(OutputStream &S) const override { + bool hasFunctionSlow(OutputBuffer &OB) const override { if (Printing) return false; - SwapAndRestore<bool> SavePrinting(Printing, true); - return Ref->hasFunction(S); + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->hasFunction(OB); } - const Node *getSyntaxNode(OutputStream &S) const override { + const Node *getSyntaxNode(OutputBuffer &OB) const override { if (Printing) return this; - SwapAndRestore<bool> SavePrinting(Printing, true); - return Ref->getSyntaxNode(S); + ScopedOverride<bool> SavePrinting(Printing, true); + return Ref->getSyntaxNode(OB); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore<bool> SavePrinting(Printing, true); - Ref->printLeft(S); + ScopedOverride<bool> SavePrinting(Printing, true); + Ref->printLeft(OB); } - void printRight(OutputStream &S) const override { + void printRight(OutputBuffer &OB) const override { if (Printing) return; - SwapAndRestore<bool> SavePrinting(Printing, true); - Ref->printRight(S); + ScopedOverride<bool> SavePrinting(Printing, true); + Ref->printRight(OB); } }; @@ -1338,11 +1490,11 @@ struct NameWithTemplateArgs : Node { template<typename Fn> void match(Fn F) const { F(Name, TemplateArgs); } - StringView getBaseName() const override { return Name->getBaseName(); } + std::string_view getBaseName() const override { return Name->getBaseName(); } - void printLeft(OutputStream &S) const override { - Name->print(S); - TemplateArgs->print(S); + void printLeft(OutputBuffer &OB) const override { + Name->print(OB); + TemplateArgs->print(OB); } }; @@ -1355,26 +1507,11 @@ public: template<typename Fn> void match(Fn F) const { F(Child); } - StringView getBaseName() const override { return Child->getBaseName(); } + std::string_view getBaseName() const override { return Child->getBaseName(); } - void printLeft(OutputStream &S) const override { - S += "::"; - Child->print(S); - } -}; - -struct StdQualifiedName : Node { - Node *Child; - - StdQualifiedName(Node *Child_) : Node(KStdQualifiedName), Child(Child_) {} - - template<typename Fn> void match(Fn F) const { F(Child); } - - StringView getBaseName() const override { return Child->getBaseName(); } - - void printLeft(OutputStream &S) const override { - S += "std::"; - Child->print(S); + void printLeft(OutputBuffer &OB) const override { + OB += "::"; + Child->print(OB); } }; @@ -1387,109 +1524,81 @@ enum class SpecialSubKind { iostream, }; -class ExpandedSpecialSubstitution final : public Node { +class SpecialSubstitution; +class ExpandedSpecialSubstitution : public Node { +protected: SpecialSubKind SSK; + ExpandedSpecialSubstitution(SpecialSubKind SSK_, Kind K_) + : Node(K_), SSK(SSK_) {} public: ExpandedSpecialSubstitution(SpecialSubKind SSK_) - : Node(KExpandedSpecialSubstitution), SSK(SSK_) {} + : ExpandedSpecialSubstitution(SSK_, KExpandedSpecialSubstitution) {} + inline ExpandedSpecialSubstitution(SpecialSubstitution const *); template<typename Fn> void match(Fn F) const { F(SSK); } - StringView getBaseName() const override { +protected: + bool isInstantiation() const { + return unsigned(SSK) >= unsigned(SpecialSubKind::string); + } + + std::string_view getBaseName() const override { switch (SSK) { case SpecialSubKind::allocator: - return StringView("allocator"); + return {"allocator"}; case SpecialSubKind::basic_string: - return StringView("basic_string"); + return {"basic_string"}; case SpecialSubKind::string: - return StringView("basic_string"); + return {"basic_string"}; case SpecialSubKind::istream: - return StringView("basic_istream"); + return {"basic_istream"}; case SpecialSubKind::ostream: - return StringView("basic_ostream"); + return {"basic_ostream"}; case SpecialSubKind::iostream: - return StringView("basic_iostream"); + return {"basic_iostream"}; } DEMANGLE_UNREACHABLE; } - void printLeft(OutputStream &S) const override { - switch (SSK) { - case SpecialSubKind::allocator: - S += "std::allocator"; - break; - case SpecialSubKind::basic_string: - S += "std::basic_string"; - break; - case SpecialSubKind::string: - S += "std::basic_string<char, std::char_traits<char>, " - "std::allocator<char> >"; - break; - case SpecialSubKind::istream: - S += "std::basic_istream<char, std::char_traits<char> >"; - break; - case SpecialSubKind::ostream: - S += "std::basic_ostream<char, std::char_traits<char> >"; - break; - case SpecialSubKind::iostream: - S += "std::basic_iostream<char, std::char_traits<char> >"; - break; +private: + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); + if (isInstantiation()) { + OB << "<char, std::char_traits<char>"; + if (SSK == SpecialSubKind::string) + OB << ", std::allocator<char>"; + OB << ">"; } } }; -class SpecialSubstitution final : public Node { +class SpecialSubstitution final : public ExpandedSpecialSubstitution { public: - SpecialSubKind SSK; - SpecialSubstitution(SpecialSubKind SSK_) - : Node(KSpecialSubstitution), SSK(SSK_) {} + : ExpandedSpecialSubstitution(SSK_, KSpecialSubstitution) {} template<typename Fn> void match(Fn F) const { F(SSK); } - StringView getBaseName() const override { - switch (SSK) { - case SpecialSubKind::allocator: - return StringView("allocator"); - case SpecialSubKind::basic_string: - return StringView("basic_string"); - case SpecialSubKind::string: - return StringView("string"); - case SpecialSubKind::istream: - return StringView("istream"); - case SpecialSubKind::ostream: - return StringView("ostream"); - case SpecialSubKind::iostream: - return StringView("iostream"); + std::string_view getBaseName() const override { + std::string_view SV = ExpandedSpecialSubstitution::getBaseName(); + if (isInstantiation()) { + // The instantiations are typedefs that drop the "basic_" prefix. + assert(llvm::itanium_demangle::starts_with(SV, "basic_")); + SV.remove_prefix(sizeof("basic_") - 1); } - DEMANGLE_UNREACHABLE; + return SV; } - void printLeft(OutputStream &S) const override { - switch (SSK) { - case SpecialSubKind::allocator: - S += "std::allocator"; - break; - case SpecialSubKind::basic_string: - S += "std::basic_string"; - break; - case SpecialSubKind::string: - S += "std::string"; - break; - case SpecialSubKind::istream: - S += "std::istream"; - break; - case SpecialSubKind::ostream: - S += "std::ostream"; - break; - case SpecialSubKind::iostream: - S += "std::iostream"; - break; - } + void printLeft(OutputBuffer &OB) const override { + OB << "std::" << getBaseName(); } }; +inline ExpandedSpecialSubstitution::ExpandedSpecialSubstitution( + SpecialSubstitution const *SS) + : ExpandedSpecialSubstitution(SS->SSK) {} + class CtorDtorName final : public Node { const Node *Basename; const bool IsDtor; @@ -1502,10 +1611,10 @@ public: template<typename Fn> void match(Fn F) const { F(Basename, IsDtor, Variant); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsDtor) - S += "~"; - S += Basename->getBaseName(); + OB += "~"; + OB += Basename->getBaseName(); } }; @@ -1517,35 +1626,36 @@ public: template<typename Fn> void match(Fn F) const { F(Base); } - void printLeft(OutputStream &S) const override { - S += "~"; - Base->printLeft(S); + void printLeft(OutputBuffer &OB) const override { + OB += "~"; + Base->printLeft(OB); } }; class UnnamedTypeName : public Node { - const StringView Count; + const std::string_view Count; public: - UnnamedTypeName(StringView Count_) : Node(KUnnamedTypeName), Count(Count_) {} + UnnamedTypeName(std::string_view Count_) + : Node(KUnnamedTypeName), Count(Count_) {} template<typename Fn> void match(Fn F) const { F(Count); } - void printLeft(OutputStream &S) const override { - S += "'unnamed"; - S += Count; - S += "\'"; + void printLeft(OutputBuffer &OB) const override { + OB += "'unnamed"; + OB += Count; + OB += "\'"; } }; class ClosureTypeName : public Node { NodeArray TemplateParams; NodeArray Params; - StringView Count; + std::string_view Count; public: ClosureTypeName(NodeArray TemplateParams_, NodeArray Params_, - StringView Count_) + std::string_view Count_) : Node(KClosureTypeName), TemplateParams(TemplateParams_), Params(Params_), Count(Count_) {} @@ -1553,22 +1663,23 @@ public: F(TemplateParams, Params, Count); } - void printDeclarator(OutputStream &S) const { + void printDeclarator(OutputBuffer &OB) const { if (!TemplateParams.empty()) { - S += "<"; - TemplateParams.printWithComma(S); - S += ">"; + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + TemplateParams.printWithComma(OB); + OB += ">"; } - S += "("; - Params.printWithComma(S); - S += ")"; + OB.printOpen(); + Params.printWithComma(OB); + OB.printClose(); } - void printLeft(OutputStream &S) const override { - S += "\'lambda"; - S += Count; - S += "\'"; - printDeclarator(S); + void printLeft(OutputBuffer &OB) const override { + OB += "\'lambda"; + OB += Count; + OB += "\'"; + printDeclarator(OB); } }; @@ -1580,10 +1691,10 @@ public: template<typename Fn> void match(Fn F) const { F(Bindings); } - void printLeft(OutputStream &S) const override { - S += '['; - Bindings.printWithComma(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB.printOpen('['); + Bindings.printWithComma(OB); + OB.printClose(']'); } }; @@ -1591,32 +1702,35 @@ public: class BinaryExpr : public Node { const Node *LHS; - const StringView InfixOperator; + const std::string_view InfixOperator; const Node *RHS; public: - BinaryExpr(const Node *LHS_, StringView InfixOperator_, const Node *RHS_) - : Node(KBinaryExpr), LHS(LHS_), InfixOperator(InfixOperator_), RHS(RHS_) { - } - - template<typename Fn> void match(Fn F) const { F(LHS, InfixOperator, RHS); } - - void printLeft(OutputStream &S) const override { - // might be a template argument expression, then we need to disambiguate - // with parens. - if (InfixOperator == ">") - S += "("; - - S += "("; - LHS->print(S); - S += ") "; - S += InfixOperator; - S += " ("; - RHS->print(S); - S += ")"; - - if (InfixOperator == ">") - S += ")"; + BinaryExpr(const Node *LHS_, std::string_view InfixOperator_, + const Node *RHS_, Prec Prec_) + : Node(KBinaryExpr, Prec_), LHS(LHS_), InfixOperator(InfixOperator_), + RHS(RHS_) {} + + template <typename Fn> void match(Fn F) const { + F(LHS, InfixOperator, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + bool ParenAll = OB.isGtInsideTemplateArgs() && + (InfixOperator == ">" || InfixOperator == ">>"); + if (ParenAll) + OB.printOpen(); + // Assignment is right associative, with special LHS precedence. + bool IsAssign = getPrecedence() == Prec::Assign; + LHS->printAsOperand(OB, IsAssign ? Prec::OrIf : getPrecedence(), !IsAssign); + // No space before comma operator + if (!(InfixOperator == ",")) + OB += " "; + OB += InfixOperator; + OB += " "; + RHS->printAsOperand(OB, getPrecedence(), IsAssign); + if (ParenAll) + OB.printClose(); } }; @@ -1625,35 +1739,36 @@ class ArraySubscriptExpr : public Node { const Node *Op2; public: - ArraySubscriptExpr(const Node *Op1_, const Node *Op2_) - : Node(KArraySubscriptExpr), Op1(Op1_), Op2(Op2_) {} + ArraySubscriptExpr(const Node *Op1_, const Node *Op2_, Prec Prec_) + : Node(KArraySubscriptExpr, Prec_), Op1(Op1_), Op2(Op2_) {} - template<typename Fn> void match(Fn F) const { F(Op1, Op2); } + template <typename Fn> void match(Fn F) const { + F(Op1, Op2, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += "("; - Op1->print(S); - S += ")["; - Op2->print(S); - S += "]"; + void printLeft(OutputBuffer &OB) const override { + Op1->printAsOperand(OB, getPrecedence()); + OB.printOpen('['); + Op2->printAsOperand(OB); + OB.printClose(']'); } }; class PostfixExpr : public Node { const Node *Child; - const StringView Operator; + const std::string_view Operator; public: - PostfixExpr(const Node *Child_, StringView Operator_) - : Node(KPostfixExpr), Child(Child_), Operator(Operator_) {} + PostfixExpr(const Node *Child_, std::string_view Operator_, Prec Prec_) + : Node(KPostfixExpr, Prec_), Child(Child_), Operator(Operator_) {} - template<typename Fn> void match(Fn F) const { F(Child, Operator); } + template <typename Fn> void match(Fn F) const { + F(Child, Operator, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += "("; - Child->print(S); - S += ")"; - S += Operator; + void printLeft(OutputBuffer &OB) const override { + Child->printAsOperand(OB, getPrecedence(), true); + OB += Operator; } }; @@ -1663,78 +1778,128 @@ class ConditionalExpr : public Node { const Node *Else; public: - ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_) - : Node(KConditionalExpr), Cond(Cond_), Then(Then_), Else(Else_) {} + ConditionalExpr(const Node *Cond_, const Node *Then_, const Node *Else_, + Prec Prec_) + : Node(KConditionalExpr, Prec_), Cond(Cond_), Then(Then_), Else(Else_) {} - template<typename Fn> void match(Fn F) const { F(Cond, Then, Else); } + template <typename Fn> void match(Fn F) const { + F(Cond, Then, Else, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += "("; - Cond->print(S); - S += ") ? ("; - Then->print(S); - S += ") : ("; - Else->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + Cond->printAsOperand(OB, getPrecedence()); + OB += " ? "; + Then->printAsOperand(OB); + OB += " : "; + Else->printAsOperand(OB, Prec::Assign, true); } }; class MemberExpr : public Node { const Node *LHS; - const StringView Kind; + const std::string_view Kind; const Node *RHS; public: - MemberExpr(const Node *LHS_, StringView Kind_, const Node *RHS_) - : Node(KMemberExpr), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + MemberExpr(const Node *LHS_, std::string_view Kind_, const Node *RHS_, + Prec Prec_) + : Node(KMemberExpr, Prec_), LHS(LHS_), Kind(Kind_), RHS(RHS_) {} + + template <typename Fn> void match(Fn F) const { + F(LHS, Kind, RHS, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + LHS->printAsOperand(OB, getPrecedence(), true); + OB += Kind; + RHS->printAsOperand(OB, getPrecedence(), false); + } +}; + +class SubobjectExpr : public Node { + const Node *Type; + const Node *SubExpr; + std::string_view Offset; + NodeArray UnionSelectors; + bool OnePastTheEnd; - template<typename Fn> void match(Fn F) const { F(LHS, Kind, RHS); } +public: + SubobjectExpr(const Node *Type_, const Node *SubExpr_, + std::string_view Offset_, NodeArray UnionSelectors_, + bool OnePastTheEnd_) + : Node(KSubobjectExpr), Type(Type_), SubExpr(SubExpr_), Offset(Offset_), + UnionSelectors(UnionSelectors_), OnePastTheEnd(OnePastTheEnd_) {} - void printLeft(OutputStream &S) const override { - LHS->print(S); - S += Kind; - RHS->print(S); + template<typename Fn> void match(Fn F) const { + F(Type, SubExpr, Offset, UnionSelectors, OnePastTheEnd); + } + + void printLeft(OutputBuffer &OB) const override { + SubExpr->print(OB); + OB += ".<"; + Type->print(OB); + OB += " at offset "; + if (Offset.empty()) { + OB += "0"; + } else if (Offset[0] == 'n') { + OB += "-"; + OB += std::string_view(Offset.data() + 1, Offset.size() - 1); + } else { + OB += Offset; + } + OB += ">"; } }; class EnclosingExpr : public Node { - const StringView Prefix; + const std::string_view Prefix; const Node *Infix; - const StringView Postfix; + const std::string_view Postfix; public: - EnclosingExpr(StringView Prefix_, Node *Infix_, StringView Postfix_) - : Node(KEnclosingExpr), Prefix(Prefix_), Infix(Infix_), - Postfix(Postfix_) {} + EnclosingExpr(std::string_view Prefix_, const Node *Infix_, + Prec Prec_ = Prec::Primary) + : Node(KEnclosingExpr, Prec_), Prefix(Prefix_), Infix(Infix_) {} - template<typename Fn> void match(Fn F) const { F(Prefix, Infix, Postfix); } + template <typename Fn> void match(Fn F) const { + F(Prefix, Infix, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += Prefix; - Infix->print(S); - S += Postfix; + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + OB.printOpen(); + Infix->print(OB); + OB.printClose(); + OB += Postfix; } }; class CastExpr : public Node { // cast_kind<to>(from) - const StringView CastKind; + const std::string_view CastKind; const Node *To; const Node *From; public: - CastExpr(StringView CastKind_, const Node *To_, const Node *From_) - : Node(KCastExpr), CastKind(CastKind_), To(To_), From(From_) {} + CastExpr(std::string_view CastKind_, const Node *To_, const Node *From_, + Prec Prec_) + : Node(KCastExpr, Prec_), CastKind(CastKind_), To(To_), From(From_) {} - template<typename Fn> void match(Fn F) const { F(CastKind, To, From); } + template <typename Fn> void match(Fn F) const { + F(CastKind, To, From, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += CastKind; - S += "<"; - To->printLeft(S); - S += ">("; - From->printLeft(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += CastKind; + { + ScopedOverride<unsigned> LT(OB.GtIsGt, 0); + OB += "<"; + To->printLeft(OB); + OB += ">"; + } + OB.printOpen(); + From->printAsOperand(OB); + OB.printClose(); } }; @@ -1747,11 +1912,12 @@ public: template<typename Fn> void match(Fn F) const { F(Pack); } - void printLeft(OutputStream &S) const override { - S += "sizeof...("; + void printLeft(OutputBuffer &OB) const override { + OB += "sizeof..."; + OB.printOpen(); ParameterPackExpansion PPE(Pack); - PPE.printLeft(S); - S += ")"; + PPE.printLeft(OB); + OB.printClose(); } }; @@ -1760,16 +1926,18 @@ class CallExpr : public Node { NodeArray Args; public: - CallExpr(const Node *Callee_, NodeArray Args_) - : Node(KCallExpr), Callee(Callee_), Args(Args_) {} + CallExpr(const Node *Callee_, NodeArray Args_, Prec Prec_) + : Node(KCallExpr, Prec_), Callee(Callee_), Args(Args_) {} - template<typename Fn> void match(Fn F) const { F(Callee, Args); } + template <typename Fn> void match(Fn F) const { + F(Callee, Args, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - Callee->print(S); - S += "("; - Args.printWithComma(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + Callee->print(OB); + OB.printOpen(); + Args.printWithComma(OB); + OB.printClose(); } }; @@ -1782,33 +1950,32 @@ class NewExpr : public Node { bool IsArray; // new[] ? public: NewExpr(NodeArray ExprList_, Node *Type_, NodeArray InitList_, bool IsGlobal_, - bool IsArray_) - : Node(KNewExpr), ExprList(ExprList_), Type(Type_), InitList(InitList_), - IsGlobal(IsGlobal_), IsArray(IsArray_) {} + bool IsArray_, Prec Prec_) + : Node(KNewExpr, Prec_), ExprList(ExprList_), Type(Type_), + InitList(InitList_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} template<typename Fn> void match(Fn F) const { - F(ExprList, Type, InitList, IsGlobal, IsArray); + F(ExprList, Type, InitList, IsGlobal, IsArray, getPrecedence()); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsGlobal) - S += "::operator "; - S += "new"; + OB += "::"; + OB += "new"; if (IsArray) - S += "[]"; - S += ' '; + OB += "[]"; if (!ExprList.empty()) { - S += "("; - ExprList.printWithComma(S); - S += ")"; + OB.printOpen(); + ExprList.printWithComma(OB); + OB.printClose(); } - Type->print(S); + OB += " "; + Type->print(OB); if (!InitList.empty()) { - S += "("; - InitList.printWithComma(S); - S += ")"; + OB.printOpen(); + InitList.printWithComma(OB); + OB.printClose(); } - } }; @@ -1818,50 +1985,55 @@ class DeleteExpr : public Node { bool IsArray; public: - DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_) - : Node(KDeleteExpr), Op(Op_), IsGlobal(IsGlobal_), IsArray(IsArray_) {} + DeleteExpr(Node *Op_, bool IsGlobal_, bool IsArray_, Prec Prec_) + : Node(KDeleteExpr, Prec_), Op(Op_), IsGlobal(IsGlobal_), + IsArray(IsArray_) {} - template<typename Fn> void match(Fn F) const { F(Op, IsGlobal, IsArray); } + template <typename Fn> void match(Fn F) const { + F(Op, IsGlobal, IsArray, getPrecedence()); + } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsGlobal) - S += "::"; - S += "delete"; + OB += "::"; + OB += "delete"; if (IsArray) - S += "[] "; - Op->print(S); + OB += "[]"; + OB += ' '; + Op->print(OB); } }; class PrefixExpr : public Node { - StringView Prefix; + std::string_view Prefix; Node *Child; public: - PrefixExpr(StringView Prefix_, Node *Child_) - : Node(KPrefixExpr), Prefix(Prefix_), Child(Child_) {} + PrefixExpr(std::string_view Prefix_, Node *Child_, Prec Prec_) + : Node(KPrefixExpr, Prec_), Prefix(Prefix_), Child(Child_) {} - template<typename Fn> void match(Fn F) const { F(Prefix, Child); } + template <typename Fn> void match(Fn F) const { + F(Prefix, Child, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += Prefix; - S += "("; - Child->print(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB += Prefix; + Child->printAsOperand(OB, getPrecedence()); } }; class FunctionParam : public Node { - StringView Number; + std::string_view Number; public: - FunctionParam(StringView Number_) : Node(KFunctionParam), Number(Number_) {} + FunctionParam(std::string_view Number_) + : Node(KFunctionParam), Number(Number_) {} template<typename Fn> void match(Fn F) const { F(Number); } - void printLeft(OutputStream &S) const override { - S += "fp"; - S += Number; + void printLeft(OutputBuffer &OB) const override { + OB += "fp"; + OB += Number; } }; @@ -1870,17 +2042,45 @@ class ConversionExpr : public Node { NodeArray Expressions; public: - ConversionExpr(const Node *Type_, NodeArray Expressions_) - : Node(KConversionExpr), Type(Type_), Expressions(Expressions_) {} + ConversionExpr(const Node *Type_, NodeArray Expressions_, Prec Prec_) + : Node(KConversionExpr, Prec_), Type(Type_), Expressions(Expressions_) {} + + template <typename Fn> void match(Fn F) const { + F(Type, Expressions, getPrecedence()); + } + + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + Expressions.printWithComma(OB); + OB.printClose(); + } +}; + +class PointerToMemberConversionExpr : public Node { + const Node *Type; + const Node *SubExpr; + std::string_view Offset; + +public: + PointerToMemberConversionExpr(const Node *Type_, const Node *SubExpr_, + std::string_view Offset_, Prec Prec_) + : Node(KPointerToMemberConversionExpr, Prec_), Type(Type_), + SubExpr(SubExpr_), Offset(Offset_) {} - template<typename Fn> void match(Fn F) const { F(Type, Expressions); } + template <typename Fn> void match(Fn F) const { + F(Type, SubExpr, Offset, getPrecedence()); + } - void printLeft(OutputStream &S) const override { - S += "("; - Type->print(S); - S += ")("; - Expressions.printWithComma(S); - S += ")"; + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Type->print(OB); + OB.printClose(); + OB.printOpen(); + SubExpr->print(OB); + OB.printClose(); } }; @@ -1893,12 +2093,12 @@ public: template<typename Fn> void match(Fn F) const { F(Ty, Inits); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Ty) - Ty->print(S); - S += '{'; - Inits.printWithComma(S); - S += '}'; + Ty->print(OB); + OB += '{'; + Inits.printWithComma(OB); + OB += '}'; } }; @@ -1912,18 +2112,18 @@ public: template<typename Fn> void match(Fn F) const { F(Elem, Init, IsArray); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (IsArray) { - S += '['; - Elem->print(S); - S += ']'; + OB += '['; + Elem->print(OB); + OB += ']'; } else { - S += '.'; - Elem->print(S); + OB += '.'; + Elem->print(OB); } if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) - S += " = "; - Init->print(S); + OB += " = "; + Init->print(OB); } }; @@ -1937,25 +2137,25 @@ public: template<typename Fn> void match(Fn F) const { F(First, Last, Init); } - void printLeft(OutputStream &S) const override { - S += '['; - First->print(S); - S += " ... "; - Last->print(S); - S += ']'; + void printLeft(OutputBuffer &OB) const override { + OB += '['; + First->print(OB); + OB += " ... "; + Last->print(OB); + OB += ']'; if (Init->getKind() != KBracedExpr && Init->getKind() != KBracedRangeExpr) - S += " = "; - Init->print(S); + OB += " = "; + Init->print(OB); } }; class FoldExpr : public Node { const Node *Pack, *Init; - StringView OperatorName; + std::string_view OperatorName; bool IsLeftFold; public: - FoldExpr(bool IsLeftFold_, StringView OperatorName_, const Node *Pack_, + FoldExpr(bool IsLeftFold_, std::string_view OperatorName_, const Node *Pack_, const Node *Init_) : Node(KFoldExpr), Pack(Pack_), Init(Init_), OperatorName(OperatorName_), IsLeftFold(IsLeftFold_) {} @@ -1964,43 +2164,35 @@ public: F(IsLeftFold, OperatorName, Pack, Init); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { auto PrintPack = [&] { - S += '('; - ParameterPackExpansion(Pack).print(S); - S += ')'; + OB.printOpen(); + ParameterPackExpansion(Pack).print(OB); + OB.printClose(); }; - S += '('; - - if (IsLeftFold) { - // init op ... op pack - if (Init != nullptr) { - Init->print(S); - S += ' '; - S += OperatorName; - S += ' '; - } - // ... op pack - S += "... "; - S += OperatorName; - S += ' '; - PrintPack(); - } else { // !IsLeftFold - // pack op ... - PrintPack(); - S += ' '; - S += OperatorName; - S += " ..."; - // pack op ... op init - if (Init != nullptr) { - S += ' '; - S += OperatorName; - S += ' '; - Init->print(S); - } + OB.printOpen(); + // Either '[init op ]... op pack' or 'pack op ...[ op init]' + // Refactored to '[(init|pack) op ]...[ op (pack|init)]' + // Fold expr operands are cast-expressions + if (!IsLeftFold || Init != nullptr) { + // '(init|pack) op ' + if (IsLeftFold) + Init->printAsOperand(OB, Prec::Cast, true); + else + PrintPack(); + OB << " " << OperatorName << " "; + } + OB << "..."; + if (IsLeftFold || Init != nullptr) { + // ' op (init|pack)' + OB << " " << OperatorName << " "; + if (IsLeftFold) + PrintPack(); + else + Init->printAsOperand(OB, Prec::Cast, true); } - S += ')'; + OB.printClose(); } }; @@ -2012,24 +2204,9 @@ public: template<typename Fn> void match(Fn F) const { F(Op); } - void printLeft(OutputStream &S) const override { - S += "throw "; - Op->print(S); - } -}; - -// MSVC __uuidof extension, generated by clang in -fms-extensions mode. -class UUIDOfExpr : public Node { - Node *Operand; -public: - UUIDOfExpr(Node *Operand_) : Node(KUUIDOfExpr), Operand(Operand_) {} - - template<typename Fn> void match(Fn F) const { F(Operand); } - - void printLeft(OutputStream &S) const override { - S << "__uuidof("; - Operand->print(S); - S << ")"; + void printLeft(OutputBuffer &OB) const override { + OB += "throw "; + Op->print(OB); } }; @@ -2041,8 +2218,8 @@ public: template<typename Fn> void match(Fn F) const { F(Value); } - void printLeft(OutputStream &S) const override { - S += Value ? StringView("true") : StringView("false"); + void printLeft(OutputBuffer &OB) const override { + OB += Value ? std::string_view("true") : std::string_view("false"); } }; @@ -2054,10 +2231,10 @@ public: template<typename Fn> void match(Fn F) const { F(Type); } - void printLeft(OutputStream &S) const override { - S += "\"<"; - Type->print(S); - S += ">\""; + void printLeft(OutputBuffer &OB) const override { + OB += "\"<"; + Type->print(OB); + OB += ">\""; } }; @@ -2069,58 +2246,61 @@ public: template<typename Fn> void match(Fn F) const { F(Type); } - void printLeft(OutputStream &S) const override { - S += "[]"; + void printLeft(OutputBuffer &OB) const override { + OB += "[]"; if (Type->getKind() == KClosureTypeName) - static_cast<const ClosureTypeName *>(Type)->printDeclarator(S); - S += "{...}"; + static_cast<const ClosureTypeName *>(Type)->printDeclarator(OB); + OB += "{...}"; } }; -class IntegerCastExpr : public Node { +class EnumLiteral : public Node { // ty(integer) const Node *Ty; - StringView Integer; + std::string_view Integer; public: - IntegerCastExpr(const Node *Ty_, StringView Integer_) - : Node(KIntegerCastExpr), Ty(Ty_), Integer(Integer_) {} + EnumLiteral(const Node *Ty_, std::string_view Integer_) + : Node(KEnumLiteral), Ty(Ty_), Integer(Integer_) {} template<typename Fn> void match(Fn F) const { F(Ty, Integer); } - void printLeft(OutputStream &S) const override { - S += "("; - Ty->print(S); - S += ")"; - S += Integer; + void printLeft(OutputBuffer &OB) const override { + OB.printOpen(); + Ty->print(OB); + OB.printClose(); + + if (Integer[0] == 'n') + OB << '-' << std::string_view(Integer.data() + 1, Integer.size() - 1); + else + OB << Integer; } }; class IntegerLiteral : public Node { - StringView Type; - StringView Value; + std::string_view Type; + std::string_view Value; public: - IntegerLiteral(StringView Type_, StringView Value_) + IntegerLiteral(std::string_view Type_, std::string_view Value_) : Node(KIntegerLiteral), Type(Type_), Value(Value_) {} template<typename Fn> void match(Fn F) const { F(Type, Value); } - void printLeft(OutputStream &S) const override { + void printLeft(OutputBuffer &OB) const override { if (Type.size() > 3) { - S += "("; - S += Type; - S += ")"; + OB.printOpen(); + OB += Type; + OB.printClose(); } - if (Value[0] == 'n') { - S += "-"; - S += Value.dropFront(1); - } else - S += Value; + if (Value[0] == 'n') + OB << '-' << std::string_view(Value.data() + 1, Value.size() - 1); + else + OB += Value; if (Type.size() <= 3) - S += Type; + OB += Type; } }; @@ -2139,29 +2319,26 @@ constexpr Node::Kind getFloatLiteralKind(long double *) { } template <class Float> class FloatLiteralImpl : public Node { - const StringView Contents; + const std::string_view Contents; static constexpr Kind KindForClass = float_literal_impl::getFloatLiteralKind((Float *)nullptr); public: - FloatLiteralImpl(StringView Contents_) + FloatLiteralImpl(std::string_view Contents_) : Node(KindForClass), Contents(Contents_) {} template<typename Fn> void match(Fn F) const { F(Contents); } - void printLeft(OutputStream &s) const override { - const char *first = Contents.begin(); - const char *last = Contents.end() + 1; - + void printLeft(OutputBuffer &OB) const override { const size_t N = FloatData<Float>::mangled_size; - if (static_cast<std::size_t>(last - first) > N) { - last = first + N; + if (Contents.size() >= N) { union { Float value; char buf[sizeof(Float)]; }; - const char *t = first; + const char *t = Contents.data(); + const char *last = t + N; char *e = buf; for (; t != last; ++t, ++e) { unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0') @@ -2176,7 +2353,7 @@ public: #endif char num[FloatData<Float>::max_demangled_size] = {0}; int n = snprintf(num, sizeof(num), FloatData<Float>::spec, value); - s += StringView(num, num + n); + OB += std::string_view(num, n); } } }; @@ -2190,143 +2367,22 @@ using LongDoubleLiteral = FloatLiteralImpl<long double>; template<typename Fn> void Node::visit(Fn F) const { switch (K) { -#define CASE(X) case K ## X: return F(static_cast<const X*>(this)); - FOR_EACH_NODE_KIND(CASE) -#undef CASE +#define NODE(X) \ + case K##X: \ + return F(static_cast<const X *>(this)); +#include "ItaniumNodes.def" } assert(0 && "unknown mangling node kind"); } /// Determine the kind of a node from its type. template<typename NodeT> struct NodeKind; -#define SPECIALIZATION(X) \ - template<> struct NodeKind<X> { \ - static constexpr Node::Kind Kind = Node::K##X; \ - static constexpr const char *name() { return #X; } \ +#define NODE(X) \ + template <> struct NodeKind<X> { \ + static constexpr Node::Kind Kind = Node::K##X; \ + static constexpr const char *name() { return #X; } \ }; -FOR_EACH_NODE_KIND(SPECIALIZATION) -#undef SPECIALIZATION - -#undef FOR_EACH_NODE_KIND - -template <class T, size_t N> -class PODSmallVector { - static_assert(std::is_pod<T>::value, - "T is required to be a plain old data type"); - - T* First; - T* Last; - T* Cap; - T Inline[N]; - - bool isInline() const { return First == Inline; } - - void clearInline() { - First = Inline; - Last = Inline; - Cap = Inline + N; - } - - void reserve(size_t NewCap) { - size_t S = size(); - if (isInline()) { - auto* Tmp = static_cast<T*>(std::malloc(NewCap * sizeof(T))); - if (Tmp == nullptr) - std::terminate(); - std::copy(First, Last, Tmp); - First = Tmp; - } else { - First = static_cast<T*>(std::realloc(First, NewCap * sizeof(T))); - if (First == nullptr) - std::terminate(); - } - Last = First + S; - Cap = First + NewCap; - } - -public: - PODSmallVector() : First(Inline), Last(First), Cap(Inline + N) {} - - PODSmallVector(const PODSmallVector&) = delete; - PODSmallVector& operator=(const PODSmallVector&) = delete; - - PODSmallVector(PODSmallVector&& Other) : PODSmallVector() { - if (Other.isInline()) { - std::copy(Other.begin(), Other.end(), First); - Last = First + Other.size(); - Other.clear(); - return; - } - - First = Other.First; - Last = Other.Last; - Cap = Other.Cap; - Other.clearInline(); - } - - PODSmallVector& operator=(PODSmallVector&& Other) { - if (Other.isInline()) { - if (!isInline()) { - std::free(First); - clearInline(); - } - std::copy(Other.begin(), Other.end(), First); - Last = First + Other.size(); - Other.clear(); - return *this; - } - - if (isInline()) { - First = Other.First; - Last = Other.Last; - Cap = Other.Cap; - Other.clearInline(); - return *this; - } - - std::swap(First, Other.First); - std::swap(Last, Other.Last); - std::swap(Cap, Other.Cap); - Other.clear(); - return *this; - } - - void push_back(const T& Elem) { - if (Last == Cap) - reserve(size() * 2); - *Last++ = Elem; - } - - void pop_back() { - assert(Last != First && "Popping empty vector!"); - --Last; - } - - void dropBack(size_t Index) { - assert(Index <= size() && "dropBack() can't expand!"); - Last = First + Index; - } - - T* begin() { return First; } - T* end() { return Last; } - - bool empty() const { return First == Last; } - size_t size() const { return static_cast<size_t>(Last - First); } - T& back() { - assert(Last != First && "Calling back() on empty vector!"); - return *(Last - 1); - } - T& operator[](size_t Index) { - assert(Index < size() && "Invalid access!"); - return *(begin() + Index); - } - void clear() { Last = First; } - - ~PODSmallVector() { - if (!isInline()) - std::free(First); - } -}; +#include "ItaniumNodes.def" template <typename Derived, typename Alloc> struct AbstractManglingParser { const char *First; @@ -2350,9 +2406,9 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { TemplateParamList Params; public: - ScopedTemplateParamList(AbstractManglingParser *Parser) - : Parser(Parser), - OldNumTemplateParamLists(Parser->TemplateParams.size()) { + ScopedTemplateParamList(AbstractManglingParser *TheParser) + : Parser(TheParser), + OldNumTemplateParamLists(TheParser->TemplateParams.size()) { Parser->TemplateParams.push_back(&Params); } ~ScopedTemplateParamList() { @@ -2424,8 +2480,9 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { return res; } - bool consumeIf(StringView S) { - if (StringView(First, Last).startsWith(S)) { + bool consumeIf(std::string_view S) { + if (llvm::itanium_demangle::starts_with( + std::string_view(First, Last - First), S)) { First += S.size(); return true; } @@ -2442,7 +2499,7 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { char consume() { return First != Last ? *First++ : '\0'; } - char look(unsigned Lookahead = 0) { + char look(unsigned Lookahead = 0) const { if (static_cast<size_t>(Last - First) <= Lookahead) return '\0'; return First[Lookahead]; @@ -2450,10 +2507,10 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { size_t numLeft() const { return static_cast<size_t>(Last - First); } - StringView parseNumber(bool AllowNegative = false); + std::string_view parseNumber(bool AllowNegative = false); Qualifiers parseCVQualifiers(); bool parsePositiveInteger(size_t *Out); - StringView parseBareSourceName(); + std::string_view parseBareSourceName(); bool parseSeqId(size_t *Out); Node *parseSubstitution(); @@ -2464,16 +2521,17 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { /// Parse the <expr> production. Node *parseExpr(); - Node *parsePrefixExpr(StringView Kind); - Node *parseBinaryExpr(StringView Kind); - Node *parseIntegerLiteral(StringView Lit); + Node *parsePrefixExpr(std::string_view Kind, Node::Prec Prec); + Node *parseBinaryExpr(std::string_view Kind, Node::Prec Prec); + Node *parseIntegerLiteral(std::string_view Lit); Node *parseExprPrimary(); template <class Float> Node *parseFloatingLiteral(); Node *parseFunctionParam(); - Node *parseNewExpr(); Node *parseConversionExpr(); Node *parseBracedExpr(); Node *parseFoldExpr(); + Node *parsePointerToMemberConversionExpr(Node::Prec Prec); + Node *parseSubobjectExpr(); /// Parse the <type> production. Node *parseType(); @@ -2520,17 +2578,81 @@ template <typename Derived, typename Alloc> struct AbstractManglingParser { Node *parseName(NameState *State = nullptr); Node *parseLocalName(NameState *State); Node *parseOperatorName(NameState *State); - Node *parseUnqualifiedName(NameState *State); + bool parseModuleNameOpt(ModuleName *&Module); + Node *parseUnqualifiedName(NameState *State, Node *Scope, ModuleName *Module); Node *parseUnnamedTypeName(NameState *State); Node *parseSourceName(NameState *State); - Node *parseUnscopedName(NameState *State); + Node *parseUnscopedName(NameState *State, bool *isSubstName); Node *parseNestedName(NameState *State); Node *parseCtorDtorName(Node *&SoFar, NameState *State); Node *parseAbiTags(Node *N); + struct OperatorInfo { + enum OIKind : unsigned char { + Prefix, // Prefix unary: @ expr + Postfix, // Postfix unary: expr @ + Binary, // Binary: lhs @ rhs + Array, // Array index: lhs [ rhs ] + Member, // Member access: lhs @ rhs + New, // New + Del, // Delete + Call, // Function call: expr (expr*) + CCast, // C cast: (type)expr + Conditional, // Conditional: expr ? expr : expr + NameOnly, // Overload only, not allowed in expression. + // Below do not have operator names + NamedCast, // Named cast, @<type>(expr) + OfIdOp, // alignof, sizeof, typeid + + Unnameable = NamedCast, + }; + char Enc[2]; // Encoding + OIKind Kind; // Kind of operator + bool Flag : 1; // Entry-specific flag + Node::Prec Prec : 7; // Precedence + const char *Name; // Spelling + + public: + constexpr OperatorInfo(const char (&E)[3], OIKind K, bool F, Node::Prec P, + const char *N) + : Enc{E[0], E[1]}, Kind{K}, Flag{F}, Prec{P}, Name{N} {} + + public: + bool operator<(const OperatorInfo &Other) const { + return *this < Other.Enc; + } + bool operator<(const char *Peek) const { + return Enc[0] < Peek[0] || (Enc[0] == Peek[0] && Enc[1] < Peek[1]); + } + bool operator==(const char *Peek) const { + return Enc[0] == Peek[0] && Enc[1] == Peek[1]; + } + bool operator!=(const char *Peek) const { return !this->operator==(Peek); } + + public: + std::string_view getSymbol() const { + std::string_view Res = Name; + if (Kind < Unnameable) { + assert(llvm::itanium_demangle::starts_with(Res, "operator") && + "operator name does not start with 'operator'"); + Res.remove_prefix(sizeof("operator") - 1); + if (llvm::itanium_demangle::starts_with(Res, ' ')) + Res.remove_prefix(1); + } + return Res; + } + std::string_view getName() const { return Name; } + OIKind getKind() const { return Kind; } + bool getFlag() const { return Flag; } + Node::Prec getPrecedence() const { return Prec; } + }; + static const OperatorInfo Ops[]; + static const size_t NumOps; + const OperatorInfo *parseOperatorEncoding(); + /// Parse the <unresolved-name> production. - Node *parseUnresolvedName(); + Node *parseUnresolvedName(bool Global); Node *parseSimpleId(); Node *parseBaseUnresolvedName(); Node *parseUnresolvedType(); @@ -2551,41 +2673,35 @@ const char* parse_discriminator(const char* first, const char* last); // ::= <substitution> template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseName(NameState *State) { - consumeIf('L'); // extension - if (look() == 'N') return getDerived().parseNestedName(State); if (look() == 'Z') return getDerived().parseLocalName(State); - // ::= <unscoped-template-name> <template-args> - if (look() == 'S' && look(1) != 't') { - Node *S = getDerived().parseSubstitution(); - if (S == nullptr) - return nullptr; - if (look() != 'I') - return nullptr; - Node *TA = getDerived().parseTemplateArgs(State != nullptr); - if (TA == nullptr) - return nullptr; - if (State) State->EndsWithTemplateArgs = true; - return make<NameWithTemplateArgs>(S, TA); - } + Node *Result = nullptr; + bool IsSubst = false; - Node *N = getDerived().parseUnscopedName(State); - if (N == nullptr) + Result = getDerived().parseUnscopedName(State, &IsSubst); + if (!Result) return nullptr; - // ::= <unscoped-template-name> <template-args> + if (look() == 'I') { - Subs.push_back(N); + // ::= <unscoped-template-name> <template-args> + if (!IsSubst) + // An unscoped-template-name is substitutable. + Subs.push_back(Result); Node *TA = getDerived().parseTemplateArgs(State != nullptr); if (TA == nullptr) return nullptr; - if (State) State->EndsWithTemplateArgs = true; - return make<NameWithTemplateArgs>(N, TA); + if (State) + State->EndsWithTemplateArgs = true; + Result = make<NameWithTemplateArgs>(Result, TA); + } else if (IsSubst) { + // The substitution case must be followed by <template-args>. + return nullptr; } - // ::= <unscoped-name> - return N; + + return Result; } // <local-name> := Z <function encoding> E <entity name> [<discriminator>] @@ -2626,34 +2742,63 @@ Node *AbstractManglingParser<Derived, Alloc>::parseLocalName(NameState *State) { // <unscoped-name> ::= <unqualified-name> // ::= St <unqualified-name> # ::std:: -// extension ::= StL<unqualified-name> +// [*] extension template <typename Derived, typename Alloc> Node * -AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State) { - if (consumeIf("StL") || consumeIf("St")) { - Node *R = getDerived().parseUnqualifiedName(State); - if (R == nullptr) +AbstractManglingParser<Derived, Alloc>::parseUnscopedName(NameState *State, + bool *IsSubst) { + + Node *Std = nullptr; + if (consumeIf("St")) { + Std = make<NameType>("std"); + if (Std == nullptr) return nullptr; - return make<StdQualifiedName>(R); } - return getDerived().parseUnqualifiedName(State); + + Node *Res = nullptr; + ModuleName *Module = nullptr; + if (look() == 'S') { + Node *S = getDerived().parseSubstitution(); + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) + Module = static_cast<ModuleName *>(S); + else if (IsSubst && Std == nullptr) { + Res = S; + *IsSubst = true; + } else { + return nullptr; + } + } + + if (Res == nullptr || Std != nullptr) { + Res = getDerived().parseUnqualifiedName(State, Std, Module); + } + + return Res; } -// <unqualified-name> ::= <operator-name> [abi-tags] -// ::= <ctor-dtor-name> -// ::= <source-name> -// ::= <unnamed-type-name> -// ::= DC <source-name>+ E # structured binding declaration +// <unqualified-name> ::= [<module-name>] L? <operator-name> [<abi-tags>] +// ::= [<module-name>] <ctor-dtor-name> [<abi-tags>] +// ::= [<module-name>] L? <source-name> [<abi-tags>] +// ::= [<module-name>] L? <unnamed-type-name> [<abi-tags>] +// # structured binding declaration +// ::= [<module-name>] L? DC <source-name>+ E template <typename Derived, typename Alloc> -Node * -AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName(NameState *State) { - // <ctor-dtor-name>s are special-cased in parseNestedName(). +Node *AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName( + NameState *State, Node *Scope, ModuleName *Module) { + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + + consumeIf('L'); + Node *Result; - if (look() == 'U') - Result = getDerived().parseUnnamedTypeName(State); - else if (look() >= '1' && look() <= '9') + if (look() >= '1' && look() <= '9') { Result = getDerived().parseSourceName(State); - else if (consumeIf("DC")) { + } else if (look() == 'U') { + Result = getDerived().parseUnnamedTypeName(State); + } else if (consumeIf("DC")) { + // Structured binding size_t BindingsBegin = Names.size(); do { Node *Binding = getDerived().parseSourceName(State); @@ -2662,13 +2807,46 @@ AbstractManglingParser<Derived, Alloc>::parseUnqualifiedName(NameState *State) { Names.push_back(Binding); } while (!consumeIf('E')); Result = make<StructuredBindingName>(popTrailingNodeArray(BindingsBegin)); - } else + } else if (look() == 'C' || look() == 'D') { + // A <ctor-dtor-name>. + if (Scope == nullptr || Module != nullptr) + return nullptr; + Result = getDerived().parseCtorDtorName(Scope, State); + } else { Result = getDerived().parseOperatorName(State); + } + + if (Result != nullptr && Module != nullptr) + Result = make<ModuleEntity>(Module, Result); if (Result != nullptr) Result = getDerived().parseAbiTags(Result); + if (Result != nullptr && Scope != nullptr) + Result = make<NestedName>(Scope, Result); + return Result; } +// <module-name> ::= <module-subname> +// ::= <module-name> <module-subname> +// ::= <substitution> # passed in by caller +// <module-subname> ::= W <source-name> +// ::= W P <source-name> +template <typename Derived, typename Alloc> +bool AbstractManglingParser<Derived, Alloc>::parseModuleNameOpt( + ModuleName *&Module) { + while (consumeIf('W')) { + bool IsPartition = consumeIf('P'); + Node *Sub = getDerived().parseSourceName(nullptr); + if (!Sub) + return true; + Module = + static_cast<ModuleName *>(make<ModuleName>(Module, Sub, IsPartition)); + Subs.push_back(Module); + } + + return false; +} + // <unnamed-type-name> ::= Ut [<nonnegative number>] _ // ::= <closure-type-name> // @@ -2684,19 +2862,19 @@ AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) { TemplateParams.clear(); if (consumeIf("Ut")) { - StringView Count = parseNumber(); + std::string_view Count = parseNumber(); if (!consumeIf('_')) return nullptr; return make<UnnamedTypeName>(Count); } if (consumeIf("Ul")) { - SwapAndRestore<size_t> SwapParams(ParsingLambdaParamsAtLevel, + ScopedOverride<size_t> SwapParams(ParsingLambdaParamsAtLevel, TemplateParams.size()); ScopedTemplateParamList LambdaTemplateParams(this); size_t ParamsBegin = Names.size(); while (look() == 'T' && - StringView("yptn").find(look(1)) != StringView::npos) { + std::string_view("yptn").find(look(1)) != std::string_view::npos) { Node *T = parseTemplateParamDecl(); if (!T) return nullptr; @@ -2739,7 +2917,7 @@ AbstractManglingParser<Derived, Alloc>::parseUnnamedTypeName(NameState *State) { } NodeArray Params = popTrailingNodeArray(ParamsBegin); - StringView Count = parseNumber(); + std::string_view Count = parseNumber(); if (!consumeIf('_')) return nullptr; return make<ClosureTypeName>(TempParams, Params, Count); @@ -2761,104 +2939,138 @@ Node *AbstractManglingParser<Derived, Alloc>::parseSourceName(NameState *) { return nullptr; if (numLeft() < Length || Length == 0) return nullptr; - StringView Name(First, First + Length); + std::string_view Name(First, Length); First += Length; - if (Name.startsWith("_GLOBAL__N")) + if (llvm::itanium_demangle::starts_with(Name, "_GLOBAL__N")) return make<NameType>("(anonymous namespace)"); return make<NameType>(Name); } -// <operator-name> ::= aa # && -// ::= ad # & (unary) -// ::= an # & -// ::= aN # &= -// ::= aS # = -// ::= cl # () -// ::= cm # , -// ::= co # ~ -// ::= cv <type> # (cast) -// ::= da # delete[] -// ::= de # * (unary) -// ::= dl # delete -// ::= dv # / -// ::= dV # /= -// ::= eo # ^ -// ::= eO # ^= -// ::= eq # == -// ::= ge # >= -// ::= gt # > -// ::= ix # [] -// ::= le # <= +// Operator encodings +template <typename Derived, typename Alloc> +const typename AbstractManglingParser< + Derived, Alloc>::OperatorInfo AbstractManglingParser<Derived, + Alloc>::Ops[] = { + // Keep ordered by encoding + {"aN", OperatorInfo::Binary, false, Node::Prec::Assign, "operator&="}, + {"aS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator="}, + {"aa", OperatorInfo::Binary, false, Node::Prec::AndIf, "operator&&"}, + {"ad", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator&"}, + {"an", OperatorInfo::Binary, false, Node::Prec::And, "operator&"}, + {"at", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "alignof "}, + {"aw", OperatorInfo::NameOnly, false, Node::Prec::Primary, + "operator co_await"}, + {"az", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "alignof "}, + {"cc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "const_cast"}, + {"cl", OperatorInfo::Call, false, Node::Prec::Postfix, "operator()"}, + {"cm", OperatorInfo::Binary, false, Node::Prec::Comma, "operator,"}, + {"co", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator~"}, + {"cv", OperatorInfo::CCast, false, Node::Prec::Cast, "operator"}, // C Cast + {"dV", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/="}, + {"da", OperatorInfo::Del, /*Ary*/ true, Node::Prec::Unary, + "operator delete[]"}, + {"dc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "dynamic_cast"}, + {"de", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator*"}, + {"dl", OperatorInfo::Del, /*Ary*/ false, Node::Prec::Unary, + "operator delete"}, + {"ds", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator.*"}, + {"dt", OperatorInfo::Member, /*Named*/ false, Node::Prec::Postfix, + "operator."}, + {"dv", OperatorInfo::Binary, false, Node::Prec::Assign, "operator/"}, + {"eO", OperatorInfo::Binary, false, Node::Prec::Assign, "operator^="}, + {"eo", OperatorInfo::Binary, false, Node::Prec::Xor, "operator^"}, + {"eq", OperatorInfo::Binary, false, Node::Prec::Equality, "operator=="}, + {"ge", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>="}, + {"gt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator>"}, + {"ix", OperatorInfo::Array, false, Node::Prec::Postfix, "operator[]"}, + {"lS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator<<="}, + {"le", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<="}, + {"ls", OperatorInfo::Binary, false, Node::Prec::Shift, "operator<<"}, + {"lt", OperatorInfo::Binary, false, Node::Prec::Relational, "operator<"}, + {"mI", OperatorInfo::Binary, false, Node::Prec::Assign, "operator-="}, + {"mL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator*="}, + {"mi", OperatorInfo::Binary, false, Node::Prec::Additive, "operator-"}, + {"ml", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator*"}, + {"mm", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator--"}, + {"na", OperatorInfo::New, /*Ary*/ true, Node::Prec::Unary, + "operator new[]"}, + {"ne", OperatorInfo::Binary, false, Node::Prec::Equality, "operator!="}, + {"ng", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator-"}, + {"nt", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator!"}, + {"nw", OperatorInfo::New, /*Ary*/ false, Node::Prec::Unary, "operator new"}, + {"oR", OperatorInfo::Binary, false, Node::Prec::Assign, "operator|="}, + {"oo", OperatorInfo::Binary, false, Node::Prec::OrIf, "operator||"}, + {"or", OperatorInfo::Binary, false, Node::Prec::Ior, "operator|"}, + {"pL", OperatorInfo::Binary, false, Node::Prec::Assign, "operator+="}, + {"pl", OperatorInfo::Binary, false, Node::Prec::Additive, "operator+"}, + {"pm", OperatorInfo::Member, /*Named*/ false, Node::Prec::PtrMem, + "operator->*"}, + {"pp", OperatorInfo::Postfix, false, Node::Prec::Postfix, "operator++"}, + {"ps", OperatorInfo::Prefix, false, Node::Prec::Unary, "operator+"}, + {"pt", OperatorInfo::Member, /*Named*/ true, Node::Prec::Postfix, + "operator->"}, + {"qu", OperatorInfo::Conditional, false, Node::Prec::Conditional, + "operator?"}, + {"rM", OperatorInfo::Binary, false, Node::Prec::Assign, "operator%="}, + {"rS", OperatorInfo::Binary, false, Node::Prec::Assign, "operator>>="}, + {"rc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, + "reinterpret_cast"}, + {"rm", OperatorInfo::Binary, false, Node::Prec::Multiplicative, + "operator%"}, + {"rs", OperatorInfo::Binary, false, Node::Prec::Shift, "operator>>"}, + {"sc", OperatorInfo::NamedCast, false, Node::Prec::Postfix, "static_cast"}, + {"ss", OperatorInfo::Binary, false, Node::Prec::Spaceship, "operator<=>"}, + {"st", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Unary, "sizeof "}, + {"sz", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Unary, "sizeof "}, + {"te", OperatorInfo::OfIdOp, /*Type*/ false, Node::Prec::Postfix, + "typeid "}, + {"ti", OperatorInfo::OfIdOp, /*Type*/ true, Node::Prec::Postfix, "typeid "}, +}; +template <typename Derived, typename Alloc> +const size_t AbstractManglingParser<Derived, Alloc>::NumOps = sizeof(Ops) / + sizeof(Ops[0]); + +// If the next 2 chars are an operator encoding, consume them and return their +// OperatorInfo. Otherwise return nullptr. +template <typename Derived, typename Alloc> +const typename AbstractManglingParser<Derived, Alloc>::OperatorInfo * +AbstractManglingParser<Derived, Alloc>::parseOperatorEncoding() { + if (numLeft() < 2) + return nullptr; + + // We can't use lower_bound as that can link to symbols in the C++ library, + // and this must remain independant of that. + size_t lower = 0u, upper = NumOps - 1; // Inclusive bounds. + while (upper != lower) { + size_t middle = (upper + lower) / 2; + if (Ops[middle] < First) + lower = middle + 1; + else + upper = middle; + } + if (Ops[lower] != First) + return nullptr; + + First += 2; + return &Ops[lower]; +} + +// <operator-name> ::= See parseOperatorEncoding() // ::= li <source-name> # operator "" -// ::= ls # << -// ::= lS # <<= -// ::= lt # < -// ::= mi # - -// ::= mI # -= -// ::= ml # * -// ::= mL # *= -// ::= mm # -- (postfix in <expression> context) -// ::= na # new[] -// ::= ne # != -// ::= ng # - (unary) -// ::= nt # ! -// ::= nw # new -// ::= oo # || -// ::= or # | -// ::= oR # |= -// ::= pm # ->* -// ::= pl # + -// ::= pL # += -// ::= pp # ++ (postfix in <expression> context) -// ::= ps # + (unary) -// ::= pt # -> -// ::= qu # ? -// ::= rm # % -// ::= rM # %= -// ::= rs # >> -// ::= rS # >>= -// ::= ss # <=> C++2a -// ::= v <digit> <source-name> # vendor extended operator +// ::= v <digit> <source-name> # vendor extended operator template <typename Derived, typename Alloc> Node * AbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) { - switch (look()) { - case 'a': - switch (look(1)) { - case 'a': - First += 2; - return make<NameType>("operator&&"); - case 'd': - case 'n': - First += 2; - return make<NameType>("operator&"); - case 'N': - First += 2; - return make<NameType>("operator&="); - case 'S': - First += 2; - return make<NameType>("operator="); - } - return nullptr; - case 'c': - switch (look(1)) { - case 'l': - First += 2; - return make<NameType>("operator()"); - case 'm': - First += 2; - return make<NameType>("operator,"); - case 'o': - First += 2; - return make<NameType>("operator~"); - // ::= cv <type> # (cast) - case 'v': { - First += 2; - SwapAndRestore<bool> SaveTemplate(TryToParseTemplateArgs, false); + if (const auto *Op = parseOperatorEncoding()) { + if (Op->getKind() == OperatorInfo::CCast) { + // ::= cv <type> # (cast) + ScopedOverride<bool> SaveTemplate(TryToParseTemplateArgs, false); // If we're parsing an encoding, State != nullptr and the conversion // operators' <type> could have a <template-param> that refers to some // <template-arg>s further ahead in the mangled name. - SwapAndRestore<bool> SavePermit(PermitForwardTemplateReferences, + ScopedOverride<bool> SavePermit(PermitForwardTemplateReferences, PermitForwardTemplateReferences || State != nullptr); Node *Ty = getDerived().parseType(); @@ -2867,185 +3079,29 @@ AbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) { if (State) State->CtorDtorConversion = true; return make<ConversionOperatorType>(Ty); } - } - return nullptr; - case 'd': - switch (look(1)) { - case 'a': - First += 2; - return make<NameType>("operator delete[]"); - case 'e': - First += 2; - return make<NameType>("operator*"); - case 'l': - First += 2; - return make<NameType>("operator delete"); - case 'v': - First += 2; - return make<NameType>("operator/"); - case 'V': - First += 2; - return make<NameType>("operator/="); - } - return nullptr; - case 'e': - switch (look(1)) { - case 'o': - First += 2; - return make<NameType>("operator^"); - case 'O': - First += 2; - return make<NameType>("operator^="); - case 'q': - First += 2; - return make<NameType>("operator=="); - } - return nullptr; - case 'g': - switch (look(1)) { - case 'e': - First += 2; - return make<NameType>("operator>="); - case 't': - First += 2; - return make<NameType>("operator>"); - } - return nullptr; - case 'i': - if (look(1) == 'x') { - First += 2; - return make<NameType>("operator[]"); - } - return nullptr; - case 'l': - switch (look(1)) { - case 'e': - First += 2; - return make<NameType>("operator<="); + + if (Op->getKind() >= OperatorInfo::Unnameable) + /* Not a nameable operator. */ + return nullptr; + if (Op->getKind() == OperatorInfo::Member && !Op->getFlag()) + /* Not a nameable MemberExpr */ + return nullptr; + + return make<NameType>(Op->getName()); + } + + if (consumeIf("li")) { // ::= li <source-name> # operator "" - case 'i': { - First += 2; - Node *SN = getDerived().parseSourceName(State); - if (SN == nullptr) - return nullptr; - return make<LiteralOperator>(SN); - } - case 's': - First += 2; - return make<NameType>("operator<<"); - case 'S': - First += 2; - return make<NameType>("operator<<="); - case 't': - First += 2; - return make<NameType>("operator<"); - } - return nullptr; - case 'm': - switch (look(1)) { - case 'i': - First += 2; - return make<NameType>("operator-"); - case 'I': - First += 2; - return make<NameType>("operator-="); - case 'l': - First += 2; - return make<NameType>("operator*"); - case 'L': - First += 2; - return make<NameType>("operator*="); - case 'm': - First += 2; - return make<NameType>("operator--"); - } - return nullptr; - case 'n': - switch (look(1)) { - case 'a': - First += 2; - return make<NameType>("operator new[]"); - case 'e': - First += 2; - return make<NameType>("operator!="); - case 'g': - First += 2; - return make<NameType>("operator-"); - case 't': - First += 2; - return make<NameType>("operator!"); - case 'w': - First += 2; - return make<NameType>("operator new"); - } - return nullptr; - case 'o': - switch (look(1)) { - case 'o': - First += 2; - return make<NameType>("operator||"); - case 'r': - First += 2; - return make<NameType>("operator|"); - case 'R': - First += 2; - return make<NameType>("operator|="); - } - return nullptr; - case 'p': - switch (look(1)) { - case 'm': - First += 2; - return make<NameType>("operator->*"); - case 'l': - First += 2; - return make<NameType>("operator+"); - case 'L': - First += 2; - return make<NameType>("operator+="); - case 'p': - First += 2; - return make<NameType>("operator++"); - case 's': - First += 2; - return make<NameType>("operator+"); - case 't': - First += 2; - return make<NameType>("operator->"); - } - return nullptr; - case 'q': - if (look(1) == 'u') { - First += 2; - return make<NameType>("operator?"); - } - return nullptr; - case 'r': - switch (look(1)) { - case 'm': - First += 2; - return make<NameType>("operator%"); - case 'M': - First += 2; - return make<NameType>("operator%="); - case 's': - First += 2; - return make<NameType>("operator>>"); - case 'S': - First += 2; - return make<NameType>("operator>>="); - } - return nullptr; - case 's': - if (look(1) == 's') { - First += 2; - return make<NameType>("operator<=>"); - } - return nullptr; - // ::= v <digit> <source-name> # vendor extended operator - case 'v': - if (std::isdigit(look(1))) { - First += 2; + Node *SN = getDerived().parseSourceName(State); + if (SN == nullptr) + return nullptr; + return make<LiteralOperator>(SN); + } + + if (consumeIf('v')) { + // ::= v <digit> <source-name> # vendor extended operator + if (look() >= '0' && look() <= '9') { + First++; Node *SN = getDerived().parseSourceName(State); if (SN == nullptr) return nullptr; @@ -3053,6 +3109,7 @@ AbstractManglingParser<Derived, Alloc>::parseOperatorName(NameState *State) { } return nullptr; } + return nullptr; } @@ -3071,19 +3128,11 @@ Node * AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar, NameState *State) { if (SoFar->getKind() == Node::KSpecialSubstitution) { - auto SSK = static_cast<SpecialSubstitution *>(SoFar)->SSK; - switch (SSK) { - case SpecialSubKind::string: - case SpecialSubKind::istream: - case SpecialSubKind::ostream: - case SpecialSubKind::iostream: - SoFar = make<ExpandedSpecialSubstitution>(SSK); - if (!SoFar) - return nullptr; - break; - default: - break; - } + // Expand the special substitution. + SoFar = make<ExpandedSpecialSubstitution>( + static_cast<SpecialSubstitution *>(SoFar)); + if (!SoFar) + return nullptr; } if (consumeIf('C')) { @@ -3112,8 +3161,10 @@ AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar, return nullptr; } -// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E -// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E +// <nested-name> ::= N [<CV-Qualifiers>] [<ref-qualifier>] <prefix> +// <unqualified-name> E +// ::= N [<CV-Qualifiers>] [<ref-qualifier>] <template-prefix> +// <template-args> E // // <prefix> ::= <prefix> <unqualified-name> // ::= <template-prefix> <template-args> @@ -3122,7 +3173,7 @@ AbstractManglingParser<Derived, Alloc>::parseCtorDtorName(Node *&SoFar, // ::= # empty // ::= <substitution> // ::= <prefix> <data-member-prefix> -// extension ::= L +// [*] extension // // <data-member-prefix> := <member source-name> [<template-args>] M // @@ -3142,90 +3193,76 @@ AbstractManglingParser<Derived, Alloc>::parseNestedName(NameState *State) { if (State) State->ReferenceQualifier = FrefQualRValue; } else if (consumeIf('R')) { if (State) State->ReferenceQualifier = FrefQualLValue; - } else + } else { if (State) State->ReferenceQualifier = FrefQualNone; - - Node *SoFar = nullptr; - auto PushComponent = [&](Node *Comp) { - if (!Comp) return false; - if (SoFar) SoFar = make<NestedName>(SoFar, Comp); - else SoFar = Comp; - if (State) State->EndsWithTemplateArgs = false; - return SoFar != nullptr; - }; - - if (consumeIf("St")) { - SoFar = make<NameType>("std"); - if (!SoFar) - return nullptr; } + Node *SoFar = nullptr; while (!consumeIf('E')) { - consumeIf('L'); // extension + if (State) + // Only set end-with-template on the case that does that. + State->EndsWithTemplateArgs = false; - // <data-member-prefix> := <member source-name> [<template-args>] M - if (consumeIf('M')) { - if (SoFar == nullptr) - return nullptr; - continue; - } - - // ::= <template-param> if (look() == 'T') { - if (!PushComponent(getDerived().parseTemplateParam())) - return nullptr; - Subs.push_back(SoFar); - continue; - } - - // ::= <template-prefix> <template-args> - if (look() == 'I') { + // ::= <template-param> + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseTemplateParam(); + } else if (look() == 'I') { + // ::= <template-prefix> <template-args> + if (SoFar == nullptr) + return nullptr; // Must have a prefix. Node *TA = getDerived().parseTemplateArgs(State != nullptr); - if (TA == nullptr || SoFar == nullptr) - return nullptr; - SoFar = make<NameWithTemplateArgs>(SoFar, TA); - if (!SoFar) - return nullptr; - if (State) State->EndsWithTemplateArgs = true; - Subs.push_back(SoFar); - continue; - } - - // ::= <decltype> - if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) { - if (!PushComponent(getDerived().parseDecltype())) + if (TA == nullptr) return nullptr; - Subs.push_back(SoFar); - continue; - } - - // ::= <substitution> - if (look() == 'S' && look(1) != 't') { - Node *S = getDerived().parseSubstitution(); - if (!PushComponent(S)) + if (SoFar->getKind() == Node::KNameWithTemplateArgs) + // Semantically <template-args> <template-args> cannot be generated by a + // C++ entity. There will always be [something like] a name between + // them. return nullptr; - if (SoFar != S) - Subs.push_back(S); - continue; - } + if (State) + State->EndsWithTemplateArgs = true; + SoFar = make<NameWithTemplateArgs>(SoFar, TA); + } else if (look() == 'D' && (look(1) == 't' || look(1) == 'T')) { + // ::= <decltype> + if (SoFar != nullptr) + return nullptr; // Cannot have a prefix. + SoFar = getDerived().parseDecltype(); + } else { + ModuleName *Module = nullptr; + + if (look() == 'S') { + // ::= <substitution> + Node *S = nullptr; + if (look(1) == 't') { + First += 2; + S = make<NameType>("std"); + } else { + S = getDerived().parseSubstitution(); + } + if (!S) + return nullptr; + if (S->getKind() == Node::KModuleName) { + Module = static_cast<ModuleName *>(S); + } else if (SoFar != nullptr) { + return nullptr; // Cannot have a prefix. + } else { + SoFar = S; + continue; // Do not push a new substitution. + } + } - // Parse an <unqualified-name> thats actually a <ctor-dtor-name>. - if (look() == 'C' || (look() == 'D' && look(1) != 'C')) { - if (SoFar == nullptr) - return nullptr; - if (!PushComponent(getDerived().parseCtorDtorName(SoFar, State))) - return nullptr; - SoFar = getDerived().parseAbiTags(SoFar); - if (SoFar == nullptr) - return nullptr; - Subs.push_back(SoFar); - continue; + // ::= [<prefix>] <unqualified-name> + SoFar = getDerived().parseUnqualifiedName(State, SoFar, Module); } - // ::= <prefix> <unqualified-name> - if (!PushComponent(getDerived().parseUnqualifiedName(State))) + if (SoFar == nullptr) return nullptr; Subs.push_back(SoFar); + + // No longer used. + // <data-member-prefix> := <member source-name> [<template-args>] M + consumeIf('M'); } if (SoFar == nullptr || Subs.empty()) @@ -3320,6 +3357,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseBaseUnresolvedName() { // ::= [gs] <base-unresolved-name> # x or (with "gs") ::x // ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> // # A::x, N::y, A<T>::z; "gs" means leading "::" +// [gs] has been parsed by caller. // ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x // extension ::= sr <unresolved-type> <template-args> <base-unresolved-name> // # T::N::x /decltype(p)::N::x @@ -3327,7 +3365,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseBaseUnresolvedName() { // // <unresolved-qualifier-level> ::= <simple-id> template <typename Derived, typename Alloc> -Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName() { +Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName(bool Global) { Node *SoFar = nullptr; // srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> @@ -3361,8 +3399,6 @@ Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName() { return make<QualifiedName>(SoFar, Base); } - bool Global = consumeIf("gs"); - // [gs] <base-unresolved-name> # x or (with "gs") ::x if (!consumeIf("sr")) { SoFar = getDerived().parseBaseUnresolvedName(); @@ -3419,7 +3455,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseUnresolvedName() { template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseAbiTags(Node *N) { while (consumeIf('B')) { - StringView SN = parseBareSourceName(); + std::string_view SN = parseBareSourceName(); if (SN.empty()) return nullptr; N = make<AbiTagAttr>(N, SN); @@ -3431,16 +3467,16 @@ Node *AbstractManglingParser<Derived, Alloc>::parseAbiTags(Node *N) { // <number> ::= [n] <non-negative decimal integer> template <typename Alloc, typename Derived> -StringView +std::string_view AbstractManglingParser<Alloc, Derived>::parseNumber(bool AllowNegative) { const char *Tmp = First; if (AllowNegative) consumeIf('n'); if (numLeft() == 0 || !std::isdigit(*First)) - return StringView(); + return std::string_view(); while (numLeft() != 0 && std::isdigit(*First)) ++First; - return StringView(Tmp, First); + return std::string_view(Tmp, First - Tmp); } // <positive length number> ::= [0-9]* @@ -3457,11 +3493,11 @@ bool AbstractManglingParser<Alloc, Derived>::parsePositiveInteger(size_t *Out) { } template <typename Alloc, typename Derived> -StringView AbstractManglingParser<Alloc, Derived>::parseBareSourceName() { +std::string_view AbstractManglingParser<Alloc, Derived>::parseBareSourceName() { size_t Int = 0; if (parsePositiveInteger(&Int) || numLeft() < Int) - return StringView(); - StringView R(First, First + Int); + return {}; + std::string_view R(First, Int); First += Int; return R; } @@ -3549,7 +3585,9 @@ Node *AbstractManglingParser<Derived, Alloc>::parseVectorType() { if (!consumeIf("Dv")) return nullptr; if (look() >= '1' && look() <= '9') { - StringView DimensionNumber = parseNumber(); + Node *DimensionNumber = make<NameType>(parseNumber()); + if (!DimensionNumber) + return nullptr; if (!consumeIf('_')) return nullptr; if (consumeIf('p')) @@ -3574,7 +3612,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseVectorType() { Node *ElemType = getDerived().parseType(); if (!ElemType) return nullptr; - return make<VectorType>(ElemType, StringView()); + return make<VectorType>(ElemType, /*Dimension=*/nullptr); } // <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) @@ -3590,7 +3628,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseDecltype() { return nullptr; if (!consumeIf('E')) return nullptr; - return make<EnclosingExpr>("decltype(", E, ")"); + return make<EnclosingExpr>("decltype", E); } // <array-type> ::= A <positive dimension number> _ <element type> @@ -3600,10 +3638,12 @@ Node *AbstractManglingParser<Derived, Alloc>::parseArrayType() { if (!consumeIf('A')) return nullptr; - NodeOrString Dimension; + Node *Dimension = nullptr; if (std::isdigit(look())) { - Dimension = parseNumber(); + Dimension = make<NameType>(parseNumber()); + if (!Dimension) + return nullptr; if (!consumeIf('_')) return nullptr; } else if (!consumeIf('_')) { @@ -3641,7 +3681,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parsePointerToMemberType() { // ::= Te <name> # dependent elaborated type specifier using 'enum' template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseClassEnumType() { - StringView ElabSpef; + std::string_view ElabSpef; if (consumeIf("Ts")) ElabSpef = "struct"; else if (consumeIf("Tu")) @@ -3665,19 +3705,18 @@ Node *AbstractManglingParser<Derived, Alloc>::parseClassEnumType() { template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseQualifiedType() { if (consumeIf('U')) { - StringView Qual = parseBareSourceName(); + std::string_view Qual = parseBareSourceName(); if (Qual.empty()) return nullptr; - // FIXME parse the optional <template-args> here! - // extension ::= U <objc-name> <objc-type> # objc-type<identifier> - if (Qual.startsWith("objcproto")) { - StringView ProtoSourceName = Qual.dropFront(std::strlen("objcproto")); - StringView Proto; + if (llvm::itanium_demangle::starts_with(Qual, "objcproto")) { + constexpr size_t Len = sizeof("objcproto") - 1; + std::string_view ProtoSourceName(Qual.data() + Len, Qual.size() - Len); + std::string_view Proto; { - SwapAndRestore<const char *> SaveFirst(First, ProtoSourceName.begin()), - SaveLast(Last, ProtoSourceName.end()); + ScopedOverride<const char *> SaveFirst(First, ProtoSourceName.data()), + SaveLast(Last, &*ProtoSourceName.rbegin() + 1); Proto = parseBareSourceName(); } if (Proto.empty()) @@ -3688,10 +3727,17 @@ Node *AbstractManglingParser<Derived, Alloc>::parseQualifiedType() { return make<ObjCProtoName>(Child, Proto); } + Node *TA = nullptr; + if (look() == 'I') { + TA = getDerived().parseTemplateArgs(); + if (TA == nullptr) + return nullptr; + } + Node *Child = getDerived().parseQualifiedType(); if (Child == nullptr) return nullptr; - return make<VendorExtQualType>(Child, Qual); + return make<VendorExtQualType>(Child, Qual, TA); } Qualifiers Quals = parseCVQualifiers(); @@ -3838,7 +3884,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() { // <builtin-type> ::= u <source-name> # vendor extended type case 'u': { ++First; - StringView Res = parseBareSourceName(); + std::string_view Res = parseBareSourceName(); if (Res.empty()) return nullptr; // Typically, <builtin-type>s are not considered substitution candidates, @@ -3864,7 +3910,33 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() { // ::= Dh # IEEE 754r half-precision floating point (16 bits) case 'h': First += 2; - return make<NameType>("decimal16"); + return make<NameType>("half"); + // ::= DF <number> _ # ISO/IEC TS 18661 binary floating point (N bits) + case 'F': { + First += 2; + Node *DimensionNumber = make<NameType>(parseNumber()); + if (!DimensionNumber) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make<BinaryFPType>(DimensionNumber); + } + // ::= DB <number> _ # C23 signed _BitInt(N) + // ::= DB <instantiation-dependent expression> _ # C23 signed _BitInt(N) + // ::= DU <number> _ # C23 unsigned _BitInt(N) + // ::= DU <instantiation-dependent expression> _ # C23 unsigned _BitInt(N) + case 'B': + case 'U': { + bool Signed = look(1) == 'B'; + First += 2; + Node *Size = std::isdigit(look()) ? make<NameType>(parseNumber()) + : getDerived().parseExpr(); + if (!Size) + return nullptr; + if (!consumeIf('_')) + return nullptr; + return make<BitIntType>(Size, Signed); + } // ::= Di # char32_t case 'i': First += 2; @@ -4012,9 +4084,10 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() { } // ::= <substitution> # See Compression below case 'S': { - if (look(1) && look(1) != 't') { - Node *Sub = getDerived().parseSubstitution(); - if (Sub == nullptr) + if (look(1) != 't') { + bool IsSubst = false; + Result = getDerived().parseUnscopedName(nullptr, &IsSubst); + if (!Result) return nullptr; // Sub could be either of: @@ -4027,17 +4100,19 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() { // If this is followed by some <template-args>, and we're permitted to // parse them, take the second production. - if (TryToParseTemplateArgs && look() == 'I') { + if (look() == 'I' && (!IsSubst || TryToParseTemplateArgs)) { + if (!IsSubst) + Subs.push_back(Result); Node *TA = getDerived().parseTemplateArgs(); if (TA == nullptr) return nullptr; - Result = make<NameWithTemplateArgs>(Sub, TA); - break; + Result = make<NameWithTemplateArgs>(Result, TA); + } else if (IsSubst) { + // If all we parsed was a substitution, don't re-insert into the + // substitution table. + return Result; } - - // If all we parsed was a substitution, don't re-insert into the - // substitution table. - return Sub; + break; } DEMANGLE_FALLTHROUGH; } @@ -4057,28 +4132,32 @@ Node *AbstractManglingParser<Derived, Alloc>::parseType() { } template <typename Derived, typename Alloc> -Node *AbstractManglingParser<Derived, Alloc>::parsePrefixExpr(StringView Kind) { +Node * +AbstractManglingParser<Derived, Alloc>::parsePrefixExpr(std::string_view Kind, + Node::Prec Prec) { Node *E = getDerived().parseExpr(); if (E == nullptr) return nullptr; - return make<PrefixExpr>(Kind, E); + return make<PrefixExpr>(Kind, E, Prec); } template <typename Derived, typename Alloc> -Node *AbstractManglingParser<Derived, Alloc>::parseBinaryExpr(StringView Kind) { +Node * +AbstractManglingParser<Derived, Alloc>::parseBinaryExpr(std::string_view Kind, + Node::Prec Prec) { Node *LHS = getDerived().parseExpr(); if (LHS == nullptr) return nullptr; Node *RHS = getDerived().parseExpr(); if (RHS == nullptr) return nullptr; - return make<BinaryExpr>(LHS, Kind, RHS); + return make<BinaryExpr>(LHS, Kind, RHS, Prec); } template <typename Derived, typename Alloc> -Node * -AbstractManglingParser<Derived, Alloc>::parseIntegerLiteral(StringView Lit) { - StringView Tmp = parseNumber(true); +Node *AbstractManglingParser<Derived, Alloc>::parseIntegerLiteral( + std::string_view Lit) { + std::string_view Tmp = parseNumber(true); if (!Tmp.empty() && consumeIf('E')) return make<IntegerLiteral>(Lit, Tmp); return nullptr; @@ -4101,11 +4180,14 @@ Qualifiers AbstractManglingParser<Alloc, Derived>::parseCVQualifiers() { // ::= fp <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters // ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> _ # L > 0, first parameter // ::= fL <L-1 non-negative number> p <top-level CV-Qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters +// ::= fpT # 'this' expression (not part of standard?) template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() { + if (consumeIf("fpT")) + return make<NameType>("this"); if (consumeIf("fp")) { parseCVQualifiers(); - StringView Num = parseNumber(); + std::string_view Num = parseNumber(); if (!consumeIf('_')) return nullptr; return make<FunctionParam>(Num); @@ -4116,7 +4198,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() { if (!consumeIf('p')) return nullptr; parseCVQualifiers(); - StringView Num = parseNumber(); + std::string_view Num = parseNumber(); if (!consumeIf('_')) return nullptr; return make<FunctionParam>(Num); @@ -4124,43 +4206,6 @@ Node *AbstractManglingParser<Derived, Alloc>::parseFunctionParam() { return nullptr; } -// [gs] nw <expression>* _ <type> E # new (expr-list) type -// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) -// [gs] na <expression>* _ <type> E # new[] (expr-list) type -// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) -// <initializer> ::= pi <expression>* E # parenthesized initialization -template <typename Derived, typename Alloc> -Node *AbstractManglingParser<Derived, Alloc>::parseNewExpr() { - bool Global = consumeIf("gs"); - bool IsArray = look(1) == 'a'; - if (!consumeIf("nw") && !consumeIf("na")) - return nullptr; - size_t Exprs = Names.size(); - while (!consumeIf('_')) { - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return nullptr; - Names.push_back(Ex); - } - NodeArray ExprList = popTrailingNodeArray(Exprs); - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - if (consumeIf("pi")) { - size_t InitsBegin = Names.size(); - while (!consumeIf('E')) { - Node *Init = getDerived().parseExpr(); - if (Init == nullptr) - return Init; - Names.push_back(Init); - } - NodeArray Inits = popTrailingNodeArray(InitsBegin); - return make<NewExpr>(ExprList, Ty, Inits, Global, IsArray); - } else if (!consumeIf('E')) - return nullptr; - return make<NewExpr>(ExprList, Ty, NodeArray(), Global, IsArray); -} - // cv <type> <expression> # conversion with one argument // cv <type> _ <expression>* E # conversion with a different number of arguments template <typename Derived, typename Alloc> @@ -4169,7 +4214,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseConversionExpr() { return nullptr; Node *Ty; { - SwapAndRestore<bool> SaveTemp(TryToParseTemplateArgs, false); + ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false); Ty = getDerived().parseType(); } @@ -4262,7 +4307,13 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() { return getDerived().template parseFloatingLiteral<double>(); case 'e': ++First; +#if defined(__powerpc__) || defined(__s390__) + // Handle cases where long doubles encoded with e have the same size + // and representation as doubles. + return getDerived().template parseFloatingLiteral<double>(); +#else return getDerived().template parseFloatingLiteral<long double>(); +#endif case '_': if (consumeIf("_Z")) { Node *R = getDerived().parseEncoding(); @@ -4280,7 +4331,7 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() { return nullptr; } case 'D': - if (consumeIf("DnE")) + if (consumeIf("Dn") && (consumeIf('0'), consumeIf('E'))) return make<NameType>("nullptr"); return nullptr; case 'T': @@ -4301,12 +4352,12 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExprPrimary() { Node *T = getDerived().parseType(); if (T == nullptr) return nullptr; - StringView N = parseNumber(); + std::string_view N = parseNumber(/*AllowNegative=*/true); if (N.empty()) return nullptr; if (!consumeIf('E')) return nullptr; - return make<IntegerCastExpr>(T, N); + return make<EnumLiteral>(T, N); } } } @@ -4367,55 +4418,38 @@ Node *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() { if (!consumeIf('f')) return nullptr; - char FoldKind = look(); - bool IsLeftFold, HasInitializer; - HasInitializer = FoldKind == 'L' || FoldKind == 'R'; - if (FoldKind == 'l' || FoldKind == 'L') - IsLeftFold = true; - else if (FoldKind == 'r' || FoldKind == 'R') - IsLeftFold = false; - else + bool IsLeftFold = false, HasInitializer = false; + switch (look()) { + default: return nullptr; + case 'L': + IsLeftFold = true; + HasInitializer = true; + break; + case 'R': + HasInitializer = true; + break; + case 'l': + IsLeftFold = true; + break; + case 'r': + break; + } ++First; - // FIXME: This map is duplicated in parseOperatorName and parseExpr. - StringView OperatorName; - if (consumeIf("aa")) OperatorName = "&&"; - else if (consumeIf("an")) OperatorName = "&"; - else if (consumeIf("aN")) OperatorName = "&="; - else if (consumeIf("aS")) OperatorName = "="; - else if (consumeIf("cm")) OperatorName = ","; - else if (consumeIf("ds")) OperatorName = ".*"; - else if (consumeIf("dv")) OperatorName = "/"; - else if (consumeIf("dV")) OperatorName = "/="; - else if (consumeIf("eo")) OperatorName = "^"; - else if (consumeIf("eO")) OperatorName = "^="; - else if (consumeIf("eq")) OperatorName = "=="; - else if (consumeIf("ge")) OperatorName = ">="; - else if (consumeIf("gt")) OperatorName = ">"; - else if (consumeIf("le")) OperatorName = "<="; - else if (consumeIf("ls")) OperatorName = "<<"; - else if (consumeIf("lS")) OperatorName = "<<="; - else if (consumeIf("lt")) OperatorName = "<"; - else if (consumeIf("mi")) OperatorName = "-"; - else if (consumeIf("mI")) OperatorName = "-="; - else if (consumeIf("ml")) OperatorName = "*"; - else if (consumeIf("mL")) OperatorName = "*="; - else if (consumeIf("ne")) OperatorName = "!="; - else if (consumeIf("oo")) OperatorName = "||"; - else if (consumeIf("or")) OperatorName = "|"; - else if (consumeIf("oR")) OperatorName = "|="; - else if (consumeIf("pl")) OperatorName = "+"; - else if (consumeIf("pL")) OperatorName = "+="; - else if (consumeIf("rm")) OperatorName = "%"; - else if (consumeIf("rM")) OperatorName = "%="; - else if (consumeIf("rs")) OperatorName = ">>"; - else if (consumeIf("rS")) OperatorName = ">>="; - else return nullptr; - - Node *Pack = getDerived().parseExpr(), *Init = nullptr; + const auto *Op = parseOperatorEncoding(); + if (!Op) + return nullptr; + if (!(Op->getKind() == OperatorInfo::Binary + || (Op->getKind() == OperatorInfo::Member + && Op->getName().back() == '*'))) + return nullptr; + + Node *Pack = getDerived().parseExpr(); if (Pack == nullptr) return nullptr; + + Node *Init = nullptr; if (HasInitializer) { Init = getDerived().parseExpr(); if (Init == nullptr) @@ -4425,7 +4459,53 @@ Node *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() { if (IsLeftFold && Init) std::swap(Pack, Init); - return make<FoldExpr>(IsLeftFold, OperatorName, Pack, Init); + return make<FoldExpr>(IsLeftFold, Op->getSymbol(), Pack, Init); +} + +// <expression> ::= mc <parameter type> <expr> [<offset number>] E +// +// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47 +template <typename Derived, typename Alloc> +Node * +AbstractManglingParser<Derived, Alloc>::parsePointerToMemberConversionExpr( + Node::Prec Prec) { + Node *Ty = getDerived().parseType(); + if (!Ty) + return nullptr; + Node *Expr = getDerived().parseExpr(); + if (!Expr) + return nullptr; + std::string_view Offset = getDerived().parseNumber(true); + if (!consumeIf('E')) + return nullptr; + return make<PointerToMemberConversionExpr>(Ty, Expr, Offset, Prec); +} + +// <expression> ::= so <referent type> <expr> [<offset number>] <union-selector>* [p] E +// <union-selector> ::= _ [<number>] +// +// Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/47 +template <typename Derived, typename Alloc> +Node *AbstractManglingParser<Derived, Alloc>::parseSubobjectExpr() { + Node *Ty = getDerived().parseType(); + if (!Ty) + return nullptr; + Node *Expr = getDerived().parseExpr(); + if (!Expr) + return nullptr; + std::string_view Offset = getDerived().parseNumber(true); + size_t SelectorsBegin = Names.size(); + while (consumeIf('_')) { + Node *Selector = make<NameType>(parseNumber()); + if (!Selector) + return nullptr; + Names.push_back(Selector); + } + bool OnePastTheEnd = consumeIf('p'); + if (!consumeIf('E')) + return nullptr; + return make<SubobjectExpr>( + Ty, Expr, Offset, popTrailingNodeArray(SelectorsBegin), OnePastTheEnd); } // <expression> ::= <unary operator-name> <expression> @@ -4475,313 +4555,127 @@ Node *AbstractManglingParser<Derived, Alloc>::parseFoldExpr() { template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseExpr() { bool Global = consumeIf("gs"); - if (numLeft() < 2) - return nullptr; - switch (*First) { - case 'L': - return getDerived().parseExprPrimary(); - case 'T': - return getDerived().parseTemplateParam(); - case 'f': { - // Disambiguate a fold expression from a <function-param>. - if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2)))) - return getDerived().parseFunctionParam(); - return getDerived().parseFoldExpr(); - } - case 'a': - switch (First[1]) { - case 'a': - First += 2; - return getDerived().parseBinaryExpr("&&"); - case 'd': - First += 2; - return getDerived().parsePrefixExpr("&"); - case 'n': - First += 2; - return getDerived().parseBinaryExpr("&"); - case 'N': - First += 2; - return getDerived().parseBinaryExpr("&="); - case 'S': - First += 2; - return getDerived().parseBinaryExpr("="); - case 't': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return nullptr; - return make<EnclosingExpr>("alignof (", Ty, ")"); - } - case 'z': { - First += 2; - Node *Ty = getDerived().parseExpr(); - if (Ty == nullptr) - return nullptr; - return make<EnclosingExpr>("alignof (", Ty, ")"); - } - } - return nullptr; - case 'c': - switch (First[1]) { - // cc <type> <expression> # const_cast<type>(expression) - case 'c': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<CastExpr>("const_cast", Ty, Ex); - } - // cl <expression>+ E # call - case 'l': { - First += 2; - Node *Callee = getDerived().parseExpr(); - if (Callee == nullptr) - return Callee; - size_t ExprsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseExpr(); - if (E == nullptr) - return E; - Names.push_back(E); - } - return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin)); - } - case 'm': - First += 2; - return getDerived().parseBinaryExpr(","); - case 'o': - First += 2; - return getDerived().parsePrefixExpr("~"); - case 'v': - return getDerived().parseConversionExpr(); - } - return nullptr; - case 'd': - switch (First[1]) { - case 'a': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<DeleteExpr>(Ex, Global, /*is_array=*/true); - } - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; + const auto *Op = parseOperatorEncoding(); + if (Op) { + auto Sym = Op->getSymbol(); + switch (Op->getKind()) { + case OperatorInfo::Binary: + // Binary operator: lhs @ rhs + return getDerived().parseBinaryExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Prefix: + // Prefix unary operator: @ expr + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); + case OperatorInfo::Postfix: { + // Postfix unary operator: expr @ + if (consumeIf('_')) + return getDerived().parsePrefixExpr(Sym, Op->getPrecedence()); Node *Ex = getDerived().parseExpr(); if (Ex == nullptr) - return Ex; - return make<CastExpr>("dynamic_cast", T, Ex); - } - case 'e': - First += 2; - return getDerived().parsePrefixExpr("*"); - case 'l': { - First += 2; - Node *E = getDerived().parseExpr(); - if (E == nullptr) - return E; - return make<DeleteExpr>(E, Global, /*is_array=*/false); + return nullptr; + return make<PostfixExpr>(Ex, Sym, Op->getPrecedence()); } - case 'n': - return getDerived().parseUnresolvedName(); - case 's': { - First += 2; - Node *LHS = getDerived().parseExpr(); - if (LHS == nullptr) + case OperatorInfo::Array: { + // Array Index: lhs [ rhs ] + Node *Base = getDerived().parseExpr(); + if (Base == nullptr) return nullptr; - Node *RHS = getDerived().parseExpr(); - if (RHS == nullptr) + Node *Index = getDerived().parseExpr(); + if (Index == nullptr) return nullptr; - return make<MemberExpr>(LHS, ".*", RHS); + return make<ArraySubscriptExpr>(Base, Index, Op->getPrecedence()); } - case 't': { - First += 2; + case OperatorInfo::Member: { + // Member access lhs @ rhs Node *LHS = getDerived().parseExpr(); if (LHS == nullptr) - return LHS; + return nullptr; Node *RHS = getDerived().parseExpr(); if (RHS == nullptr) return nullptr; - return make<MemberExpr>(LHS, ".", RHS); - } - case 'v': - First += 2; - return getDerived().parseBinaryExpr("/"); - case 'V': - First += 2; - return getDerived().parseBinaryExpr("/="); - } - return nullptr; - case 'e': - switch (First[1]) { - case 'o': - First += 2; - return getDerived().parseBinaryExpr("^"); - case 'O': - First += 2; - return getDerived().parseBinaryExpr("^="); - case 'q': - First += 2; - return getDerived().parseBinaryExpr("=="); - } - return nullptr; - case 'g': - switch (First[1]) { - case 'e': - First += 2; - return getDerived().parseBinaryExpr(">="); - case 't': - First += 2; - return getDerived().parseBinaryExpr(">"); - } - return nullptr; - case 'i': - switch (First[1]) { - case 'x': { - First += 2; - Node *Base = getDerived().parseExpr(); - if (Base == nullptr) + return make<MemberExpr>(LHS, Sym, RHS, Op->getPrecedence()); + } + case OperatorInfo::New: { + // New + // # new (expr-list) type [(init)] + // [gs] nw <expression>* _ <type> [pi <expression>*] E + // # new[] (expr-list) type [(init)] + // [gs] na <expression>* _ <type> [pi <expression>*] E + size_t Exprs = Names.size(); + while (!consumeIf('_')) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return nullptr; + Names.push_back(Ex); + } + NodeArray ExprList = popTrailingNodeArray(Exprs); + Node *Ty = getDerived().parseType(); + if (Ty == nullptr) return nullptr; - Node *Index = getDerived().parseExpr(); - if (Index == nullptr) - return Index; - return make<ArraySubscriptExpr>(Base, Index); - } - case 'l': { - First += 2; + bool HaveInits = consumeIf("pi"); size_t InitsBegin = Names.size(); while (!consumeIf('E')) { - Node *E = getDerived().parseBracedExpr(); - if (E == nullptr) + if (!HaveInits) return nullptr; - Names.push_back(E); + Node *Init = getDerived().parseExpr(); + if (Init == nullptr) + return Init; + Names.push_back(Init); } - return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin)); - } + NodeArray Inits = popTrailingNodeArray(InitsBegin); + return make<NewExpr>(ExprList, Ty, Inits, Global, + /*IsArray=*/Op->getFlag(), Op->getPrecedence()); } - return nullptr; - case 'l': - switch (First[1]) { - case 'e': - First += 2; - return getDerived().parseBinaryExpr("<="); - case 's': - First += 2; - return getDerived().parseBinaryExpr("<<"); - case 'S': - First += 2; - return getDerived().parseBinaryExpr("<<="); - case 't': - First += 2; - return getDerived().parseBinaryExpr("<"); - } - return nullptr; - case 'm': - switch (First[1]) { - case 'i': - First += 2; - return getDerived().parseBinaryExpr("-"); - case 'I': - First += 2; - return getDerived().parseBinaryExpr("-="); - case 'l': - First += 2; - return getDerived().parseBinaryExpr("*"); - case 'L': - First += 2; - return getDerived().parseBinaryExpr("*="); - case 'm': - First += 2; - if (consumeIf('_')) - return getDerived().parsePrefixExpr("--"); + case OperatorInfo::Del: { + // Delete Node *Ex = getDerived().parseExpr(); if (Ex == nullptr) return nullptr; - return make<PostfixExpr>(Ex, "--"); - } - return nullptr; - case 'n': - switch (First[1]) { - case 'a': - case 'w': - return getDerived().parseNewExpr(); - case 'e': - First += 2; - return getDerived().parseBinaryExpr("!="); - case 'g': - First += 2; - return getDerived().parsePrefixExpr("-"); - case 't': - First += 2; - return getDerived().parsePrefixExpr("!"); - case 'x': - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<EnclosingExpr>("noexcept (", Ex, ")"); - } - return nullptr; - case 'o': - switch (First[1]) { - case 'n': - return getDerived().parseUnresolvedName(); - case 'o': - First += 2; - return getDerived().parseBinaryExpr("||"); - case 'r': - First += 2; - return getDerived().parseBinaryExpr("|"); - case 'R': - First += 2; - return getDerived().parseBinaryExpr("|="); + return make<DeleteExpr>(Ex, Global, /*IsArray=*/Op->getFlag(), + Op->getPrecedence()); } - return nullptr; - case 'p': - switch (First[1]) { - case 'm': - First += 2; - return getDerived().parseBinaryExpr("->*"); - case 'l': - First += 2; - return getDerived().parseBinaryExpr("+"); - case 'L': - First += 2; - return getDerived().parseBinaryExpr("+="); - case 'p': { - First += 2; - if (consumeIf('_')) - return getDerived().parsePrefixExpr("++"); - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<PostfixExpr>(Ex, "++"); + case OperatorInfo::Call: { + // Function Call + Node *Callee = getDerived().parseExpr(); + if (Callee == nullptr) + return nullptr; + size_t ExprsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make<CallExpr>(Callee, popTrailingNodeArray(ExprsBegin), + Op->getPrecedence()); } - case 's': - First += 2; - return getDerived().parsePrefixExpr("+"); - case 't': { - First += 2; - Node *L = getDerived().parseExpr(); - if (L == nullptr) + case OperatorInfo::CCast: { + // C Cast: (type)expr + Node *Ty; + { + ScopedOverride<bool> SaveTemp(TryToParseTemplateArgs, false); + Ty = getDerived().parseType(); + } + if (Ty == nullptr) return nullptr; - Node *R = getDerived().parseExpr(); - if (R == nullptr) + + size_t ExprsBegin = Names.size(); + bool IsMany = consumeIf('_'); + while (!consumeIf('E')) { + Node *E = getDerived().parseExpr(); + if (E == nullptr) + return E; + Names.push_back(E); + if (!IsMany) + break; + } + NodeArray Exprs = popTrailingNodeArray(ExprsBegin); + if (!IsMany && Exprs.size() != 1) return nullptr; - return make<MemberExpr>(L, "->", R); + return make<ConversionExpr>(Ty, Exprs, Op->getPrecedence()); } - } - return nullptr; - case 'q': - if (First[1] == 'u') { - First += 2; + case OperatorInfo::Conditional: { + // Conditional operator: expr ? expr : expr Node *Cond = getDerived().parseExpr(); if (Cond == nullptr) return nullptr; @@ -4791,169 +4685,158 @@ Node *AbstractManglingParser<Derived, Alloc>::parseExpr() { Node *RHS = getDerived().parseExpr(); if (RHS == nullptr) return nullptr; - return make<ConditionalExpr>(Cond, LHS, RHS); - } - return nullptr; - case 'r': - switch (First[1]) { - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<CastExpr>("reinterpret_cast", T, Ex); + return make<ConditionalExpr>(Cond, LHS, RHS, Op->getPrecedence()); } - case 'm': - First += 2; - return getDerived().parseBinaryExpr("%"); - case 'M': - First += 2; - return getDerived().parseBinaryExpr("%="); - case 's': - First += 2; - return getDerived().parseBinaryExpr(">>"); - case 'S': - First += 2; - return getDerived().parseBinaryExpr(">>="); - } - return nullptr; - case 's': - switch (First[1]) { - case 'c': { - First += 2; - Node *T = getDerived().parseType(); - if (T == nullptr) - return T; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<CastExpr>("static_cast", T, Ex); - } - case 'p': { - First += 2; - Node *Child = getDerived().parseExpr(); - if (Child == nullptr) - return nullptr; - return make<ParameterPackExpansion>(Child); - } - case 'r': - return getDerived().parseUnresolvedName(); - case 't': { - First += 2; + case OperatorInfo::NamedCast: { + // Named cast operation, @<type>(expr) Node *Ty = getDerived().parseType(); if (Ty == nullptr) - return Ty; - return make<EnclosingExpr>("sizeof (", Ty, ")"); - } - case 'z': { - First += 2; + return nullptr; Node *Ex = getDerived().parseExpr(); if (Ex == nullptr) - return Ex; - return make<EnclosingExpr>("sizeof (", Ex, ")"); - } - case 'Z': - First += 2; - if (look() == 'T') { - Node *R = getDerived().parseTemplateParam(); - if (R == nullptr) - return nullptr; - return make<SizeofParamPackExpr>(R); - } else if (look() == 'f') { - Node *FP = getDerived().parseFunctionParam(); - if (FP == nullptr) - return nullptr; - return make<EnclosingExpr>("sizeof... (", FP, ")"); - } - return nullptr; - case 'P': { - First += 2; - size_t ArgsBegin = Names.size(); - while (!consumeIf('E')) { - Node *Arg = getDerived().parseTemplateArg(); - if (Arg == nullptr) - return nullptr; - Names.push_back(Arg); - } - auto *Pack = make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin)); - if (!Pack) return nullptr; - return make<EnclosingExpr>("sizeof... (", Pack, ")"); + return make<CastExpr>(Sym, Ty, Ex, Op->getPrecedence()); } + case OperatorInfo::OfIdOp: { + // [sizeof/alignof/typeid] ( <type>|<expr> ) + Node *Arg = + Op->getFlag() ? getDerived().parseType() : getDerived().parseExpr(); + if (!Arg) + return nullptr; + return make<EnclosingExpr>(Sym, Arg, Op->getPrecedence()); } - return nullptr; - case 't': - switch (First[1]) { - case 'e': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) - return Ex; - return make<EnclosingExpr>("typeid (", Ex, ")"); + case OperatorInfo::NameOnly: { + // Not valid as an expression operand. + return nullptr; } - case 'i': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) - return Ty; - return make<EnclosingExpr>("typeid (", Ty, ")"); } - case 'l': { - First += 2; - Node *Ty = getDerived().parseType(); - if (Ty == nullptr) + DEMANGLE_UNREACHABLE; + } + + if (numLeft() < 2) + return nullptr; + + if (look() == 'L') + return getDerived().parseExprPrimary(); + if (look() == 'T') + return getDerived().parseTemplateParam(); + if (look() == 'f') { + // Disambiguate a fold expression from a <function-param>. + if (look(1) == 'p' || (look(1) == 'L' && std::isdigit(look(2)))) + return getDerived().parseFunctionParam(); + return getDerived().parseFoldExpr(); + } + if (consumeIf("il")) { + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); + if (E == nullptr) return nullptr; - size_t InitsBegin = Names.size(); - while (!consumeIf('E')) { - Node *E = getDerived().parseBracedExpr(); - if (E == nullptr) - return nullptr; - Names.push_back(E); - } - return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin)); + Names.push_back(E); } - case 'r': - First += 2; - return make<NameType>("throw"); - case 'w': { - First += 2; - Node *Ex = getDerived().parseExpr(); - if (Ex == nullptr) + return make<InitListExpr>(nullptr, popTrailingNodeArray(InitsBegin)); + } + if (consumeIf("mc")) + return parsePointerToMemberConversionExpr(Node::Prec::Unary); + if (consumeIf("nx")) { + Node *Ex = getDerived().parseExpr(); + if (Ex == nullptr) + return Ex; + return make<EnclosingExpr>("noexcept ", Ex, Node::Prec::Unary); + } + if (consumeIf("so")) + return parseSubobjectExpr(); + if (consumeIf("sp")) { + Node *Child = getDerived().parseExpr(); + if (Child == nullptr) + return nullptr; + return make<ParameterPackExpansion>(Child); + } + if (consumeIf("sZ")) { + if (look() == 'T') { + Node *R = getDerived().parseTemplateParam(); + if (R == nullptr) return nullptr; - return make<ThrowExpr>(Ex); + return make<SizeofParamPackExpr>(R); } + Node *FP = getDerived().parseFunctionParam(); + if (FP == nullptr) + return nullptr; + return make<EnclosingExpr>("sizeof... ", FP); + } + if (consumeIf("sP")) { + size_t ArgsBegin = Names.size(); + while (!consumeIf('E')) { + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + Names.push_back(Arg); } - return nullptr; - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - return getDerived().parseUnresolvedName(); - } - - if (consumeIf("u8__uuidoft")) { + auto *Pack = make<NodeArrayNode>(popTrailingNodeArray(ArgsBegin)); + if (!Pack) + return nullptr; + return make<EnclosingExpr>("sizeof... ", Pack); + } + if (consumeIf("tl")) { Node *Ty = getDerived().parseType(); - if (!Ty) + if (Ty == nullptr) return nullptr; - return make<UUIDOfExpr>(Ty); + size_t InitsBegin = Names.size(); + while (!consumeIf('E')) { + Node *E = getDerived().parseBracedExpr(); + if (E == nullptr) + return nullptr; + Names.push_back(E); + } + return make<InitListExpr>(Ty, popTrailingNodeArray(InitsBegin)); } - - if (consumeIf("u8__uuidofz")) { + if (consumeIf("tr")) + return make<NameType>("throw"); + if (consumeIf("tw")) { Node *Ex = getDerived().parseExpr(); - if (!Ex) + if (Ex == nullptr) return nullptr; - return make<UUIDOfExpr>(Ex); + return make<ThrowExpr>(Ex); + } + if (consumeIf('u')) { + Node *Name = getDerived().parseSourceName(/*NameState=*/nullptr); + if (!Name) + return nullptr; + // Special case legacy __uuidof mangling. The 't' and 'z' appear where the + // standard encoding expects a <template-arg>, and would be otherwise be + // interpreted as <type> node 'short' or 'ellipsis'. However, neither + // __uuidof(short) nor __uuidof(...) can actually appear, so there is no + // actual conflict here. + bool IsUUID = false; + Node *UUID = nullptr; + if (Name->getBaseName() == "__uuidof") { + if (consumeIf('t')) { + UUID = getDerived().parseType(); + IsUUID = true; + } else if (consumeIf('z')) { + UUID = getDerived().parseExpr(); + IsUUID = true; + } + } + size_t ExprsBegin = Names.size(); + if (IsUUID) { + if (UUID == nullptr) + return nullptr; + Names.push_back(UUID); + } else { + while (!consumeIf('E')) { + Node *E = getDerived().parseTemplateArg(); + if (E == nullptr) + return E; + Names.push_back(E); + } + } + return make<CallExpr>(Name, popTrailingNodeArray(ExprsBegin), + Node::Prec::Postfix); } - return nullptr; + // Only unresolved names remain. + return getDerived().parseUnresolvedName(Global); } // <call-offset> ::= h <nv-offset> _ @@ -4986,19 +4869,32 @@ bool AbstractManglingParser<Alloc, Derived>::parseCallOffset() { // # second call-offset is result adjustment // ::= T <call-offset> <base encoding> // # base is the nominal target function of thunk -// ::= GV <object name> # Guard variable for one-time initialization +// # Guard variable for one-time initialization +// ::= GV <object name> // # No <type> // ::= TW <object name> # Thread-local wrapper // ::= TH <object name> # Thread-local initialization // ::= GR <object name> _ # First temporary // ::= GR <object name> <seq-id> _ # Subsequent temporaries -// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first +// # construction vtable for second-in-first +// extension ::= TC <first type> <number> _ <second type> // extension ::= GR <object name> # reference temporary for object +// extension ::= GI <module name> # module global initializer template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseSpecialName() { switch (look()) { case 'T': switch (look(1)) { + // TA <template-arg> # template parameter object + // + // Not yet in the spec: https://github.com/itanium-cxx-abi/cxx-abi/issues/63 + case 'A': { + First += 2; + Node *Arg = getDerived().parseTemplateArg(); + if (Arg == nullptr) + return nullptr; + return make<SpecialName>("template parameter object for ", Arg); + } // TV <type> # virtual table case 'V': { First += 2; @@ -5110,6 +5006,16 @@ Node *AbstractManglingParser<Derived, Alloc>::parseSpecialName() { return nullptr; return make<SpecialName>("reference temporary for ", Name); } + // GI <module-name> v + case 'I': { + First += 2; + ModuleName *Module = nullptr; + if (getDerived().parseModuleNameOpt(Module)) + return nullptr; + if (Module == nullptr) + return nullptr; + return make<SpecialName>("initializer for module ", Module); + } } } return nullptr; @@ -5120,6 +5026,26 @@ Node *AbstractManglingParser<Derived, Alloc>::parseSpecialName() { // ::= <special-name> template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseEncoding() { + // The template parameters of an encoding are unrelated to those of the + // enclosing context. + class SaveTemplateParams { + AbstractManglingParser *Parser; + decltype(TemplateParams) OldParams; + decltype(OuterTemplateParams) OldOuterParams; + + public: + SaveTemplateParams(AbstractManglingParser *TheParser) : Parser(TheParser) { + OldParams = std::move(Parser->TemplateParams); + OldOuterParams = std::move(Parser->OuterTemplateParams); + Parser->TemplateParams.clear(); + Parser->OuterTemplateParams.clear(); + } + ~SaveTemplateParams() { + Parser->TemplateParams = std::move(OldParams); + Parser->OuterTemplateParams = std::move(OldOuterParams); + } + } SaveTemplateParams(this); + if (look() == 'G' || look() == 'T') return getDerived().parseSpecialName(); @@ -5204,14 +5130,19 @@ template <> struct FloatData<long double> { #if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ - defined(__wasm__) + defined(__wasm__) || defined(__riscv) || defined(__loongarch__) static const size_t mangled_size = 32; #elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) static const size_t mangled_size = 16; #else static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms #endif - static const size_t max_demangled_size = 40; + // `-0x1.ffffffffffffffffffffffffffffp+16383` + 'L' + '\0' == 42 bytes. + // 28 'f's * 4 bits == 112 bits, which is the number of mantissa bits. + // Negatives are one character longer than positives. + // `0x1.` and `p` are constant, and exponents `+16383` and `-16382` are the + // same length. 1 sign bit, 112 mantissa bits, and 15 exponent bits == 128. + static const size_t max_demangled_size = 42; static constexpr const char *spec = "%LaL"; }; @@ -5221,7 +5152,7 @@ Node *AbstractManglingParser<Alloc, Derived>::parseFloatingLiteral() { const size_t N = FloatData<Float>::mangled_size; if (numLeft() <= N) return nullptr; - StringView Data(First, First + N); + std::string_view Data(First, N); for (char C : Data) if (!std::isxdigit(C)) return nullptr; @@ -5264,43 +5195,41 @@ bool AbstractManglingParser<Alloc, Derived>::parseSeqId(size_t *Out) { // <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > // <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > // <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > +// The St case is handled specially in parseNestedName. template <typename Derived, typename Alloc> Node *AbstractManglingParser<Derived, Alloc>::parseSubstitution() { if (!consumeIf('S')) return nullptr; - if (std::islower(look())) { - Node *SpecialSub; + if (look() >= 'a' && look() <= 'z') { + SpecialSubKind Kind; switch (look()) { case 'a': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::allocator); + Kind = SpecialSubKind::allocator; break; case 'b': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::basic_string); + Kind = SpecialSubKind::basic_string; break; - case 's': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::string); + case 'd': + Kind = SpecialSubKind::iostream; break; case 'i': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::istream); + Kind = SpecialSubKind::istream; break; case 'o': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::ostream); + Kind = SpecialSubKind::ostream; break; - case 'd': - ++First; - SpecialSub = make<SpecialSubstitution>(SpecialSubKind::iostream); + case 's': + Kind = SpecialSubKind::string; break; default: return nullptr; } + ++First; + auto *SpecialSub = make<SpecialSubstitution>(Kind); if (!SpecialSub) return nullptr; + // Itanium C++ ABI 5.1.2: If a name that would use a built-in <substitution> // has ABI tags, the tags are appended to the substitution; the result is a // substitutable component. @@ -5543,7 +5472,8 @@ Node *AbstractManglingParser<Derived, Alloc>::parse() { if (Encoding == nullptr) return nullptr; if (look() == '.') { - Encoding = make<DotSuffix>(Encoding, StringView(First, Last)); + Encoding = + make<DotSuffix>(Encoding, std::string_view(First, Last - First)); First = Last; } if (numLeft() != 0) diff --git a/externals/demangle/llvm/Demangle/ItaniumNodes.def b/externals/demangle/llvm/Demangle/ItaniumNodes.def new file mode 100644 index 000000000..5985769ef --- /dev/null +++ b/externals/demangle/llvm/Demangle/ItaniumNodes.def @@ -0,0 +1,96 @@ +//===--- ItaniumNodes.def ------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-FileCopyrightText: Part of the LLVM Project +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Define the demangler's node names + +#ifndef NODE +#error Define NODE to handle nodes +#endif + +NODE(NodeArrayNode) +NODE(DotSuffix) +NODE(VendorExtQualType) +NODE(QualType) +NODE(ConversionOperatorType) +NODE(PostfixQualifiedType) +NODE(ElaboratedTypeSpefType) +NODE(NameType) +NODE(AbiTagAttr) +NODE(EnableIfAttr) +NODE(ObjCProtoName) +NODE(PointerType) +NODE(ReferenceType) +NODE(PointerToMemberType) +NODE(ArrayType) +NODE(FunctionType) +NODE(NoexceptSpec) +NODE(DynamicExceptionSpec) +NODE(FunctionEncoding) +NODE(LiteralOperator) +NODE(SpecialName) +NODE(CtorVtableSpecialName) +NODE(QualifiedName) +NODE(NestedName) +NODE(LocalName) +NODE(ModuleName) +NODE(ModuleEntity) +NODE(VectorType) +NODE(PixelVectorType) +NODE(BinaryFPType) +NODE(BitIntType) +NODE(SyntheticTemplateParamName) +NODE(TypeTemplateParamDecl) +NODE(NonTypeTemplateParamDecl) +NODE(TemplateTemplateParamDecl) +NODE(TemplateParamPackDecl) +NODE(ParameterPack) +NODE(TemplateArgumentPack) +NODE(ParameterPackExpansion) +NODE(TemplateArgs) +NODE(ForwardTemplateReference) +NODE(NameWithTemplateArgs) +NODE(GlobalQualifiedName) +NODE(ExpandedSpecialSubstitution) +NODE(SpecialSubstitution) +NODE(CtorDtorName) +NODE(DtorName) +NODE(UnnamedTypeName) +NODE(ClosureTypeName) +NODE(StructuredBindingName) +NODE(BinaryExpr) +NODE(ArraySubscriptExpr) +NODE(PostfixExpr) +NODE(ConditionalExpr) +NODE(MemberExpr) +NODE(SubobjectExpr) +NODE(EnclosingExpr) +NODE(CastExpr) +NODE(SizeofParamPackExpr) +NODE(CallExpr) +NODE(NewExpr) +NODE(DeleteExpr) +NODE(PrefixExpr) +NODE(FunctionParam) +NODE(ConversionExpr) +NODE(PointerToMemberConversionExpr) +NODE(InitListExpr) +NODE(FoldExpr) +NODE(ThrowExpr) +NODE(BoolExpr) +NODE(StringLiteral) +NODE(LambdaExpr) +NODE(EnumLiteral) +NODE(IntegerLiteral) +NODE(FloatLiteral) +NODE(DoubleLiteral) +NODE(LongDoubleLiteral) +NODE(BracedExpr) +NODE(BracedRangeExpr) + +#undef NODE diff --git a/externals/demangle/llvm/Demangle/StringView.h b/externals/demangle/llvm/Demangle/StringView.h index 44d2b18a3..76b215252 100644 --- a/externals/demangle/llvm/Demangle/StringView.h +++ b/externals/demangle/llvm/Demangle/StringView.h @@ -1,5 +1,5 @@ -//===--- StringView.h -------------------------------------------*- C++ -*-===// -// +//===--- StringView.h ----------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-FileCopyrightText: Part of the LLVM Project @@ -8,6 +8,9 @@ //===----------------------------------------------------------------------===// // // FIXME: Use std::string_view instead when we support C++17. +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. // //===----------------------------------------------------------------------===// @@ -15,7 +18,6 @@ #define DEMANGLE_STRINGVIEW_H #include "DemangleConfig.h" -#include <algorithm> #include <cassert> #include <cstring> @@ -37,29 +39,23 @@ public: StringView(const char *Str) : First(Str), Last(Str + std::strlen(Str)) {} StringView() : First(nullptr), Last(nullptr) {} - StringView substr(size_t From) const { - return StringView(begin() + From, size() - From); + StringView substr(size_t Pos, size_t Len = npos) const { + assert(Pos <= size()); + if (Len > size() - Pos) + Len = size() - Pos; + return StringView(begin() + Pos, Len); } size_t find(char C, size_t From = 0) const { - size_t FindBegin = std::min(From, size()); // Avoid calling memchr with nullptr. - if (FindBegin < size()) { + if (From < size()) { // Just forward to memchr, which is faster than a hand-rolled loop. - if (const void *P = ::memchr(First + FindBegin, C, size() - FindBegin)) + if (const void *P = ::memchr(First + From, C, size() - From)) return size_t(static_cast<const char *>(P) - First); } return npos; } - StringView substr(size_t From, size_t To) const { - if (To >= size()) - To = size() - 1; - if (From >= size()) - From = size() - 1; - return StringView(First + From, First + To); - } - StringView dropFront(size_t N = 1) const { if (N >= size()) N = size(); @@ -106,7 +102,7 @@ public: bool startsWith(StringView Str) const { if (Str.size() > size()) return false; - return std::equal(Str.begin(), Str.end(), begin()); + return std::strncmp(Str.begin(), begin(), Str.size()) == 0; } const char &operator[](size_t Idx) const { return *(begin() + Idx); } @@ -119,7 +115,7 @@ public: inline bool operator==(const StringView &LHS, const StringView &RHS) { return LHS.size() == RHS.size() && - std::equal(LHS.begin(), LHS.end(), RHS.begin()); + std::strncmp(LHS.begin(), RHS.begin(), LHS.size()) == 0; } DEMANGLE_NAMESPACE_END diff --git a/externals/demangle/llvm/Demangle/StringViewExtras.h b/externals/demangle/llvm/Demangle/StringViewExtras.h new file mode 100644 index 000000000..83685c304 --- /dev/null +++ b/externals/demangle/llvm/Demangle/StringViewExtras.h @@ -0,0 +1,39 @@ +//===--- StringViewExtras.h ----------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-FileCopyrightText: Part of the LLVM Project +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// There are two copies of this file in the source tree. The one under +// libcxxabi is the original and the one under llvm is the copy. Use +// cp-to-llvm.sh to update the copy. See README.txt for more details. +// +//===----------------------------------------------------------------------===// + +#ifndef DEMANGLE_STRINGVIEW_H +#define DEMANGLE_STRINGVIEW_H + +#include "DemangleConfig.h" + +#include <string_view> + +DEMANGLE_NAMESPACE_BEGIN + +inline bool starts_with(std::string_view self, char C) noexcept { + return !self.empty() && *self.begin() == C; +} + +inline bool starts_with(std::string_view haystack, + std::string_view needle) noexcept { + if (needle.size() > haystack.size()) + return false; + haystack.remove_suffix(haystack.size() - needle.size()); + return haystack == needle; +} + +DEMANGLE_NAMESPACE_END + +#endif diff --git a/externals/demangle/llvm/Demangle/Utility.h b/externals/demangle/llvm/Demangle/Utility.h index 50d05c6b1..30dfbfc8d 100644 --- a/externals/demangle/llvm/Demangle/Utility.h +++ b/externals/demangle/llvm/Demangle/Utility.h @@ -1,5 +1,5 @@ -//===--- Utility.h ----------------------------------------------*- C++ -*-===// -// +//===--- Utility.h -------------------*- mode:c++;eval:(read-only-mode) -*-===// +// Do not edit! See README.txt. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-FileCopyrightText: Part of the LLVM Project @@ -7,70 +7,83 @@ // //===----------------------------------------------------------------------===// // -// Provide some utility classes for use in the demangler(s). +// Provide some utility classes for use in the demangler. +// There are two copies of this file in the source tree. The one in libcxxabi +// is the original and the one in llvm is the copy. Use cp-to-llvm.sh to update +// the copy. See README.txt for more details. // //===----------------------------------------------------------------------===// #ifndef DEMANGLE_UTILITY_H #define DEMANGLE_UTILITY_H -#include "StringView.h" +#include "DemangleConfig.h" + +#include <array> +#include <cassert> #include <cstdint> #include <cstdlib> #include <cstring> -#include <iterator> +#include <exception> #include <limits> +#include <string_view> DEMANGLE_NAMESPACE_BEGIN // Stream that AST nodes write their string representation into after the AST // has been parsed. -class OutputStream { - char *Buffer; - size_t CurrentPosition; - size_t BufferCapacity; +class OutputBuffer { + char *Buffer = nullptr; + size_t CurrentPosition = 0; + size_t BufferCapacity = 0; - // Ensure there is at least n more positions in buffer. + // Ensure there are at least N more positions in the buffer. void grow(size_t N) { - if (N + CurrentPosition >= BufferCapacity) { + size_t Need = N + CurrentPosition; + if (Need > BufferCapacity) { + // Reduce the number of reallocations, with a bit of hysteresis. The + // number here is chosen so the first allocation will more-than-likely not + // allocate more than 1K. + Need += 1024 - 32; BufferCapacity *= 2; - if (BufferCapacity < N + CurrentPosition) - BufferCapacity = N + CurrentPosition; + if (BufferCapacity < Need) + BufferCapacity = Need; Buffer = static_cast<char *>(std::realloc(Buffer, BufferCapacity)); if (Buffer == nullptr) std::terminate(); } } - void writeUnsigned(uint64_t N, bool isNeg = false) { - // Handle special case... - if (N == 0) { - *this << '0'; - return; - } - - char Temp[21]; - char *TempPtr = std::end(Temp); + OutputBuffer &writeUnsigned(uint64_t N, bool isNeg = false) { + std::array<char, 21> Temp; + char *TempPtr = Temp.data() + Temp.size(); - while (N) { - *--TempPtr = '0' + char(N % 10); + // Output at least one character. + do { + *--TempPtr = char('0' + N % 10); N /= 10; - } + } while (N); - // Add negative sign... + // Add negative sign. if (isNeg) *--TempPtr = '-'; - this->operator<<(StringView(TempPtr, std::end(Temp))); + + return operator+=( + std::string_view(TempPtr, Temp.data() + Temp.size() - TempPtr)); } public: - OutputStream(char *StartBuf, size_t Size) - : Buffer(StartBuf), CurrentPosition(0), BufferCapacity(Size) {} - OutputStream() = default; - void reset(char *Buffer_, size_t BufferCapacity_) { - CurrentPosition = 0; - Buffer = Buffer_; - BufferCapacity = BufferCapacity_; + OutputBuffer(char *StartBuf, size_t Size) + : Buffer(StartBuf), BufferCapacity(Size) {} + OutputBuffer(char *StartBuf, size_t *SizePtr) + : OutputBuffer(StartBuf, StartBuf ? *SizePtr : 0) {} + OutputBuffer() = default; + // Non-copyable + OutputBuffer(const OutputBuffer &) = delete; + OutputBuffer &operator=(const OutputBuffer &) = delete; + + operator std::string_view() const { + return std::string_view(Buffer, CurrentPosition); } /// If a ParameterPackExpansion (or similar type) is encountered, the offset @@ -78,115 +91,116 @@ public: unsigned CurrentPackIndex = std::numeric_limits<unsigned>::max(); unsigned CurrentPackMax = std::numeric_limits<unsigned>::max(); - OutputStream &operator+=(StringView R) { - size_t Size = R.size(); - if (Size == 0) - return *this; - grow(Size); - std::memmove(Buffer + CurrentPosition, R.begin(), Size); - CurrentPosition += Size; + /// When zero, we're printing template args and '>' needs to be parenthesized. + /// Use a counter so we can simply increment inside parentheses. + unsigned GtIsGt = 1; + + bool isGtInsideTemplateArgs() const { return GtIsGt == 0; } + + void printOpen(char Open = '(') { + GtIsGt++; + *this += Open; + } + void printClose(char Close = ')') { + GtIsGt--; + *this += Close; + } + + OutputBuffer &operator+=(std::string_view R) { + if (size_t Size = R.size()) { + grow(Size); + std::memcpy(Buffer + CurrentPosition, &*R.begin(), Size); + CurrentPosition += Size; + } return *this; } - OutputStream &operator+=(char C) { + OutputBuffer &operator+=(char C) { grow(1); Buffer[CurrentPosition++] = C; return *this; } - OutputStream &operator<<(StringView R) { return (*this += R); } + OutputBuffer &prepend(std::string_view R) { + size_t Size = R.size(); - OutputStream &operator<<(char C) { return (*this += C); } + grow(Size); + std::memmove(Buffer + Size, Buffer, CurrentPosition); + std::memcpy(Buffer, &*R.begin(), Size); + CurrentPosition += Size; - OutputStream &operator<<(long long N) { - if (N < 0) - writeUnsigned(static_cast<unsigned long long>(-N), true); - else - writeUnsigned(static_cast<unsigned long long>(N)); return *this; } - OutputStream &operator<<(unsigned long long N) { - writeUnsigned(N, false); - return *this; + OutputBuffer &operator<<(std::string_view R) { return (*this += R); } + + OutputBuffer &operator<<(char C) { return (*this += C); } + + OutputBuffer &operator<<(long long N) { + return writeUnsigned(static_cast<unsigned long long>(std::abs(N)), N < 0); } - OutputStream &operator<<(long N) { + OutputBuffer &operator<<(unsigned long long N) { + return writeUnsigned(N, false); + } + + OutputBuffer &operator<<(long N) { return this->operator<<(static_cast<long long>(N)); } - OutputStream &operator<<(unsigned long N) { + OutputBuffer &operator<<(unsigned long N) { return this->operator<<(static_cast<unsigned long long>(N)); } - OutputStream &operator<<(int N) { + OutputBuffer &operator<<(int N) { return this->operator<<(static_cast<long long>(N)); } - OutputStream &operator<<(unsigned int N) { + OutputBuffer &operator<<(unsigned int N) { return this->operator<<(static_cast<unsigned long long>(N)); } + void insert(size_t Pos, const char *S, size_t N) { + assert(Pos <= CurrentPosition); + if (N == 0) + return; + grow(N); + std::memmove(Buffer + Pos + N, Buffer + Pos, CurrentPosition - Pos); + std::memcpy(Buffer + Pos, S, N); + CurrentPosition += N; + } + size_t getCurrentPosition() const { return CurrentPosition; } void setCurrentPosition(size_t NewPos) { CurrentPosition = NewPos; } char back() const { - return CurrentPosition ? Buffer[CurrentPosition - 1] : '\0'; + assert(CurrentPosition); + return Buffer[CurrentPosition - 1]; } bool empty() const { return CurrentPosition == 0; } char *getBuffer() { return Buffer; } char *getBufferEnd() { return Buffer + CurrentPosition - 1; } - size_t getBufferCapacity() { return BufferCapacity; } + size_t getBufferCapacity() const { return BufferCapacity; } }; -template <class T> class SwapAndRestore { - T &Restore; - T OriginalValue; - bool ShouldRestore = true; +template <class T> class ScopedOverride { + T &Loc; + T Original; public: - SwapAndRestore(T &Restore_) : SwapAndRestore(Restore_, Restore_) {} - - SwapAndRestore(T &Restore_, T NewVal) - : Restore(Restore_), OriginalValue(Restore) { - Restore = std::move(NewVal); - } - ~SwapAndRestore() { - if (ShouldRestore) - Restore = std::move(OriginalValue); - } + ScopedOverride(T &Loc_) : ScopedOverride(Loc_, Loc_) {} - void shouldRestore(bool ShouldRestore_) { ShouldRestore = ShouldRestore_; } - - void restoreNow(bool Force) { - if (!Force && !ShouldRestore) - return; - - Restore = std::move(OriginalValue); - ShouldRestore = false; + ScopedOverride(T &Loc_, T NewVal) : Loc(Loc_), Original(Loc_) { + Loc_ = std::move(NewVal); } + ~ScopedOverride() { Loc = std::move(Original); } - SwapAndRestore(const SwapAndRestore &) = delete; - SwapAndRestore &operator=(const SwapAndRestore &) = delete; + ScopedOverride(const ScopedOverride &) = delete; + ScopedOverride &operator=(const ScopedOverride &) = delete; }; -inline bool initializeOutputStream(char *Buf, size_t *N, OutputStream &S, - size_t InitSize) { - size_t BufferSize; - if (Buf == nullptr) { - Buf = static_cast<char *>(std::malloc(InitSize)); - if (Buf == nullptr) - return false; - BufferSize = InitSize; - } else - BufferSize = *N; - - S.reset(Buf, BufferSize); - return true; -} - DEMANGLE_NAMESPACE_END #endif diff --git a/externals/vma/VulkanMemoryAllocator b/externals/vma/VulkanMemoryAllocator deleted file mode 160000 -Subproject 0aa3989b8f382f185fdf646cc83a1d16fa31d6a diff --git a/src/android/app/src/main/AndroidManifest.xml b/src/android/app/src/main/AndroidManifest.xml index 742685fb0..6184f3eb6 100644 --- a/src/android/app/src/main/AndroidManifest.xml +++ b/src/android/app/src/main/AndroidManifest.xml @@ -22,7 +22,7 @@ SPDX-License-Identifier: GPL-3.0-or-later android:label="@string/app_name_suffixed" android:icon="@drawable/ic_launcher" android:allowBackup="true" - android:hasFragileUserData="true" + android:hasFragileUserData="false" android:supportsRtl="true" android:isGame="true" android:localeConfig="@xml/locales_config" diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt index d3df3bc81..aadc445f9 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt @@ -12,6 +12,7 @@ import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding +import org.yuzu.yuzu_emu.fragments.MessageDialogFragment import org.yuzu.yuzu_emu.model.HomeSetting class HomeSettingAdapter(private val activity: AppCompatActivity, var options: List<HomeSetting>) : @@ -34,7 +35,14 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L override fun onClick(view: View) { val holder = view.tag as HomeOptionViewHolder - holder.option.onClick.invoke() + if (holder.option.isEnabled.invoke()) { + holder.option.onClick.invoke() + } else { + MessageDialogFragment.newInstance( + holder.option.disabledTitleId, + holder.option.disabledMessageId + ).show(activity.supportFragmentManager, MessageDialogFragment.TAG) + } } inner class HomeOptionViewHolder(val binding: CardHomeOptionBinding) : @@ -65,6 +73,12 @@ class HomeSettingAdapter(private val activity: AppCompatActivity, var options: L R.drawable.premium_background ) } + + if (!option.isEnabled.invoke()) { + binding.optionTitle.alpha = 0.5f + binding.optionDescription.alpha = 0.5f + binding.optionIcon.alpha = 0.5f + } } } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt index 5a36ffad4..c001af892 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/HomeSettingsFragment.kt @@ -73,102 +73,113 @@ class HomeSettingsFragment : Fragment() { HomeSetting( R.string.advanced_settings, R.string.settings_description, - R.drawable.ic_settings - ) { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } + R.drawable.ic_settings, + { SettingsActivity.launch(requireContext(), SettingsFile.FILE_NAME_CONFIG, "") } + ) ) add( HomeSetting( R.string.open_user_folder, R.string.open_user_folder_description, - R.drawable.ic_folder_open - ) { openFileManager() } + R.drawable.ic_folder_open, + { openFileManager() } + ) ) add( HomeSetting( R.string.preferences_theme, R.string.theme_and_color_description, - R.drawable.ic_palette - ) { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } + R.drawable.ic_palette, + { SettingsActivity.launch(requireContext(), Settings.SECTION_THEME, "") } + ) ) - - if (GpuDriverHelper.supportsCustomDriverLoading()) { - add( - HomeSetting( - R.string.install_gpu_driver, - R.string.install_gpu_driver_description, - R.drawable.ic_exit - ) { driverInstaller() } + add( + HomeSetting( + R.string.install_gpu_driver, + R.string.install_gpu_driver_description, + R.drawable.ic_exit, + { driverInstaller() }, + { GpuDriverHelper.supportsCustomDriverLoading() }, + R.string.custom_driver_not_supported, + R.string.custom_driver_not_supported_description ) - } - + ) add( HomeSetting( R.string.install_amiibo_keys, R.string.install_amiibo_keys_description, - R.drawable.ic_nfc - ) { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) } + R.drawable.ic_nfc, + { mainActivity.getAmiiboKey.launch(arrayOf("*/*")) } + ) ) add( HomeSetting( R.string.install_game_content, R.string.install_game_content_description, - R.drawable.ic_system_update_alt - ) { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } + R.drawable.ic_system_update_alt, + { mainActivity.installGameUpdate.launch(arrayOf("*/*")) } + ) ) add( HomeSetting( R.string.select_games_folder, R.string.select_games_folder_description, - R.drawable.ic_add - ) { - mainActivity.getGamesDirectory.launch( - Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data - ) - } + R.drawable.ic_add, + { + mainActivity.getGamesDirectory.launch( + Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data + ) + } + ) ) add( HomeSetting( R.string.manage_save_data, R.string.import_export_saves_description, - R.drawable.ic_save - ) { - ImportExportSavesFragment().show( - parentFragmentManager, - ImportExportSavesFragment.TAG - ) - } + R.drawable.ic_save, + { + ImportExportSavesFragment().show( + parentFragmentManager, + ImportExportSavesFragment.TAG + ) + } + ) ) add( HomeSetting( R.string.install_prod_keys, R.string.install_prod_keys_description, - R.drawable.ic_unlock - ) { mainActivity.getProdKey.launch(arrayOf("*/*")) } + R.drawable.ic_unlock, + { mainActivity.getProdKey.launch(arrayOf("*/*")) } + ) ) add( HomeSetting( R.string.install_firmware, R.string.install_firmware_description, - R.drawable.ic_firmware - ) { mainActivity.getFirmware.launch(arrayOf("application/zip")) } + R.drawable.ic_firmware, + { mainActivity.getFirmware.launch(arrayOf("application/zip")) } + ) ) add( HomeSetting( R.string.share_log, R.string.share_log_description, - R.drawable.ic_log - ) { shareLog() } + R.drawable.ic_log, + { shareLog() } + ) ) add( HomeSetting( R.string.about, R.string.about_description, - R.drawable.ic_info_outline - ) { - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - parentFragmentManager.primaryNavigationFragment?.findNavController() - ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) - } + R.drawable.ic_info_outline, + { + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + parentFragmentManager.primaryNavigationFragment?.findNavController() + ?.navigate(R.id.action_homeSettingsFragment_to_aboutFragment) + } + ) ) } @@ -178,12 +189,13 @@ class HomeSettingsFragment : Fragment() { HomeSetting( R.string.get_early_access, R.string.get_early_access_description, - R.drawable.ic_diamond - ) { - exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) - parentFragmentManager.primaryNavigationFragment?.findNavController() - ?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment) - } + R.drawable.ic_diamond, + { + exitTransition = MaterialSharedAxis(MaterialSharedAxis.X, true) + parentFragmentManager.primaryNavigationFragment?.findNavController() + ?.navigate(R.id.action_homeSettingsFragment_to_earlyAccessFragment) + } + ) ) } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt index 7049f2fa5..522d07c37 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/model/HomeSetting.kt @@ -7,5 +7,8 @@ data class HomeSetting( val titleId: Int, val descriptionId: Int, val iconId: Int, - val onClick: () -> Unit + val onClick: () -> Unit, + val isEnabled: () -> Boolean = { true }, + val disabledTitleId: Int = 0, + val disabledMessageId: Int = 0 ) diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index 8bc6a4a04..c23b2f19e 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -449,7 +449,7 @@ private: loader->ReadTitle(entry.title); loader->ReadIcon(entry.icon); if (loader->GetFileType() == Loader::FileType::NRO) { - jauto loader_nro = dynamic_cast<Loader::AppLoader_NRO*>(loader.get()); + jauto loader_nro = reinterpret_cast<Loader::AppLoader_NRO*>(loader.get()); entry.isHomebrew = loader_nro->IsHomebrew(); } else { entry.isHomebrew = false; diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index b3c737979..bfdebd35b 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -113,6 +113,8 @@ <string name="install_game_content_success_install">%1$d installed successfully</string> <string name="install_game_content_success_overwrite">%1$d overwritten successfully</string> <string name="install_game_content_help_link">https://yuzu-emu.org/help/quickstart/#dumping-installed-updates</string> + <string name="custom_driver_not_supported">Custom drivers not supported</string> + <string name="custom_driver_not_supported_description">Custom driver loading isn\'t currently supported for this device.\nCheck this option again in the future to see if support was added!</string> <!-- About screen strings --> <string name="gaia_is_not_real">Gaia isn\'t real</string> @@ -230,7 +232,7 @@ <!-- ROM loading errors --> <string name="loader_error_encrypted">Your ROM is encrypted</string> - <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-cartridge-games">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-installed-titles-eshop">installed titles</a>.]]></string> + <string name="loader_error_encrypted_roms_description"><![CDATA[Please follow the guides to redump your <a href="https://yuzu-emu.org/help/quickstart/#dumping-physical-titles-game-cards">game cartidges</a> or <a href="https://yuzu-emu.org/help/quickstart/#dumping-digital-titles-eshop">installed titles</a>.]]></string> <string name="loader_error_encrypted_keys_description"><![CDATA[Please ensure your <a href="https://yuzu-emu.org/help/quickstart/#dumping-prodkeys-and-titlekeys">prod.keys</a> file is installed so that games can be decrypted.]]></string> <string name="loader_error_video_core">An error occurred initializing the video core</string> <string name="loader_error_video_core_description">This is usually caused by an incompatible GPU driver. Installing a custom GPU driver may resolve this problem.</string> diff --git a/src/audio_core/device/device_session.cpp b/src/audio_core/device/device_session.cpp index 86811fcb8..c41d9d1ea 100644 --- a/src/audio_core/device/device_session.cpp +++ b/src/audio_core/device/device_session.cpp @@ -92,9 +92,9 @@ void DeviceSession::AppendBuffers(std::span<const AudioBuffer> buffers) { if (type == Sink::StreamType::In) { stream->AppendBuffer(new_buffer, tmp_samples); } else { - system.ApplicationMemory().ReadBlockUnsafe(buffer.samples, tmp_samples.data(), - buffer.size); - stream->AppendBuffer(new_buffer, tmp_samples); + Core::Memory::CpuGuestMemory<s16, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( + system.ApplicationMemory(), buffer.samples, buffer.size / sizeof(s16)); + stream->AppendBuffer(new_buffer, samples); } } } diff --git a/src/audio_core/renderer/command/data_source/decode.cpp b/src/audio_core/renderer/command/data_source/decode.cpp index f45933203..257aa866e 100644 --- a/src/audio_core/renderer/command/data_source/decode.cpp +++ b/src/audio_core/renderer/command/data_source/decode.cpp @@ -28,7 +28,6 @@ constexpr std::array<u8, 3> PitchBySrcQuality = {4, 8, 4}; template <typename T> static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) { - std::array<T, TempBufferSize> tmp_samples{}; constexpr s32 min{std::numeric_limits<s16>::min()}; constexpr s32 max{std::numeric_limits<s16>::max()}; @@ -49,19 +48,18 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const VAddr source{req.buffer + (((req.start_offset + req.offset) * channel_count) * sizeof(T))}; const u64 size{channel_count * samples_to_decode}; - const u64 size_bytes{size * sizeof(T)}; - - memory.ReadBlockUnsafe(source, tmp_samples.data(), size_bytes); + Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( + memory, source, size); if constexpr (std::is_floating_point_v<T>) { for (u32 i = 0; i < samples_to_decode; i++) { - auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * + auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * std::numeric_limits<s16>::max())}; out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); } } else { for (u32 i = 0; i < samples_to_decode; i++) { - out_buffer[i] = tmp_samples[i * channel_count + req.target_channel]; + out_buffer[i] = samples[i * channel_count + req.target_channel]; } } } break; @@ -74,16 +72,17 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, } const VAddr source{req.buffer + ((req.start_offset + req.offset) * sizeof(T))}; - memory.ReadBlockUnsafe(source, tmp_samples.data(), samples_to_decode * sizeof(T)); + Core::Memory::CpuGuestMemory<T, Core::Memory::GuestMemoryFlags::UnsafeRead> samples( + memory, source, samples_to_decode); if constexpr (std::is_floating_point_v<T>) { for (u32 i = 0; i < samples_to_decode; i++) { - auto sample{static_cast<s32>(tmp_samples[i * channel_count + req.target_channel] * + auto sample{static_cast<s32>(samples[i * channel_count + req.target_channel] * std::numeric_limits<s16>::max())}; out_buffer[i] = static_cast<s16>(std::clamp(sample, min, max)); } } else { - std::memcpy(out_buffer.data(), tmp_samples.data(), samples_to_decode * sizeof(s16)); + std::memcpy(out_buffer.data(), samples.data(), samples_to_decode * sizeof(s16)); } break; } @@ -101,7 +100,6 @@ static u32 DecodePcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, */ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, const DecodeArg& req) { - std::array<u8, TempBufferSize> wavebuffer{}; constexpr u32 SamplesPerFrame{14}; constexpr u32 NibblesPerFrame{16}; @@ -139,7 +137,8 @@ static u32 DecodeAdpcm(Core::Memory::Memory& memory, std::span<s16> out_buffer, } const auto size{std::max((samples_to_process / 8U) * SamplesPerFrame, 8U)}; - memory.ReadBlockUnsafe(req.buffer + position_in_frame / 2, wavebuffer.data(), size); + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> wavebuffer( + memory, req.buffer + position_in_frame / 2, size); auto context{req.adpcm_context}; auto header{context->header}; diff --git a/src/audio_core/renderer/command/effect/aux_.cpp b/src/audio_core/renderer/command/effect/aux_.cpp index c5650effa..a3e12b3e7 100644 --- a/src/audio_core/renderer/command/effect/aux_.cpp +++ b/src/audio_core/renderer/command/effect/aux_.cpp @@ -21,23 +21,13 @@ static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_in } AuxInfo::AuxInfoDsp info{}; - auto info_ptr{&info}; - bool host_safe{(aux_info & Core::Memory::YUZU_PAGEMASK) <= - (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp))}; + memory.ReadBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp)); - if (host_safe) [[likely]] { - info_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(aux_info); - } else { - memory.ReadBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp)); - } + info.read_offset = 0; + info.write_offset = 0; + info.total_sample_count = 0; - info_ptr->read_offset = 0; - info_ptr->write_offset = 0; - info_ptr->total_sample_count = 0; - - if (!host_safe) [[unlikely]] { - memory.WriteBlockUnsafe(aux_info, info_ptr, sizeof(AuxInfo::AuxInfoDsp)); - } + memory.WriteBlockUnsafe(aux_info, &info, sizeof(AuxInfo::AuxInfoDsp)); } /** @@ -86,17 +76,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_, } AuxInfo::AuxInfoDsp send_info{}; - auto send_ptr = &send_info; - bool host_safe = (send_info_ & Core::Memory::YUZU_PAGEMASK) <= - (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp)); - - if (host_safe) [[likely]] { - send_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(send_info_); - } else { - memory.ReadBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp)); - } + memory.ReadBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp)); - u32 target_write_offset{send_ptr->write_offset + write_offset}; + u32 target_write_offset{send_info.write_offset + write_offset}; if (target_write_offset > count_max) { return 0; } @@ -105,15 +87,9 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_, u32 read_pos{0}; while (write_count > 0) { u32 to_write{std::min(count_max - target_write_offset, write_count)}; - const auto write_addr = send_buffer + target_write_offset * sizeof(s32); - bool write_safe{(write_addr & Core::Memory::YUZU_PAGEMASK) <= - (Core::Memory::YUZU_PAGESIZE - (write_addr + to_write * sizeof(s32)))}; - if (write_safe) [[likely]] { - auto ptr = memory.GetPointer(write_addr); - std::memcpy(ptr, &input[read_pos], to_write * sizeof(s32)); - } else { - memory.WriteBlockUnsafe(send_buffer + target_write_offset * sizeof(s32), - &input[read_pos], to_write * sizeof(s32)); + if (to_write > 0) { + const auto write_addr = send_buffer + target_write_offset * sizeof(s32); + memory.WriteBlockUnsafe(write_addr, &input[read_pos], to_write * sizeof(s32)); } target_write_offset = (target_write_offset + to_write) % count_max; write_count -= to_write; @@ -121,13 +97,10 @@ static u32 WriteAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr send_info_, } if (update_count) { - send_ptr->write_offset = (send_ptr->write_offset + update_count) % count_max; - } - - if (!host_safe) [[unlikely]] { - memory.WriteBlockUnsafe(send_info_, send_ptr, sizeof(AuxInfo::AuxInfoDsp)); + send_info.write_offset = (send_info.write_offset + update_count) % count_max; } + memory.WriteBlockUnsafe(send_info_, &send_info, sizeof(AuxInfo::AuxInfoDsp)); return write_count_; } @@ -174,17 +147,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_, } AuxInfo::AuxInfoDsp return_info{}; - auto return_ptr = &return_info; - bool host_safe = (return_info_ & Core::Memory::YUZU_PAGEMASK) <= - (Core::Memory::YUZU_PAGESIZE - sizeof(AuxInfo::AuxInfoDsp)); + memory.ReadBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp)); - if (host_safe) [[likely]] { - return_ptr = memory.GetPointer<AuxInfo::AuxInfoDsp>(return_info_); - } else { - memory.ReadBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp)); - } - - u32 target_read_offset{return_ptr->read_offset + read_offset}; + u32 target_read_offset{return_info.read_offset + read_offset}; if (target_read_offset > count_max) { return 0; } @@ -193,15 +158,9 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_, u32 write_pos{0}; while (read_count > 0) { u32 to_read{std::min(count_max - target_read_offset, read_count)}; - const auto read_addr = return_buffer + target_read_offset * sizeof(s32); - bool read_safe{(read_addr & Core::Memory::YUZU_PAGEMASK) <= - (Core::Memory::YUZU_PAGESIZE - (read_addr + to_read * sizeof(s32)))}; - if (read_safe) [[likely]] { - auto ptr = memory.GetPointer(read_addr); - std::memcpy(&output[write_pos], ptr, to_read * sizeof(s32)); - } else { - memory.ReadBlockUnsafe(return_buffer + target_read_offset * sizeof(s32), - &output[write_pos], to_read * sizeof(s32)); + if (to_read > 0) { + const auto read_addr = return_buffer + target_read_offset * sizeof(s32); + memory.ReadBlockUnsafe(read_addr, &output[write_pos], to_read * sizeof(s32)); } target_read_offset = (target_read_offset + to_read) % count_max; read_count -= to_read; @@ -209,13 +168,10 @@ static u32 ReadAuxBufferDsp(Core::Memory::Memory& memory, CpuAddr return_info_, } if (update_count) { - return_ptr->read_offset = (return_ptr->read_offset + update_count) % count_max; - } - - if (!host_safe) [[unlikely]] { - memory.WriteBlockUnsafe(return_info_, return_ptr, sizeof(AuxInfo::AuxInfoDsp)); + return_info.read_offset = (return_info.read_offset + update_count) % count_max; } + memory.WriteBlockUnsafe(return_info_, &return_info, sizeof(AuxInfo::AuxInfoDsp)); return read_count_; } diff --git a/src/common/demangle.cpp b/src/common/demangle.cpp index 3310faf86..6e117cb41 100644 --- a/src/common/demangle.cpp +++ b/src/common/demangle.cpp @@ -23,7 +23,7 @@ std::string DemangleSymbol(const std::string& mangled) { SCOPE_EXIT({ std::free(demangled); }); if (is_itanium(mangled)) { - demangled = llvm::itaniumDemangle(mangled.c_str(), nullptr, nullptr, nullptr); + demangled = llvm::itaniumDemangle(mangled.c_str()); } if (!demangled) { diff --git a/src/common/detached_tasks.cpp b/src/common/detached_tasks.cpp index da64848da..f2ed795cc 100644 --- a/src/common/detached_tasks.cpp +++ b/src/common/detached_tasks.cpp @@ -30,8 +30,8 @@ DetachedTasks::~DetachedTasks() { void DetachedTasks::AddTask(std::function<void()> task) { std::unique_lock lock{instance->mutex}; ++instance->count; - std::thread([task{std::move(task)}]() { - task(); + std::thread([task_{std::move(task)}]() { + task_(); std::unique_lock thread_lock{instance->mutex}; --instance->count; std::notify_all_at_thread_exit(instance->cv, std::move(thread_lock)); diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp index b744b68ce..4b1690269 100644 --- a/src/common/page_table.cpp +++ b/src/common/page_table.cpp @@ -66,6 +66,7 @@ void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page << (address_space_width_in_bits - page_size_in_bits)}; pointers.resize(num_page_table_entries); backing_addr.resize(num_page_table_entries); + blocks.resize(num_page_table_entries); current_address_space_width_in_bits = address_space_width_in_bits; page_size = 1ULL << page_size_in_bits; } diff --git a/src/common/page_table.h b/src/common/page_table.h index 1ad3a9f8b..fec8378f3 100644 --- a/src/common/page_table.h +++ b/src/common/page_table.h @@ -122,6 +122,7 @@ struct PageTable { * corresponding attribute element is of type `Memory`. */ VirtualBuffer<PageInfo> pointers; + VirtualBuffer<u64> blocks; VirtualBuffer<u64> backing_addr; diff --git a/src/common/scratch_buffer.h b/src/common/scratch_buffer.h index d5961b020..2a98cda53 100644 --- a/src/common/scratch_buffer.h +++ b/src/common/scratch_buffer.h @@ -40,8 +40,21 @@ public: ~ScratchBuffer() = default; ScratchBuffer(const ScratchBuffer&) = delete; ScratchBuffer& operator=(const ScratchBuffer&) = delete; - ScratchBuffer(ScratchBuffer&&) = default; - ScratchBuffer& operator=(ScratchBuffer&&) = default; + + ScratchBuffer(ScratchBuffer&& other) noexcept { + swap(other); + other.last_requested_size = 0; + other.buffer_capacity = 0; + other.buffer.reset(); + } + + ScratchBuffer& operator=(ScratchBuffer&& other) noexcept { + swap(other); + other.last_requested_size = 0; + other.buffer_capacity = 0; + other.buffer.reset(); + return *this; + } /// This will only grow the buffer's capacity if size is greater than the current capacity. /// The previously held data will remain intact. diff --git a/src/common/settings.cpp b/src/common/settings.cpp index 6cbbea1b2..d4e55f988 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp @@ -26,9 +26,10 @@ std::string GetTimeZoneString() { std::string location_name; if (time_zone_index == 0) { // Auto -#if __cpp_lib_chrono >= 201907L - const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); +#if __cpp_lib_chrono >= 201907L && !defined(MINGW) + // Disabled for MinGW -- tzdb always returns Etc/UTC try { + const struct std::chrono::tzdb& time_zone_data = std::chrono::get_tzdb(); const std::chrono::time_zone* current_zone = time_zone_data.current_zone(); std::string_view current_zone_name = current_zone->name(); location_name = current_zone_name; diff --git a/src/common/socket_types.h b/src/common/socket_types.h index 0a801a443..63824a5c4 100644 --- a/src/common/socket_types.h +++ b/src/common/socket_types.h @@ -3,17 +3,22 @@ #pragma once +#include <optional> +#include <string> + #include "common/common_types.h" namespace Network { /// Address families enum class Domain : u8 { - INET, ///< Address family for IPv4 + Unspecified, ///< Represents 0, used in getaddrinfo hints + INET, ///< Address family for IPv4 }; /// Socket types enum class Type { + Unspecified, ///< Represents 0, used in getaddrinfo hints STREAM, DGRAM, RAW, @@ -22,6 +27,7 @@ enum class Type { /// Protocol values for sockets enum class Protocol : u8 { + Unspecified, ///< Represents 0, usable in various places ICMP, TCP, UDP, @@ -48,4 +54,13 @@ constexpr u32 FLAG_MSG_PEEK = 0x2; constexpr u32 FLAG_MSG_DONTWAIT = 0x80; constexpr u32 FLAG_O_NONBLOCK = 0x800; +/// Cross-platform addrinfo structure +struct AddrInfo { + Domain family; + Type socket_type; + Protocol protocol; + SockAddrIn addr; + std::optional<std::string> canon_name; +}; + } // namespace Network diff --git a/src/common/time_zone.cpp b/src/common/time_zone.cpp index d8d7896c6..69e728a9d 100644 --- a/src/common/time_zone.cpp +++ b/src/common/time_zone.cpp @@ -4,13 +4,13 @@ #include <chrono> #include <exception> #include <iomanip> +#include <map> #include <sstream> #include <stdexcept> #include <fmt/chrono.h> #include <fmt/core.h> #include "common/logging/log.h" -#include "common/settings.h" #include "common/time_zone.h" namespace Common::TimeZone { @@ -33,32 +33,29 @@ std::string GetDefaultTimeZone() { return "GMT"; } -static std::string GetOsTimeZoneOffset() { - const std::time_t t{std::time(nullptr)}; - const std::tm tm{*std::localtime(&t)}; - - return fmt::format("{:%z}", tm); -} - -static int ConvertOsTimeZoneOffsetToInt(const std::string& timezone) { - try { - return std::stoi(timezone); - } catch (const std::invalid_argument&) { - LOG_CRITICAL(Common, "invalid_argument with {}!", timezone); - return 0; - } catch (const std::out_of_range&) { - LOG_CRITICAL(Common, "out_of_range with {}!", timezone); - return 0; - } +// Results are not comparable to seconds since Epoch +static std::time_t TmSpecToSeconds(const struct std::tm& spec) { + const int year = spec.tm_year - 1; // Years up to now + const int leap_years = year / 4 - year / 100; + std::time_t cumulative = spec.tm_year; + cumulative = cumulative * 365 + leap_years + spec.tm_yday; // Years to days + cumulative = cumulative * 24 + spec.tm_hour; // Days to hours + cumulative = cumulative * 60 + spec.tm_min; // Hours to minutes + cumulative = cumulative * 60 + spec.tm_sec; // Minutes to seconds + return cumulative; } std::chrono::seconds GetCurrentOffsetSeconds() { - const int offset{ConvertOsTimeZoneOffsetToInt(GetOsTimeZoneOffset())}; + const std::time_t t{std::time(nullptr)}; + const std::tm local{*std::localtime(&t)}; + const std::tm gmt{*std::gmtime(&t)}; - int seconds{(offset / 100) * 60 * 60}; // Convert hour component to seconds - seconds += (offset % 100) * 60; // Convert minute component to seconds + // gmt_seconds is a different offset than time(nullptr) + const auto gmt_seconds = TmSpecToSeconds(gmt); + const auto local_seconds = TmSpecToSeconds(local); + const auto seconds_offset = local_seconds - gmt_seconds; - return std::chrono::seconds{seconds}; + return std::chrono::seconds{seconds_offset}; } // Key is [Hours * 100 + Minutes], multiplied by 100 if DST @@ -71,11 +68,6 @@ const static std::map<s64, const char*> off_timezones = { }; std::string FindSystemTimeZone() { -#if defined(MINGW) - // MinGW has broken strftime -- https://sourceforge.net/p/mingw-w64/bugs/793/ - // e.g. fmt::format("{:%z}") -- returns "Eastern Daylight Time" when it should be "-0400" - return timezones[0]; -#else const s64 seconds = static_cast<s64>(GetCurrentOffsetSeconds().count()); const s64 minutes = seconds / 60; @@ -97,7 +89,6 @@ std::string FindSystemTimeZone() { } } return fmt::format("Etc/GMT{:s}{:d}", hours > 0 ? "-" : "+", std::abs(hours)); -#endif } } // namespace Common::TimeZone diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3655b8478..4b7395be8 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -285,6 +285,7 @@ add_library(core STATIC hle/kernel/kernel.cpp hle/kernel/kernel.h hle/kernel/memory_types.h + hle/kernel/message_buffer.h hle/kernel/physical_core.cpp hle/kernel/physical_core.h hle/kernel/physical_memory.h @@ -722,6 +723,7 @@ add_library(core STATIC hle/service/spl/spl_types.h hle/service/ssl/ssl.cpp hle/service/ssl/ssl.h + hle/service/ssl/ssl_backend.h hle/service/time/clock_types.h hle/service/time/ephemeral_network_system_clock_context_writer.h hle/service/time/ephemeral_network_system_clock_core.h @@ -863,6 +865,23 @@ if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64) target_link_libraries(core PRIVATE dynarmic::dynarmic) endif() +if(ENABLE_OPENSSL) + target_sources(core PRIVATE + hle/service/ssl/ssl_backend_openssl.cpp) + target_link_libraries(core PRIVATE OpenSSL::SSL) +elseif (APPLE) + target_sources(core PRIVATE + hle/service/ssl/ssl_backend_securetransport.cpp) + target_link_libraries(core PRIVATE "-framework Security") +elseif (WIN32) + target_sources(core PRIVATE + hle/service/ssl/ssl_backend_schannel.cpp) + target_link_libraries(core PRIVATE crypt32 secur32) +else() + target_sources(core PRIVATE + hle/service/ssl/ssl_backend_none.cpp) +endif() + if (YUZU_USE_PRECOMPILED_HEADERS) target_precompile_headers(core PRIVATE precompiled_headers.h) endif() diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp index beaea64b3..aa0eb9791 100644 --- a/src/core/arm/arm_interface.cpp +++ b/src/core/arm/arm_interface.cpp @@ -185,7 +185,7 @@ void ARM_Interface::Run() { // Notify the debugger and go to sleep if a breakpoint was hit, // or if the thread is unable to continue for any reason. if (True(hr & HaltReason::InstructionBreakpoint) || True(hr & HaltReason::PrefetchAbort)) { - if (!True(hr & HaltReason::InstructionBreakpoint)) { + if (!True(hr & HaltReason::PrefetchAbort)) { RewindBreakpointInstruction(); } if (system.DebuggerEnabled()) { diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp index 3b82fb73c..c97158a71 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp @@ -346,11 +346,11 @@ void ARM_Dynarmic_32::RewindBreakpointInstruction() { } ARM_Dynarmic_32::ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + DynarmicExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_) : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks32>(*this)), cp15(std::make_shared<DynarmicCP15>(*this)), core_index{core_index_}, - exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, - null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} + exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr)}, jit{null_jit.get()} {} ARM_Dynarmic_32::~ARM_Dynarmic_32() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.h b/src/core/arm/dynarmic/arm_dynarmic_32.h index a990845cb..92fb3f836 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_32.h +++ b/src/core/arm/dynarmic/arm_dynarmic_32.h @@ -12,7 +12,7 @@ #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" -#include "core/arm/exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" namespace Core::Memory { class Memory; @@ -28,8 +28,8 @@ class System; class ARM_Dynarmic_32 final : public ARM_Interface { public: - ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_); + ARM_Dynarmic_32(System& system_, bool uses_wall_clock_, + DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); ~ARM_Dynarmic_32() override; void SetPC(u64 pc) override; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp index bb97ed5bc..791d466ca 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp @@ -405,11 +405,11 @@ void ARM_Dynarmic_64::RewindBreakpointInstruction() { } ARM_Dynarmic_64::ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, - ExclusiveMonitor& exclusive_monitor_, std::size_t core_index_) + DynarmicExclusiveMonitor& exclusive_monitor_, + std::size_t core_index_) : ARM_Interface{system_, uses_wall_clock_}, cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index_}, - exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor_)}, - null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} + exclusive_monitor{exclusive_monitor_}, null_jit{MakeJit(nullptr, 48)}, jit{null_jit.get()} {} ARM_Dynarmic_64::~ARM_Dynarmic_64() = default; diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h index af2aa1f1c..2b88a08e2 100644 --- a/src/core/arm/dynarmic/arm_dynarmic_64.h +++ b/src/core/arm/dynarmic/arm_dynarmic_64.h @@ -11,7 +11,7 @@ #include "common/common_types.h" #include "common/hash.h" #include "core/arm/arm_interface.h" -#include "core/arm/exclusive_monitor.h" +#include "core/arm/dynarmic/dynarmic_exclusive_monitor.h" namespace Core::Memory { class Memory; @@ -25,8 +25,8 @@ class System; class ARM_Dynarmic_64 final : public ARM_Interface { public: - ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, ExclusiveMonitor& exclusive_monitor_, - std::size_t core_index_); + ARM_Dynarmic_64(System& system_, bool uses_wall_clock_, + DynarmicExclusiveMonitor& exclusive_monitor_, std::size_t core_index_); ~ARM_Dynarmic_64() override; void SetPC(u64 pc) override; diff --git a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h index 57e6dd0d0..fbfcd8d95 100644 --- a/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h +++ b/src/core/arm/dynarmic/dynarmic_exclusive_monitor.h @@ -6,8 +6,6 @@ #include <dynarmic/interface/exclusive_monitor.h> #include "common/common_types.h" -#include "core/arm/dynarmic/arm_dynarmic_32.h" -#include "core/arm/dynarmic/arm_dynarmic_64.h" #include "core/arm/exclusive_monitor.h" namespace Core::Memory { @@ -16,6 +14,9 @@ class Memory; namespace Core { +class ARM_Dynarmic_32; +class ARM_Dynarmic_64; + class DynarmicExclusiveMonitor final : public ExclusiveMonitor { public: explicit DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count_); diff --git a/src/core/core.cpp b/src/core/core.cpp index 9e3eb3795..48233d7c8 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -880,6 +880,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const { return *impl->content_provider; } +FileSys::ContentProviderUnion& System::GetContentProviderUnion() { + return *impl->content_provider; +} + +const FileSys::ContentProviderUnion& System::GetContentProviderUnion() const { + return *impl->content_provider; +} + Service::FileSystem::FileSystemController& System::GetFileSystemController() { return impl->fs_controller; } diff --git a/src/core/core.h b/src/core/core.h index 14b2f7785..c70ea1965 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -381,6 +381,9 @@ public: [[nodiscard]] FileSys::ContentProvider& GetContentProvider(); [[nodiscard]] const FileSys::ContentProvider& GetContentProvider() const; + [[nodiscard]] FileSys::ContentProviderUnion& GetContentProviderUnion(); + [[nodiscard]] const FileSys::ContentProviderUnion& GetContentProviderUnion() const; + [[nodiscard]] Service::FileSystem::FileSystemController& GetFileSystemController(); [[nodiscard]] const Service::FileSystem::FileSystemController& GetFileSystemController() const; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index e6112a3c9..b98a0cb33 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -70,7 +70,7 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) { -> std::optional<std::chrono::nanoseconds> { return std::nullopt; }; ev_lost = CreateEvent("_lost_event", empty_timed_callback); if (is_multicore) { - timer_thread = std::make_unique<std::thread>(ThreadEntry, std::ref(*this)); + timer_thread = std::make_unique<std::jthread>(ThreadEntry, std::ref(*this)); } } @@ -255,7 +255,6 @@ void CoreTiming::ThreadLoop() { #ifdef _WIN32 while (!paused && !event.IsSet() && wait_time > 0) { wait_time = *next_time - GetGlobalTimeNs().count(); - if (wait_time >= timer_resolution_ns) { Common::Windows::SleepForOneTick(); } else { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index 5bca1c78d..c20e906fb 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -163,7 +163,7 @@ private: Common::Event pause_event{}; std::mutex basic_lock; std::mutex advance_lock; - std::unique_ptr<std::thread> timer_thread; + std::unique_ptr<std::jthread> timer_thread; std::atomic<bool> paused{}; std::atomic<bool> paused_set{}; std::atomic<bool> wait_set{}; diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index e2a13bbd2..da6078372 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -556,7 +556,7 @@ void GDBStub::HandleQuery(std::string_view command) { } else { SendReply(fmt::format( "TextSeg={:x}", - GetInteger(system.ApplicationProcess()->PageTable().GetCodeRegionStart()))); + GetInteger(system.ApplicationProcess()->GetPageTable().GetCodeRegionStart()))); } } else if (command.starts_with("Xfer:libraries:read::")) { Loader::AppLoader::Modules modules; @@ -731,7 +731,7 @@ void GDBStub::HandleRcmd(const std::vector<u8>& command) { std::string reply; auto* process = system.ApplicationProcess(); - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); const char* commands = "Commands:\n" " get fastmem\n" diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 50303fe42..06efab46d 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp @@ -57,11 +57,34 @@ struct NCASectionHeaderBlock { }; static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); +struct NCABucketInfo { + u64 table_offset; + u64 table_size; + std::array<u8, 0x10> table_header; +}; +static_assert(sizeof(NCABucketInfo) == 0x20, "NCABucketInfo has incorrect size."); + +struct NCASparseInfo { + NCABucketInfo bucket; + u64 physical_offset; + u16 generation; + INSERT_PADDING_BYTES_NOINIT(0x6); +}; +static_assert(sizeof(NCASparseInfo) == 0x30, "NCASparseInfo has incorrect size."); + +struct NCACompressionInfo { + NCABucketInfo bucket; + INSERT_PADDING_BYTES_NOINIT(0x8); +}; +static_assert(sizeof(NCACompressionInfo) == 0x28, "NCACompressionInfo has incorrect size."); + struct NCASectionRaw { NCASectionHeaderBlock header; std::array<u8, 0x138> block_data; std::array<u8, 0x8> section_ctr; - INSERT_PADDING_BYTES_NOINIT(0xB8); + NCASparseInfo sparse_info; + NCACompressionInfo compression_info; + INSERT_PADDING_BYTES_NOINIT(0x60); }; static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size."); @@ -225,6 +248,20 @@ bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_b for (std::size_t i = 0; i < sections.size(); ++i) { const auto& section = sections[i]; + if (section.raw.sparse_info.bucket.table_offset != 0 && + section.raw.sparse_info.bucket.table_size != 0) { + LOG_ERROR(Loader, "Sparse NCAs are not supported."); + status = Loader::ResultStatus::ErrorSparseNCA; + return false; + } + + if (section.raw.compression_info.bucket.table_offset != 0 && + section.raw.compression_info.bucket.table_size != 0) { + LOG_ERROR(Loader, "Compressed NCAs are not supported."); + status = Loader::ResultStatus::ErrorCompressedNCA; + return false; + } + if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) { return false; diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index 3583bee44..7454be55c 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -25,7 +25,7 @@ Result KCodeMemory::Initialize(Core::DeviceMemory& device_memory, KProcessAddres m_owner = GetCurrentProcessPointer(m_kernel); // Get the owner page table. - auto& page_table = m_owner->PageTable(); + auto& page_table = m_owner->GetPageTable(); // Construct the page group. m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); @@ -53,7 +53,7 @@ void KCodeMemory::Finalize() { // Unlock. if (!m_is_mapped && !m_is_owner_mapped) { const size_t size = m_page_group->GetNumPages() * PageSize; - m_owner->PageTable().UnlockForCodeMemory(m_address, size, *m_page_group); + m_owner->GetPageTable().UnlockForCodeMemory(m_address, size, *m_page_group); } // Close the page group. @@ -75,7 +75,7 @@ Result KCodeMemory::Map(KProcessAddress address, size_t size) { R_UNLESS(!m_is_mapped, ResultInvalidState); // Map the memory. - R_TRY(GetCurrentProcess(m_kernel).PageTable().MapPageGroup( + R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite)); // Mark ourselves as mapped. @@ -92,8 +92,8 @@ Result KCodeMemory::Unmap(KProcessAddress address, size_t size) { KScopedLightLock lk(m_lock); // Unmap the memory. - R_TRY(GetCurrentProcess(m_kernel).PageTable().UnmapPageGroup(address, *m_page_group, - KMemoryState::CodeOut)); + R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, + KMemoryState::CodeOut)); // Mark ourselves as unmapped. m_is_mapped = false; @@ -126,8 +126,8 @@ Result KCodeMemory::MapToOwner(KProcessAddress address, size_t size, Svc::Memory } // Map the memory. - R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode, - k_perm)); + R_TRY(m_owner->GetPageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode, + k_perm)); // Mark ourselves as mapped. m_is_owner_mapped = true; @@ -143,7 +143,8 @@ Result KCodeMemory::UnmapFromOwner(KProcessAddress address, size_t size) { KScopedLightLock lk(m_lock); // Unmap the memory. - R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode)); + R_TRY(m_owner->GetPageTable().UnmapPageGroup(address, *m_page_group, + KMemoryState::GeneratedCode)); // Mark ourselves as unmapped. m_is_owner_mapped = false; diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 022d15f35..b9e8c6042 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -388,39 +388,6 @@ public: constexpr size_t GetHeapSize() const { return m_current_heap_end - m_heap_region_start; } - constexpr bool IsInsideAddressSpace(KProcessAddress address, size_t size) const { - return m_address_space_start <= address && address + size - 1 <= m_address_space_end - 1; - } - constexpr bool IsOutsideAliasRegion(KProcessAddress address, size_t size) const { - return m_alias_region_start > address || address + size - 1 > m_alias_region_end - 1; - } - constexpr bool IsOutsideStackRegion(KProcessAddress address, size_t size) const { - return m_stack_region_start > address || address + size - 1 > m_stack_region_end - 1; - } - constexpr bool IsInvalidRegion(KProcessAddress address, size_t size) const { - return address + size - 1 > GetAliasCodeRegionStart() + GetAliasCodeRegionSize() - 1; - } - constexpr bool IsInsideHeapRegion(KProcessAddress address, size_t size) const { - return address + size > m_heap_region_start && m_heap_region_end > address; - } - constexpr bool IsInsideAliasRegion(KProcessAddress address, size_t size) const { - return address + size > m_alias_region_start && m_alias_region_end > address; - } - constexpr bool IsOutsideASLRRegion(KProcessAddress address, size_t size) const { - if (IsInvalidRegion(address, size)) { - return true; - } - if (IsInsideHeapRegion(address, size)) { - return true; - } - if (IsInsideAliasRegion(address, size)) { - return true; - } - return {}; - } - constexpr bool IsInsideASLRRegion(KProcessAddress address, size_t size) const { - return !IsOutsideASLRRegion(address, size); - } constexpr size_t GetNumGuardPages() const { return IsKernel() ? 1 : 4; } @@ -436,6 +403,14 @@ public: return m_address_space_start <= addr && addr < addr + size && addr + size - 1 <= m_address_space_end - 1; } + constexpr bool IsInAliasRegion(KProcessAddress addr, size_t size) const { + return this->Contains(addr, size) && m_alias_region_start <= addr && + addr + size - 1 <= m_alias_region_end - 1; + } + constexpr bool IsInHeapRegion(KProcessAddress addr, size_t size) const { + return this->Contains(addr, size) && m_heap_region_start <= addr && + addr + size - 1 <= m_heap_region_end - 1; + } public: static KVirtualAddress GetLinearMappedVirtualAddress(const KMemoryLayout& layout, diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index efe86ad27..44c7cb22f 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -38,7 +38,7 @@ namespace { */ void SetupMainThread(Core::System& system, KProcess& owner_process, u32 priority, KProcessAddress stack_top) { - const KProcessAddress entry_point = owner_process.PageTable().GetCodeRegionStart(); + const KProcessAddress entry_point = owner_process.GetPageTable().GetCodeRegionStart(); ASSERT(owner_process.GetResourceLimit()->Reserve(LimitableResource::ThreadCountMax, 1)); KThread* thread = KThread::Create(system.Kernel()); diff --git a/src/core/hle/kernel/k_process.h b/src/core/hle/kernel/k_process.h index 925981d06..c9b37e138 100644 --- a/src/core/hle/kernel/k_process.h +++ b/src/core/hle/kernel/k_process.h @@ -110,16 +110,6 @@ public: ProcessType type, KResourceLimit* res_limit); /// Gets a reference to the process' page table. - KPageTable& PageTable() { - return m_page_table; - } - - /// Gets const a reference to the process' page table. - const KPageTable& PageTable() const { - return m_page_table; - } - - /// Gets a reference to the process' page table. KPageTable& GetPageTable() { return m_page_table; } diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index c66aff501..c64ceb530 100644 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -20,12 +20,132 @@ #include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread_queue.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/message_buffer.h" #include "core/hle/service/hle_ipc.h" #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" namespace Kernel { +namespace { + +template <bool MoveHandleAllowed> +Result ProcessMessageSpecialData(KProcess& dst_process, KProcess& src_process, KThread& src_thread, + MessageBuffer& dst_msg, const MessageBuffer& src_msg, + MessageBuffer::SpecialHeader& src_special_header) { + // Copy the special header to the destination. + s32 offset = dst_msg.Set(src_special_header); + + // Copy the process ID. + if (src_special_header.GetHasProcessId()) { + offset = dst_msg.SetProcessId(offset, src_process.GetProcessId()); + } + + // Prepare to process handles. + auto& dst_handle_table = dst_process.GetHandleTable(); + auto& src_handle_table = src_process.GetHandleTable(); + Result result = ResultSuccess; + + // Process copy handles. + for (auto i = 0; i < src_special_header.GetCopyHandleCount(); ++i) { + // Get the handles. + const Handle src_handle = src_msg.GetHandle(offset); + Handle dst_handle = Svc::InvalidHandle; + + // If we're in a success state, try to move the handle to the new table. + if (R_SUCCEEDED(result) && src_handle != Svc::InvalidHandle) { + KScopedAutoObject obj = + src_handle_table.GetObjectForIpc(src_handle, std::addressof(src_thread)); + if (obj.IsNotNull()) { + Result add_result = + dst_handle_table.Add(std::addressof(dst_handle), obj.GetPointerUnsafe()); + if (R_FAILED(add_result)) { + result = add_result; + dst_handle = Svc::InvalidHandle; + } + } else { + result = ResultInvalidHandle; + } + } + + // Set the handle. + offset = dst_msg.SetHandle(offset, dst_handle); + } + + // Process move handles. + if constexpr (MoveHandleAllowed) { + for (auto i = 0; i < src_special_header.GetMoveHandleCount(); ++i) { + // Get the handles. + const Handle src_handle = src_msg.GetHandle(offset); + Handle dst_handle = Svc::InvalidHandle; + + // Whether or not we've succeeded, we need to remove the handles from the source table. + if (src_handle != Svc::InvalidHandle) { + if (R_SUCCEEDED(result)) { + KScopedAutoObject obj = + src_handle_table.GetObjectForIpcWithoutPseudoHandle(src_handle); + if (obj.IsNotNull()) { + Result add_result = dst_handle_table.Add(std::addressof(dst_handle), + obj.GetPointerUnsafe()); + + src_handle_table.Remove(src_handle); + + if (R_FAILED(add_result)) { + result = add_result; + dst_handle = Svc::InvalidHandle; + } + } else { + result = ResultInvalidHandle; + } + } else { + src_handle_table.Remove(src_handle); + } + } + + // Set the handle. + offset = dst_msg.SetHandle(offset, dst_handle); + } + } + + R_RETURN(result); +} + +void CleanupSpecialData(KProcess& dst_process, u32* dst_msg_ptr, size_t dst_buffer_size) { + // Parse the message. + const MessageBuffer dst_msg(dst_msg_ptr, dst_buffer_size); + const MessageBuffer::MessageHeader dst_header(dst_msg); + const MessageBuffer::SpecialHeader dst_special_header(dst_msg, dst_header); + + // Check that the size is big enough. + if (MessageBuffer::GetMessageBufferSize(dst_header, dst_special_header) > dst_buffer_size) { + return; + } + + // Set the special header. + int offset = dst_msg.Set(dst_special_header); + + // Clear the process id, if needed. + if (dst_special_header.GetHasProcessId()) { + offset = dst_msg.SetProcessId(offset, 0); + } + + // Clear handles, as relevant. + auto& dst_handle_table = dst_process.GetHandleTable(); + for (auto i = 0; + i < (dst_special_header.GetCopyHandleCount() + dst_special_header.GetMoveHandleCount()); + ++i) { + const Handle handle = dst_msg.GetHandle(offset); + + if (handle != Svc::InvalidHandle) { + dst_handle_table.Remove(handle); + } + + offset = dst_msg.SetHandle(offset, Svc::InvalidHandle); + } +} + +} // namespace + using ThreadQueueImplForKServerSessionRequest = KThreadQueue; KServerSession::KServerSession(KernelCore& kernel) @@ -223,12 +343,27 @@ Result KServerSession::SendReply(bool is_hle) { // the reply has already been written in this case. } else { Core::Memory::Memory& memory{client_thread->GetOwnerProcess()->GetMemory()}; - KThread* server_thread{GetCurrentThreadPointer(m_kernel)}; + KThread* server_thread = GetCurrentThreadPointer(m_kernel); + KProcess& src_process = *client_thread->GetOwnerProcess(); + KProcess& dst_process = *server_thread->GetOwnerProcess(); UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress()); - auto* dst_msg_buffer = memory.GetPointer(client_message); + auto* src_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress()); + auto* dst_msg_buffer = memory.GetPointer<u32>(client_message); std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + + // Translate special header ad-hoc. + MessageBuffer src_msg(src_msg_buffer, client_buffer_size); + MessageBuffer::MessageHeader src_header(src_msg); + MessageBuffer::SpecialHeader src_special_header(src_msg, src_header); + if (src_header.GetHasSpecialHeader()) { + MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size); + result = ProcessMessageSpecialData<true>(dst_process, src_process, *server_thread, + dst_msg, src_msg, src_special_header); + if (R_FAILED(result)) { + CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size); + } + } } } else { result = ResultSessionClosed; @@ -330,12 +465,28 @@ Result KServerSession::ReceiveRequest(std::shared_ptr<Service::HLERequestContext ->PopulateFromIncomingCommandBuffer(client_thread->GetOwnerProcess()->GetHandleTable(), cmd_buf); } else { - KThread* server_thread{GetCurrentThreadPointer(m_kernel)}; - UNIMPLEMENTED_IF(server_thread->GetOwnerProcess() != client_thread->GetOwnerProcess()); + KThread* server_thread = GetCurrentThreadPointer(m_kernel); + KProcess& src_process = *client_thread->GetOwnerProcess(); + KProcess& dst_process = *server_thread->GetOwnerProcess(); + UNIMPLEMENTED_IF(client_thread->GetOwnerProcess() != server_thread->GetOwnerProcess()); - auto* src_msg_buffer = memory.GetPointer(client_message); - auto* dst_msg_buffer = memory.GetPointer(server_thread->GetTlsAddress()); + auto* src_msg_buffer = memory.GetPointer<u32>(client_message); + auto* dst_msg_buffer = memory.GetPointer<u32>(server_thread->GetTlsAddress()); std::memcpy(dst_msg_buffer, src_msg_buffer, client_buffer_size); + + // Translate special header ad-hoc. + // TODO: fix this mess + MessageBuffer src_msg(src_msg_buffer, client_buffer_size); + MessageBuffer::MessageHeader src_header(src_msg); + MessageBuffer::SpecialHeader src_special_header(src_msg, src_header); + if (src_header.GetHasSpecialHeader()) { + MessageBuffer dst_msg(dst_msg_buffer, client_buffer_size); + Result res = ProcessMessageSpecialData<false>(dst_process, src_process, *client_thread, + dst_msg, src_msg, src_special_header); + if (R_FAILED(res)) { + CleanupSpecialData(dst_process, dst_msg_buffer, client_buffer_size); + } + } } // We succeeded. diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index efb5699de..f713968f6 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -90,8 +90,8 @@ Result KSharedMemory::Map(KProcess& target_process, KProcessAddress address, std R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission); } - R_RETURN(target_process.PageTable().MapPageGroup(address, *m_page_group, KMemoryState::Shared, - ConvertToKMemoryPermission(map_perm))); + R_RETURN(target_process.GetPageTable().MapPageGroup( + address, *m_page_group, KMemoryState::Shared, ConvertToKMemoryPermission(map_perm))); } Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address, @@ -100,7 +100,7 @@ Result KSharedMemory::Unmap(KProcess& target_process, KProcessAddress address, R_UNLESS(m_size == unmap_size, ResultInvalidSize); R_RETURN( - target_process.PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::Shared)); + target_process.GetPageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::Shared)); } } // namespace Kernel diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index adb6ec581..d88909889 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -302,12 +302,12 @@ Result KThread::InitializeServiceThread(Core::System& system, KThread* thread, std::function<void()>&& func, s32 prio, s32 virt_core, KProcess* owner) { system.Kernel().GlobalSchedulerContext().AddThread(thread); - std::function<void()> func2{[&system, func{std::move(func)}] { + std::function<void()> func2{[&system, func_{std::move(func)}] { // Similar to UserModeThreadStarter. system.Kernel().CurrentScheduler()->OnThreadStart(); // Run the guest function. - func(); + func_(); // Exit. Svc::ExitThread(system); diff --git a/src/core/hle/kernel/k_thread_local_page.cpp b/src/core/hle/kernel/k_thread_local_page.cpp index b4a1e3cdb..2c45b4232 100644 --- a/src/core/hle/kernel/k_thread_local_page.cpp +++ b/src/core/hle/kernel/k_thread_local_page.cpp @@ -25,9 +25,9 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { // Map the address in. const auto phys_addr = kernel.System().DeviceMemory().GetPhysicalAddr(page_buf); - R_TRY(m_owner->PageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, - KMemoryState::ThreadLocal, - KMemoryPermission::UserReadWrite)); + R_TRY(m_owner->GetPageTable().MapPages(std::addressof(m_virt_addr), 1, PageSize, phys_addr, + KMemoryState::ThreadLocal, + KMemoryPermission::UserReadWrite)); // We succeeded. page_buf_guard.Cancel(); @@ -37,11 +37,11 @@ Result KThreadLocalPage::Initialize(KernelCore& kernel, KProcess* process) { Result KThreadLocalPage::Finalize() { // Get the physical address of the page. - const KPhysicalAddress phys_addr = m_owner->PageTable().GetPhysicalAddr(m_virt_addr); + const KPhysicalAddress phys_addr = m_owner->GetPageTable().GetPhysicalAddr(m_virt_addr); ASSERT(phys_addr); // Unmap the page. - R_TRY(m_owner->PageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); + R_TRY(m_owner->GetPageTable().UnmapPages(this->GetAddress(), 1, KMemoryState::ThreadLocal)); // Free the page. KPageBuffer::Free(*m_kernel, KPageBuffer::FromPhysicalAddress(m_kernel->System(), phys_addr)); diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index f33600ca5..ebe7582c6 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1089,15 +1089,15 @@ static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process, KThread::Register(kernel, thread); return std::jthread( - [&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] { + [&kernel, thread, thread_name_{std::move(thread_name)}, func_{std::move(func)}] { // Set the thread name. - Common::SetCurrentThreadName(thread_name.c_str()); + Common::SetCurrentThreadName(thread_name_.c_str()); // Set the thread as current. kernel.RegisterHostThread(thread); // Run the callback. - func(); + func_(); // Close the thread. // This will free the process if it is the last reference. diff --git a/src/core/hle/kernel/message_buffer.h b/src/core/hle/kernel/message_buffer.h new file mode 100644 index 000000000..75b275310 --- /dev/null +++ b/src/core/hle/kernel/message_buffer.h @@ -0,0 +1,612 @@ +// SPDX-FileCopyrightText: 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/alignment.h" +#include "common/bit_field.h" +#include "core/hle/kernel/k_thread.h" + +namespace Kernel { + +constexpr inline size_t MessageBufferSize = 0x100; + +class MessageBuffer { +public: + class MessageHeader { + private: + static constexpr inline u64 NullTag = 0; + + public: + enum class ReceiveListCountType : u32 { + None = 0, + ToMessageBuffer = 1, + ToSingleBuffer = 2, + + CountOffset = 2, + CountMax = 13, + }; + + private: + union { + std::array<u32, 2> raw; + + struct { + // Define fields for the first header word. + union { + BitField<0, 16, u16> tag; + BitField<16, 4, u32> pointer_count; + BitField<20, 4, u32> send_count; + BitField<24, 4, u32> receive_count; + BitField<28, 4, u32> exchange_count; + }; + + // Define fields for the second header word. + union { + BitField<0, 10, u32> raw_count; + BitField<10, 4, ReceiveListCountType> receive_list_count; + BitField<14, 6, u32> reserved0; + BitField<20, 11, u32> receive_list_offset; + BitField<31, 1, u32> has_special_header; + }; + }; + } m_header; + + public: + constexpr MessageHeader() : m_header{} {} + + constexpr MessageHeader(u16 tag, bool special, s32 ptr, s32 send, s32 recv, s32 exch, + s32 raw, ReceiveListCountType recv_list) + : m_header{} { + m_header.raw[0] = 0; + m_header.raw[1] = 0; + + m_header.tag.Assign(tag); + m_header.pointer_count.Assign(ptr); + m_header.send_count.Assign(send); + m_header.receive_count.Assign(recv); + m_header.exchange_count.Assign(exch); + + m_header.raw_count.Assign(raw); + m_header.receive_list_count.Assign(recv_list); + m_header.has_special_header.Assign(special); + } + + explicit MessageHeader(const MessageBuffer& buf) : m_header{} { + buf.Get(0, m_header.raw.data(), 2); + } + + explicit MessageHeader(const u32* msg) : m_header{{msg[0], msg[1]}} {} + + constexpr u16 GetTag() const { + return m_header.tag; + } + + constexpr s32 GetPointerCount() const { + return m_header.pointer_count; + } + + constexpr s32 GetSendCount() const { + return m_header.send_count; + } + + constexpr s32 GetReceiveCount() const { + return m_header.receive_count; + } + + constexpr s32 GetExchangeCount() const { + return m_header.exchange_count; + } + + constexpr s32 GetMapAliasCount() const { + return this->GetSendCount() + this->GetReceiveCount() + this->GetExchangeCount(); + } + + constexpr s32 GetRawCount() const { + return m_header.raw_count; + } + + constexpr ReceiveListCountType GetReceiveListCount() const { + return m_header.receive_list_count; + } + + constexpr s32 GetReceiveListOffset() const { + return m_header.receive_list_offset; + } + + constexpr bool GetHasSpecialHeader() const { + return m_header.has_special_header.Value() != 0; + } + + constexpr void SetReceiveListCount(ReceiveListCountType recv_list) { + m_header.receive_list_count.Assign(recv_list); + } + + constexpr const u32* GetData() const { + return m_header.raw.data(); + } + + static constexpr size_t GetDataSize() { + return sizeof(m_header); + } + }; + + class SpecialHeader { + private: + union { + std::array<u32, 1> raw; + + // Define fields for the header word. + BitField<0, 1, u32> has_process_id; + BitField<1, 4, u32> copy_handle_count; + BitField<5, 4, u32> move_handle_count; + } m_header; + bool m_has_header; + + public: + constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move) + : m_header{}, m_has_header(true) { + m_header.has_process_id.Assign(pid); + m_header.copy_handle_count.Assign(copy); + m_header.move_handle_count.Assign(move); + } + + constexpr explicit SpecialHeader(bool pid, s32 copy, s32 move, bool _has_header) + : m_header{}, m_has_header(_has_header) { + m_header.has_process_id.Assign(pid); + m_header.copy_handle_count.Assign(copy); + m_header.move_handle_count.Assign(move); + } + + explicit SpecialHeader(const MessageBuffer& buf, const MessageHeader& hdr) + : m_header{}, m_has_header(hdr.GetHasSpecialHeader()) { + if (m_has_header) { + buf.Get(static_cast<s32>(MessageHeader::GetDataSize() / sizeof(u32)), + m_header.raw.data(), sizeof(m_header) / sizeof(u32)); + } + } + + constexpr bool GetHasProcessId() const { + return m_header.has_process_id.Value() != 0; + } + + constexpr s32 GetCopyHandleCount() const { + return m_header.copy_handle_count; + } + + constexpr s32 GetMoveHandleCount() const { + return m_header.move_handle_count; + } + + constexpr const u32* GetHeader() const { + return m_header.raw.data(); + } + + constexpr size_t GetHeaderSize() const { + if (m_has_header) { + return sizeof(m_header); + } else { + return 0; + } + } + + constexpr size_t GetDataSize() const { + if (m_has_header) { + return (this->GetHasProcessId() ? sizeof(u64) : 0) + + (this->GetCopyHandleCount() * sizeof(Handle)) + + (this->GetMoveHandleCount() * sizeof(Handle)); + } else { + return 0; + } + } + }; + + class MapAliasDescriptor { + public: + enum class Attribute : u32 { + Ipc = 0, + NonSecureIpc = 1, + NonDeviceIpc = 3, + }; + + private: + static constexpr u32 SizeLowCount = 32; + static constexpr u32 SizeHighCount = 4; + static constexpr u32 AddressLowCount = 32; + static constexpr u32 AddressMidCount = 4; + + constexpr u32 GetAddressMid(u64 address) { + return static_cast<u32>(address >> AddressLowCount) & ((1U << AddressMidCount) - 1); + } + + constexpr u32 GetAddressHigh(u64 address) { + return static_cast<u32>(address >> (AddressLowCount + AddressMidCount)); + } + + private: + union { + std::array<u32, 3> raw; + + struct { + // Define fields for the first two words. + u32 size_low; + u32 address_low; + + // Define fields for the packed descriptor word. + union { + BitField<0, 2, Attribute> attributes; + BitField<2, 3, u32> address_high; + BitField<5, 19, u32> reserved; + BitField<24, 4, u32> size_high; + BitField<28, 4, u32> address_mid; + }; + }; + } m_data; + + public: + constexpr MapAliasDescriptor() : m_data{} {} + + MapAliasDescriptor(const void* buffer, size_t _size, Attribute attr = Attribute::Ipc) + : m_data{} { + const u64 address = reinterpret_cast<u64>(buffer); + const u64 size = static_cast<u64>(_size); + m_data.size_low = static_cast<u32>(size); + m_data.address_low = static_cast<u32>(address); + m_data.attributes.Assign(attr); + m_data.address_mid.Assign(GetAddressMid(address)); + m_data.size_high.Assign(static_cast<u32>(size >> SizeLowCount)); + m_data.address_high.Assign(GetAddressHigh(address)); + } + + MapAliasDescriptor(const MessageBuffer& buf, s32 index) : m_data{} { + buf.Get(index, m_data.raw.data(), 3); + } + + constexpr uintptr_t GetAddress() const { + return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid) + << AddressLowCount) | + m_data.address_low; + } + + constexpr uintptr_t GetSize() const { + return (static_cast<u64>(m_data.size_high) << SizeLowCount) | m_data.size_low; + } + + constexpr Attribute GetAttribute() const { + return m_data.attributes; + } + + constexpr const u32* GetData() const { + return m_data.raw.data(); + } + + static constexpr size_t GetDataSize() { + return sizeof(m_data); + } + }; + + class PointerDescriptor { + private: + static constexpr u32 AddressLowCount = 32; + static constexpr u32 AddressMidCount = 4; + + constexpr u32 GetAddressMid(u64 address) { + return static_cast<u32>(address >> AddressLowCount) & ((1u << AddressMidCount) - 1); + } + + constexpr u32 GetAddressHigh(u64 address) { + return static_cast<u32>(address >> (AddressLowCount + AddressMidCount)); + } + + private: + union { + std::array<u32, 2> raw; + + struct { + // Define fields for the packed descriptor word. + union { + BitField<0, 4, u32> index; + BitField<4, 2, u32> reserved0; + BitField<6, 3, u32> address_high; + BitField<9, 3, u32> reserved1; + BitField<12, 4, u32> address_mid; + BitField<16, 16, u32> size; + }; + + // Define fields for the second word. + u32 address_low; + }; + } m_data; + + public: + constexpr PointerDescriptor() : m_data{} {} + + PointerDescriptor(const void* buffer, size_t size, s32 index) : m_data{} { + const u64 address = reinterpret_cast<u64>(buffer); + + m_data.index.Assign(index); + m_data.address_high.Assign(GetAddressHigh(address)); + m_data.address_mid.Assign(GetAddressMid(address)); + m_data.size.Assign(static_cast<u32>(size)); + + m_data.address_low = static_cast<u32>(address); + } + + PointerDescriptor(const MessageBuffer& buf, s32 index) : m_data{} { + buf.Get(index, m_data.raw.data(), 2); + } + + constexpr s32 GetIndex() const { + return m_data.index; + } + + constexpr uintptr_t GetAddress() const { + return (static_cast<u64>((m_data.address_high << AddressMidCount) | m_data.address_mid) + << AddressLowCount) | + m_data.address_low; + } + + constexpr size_t GetSize() const { + return m_data.size; + } + + constexpr const u32* GetData() const { + return m_data.raw.data(); + } + + static constexpr size_t GetDataSize() { + return sizeof(m_data); + } + }; + + class ReceiveListEntry { + private: + static constexpr u32 AddressLowCount = 32; + + constexpr u32 GetAddressHigh(u64 address) { + return static_cast<u32>(address >> (AddressLowCount)); + } + + private: + union { + std::array<u32, 2> raw; + + struct { + // Define fields for the first word. + u32 address_low; + + // Define fields for the packed descriptor word. + union { + BitField<0, 7, u32> address_high; + BitField<7, 9, u32> reserved; + BitField<16, 16, u32> size; + }; + }; + } m_data; + + public: + constexpr ReceiveListEntry() : m_data{} {} + + ReceiveListEntry(const void* buffer, size_t size) : m_data{} { + const u64 address = reinterpret_cast<u64>(buffer); + + m_data.address_low = static_cast<u32>(address); + + m_data.address_high.Assign(GetAddressHigh(address)); + m_data.size.Assign(static_cast<u32>(size)); + } + + ReceiveListEntry(u32 a, u32 b) : m_data{{a, b}} {} + + constexpr uintptr_t GetAddress() const { + return (static_cast<u64>(m_data.address_high) << AddressLowCount) | m_data.address_low; + } + + constexpr size_t GetSize() const { + return m_data.size; + } + + constexpr const u32* GetData() const { + return m_data.raw.data(); + } + + static constexpr size_t GetDataSize() { + return sizeof(m_data); + } + }; + +private: + u32* m_buffer; + size_t m_size; + +public: + constexpr MessageBuffer(u32* b, size_t sz) : m_buffer(b), m_size(sz) {} + constexpr explicit MessageBuffer(u32* b) : m_buffer(b), m_size(MessageBufferSize) {} + + constexpr void* GetBufferForDebug() const { + return m_buffer; + } + + constexpr size_t GetBufferSize() const { + return m_size; + } + + void Get(s32 index, u32* dst, size_t count) const { + // Ensure that this doesn't get re-ordered. + std::atomic_thread_fence(std::memory_order_seq_cst); + + // Get the words. + static_assert(sizeof(*dst) == sizeof(*m_buffer)); + + memcpy(dst, m_buffer + index, count * sizeof(*dst)); + } + + s32 Set(s32 index, u32* src, size_t count) const { + // Ensure that this doesn't get re-ordered. + std::atomic_thread_fence(std::memory_order_seq_cst); + + // Set the words. + memcpy(m_buffer + index, src, count * sizeof(*src)); + + // Ensure that this doesn't get re-ordered. + std::atomic_thread_fence(std::memory_order_seq_cst); + + return static_cast<s32>(index + count); + } + + template <typename T> + const T& GetRaw(s32 index) const { + return *reinterpret_cast<const T*>(m_buffer + index); + } + + template <typename T> + s32 SetRaw(s32 index, const T& val) const { + *reinterpret_cast<const T*>(m_buffer + index) = val; + return index + (Common::AlignUp(sizeof(val), sizeof(*m_buffer)) / sizeof(*m_buffer)); + } + + void GetRawArray(s32 index, void* dst, size_t len) const { + memcpy(dst, m_buffer + index, len); + } + + void SetRawArray(s32 index, const void* src, size_t len) const { + memcpy(m_buffer + index, src, len); + } + + void SetNull() const { + this->Set(MessageHeader()); + } + + s32 Set(const MessageHeader& hdr) const { + memcpy(m_buffer, hdr.GetData(), hdr.GetDataSize()); + return static_cast<s32>(hdr.GetDataSize() / sizeof(*m_buffer)); + } + + s32 Set(const SpecialHeader& spc) const { + const s32 index = static_cast<s32>(MessageHeader::GetDataSize() / sizeof(*m_buffer)); + memcpy(m_buffer + index, spc.GetHeader(), spc.GetHeaderSize()); + return static_cast<s32>(index + (spc.GetHeaderSize() / sizeof(*m_buffer))); + } + + s32 SetHandle(s32 index, const Handle& hnd) const { + memcpy(m_buffer + index, std::addressof(hnd), sizeof(hnd)); + return static_cast<s32>(index + (sizeof(hnd) / sizeof(*m_buffer))); + } + + s32 SetProcessId(s32 index, const u64 pid) const { + memcpy(m_buffer + index, std::addressof(pid), sizeof(pid)); + return static_cast<s32>(index + (sizeof(pid) / sizeof(*m_buffer))); + } + + s32 Set(s32 index, const MapAliasDescriptor& desc) const { + memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize()); + return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer))); + } + + s32 Set(s32 index, const PointerDescriptor& desc) const { + memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize()); + return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer))); + } + + s32 Set(s32 index, const ReceiveListEntry& desc) const { + memcpy(m_buffer + index, desc.GetData(), desc.GetDataSize()); + return static_cast<s32>(index + (desc.GetDataSize() / sizeof(*m_buffer))); + } + + s32 Set(s32 index, const u32 val) const { + memcpy(m_buffer + index, std::addressof(val), sizeof(val)); + return static_cast<s32>(index + (sizeof(val) / sizeof(*m_buffer))); + } + + Result GetAsyncResult() const { + MessageHeader hdr(m_buffer); + MessageHeader null{}; + if (memcmp(hdr.GetData(), null.GetData(), MessageHeader::GetDataSize()) != 0) [[unlikely]] { + R_SUCCEED(); + } + return Result(m_buffer[MessageHeader::GetDataSize() / sizeof(*m_buffer)]); + } + + void SetAsyncResult(Result res) const { + const s32 index = this->Set(MessageHeader()); + const auto value = res.raw; + memcpy(m_buffer + index, std::addressof(value), sizeof(value)); + } + + u32 Get32(s32 index) const { + return m_buffer[index]; + } + + u64 Get64(s32 index) const { + u64 value; + memcpy(std::addressof(value), m_buffer + index, sizeof(value)); + return value; + } + + u64 GetProcessId(s32 index) const { + return this->Get64(index); + } + + Handle GetHandle(s32 index) const { + static_assert(sizeof(Handle) == sizeof(*m_buffer)); + return Handle(m_buffer[index]); + } + + static constexpr s32 GetSpecialDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) { + return static_cast<s32>((MessageHeader::GetDataSize() / sizeof(u32)) + + (spc.GetHeaderSize() / sizeof(u32))); + } + + static constexpr s32 GetPointerDescriptorIndex(const MessageHeader& hdr, + const SpecialHeader& spc) { + return static_cast<s32>(GetSpecialDataIndex(hdr, spc) + (spc.GetDataSize() / sizeof(u32))); + } + + static constexpr s32 GetMapAliasDescriptorIndex(const MessageHeader& hdr, + const SpecialHeader& spc) { + return GetPointerDescriptorIndex(hdr, spc) + + static_cast<s32>(hdr.GetPointerCount() * PointerDescriptor::GetDataSize() / + sizeof(u32)); + } + + static constexpr s32 GetRawDataIndex(const MessageHeader& hdr, const SpecialHeader& spc) { + return GetMapAliasDescriptorIndex(hdr, spc) + + static_cast<s32>(hdr.GetMapAliasCount() * MapAliasDescriptor::GetDataSize() / + sizeof(u32)); + } + + static constexpr s32 GetReceiveListIndex(const MessageHeader& hdr, const SpecialHeader& spc) { + if (const s32 recv_list_index = hdr.GetReceiveListOffset()) { + return recv_list_index; + } else { + return GetRawDataIndex(hdr, spc) + hdr.GetRawCount(); + } + } + + static constexpr size_t GetMessageBufferSize(const MessageHeader& hdr, + const SpecialHeader& spc) { + // Get the size of the plain message. + size_t msg_size = GetReceiveListIndex(hdr, spc) * sizeof(u32); + + // Add the size of the receive list. + const auto count = hdr.GetReceiveListCount(); + switch (count) { + case MessageHeader::ReceiveListCountType::None: + break; + case MessageHeader::ReceiveListCountType::ToMessageBuffer: + break; + case MessageHeader::ReceiveListCountType::ToSingleBuffer: + msg_size += ReceiveListEntry::GetDataSize(); + break; + default: + msg_size += (static_cast<s32>(count) - + static_cast<s32>(MessageHeader::ReceiveListCountType::CountOffset)) * + ReceiveListEntry::GetDataSize(); + break; + } + + return msg_size; + } +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp index 2e0c36129..5ee869fa2 100644 --- a/src/core/hle/kernel/physical_core.cpp +++ b/src/core/hle/kernel/physical_core.cpp @@ -17,7 +17,9 @@ PhysicalCore::PhysicalCore(std::size_t core_index, Core::System& system, KSchedu // a 32-bit instance of Dynarmic. This should be abstracted out to a CPU manager. auto& kernel = system.Kernel(); m_arm_interface = std::make_unique<Core::ARM_Dynarmic_64>( - system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), m_core_index); + system, kernel.IsMulticore(), + reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()), + m_core_index); #else #error Platform not supported yet. #endif @@ -31,7 +33,9 @@ void PhysicalCore::Initialize(bool is_64_bit) { if (!is_64_bit) { // We already initialized a 64-bit core, replace with a 32-bit one. m_arm_interface = std::make_unique<Core::ARM_Dynarmic_32>( - m_system, kernel.IsMulticore(), kernel.GetExclusiveMonitor(), m_core_index); + m_system, kernel.IsMulticore(), + reinterpret_cast<Core::DynarmicExclusiveMonitor&>(kernel.GetExclusiveMonitor()), + m_core_index); } #else #error Platform not supported yet. diff --git a/src/core/hle/kernel/svc/svc_cache.cpp b/src/core/hle/kernel/svc/svc_cache.cpp index 082942dab..c2c8be10f 100644 --- a/src/core/hle/kernel/svc/svc_cache.cpp +++ b/src/core/hle/kernel/svc/svc_cache.cpp @@ -42,7 +42,7 @@ Result FlushProcessDataCache(Core::System& system, Handle process_handle, u64 ad R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Verify the region is within range. - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); // Perform the operation. diff --git a/src/core/hle/kernel/svc/svc_code_memory.cpp b/src/core/hle/kernel/svc/svc_code_memory.cpp index 687baff82..bae4cb0cd 100644 --- a/src/core/hle/kernel/svc/svc_code_memory.cpp +++ b/src/core/hle/kernel/svc/svc_code_memory.cpp @@ -48,7 +48,7 @@ Result CreateCodeMemory(Core::System& system, Handle* out, u64 address, uint64_t SCOPE_EXIT({ code_mem->Close(); }); // Verify that the region is in range. - R_UNLESS(GetCurrentProcess(system.Kernel()).PageTable().Contains(address, size), + R_UNLESS(GetCurrentProcess(system.Kernel()).GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); // Initialize the code memory. @@ -92,7 +92,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, case CodeMemoryOperation::Map: { // Check that the region is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) - .PageTable() + .GetPageTable() .CanContain(address, size, KMemoryState::CodeOut), ResultInvalidMemoryRegion); @@ -105,7 +105,7 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, case CodeMemoryOperation::Unmap: { // Check that the region is in range. R_UNLESS(GetCurrentProcess(system.Kernel()) - .PageTable() + .GetPageTable() .CanContain(address, size, KMemoryState::CodeOut), ResultInvalidMemoryRegion); @@ -117,8 +117,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, } break; case CodeMemoryOperation::MapToOwner: { // Check that the region is in range. - R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, - KMemoryState::GeneratedCode), + R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, + KMemoryState::GeneratedCode), ResultInvalidMemoryRegion); // Check the memory permission. @@ -129,8 +129,8 @@ Result ControlCodeMemory(Core::System& system, Handle code_memory_handle, } break; case CodeMemoryOperation::UnmapFromOwner: { // Check that the region is in range. - R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size, - KMemoryState::GeneratedCode), + R_UNLESS(code_mem->GetOwner()->GetPageTable().CanContain(address, size, + KMemoryState::GeneratedCode), ResultInvalidMemoryRegion); // Check the memory permission. diff --git a/src/core/hle/kernel/svc/svc_device_address_space.cpp b/src/core/hle/kernel/svc/svc_device_address_space.cpp index ec3143e67..42add9473 100644 --- a/src/core/hle/kernel/svc/svc_device_address_space.cpp +++ b/src/core/hle/kernel/svc/svc_device_address_space.cpp @@ -107,7 +107,7 @@ Result MapDeviceAddressSpaceByForce(Core::System& system, Handle das_handle, Han R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); // Map. @@ -148,7 +148,7 @@ Result MapDeviceAddressSpaceAligned(Core::System& system, Handle das_handle, Han R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); // Map. @@ -180,7 +180,7 @@ Result UnmapDeviceAddressSpace(Core::System& system, Handle das_handle, Handle p R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the process address is within range. - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); R_UNLESS(page_table.Contains(process_address, size), ResultInvalidCurrentMemory); R_RETURN(das->Unmap(std::addressof(page_table), process_address, size, device_address)); diff --git a/src/core/hle/kernel/svc/svc_info.cpp b/src/core/hle/kernel/svc/svc_info.cpp index 445cdd87b..f99964028 100644 --- a/src/core/hle/kernel/svc/svc_info.cpp +++ b/src/core/hle/kernel/svc/svc_info.cpp @@ -54,35 +54,35 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle R_SUCCEED(); case InfoType::AliasRegionAddress: - *result = GetInteger(process->PageTable().GetAliasRegionStart()); + *result = GetInteger(process->GetPageTable().GetAliasRegionStart()); R_SUCCEED(); case InfoType::AliasRegionSize: - *result = process->PageTable().GetAliasRegionSize(); + *result = process->GetPageTable().GetAliasRegionSize(); R_SUCCEED(); case InfoType::HeapRegionAddress: - *result = GetInteger(process->PageTable().GetHeapRegionStart()); + *result = GetInteger(process->GetPageTable().GetHeapRegionStart()); R_SUCCEED(); case InfoType::HeapRegionSize: - *result = process->PageTable().GetHeapRegionSize(); + *result = process->GetPageTable().GetHeapRegionSize(); R_SUCCEED(); case InfoType::AslrRegionAddress: - *result = GetInteger(process->PageTable().GetAliasCodeRegionStart()); + *result = GetInteger(process->GetPageTable().GetAliasCodeRegionStart()); R_SUCCEED(); case InfoType::AslrRegionSize: - *result = process->PageTable().GetAliasCodeRegionSize(); + *result = process->GetPageTable().GetAliasCodeRegionSize(); R_SUCCEED(); case InfoType::StackRegionAddress: - *result = GetInteger(process->PageTable().GetStackRegionStart()); + *result = GetInteger(process->GetPageTable().GetStackRegionStart()); R_SUCCEED(); case InfoType::StackRegionSize: - *result = process->PageTable().GetStackRegionSize(); + *result = process->GetPageTable().GetStackRegionSize(); R_SUCCEED(); case InfoType::TotalMemorySize: diff --git a/src/core/hle/kernel/svc/svc_memory.cpp b/src/core/hle/kernel/svc/svc_memory.cpp index 5dcb7f045..2cab74127 100644 --- a/src/core/hle/kernel/svc/svc_memory.cpp +++ b/src/core/hle/kernel/svc/svc_memory.cpp @@ -63,36 +63,13 @@ Result MapUnmapMemorySanityChecks(const KPageTable& manager, u64 dst_addr, u64 s R_THROW(ResultInvalidCurrentMemory); } - if (!manager.IsInsideAddressSpace(src_addr, size)) { + if (!manager.Contains(src_addr, size)) { LOG_ERROR(Kernel_SVC, "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", src_addr, size); R_THROW(ResultInvalidCurrentMemory); } - if (manager.IsOutsideStackRegion(dst_addr, size)) { - LOG_ERROR(Kernel_SVC, - "Destination is not within the stack region, addr=0x{:016X}, size=0x{:016X}", - dst_addr, size); - R_THROW(ResultInvalidMemoryRegion); - } - - if (manager.IsInsideHeapRegion(dst_addr, size)) { - LOG_ERROR(Kernel_SVC, - "Destination does not fit within the heap region, addr=0x{:016X}, " - "size=0x{:016X}", - dst_addr, size); - R_THROW(ResultInvalidMemoryRegion); - } - - if (manager.IsInsideAliasRegion(dst_addr, size)) { - LOG_ERROR(Kernel_SVC, - "Destination does not fit within the map region, addr=0x{:016X}, " - "size=0x{:016X}", - dst_addr, size); - R_THROW(ResultInvalidMemoryRegion); - } - R_SUCCEED(); } @@ -112,7 +89,7 @@ Result SetMemoryPermission(Core::System& system, u64 address, u64 size, MemoryPe R_UNLESS(IsValidSetMemoryPermission(perm), ResultInvalidNewMemoryPermission); // Validate that the region is in range for the current process. - auto& page_table = GetCurrentProcess(system.Kernel()).PageTable(); + auto& page_table = GetCurrentProcess(system.Kernel()).GetPageTable(); R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); // Set the memory attribute. @@ -136,7 +113,7 @@ Result SetMemoryAttribute(Core::System& system, u64 address, u64 size, u32 mask, R_UNLESS((mask | attr | SupportedMask) == SupportedMask, ResultInvalidCombination); // Validate that the region is in range for the current process. - auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; + auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()}; R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); // Set the memory attribute. @@ -148,7 +125,7 @@ Result MapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; + auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()}; if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { @@ -163,7 +140,7 @@ Result UnmapMemory(Core::System& system, u64 dst_addr, u64 src_addr, u64 size) { LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, src_addr, size); - auto& page_table{GetCurrentProcess(system.Kernel()).PageTable()}; + auto& page_table{GetCurrentProcess(system.Kernel()).GetPageTable()}; if (const Result result{MapUnmapMemorySanityChecks(page_table, dst_addr, src_addr, size)}; result.IsError()) { diff --git a/src/core/hle/kernel/svc/svc_physical_memory.cpp b/src/core/hle/kernel/svc/svc_physical_memory.cpp index c2fbfb59a..d3545f232 100644 --- a/src/core/hle/kernel/svc/svc_physical_memory.cpp +++ b/src/core/hle/kernel/svc/svc_physical_memory.cpp @@ -16,7 +16,7 @@ Result SetHeapSize(Core::System& system, u64* out_address, u64 size) { R_UNLESS(size < MainMemorySizeMax, ResultInvalidSize); // Set the heap size. - R_RETURN(GetCurrentProcess(system.Kernel()).PageTable().SetHeapSize(out_address, size)); + R_RETURN(GetCurrentProcess(system.Kernel()).GetPageTable().SetHeapSize(out_address, size)); } /// Maps memory at a desired address @@ -44,21 +44,21 @@ Result MapPhysicalMemory(Core::System& system, u64 addr, u64 size) { } KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; - auto& page_table{current_process->PageTable()}; + auto& page_table{current_process->GetPageTable()}; if (current_process->GetSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } - if (!page_table.IsInsideAddressSpace(addr, size)) { + if (!page_table.Contains(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, size); R_THROW(ResultInvalidMemoryRegion); } - if (page_table.IsOutsideAliasRegion(addr, size)) { + if (!page_table.IsInAliasRegion(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, size); @@ -93,21 +93,21 @@ Result UnmapPhysicalMemory(Core::System& system, u64 addr, u64 size) { } KProcess* const current_process{GetCurrentProcessPointer(system.Kernel())}; - auto& page_table{current_process->PageTable()}; + auto& page_table{current_process->GetPageTable()}; if (current_process->GetSystemResourceSize() == 0) { LOG_ERROR(Kernel_SVC, "System Resource Size is zero"); R_THROW(ResultInvalidState); } - if (!page_table.IsInsideAddressSpace(addr, size)) { + if (!page_table.Contains(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr, size); R_THROW(ResultInvalidMemoryRegion); } - if (page_table.IsOutsideAliasRegion(addr, size)) { + if (!page_table.IsInAliasRegion(addr, size)) { LOG_ERROR(Kernel_SVC, "Address is not within the alias region, addr=0x{:016X}, size=0x{:016X}", addr, size); diff --git a/src/core/hle/kernel/svc/svc_process.cpp b/src/core/hle/kernel/svc/svc_process.cpp index 619ed16a3..caa8bee9a 100644 --- a/src/core/hle/kernel/svc/svc_process.cpp +++ b/src/core/hle/kernel/svc/svc_process.cpp @@ -66,8 +66,8 @@ Result GetProcessList(Core::System& system, s32* out_num_processes, u64 out_proc auto& kernel = system.Kernel(); const auto total_copy_size = out_process_ids_size * sizeof(u64); - if (out_process_ids_size > 0 && !GetCurrentProcess(kernel).PageTable().IsInsideAddressSpace( - out_process_ids, total_copy_size)) { + if (out_process_ids_size > 0 && + !GetCurrentProcess(kernel).GetPageTable().Contains(out_process_ids, total_copy_size)) { LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", out_process_ids, out_process_ids + total_copy_size); R_THROW(ResultInvalidCurrentMemory); diff --git a/src/core/hle/kernel/svc/svc_process_memory.cpp b/src/core/hle/kernel/svc/svc_process_memory.cpp index aee0f2f36..07cd48175 100644 --- a/src/core/hle/kernel/svc/svc_process_memory.cpp +++ b/src/core/hle/kernel/svc/svc_process_memory.cpp @@ -49,7 +49,7 @@ Result SetProcessMemoryPermission(Core::System& system, Handle process_handle, u R_UNLESS(process.IsNotNull(), ResultInvalidHandle); // Validate that the address is in range. - auto& page_table = process->PageTable(); + auto& page_table = process->GetPageTable(); R_UNLESS(page_table.Contains(address, size), ResultInvalidCurrentMemory); // Set the memory permission. @@ -77,8 +77,8 @@ Result MapProcessMemory(Core::System& system, u64 dst_address, Handle process_ha R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); // Get the page tables. - auto& dst_pt = dst_process->PageTable(); - auto& src_pt = src_process->PageTable(); + auto& dst_pt = dst_process->GetPageTable(); + auto& src_pt = src_process->GetPageTable(); // Validate that the mapping is in range. R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); @@ -118,8 +118,8 @@ Result UnmapProcessMemory(Core::System& system, u64 dst_address, Handle process_ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle); // Get the page tables. - auto& dst_pt = dst_process->PageTable(); - auto& src_pt = src_process->PageTable(); + auto& dst_pt = dst_process->GetPageTable(); + auto& src_pt = src_process->GetPageTable(); // Validate that the mapping is in range. R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory); @@ -178,8 +178,8 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst R_THROW(ResultInvalidHandle); } - auto& page_table = process->PageTable(); - if (!page_table.IsInsideAddressSpace(src_address, size)) { + auto& page_table = process->GetPageTable(); + if (!page_table.Contains(src_address, size)) { LOG_ERROR(Kernel_SVC, "Source address range is not within the address space (src_address=0x{:016X}, " "size=0x{:016X}).", @@ -187,14 +187,6 @@ Result MapProcessCodeMemory(Core::System& system, Handle process_handle, u64 dst R_THROW(ResultInvalidCurrentMemory); } - if (!page_table.IsInsideASLRRegion(dst_address, size)) { - LOG_ERROR(Kernel_SVC, - "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " - "size=0x{:016X}).", - dst_address, size); - R_THROW(ResultInvalidMemoryRegion); - } - R_RETURN(page_table.MapCodeMemory(dst_address, src_address, size)); } @@ -246,8 +238,8 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d R_THROW(ResultInvalidHandle); } - auto& page_table = process->PageTable(); - if (!page_table.IsInsideAddressSpace(src_address, size)) { + auto& page_table = process->GetPageTable(); + if (!page_table.Contains(src_address, size)) { LOG_ERROR(Kernel_SVC, "Source address range is not within the address space (src_address=0x{:016X}, " "size=0x{:016X}).", @@ -255,14 +247,6 @@ Result UnmapProcessCodeMemory(Core::System& system, Handle process_handle, u64 d R_THROW(ResultInvalidCurrentMemory); } - if (!page_table.IsInsideASLRRegion(dst_address, size)) { - LOG_ERROR(Kernel_SVC, - "Destination address range is not within the ASLR region (dst_address=0x{:016X}, " - "size=0x{:016X}).", - dst_address, size); - R_THROW(ResultInvalidMemoryRegion); - } - R_RETURN(page_table.UnmapCodeMemory(dst_address, src_address, size, KPageTable::ICacheInvalidationStrategy::InvalidateAll)); } diff --git a/src/core/hle/kernel/svc/svc_query_memory.cpp b/src/core/hle/kernel/svc/svc_query_memory.cpp index 4d9fcd25f..51af06e97 100644 --- a/src/core/hle/kernel/svc/svc_query_memory.cpp +++ b/src/core/hle/kernel/svc/svc_query_memory.cpp @@ -31,7 +31,7 @@ Result QueryProcessMemory(Core::System& system, uint64_t out_memory_info, PageIn } auto& current_memory{GetCurrentMemory(system.Kernel())}; - const auto memory_info{process->PageTable().QueryInfo(address).GetSvcMemoryInfo()}; + const auto memory_info{process->GetPageTable().QueryInfo(address).GetSvcMemoryInfo()}; current_memory.WriteBlock(out_memory_info, std::addressof(memory_info), sizeof(memory_info)); diff --git a/src/core/hle/kernel/svc/svc_shared_memory.cpp b/src/core/hle/kernel/svc/svc_shared_memory.cpp index a698596aa..012b1ae2b 100644 --- a/src/core/hle/kernel/svc/svc_shared_memory.cpp +++ b/src/core/hle/kernel/svc/svc_shared_memory.cpp @@ -43,7 +43,7 @@ Result MapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, u // Get the current process. auto& process = GetCurrentProcess(system.Kernel()); - auto& page_table = process.PageTable(); + auto& page_table = process.GetPageTable(); // Get the shared memory. KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); @@ -73,7 +73,7 @@ Result UnmapSharedMemory(Core::System& system, Handle shmem_handle, u64 address, // Get the current process. auto& process = GetCurrentProcess(system.Kernel()); - auto& page_table = process.PageTable(); + auto& page_table = process.GetPageTable(); // Get the shared memory. KScopedAutoObject shmem = process.GetHandleTable().GetObject<KSharedMemory>(shmem_handle); diff --git a/src/core/hle/kernel/svc/svc_thread.cpp b/src/core/hle/kernel/svc/svc_thread.cpp index 36b94e6bf..92bcea72b 100644 --- a/src/core/hle/kernel/svc/svc_thread.cpp +++ b/src/core/hle/kernel/svc/svc_thread.cpp @@ -236,7 +236,7 @@ Result GetThreadList(Core::System& system, s32* out_num_threads, u64 out_thread_ const auto total_copy_size = out_thread_ids_size * sizeof(u64); if (out_thread_ids_size > 0 && - !current_process->PageTable().IsInsideAddressSpace(out_thread_ids, total_copy_size)) { + !current_process->GetPageTable().Contains(out_thread_ids, total_copy_size)) { LOG_ERROR(Kernel_SVC, "Address range outside address space. begin=0x{:016X}, end=0x{:016X}", out_thread_ids, out_thread_ids + total_copy_size); R_THROW(ResultInvalidCurrentMemory); diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 82d469a37..7d94e7f09 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp @@ -55,7 +55,7 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 SCOPE_EXIT({ trmem->Close(); }); // Ensure that the region is in range. - R_UNLESS(process.PageTable().Contains(address, size), ResultInvalidCurrentMemory); + R_UNLESS(process.GetPageTable().Contains(address, size), ResultInvalidCurrentMemory); // Initialize the transfer memory. R_TRY(trmem->Initialize(address, size, map_perm)); diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a2375508a..4f400d341 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -506,8 +506,8 @@ void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) { void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; idle_time_detection_extension = rp.Pop<u32>(); - LOG_WARNING(Service_AM, "(STUBBED) called idle_time_detection_extension={}", - idle_time_detection_extension); + LOG_DEBUG(Service_AM, "(STUBBED) called idle_time_detection_extension={}", + idle_time_detection_extension); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/hle_ipc.cpp b/src/core/hle/service/hle_ipc.cpp index 2290df705..f6a1e54f2 100644 --- a/src/core/hle/service/hle_ipc.cpp +++ b/src/core/hle/service/hle_ipc.cpp @@ -329,8 +329,22 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons } std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const { - static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_a; - static thread_local std::array<Common::ScratchBuffer<u8>, 2> read_buffer_x; + static thread_local std::array read_buffer_a{ + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + }; + static thread_local std::array read_buffer_data_a{ + Common::ScratchBuffer<u8>(), + Common::ScratchBuffer<u8>(), + }; + static thread_local std::array read_buffer_x{ + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0), + }; + static thread_local std::array read_buffer_data_x{ + Common::ScratchBuffer<u8>(), + Common::ScratchBuffer<u8>(), + }; const bool is_buffer_a{BufferDescriptorA().size() > buffer_index && BufferDescriptorA()[buffer_index].Size()}; @@ -339,19 +353,17 @@ std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) cons BufferDescriptorA().size() > buffer_index, { return {}; }, "BufferDescriptorA invalid buffer_index {}", buffer_index); auto& read_buffer = read_buffer_a[buffer_index]; - read_buffer.resize_destructive(BufferDescriptorA()[buffer_index].Size()); - memory.ReadBlock(BufferDescriptorA()[buffer_index].Address(), read_buffer.data(), - read_buffer.size()); - return read_buffer; + return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(), + BufferDescriptorA()[buffer_index].Size(), + &read_buffer_data_a[buffer_index]); } else { ASSERT_OR_EXECUTE_MSG( BufferDescriptorX().size() > buffer_index, { return {}; }, "BufferDescriptorX invalid buffer_index {}", buffer_index); auto& read_buffer = read_buffer_x[buffer_index]; - read_buffer.resize_destructive(BufferDescriptorX()[buffer_index].Size()); - memory.ReadBlock(BufferDescriptorX()[buffer_index].Address(), read_buffer.data(), - read_buffer.size()); - return read_buffer; + return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(), + BufferDescriptorX()[buffer_index].Size(), + &read_buffer_data_x[buffer_index]); } } diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index c42489ff9..055c0a2db 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp @@ -318,15 +318,15 @@ public: return false; } - if (!page_table.IsInsideAddressSpace(out_addr, size)) { + if (!page_table.Contains(out_addr, size)) { return false; } - if (page_table.IsInsideHeapRegion(out_addr, size)) { + if (page_table.IsInHeapRegion(out_addr, size)) { return false; } - if (page_table.IsInsideAliasRegion(out_addr, size)) { + if (page_table.IsInAliasRegion(out_addr, size)) { return false; } @@ -358,7 +358,7 @@ public: } ResultVal<VAddr> MapProcessCodeMemory(Kernel::KProcess* process, VAddr base_addr, u64 size) { - auto& page_table{process->PageTable()}; + auto& page_table{process->GetPageTable()}; VAddr addr{}; for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { @@ -382,7 +382,7 @@ public: ResultVal<VAddr> MapNro(Kernel::KProcess* process, VAddr nro_addr, std::size_t nro_size, VAddr bss_addr, std::size_t bss_size, std::size_t size) { for (std::size_t retry = 0; retry < MAXIMUM_MAP_RETRIES; retry++) { - auto& page_table{process->PageTable()}; + auto& page_table{process->GetPageTable()}; VAddr addr{}; CASCADE_RESULT(addr, MapProcessCodeMemory(process, nro_addr, nro_size)); @@ -437,12 +437,12 @@ public: CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, nro_header.segment_headers[DATA_INDEX].memory_size); - CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( + CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission( text_start, ro_start - text_start, Kernel::Svc::MemoryPermission::ReadExecute)); - CASCADE_CODE(process->PageTable().SetProcessMemoryPermission( + CASCADE_CODE(process->GetPageTable().SetProcessMemoryPermission( ro_start, data_start - ro_start, Kernel::Svc::MemoryPermission::Read)); - return process->PageTable().SetProcessMemoryPermission( + return process->GetPageTable().SetProcessMemoryPermission( data_start, bss_end_addr - data_start, Kernel::Svc::MemoryPermission::ReadWrite); } @@ -571,7 +571,7 @@ public: Result UnmapNro(const NROInfo& info) { // Each region must be unmapped separately to validate memory state - auto& page_table{system.ApplicationProcess()->PageTable()}; + auto& page_table{system.ApplicationProcess()->GetPageTable()}; if (info.bss_size != 0) { CASCADE_CODE(page_table.UnmapCodeMemory( @@ -643,7 +643,7 @@ public: initialized = true; current_map_addr = - GetInteger(system.ApplicationProcess()->PageTable().GetAliasCodeRegionStart()); + GetInteger(system.ApplicationProcess()->GetPageTable().GetAliasCodeRegionStart()); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.cpp b/src/core/hle/service/nfc/common/amiibo_crypto.cpp index bc232c334..9556e9193 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.cpp +++ b/src/core/hle/service/nfc/common/amiibo_crypto.cpp @@ -180,7 +180,7 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed } void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, - const std::vector<u8>& seed) { + std::span<const u8> seed) { // Initialize context ctx.used = false; ctx.counter = 0; diff --git a/src/core/hle/service/nfc/common/amiibo_crypto.h b/src/core/hle/service/nfc/common/amiibo_crypto.h index 6a3e0841e..2cc0e4d51 100644 --- a/src/core/hle/service/nfc/common/amiibo_crypto.h +++ b/src/core/hle/service/nfc/common/amiibo_crypto.h @@ -75,7 +75,7 @@ std::vector<u8> GenerateInternalKey(const InternalKey& key, const HashSeed& seed // Initializes mbedtls context void CryptoInit(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, const HmacKey& hmac_key, - const std::vector<u8>& seed); + std::span<const u8> seed); // Feeds data to mbedtls context to generate the derived key void CryptoStep(CryptoCtx& ctx, mbedtls_md_context_t& hmac_ctx, DrgbOutput& output); diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp index 2d633b03f..49446bc42 100644 --- a/src/core/hle/service/nfc/common/device.cpp +++ b/src/core/hle/service/nfc/common/device.cpp @@ -34,8 +34,6 @@ #include "core/hle/service/nfc/mifare_result.h" #include "core/hle/service/nfc/nfc_result.h" #include "core/hle/service/time/time_manager.h" -#include "core/hle/service/time/time_zone_content_manager.h" -#include "core/hle/service/time/time_zone_types.h" namespace Service::NFC { NfcDevice::NfcDevice(Core::HID::NpadIdType npad_id_, Core::System& system_, @@ -1486,6 +1484,7 @@ DeviceState NfcDevice::GetCurrentState() const { } Result NfcDevice::GetNpadId(Core::HID::NpadIdType& out_npad_id) const { + // TODO: This should get the npad id from nn::hid::system::GetXcdHandleForNpadWithNfc out_npad_id = npad_id; return ResultSuccess; } diff --git a/src/core/hle/service/nfc/common/device_manager.cpp b/src/core/hle/service/nfc/common/device_manager.cpp index 562f3a28e..a71d26157 100644 --- a/src/core/hle/service/nfc/common/device_manager.cpp +++ b/src/core/hle/service/nfc/common/device_manager.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include <algorithm> + #include "common/logging/log.h" #include "core/core.h" #include "core/hid/hid_types.h" @@ -10,6 +12,7 @@ #include "core/hle/service/nfc/common/device_manager.h" #include "core/hle/service/nfc/nfc_result.h" #include "core/hle/service/time/clock_types.h" +#include "core/hle/service/time/time_manager.h" namespace Service::NFC { @@ -51,22 +54,53 @@ Result DeviceManager::Finalize() { return ResultSuccess; } -Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, - std::size_t max_allowed_devices) const { +Result DeviceManager::ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices, + bool skip_fatal_errors) const { + std::scoped_lock lock{mutex}; + if (max_allowed_devices < 1) { + return ResultInvalidArgument; + } + + Result result = IsNfcParameterSet(); + if (result.IsError()) { + return result; + } + + result = IsNfcEnabled(); + if (result.IsError()) { + return result; + } + + result = IsNfcInitialized(); + if (result.IsError()) { + return result; + } + for (auto& device : devices) { if (nfp_devices.size() >= max_allowed_devices) { continue; } - if (device->GetCurrentState() != DeviceState::Unavailable) { - nfp_devices.push_back(device->GetHandle()); + if (skip_fatal_errors) { + constexpr u64 MinimumRecoveryTime = 60; + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + const u64 elapsed_time = standard_steady_clock.GetCurrentTimePoint(system).time_point - + time_since_last_error; + + if (time_since_last_error != 0 && elapsed_time < MinimumRecoveryTime) { + continue; + } } + if (device->GetCurrentState() == DeviceState::Unavailable) { + continue; + } + nfp_devices.push_back(device->GetHandle()); } if (nfp_devices.empty()) { return ResultDeviceNotFound; } - return ResultSuccess; + return result; } DeviceState DeviceManager::GetDeviceState(u64 device_handle) const { @@ -79,10 +113,10 @@ DeviceState DeviceManager::GetDeviceState(u64 device_handle) const { return device->GetCurrentState(); } - return DeviceState::Unavailable; + return DeviceState::Finalized; } -Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const { +Result DeviceManager::GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -128,7 +162,7 @@ Result DeviceManager::StopDetection(u64 device_handle) { return result; } -Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const { +Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -142,24 +176,46 @@ Result DeviceManager::GetTagInfo(u64 device_handle, TagInfo& tag_info) const { return result; } -Kernel::KReadableEvent& DeviceManager::AttachActivateEvent(u64 device_handle) const { - std::scoped_lock lock{mutex}; - +Result DeviceManager::AttachActivateEvent(Kernel::KReadableEvent** out_event, + u64 device_handle) const { + std::vector<u64> nfp_devices; std::shared_ptr<NfcDevice> device = nullptr; - GetDeviceFromHandle(device_handle, device, false); + Result result = ListDevices(nfp_devices, 9, false); - // TODO: Return proper error code on failure - return device->GetActivateEvent(); -} + if (result.IsSuccess()) { + result = CheckHandleOnList(device_handle, nfp_devices); + } -Kernel::KReadableEvent& DeviceManager::AttachDeactivateEvent(u64 device_handle) const { - std::scoped_lock lock{mutex}; + if (result.IsSuccess()) { + result = GetDeviceFromHandle(device_handle, device, false); + } + + if (result.IsSuccess()) { + *out_event = &device->GetActivateEvent(); + } + + return result; +} +Result DeviceManager::AttachDeactivateEvent(Kernel::KReadableEvent** out_event, + u64 device_handle) const { + std::vector<u64> nfp_devices; std::shared_ptr<NfcDevice> device = nullptr; - GetDeviceFromHandle(device_handle, device, false); + Result result = ListDevices(nfp_devices, 9, false); - // TODO: Return proper error code on failure - return device->GetDeactivateEvent(); + if (result.IsSuccess()) { + result = CheckHandleOnList(device_handle, nfp_devices); + } + + if (result.IsSuccess()) { + result = GetDeviceFromHandle(device_handle, device, false); + } + + if (result.IsSuccess()) { + *out_event = &device->GetDeactivateEvent(); + } + + return result; } Result DeviceManager::ReadMifare(u64 device_handle, @@ -253,7 +309,7 @@ Result DeviceManager::OpenApplicationArea(u64 device_handle, u32 access_id) { return result; } -Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) const { +Result DeviceManager::GetApplicationArea(u64 device_handle, std::span<u8> data) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -324,7 +380,7 @@ Result DeviceManager::CreateApplicationArea(u64 device_handle, u32 access_id, return result; } -Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const { +Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -338,7 +394,7 @@ Result DeviceManager::GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& regi return result; } -Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const { +Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -352,7 +408,7 @@ Result DeviceManager::GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_i return result; } -Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const { +Result DeviceManager::GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -399,7 +455,7 @@ Result DeviceManager::Format(u64 device_handle) { return result; } -Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const { +Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -414,7 +470,7 @@ Result DeviceManager::GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info } Result DeviceManager::GetRegisterInfoPrivate(u64 device_handle, - NFP::RegisterInfoPrivate& register_info) const { + NFP::RegisterInfoPrivate& register_info) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -471,7 +527,7 @@ Result DeviceManager::DeleteApplicationArea(u64 device_handle) { return result; } -Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) const { +Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_application_area) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -485,7 +541,7 @@ Result DeviceManager::ExistsApplicationArea(u64 device_handle, bool& has_applica return result; } -Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) const { +Result DeviceManager::GetAll(u64 device_handle, NFP::NfpData& nfp_data) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -541,7 +597,7 @@ Result DeviceManager::BreakTag(u64 device_handle, NFP::BreakType break_type) { return result; } -Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) const { +Result DeviceManager::ReadBackupData(u64 device_handle, std::span<u8> data) { std::scoped_lock lock{mutex}; std::shared_ptr<NfcDevice> device = nullptr; @@ -593,6 +649,19 @@ Result DeviceManager::WriteNtf(u64 device_handle, NFP::WriteType, std::span<cons return result; } +Result DeviceManager::CheckHandleOnList(u64 device_handle, + const std::span<const u64> device_list) const { + if (device_list.size() < 1) { + return ResultDeviceNotFound; + } + + if (std::find(device_list.begin(), device_list.end(), device_handle) != device_list.end()) { + return ResultSuccess; + } + + return ResultDeviceNotFound; +} + Result DeviceManager::GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& nfc_device, bool check_state) const { if (check_state) { @@ -647,7 +716,7 @@ Result DeviceManager::GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& de } Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, - Result operation_result) const { + Result operation_result) { if (operation_result.IsSuccess()) { return operation_result; } @@ -669,6 +738,12 @@ Result DeviceManager::VerifyDeviceResult(std::shared_ptr<NfcDevice> device, return device_state; } + if (operation_result == ResultUnknown112 || operation_result == ResultUnknown114 || + operation_result == ResultUnknown115) { + auto& standard_steady_clock{system.GetTimeManager().GetStandardSteadyClockCore()}; + time_since_last_error = standard_steady_clock.GetCurrentTimePoint(system).time_point; + } + return operation_result; } diff --git a/src/core/hle/service/nfc/common/device_manager.h b/src/core/hle/service/nfc/common/device_manager.h index c61ba0cf3..c9f038e32 100644 --- a/src/core/hle/service/nfc/common/device_manager.h +++ b/src/core/hle/service/nfc/common/device_manager.h @@ -27,15 +27,16 @@ public: // Nfc device manager Result Initialize(); Result Finalize(); - Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices) const; + Result ListDevices(std::vector<u64>& nfp_devices, std::size_t max_allowed_devices, + bool skip_fatal_errors) const; DeviceState GetDeviceState(u64 device_handle) const; - Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id) const; + Result GetNpadId(u64 device_handle, Core::HID::NpadIdType& npad_id); Kernel::KReadableEvent& AttachAvailabilityChangeEvent() const; Result StartDetection(u64 device_handle, NfcProtocol tag_protocol); Result StopDetection(u64 device_handle); - Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info) const; - Kernel::KReadableEvent& AttachActivateEvent(u64 device_handle) const; - Kernel::KReadableEvent& AttachDeactivateEvent(u64 device_handle) const; + Result GetTagInfo(u64 device_handle, NFP::TagInfo& tag_info); + Result AttachActivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const; + Result AttachDeactivateEvent(Kernel::KReadableEvent** event, u64 device_handle) const; Result ReadMifare(u64 device_handle, const std::span<const MifareReadBlockParameter> read_parameters, std::span<MifareReadBlockData> read_data); @@ -48,28 +49,28 @@ public: Result Mount(u64 device_handle, NFP::ModelType model_type, NFP::MountTarget mount_target); Result Unmount(u64 device_handle); Result OpenApplicationArea(u64 device_handle, u32 access_id); - Result GetApplicationArea(u64 device_handle, std::span<u8> data) const; + Result GetApplicationArea(u64 device_handle, std::span<u8> data); Result SetApplicationArea(u64 device_handle, std::span<const u8> data); Result Flush(u64 device_handle); Result Restore(u64 device_handle); Result CreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data); - Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info) const; - Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info) const; - Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info) const; + Result GetRegisterInfo(u64 device_handle, NFP::RegisterInfo& register_info); + Result GetCommonInfo(u64 device_handle, NFP::CommonInfo& common_info); + Result GetModelInfo(u64 device_handle, NFP::ModelInfo& model_info); u32 GetApplicationAreaSize() const; Result RecreateApplicationArea(u64 device_handle, u32 access_id, std::span<const u8> data); Result Format(u64 device_handle); - Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info) const; - Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info) const; + Result GetAdminInfo(u64 device_handle, NFP::AdminInfo& admin_info); + Result GetRegisterInfoPrivate(u64 device_handle, NFP::RegisterInfoPrivate& register_info); Result SetRegisterInfoPrivate(u64 device_handle, const NFP::RegisterInfoPrivate& register_info); Result DeleteRegisterInfo(u64 device_handle); Result DeleteApplicationArea(u64 device_handle); - Result ExistsApplicationArea(u64 device_handle, bool& has_application_area) const; - Result GetAll(u64 device_handle, NFP::NfpData& nfp_data) const; + Result ExistsApplicationArea(u64 device_handle, bool& has_application_area); + Result GetAll(u64 device_handle, NFP::NfpData& nfp_data); Result SetAll(u64 device_handle, const NFP::NfpData& nfp_data); Result FlushDebug(u64 device_handle); Result BreakTag(u64 device_handle, NFP::BreakType break_type); - Result ReadBackupData(u64 device_handle, std::span<u8> data) const; + Result ReadBackupData(u64 device_handle, std::span<u8> data); Result WriteBackupData(u64 device_handle, std::span<const u8> data); Result WriteNtf(u64 device_handle, NFP::WriteType, std::span<const u8> data); @@ -78,17 +79,20 @@ private: Result IsNfcParameterSet() const; Result IsNfcInitialized() const; + Result CheckHandleOnList(u64 device_handle, std::span<const u64> device_list) const; + Result GetDeviceFromHandle(u64 handle, std::shared_ptr<NfcDevice>& device, bool check_state) const; Result GetDeviceHandle(u64 handle, std::shared_ptr<NfcDevice>& device) const; - Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result) const; + Result VerifyDeviceResult(std::shared_ptr<NfcDevice> device, Result operation_result); Result CheckDeviceState(std::shared_ptr<NfcDevice> device) const; std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle); const std::optional<std::shared_ptr<NfcDevice>> GetNfcDevice(u64 handle) const; bool is_initialized = false; + u64 time_since_last_error = 0; mutable std::mutex mutex; std::array<std::shared_ptr<NfcDevice>, 10> devices{}; diff --git a/src/core/hle/service/nfc/nfc_interface.cpp b/src/core/hle/service/nfc/nfc_interface.cpp index e7ca7582e..179c7ba2c 100644 --- a/src/core/hle/service/nfc/nfc_interface.cpp +++ b/src/core/hle/service/nfc/nfc_interface.cpp @@ -79,7 +79,7 @@ void NfcInterface::ListDevices(HLERequestContext& ctx) { const std::size_t max_allowed_devices = ctx.GetWriteBufferNumElements<u64>(); LOG_DEBUG(Service_NFC, "called"); - auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices); + auto result = GetManager()->ListDevices(nfp_devices, max_allowed_devices, true); result = TranslateResultToServiceError(result); if (result.IsError()) { @@ -190,9 +190,13 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) { const auto device_handle{rp.Pop<u64>()}; LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + Kernel::KReadableEvent* out_event = nullptr; + auto result = GetManager()->AttachActivateEvent(&out_event, device_handle); + result = TranslateResultToServiceError(result); + IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(GetManager()->AttachActivateEvent(device_handle)); + rb.Push(result); + rb.PushCopyObjects(out_event); } void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { @@ -200,9 +204,13 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { const auto device_handle{rp.Pop<u64>()}; LOG_DEBUG(Service_NFC, "called, device_handle={}", device_handle); + Kernel::KReadableEvent* out_event = nullptr; + auto result = GetManager()->AttachDeactivateEvent(&out_event, device_handle); + result = TranslateResultToServiceError(result); + IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(GetManager()->AttachDeactivateEvent(device_handle)); + rb.Push(result); + rb.PushCopyObjects(out_event); } void NfcInterface::ReadMifare(HLERequestContext& ctx) { diff --git a/src/core/hle/service/nfc/nfc_result.h b/src/core/hle/service/nfc/nfc_result.h index 715c0e80c..464b5fd69 100644 --- a/src/core/hle/service/nfc/nfc_result.h +++ b/src/core/hle/service/nfc/nfc_result.h @@ -17,7 +17,10 @@ constexpr Result ResultNfcNotInitialized(ErrorModule::NFC, 77); constexpr Result ResultNfcDisabled(ErrorModule::NFC, 80); constexpr Result ResultWriteAmiiboFailed(ErrorModule::NFC, 88); constexpr Result ResultTagRemoved(ErrorModule::NFC, 97); +constexpr Result ResultUnknown112(ErrorModule::NFC, 112); constexpr Result ResultUnableToAccessBackupFile(ErrorModule::NFC, 113); +constexpr Result ResultUnknown114(ErrorModule::NFC, 114); +constexpr Result ResultUnknown115(ErrorModule::NFC, 115); constexpr Result ResultRegistrationIsNotInitialized(ErrorModule::NFC, 120); constexpr Result ResultApplicationAreaIsNotInitialized(ErrorModule::NFC, 128); constexpr Result ResultCorruptedDataWithBackup(ErrorModule::NFC, 136); diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 91d42853e..21b06d10b 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp @@ -7,6 +7,7 @@ #include "core/hle/service/kernel_helpers.h" #include "core/hle/service/nifm/nifm.h" #include "core/hle/service/server_manager.h" +#include "network/network.h" namespace { diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h index 9b20e6823..ae99c4695 100644 --- a/src/core/hle/service/nifm/nifm.h +++ b/src/core/hle/service/nifm/nifm.h @@ -4,14 +4,15 @@ #pragma once #include "core/hle/service/service.h" -#include "network/network.h" -#include "network/room.h" -#include "network/room_member.h" namespace Core { class System; } +namespace Network { +class RoomNetwork; +} + namespace Service::NIFM { void LoopProcess(Core::System& system); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index e7f7e273b..968eaa175 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -128,7 +128,7 @@ NvResult nvmap::IocAlloc(std::span<const u8> input, std::span<u8> output) { } bool is_out_io{}; ASSERT(system.ApplicationProcess() - ->PageTable() + ->GetPageTable() .LockForMapDeviceAddressSpace(&is_out_io, handle_description->address, handle_description->size, Kernel::KMemoryPermission::None, true, false) @@ -255,7 +255,7 @@ NvResult nvmap::IocFree(std::span<const u8> input, std::span<u8> output) { if (auto freeInfo{file.FreeHandle(params.handle, false)}) { if (freeInfo->can_unlock) { ASSERT(system.ApplicationProcess() - ->PageTable() + ->GetPageTable() .UnlockForDeviceAddressSpace(freeInfo->address, freeInfo->size) .IsSuccess()); } diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index bce45d321..e63b0a357 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -20,6 +20,9 @@ #include "core/internal_network/sockets.h" #include "network/network.h" +using Common::Expected; +using Common::Unexpected; + namespace Service::Sockets { namespace { @@ -265,16 +268,19 @@ void BSD::GetSockOpt(HLERequestContext& ctx) { const u32 level = rp.Pop<u32>(); const auto optname = static_cast<OptName>(rp.Pop<u32>()); - LOG_WARNING(Service, "(STUBBED) called. fd={} level={} optname=0x{:x}", fd, level, optname); - std::vector<u8> optval(ctx.GetWriteBufferSize()); + LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} len=0x{:x}", fd, level, optname, + optval.size()); + + const Errno err = GetSockOptImpl(fd, level, optname, optval); + ctx.WriteBuffer(optval); IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push<s32>(-1); - rb.PushEnum(Errno::NOTCONN); + rb.Push<s32>(err == Errno::SUCCESS ? 0 : -1); + rb.PushEnum(err); rb.Push<u32>(static_cast<u32>(optval.size())); } @@ -436,6 +442,31 @@ void BSD::Close(HLERequestContext& ctx) { BuildErrnoResponse(ctx, CloseImpl(fd)); } +void BSD::DuplicateSocket(HLERequestContext& ctx) { + struct InputParameters { + s32 fd; + u64 reserved; + }; + static_assert(sizeof(InputParameters) == 0x10); + + struct OutputParameters { + s32 ret; + Errno bsd_errno; + }; + static_assert(sizeof(OutputParameters) == 0x8); + + IPC::RequestParser rp{ctx}; + auto input = rp.PopRaw<InputParameters>(); + + Expected<s32, Errno> res = DuplicateSocketImpl(input.fd); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.PushRaw(OutputParameters{ + .ret = res.value_or(0), + .bsd_errno = res ? Errno::SUCCESS : res.error(), + }); +} + void BSD::EventFd(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 initval = rp.Pop<u64>(); @@ -477,12 +508,12 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco auto room_member = room_network.GetRoomMember().lock(); if (room_member && room_member->IsConnected()) { - descriptor.socket = std::make_unique<Network::ProxySocket>(room_network); + descriptor.socket = std::make_shared<Network::ProxySocket>(room_network); } else { - descriptor.socket = std::make_unique<Network::Socket>(); + descriptor.socket = std::make_shared<Network::Socket>(); } - descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol)); + descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol)); descriptor.is_connection_based = IsConnectionBased(type); return {fd, Errno::SUCCESS}; @@ -538,7 +569,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) { Network::PollFD result; result.socket = file_descriptors[pollfd.fd]->socket.get(); - result.events = TranslatePollEventsToHost(pollfd.events); + result.events = Translate(pollfd.events); result.revents = Network::PollEvents{}; return result; }); @@ -547,7 +578,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::span<con const size_t num = host_pollfds.size(); for (size_t i = 0; i < num; ++i) { - fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents); + fds[i].revents = Translate(host_pollfds[i].revents); } std::memcpy(write_buffer.data(), fds.data(), length); @@ -617,7 +648,8 @@ Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) { } const SockAddrIn guest_addrin = Translate(addr_in); - ASSERT(write_buffer.size() == sizeof(guest_addrin)); + ASSERT(write_buffer.size() >= sizeof(guest_addrin)); + write_buffer.resize(sizeof(guest_addrin)); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); return Translate(bsd_errno); } @@ -633,7 +665,8 @@ Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) { } const SockAddrIn guest_addrin = Translate(addr_in); - ASSERT(write_buffer.size() == sizeof(guest_addrin)); + ASSERT(write_buffer.size() >= sizeof(guest_addrin)); + write_buffer.resize(sizeof(guest_addrin)); std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin)); return Translate(bsd_errno); } @@ -671,13 +704,47 @@ std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) { } } -Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { - UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET +Errno BSD::GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval) { + if (!IsFileDescriptorValid(fd)) { + return Errno::BADF; + } + + if (level != static_cast<u32>(SocketLevel::SOCKET)) { + UNIMPLEMENTED_MSG("Unknown getsockopt level"); + return Errno::SUCCESS; + } + + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); + + switch (optname) { + case OptName::ERROR_: { + auto [pending_err, getsockopt_err] = socket->GetPendingError(); + if (getsockopt_err == Network::Errno::SUCCESS) { + Errno translated_pending_err = Translate(pending_err); + ASSERT_OR_EXECUTE_MSG( + optval.size() == sizeof(Errno), { return Errno::INVAL; }, + "Incorrect getsockopt option size"); + optval.resize(sizeof(Errno)); + memcpy(optval.data(), &translated_pending_err, sizeof(Errno)); + } + return Translate(getsockopt_err); + } + default: + UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); + return Errno::SUCCESS; + } +} +Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) { if (!IsFileDescriptorValid(fd)) { return Errno::BADF; } + if (level != static_cast<u32>(SocketLevel::SOCKET)) { + UNIMPLEMENTED_MSG("Unknown setsockopt level"); + return Errno::SUCCESS; + } + Network::SocketBase* const socket = file_descriptors[fd]->socket.get(); if (optname == OptName::LINGER) { @@ -711,6 +778,9 @@ Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, con return Translate(socket->SetSndTimeo(value)); case OptName::RCVTIMEO: return Translate(socket->SetRcvTimeo(value)); + case OptName::NOSIGPIPE: + LOG_WARNING(Service, "(STUBBED) setting NOSIGPIPE to {}", value); + return Errno::SUCCESS; default: UNIMPLEMENTED_MSG("Unimplemented optname={}", optname); return Errno::SUCCESS; @@ -841,6 +911,28 @@ Errno BSD::CloseImpl(s32 fd) { return bsd_errno; } +Expected<s32, Errno> BSD::DuplicateSocketImpl(s32 fd) { + if (!IsFileDescriptorValid(fd)) { + return Unexpected(Errno::BADF); + } + + const s32 new_fd = FindFreeFileDescriptorHandle(); + if (new_fd < 0) { + LOG_ERROR(Service, "No more file descriptors available"); + return Unexpected(Errno::MFILE); + } + + file_descriptors[new_fd] = file_descriptors[fd]; + return new_fd; +} + +std::optional<std::shared_ptr<Network::SocketBase>> BSD::GetSocket(s32 fd) { + if (!IsFileDescriptorValid(fd)) { + return std::nullopt; + } + return file_descriptors[fd]->socket; +} + s32 BSD::FindFreeFileDescriptorHandle() noexcept { for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) { if (!file_descriptors[fd]) { @@ -911,7 +1003,7 @@ BSD::BSD(Core::System& system_, const char* name) {24, &BSD::Write, "Write"}, {25, &BSD::Read, "Read"}, {26, &BSD::Close, "Close"}, - {27, nullptr, "DuplicateSocket"}, + {27, &BSD::DuplicateSocket, "DuplicateSocket"}, {28, nullptr, "GetResourceStatistics"}, {29, nullptr, "RecvMMsg"}, {30, nullptr, "SendMMsg"}, diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h index 30ae9c140..430edb97c 100644 --- a/src/core/hle/service/sockets/bsd.h +++ b/src/core/hle/service/sockets/bsd.h @@ -8,6 +8,7 @@ #include <vector> #include "common/common_types.h" +#include "common/expected.h" #include "common/socket_types.h" #include "core/hle/service/service.h" #include "core/hle/service/sockets/sockets.h" @@ -29,12 +30,19 @@ public: explicit BSD(Core::System& system_, const char* name); ~BSD() override; + // These methods are called from SSL; the first two are also called from + // this class for the corresponding IPC methods. + // On the real device, the SSL service makes IPC calls to this service. + Common::Expected<s32, Errno> DuplicateSocketImpl(s32 fd); + Errno CloseImpl(s32 fd); + std::optional<std::shared_ptr<Network::SocketBase>> GetSocket(s32 fd); + private: /// Maximum number of file descriptors static constexpr size_t MAX_FD = 128; struct FileDescriptor { - std::unique_ptr<Network::SocketBase> socket; + std::shared_ptr<Network::SocketBase> socket; s32 flags = 0; bool is_connection_based = false; }; @@ -138,6 +146,7 @@ private: void Write(HLERequestContext& ctx); void Read(HLERequestContext& ctx); void Close(HLERequestContext& ctx); + void DuplicateSocket(HLERequestContext& ctx); void EventFd(HLERequestContext& ctx); template <typename Work> @@ -153,6 +162,7 @@ private: Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer); Errno ListenImpl(s32 fd, s32 backlog); std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg); + Errno GetSockOptImpl(s32 fd, u32 level, OptName optname, std::vector<u8>& optval); Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval); Errno ShutdownImpl(s32 fd, s32 how); std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message); @@ -161,7 +171,6 @@ private: std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, std::span<const u8> message); std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, std::span<const u8> message, std::span<const u8> addr); - Errno CloseImpl(s32 fd); s32 FindFreeFileDescriptorHandle() noexcept; bool IsFileDescriptorValid(s32 fd) const noexcept; diff --git a/src/core/hle/service/sockets/nsd.cpp b/src/core/hle/service/sockets/nsd.cpp index 6491a73be..36c6cd05c 100644 --- a/src/core/hle/service/sockets/nsd.cpp +++ b/src/core/hle/service/sockets/nsd.cpp @@ -1,10 +1,24 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/sockets/nsd.h" +#include "common/string_util.h" + namespace Service::Sockets { +constexpr Result ResultOverflow{ErrorModule::NSD, 6}; + +// This is nn::oe::ServerEnvironmentType +enum class ServerEnvironmentType : u8 { + Dd, + Lp, + Sd, + Sp, + Dp, +}; + NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} { // clang-format off static const FunctionInfo functions[] = { @@ -15,8 +29,8 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {13, nullptr, "DeleteSettings"}, {14, nullptr, "ImportSettings"}, {15, nullptr, "SetChangeEnvironmentIdentifierDisabled"}, - {20, nullptr, "Resolve"}, - {21, nullptr, "ResolveEx"}, + {20, &NSD::Resolve, "Resolve"}, + {21, &NSD::ResolveEx, "ResolveEx"}, {30, nullptr, "GetNasServiceSetting"}, {31, nullptr, "GetNasServiceSettingEx"}, {40, nullptr, "GetNasRequestFqdn"}, @@ -31,7 +45,7 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na {62, nullptr, "DeleteSaveDataOfFsForTest"}, {63, nullptr, "IsChangeEnvironmentIdentifierDisabled"}, {64, nullptr, "SetWithoutDomainExchangeFqdns"}, - {100, nullptr, "GetApplicationServerEnvironmentType"}, + {100, &NSD::GetApplicationServerEnvironmentType, "GetApplicationServerEnvironmentType"}, {101, nullptr, "SetApplicationServerEnvironmentType"}, {102, nullptr, "DeleteApplicationServerEnvironmentType"}, }; @@ -40,6 +54,61 @@ NSD::NSD(Core::System& system_, const char* name) : ServiceFramework{system_, na RegisterHandlers(functions); } +static ResultVal<std::string> ResolveImpl(const std::string& fqdn_in) { + // The real implementation makes various substitutions. + // For now we just return the string as-is, which is good enough when not + // connecting to real Nintendo servers. + LOG_WARNING(Service, "(STUBBED) called, fqdn_in={}", fqdn_in); + return fqdn_in; +} + +static Result ResolveCommon(const std::string& fqdn_in, std::array<char, 0x100>& fqdn_out) { + const auto res = ResolveImpl(fqdn_in); + if (res.Failed()) { + return res.Code(); + } + if (res->size() >= fqdn_out.size()) { + return ResultOverflow; + } + std::memcpy(fqdn_out.data(), res->c_str(), res->size() + 1); + return ResultSuccess; +} + +void NSD::Resolve(HLERequestContext& ctx) { + const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0)); + + std::array<char, 0x100> fqdn_out{}; + const Result res = ResolveCommon(fqdn_in, fqdn_out); + + ctx.WriteBuffer(fqdn_out); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + +void NSD::ResolveEx(HLERequestContext& ctx) { + const std::string fqdn_in = Common::StringFromBuffer(ctx.ReadBuffer(0)); + + std::array<char, 0x100> fqdn_out; + const Result res = ResolveCommon(fqdn_in, fqdn_out); + + if (res.IsError()) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + return; + } + + ctx.WriteBuffer(fqdn_out); + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(ResultSuccess); + rb.Push(ResultSuccess); +} + +void NSD::GetApplicationServerEnvironmentType(HLERequestContext& ctx) { + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(ResultSuccess); + rb.Push(static_cast<u32>(ServerEnvironmentType::Lp)); +} + NSD::~NSD() = default; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/nsd.h b/src/core/hle/service/sockets/nsd.h index 5cc12b855..57760a0c8 100644 --- a/src/core/hle/service/sockets/nsd.h +++ b/src/core/hle/service/sockets/nsd.h @@ -15,6 +15,11 @@ class NSD final : public ServiceFramework<NSD> { public: explicit NSD(Core::System& system_, const char* name); ~NSD() override; + +private: + void Resolve(HLERequestContext& ctx); + void ResolveEx(HLERequestContext& ctx); + void GetApplicationServerEnvironmentType(HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.cpp b/src/core/hle/service/sockets/sfdnsres.cpp index 132dd5797..84cc79de8 100644 --- a/src/core/hle/service/sockets/sfdnsres.cpp +++ b/src/core/hle/service/sockets/sfdnsres.cpp @@ -10,27 +10,18 @@ #include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/sockets/sfdnsres.h" +#include "core/hle/service/sockets/sockets.h" +#include "core/hle/service/sockets/sockets_translate.h" +#include "core/internal_network/network.h" #include "core/memory.h" -#ifdef _WIN32 -#include <ws2tcpip.h> -#elif YUZU_UNIX -#include <arpa/inet.h> -#include <netdb.h> -#include <netinet/in.h> -#include <sys/socket.h> -#ifndef EAI_NODATA -#define EAI_NODATA EAI_NONAME -#endif -#endif - namespace Service::Sockets { SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres"} { static const FunctionInfo functions[] = { {0, nullptr, "SetDnsAddressesPrivateRequest"}, {1, nullptr, "GetDnsAddressPrivateRequest"}, - {2, nullptr, "GetHostByNameRequest"}, + {2, &SFDNSRES::GetHostByNameRequest, "GetHostByNameRequest"}, {3, nullptr, "GetHostByAddrRequest"}, {4, nullptr, "GetHostStringErrorRequest"}, {5, nullptr, "GetGaiStringErrorRequest"}, @@ -38,11 +29,11 @@ SFDNSRES::SFDNSRES(Core::System& system_) : ServiceFramework{system_, "sfdnsres" {7, nullptr, "GetNameInfoRequest"}, {8, nullptr, "RequestCancelHandleRequest"}, {9, nullptr, "CancelRequest"}, - {10, nullptr, "GetHostByNameRequestWithOptions"}, + {10, &SFDNSRES::GetHostByNameRequestWithOptions, "GetHostByNameRequestWithOptions"}, {11, nullptr, "GetHostByAddrRequestWithOptions"}, {12, &SFDNSRES::GetAddrInfoRequestWithOptions, "GetAddrInfoRequestWithOptions"}, {13, nullptr, "GetNameInfoRequestWithOptions"}, - {14, nullptr, "ResolverSetOptionRequest"}, + {14, &SFDNSRES::ResolverSetOptionRequest, "ResolverSetOptionRequest"}, {15, nullptr, "ResolverGetOptionRequest"}, }; RegisterHandlers(functions); @@ -59,188 +50,285 @@ enum class NetDbError : s32 { NoData = 4, }; -static NetDbError AddrInfoErrorToNetDbError(s32 result) { - // Best effort guess to map errors +static NetDbError GetAddrInfoErrorToNetDbError(GetAddrInfoError result) { + // These combinations have been verified on console (but are not + // exhaustive). switch (result) { - case 0: + case GetAddrInfoError::SUCCESS: return NetDbError::Success; - case EAI_AGAIN: + case GetAddrInfoError::AGAIN: return NetDbError::TryAgain; - case EAI_NODATA: - return NetDbError::NoData; + case GetAddrInfoError::NODATA: + return NetDbError::HostNotFound; + case GetAddrInfoError::SERVICE: + return NetDbError::Success; default: return NetDbError::HostNotFound; } } -static std::vector<u8> SerializeAddrInfo(const addrinfo* addrinfo, s32 result_code, +static Errno GetAddrInfoErrorToErrno(GetAddrInfoError result) { + // These combinations have been verified on console (but are not + // exhaustive). + switch (result) { + case GetAddrInfoError::SUCCESS: + // Note: Sometimes a successful lookup sets errno to EADDRNOTAVAIL for + // some reason, but that doesn't seem useful to implement. + return Errno::SUCCESS; + case GetAddrInfoError::AGAIN: + return Errno::SUCCESS; + case GetAddrInfoError::NODATA: + return Errno::SUCCESS; + case GetAddrInfoError::SERVICE: + return Errno::INVAL; + default: + return Errno::SUCCESS; + } +} + +template <typename T> +static void Append(std::vector<u8>& vec, T t) { + const size_t offset = vec.size(); + vec.resize(offset + sizeof(T)); + std::memcpy(vec.data() + offset, &t, sizeof(T)); +} + +static void AppendNulTerminated(std::vector<u8>& vec, std::string_view str) { + const size_t offset = vec.size(); + vec.resize(offset + str.size() + 1); + std::memmove(vec.data() + offset, str.data(), str.size()); +} + +// We implement gethostbyname using the host's getaddrinfo rather than the +// host's gethostbyname, because it simplifies portability: e.g., getaddrinfo +// behaves the same on Unix and Windows, unlike gethostbyname where Windows +// doesn't implement h_errno. +static std::vector<u8> SerializeAddrInfoAsHostEnt(const std::vector<Network::AddrInfo>& vec, + std::string_view host) { + + std::vector<u8> data; + // h_name: use the input hostname (append nul-terminated) + AppendNulTerminated(data, host); + // h_aliases: leave empty + + Append<u32_be>(data, 0); // count of h_aliases + // (If the count were nonzero, the aliases would be appended as nul-terminated here.) + Append<u16_be>(data, static_cast<u16>(Domain::INET)); // h_addrtype + Append<u16_be>(data, sizeof(Network::IPv4Address)); // h_length + // h_addr_list: + size_t count = vec.size(); + ASSERT(count <= UINT32_MAX); + Append<u32_be>(data, static_cast<uint32_t>(count)); + for (const Network::AddrInfo& addrinfo : vec) { + // On the Switch, this is passed through htonl despite already being + // big-endian, so it ends up as little-endian. + Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); + + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, + Network::IPv4AddressToString(addrinfo.addr.ip)); + } + return data; +} + +static std::pair<u32, GetAddrInfoError> GetHostByNameRequestImpl(HLERequestContext& ctx) { + struct InputParameters { + u8 use_nsd_resolve; + u32 cancel_handle; + u64 process_id; + }; + static_assert(sizeof(InputParameters) == 0x10); + + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw<InputParameters>(); + + LOG_WARNING( + Service, + "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}", + parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id); + + const auto host_buffer = ctx.ReadBuffer(0); + const std::string host = Common::StringFromBuffer(host_buffer); + // For now, ignore options, which are in input buffer 1 for GetHostByNameRequestWithOptions. + + auto res = Network::GetAddressInfo(host, /*service*/ std::nullopt); + if (!res.has_value()) { + return {0, Translate(res.error())}; + } + + const std::vector<u8> data = SerializeAddrInfoAsHostEnt(res.value(), host); + const u32 data_size = static_cast<u32>(data.size()); + ctx.WriteBuffer(data, 0); + + return {data_size, GetAddrInfoError::SUCCESS}; +} + +void SFDNSRES::GetHostByNameRequest(HLERequestContext& ctx) { + auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx); + + struct OutputParameters { + NetDbError netdb_error; + Errno bsd_errno; + u32 data_size; + }; + static_assert(sizeof(OutputParameters) == 0xc); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(ResultSuccess); + rb.PushRaw(OutputParameters{ + .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err), + .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err), + .data_size = data_size, + }); +} + +void SFDNSRES::GetHostByNameRequestWithOptions(HLERequestContext& ctx) { + auto [data_size, emu_gai_err] = GetHostByNameRequestImpl(ctx); + + struct OutputParameters { + u32 data_size; + NetDbError netdb_error; + Errno bsd_errno; + }; + static_assert(sizeof(OutputParameters) == 0xc); + + IPC::ResponseBuilder rb{ctx, 5}; + rb.Push(ResultSuccess); + rb.PushRaw(OutputParameters{ + .data_size = data_size, + .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err), + .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err), + }); +} + +static std::vector<u8> SerializeAddrInfo(const std::vector<Network::AddrInfo>& vec, std::string_view host) { // Adapted from // https://github.com/switchbrew/libnx/blob/c5a9a909a91657a9818a3b7e18c9b91ff0cbb6e3/nx/source/runtime/resolver.c#L190 std::vector<u8> data; - auto* current = addrinfo; - while (current != nullptr) { - struct SerializedResponseHeader { - u32 magic; - s32 flags; - s32 family; - s32 socket_type; - s32 protocol; - u32 address_length; - }; - static_assert(sizeof(SerializedResponseHeader) == 0x18, - "Response header size must be 0x18 bytes"); - - constexpr auto header_size = sizeof(SerializedResponseHeader); - const auto addr_size = - current->ai_addr && current->ai_addrlen > 0 ? current->ai_addrlen : 4; - const auto canonname_size = current->ai_canonname ? strlen(current->ai_canonname) + 1 : 1; - - const auto last_size = data.size(); - data.resize(last_size + header_size + addr_size + canonname_size); - - // Header in network byte order - SerializedResponseHeader header{}; - - constexpr auto HEADER_MAGIC = 0xBEEFCAFE; - header.magic = htonl(HEADER_MAGIC); - header.family = htonl(current->ai_family); - header.flags = htonl(current->ai_flags); - header.socket_type = htonl(current->ai_socktype); - header.protocol = htonl(current->ai_protocol); - header.address_length = current->ai_addr ? htonl((u32)current->ai_addrlen) : 0; - - auto* header_ptr = data.data() + last_size; - std::memcpy(header_ptr, &header, header_size); - - if (header.address_length == 0) { - std::memset(header_ptr + header_size, 0, 4); - } else { - switch (current->ai_family) { - case AF_INET: { - struct SockAddrIn { - s16 sin_family; - u16 sin_port; - u32 sin_addr; - u8 sin_zero[8]; - }; - - SockAddrIn serialized_addr{}; - const auto addr = *reinterpret_cast<sockaddr_in*>(current->ai_addr); - serialized_addr.sin_port = htons(addr.sin_port); - serialized_addr.sin_family = htons(addr.sin_family); - serialized_addr.sin_addr = htonl(addr.sin_addr.s_addr); - std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn)); - - char addr_string_buf[64]{}; - inet_ntop(AF_INET, &addr.sin_addr, addr_string_buf, std::size(addr_string_buf)); - LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, addr_string_buf); - break; - } - case AF_INET6: { - struct SockAddrIn6 { - s16 sin6_family; - u16 sin6_port; - u32 sin6_flowinfo; - u8 sin6_addr[16]; - u32 sin6_scope_id; - }; - - SockAddrIn6 serialized_addr{}; - const auto addr = *reinterpret_cast<sockaddr_in6*>(current->ai_addr); - serialized_addr.sin6_family = htons(addr.sin6_family); - serialized_addr.sin6_port = htons(addr.sin6_port); - serialized_addr.sin6_flowinfo = htonl(addr.sin6_flowinfo); - serialized_addr.sin6_scope_id = htonl(addr.sin6_scope_id); - std::memcpy(serialized_addr.sin6_addr, &addr.sin6_addr, - sizeof(SockAddrIn6::sin6_addr)); - std::memcpy(header_ptr + header_size, &serialized_addr, sizeof(SockAddrIn6)); - - char addr_string_buf[64]{}; - inet_ntop(AF_INET6, &addr.sin6_addr, addr_string_buf, std::size(addr_string_buf)); - LOG_INFO(Service, "Resolved host '{}' to IPv6 address {}", host, addr_string_buf); - break; - } - default: - std::memcpy(header_ptr + header_size, current->ai_addr, addr_size); - break; - } - } - if (current->ai_canonname) { - std::memcpy(header_ptr + addr_size, current->ai_canonname, canonname_size); + for (const Network::AddrInfo& addrinfo : vec) { + // serialized addrinfo: + Append<u32_be>(data, 0xBEEFCAFE); // magic + Append<u32_be>(data, 0); // ai_flags + Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.family))); // ai_family + Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.socket_type))); // ai_socktype + Append<u32_be>(data, static_cast<u32>(Translate(addrinfo.protocol))); // ai_protocol + Append<u32_be>(data, sizeof(SockAddrIn)); // ai_addrlen + // ^ *not* sizeof(SerializedSockAddrIn), not that it matters since they're the same size + + // ai_addr: + Append<u16_be>(data, static_cast<u16>(Translate(addrinfo.addr.family))); // sin_family + // On the Switch, the following fields are passed through htonl despite + // already being big-endian, so they end up as little-endian. + Append<u16_le>(data, addrinfo.addr.portno); // sin_port + Append<u32_le>(data, Network::IPv4AddressToInteger(addrinfo.addr.ip)); // sin_addr + data.resize(data.size() + 8, 0); // sin_zero + + if (addrinfo.canon_name.has_value()) { + AppendNulTerminated(data, *addrinfo.canon_name); } else { - *(header_ptr + header_size + addr_size) = 0; + data.push_back(0); } - current = current->ai_next; + LOG_INFO(Service, "Resolved host '{}' to IPv4 address {}", host, + Network::IPv4AddressToString(addrinfo.addr.ip)); } - // 4-byte sentinel value - data.push_back(0); - data.push_back(0); - data.push_back(0); - data.push_back(0); + data.resize(data.size() + 4, 0); // 4-byte sentinel value return data; } -static std::pair<u32, s32> GetAddrInfoRequestImpl(HLERequestContext& ctx) { - struct Parameters { +static std::pair<u32, GetAddrInfoError> GetAddrInfoRequestImpl(HLERequestContext& ctx) { + struct InputParameters { u8 use_nsd_resolve; - u32 unknown; + u32 cancel_handle; u64 process_id; }; + static_assert(sizeof(InputParameters) == 0x10); IPC::RequestParser rp{ctx}; - const auto parameters = rp.PopRaw<Parameters>(); + const auto parameters = rp.PopRaw<InputParameters>(); + + LOG_WARNING( + Service, + "called with ignored parameters: use_nsd_resolve={}, cancel_handle={}, process_id={}", + parameters.use_nsd_resolve, parameters.cancel_handle, parameters.process_id); - LOG_WARNING(Service, - "called with ignored parameters: use_nsd_resolve={}, unknown={}, process_id={}", - parameters.use_nsd_resolve, parameters.unknown, parameters.process_id); + // TODO: If use_nsd_resolve is true, pass the name through NSD::Resolve + // before looking up. const auto host_buffer = ctx.ReadBuffer(0); const std::string host = Common::StringFromBuffer(host_buffer); - const auto service_buffer = ctx.ReadBuffer(1); - const std::string service = Common::StringFromBuffer(service_buffer); - - addrinfo* addrinfo; - // Pass null for hints. Serialized hints are also passed in a buffer, but are ignored for now - s32 result_code = getaddrinfo(host.c_str(), service.c_str(), nullptr, &addrinfo); + std::optional<std::string> service = std::nullopt; + if (ctx.CanReadBuffer(1)) { + const std::span<const u8> service_buffer = ctx.ReadBuffer(1); + service = Common::StringFromBuffer(service_buffer); + } - u32 data_size = 0; - if (result_code == 0 && addrinfo != nullptr) { - const std::vector<u8>& data = SerializeAddrInfo(addrinfo, result_code, host); - data_size = static_cast<u32>(data.size()); - freeaddrinfo(addrinfo); + // Serialized hints are also passed in a buffer, but are ignored for now. - ctx.WriteBuffer(data, 0); + auto res = Network::GetAddressInfo(host, service); + if (!res.has_value()) { + return {0, Translate(res.error())}; } - return std::make_pair(data_size, result_code); + const std::vector<u8> data = SerializeAddrInfo(res.value(), host); + const u32 data_size = static_cast<u32>(data.size()); + ctx.WriteBuffer(data, 0); + + return {data_size, GetAddrInfoError::SUCCESS}; } void SFDNSRES::GetAddrInfoRequest(HLERequestContext& ctx) { - auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx); + + struct OutputParameters { + Errno bsd_errno; + GetAddrInfoError gai_error; + u32 data_size; + }; + static_assert(sizeof(OutputParameters) == 0xc); - IPC::ResponseBuilder rb{ctx, 4}; + IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode - rb.Push(result_code); // errno - rb.Push(data_size); // serialized size + rb.PushRaw(OutputParameters{ + .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err), + .gai_error = emu_gai_err, + .data_size = data_size, + }); } void SFDNSRES::GetAddrInfoRequestWithOptions(HLERequestContext& ctx) { // Additional options are ignored - auto [data_size, result_code] = GetAddrInfoRequestImpl(ctx); + auto [data_size, emu_gai_err] = GetAddrInfoRequestImpl(ctx); + + struct OutputParameters { + u32 data_size; + GetAddrInfoError gai_error; + NetDbError netdb_error; + Errno bsd_errno; + }; + static_assert(sizeof(OutputParameters) == 0x10); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(ResultSuccess); + rb.PushRaw(OutputParameters{ + .data_size = data_size, + .gai_error = emu_gai_err, + .netdb_error = GetAddrInfoErrorToNetDbError(emu_gai_err), + .bsd_errno = GetAddrInfoErrorToErrno(emu_gai_err), + }); +} + +void SFDNSRES::ResolverSetOptionRequest(HLERequestContext& ctx) { + LOG_WARNING(Service, "(STUBBED) called"); + + IPC::ResponseBuilder rb{ctx, 3}; - IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); - rb.Push(data_size); // serialized size - rb.Push(result_code); // errno - rb.Push(static_cast<s32>(AddrInfoErrorToNetDbError(result_code))); // NetDBErrorCode - rb.Push(0); + rb.Push<s32>(0); // bsd errno } } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sfdnsres.h b/src/core/hle/service/sockets/sfdnsres.h index 18e3cd60c..d99a9d560 100644 --- a/src/core/hle/service/sockets/sfdnsres.h +++ b/src/core/hle/service/sockets/sfdnsres.h @@ -17,8 +17,11 @@ public: ~SFDNSRES() override; private: + void GetHostByNameRequest(HLERequestContext& ctx); + void GetHostByNameRequestWithOptions(HLERequestContext& ctx); void GetAddrInfoRequest(HLERequestContext& ctx); void GetAddrInfoRequestWithOptions(HLERequestContext& ctx); + void ResolverSetOptionRequest(HLERequestContext& ctx); }; } // namespace Service::Sockets diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h index acd2dae7b..77426c46e 100644 --- a/src/core/hle/service/sockets/sockets.h +++ b/src/core/hle/service/sockets/sockets.h @@ -22,13 +22,35 @@ enum class Errno : u32 { CONNRESET = 104, NOTCONN = 107, TIMEDOUT = 110, + INPROGRESS = 115, +}; + +enum class GetAddrInfoError : s32 { + SUCCESS = 0, + ADDRFAMILY = 1, + AGAIN = 2, + BADFLAGS = 3, + FAIL = 4, + FAMILY = 5, + MEMORY = 6, + NODATA = 7, + NONAME = 8, + SERVICE = 9, + SOCKTYPE = 10, + SYSTEM = 11, + BADHINTS = 12, + PROTOCOL = 13, + OVERFLOW_ = 14, // avoid name collision with Windows macro + OTHER = 15, }; enum class Domain : u32 { + Unspecified = 0, INET = 2, }; enum class Type : u32 { + Unspecified = 0, STREAM = 1, DGRAM = 2, RAW = 3, @@ -36,12 +58,16 @@ enum class Type : u32 { }; enum class Protocol : u32 { - UNSPECIFIED = 0, + Unspecified = 0, ICMP = 1, TCP = 6, UDP = 17, }; +enum class SocketLevel : u32 { + SOCKET = 0xffff, // i.e. SOL_SOCKET +}; + enum class OptName : u32 { REUSEADDR = 0x4, KEEPALIVE = 0x8, @@ -51,6 +77,8 @@ enum class OptName : u32 { RCVBUF = 0x1002, SNDTIMEO = 0x1005, RCVTIMEO = 0x1006, + ERROR_ = 0x1007, // avoid name collision with Windows macro + NOSIGPIPE = 0x800, // at least according to libnx }; enum class ShutdownHow : s32 { @@ -80,6 +108,9 @@ enum class PollEvents : u16 { Err = 1 << 3, Hup = 1 << 4, Nval = 1 << 5, + RdNorm = 1 << 6, + RdBand = 1 << 7, + WrBand = 1 << 8, }; DECLARE_ENUM_FLAG_OPERATORS(PollEvents); diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp index 594e58f90..2f9a0e39c 100644 --- a/src/core/hle/service/sockets/sockets_translate.cpp +++ b/src/core/hle/service/sockets/sockets_translate.cpp @@ -29,6 +29,8 @@ Errno Translate(Network::Errno value) { return Errno::TIMEDOUT; case Network::Errno::CONNRESET: return Errno::CONNRESET; + case Network::Errno::INPROGRESS: + return Errno::INPROGRESS; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", value); return Errno::SUCCESS; @@ -39,8 +41,50 @@ std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) { return {value.first, Translate(value.second)}; } +GetAddrInfoError Translate(Network::GetAddrInfoError error) { + switch (error) { + case Network::GetAddrInfoError::SUCCESS: + return GetAddrInfoError::SUCCESS; + case Network::GetAddrInfoError::ADDRFAMILY: + return GetAddrInfoError::ADDRFAMILY; + case Network::GetAddrInfoError::AGAIN: + return GetAddrInfoError::AGAIN; + case Network::GetAddrInfoError::BADFLAGS: + return GetAddrInfoError::BADFLAGS; + case Network::GetAddrInfoError::FAIL: + return GetAddrInfoError::FAIL; + case Network::GetAddrInfoError::FAMILY: + return GetAddrInfoError::FAMILY; + case Network::GetAddrInfoError::MEMORY: + return GetAddrInfoError::MEMORY; + case Network::GetAddrInfoError::NODATA: + return GetAddrInfoError::NODATA; + case Network::GetAddrInfoError::NONAME: + return GetAddrInfoError::NONAME; + case Network::GetAddrInfoError::SERVICE: + return GetAddrInfoError::SERVICE; + case Network::GetAddrInfoError::SOCKTYPE: + return GetAddrInfoError::SOCKTYPE; + case Network::GetAddrInfoError::SYSTEM: + return GetAddrInfoError::SYSTEM; + case Network::GetAddrInfoError::BADHINTS: + return GetAddrInfoError::BADHINTS; + case Network::GetAddrInfoError::PROTOCOL: + return GetAddrInfoError::PROTOCOL; + case Network::GetAddrInfoError::OVERFLOW_: + return GetAddrInfoError::OVERFLOW_; + case Network::GetAddrInfoError::OTHER: + return GetAddrInfoError::OTHER; + default: + UNIMPLEMENTED_MSG("Unimplemented GetAddrInfoError={}", error); + return GetAddrInfoError::OTHER; + } +} + Network::Domain Translate(Domain domain) { switch (domain) { + case Domain::Unspecified: + return Network::Domain::Unspecified; case Domain::INET: return Network::Domain::INET; default: @@ -51,6 +95,8 @@ Network::Domain Translate(Domain domain) { Domain Translate(Network::Domain domain) { switch (domain) { + case Network::Domain::Unspecified: + return Domain::Unspecified; case Network::Domain::INET: return Domain::INET; default: @@ -61,39 +107,69 @@ Domain Translate(Network::Domain domain) { Network::Type Translate(Type type) { switch (type) { + case Type::Unspecified: + return Network::Type::Unspecified; case Type::STREAM: return Network::Type::STREAM; case Type::DGRAM: return Network::Type::DGRAM; + case Type::RAW: + return Network::Type::RAW; + case Type::SEQPACKET: + return Network::Type::SEQPACKET; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return Network::Type{}; } } -Network::Protocol Translate(Type type, Protocol protocol) { +Type Translate(Network::Type type) { + switch (type) { + case Network::Type::Unspecified: + return Type::Unspecified; + case Network::Type::STREAM: + return Type::STREAM; + case Network::Type::DGRAM: + return Type::DGRAM; + case Network::Type::RAW: + return Type::RAW; + case Network::Type::SEQPACKET: + return Type::SEQPACKET; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return Type{}; + } +} + +Network::Protocol Translate(Protocol protocol) { switch (protocol) { - case Protocol::UNSPECIFIED: - LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type"); - switch (type) { - case Type::DGRAM: - return Network::Protocol::UDP; - case Type::STREAM: - return Network::Protocol::TCP; - default: - return Network::Protocol::TCP; - } + case Protocol::Unspecified: + return Network::Protocol::Unspecified; case Protocol::TCP: return Network::Protocol::TCP; case Protocol::UDP: return Network::Protocol::UDP; default: UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); - return Network::Protocol::TCP; + return Network::Protocol::Unspecified; + } +} + +Protocol Translate(Network::Protocol protocol) { + switch (protocol) { + case Network::Protocol::Unspecified: + return Protocol::Unspecified; + case Network::Protocol::TCP: + return Protocol::TCP; + case Network::Protocol::UDP: + return Protocol::UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return Protocol::Unspecified; } } -Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { +Network::PollEvents Translate(PollEvents flags) { Network::PollEvents result{}; const auto translate = [&result, &flags](PollEvents from, Network::PollEvents to) { if (True(flags & from)) { @@ -107,12 +183,15 @@ Network::PollEvents TranslatePollEventsToHost(PollEvents flags) { translate(PollEvents::Err, Network::PollEvents::Err); translate(PollEvents::Hup, Network::PollEvents::Hup); translate(PollEvents::Nval, Network::PollEvents::Nval); + translate(PollEvents::RdNorm, Network::PollEvents::RdNorm); + translate(PollEvents::RdBand, Network::PollEvents::RdBand); + translate(PollEvents::WrBand, Network::PollEvents::WrBand); UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); return result; } -PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { +PollEvents Translate(Network::PollEvents flags) { PollEvents result{}; const auto translate = [&result, &flags](Network::PollEvents from, PollEvents to) { if (True(flags & from)) { @@ -127,13 +206,18 @@ PollEvents TranslatePollEventsToGuest(Network::PollEvents flags) { translate(Network::PollEvents::Err, PollEvents::Err); translate(Network::PollEvents::Hup, PollEvents::Hup); translate(Network::PollEvents::Nval, PollEvents::Nval); + translate(Network::PollEvents::RdNorm, PollEvents::RdNorm); + translate(Network::PollEvents::RdBand, PollEvents::RdBand); + translate(Network::PollEvents::WrBand, PollEvents::WrBand); UNIMPLEMENTED_IF_MSG((u16)flags != 0, "Unimplemented flags={}", (u16)flags); return result; } Network::SockAddrIn Translate(SockAddrIn value) { - ASSERT(value.len == 0 || value.len == sizeof(value)); + // Note: 6 is incorrect, but can be passed by homebrew (because libnx sets + // sin_len to 6 when deserializing getaddrinfo results). + ASSERT(value.len == 0 || value.len == sizeof(value) || value.len == 6); return { .family = Translate(static_cast<Domain>(value.family)), diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h index c93291d3e..694868b37 100644 --- a/src/core/hle/service/sockets/sockets_translate.h +++ b/src/core/hle/service/sockets/sockets_translate.h @@ -17,6 +17,9 @@ Errno Translate(Network::Errno value); /// Translate abstract return value errno pair to guest return value errno pair std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value); +/// Translate abstract getaddrinfo error to guest getaddrinfo error +GetAddrInfoError Translate(Network::GetAddrInfoError value); + /// Translate guest domain to abstract domain Network::Domain Translate(Domain domain); @@ -26,14 +29,20 @@ Domain Translate(Network::Domain domain); /// Translate guest type to abstract type Network::Type Translate(Type type); +/// Translate abstract type to guest type +Type Translate(Network::Type type); + /// Translate guest protocol to abstract protocol -Network::Protocol Translate(Type type, Protocol protocol); +Network::Protocol Translate(Protocol protocol); -/// Translate abstract poll event flags to guest poll event flags -Network::PollEvents TranslatePollEventsToHost(PollEvents flags); +/// Translate abstract protocol to guest protocol +Protocol Translate(Network::Protocol protocol); /// Translate guest poll event flags to abstract poll event flags -PollEvents TranslatePollEventsToGuest(Network::PollEvents flags); +Network::PollEvents Translate(PollEvents flags); + +/// Translate abstract poll event flags to guest poll event flags +PollEvents Translate(Network::PollEvents flags); /// Translate guest socket address structure to abstract socket address structure Network::SockAddrIn Translate(SockAddrIn value); diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 2b99dd7ac..9c96f9763 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp @@ -1,10 +1,18 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/string_util.h" + +#include "core/core.h" #include "core/hle/service/ipc_helpers.h" #include "core/hle/service/server_manager.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sockets/bsd.h" #include "core/hle/service/ssl/ssl.h" +#include "core/hle/service/ssl/ssl_backend.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" namespace Service::SSL { @@ -20,6 +28,18 @@ enum class ContextOption : u32 { CrlImportDateCheckEnable = 1, }; +// This is nn::ssl::Connection::IoMode +enum class IoMode : u32 { + Blocking = 1, + NonBlocking = 2, +}; + +// This is nn::ssl::sf::OptionType +enum class OptionType : u32 { + DoNotCloseSocket = 0, + GetServerCertChain = 1, +}; + // This is nn::ssl::sf::SslVersion struct SslVersion { union { @@ -34,35 +54,42 @@ struct SslVersion { }; }; +struct SslContextSharedData { + u32 connection_count = 0; +}; + class ISslConnection final : public ServiceFramework<ISslConnection> { public: - explicit ISslConnection(Core::System& system_, SslVersion version) - : ServiceFramework{system_, "ISslConnection"}, ssl_version{version} { + explicit ISslConnection(Core::System& system_in, SslVersion ssl_version_in, + std::shared_ptr<SslContextSharedData>& shared_data_in, + std::unique_ptr<SSLConnectionBackend>&& backend_in) + : ServiceFramework{system_in, "ISslConnection"}, ssl_version{ssl_version_in}, + shared_data{shared_data_in}, backend{std::move(backend_in)} { // clang-format off static const FunctionInfo functions[] = { - {0, nullptr, "SetSocketDescriptor"}, - {1, nullptr, "SetHostName"}, - {2, nullptr, "SetVerifyOption"}, - {3, nullptr, "SetIoMode"}, + {0, &ISslConnection::SetSocketDescriptor, "SetSocketDescriptor"}, + {1, &ISslConnection::SetHostName, "SetHostName"}, + {2, &ISslConnection::SetVerifyOption, "SetVerifyOption"}, + {3, &ISslConnection::SetIoMode, "SetIoMode"}, {4, nullptr, "GetSocketDescriptor"}, {5, nullptr, "GetHostName"}, {6, nullptr, "GetVerifyOption"}, {7, nullptr, "GetIoMode"}, - {8, nullptr, "DoHandshake"}, - {9, nullptr, "DoHandshakeGetServerCert"}, - {10, nullptr, "Read"}, - {11, nullptr, "Write"}, - {12, nullptr, "Pending"}, + {8, &ISslConnection::DoHandshake, "DoHandshake"}, + {9, &ISslConnection::DoHandshakeGetServerCert, "DoHandshakeGetServerCert"}, + {10, &ISslConnection::Read, "Read"}, + {11, &ISslConnection::Write, "Write"}, + {12, &ISslConnection::Pending, "Pending"}, {13, nullptr, "Peek"}, {14, nullptr, "Poll"}, {15, nullptr, "GetVerifyCertError"}, {16, nullptr, "GetNeededServerCertBufferSize"}, - {17, nullptr, "SetSessionCacheMode"}, + {17, &ISslConnection::SetSessionCacheMode, "SetSessionCacheMode"}, {18, nullptr, "GetSessionCacheMode"}, {19, nullptr, "FlushSessionCache"}, {20, nullptr, "SetRenegotiationMode"}, {21, nullptr, "GetRenegotiationMode"}, - {22, nullptr, "SetOption"}, + {22, &ISslConnection::SetOption, "SetOption"}, {23, nullptr, "GetOption"}, {24, nullptr, "GetVerifyCertErrors"}, {25, nullptr, "GetCipherInfo"}, @@ -80,21 +107,299 @@ public: // clang-format on RegisterHandlers(functions); + + shared_data->connection_count++; + } + + ~ISslConnection() { + shared_data->connection_count--; + if (fd_to_close.has_value()) { + const s32 fd = *fd_to_close; + if (!do_not_close_socket) { + LOG_ERROR(Service_SSL, + "do_not_close_socket was changed after setting socket; is this right?"); + } else { + auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u"); + if (bsd) { + auto err = bsd->CloseImpl(fd); + if (err != Service::Sockets::Errno::SUCCESS) { + LOG_ERROR(Service_SSL, "Failed to close duplicated socket: {}", err); + } + } + } + } } private: SslVersion ssl_version; + std::shared_ptr<SslContextSharedData> shared_data; + std::unique_ptr<SSLConnectionBackend> backend; + std::optional<int> fd_to_close; + bool do_not_close_socket = false; + bool get_server_cert_chain = false; + std::shared_ptr<Network::SocketBase> socket; + bool did_set_host_name = false; + bool did_handshake = false; + + ResultVal<s32> SetSocketDescriptorImpl(s32 fd) { + LOG_DEBUG(Service_SSL, "called, fd={}", fd); + ASSERT(!did_handshake); + auto bsd = system.ServiceManager().GetService<Service::Sockets::BSD>("bsd:u"); + ASSERT_OR_EXECUTE(bsd, { return ResultInternalError; }); + s32 ret_fd; + // Based on https://switchbrew.org/wiki/SSL_services#SetSocketDescriptor + if (do_not_close_socket) { + auto res = bsd->DuplicateSocketImpl(fd); + if (!res.has_value()) { + LOG_ERROR(Service_SSL, "Failed to duplicate socket with fd {}", fd); + return ResultInvalidSocket; + } + fd = *res; + fd_to_close = fd; + ret_fd = fd; + } else { + ret_fd = -1; + } + std::optional<std::shared_ptr<Network::SocketBase>> sock = bsd->GetSocket(fd); + if (!sock.has_value()) { + LOG_ERROR(Service_SSL, "invalid socket fd {}", fd); + return ResultInvalidSocket; + } + socket = std::move(*sock); + backend->SetSocket(socket); + return ret_fd; + } + + Result SetHostNameImpl(const std::string& hostname) { + LOG_DEBUG(Service_SSL, "called. hostname={}", hostname); + ASSERT(!did_handshake); + Result res = backend->SetHostName(hostname); + if (res == ResultSuccess) { + did_set_host_name = true; + } + return res; + } + + Result SetVerifyOptionImpl(u32 option) { + ASSERT(!did_handshake); + LOG_WARNING(Service_SSL, "(STUBBED) called. option={}", option); + return ResultSuccess; + } + + Result SetIoModeImpl(u32 input_mode) { + auto mode = static_cast<IoMode>(input_mode); + ASSERT(mode == IoMode::Blocking || mode == IoMode::NonBlocking); + ASSERT_OR_EXECUTE(socket, { return ResultNoSocket; }); + + const bool non_block = mode == IoMode::NonBlocking; + const Network::Errno error = socket->SetNonBlock(non_block); + if (error != Network::Errno::SUCCESS) { + LOG_ERROR(Service_SSL, "Failed to set native socket non-block flag to {}", non_block); + } + return ResultSuccess; + } + + Result SetSessionCacheModeImpl(u32 mode) { + ASSERT(!did_handshake); + LOG_WARNING(Service_SSL, "(STUBBED) called. value={}", mode); + return ResultSuccess; + } + + Result DoHandshakeImpl() { + ASSERT_OR_EXECUTE(!did_handshake && socket, { return ResultNoSocket; }); + ASSERT_OR_EXECUTE_MSG( + did_set_host_name, { return ResultInternalError; }, + "Expected SetHostName before DoHandshake"); + Result res = backend->DoHandshake(); + did_handshake = res.IsSuccess(); + return res; + } + + std::vector<u8> SerializeServerCerts(const std::vector<std::vector<u8>>& certs) { + struct Header { + u64 magic; + u32 count; + u32 pad; + }; + struct EntryHeader { + u32 size; + u32 offset; + }; + if (!get_server_cert_chain) { + // Just return the first one, unencoded. + ASSERT_OR_EXECUTE_MSG( + !certs.empty(), { return {}; }, "Should be at least one server cert"); + return certs[0]; + } + std::vector<u8> ret; + Header header{0x4E4D684374726543, static_cast<u32>(certs.size()), 0}; + ret.insert(ret.end(), reinterpret_cast<u8*>(&header), reinterpret_cast<u8*>(&header + 1)); + size_t data_offset = sizeof(Header) + certs.size() * sizeof(EntryHeader); + for (auto& cert : certs) { + EntryHeader entry_header{static_cast<u32>(cert.size()), static_cast<u32>(data_offset)}; + data_offset += cert.size(); + ret.insert(ret.end(), reinterpret_cast<u8*>(&entry_header), + reinterpret_cast<u8*>(&entry_header + 1)); + } + for (auto& cert : certs) { + ret.insert(ret.end(), cert.begin(), cert.end()); + } + return ret; + } + + ResultVal<std::vector<u8>> ReadImpl(size_t size) { + ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); + std::vector<u8> res(size); + ResultVal<size_t> actual = backend->Read(res); + if (actual.Failed()) { + return actual.Code(); + } + res.resize(*actual); + return res; + } + + ResultVal<size_t> WriteImpl(std::span<const u8> data) { + ASSERT_OR_EXECUTE(did_handshake, { return ResultInternalError; }); + return backend->Write(data); + } + + ResultVal<s32> PendingImpl() { + LOG_WARNING(Service_SSL, "(STUBBED) called."); + return 0; + } + + void SetSocketDescriptor(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s32 fd = rp.Pop<s32>(); + const ResultVal<s32> res = SetSocketDescriptorImpl(fd); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res.Code()); + rb.Push<s32>(res.ValueOr(-1)); + } + + void SetHostName(HLERequestContext& ctx) { + const std::string hostname = Common::StringFromBuffer(ctx.ReadBuffer()); + const Result res = SetHostNameImpl(hostname); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } + + void SetVerifyOption(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 option = rp.Pop<u32>(); + const Result res = SetVerifyOptionImpl(option); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } + + void SetIoMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 mode = rp.Pop<u32>(); + const Result res = SetIoModeImpl(mode); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } + + void DoHandshake(HLERequestContext& ctx) { + const Result res = DoHandshakeImpl(); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } + + void DoHandshakeGetServerCert(HLERequestContext& ctx) { + struct OutputParameters { + u32 certs_size; + u32 certs_count; + }; + static_assert(sizeof(OutputParameters) == 0x8); + + const Result res = DoHandshakeImpl(); + OutputParameters out{}; + if (res == ResultSuccess) { + auto certs = backend->GetServerCerts(); + if (certs.Succeeded()) { + const std::vector<u8> certs_buf = SerializeServerCerts(*certs); + ctx.WriteBuffer(certs_buf); + out.certs_count = static_cast<u32>(certs->size()); + out.certs_size = static_cast<u32>(certs_buf.size()); + } + } + IPC::ResponseBuilder rb{ctx, 4}; + rb.Push(res); + rb.PushRaw(out); + } + + void Read(HLERequestContext& ctx) { + const ResultVal<std::vector<u8>> res = ReadImpl(ctx.GetWriteBufferSize()); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res.Code()); + if (res.Succeeded()) { + rb.Push(static_cast<u32>(res->size())); + ctx.WriteBuffer(*res); + } else { + rb.Push(static_cast<u32>(0)); + } + } + + void Write(HLERequestContext& ctx) { + const ResultVal<size_t> res = WriteImpl(ctx.ReadBuffer()); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res.Code()); + rb.Push(static_cast<u32>(res.ValueOr(0))); + } + + void Pending(HLERequestContext& ctx) { + const ResultVal<s32> res = PendingImpl(); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res.Code()); + rb.Push<s32>(res.ValueOr(0)); + } + + void SetSessionCacheMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const u32 mode = rp.Pop<u32>(); + const Result res = SetSessionCacheModeImpl(mode); + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); + } + + void SetOption(HLERequestContext& ctx) { + struct Parameters { + OptionType option; + s32 value; + }; + static_assert(sizeof(Parameters) == 0x8, "Parameters is an invalid size"); + + IPC::RequestParser rp{ctx}; + const auto parameters = rp.PopRaw<Parameters>(); + + switch (parameters.option) { + case OptionType::DoNotCloseSocket: + do_not_close_socket = static_cast<bool>(parameters.value); + break; + case OptionType::GetServerCertChain: + get_server_cert_chain = static_cast<bool>(parameters.value); + break; + default: + LOG_WARNING(Service_SSL, "Unknown option={}, value={}", parameters.option, + parameters.value); + } + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(ResultSuccess); + } }; class ISslContext final : public ServiceFramework<ISslContext> { public: explicit ISslContext(Core::System& system_, SslVersion version) - : ServiceFramework{system_, "ISslContext"}, ssl_version{version} { + : ServiceFramework{system_, "ISslContext"}, ssl_version{version}, + shared_data{std::make_shared<SslContextSharedData>()} { static const FunctionInfo functions[] = { {0, &ISslContext::SetOption, "SetOption"}, {1, nullptr, "GetOption"}, {2, &ISslContext::CreateConnection, "CreateConnection"}, - {3, nullptr, "GetConnectionCount"}, + {3, &ISslContext::GetConnectionCount, "GetConnectionCount"}, {4, &ISslContext::ImportServerPki, "ImportServerPki"}, {5, &ISslContext::ImportClientPki, "ImportClientPki"}, {6, nullptr, "RemoveServerPki"}, @@ -111,6 +416,7 @@ public: private: SslVersion ssl_version; + std::shared_ptr<SslContextSharedData> shared_data; void SetOption(HLERequestContext& ctx) { struct Parameters { @@ -130,11 +436,24 @@ private: } void CreateConnection(HLERequestContext& ctx) { - LOG_WARNING(Service_SSL, "(STUBBED) called"); + LOG_WARNING(Service_SSL, "called"); + + auto backend_res = CreateSSLConnectionBackend(); IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(backend_res.Code()); + if (backend_res.Succeeded()) { + rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data, + std::move(*backend_res)); + } + } + + void GetConnectionCount(HLERequestContext& ctx) { + LOG_DEBUG(Service_SSL, "connection_count={}", shared_data->connection_count); + + IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushIpcInterface<ISslConnection>(system, ssl_version); + rb.Push(shared_data->connection_count); } void ImportServerPki(HLERequestContext& ctx) { diff --git a/src/core/hle/service/ssl/ssl_backend.h b/src/core/hle/service/ssl/ssl_backend.h new file mode 100644 index 000000000..409f4367c --- /dev/null +++ b/src/core/hle/service/ssl/ssl_backend.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <span> +#include <string> +#include <vector> + +#include "common/common_types.h" + +#include "core/hle/result.h" + +namespace Network { +class SocketBase; +} + +namespace Service::SSL { + +constexpr Result ResultNoSocket{ErrorModule::SSLSrv, 103}; +constexpr Result ResultInvalidSocket{ErrorModule::SSLSrv, 106}; +constexpr Result ResultTimeout{ErrorModule::SSLSrv, 205}; +constexpr Result ResultInternalError{ErrorModule::SSLSrv, 999}; // made up + +// ResultWouldBlock is returned from Read and Write, and oddly, DoHandshake, +// with no way in the latter case to distinguish whether the client should poll +// for read or write. The one official client I've seen handles this by always +// polling for read (with a timeout). +constexpr Result ResultWouldBlock{ErrorModule::SSLSrv, 204}; + +class SSLConnectionBackend { +public: + virtual ~SSLConnectionBackend() {} + virtual void SetSocket(std::shared_ptr<Network::SocketBase> socket) = 0; + virtual Result SetHostName(const std::string& hostname) = 0; + virtual Result DoHandshake() = 0; + virtual ResultVal<size_t> Read(std::span<u8> data) = 0; + virtual ResultVal<size_t> Write(std::span<const u8> data) = 0; + virtual ResultVal<std::vector<std::vector<u8>>> GetServerCerts() = 0; +}; + +ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend(); + +} // namespace Service::SSL diff --git a/src/core/hle/service/ssl/ssl_backend_none.cpp b/src/core/hle/service/ssl/ssl_backend_none.cpp new file mode 100644 index 000000000..2f4f23c42 --- /dev/null +++ b/src/core/hle/service/ssl/ssl_backend_none.cpp @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" + +#include "core/hle/service/ssl/ssl_backend.h" + +namespace Service::SSL { + +ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { + LOG_ERROR(Service_SSL, + "Can't create SSL connection because no SSL backend is available on this platform"); + return ResultInternalError; +} + +} // namespace Service::SSL diff --git a/src/core/hle/service/ssl/ssl_backend_openssl.cpp b/src/core/hle/service/ssl/ssl_backend_openssl.cpp new file mode 100644 index 000000000..6ca869dbf --- /dev/null +++ b/src/core/hle/service/ssl/ssl_backend_openssl.cpp @@ -0,0 +1,351 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <mutex> + +#include <openssl/bio.h> +#include <openssl/err.h> +#include <openssl/ssl.h> +#include <openssl/x509.h> + +#include "common/fs/file.h" +#include "common/hex_util.h" +#include "common/string_util.h" + +#include "core/hle/service/ssl/ssl_backend.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" + +using namespace Common::FS; + +namespace Service::SSL { + +// Import OpenSSL's `SSL` type into the namespace. This is needed because the +// namespace is also named `SSL`. +using ::SSL; + +namespace { + +std::once_flag one_time_init_flag; +bool one_time_init_success = false; + +SSL_CTX* ssl_ctx; +IOFile key_log_file; // only open if SSLKEYLOGFILE set in environment +BIO_METHOD* bio_meth; + +Result CheckOpenSSLErrors(); +void OneTimeInit(); +void OneTimeInitLogFile(); +bool OneTimeInitBIO(); + +} // namespace + +class SSLConnectionBackendOpenSSL final : public SSLConnectionBackend { +public: + Result Init() { + std::call_once(one_time_init_flag, OneTimeInit); + + if (!one_time_init_success) { + LOG_ERROR(Service_SSL, + "Can't create SSL connection because OpenSSL one-time initialization failed"); + return ResultInternalError; + } + + ssl = SSL_new(ssl_ctx); + if (!ssl) { + LOG_ERROR(Service_SSL, "SSL_new failed"); + return CheckOpenSSLErrors(); + } + + SSL_set_connect_state(ssl); + + bio = BIO_new(bio_meth); + if (!bio) { + LOG_ERROR(Service_SSL, "BIO_new failed"); + return CheckOpenSSLErrors(); + } + + BIO_set_data(bio, this); + BIO_set_init(bio, 1); + SSL_set_bio(ssl, bio, bio); + + return ResultSuccess; + } + + void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override { + socket = std::move(socket_in); + } + + Result SetHostName(const std::string& hostname) override { + if (!SSL_set1_host(ssl, hostname.c_str())) { // hostname for verification + LOG_ERROR(Service_SSL, "SSL_set1_host({}) failed", hostname); + return CheckOpenSSLErrors(); + } + if (!SSL_set_tlsext_host_name(ssl, hostname.c_str())) { // hostname for SNI + LOG_ERROR(Service_SSL, "SSL_set_tlsext_host_name({}) failed", hostname); + return CheckOpenSSLErrors(); + } + return ResultSuccess; + } + + Result DoHandshake() override { + SSL_set_verify_result(ssl, X509_V_OK); + const int ret = SSL_do_handshake(ssl); + const long verify_result = SSL_get_verify_result(ssl); + if (verify_result != X509_V_OK) { + LOG_ERROR(Service_SSL, "SSL cert verification failed because: {}", + X509_verify_cert_error_string(verify_result)); + return CheckOpenSSLErrors(); + } + if (ret <= 0) { + const int ssl_err = SSL_get_error(ssl, ret); + if (ssl_err == SSL_ERROR_ZERO_RETURN || + (ssl_err == SSL_ERROR_SYSCALL && got_read_eof)) { + LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up"); + return ResultInternalError; + } + } + return HandleReturn("SSL_do_handshake", 0, ret).Code(); + } + + ResultVal<size_t> Read(std::span<u8> data) override { + size_t actual; + const int ret = SSL_read_ex(ssl, data.data(), data.size(), &actual); + return HandleReturn("SSL_read_ex", actual, ret); + } + + ResultVal<size_t> Write(std::span<const u8> data) override { + size_t actual; + const int ret = SSL_write_ex(ssl, data.data(), data.size(), &actual); + return HandleReturn("SSL_write_ex", actual, ret); + } + + ResultVal<size_t> HandleReturn(const char* what, size_t actual, int ret) { + const int ssl_err = SSL_get_error(ssl, ret); + CheckOpenSSLErrors(); + switch (ssl_err) { + case SSL_ERROR_NONE: + return actual; + case SSL_ERROR_ZERO_RETURN: + LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_ZERO_RETURN", what); + // DoHandshake special-cases this, but for Read and Write: + return size_t(0); + case SSL_ERROR_WANT_READ: + LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_READ", what); + return ResultWouldBlock; + case SSL_ERROR_WANT_WRITE: + LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_WANT_WRITE", what); + return ResultWouldBlock; + default: + if (ssl_err == SSL_ERROR_SYSCALL && got_read_eof) { + LOG_DEBUG(Service_SSL, "{} => SSL_ERROR_SYSCALL because server hung up", what); + return size_t(0); + } + LOG_ERROR(Service_SSL, "{} => other SSL_get_error return value {}", what, ssl_err); + return ResultInternalError; + } + } + + ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { + STACK_OF(X509)* chain = SSL_get_peer_cert_chain(ssl); + if (!chain) { + LOG_ERROR(Service_SSL, "SSL_get_peer_cert_chain returned nullptr"); + return ResultInternalError; + } + std::vector<std::vector<u8>> ret; + int count = sk_X509_num(chain); + ASSERT(count >= 0); + for (int i = 0; i < count; i++) { + X509* x509 = sk_X509_value(chain, i); + ASSERT_OR_EXECUTE(x509 != nullptr, { continue; }); + unsigned char* buf = nullptr; + int len = i2d_X509(x509, &buf); + ASSERT_OR_EXECUTE(len >= 0 && buf, { continue; }); + ret.emplace_back(buf, buf + len); + OPENSSL_free(buf); + } + return ret; + } + + ~SSLConnectionBackendOpenSSL() { + // these are null-tolerant: + SSL_free(ssl); + BIO_free(bio); + } + + static void KeyLogCallback(const SSL* ssl, const char* line) { + std::string str(line); + str.push_back('\n'); + // Do this in a single WriteString for atomicity if multiple instances + // are running on different threads (though that can't currently + // happen). + if (key_log_file.WriteString(str) != str.size() || !key_log_file.Flush()) { + LOG_CRITICAL(Service_SSL, "Failed to write to SSLKEYLOGFILE"); + } + LOG_DEBUG(Service_SSL, "Wrote to SSLKEYLOGFILE: {}", line); + } + + static int WriteCallback(BIO* bio, const char* buf, size_t len, size_t* actual_p) { + auto self = static_cast<SSLConnectionBackendOpenSSL*>(BIO_get_data(bio)); + ASSERT_OR_EXECUTE_MSG( + self->socket, { return 0; }, "OpenSSL asked to send but we have no socket"); + BIO_clear_retry_flags(bio); + auto [actual, err] = self->socket->Send({reinterpret_cast<const u8*>(buf), len}, 0); + switch (err) { + case Network::Errno::SUCCESS: + *actual_p = actual; + return 1; + case Network::Errno::AGAIN: + BIO_set_flags(bio, BIO_FLAGS_WRITE | BIO_FLAGS_SHOULD_RETRY); + return 0; + default: + LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err); + return -1; + } + } + + static int ReadCallback(BIO* bio, char* buf, size_t len, size_t* actual_p) { + auto self = static_cast<SSLConnectionBackendOpenSSL*>(BIO_get_data(bio)); + ASSERT_OR_EXECUTE_MSG( + self->socket, { return 0; }, "OpenSSL asked to recv but we have no socket"); + BIO_clear_retry_flags(bio); + auto [actual, err] = self->socket->Recv(0, {reinterpret_cast<u8*>(buf), len}); + switch (err) { + case Network::Errno::SUCCESS: + *actual_p = actual; + if (actual == 0) { + self->got_read_eof = true; + } + return actual ? 1 : 0; + case Network::Errno::AGAIN: + BIO_set_flags(bio, BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY); + return 0; + default: + LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err); + return -1; + } + } + + static long CtrlCallback(BIO* bio, int cmd, long l_arg, void* p_arg) { + switch (cmd) { + case BIO_CTRL_FLUSH: + // Nothing to flush. + return 1; + case BIO_CTRL_PUSH: + case BIO_CTRL_POP: +#ifdef BIO_CTRL_GET_KTLS_SEND + case BIO_CTRL_GET_KTLS_SEND: + case BIO_CTRL_GET_KTLS_RECV: +#endif + // We don't support these operations, but don't bother logging them + // as they're nothing unusual. + return 0; + default: + LOG_DEBUG(Service_SSL, "OpenSSL BIO got ctrl({}, {}, {})", cmd, l_arg, p_arg); + return 0; + } + } + + SSL* ssl = nullptr; + BIO* bio = nullptr; + bool got_read_eof = false; + + std::shared_ptr<Network::SocketBase> socket; +}; + +ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { + auto conn = std::make_unique<SSLConnectionBackendOpenSSL>(); + const Result res = conn->Init(); + if (res.IsFailure()) { + return res; + } + return conn; +} + +namespace { + +Result CheckOpenSSLErrors() { + unsigned long rc; + const char* file; + int line; + const char* func; + const char* data; + int flags; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + while ((rc = ERR_get_error_all(&file, &line, &func, &data, &flags))) +#else + // Can't get function names from OpenSSL on this version, so use mine: + func = __func__; + while ((rc = ERR_get_error_line_data(&file, &line, &data, &flags))) +#endif + { + std::string msg; + msg.resize(1024, '\0'); + ERR_error_string_n(rc, msg.data(), msg.size()); + msg.resize(strlen(msg.data()), '\0'); + if (flags & ERR_TXT_STRING) { + msg.append(" | "); + msg.append(data); + } + Common::Log::FmtLogMessage(Common::Log::Class::Service_SSL, Common::Log::Level::Error, + Common::Log::TrimSourcePath(file), line, func, "OpenSSL: {}", + msg); + } + return ResultInternalError; +} + +void OneTimeInit() { + ssl_ctx = SSL_CTX_new(TLS_client_method()); + if (!ssl_ctx) { + LOG_ERROR(Service_SSL, "SSL_CTX_new failed"); + CheckOpenSSLErrors(); + return; + } + + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, nullptr); + + if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) { + LOG_ERROR(Service_SSL, "SSL_CTX_set_default_verify_paths failed"); + CheckOpenSSLErrors(); + return; + } + + OneTimeInitLogFile(); + + if (!OneTimeInitBIO()) { + return; + } + + one_time_init_success = true; +} + +void OneTimeInitLogFile() { + const char* logfile = getenv("SSLKEYLOGFILE"); + if (logfile) { + key_log_file.Open(logfile, FileAccessMode::Append, FileType::TextFile, + FileShareFlag::ShareWriteOnly); + if (key_log_file.IsOpen()) { + SSL_CTX_set_keylog_callback(ssl_ctx, &SSLConnectionBackendOpenSSL::KeyLogCallback); + } else { + LOG_CRITICAL(Service_SSL, + "SSLKEYLOGFILE was set but file could not be opened; not logging keys!"); + } + } +} + +bool OneTimeInitBIO() { + bio_meth = + BIO_meth_new(BIO_get_new_index() | BIO_TYPE_SOURCE_SINK, "SSLConnectionBackendOpenSSL"); + if (!bio_meth || + !BIO_meth_set_write_ex(bio_meth, &SSLConnectionBackendOpenSSL::WriteCallback) || + !BIO_meth_set_read_ex(bio_meth, &SSLConnectionBackendOpenSSL::ReadCallback) || + !BIO_meth_set_ctrl(bio_meth, &SSLConnectionBackendOpenSSL::CtrlCallback)) { + LOG_ERROR(Service_SSL, "Failed to create BIO_METHOD"); + return false; + } + return true; +} + +} // namespace + +} // namespace Service::SSL diff --git a/src/core/hle/service/ssl/ssl_backend_schannel.cpp b/src/core/hle/service/ssl/ssl_backend_schannel.cpp new file mode 100644 index 000000000..d8074339a --- /dev/null +++ b/src/core/hle/service/ssl/ssl_backend_schannel.cpp @@ -0,0 +1,544 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <mutex> + +#include "common/error.h" +#include "common/fs/file.h" +#include "common/hex_util.h" +#include "common/string_util.h" + +#include "core/hle/service/ssl/ssl_backend.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" + +namespace { + +// These includes are inside the namespace to avoid a conflict on MinGW where +// the headers define an enum containing Network and Service as enumerators +// (which clash with the correspondingly named namespaces). +#define SECURITY_WIN32 +#include <schnlsp.h> +#include <security.h> +#include <wincrypt.h> + +std::once_flag one_time_init_flag; +bool one_time_init_success = false; + +SCHANNEL_CRED schannel_cred{}; +CredHandle cred_handle; + +static void OneTimeInit() { + schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + schannel_cred.dwFlags = + SCH_USE_STRONG_CRYPTO | // don't allow insecure protocols + SCH_CRED_AUTO_CRED_VALIDATION | // validate certs + SCH_CRED_NO_DEFAULT_CREDS; // don't automatically present a client certificate + // ^ I'm assuming that nobody would want to connect Yuzu to a + // service that requires some OS-provided corporate client + // certificate, and presenting one to some arbitrary server + // might be a privacy concern? Who knows, though. + + const SECURITY_STATUS ret = + AcquireCredentialsHandle(nullptr, const_cast<LPTSTR>(UNISP_NAME), SECPKG_CRED_OUTBOUND, + nullptr, &schannel_cred, nullptr, nullptr, &cred_handle, nullptr); + if (ret != SEC_E_OK) { + // SECURITY_STATUS codes are a type of HRESULT and can be used with NativeErrorToString. + LOG_ERROR(Service_SSL, "AcquireCredentialsHandle failed: {}", + Common::NativeErrorToString(ret)); + return; + } + + if (getenv("SSLKEYLOGFILE")) { + LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but Schannel does not support exporting " + "keys; not logging keys!"); + // Not fatal. + } + + one_time_init_success = true; +} + +} // namespace + +namespace Service::SSL { + +class SSLConnectionBackendSchannel final : public SSLConnectionBackend { +public: + Result Init() { + std::call_once(one_time_init_flag, OneTimeInit); + + if (!one_time_init_success) { + LOG_ERROR( + Service_SSL, + "Can't create SSL connection because Schannel one-time initialization failed"); + return ResultInternalError; + } + + return ResultSuccess; + } + + void SetSocket(std::shared_ptr<Network::SocketBase> socket_in) override { + socket = std::move(socket_in); + } + + Result SetHostName(const std::string& hostname_in) override { + hostname = hostname_in; + return ResultSuccess; + } + + Result DoHandshake() override { + while (1) { + Result r; + switch (handshake_state) { + case HandshakeState::Initial: + if ((r = FlushCiphertextWriteBuf()) != ResultSuccess || + (r = CallInitializeSecurityContext()) != ResultSuccess) { + return r; + } + // CallInitializeSecurityContext updated `handshake_state`. + continue; + case HandshakeState::ContinueNeeded: + case HandshakeState::IncompleteMessage: + if ((r = FlushCiphertextWriteBuf()) != ResultSuccess || + (r = FillCiphertextReadBuf()) != ResultSuccess) { + return r; + } + if (ciphertext_read_buf.empty()) { + LOG_ERROR(Service_SSL, "SSL handshake failed because server hung up"); + return ResultInternalError; + } + if ((r = CallInitializeSecurityContext()) != ResultSuccess) { + return r; + } + // CallInitializeSecurityContext updated `handshake_state`. + continue; + case HandshakeState::DoneAfterFlush: + if ((r = FlushCiphertextWriteBuf()) != ResultSuccess) { + return r; + } + handshake_state = HandshakeState::Connected; + return ResultSuccess; + case HandshakeState::Connected: + LOG_ERROR(Service_SSL, "Called DoHandshake but we already handshook"); + return ResultInternalError; + case HandshakeState::Error: + return ResultInternalError; + } + } + } + + Result FillCiphertextReadBuf() { + const size_t fill_size = read_buf_fill_size ? read_buf_fill_size : 4096; + read_buf_fill_size = 0; + // This unnecessarily zeroes the buffer; oh well. + const size_t offset = ciphertext_read_buf.size(); + ASSERT_OR_EXECUTE(offset + fill_size >= offset, { return ResultInternalError; }); + ciphertext_read_buf.resize(offset + fill_size, 0); + const auto read_span = std::span(ciphertext_read_buf).subspan(offset, fill_size); + const auto [actual, err] = socket->Recv(0, read_span); + switch (err) { + case Network::Errno::SUCCESS: + ASSERT(static_cast<size_t>(actual) <= fill_size); + ciphertext_read_buf.resize(offset + actual); + return ResultSuccess; + case Network::Errno::AGAIN: + ciphertext_read_buf.resize(offset); + return ResultWouldBlock; + default: + ciphertext_read_buf.resize(offset); + LOG_ERROR(Service_SSL, "Socket recv returned Network::Errno {}", err); + return ResultInternalError; + } + } + + // Returns success if the write buffer has been completely emptied. + Result FlushCiphertextWriteBuf() { + while (!ciphertext_write_buf.empty()) { + const auto [actual, err] = socket->Send(ciphertext_write_buf, 0); + switch (err) { + case Network::Errno::SUCCESS: + ASSERT(static_cast<size_t>(actual) <= ciphertext_write_buf.size()); + ciphertext_write_buf.erase(ciphertext_write_buf.begin(), + ciphertext_write_buf.begin() + actual); + break; + case Network::Errno::AGAIN: + return ResultWouldBlock; + default: + LOG_ERROR(Service_SSL, "Socket send returned Network::Errno {}", err); + return ResultInternalError; + } + } + return ResultSuccess; + } + + Result CallInitializeSecurityContext() { + const unsigned long req = ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_CONFIDENTIALITY | + ISC_REQ_INTEGRITY | ISC_REQ_REPLAY_DETECT | + ISC_REQ_SEQUENCE_DETECT | ISC_REQ_STREAM | + ISC_REQ_USE_SUPPLIED_CREDS; + unsigned long attr; + // https://learn.microsoft.com/en-us/windows/win32/secauthn/initializesecuritycontext--schannel + std::array<SecBuffer, 2> input_buffers{{ + // only used if `initial_call_done` + { + // [0] + .cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()), + .BufferType = SECBUFFER_TOKEN, + .pvBuffer = ciphertext_read_buf.data(), + }, + { + // [1] (will be replaced by SECBUFFER_MISSING when SEC_E_INCOMPLETE_MESSAGE is + // returned, or SECBUFFER_EXTRA when SEC_E_CONTINUE_NEEDED is returned if the + // whole buffer wasn't used) + .cbBuffer = 0, + .BufferType = SECBUFFER_EMPTY, + .pvBuffer = nullptr, + }, + }}; + std::array<SecBuffer, 2> output_buffers{{ + { + .cbBuffer = 0, + .BufferType = SECBUFFER_TOKEN, + .pvBuffer = nullptr, + }, // [0] + { + .cbBuffer = 0, + .BufferType = SECBUFFER_ALERT, + .pvBuffer = nullptr, + }, // [1] + }}; + SecBufferDesc input_desc{ + .ulVersion = SECBUFFER_VERSION, + .cBuffers = static_cast<unsigned long>(input_buffers.size()), + .pBuffers = input_buffers.data(), + }; + SecBufferDesc output_desc{ + .ulVersion = SECBUFFER_VERSION, + .cBuffers = static_cast<unsigned long>(output_buffers.size()), + .pBuffers = output_buffers.data(), + }; + ASSERT_OR_EXECUTE_MSG( + input_buffers[0].cbBuffer == ciphertext_read_buf.size(), + { return ResultInternalError; }, "read buffer too large"); + + bool initial_call_done = handshake_state != HandshakeState::Initial; + if (initial_call_done) { + LOG_DEBUG(Service_SSL, "Passing {} bytes into InitializeSecurityContext", + ciphertext_read_buf.size()); + } + + const SECURITY_STATUS ret = + InitializeSecurityContextA(&cred_handle, initial_call_done ? &ctxt : nullptr, + // Caller ensured we have set a hostname: + const_cast<char*>(hostname.value().c_str()), req, + 0, // Reserved1 + 0, // TargetDataRep not used with Schannel + initial_call_done ? &input_desc : nullptr, + 0, // Reserved2 + initial_call_done ? nullptr : &ctxt, &output_desc, &attr, + nullptr); // ptsExpiry + + if (output_buffers[0].pvBuffer) { + const std::span span(static_cast<u8*>(output_buffers[0].pvBuffer), + output_buffers[0].cbBuffer); + ciphertext_write_buf.insert(ciphertext_write_buf.end(), span.begin(), span.end()); + FreeContextBuffer(output_buffers[0].pvBuffer); + } + + if (output_buffers[1].pvBuffer) { + const std::span span(static_cast<u8*>(output_buffers[1].pvBuffer), + output_buffers[1].cbBuffer); + // The documentation doesn't explain what format this data is in. + LOG_DEBUG(Service_SSL, "Got a {}-byte alert buffer: {}", span.size(), + Common::HexToString(span)); + } + + switch (ret) { + case SEC_I_CONTINUE_NEEDED: + LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_I_CONTINUE_NEEDED"); + if (input_buffers[1].BufferType == SECBUFFER_EXTRA) { + LOG_DEBUG(Service_SSL, "EXTRA of size {}", input_buffers[1].cbBuffer); + ASSERT(input_buffers[1].cbBuffer <= ciphertext_read_buf.size()); + ciphertext_read_buf.erase(ciphertext_read_buf.begin(), + ciphertext_read_buf.end() - input_buffers[1].cbBuffer); + } else { + ASSERT(input_buffers[1].BufferType == SECBUFFER_EMPTY); + ciphertext_read_buf.clear(); + } + handshake_state = HandshakeState::ContinueNeeded; + return ResultSuccess; + case SEC_E_INCOMPLETE_MESSAGE: + LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_INCOMPLETE_MESSAGE"); + ASSERT(input_buffers[1].BufferType == SECBUFFER_MISSING); + read_buf_fill_size = input_buffers[1].cbBuffer; + handshake_state = HandshakeState::IncompleteMessage; + return ResultSuccess; + case SEC_E_OK: + LOG_DEBUG(Service_SSL, "InitializeSecurityContext => SEC_E_OK"); + ciphertext_read_buf.clear(); + handshake_state = HandshakeState::DoneAfterFlush; + return GrabStreamSizes(); + default: + LOG_ERROR(Service_SSL, + "InitializeSecurityContext failed (probably certificate/protocol issue): {}", + Common::NativeErrorToString(ret)); + handshake_state = HandshakeState::Error; + return ResultInternalError; + } + } + + Result GrabStreamSizes() { + const SECURITY_STATUS ret = + QueryContextAttributes(&ctxt, SECPKG_ATTR_STREAM_SIZES, &stream_sizes); + if (ret != SEC_E_OK) { + LOG_ERROR(Service_SSL, "QueryContextAttributes(SECPKG_ATTR_STREAM_SIZES) failed: {}", + Common::NativeErrorToString(ret)); + handshake_state = HandshakeState::Error; + return ResultInternalError; + } + return ResultSuccess; + } + + ResultVal<size_t> Read(std::span<u8> data) override { + if (handshake_state != HandshakeState::Connected) { + LOG_ERROR(Service_SSL, "Called Read but we did not successfully handshake"); + return ResultInternalError; + } + if (data.size() == 0 || got_read_eof) { + return size_t(0); + } + while (1) { + if (!cleartext_read_buf.empty()) { + const size_t read_size = std::min(cleartext_read_buf.size(), data.size()); + std::memcpy(data.data(), cleartext_read_buf.data(), read_size); + cleartext_read_buf.erase(cleartext_read_buf.begin(), + cleartext_read_buf.begin() + read_size); + return read_size; + } + if (!ciphertext_read_buf.empty()) { + SecBuffer empty{ + .cbBuffer = 0, + .BufferType = SECBUFFER_EMPTY, + .pvBuffer = nullptr, + }; + std::array<SecBuffer, 5> buffers{{ + { + .cbBuffer = static_cast<unsigned long>(ciphertext_read_buf.size()), + .BufferType = SECBUFFER_DATA, + .pvBuffer = ciphertext_read_buf.data(), + }, + empty, + empty, + empty, + }}; + ASSERT_OR_EXECUTE_MSG( + buffers[0].cbBuffer == ciphertext_read_buf.size(), + { return ResultInternalError; }, "read buffer too large"); + SecBufferDesc desc{ + .ulVersion = SECBUFFER_VERSION, + .cBuffers = static_cast<unsigned long>(buffers.size()), + .pBuffers = buffers.data(), + }; + SECURITY_STATUS ret = + DecryptMessage(&ctxt, &desc, /*MessageSeqNo*/ 0, /*pfQOP*/ nullptr); + switch (ret) { + case SEC_E_OK: + ASSERT_OR_EXECUTE(buffers[0].BufferType == SECBUFFER_STREAM_HEADER, + { return ResultInternalError; }); + ASSERT_OR_EXECUTE(buffers[1].BufferType == SECBUFFER_DATA, + { return ResultInternalError; }); + ASSERT_OR_EXECUTE(buffers[2].BufferType == SECBUFFER_STREAM_TRAILER, + { return ResultInternalError; }); + cleartext_read_buf.assign(static_cast<u8*>(buffers[1].pvBuffer), + static_cast<u8*>(buffers[1].pvBuffer) + + buffers[1].cbBuffer); + if (buffers[3].BufferType == SECBUFFER_EXTRA) { + ASSERT(buffers[3].cbBuffer <= ciphertext_read_buf.size()); + ciphertext_read_buf.erase(ciphertext_read_buf.begin(), + ciphertext_read_buf.end() - buffers[3].cbBuffer); + } else { + ASSERT(buffers[3].BufferType == SECBUFFER_EMPTY); + ciphertext_read_buf.clear(); + } + continue; + case SEC_E_INCOMPLETE_MESSAGE: + break; + case SEC_I_CONTEXT_EXPIRED: + // Server hung up by sending close_notify. + got_read_eof = true; + return size_t(0); + default: + LOG_ERROR(Service_SSL, "DecryptMessage failed: {}", + Common::NativeErrorToString(ret)); + return ResultInternalError; + } + } + const Result r = FillCiphertextReadBuf(); + if (r != ResultSuccess) { + return r; + } + if (ciphertext_read_buf.empty()) { + got_read_eof = true; + return size_t(0); + } + } + } + + ResultVal<size_t> Write(std::span<const u8> data) override { + if (handshake_state != HandshakeState::Connected) { + LOG_ERROR(Service_SSL, "Called Write but we did not successfully handshake"); + return ResultInternalError; + } + if (data.size() == 0) { + return size_t(0); + } + data = data.subspan(0, std::min<size_t>(data.size(), stream_sizes.cbMaximumMessage)); + if (!cleartext_write_buf.empty()) { + // Already in the middle of a write. It wouldn't make sense to not + // finish sending the entire buffer since TLS has + // header/MAC/padding/etc. + if (data.size() != cleartext_write_buf.size() || + std::memcmp(data.data(), cleartext_write_buf.data(), data.size())) { + LOG_ERROR(Service_SSL, "Called Write but buffer does not match previous buffer"); + return ResultInternalError; + } + return WriteAlreadyEncryptedData(); + } else { + cleartext_write_buf.assign(data.begin(), data.end()); + } + + std::vector<u8> header_buf(stream_sizes.cbHeader, 0); + std::vector<u8> tmp_data_buf = cleartext_write_buf; + std::vector<u8> trailer_buf(stream_sizes.cbTrailer, 0); + + std::array<SecBuffer, 3> buffers{{ + { + .cbBuffer = stream_sizes.cbHeader, + .BufferType = SECBUFFER_STREAM_HEADER, + .pvBuffer = header_buf.data(), + }, + { + .cbBuffer = static_cast<unsigned long>(tmp_data_buf.size()), + .BufferType = SECBUFFER_DATA, + .pvBuffer = tmp_data_buf.data(), + }, + { + .cbBuffer = stream_sizes.cbTrailer, + .BufferType = SECBUFFER_STREAM_TRAILER, + .pvBuffer = trailer_buf.data(), + }, + }}; + ASSERT_OR_EXECUTE_MSG( + buffers[1].cbBuffer == tmp_data_buf.size(), { return ResultInternalError; }, + "temp buffer too large"); + SecBufferDesc desc{ + .ulVersion = SECBUFFER_VERSION, + .cBuffers = static_cast<unsigned long>(buffers.size()), + .pBuffers = buffers.data(), + }; + + const SECURITY_STATUS ret = EncryptMessage(&ctxt, /*fQOP*/ 0, &desc, /*MessageSeqNo*/ 0); + if (ret != SEC_E_OK) { + LOG_ERROR(Service_SSL, "EncryptMessage failed: {}", Common::NativeErrorToString(ret)); + return ResultInternalError; + } + ciphertext_write_buf.insert(ciphertext_write_buf.end(), header_buf.begin(), + header_buf.end()); + ciphertext_write_buf.insert(ciphertext_write_buf.end(), tmp_data_buf.begin(), + tmp_data_buf.end()); + ciphertext_write_buf.insert(ciphertext_write_buf.end(), trailer_buf.begin(), + trailer_buf.end()); + return WriteAlreadyEncryptedData(); + } + + ResultVal<size_t> WriteAlreadyEncryptedData() { + const Result r = FlushCiphertextWriteBuf(); + if (r != ResultSuccess) { + return r; + } + // write buf is empty + const size_t cleartext_bytes_written = cleartext_write_buf.size(); + cleartext_write_buf.clear(); + return cleartext_bytes_written; + } + + ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { + PCCERT_CONTEXT returned_cert = nullptr; + const SECURITY_STATUS ret = + QueryContextAttributes(&ctxt, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &returned_cert); + if (ret != SEC_E_OK) { + LOG_ERROR(Service_SSL, + "QueryContextAttributes(SECPKG_ATTR_REMOTE_CERT_CONTEXT) failed: {}", + Common::NativeErrorToString(ret)); + return ResultInternalError; + } + PCCERT_CONTEXT some_cert = nullptr; + std::vector<std::vector<u8>> certs; + while ((some_cert = CertEnumCertificatesInStore(returned_cert->hCertStore, some_cert))) { + certs.emplace_back(static_cast<u8*>(some_cert->pbCertEncoded), + static_cast<u8*>(some_cert->pbCertEncoded) + + some_cert->cbCertEncoded); + } + std::reverse(certs.begin(), + certs.end()); // Windows returns certs in reverse order from what we want + CertFreeCertificateContext(returned_cert); + return certs; + } + + ~SSLConnectionBackendSchannel() { + if (handshake_state != HandshakeState::Initial) { + DeleteSecurityContext(&ctxt); + } + } + + enum class HandshakeState { + // Haven't called anything yet. + Initial, + // `SEC_I_CONTINUE_NEEDED` was returned by + // `InitializeSecurityContext`; must finish sending data (if any) in + // the write buffer, then read at least one byte before calling + // `InitializeSecurityContext` again. + ContinueNeeded, + // `SEC_E_INCOMPLETE_MESSAGE` was returned by + // `InitializeSecurityContext`; hopefully the write buffer is empty; + // must read at least one byte before calling + // `InitializeSecurityContext` again. + IncompleteMessage, + // `SEC_E_OK` was returned by `InitializeSecurityContext`; must + // finish sending data in the write buffer before having `DoHandshake` + // report success. + DoneAfterFlush, + // We finished the above and are now connected. At this point, writing + // and reading are separate 'state machines' represented by the + // nonemptiness of the ciphertext and cleartext read and write buffers. + Connected, + // Another error was returned and we shouldn't allow initialization + // to continue. + Error, + } handshake_state = HandshakeState::Initial; + + CtxtHandle ctxt; + SecPkgContext_StreamSizes stream_sizes; + + std::shared_ptr<Network::SocketBase> socket; + std::optional<std::string> hostname; + + std::vector<u8> ciphertext_read_buf; + std::vector<u8> ciphertext_write_buf; + std::vector<u8> cleartext_read_buf; + std::vector<u8> cleartext_write_buf; + + bool got_read_eof = false; + size_t read_buf_fill_size = 0; +}; + +ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { + auto conn = std::make_unique<SSLConnectionBackendSchannel>(); + const Result res = conn->Init(); + if (res.IsFailure()) { + return res; + } + return conn; +} + +} // namespace Service::SSL diff --git a/src/core/hle/service/ssl/ssl_backend_securetransport.cpp b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp new file mode 100644 index 000000000..b3083cbad --- /dev/null +++ b/src/core/hle/service/ssl/ssl_backend_securetransport.cpp @@ -0,0 +1,222 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <mutex> + +// SecureTransport has been deprecated in its entirety in favor of +// Network.framework, but that does not allow layering TLS on top of an +// arbitrary socket. +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#include <Security/SecureTransport.h> +#pragma GCC diagnostic pop +#endif + +#include "core/hle/service/ssl/ssl_backend.h" +#include "core/internal_network/network.h" +#include "core/internal_network/sockets.h" + +namespace { + +template <typename T> +struct CFReleaser { + T ptr; + + YUZU_NON_COPYABLE(CFReleaser); + constexpr CFReleaser() : ptr(nullptr) {} + constexpr CFReleaser(T ptr) : ptr(ptr) {} + constexpr operator T() { + return ptr; + } + ~CFReleaser() { + if (ptr) { + CFRelease(ptr); + } + } +}; + +std::string CFStringToString(CFStringRef cfstr) { + CFReleaser<CFDataRef> cfdata( + CFStringCreateExternalRepresentation(nullptr, cfstr, kCFStringEncodingUTF8, 0)); + ASSERT_OR_EXECUTE(cfdata, { return "???"; }); + return std::string(reinterpret_cast<const char*>(CFDataGetBytePtr(cfdata)), + CFDataGetLength(cfdata)); +} + +std::string OSStatusToString(OSStatus status) { + CFReleaser<CFStringRef> cfstr(SecCopyErrorMessageString(status, nullptr)); + if (!cfstr) { + return "[unknown error]"; + } + return CFStringToString(cfstr); +} + +} // namespace + +namespace Service::SSL { + +class SSLConnectionBackendSecureTransport final : public SSLConnectionBackend { +public: + Result Init() { + static std::once_flag once_flag; + std::call_once(once_flag, []() { + if (getenv("SSLKEYLOGFILE")) { + LOG_CRITICAL(Service_SSL, "SSLKEYLOGFILE was set but SecureTransport does not " + "support exporting keys; not logging keys!"); + // Not fatal. + } + }); + + context.ptr = SSLCreateContext(nullptr, kSSLClientSide, kSSLStreamType); + if (!context) { + LOG_ERROR(Service_SSL, "SSLCreateContext failed"); + return ResultInternalError; + } + + OSStatus status; + if ((status = SSLSetIOFuncs(context, ReadCallback, WriteCallback)) || + (status = SSLSetConnection(context, this))) { + LOG_ERROR(Service_SSL, "SSLContext initialization failed: {}", + OSStatusToString(status)); + return ResultInternalError; + } + + return ResultSuccess; + } + + void SetSocket(std::shared_ptr<Network::SocketBase> in_socket) override { + socket = std::move(in_socket); + } + + Result SetHostName(const std::string& hostname) override { + OSStatus status = SSLSetPeerDomainName(context, hostname.c_str(), hostname.size()); + if (status) { + LOG_ERROR(Service_SSL, "SSLSetPeerDomainName failed: {}", OSStatusToString(status)); + return ResultInternalError; + } + return ResultSuccess; + } + + Result DoHandshake() override { + OSStatus status = SSLHandshake(context); + return HandleReturn("SSLHandshake", 0, status).Code(); + } + + ResultVal<size_t> Read(std::span<u8> data) override { + size_t actual; + OSStatus status = SSLRead(context, data.data(), data.size(), &actual); + ; + return HandleReturn("SSLRead", actual, status); + } + + ResultVal<size_t> Write(std::span<const u8> data) override { + size_t actual; + OSStatus status = SSLWrite(context, data.data(), data.size(), &actual); + ; + return HandleReturn("SSLWrite", actual, status); + } + + ResultVal<size_t> HandleReturn(const char* what, size_t actual, OSStatus status) { + switch (status) { + case 0: + return actual; + case errSSLWouldBlock: + return ResultWouldBlock; + default: { + std::string reason; + if (got_read_eof) { + reason = "server hung up"; + } else { + reason = OSStatusToString(status); + } + LOG_ERROR(Service_SSL, "{} failed: {}", what, reason); + return ResultInternalError; + } + } + } + + ResultVal<std::vector<std::vector<u8>>> GetServerCerts() override { + CFReleaser<SecTrustRef> trust; + OSStatus status = SSLCopyPeerTrust(context, &trust.ptr); + if (status) { + LOG_ERROR(Service_SSL, "SSLCopyPeerTrust failed: {}", OSStatusToString(status)); + return ResultInternalError; + } + std::vector<std::vector<u8>> ret; + for (CFIndex i = 0, count = SecTrustGetCertificateCount(trust); i < count; i++) { + SecCertificateRef cert = SecTrustGetCertificateAtIndex(trust, i); + CFReleaser<CFDataRef> data(SecCertificateCopyData(cert)); + ASSERT_OR_EXECUTE(data, { return ResultInternalError; }); + const u8* ptr = CFDataGetBytePtr(data); + ret.emplace_back(ptr, ptr + CFDataGetLength(data)); + } + return ret; + } + + static OSStatus ReadCallback(SSLConnectionRef connection, void* data, size_t* dataLength) { + return ReadOrWriteCallback(connection, data, dataLength, true); + } + + static OSStatus WriteCallback(SSLConnectionRef connection, const void* data, + size_t* dataLength) { + return ReadOrWriteCallback(connection, const_cast<void*>(data), dataLength, false); + } + + static OSStatus ReadOrWriteCallback(SSLConnectionRef connection, void* data, size_t* dataLength, + bool is_read) { + auto self = + static_cast<SSLConnectionBackendSecureTransport*>(const_cast<void*>(connection)); + ASSERT_OR_EXECUTE_MSG( + self->socket, { return 0; }, "SecureTransport asked to {} but we have no socket", + is_read ? "read" : "write"); + + // SecureTransport callbacks (unlike OpenSSL BIO callbacks) are + // expected to read/write the full requested dataLength or return an + // error, so we have to add a loop ourselves. + size_t requested_len = *dataLength; + size_t offset = 0; + while (offset < requested_len) { + std::span cur(reinterpret_cast<u8*>(data) + offset, requested_len - offset); + auto [actual, err] = is_read ? self->socket->Recv(0, cur) : self->socket->Send(cur, 0); + LOG_CRITICAL(Service_SSL, "op={}, offset={} actual={}/{} err={}", is_read, offset, + actual, cur.size(), static_cast<s32>(err)); + switch (err) { + case Network::Errno::SUCCESS: + offset += actual; + if (actual == 0) { + ASSERT(is_read); + self->got_read_eof = true; + return errSecEndOfData; + } + break; + case Network::Errno::AGAIN: + *dataLength = offset; + return errSSLWouldBlock; + default: + LOG_ERROR(Service_SSL, "Socket {} returned Network::Errno {}", + is_read ? "recv" : "send", err); + return errSecIO; + } + } + ASSERT(offset == requested_len); + return 0; + } + +private: + CFReleaser<SSLContextRef> context = nullptr; + bool got_read_eof = false; + + std::shared_ptr<Network::SocketBase> socket; +}; + +ResultVal<std::unique_ptr<SSLConnectionBackend>> CreateSSLConnectionBackend() { + auto conn = std::make_unique<SSLConnectionBackendSecureTransport>(); + const Result res = conn->Init(); + if (res.IsFailure()) { + return res; + } + return conn; +} + +} // namespace Service::SSL diff --git a/src/core/internal_network/network.cpp b/src/core/internal_network/network.cpp index 75ac10a9c..28f89c599 100644 --- a/src/core/internal_network/network.cpp +++ b/src/core/internal_network/network.cpp @@ -27,6 +27,7 @@ #include "common/assert.h" #include "common/common_types.h" +#include "common/expected.h" #include "common/logging/log.h" #include "common/settings.h" #include "core/internal_network/network.h" @@ -97,6 +98,8 @@ bool EnableNonBlock(SOCKET fd, bool enable) { Errno TranslateNativeError(int e) { switch (e) { + case 0: + return Errno::SUCCESS; case WSAEBADF: return Errno::BADF; case WSAEINVAL: @@ -121,6 +124,8 @@ Errno TranslateNativeError(int e) { return Errno::MSGSIZE; case WSAETIMEDOUT: return Errno::TIMEDOUT; + case WSAEINPROGRESS: + return Errno::INPROGRESS; default: UNIMPLEMENTED_MSG("Unimplemented errno={}", e); return Errno::OTHER; @@ -195,6 +200,8 @@ bool EnableNonBlock(int fd, bool enable) { Errno TranslateNativeError(int e) { switch (e) { + case 0: + return Errno::SUCCESS; case EBADF: return Errno::BADF; case EINVAL: @@ -219,8 +226,10 @@ Errno TranslateNativeError(int e) { return Errno::MSGSIZE; case ETIMEDOUT: return Errno::TIMEDOUT; + case EINPROGRESS: + return Errno::INPROGRESS; default: - UNIMPLEMENTED_MSG("Unimplemented errno={}", e); + UNIMPLEMENTED_MSG("Unimplemented errno={} ({})", e, strerror(e)); return Errno::OTHER; } } @@ -234,15 +243,84 @@ Errno GetAndLogLastError() { int e = errno; #endif const Errno err = TranslateNativeError(e); - if (err == Errno::AGAIN || err == Errno::TIMEDOUT) { + if (err == Errno::AGAIN || err == Errno::TIMEDOUT || err == Errno::INPROGRESS) { + // These happen during normal operation, so only log them at debug level. + LOG_DEBUG(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); return err; } LOG_ERROR(Network, "Socket operation error: {}", Common::NativeErrorToString(e)); return err; } -int TranslateDomain(Domain domain) { +GetAddrInfoError TranslateGetAddrInfoErrorFromNative(int gai_err) { + switch (gai_err) { + case 0: + return GetAddrInfoError::SUCCESS; +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + return GetAddrInfoError::ADDRFAMILY; +#endif + case EAI_AGAIN: + return GetAddrInfoError::AGAIN; + case EAI_BADFLAGS: + return GetAddrInfoError::BADFLAGS; + case EAI_FAIL: + return GetAddrInfoError::FAIL; + case EAI_FAMILY: + return GetAddrInfoError::FAMILY; + case EAI_MEMORY: + return GetAddrInfoError::MEMORY; + case EAI_NONAME: + return GetAddrInfoError::NONAME; + case EAI_SERVICE: + return GetAddrInfoError::SERVICE; + case EAI_SOCKTYPE: + return GetAddrInfoError::SOCKTYPE; + // These codes may not be defined on all systems: +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + return GetAddrInfoError::SYSTEM; +#endif +#ifdef EAI_BADHINTS + case EAI_BADHINTS: + return GetAddrInfoError::BADHINTS; +#endif +#ifdef EAI_PROTOCOL + case EAI_PROTOCOL: + return GetAddrInfoError::PROTOCOL; +#endif +#ifdef EAI_OVERFLOW + case EAI_OVERFLOW: + return GetAddrInfoError::OVERFLOW_; +#endif + default: +#ifdef EAI_NODATA + // This can't be a case statement because it would create a duplicate + // case on Windows where EAI_NODATA is an alias for EAI_NONAME. + if (gai_err == EAI_NODATA) { + return GetAddrInfoError::NODATA; + } +#endif + return GetAddrInfoError::OTHER; + } +} + +Domain TranslateDomainFromNative(int domain) { + switch (domain) { + case 0: + return Domain::Unspecified; + case AF_INET: + return Domain::INET; + default: + UNIMPLEMENTED_MSG("Unhandled domain={}", domain); + return Domain::INET; + } +} + +int TranslateDomainToNative(Domain domain) { switch (domain) { + case Domain::Unspecified: + return 0; case Domain::INET: return AF_INET; default: @@ -251,20 +329,58 @@ int TranslateDomain(Domain domain) { } } -int TranslateType(Type type) { +Type TranslateTypeFromNative(int type) { + switch (type) { + case 0: + return Type::Unspecified; + case SOCK_STREAM: + return Type::STREAM; + case SOCK_DGRAM: + return Type::DGRAM; + case SOCK_RAW: + return Type::RAW; + case SOCK_SEQPACKET: + return Type::SEQPACKET; + default: + UNIMPLEMENTED_MSG("Unimplemented type={}", type); + return Type::STREAM; + } +} + +int TranslateTypeToNative(Type type) { switch (type) { + case Type::Unspecified: + return 0; case Type::STREAM: return SOCK_STREAM; case Type::DGRAM: return SOCK_DGRAM; + case Type::RAW: + return SOCK_RAW; default: UNIMPLEMENTED_MSG("Unimplemented type={}", type); return 0; } } -int TranslateProtocol(Protocol protocol) { +Protocol TranslateProtocolFromNative(int protocol) { + switch (protocol) { + case 0: + return Protocol::Unspecified; + case IPPROTO_TCP: + return Protocol::TCP; + case IPPROTO_UDP: + return Protocol::UDP; + default: + UNIMPLEMENTED_MSG("Unimplemented protocol={}", protocol); + return Protocol::Unspecified; + } +} + +int TranslateProtocolToNative(Protocol protocol) { switch (protocol) { + case Protocol::Unspecified: + return 0; case Protocol::TCP: return IPPROTO_TCP; case Protocol::UDP: @@ -275,21 +391,10 @@ int TranslateProtocol(Protocol protocol) { } } -SockAddrIn TranslateToSockAddrIn(sockaddr input_) { - sockaddr_in input; - std::memcpy(&input, &input_, sizeof(input)); - +SockAddrIn TranslateToSockAddrIn(sockaddr_in input, size_t input_len) { SockAddrIn result; - switch (input.sin_family) { - case AF_INET: - result.family = Domain::INET; - break; - default: - UNIMPLEMENTED_MSG("Unhandled sockaddr family={}", input.sin_family); - result.family = Domain::INET; - break; - } + result.family = TranslateDomainFromNative(input.sin_family); result.portno = ntohs(input.sin_port); @@ -301,22 +406,33 @@ SockAddrIn TranslateToSockAddrIn(sockaddr input_) { short TranslatePollEvents(PollEvents events) { short result = 0; - if (True(events & PollEvents::In)) { - events &= ~PollEvents::In; - result |= POLLIN; - } - if (True(events & PollEvents::Pri)) { - events &= ~PollEvents::Pri; + const auto translate = [&result, &events](PollEvents guest, short host) { + if (True(events & guest)) { + events &= ~guest; + result |= host; + } + }; + + translate(PollEvents::In, POLLIN); + translate(PollEvents::Pri, POLLPRI); + translate(PollEvents::Out, POLLOUT); + translate(PollEvents::Err, POLLERR); + translate(PollEvents::Hup, POLLHUP); + translate(PollEvents::Nval, POLLNVAL); + translate(PollEvents::RdNorm, POLLRDNORM); + translate(PollEvents::RdBand, POLLRDBAND); + translate(PollEvents::WrBand, POLLWRBAND); + #ifdef _WIN32 - LOG_WARNING(Service, "Winsock doesn't support POLLPRI"); -#else - result |= POLLPRI; + short allowed_events = POLLRDBAND | POLLRDNORM | POLLWRNORM; + // Unlike poll on other OSes, WSAPoll will complain if any other flags are set on input. + if (result & ~allowed_events) { + LOG_DEBUG(Network, + "Removing WSAPoll input events 0x{:x} because Windows doesn't support them", + result & ~allowed_events); + } + result &= allowed_events; #endif - } - if (True(events & PollEvents::Out)) { - events &= ~PollEvents::Out; - result |= POLLOUT; - } UNIMPLEMENTED_IF_MSG((u16)events != 0, "Unhandled guest events=0x{:x}", (u16)events); @@ -337,6 +453,10 @@ PollEvents TranslatePollRevents(short revents) { translate(POLLOUT, PollEvents::Out); translate(POLLERR, PollEvents::Err); translate(POLLHUP, PollEvents::Hup); + translate(POLLNVAL, PollEvents::Nval); + translate(POLLRDNORM, PollEvents::RdNorm); + translate(POLLRDBAND, PollEvents::RdBand); + translate(POLLWRBAND, PollEvents::WrBand); UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents); @@ -360,12 +480,51 @@ std::optional<IPv4Address> GetHostIPv4Address() { return {}; } - std::array<char, 16> ip_addr = {}; - ASSERT(inet_ntop(AF_INET, &network_interface->ip_address, ip_addr.data(), sizeof(ip_addr)) != - nullptr); return TranslateIPv4(network_interface->ip_address); } +std::string IPv4AddressToString(IPv4Address ip_addr) { + std::array<char, INET_ADDRSTRLEN> buf = {}; + ASSERT(inet_ntop(AF_INET, &ip_addr, buf.data(), sizeof(buf)) == buf.data()); + return std::string(buf.data()); +} + +u32 IPv4AddressToInteger(IPv4Address ip_addr) { + return static_cast<u32>(ip_addr[0]) << 24 | static_cast<u32>(ip_addr[1]) << 16 | + static_cast<u32>(ip_addr[2]) << 8 | static_cast<u32>(ip_addr[3]); +} + +Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo( + const std::string& host, const std::optional<std::string>& service) { + addrinfo hints{}; + hints.ai_family = AF_INET; // Switch only supports IPv4. + addrinfo* addrinfo; + s32 gai_err = getaddrinfo(host.c_str(), service.has_value() ? service->c_str() : nullptr, + &hints, &addrinfo); + if (gai_err != 0) { + return Common::Unexpected(TranslateGetAddrInfoErrorFromNative(gai_err)); + } + std::vector<AddrInfo> ret; + for (auto* current = addrinfo; current; current = current->ai_next) { + // We should only get AF_INET results due to the hints value. + ASSERT_OR_EXECUTE(addrinfo->ai_family == AF_INET && + addrinfo->ai_addrlen == sizeof(sockaddr_in), + continue;); + + AddrInfo& out = ret.emplace_back(); + out.family = TranslateDomainFromNative(current->ai_family); + out.socket_type = TranslateTypeFromNative(current->ai_socktype); + out.protocol = TranslateProtocolFromNative(current->ai_protocol); + out.addr = TranslateToSockAddrIn(*reinterpret_cast<sockaddr_in*>(current->ai_addr), + current->ai_addrlen); + if (current->ai_canonname != nullptr) { + out.canon_name = current->ai_canonname; + } + } + freeaddrinfo(addrinfo); + return ret; +} + std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) { const size_t num = pollfds.size(); @@ -411,9 +570,21 @@ Socket::Socket(Socket&& rhs) noexcept { } template <typename T> -Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { +std::pair<T, Errno> Socket::GetSockOpt(SOCKET fd_so, int option) { + T value{}; + socklen_t len = sizeof(value); + const int result = getsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<char*>(&value), &len); + if (result != SOCKET_ERROR) { + ASSERT(len == sizeof(value)); + return {value, Errno::SUCCESS}; + } + return {value, GetAndLogLastError()}; +} + +template <typename T> +Errno Socket::SetSockOpt(SOCKET fd_so, int option, T value) { const int result = - setsockopt(fd_, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); + setsockopt(fd_so, SOL_SOCKET, option, reinterpret_cast<const char*>(&value), sizeof(value)); if (result != SOCKET_ERROR) { return Errno::SUCCESS; } @@ -421,7 +592,8 @@ Errno Socket::SetSockOpt(SOCKET fd_, int option, T value) { } Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { - fd = socket(TranslateDomain(domain), TranslateType(type), TranslateProtocol(protocol)); + fd = socket(TranslateDomainToNative(domain), TranslateTypeToNative(type), + TranslateProtocolToNative(protocol)); if (fd != INVALID_SOCKET) { return Errno::SUCCESS; } @@ -430,19 +602,17 @@ Errno Socket::Initialize(Domain domain, Type type, Protocol protocol) { } std::pair<SocketBase::AcceptResult, Errno> Socket::Accept() { - sockaddr addr; + sockaddr_in addr; socklen_t addrlen = sizeof(addr); - const SOCKET new_socket = accept(fd, &addr, &addrlen); + const SOCKET new_socket = accept(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen); if (new_socket == INVALID_SOCKET) { return {AcceptResult{}, GetAndLogLastError()}; } - ASSERT(addrlen == sizeof(sockaddr_in)); - AcceptResult result{ .socket = std::make_unique<Socket>(new_socket), - .sockaddr_in = TranslateToSockAddrIn(addr), + .sockaddr_in = TranslateToSockAddrIn(addr, addrlen), }; return {std::move(result), Errno::SUCCESS}; @@ -458,25 +628,23 @@ Errno Socket::Connect(SockAddrIn addr_in) { } std::pair<SockAddrIn, Errno> Socket::GetPeerName() { - sockaddr addr; + sockaddr_in addr; socklen_t addrlen = sizeof(addr); - if (getpeername(fd, &addr, &addrlen) == SOCKET_ERROR) { + if (getpeername(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) { return {SockAddrIn{}, GetAndLogLastError()}; } - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; + return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS}; } std::pair<SockAddrIn, Errno> Socket::GetSockName() { - sockaddr addr; + sockaddr_in addr; socklen_t addrlen = sizeof(addr); - if (getsockname(fd, &addr, &addrlen) == SOCKET_ERROR) { + if (getsockname(fd, reinterpret_cast<sockaddr*>(&addr), &addrlen) == SOCKET_ERROR) { return {SockAddrIn{}, GetAndLogLastError()}; } - ASSERT(addrlen == sizeof(sockaddr_in)); - return {TranslateToSockAddrIn(addr), Errno::SUCCESS}; + return {TranslateToSockAddrIn(addr, addrlen), Errno::SUCCESS}; } Errno Socket::Bind(SockAddrIn addr) { @@ -519,7 +687,7 @@ Errno Socket::Shutdown(ShutdownHow how) { return GetAndLogLastError(); } -std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { +std::pair<s32, Errno> Socket::Recv(int flags, std::span<u8> message) { ASSERT(flags == 0); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); @@ -532,21 +700,20 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) { return {-1, GetAndLogLastError()}; } -std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { +std::pair<s32, Errno> Socket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) { ASSERT(flags == 0); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); - sockaddr addr_in{}; + sockaddr_in addr_in{}; socklen_t addrlen = sizeof(addr_in); socklen_t* const p_addrlen = addr ? &addrlen : nullptr; - sockaddr* const p_addr_in = addr ? &addr_in : nullptr; + sockaddr* const p_addr_in = addr ? reinterpret_cast<sockaddr*>(&addr_in) : nullptr; const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0, p_addr_in, p_addrlen); if (result != SOCKET_ERROR) { if (addr) { - ASSERT(addrlen == sizeof(addr_in)); - *addr = TranslateToSockAddrIn(addr_in); + *addr = TranslateToSockAddrIn(addr_in, addrlen); } return {static_cast<s32>(result), Errno::SUCCESS}; } @@ -597,6 +764,11 @@ Errno Socket::Close() { return Errno::SUCCESS; } +std::pair<Errno, Errno> Socket::GetPendingError() { + auto [pending_err, getsockopt_err] = GetSockOpt<int>(fd, SO_ERROR); + return {TranslateNativeError(pending_err), getsockopt_err}; +} + Errno Socket::SetLinger(bool enable, u32 linger) { return SetSockOpt(fd, SO_LINGER, MakeLinger(enable, linger)); } diff --git a/src/core/internal_network/network.h b/src/core/internal_network/network.h index 1e09a007a..badcb8369 100644 --- a/src/core/internal_network/network.h +++ b/src/core/internal_network/network.h @@ -5,6 +5,7 @@ #include <array> #include <optional> +#include <vector> #include "common/common_funcs.h" #include "common/common_types.h" @@ -16,6 +17,11 @@ #include <netinet/in.h> #endif +namespace Common { +template <typename T, typename E> +class Expected; +} + namespace Network { class SocketBase; @@ -36,6 +42,26 @@ enum class Errno { NETUNREACH, TIMEDOUT, MSGSIZE, + INPROGRESS, + OTHER, +}; + +enum class GetAddrInfoError { + SUCCESS, + ADDRFAMILY, + AGAIN, + BADFLAGS, + FAIL, + FAMILY, + MEMORY, + NODATA, + NONAME, + SERVICE, + SOCKTYPE, + SYSTEM, + BADHINTS, + PROTOCOL, + OVERFLOW_, OTHER, }; @@ -49,6 +75,9 @@ enum class PollEvents : u16 { Err = 1 << 3, Hup = 1 << 4, Nval = 1 << 5, + RdNorm = 1 << 6, + RdBand = 1 << 7, + WrBand = 1 << 8, }; DECLARE_ENUM_FLAG_OPERATORS(PollEvents); @@ -82,4 +111,11 @@ constexpr IPv4Address TranslateIPv4(in_addr addr) { /// @return human ordered IPv4 address (e.g. 192.168.0.1) as an array std::optional<IPv4Address> GetHostIPv4Address(); +std::string IPv4AddressToString(IPv4Address ip_addr); +u32 IPv4AddressToInteger(IPv4Address ip_addr); + +// named to avoid name collision with Windows macro +Common::Expected<std::vector<AddrInfo>, GetAddrInfoError> GetAddressInfo( + const std::string& host, const std::optional<std::string>& service); + } // namespace Network diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index 7a77171c2..ce0dee970 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -10,6 +10,7 @@ #include "core/internal_network/network.h" #include "core/internal_network/network_interface.h" #include "core/internal_network/socket_proxy.h" +#include "network/network.h" #if YUZU_UNIX #include <sys/socket.h> @@ -98,7 +99,7 @@ Errno ProxySocket::Shutdown(ShutdownHow how) { return Errno::SUCCESS; } -std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { +std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) { LOG_WARNING(Network, "(STUBBED) called"); ASSERT(flags == 0); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); @@ -106,7 +107,7 @@ std::pair<s32, Errno> ProxySocket::Recv(int flags, std::vector<u8>& message) { return {static_cast<s32>(0), Errno::SUCCESS}; } -std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) { +std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) { ASSERT(flags == 0); ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max())); @@ -140,8 +141,8 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::vector<u8>& message, } } -std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& message, - SockAddrIn* addr, std::size_t max_length) { +std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr, + std::size_t max_length) { ProxyPacket& packet = received_packets.front(); if (addr) { addr->family = Domain::INET; @@ -153,10 +154,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes std::size_t read_bytes; if (packet.data.size() > max_length) { read_bytes = max_length; - message.clear(); - std::copy(packet.data.begin(), packet.data.begin() + read_bytes, - std::back_inserter(message)); - message.resize(max_length); + memcpy(message.data(), packet.data.data(), max_length); if (protocol == Protocol::UDP) { if (!peek) { @@ -171,9 +169,7 @@ std::pair<s32, Errno> ProxySocket::ReceivePacket(int flags, std::vector<u8>& mes } } else { read_bytes = packet.data.size(); - message.clear(); - std::copy(packet.data.begin(), packet.data.end(), std::back_inserter(message)); - message.resize(max_length); + memcpy(message.data(), packet.data.data(), read_bytes); if (!peek) { received_packets.pop(); } @@ -293,6 +289,11 @@ Errno ProxySocket::SetNonBlock(bool enable) { return Errno::SUCCESS; } +std::pair<Errno, Errno> ProxySocket::GetPendingError() { + LOG_DEBUG(Network, "(STUBBED) called"); + return {Errno::SUCCESS, Errno::SUCCESS}; +} + bool ProxySocket::IsOpened() const { return fd != INVALID_SOCKET; } diff --git a/src/core/internal_network/socket_proxy.h b/src/core/internal_network/socket_proxy.h index 6e991fa38..70500cf4a 100644 --- a/src/core/internal_network/socket_proxy.h +++ b/src/core/internal_network/socket_proxy.h @@ -10,10 +10,12 @@ #include "common/common_funcs.h" #include "core/internal_network/sockets.h" -#include "network/network.h" +#include "network/room_member.h" namespace Network { +class RoomNetwork; + class ProxySocket : public SocketBase { public: explicit ProxySocket(RoomNetwork& room_network_) noexcept; @@ -39,11 +41,11 @@ public: Errno Shutdown(ShutdownHow how) override; - std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; + std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override; - std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; + std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override; - std::pair<s32, Errno> ReceivePacket(int flags, std::vector<u8>& message, SockAddrIn* addr, + std::pair<s32, Errno> ReceivePacket(int flags, std::span<u8> message, SockAddrIn* addr, std::size_t max_length); std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; @@ -74,6 +76,8 @@ public: template <typename T> Errno SetSockOpt(SOCKET fd, int option, T value); + std::pair<Errno, Errno> GetPendingError() override; + bool IsOpened() const override; private: diff --git a/src/core/internal_network/sockets.h b/src/core/internal_network/sockets.h index 11e479e50..4ba51f62c 100644 --- a/src/core/internal_network/sockets.h +++ b/src/core/internal_network/sockets.h @@ -15,12 +15,13 @@ #include "common/common_types.h" #include "core/internal_network/network.h" -#include "network/network.h" // TODO: C++20 Replace std::vector usages with std::span namespace Network { +struct ProxyPacket; + class SocketBase { public: #ifdef YUZU_UNIX @@ -59,10 +60,9 @@ public: virtual Errno Shutdown(ShutdownHow how) = 0; - virtual std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) = 0; + virtual std::pair<s32, Errno> Recv(int flags, std::span<u8> message) = 0; - virtual std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, - SockAddrIn* addr) = 0; + virtual std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) = 0; virtual std::pair<s32, Errno> Send(std::span<const u8> message, int flags) = 0; @@ -87,6 +87,8 @@ public: virtual Errno SetNonBlock(bool enable) = 0; + virtual std::pair<Errno, Errno> GetPendingError() = 0; + virtual bool IsOpened() const = 0; virtual void HandleProxyPacket(const ProxyPacket& packet) = 0; @@ -126,9 +128,9 @@ public: Errno Shutdown(ShutdownHow how) override; - std::pair<s32, Errno> Recv(int flags, std::vector<u8>& message) override; + std::pair<s32, Errno> Recv(int flags, std::span<u8> message) override; - std::pair<s32, Errno> RecvFrom(int flags, std::vector<u8>& message, SockAddrIn* addr) override; + std::pair<s32, Errno> RecvFrom(int flags, std::span<u8> message, SockAddrIn* addr) override; std::pair<s32, Errno> Send(std::span<const u8> message, int flags) override; @@ -156,6 +158,11 @@ public: template <typename T> Errno SetSockOpt(SOCKET fd, int option, T value); + std::pair<Errno, Errno> GetPendingError() override; + + template <typename T> + std::pair<T, Errno> GetSockOpt(SOCKET fd, int option); + bool IsOpened() const override; void HandleProxyPacket(const ProxyPacket& packet) override; diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 3be9b71cf..e04ad19db 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -153,7 +153,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect // Load NSO modules modules.clear(); - const VAddr base_address{GetInteger(process.PageTable().GetCodeRegionStart())}; + const VAddr base_address{GetInteger(process.GetPageTable().GetCodeRegionStart())}; VAddr next_load_addr{base_address}; const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider()}; diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index 709e2564f..ffe976b94 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp @@ -96,7 +96,7 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, } codeset.memory = std::move(program_image); - const VAddr base_address = GetInteger(process.PageTable().GetCodeRegionStart()); + const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart()); process.LoadModule(std::move(codeset), base_address); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", kip->GetName(), base_address); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7b43f70ed..7a2a52fd4 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -79,6 +79,8 @@ enum class ResultStatus : u16 { ErrorBadPFSHeader, ErrorIncorrectPFSFileSize, ErrorBadNCAHeader, + ErrorCompressedNCA, + ErrorSparseNCA, ErrorMissingProductionKeyFile, ErrorMissingHeaderKey, ErrorIncorrectHeaderKey, diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 7be6cf5f3..506808b5d 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -203,7 +203,7 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) // Load codeset for current process codeset.memory = std::move(program_image); - process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart()); + process.LoadModule(std::move(codeset), process.GetPageTable().GetCodeRegionStart()); return true; } diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 79639f5e4..74cc9579f 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -167,7 +167,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::KProcess& process, Core::S modules.clear(); // Load module - const VAddr base_address = GetInteger(process.PageTable().GetCodeRegionStart()); + const VAddr base_address = GetInteger(process.GetPageTable().GetCodeRegionStart()); if (!LoadModule(process, system, *file, base_address, true, true)) { return {ResultStatus::ErrorLoadingNSO, {}}; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 257406f09..e1fbe8e00 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -31,10 +31,10 @@ struct Memory::Impl { explicit Impl(Core::System& system_) : system{system_} {} void SetCurrentPageTable(Kernel::KProcess& process, u32 core_id) { - current_page_table = &process.PageTable().PageTableImpl(); + current_page_table = &process.GetPageTable().PageTableImpl(); current_page_table->fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); - const std::size_t address_space_width = process.PageTable().GetAddressSpaceWidth(); + const std::size_t address_space_width = process.GetPageTable().GetAddressSpaceWidth(); system.ArmInterface(core_id).PageTableChanged(*current_page_table, address_space_width); } @@ -186,7 +186,7 @@ struct Memory::Impl { void WalkBlock(const Kernel::KProcess& process, const Common::ProcessAddress addr, const std::size_t size, auto on_unmapped, auto on_memory, auto on_rasterizer, auto increment) { - const auto& page_table = process.PageTable().PageTableImpl(); + const auto& page_table = process.GetPageTable().PageTableImpl(); std::size_t remaining_size = size; std::size_t page_index = addr >> YUZU_PAGEBITS; std::size_t page_offset = addr & YUZU_PAGEMASK; @@ -266,6 +266,22 @@ struct Memory::Impl { ReadBlockImpl<true>(*system.ApplicationProcess(), src_addr, dest_buffer, size); } + const u8* GetSpan(const VAddr src_addr, const std::size_t size) const { + if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] == + current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) { + return GetPointerSilent(src_addr); + } + return nullptr; + } + + u8* GetSpan(const VAddr src_addr, const std::size_t size) { + if (current_page_table->blocks[src_addr >> YUZU_PAGEBITS] == + current_page_table->blocks[(src_addr + size) >> YUZU_PAGEBITS]) { + return GetPointerSilent(src_addr); + } + return nullptr; + } + template <bool UNSAFE> void WriteBlockImpl(const Kernel::KProcess& process, const Common::ProcessAddress dest_addr, const void* src_buffer, const std::size_t size) { @@ -559,7 +575,7 @@ struct Memory::Impl { } } - const Common::ProcessAddress end = base + size; + const auto end = base + size; ASSERT_MSG(end <= page_table.pointers.size(), "out of range mapping at {:016X}", base + page_table.pointers.size()); @@ -570,14 +586,18 @@ struct Memory::Impl { while (base != end) { page_table.pointers[base].Store(nullptr, type); page_table.backing_addr[base] = 0; - + page_table.blocks[base] = 0; base += 1; } } else { + auto orig_base = base; while (base != end) { - page_table.pointers[base].Store( - system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS), type); - page_table.backing_addr[base] = GetInteger(target) - (base << YUZU_PAGEBITS); + auto host_ptr = + system.DeviceMemory().GetPointer<u8>(target) - (base << YUZU_PAGEBITS); + auto backing = GetInteger(target) - (base << YUZU_PAGEBITS); + page_table.pointers[base].Store(host_ptr, type); + page_table.backing_addr[base] = backing; + page_table.blocks[base] = orig_base << YUZU_PAGEBITS; ASSERT_MSG(page_table.pointers[base].Pointer(), "memory mapping base yield a nullptr within the table"); @@ -747,6 +767,14 @@ struct Memory::Impl { VAddr last_address; }; + void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { + system.GPU().InvalidateRegion(GetInteger(dest_addr), size); + } + + void FlushRegion(Common::ProcessAddress dest_addr, size_t size) { + system.GPU().FlushRegion(GetInteger(dest_addr), size); + } + Core::System& system; Common::PageTable* current_page_table = nullptr; std::array<VideoCore::RasterizerDownloadArea, Core::Hardware::NUM_CPU_CORES> @@ -780,7 +808,7 @@ void Memory::UnmapRegion(Common::PageTable& page_table, Common::ProcessAddress b bool Memory::IsValidVirtualAddress(const Common::ProcessAddress vaddr) const { const Kernel::KProcess& process = *system.ApplicationProcess(); - const auto& page_table = process.PageTable().PageTableImpl(); + const auto& page_table = process.GetPageTable().PageTableImpl(); const size_t page = vaddr >> YUZU_PAGEBITS; if (page >= page_table.pointers.size()) { return false; @@ -881,6 +909,14 @@ void Memory::ReadBlockUnsafe(const Common::ProcessAddress src_addr, void* dest_b impl->ReadBlockUnsafe(src_addr, dest_buffer, size); } +const u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) const { + return impl->GetSpan(src_addr, size); +} + +u8* Memory::GetSpan(const VAddr src_addr, const std::size_t size) { + return impl->GetSpan(src_addr, size); +} + void Memory::WriteBlock(const Common::ProcessAddress dest_addr, const void* src_buffer, const std::size_t size) { impl->WriteBlock(dest_addr, src_buffer, size); @@ -924,4 +960,12 @@ void Memory::MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug) impl->MarkRegionDebug(GetInteger(vaddr), size, debug); } +void Memory::InvalidateRegion(Common::ProcessAddress dest_addr, size_t size) { + impl->InvalidateRegion(dest_addr, size); +} + +void Memory::FlushRegion(Common::ProcessAddress dest_addr, size_t size) { + impl->FlushRegion(dest_addr, size); +} + } // namespace Core::Memory diff --git a/src/core/memory.h b/src/core/memory.h index ea01824f8..ea33c769c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -5,8 +5,12 @@ #include <cstddef> #include <memory> +#include <optional> #include <span> #include <string> +#include <vector> + +#include "common/scratch_buffer.h" #include "common/typed_address.h" #include "core/hle/result.h" @@ -24,6 +28,10 @@ class PhysicalMemory; class KProcess; } // namespace Kernel +namespace Tegra { +class MemoryManager; +} + namespace Core::Memory { /** @@ -343,6 +351,9 @@ public: */ void ReadBlockUnsafe(Common::ProcessAddress src_addr, void* dest_buffer, std::size_t size); + const u8* GetSpan(const VAddr src_addr, const std::size_t size) const; + u8* GetSpan(const VAddr src_addr, const std::size_t size); + /** * Writes a range of bytes into the current process' address space at the specified * virtual address. @@ -461,6 +472,8 @@ public: void MarkRegionDebug(Common::ProcessAddress vaddr, u64 size, bool debug); void SetGPUDirtyManagers(std::span<Core::GPUDirtyMemoryManager> managers); + void InvalidateRegion(Common::ProcessAddress dest_addr, size_t size); + void FlushRegion(Common::ProcessAddress dest_addr, size_t size); private: Core::System& system; @@ -469,4 +482,203 @@ private: std::unique_ptr<Impl> impl; }; +enum GuestMemoryFlags : u32 { + Read = 1 << 0, + Write = 1 << 1, + Safe = 1 << 2, + Cached = 1 << 3, + + SafeRead = Read | Safe, + SafeWrite = Write | Safe, + SafeReadWrite = SafeRead | SafeWrite, + SafeReadCachedWrite = SafeReadWrite | Cached, + + UnsafeRead = Read, + UnsafeWrite = Write, + UnsafeReadWrite = UnsafeRead | UnsafeWrite, + UnsafeReadCachedWrite = UnsafeReadWrite | Cached, +}; + +namespace { +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemory { + using iterator = T*; + using const_iterator = const T*; + using value_type = T; + using element_type = T; + using iterator_category = std::contiguous_iterator_tag; + +public: + GuestMemory() = delete; + explicit GuestMemory(M& memory_, u64 addr_, std::size_t size_, + Common::ScratchBuffer<T>* backup = nullptr) + : memory{memory_}, addr{addr_}, size{size_} { + static_assert(FLAGS & GuestMemoryFlags::Read || FLAGS & GuestMemoryFlags::Write); + if constexpr (FLAGS & GuestMemoryFlags::Read) { + Read(addr, size, backup); + } + } + + ~GuestMemory() = default; + + T* data() noexcept { + return data_span.data(); + } + + const T* data() const noexcept { + return data_span.data(); + } + + [[nodiscard]] T* begin() noexcept { + return data(); + } + + [[nodiscard]] const T* begin() const noexcept { + return data(); + } + + [[nodiscard]] T* end() noexcept { + return data() + size; + } + + [[nodiscard]] const T* end() const noexcept { + return data() + size; + } + + T& operator[](size_t index) noexcept { + return data_span[index]; + } + + const T& operator[](size_t index) const noexcept { + return data_span[index]; + } + + void SetAddressAndSize(u64 addr_, std::size_t size_) noexcept { + addr = addr_; + size = size_; + addr_changed = true; + } + + std::span<T> Read(u64 addr_, std::size_t size_, + Common::ScratchBuffer<T>* backup = nullptr) noexcept { + addr = addr_; + size = size_; + if (size == 0) { + is_data_copy = true; + return {}; + } + + if (TrySetSpan()) { + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + memory.FlushRegion(addr, size * sizeof(T)); + } + } else { + if (backup) { + backup->resize_destructive(size); + data_span = *backup; + } else { + data_copy.resize(size); + data_span = std::span(data_copy); + } + is_data_copy = true; + span_valid = true; + if constexpr (FLAGS & GuestMemoryFlags::Safe) { + memory.ReadBlock(addr, data_span.data(), size * sizeof(T)); + } else { + memory.ReadBlockUnsafe(addr, data_span.data(), size * sizeof(T)); + } + } + return data_span; + } + + void Write(std::span<T> write_data) noexcept { + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + memory.WriteBlockCached(addr, write_data.data(), size * sizeof(T)); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + memory.WriteBlock(addr, write_data.data(), size * sizeof(T)); + } else { + memory.WriteBlockUnsafe(addr, write_data.data(), size * sizeof(T)); + } + } + + bool TrySetSpan() noexcept { + if (u8* ptr = memory.GetSpan(addr, size * sizeof(T)); ptr) { + data_span = {reinterpret_cast<T*>(ptr), size}; + span_valid = true; + return true; + } + return false; + } + +protected: + bool IsDataCopy() const noexcept { + return is_data_copy; + } + + bool AddressChanged() const noexcept { + return addr_changed; + } + + M& memory; + u64 addr; + size_t size; + std::span<T> data_span{}; + std::vector<T> data_copy; + bool span_valid{false}; + bool is_data_copy{false}; + bool addr_changed{false}; +}; + +template <typename M, typename T, GuestMemoryFlags FLAGS> +class GuestMemoryScoped : public GuestMemory<M, T, FLAGS> { +public: + GuestMemoryScoped() = delete; + explicit GuestMemoryScoped(M& memory_, u64 addr_, std::size_t size_, + Common::ScratchBuffer<T>* backup = nullptr) + : GuestMemory<M, T, FLAGS>(memory_, addr_, size_, backup) { + if constexpr (!(FLAGS & GuestMemoryFlags::Read)) { + if (!this->TrySetSpan()) { + if (backup) { + this->data_span = *backup; + this->span_valid = true; + this->is_data_copy = true; + } + } + } + } + + ~GuestMemoryScoped() { + if constexpr (FLAGS & GuestMemoryFlags::Write) { + if (this->size == 0) [[unlikely]] { + return; + } + + if (this->AddressChanged() || this->IsDataCopy()) { + ASSERT(this->span_valid); + if constexpr (FLAGS & GuestMemoryFlags::Cached) { + this->memory.WriteBlockCached(this->addr, this->data_span.data(), + this->size * sizeof(T)); + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + this->memory.WriteBlock(this->addr, this->data_span.data(), + this->size * sizeof(T)); + } else { + this->memory.WriteBlockUnsafe(this->addr, this->data_span.data(), + this->size * sizeof(T)); + } + } else if constexpr (FLAGS & GuestMemoryFlags::Safe) { + this->memory.InvalidateRegion(this->addr, this->size * sizeof(T)); + } + } + } +}; +} // namespace + +template <typename T, GuestMemoryFlags FLAGS> +using CpuGuestMemory = GuestMemory<Memory, T, FLAGS>; +template <typename T, GuestMemoryFlags FLAGS> +using CpuGuestMemoryScoped = GuestMemoryScoped<Memory, T, FLAGS>; +template <typename T, GuestMemoryFlags FLAGS> +using GpuGuestMemory = GuestMemory<Tegra::MemoryManager, T, FLAGS>; +template <typename T, GuestMemoryFlags FLAGS> +using GpuGuestMemoryScoped = GuestMemoryScoped<Tegra::MemoryManager, T, FLAGS>; } // namespace Core::Memory diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 8742dd164..7b52f61a7 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -199,7 +199,7 @@ void CheatEngine::Initialize() { metadata.process_id = system.ApplicationProcess()->GetProcessId(); metadata.title_id = system.GetApplicationProcessProgramID(); - const auto& page_table = system.ApplicationProcess()->PageTable(); + const auto& page_table = system.ApplicationProcess()->GetPageTable(); metadata.heap_extents = { .base = GetInteger(page_table.GetHeapRegionStart()), .size = page_table.GetHeapRegionSize(), diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index 6c3dc7369..b5b3e7eda 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp @@ -117,8 +117,8 @@ json GetProcessorStateDataAuto(Core::System& system) { arm.SaveContext(context); return GetProcessorStateData(process->Is64BitProcess() ? "AArch64" : "AArch32", - GetInteger(process->PageTable().GetCodeRegionStart()), context.sp, - context.pc, context.pstate, context.cpu_registers); + GetInteger(process->GetPageTable().GetCodeRegionStart()), + context.sp, context.pc, context.pstate, context.cpu_registers); } json GetBacktraceData(Core::System& system) { diff --git a/src/input_common/drivers/sdl_driver.cpp b/src/input_common/drivers/sdl_driver.cpp index 9f26392b1..66e3ae9af 100644 --- a/src/input_common/drivers/sdl_driver.cpp +++ b/src/input_common/drivers/sdl_driver.cpp @@ -523,6 +523,8 @@ SDLDriver::SDLDriver(std::string input_engine_) : InputEngine(std::move(input_en } SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_PLAYER_LED, "1"); + // Share the same button mapping with non-Nintendo controllers + SDL_SetHint(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, "0"); // Disable hidapi driver for xbox. Already default on Windows, this causes conflict with native // driver on Linux. @@ -800,16 +802,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p // This list is missing ZL/ZR since those are not considered buttons in SDL GameController. // We will add those afterwards - // This list also excludes Screenshot since there's not really a mapping for that ButtonBindings switch_to_sdl_button; - if (SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO || - SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT || - SDL_GameControllerGetType(controller) == SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT) { - switch_to_sdl_button = GetNintendoButtonBinding(joystick); - } else { - switch_to_sdl_button = GetDefaultButtonBinding(); - } + switch_to_sdl_button = GetDefaultButtonBinding(joystick); // Add the missing bindings for ZL/ZR static constexpr ZButtonBindings switch_to_sdl_axis{{ @@ -830,32 +825,9 @@ ButtonMapping SDLDriver::GetButtonMappingForDevice(const Common::ParamPackage& p return GetSingleControllerMapping(joystick, switch_to_sdl_button, switch_to_sdl_axis); } -ButtonBindings SDLDriver::GetDefaultButtonBinding() const { - return { - std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, - {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, - {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, - {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, - {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, - {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, - {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START}, - {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK}, - {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT}, - {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP}, - {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT}, - {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN}, - {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, - {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER}, - {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE}, - {Settings::NativeButton::Screenshot, SDL_CONTROLLER_BUTTON_MISC1}, - }; -} - -ButtonBindings SDLDriver::GetNintendoButtonBinding( +ButtonBindings SDLDriver::GetDefaultButtonBinding( const std::shared_ptr<SDLJoystick>& joystick) const { - // Default SL/SR mapping for pro controllers + // Default SL/SR mapping for other controllers auto sl_button = SDL_CONTROLLER_BUTTON_LEFTSHOULDER; auto sr_button = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER; @@ -869,10 +841,10 @@ ButtonBindings SDLDriver::GetNintendoButtonBinding( } return { - std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_A}, - {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_B}, - {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_X}, - {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_Y}, + std::pair{Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B}, + {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A}, + {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y}, + {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X}, {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK}, {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK}, {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER}, diff --git a/src/input_common/drivers/sdl_driver.h b/src/input_common/drivers/sdl_driver.h index ffde169b3..fcba4e3c6 100644 --- a/src/input_common/drivers/sdl_driver.h +++ b/src/input_common/drivers/sdl_driver.h @@ -100,11 +100,8 @@ private: int axis_y, float offset_x, float offset_y) const; - /// Returns the default button bindings list for generic controllers - ButtonBindings GetDefaultButtonBinding() const; - - /// Returns the default button bindings list for nintendo controllers - ButtonBindings GetNintendoButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const; + /// Returns the default button bindings list + ButtonBindings GetDefaultButtonBinding(const std::shared_ptr<SDLJoystick>& joystick) const; /// Returns the button mappings from a single controller ButtonMapping GetSingleControllerMapping(const std::shared_ptr<SDLJoystick>& joystick, diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 3b2fe01da..7f79111e0 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -274,6 +274,7 @@ add_library(video_core STATIC vulkan_common/vulkan_wrapper.h vulkan_common/nsight_aftermath_tracker.cpp vulkan_common/nsight_aftermath_tracker.h + vulkan_common/vma.cpp ) create_target_directory_groups(video_core) @@ -291,7 +292,7 @@ target_link_options(video_core PRIVATE ${FFmpeg_LDFLAGS}) add_dependencies(video_core host_shaders) target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE}) -target_link_libraries(video_core PRIVATE sirit Vulkan::Headers vma) +target_link_libraries(video_core PRIVATE sirit Vulkan::Headers GPUOpen::VulkanMemoryAllocator) if (ENABLE_NSIGHT_AFTERMATH) if (NOT DEFINED ENV{NSIGHT_AFTERMATH_SDK}) @@ -324,6 +325,9 @@ else() # xbyak set_source_files_properties(macro/macro_jit_x64.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-shadow") + + # VMA + set_source_files_properties(vulkan_common/vma.cpp PROPERTIES COMPILE_OPTIONS "-Wno-conversion;-Wno-unused-variable;-Wno-unused-parameter;-Wno-missing-field-initializers") endif() if (ARCHITECTURE_x86_64) diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index b5ed3380f..f0f450edb 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -234,9 +234,10 @@ bool BufferCache<P>::DMACopy(GPUVAddr src_address, GPUVAddr dest_address, u64 am if (has_new_downloads) { memory_tracker.MarkRegionAsGpuModified(*cpu_dest_address, amount); } - tmp_buffer.resize_destructive(amount); - cpu_memory.ReadBlockUnsafe(*cpu_src_address, tmp_buffer.data(), amount); - cpu_memory.WriteBlockUnsafe(*cpu_dest_address, tmp_buffer.data(), amount); + + Core::Memory::CpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite> tmp( + cpu_memory, *cpu_src_address, amount, &tmp_buffer); + tmp.SetAddressAndSize(*cpu_dest_address, amount); return true; } @@ -441,6 +442,11 @@ void BufferCache<P>::UnbindComputeStorageBuffers() { template <class P> void BufferCache<P>::BindComputeStorageBuffer(size_t ssbo_index, u32 cbuf_index, u32 cbuf_offset, bool is_written) { + if (ssbo_index >= channel_state->compute_storage_buffers.size()) [[unlikely]] { + LOG_ERROR(HW_GPU, "Storage buffer index {} exceeds maximum storage buffer count", + ssbo_index); + return; + } channel_state->enabled_compute_storage_buffers |= 1U << ssbo_index; channel_state->written_compute_storage_buffers |= (is_written ? 1U : 0U) << ssbo_index; @@ -463,6 +469,11 @@ void BufferCache<P>::UnbindComputeTextureBuffers() { template <class P> void BufferCache<P>::BindComputeTextureBuffer(size_t tbo_index, GPUVAddr gpu_addr, u32 size, PixelFormat format, bool is_written, bool is_image) { + if (tbo_index >= channel_state->compute_texture_buffers.size()) [[unlikely]] { + LOG_ERROR(HW_GPU, "Texture buffer index {} exceeds maximum texture buffer count", + tbo_index); + return; + } channel_state->enabled_compute_texture_buffers |= 1U << tbo_index; channel_state->written_compute_texture_buffers |= (is_written ? 1U : 0U) << tbo_index; if constexpr (SEPARATE_IMAGE_BUFFERS_BINDINGS) { diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 460fc7551..0b7135d49 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -67,7 +67,7 @@ constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4; constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18; constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8; constexpr u32 NUM_STORAGE_BUFFERS = 16; -constexpr u32 NUM_TEXTURE_BUFFERS = 16; +constexpr u32 NUM_TEXTURE_BUFFERS = 32; constexpr u32 NUM_STAGES = 5; using UniformBufferSizes = std::array<std::array<u32, NUM_GRAPHICS_UNIFORM_BUFFERS>, NUM_STAGES>; diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 551929824..9f1b340a9 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp @@ -5,6 +5,7 @@ #include "common/microprofile.h" #include "common/settings.h" #include "core/core.h" +#include "core/memory.h" #include "video_core/dma_pusher.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/gpu.h" @@ -12,6 +13,8 @@ namespace Tegra { +constexpr u32 MacroRegistersStart = 0xE00; + DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, Control::ChannelState& channel_state_) : gpu{gpu_}, system{system_}, memory_manager{memory_manager_}, puller{gpu_, memory_manager_, @@ -74,25 +77,16 @@ bool DmaPusher::Step() { } // Push buffer non-empty, read a word - command_headers.resize_destructive(command_list_header.size); - constexpr u32 MacroRegistersStart = 0xE00; - if (dma_state.method < MacroRegistersStart) { - if (Settings::IsGPULevelHigh()) { - memory_manager.ReadBlock(dma_state.dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); - } else { - memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), - command_list_header.size * sizeof(u32)); - } - } else { - const size_t copy_size = command_list_header.size * sizeof(u32); + if (dma_state.method >= MacroRegistersStart) { if (subchannels[dma_state.subchannel]) { - subchannels[dma_state.subchannel]->current_dirty = - memory_manager.IsMemoryDirty(dma_state.dma_get, copy_size); + subchannels[dma_state.subchannel]->current_dirty = memory_manager.IsMemoryDirty( + dma_state.dma_get, command_list_header.size * sizeof(u32)); } - memory_manager.ReadBlockUnsafe(dma_state.dma_get, command_headers.data(), copy_size); } - ProcessCommands(command_headers); + Core::Memory::GpuGuestMemory<Tegra::CommandHeader, + Core::Memory::GuestMemoryFlags::UnsafeRead> + headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers); + ProcessCommands(headers); } return true; diff --git a/src/video_core/engines/engine_upload.cpp b/src/video_core/engines/engine_upload.cpp index 7f5a0c29d..bc64d4486 100644 --- a/src/video_core/engines/engine_upload.cpp +++ b/src/video_core/engines/engine_upload.cpp @@ -5,6 +5,7 @@ #include "common/algorithm.h" #include "common/assert.h" +#include "core/memory.h" #include "video_core/engines/engine_upload.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" @@ -46,15 +47,11 @@ void State::ProcessData(const u32* data, size_t num_data) { void State::ProcessData(std::span<const u8> read_buffer) { const GPUVAddr address{regs.dest.Address()}; if (is_linear) { - if (regs.line_count == 1) { - rasterizer->AccelerateInlineToMemory(address, copy_size, read_buffer); - } else { - for (size_t line = 0; line < regs.line_count; ++line) { - const GPUVAddr dest_line = address + line * regs.dest.pitch; - std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in, - regs.line_length_in); - rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer); - } + for (size_t line = 0; line < regs.line_count; ++line) { + const GPUVAddr dest_line = address + line * regs.dest.pitch; + std::span<const u8> buffer(read_buffer.data() + line * regs.line_length_in, + regs.line_length_in); + rasterizer->AccelerateInlineToMemory(dest_line, regs.line_length_in, buffer); } } else { u32 width = regs.dest.width; @@ -70,13 +67,14 @@ void State::ProcessData(std::span<const u8> read_buffer) { const std::size_t dst_size = Tegra::Texture::CalculateSize( true, bytes_per_pixel, width, regs.dest.height, regs.dest.depth, regs.dest.BlockHeight(), regs.dest.BlockDepth()); - tmp_buffer.resize_destructive(dst_size); - memory_manager.ReadBlock(address, tmp_buffer.data(), dst_size); - Tegra::Texture::SwizzleSubrect(tmp_buffer, read_buffer, bytes_per_pixel, width, - regs.dest.height, regs.dest.depth, x_offset, regs.dest.y, - x_elements, regs.line_count, regs.dest.BlockHeight(), + + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp(memory_manager, address, dst_size, &tmp_buffer); + + Tegra::Texture::SwizzleSubrect(tmp, read_buffer, bytes_per_pixel, width, regs.dest.height, + regs.dest.depth, x_offset, regs.dest.y, x_elements, + regs.line_count, regs.dest.BlockHeight(), regs.dest.BlockDepth(), regs.line_length_in); - memory_manager.WriteBlockCached(address, tmp_buffer.data(), dst_size); } } diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index 601095f03..a38d9528a 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp @@ -84,7 +84,6 @@ Texture::TICEntry KeplerCompute::GetTICEntry(u32 tic_index) const { Texture::TICEntry tic_entry; memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); - return tic_entry; } diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 62d70e9f3..c3696096d 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -9,6 +9,7 @@ #include "common/settings.h" #include "core/core.h" #include "core/core_timing.h" +#include "core/memory.h" #include "video_core/dirty_flags.h" #include "video_core/engines/draw_manager.h" #include "video_core/engines/maxwell_3d.h" @@ -679,17 +680,14 @@ void Maxwell3D::ProcessCBData(u32 value) { Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const { const GPUVAddr tic_address_gpu{regs.tex_header.Address() + tic_index * sizeof(Texture::TICEntry)}; - Texture::TICEntry tic_entry; memory_manager.ReadBlockUnsafe(tic_address_gpu, &tic_entry, sizeof(Texture::TICEntry)); - return tic_entry; } Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { const GPUVAddr tsc_address_gpu{regs.tex_sampler.Address() + tsc_index * sizeof(Texture::TSCEntry)}; - Texture::TSCEntry tsc_entry; memory_manager.ReadBlockUnsafe(tsc_address_gpu, &tsc_entry, sizeof(Texture::TSCEntry)); return tsc_entry; diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index f8598fd98..cd8e24b0b 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp @@ -7,6 +7,7 @@ #include "common/microprofile.h" #include "common/settings.h" #include "core/core.h" +#include "core/memory.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/engines/maxwell_dma.h" #include "video_core/memory_manager.h" @@ -130,11 +131,12 @@ void MaxwellDMA::Launch() { UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); read_buffer.resize_destructive(16); for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { - memory_manager.ReadBlock( - convert_linear_2_blocklinear_addr(regs.offset_in + offset), - read_buffer.data(), read_buffer.size()); - memory_manager.WriteBlockCached(regs.offset_out + offset, read_buffer.data(), - read_buffer.size()); + Core::Memory::GpuGuestMemoryScoped< + u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, + convert_linear_2_blocklinear_addr(regs.offset_in + offset), + 16, &read_buffer); + tmp_write_buffer.SetAddressAndSize(regs.offset_out + offset, 16); } } else if (is_src_pitch && !is_dst_pitch) { UNIMPLEMENTED_IF(regs.line_length_in % 16 != 0); @@ -142,20 +144,19 @@ void MaxwellDMA::Launch() { UNIMPLEMENTED_IF(regs.offset_out % 16 != 0); read_buffer.resize_destructive(16); for (u32 offset = 0; offset < regs.line_length_in; offset += 16) { - memory_manager.ReadBlock(regs.offset_in + offset, read_buffer.data(), - read_buffer.size()); - memory_manager.WriteBlockCached( - convert_linear_2_blocklinear_addr(regs.offset_out + offset), - read_buffer.data(), read_buffer.size()); + Core::Memory::GpuGuestMemoryScoped< + u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, regs.offset_in + offset, 16, &read_buffer); + tmp_write_buffer.SetAddressAndSize( + convert_linear_2_blocklinear_addr(regs.offset_out + offset), 16); } } else { if (!accelerate.BufferCopy(regs.offset_in, regs.offset_out, regs.line_length_in)) { - read_buffer.resize_destructive(regs.line_length_in); - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), - regs.line_length_in, - VideoCommon::CacheType::NoBufferCache); - memory_manager.WriteBlockCached(regs.offset_out, read_buffer.data(), - regs.line_length_in); + Core::Memory::GpuGuestMemoryScoped< + u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, regs.offset_in, regs.line_length_in, + &read_buffer); + tmp_write_buffer.SetAddressAndSize(regs.offset_out, regs.line_length_in); } } } @@ -222,17 +223,15 @@ void MaxwellDMA::CopyBlockLinearToPitch() { CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); const size_t dst_size = dst_operand.pitch * regs.line_count; - read_buffer.resize_destructive(src_size); - write_buffer.resize_destructive(dst_size); - memory_manager.ReadBlock(src_operand.address, read_buffer.data(), src_size); - memory_manager.ReadBlock(dst_operand.address, write_buffer.data(), dst_size); + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( + memory_manager, src_operand.address, src_size, &read_buffer); + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, dst_operand.address, dst_size, &write_buffer); - UnswizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, - src_params.origin.y, x_elements, regs.line_count, block_height, block_depth, - dst_operand.pitch); - - memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); + UnswizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth, + x_offset, src_params.origin.y, x_elements, regs.line_count, block_height, + block_depth, dst_operand.pitch); } void MaxwellDMA::CopyPitchToBlockLinear() { @@ -287,18 +286,17 @@ void MaxwellDMA::CopyPitchToBlockLinear() { CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth); const size_t src_size = static_cast<size_t>(regs.pitch_in) * regs.line_count; - read_buffer.resize_destructive(src_size); - write_buffer.resize_destructive(dst_size); - - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); - memory_manager.ReadBlockUnsafe(regs.offset_out, write_buffer.data(), dst_size); - - // If the input is linear and the output is tiled, swizzle the input and copy it over. - SwizzleSubrect(write_buffer, read_buffer, bytes_per_pixel, width, height, depth, x_offset, - dst_params.origin.y, x_elements, regs.line_count, block_height, block_depth, - regs.pitch_in); - - memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); + GPUVAddr src_addr = regs.offset_in; + GPUVAddr dst_addr = regs.offset_out; + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( + memory_manager, src_addr, src_size, &read_buffer); + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, dst_addr, dst_size, &write_buffer); + + // If the input is linear and the output is tiled, swizzle the input and copy it over. + SwizzleSubrect(tmp_write_buffer, tmp_read_buffer, bytes_per_pixel, width, height, depth, + x_offset, dst_params.origin.y, x_elements, regs.line_count, block_height, + block_depth, regs.pitch_in); } void MaxwellDMA::CopyBlockLinearToBlockLinear() { @@ -342,23 +340,20 @@ void MaxwellDMA::CopyBlockLinearToBlockLinear() { const u32 pitch = x_elements * bytes_per_pixel; const size_t mid_buffer_size = pitch * regs.line_count; - read_buffer.resize_destructive(src_size); - write_buffer.resize_destructive(dst_size); - intermediate_buffer.resize_destructive(mid_buffer_size); - memory_manager.ReadBlock(regs.offset_in, read_buffer.data(), src_size); - memory_manager.ReadBlock(regs.offset_out, write_buffer.data(), dst_size); + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_read_buffer( + memory_manager, regs.offset_in, src_size, &read_buffer); + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadCachedWrite> + tmp_write_buffer(memory_manager, regs.offset_out, dst_size, &write_buffer); - UnswizzleSubrect(intermediate_buffer, read_buffer, bytes_per_pixel, src_width, src.height, + UnswizzleSubrect(intermediate_buffer, tmp_read_buffer, bytes_per_pixel, src_width, src.height, src.depth, src_x_offset, src.origin.y, x_elements, regs.line_count, src.block_size.height, src.block_size.depth, pitch); - SwizzleSubrect(write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height, + SwizzleSubrect(tmp_write_buffer, intermediate_buffer, bytes_per_pixel, dst_width, dst.height, dst.depth, dst_x_offset, dst.origin.y, x_elements, regs.line_count, dst.block_size.height, dst.block_size.depth, pitch); - - memory_manager.WriteBlockCached(regs.offset_out, write_buffer.data(), dst_size); } void MaxwellDMA::ReleaseSemaphore() { diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp index ff88cd03d..3a599f466 100644 --- a/src/video_core/engines/sw_blitter/blitter.cpp +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -159,11 +159,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, const auto src_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(src.format)); const auto dst_bytes_per_pixel = BytesPerBlock(PixelFormatFromRenderTargetFormat(dst.format)); const size_t src_size = get_surface_size(src, src_bytes_per_pixel); - impl->tmp_buffer.resize_destructive(src_size); - memory_manager.ReadBlock(src.Address(), impl->tmp_buffer.data(), src_size); - const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel; + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead> tmp_buffer( + memory_manager, src.Address(), src_size, &impl->tmp_buffer); + const size_t src_copy_size = src_extent_x * src_extent_y * src_bytes_per_pixel; const size_t dst_copy_size = dst_extent_x * dst_extent_y * dst_bytes_per_pixel; impl->src_buffer.resize_destructive(src_copy_size); @@ -200,12 +200,11 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, impl->dst_buffer.resize_destructive(dst_copy_size); if (src.linear == Fermi2D::MemoryLayout::BlockLinear) { - UnswizzleSubrect(impl->src_buffer, impl->tmp_buffer, src_bytes_per_pixel, src.width, - src.height, src.depth, config.src_x0, config.src_y0, src_extent_x, - src_extent_y, src.block_height, src.block_depth, - src_extent_x * src_bytes_per_pixel); + UnswizzleSubrect(impl->src_buffer, tmp_buffer, src_bytes_per_pixel, src.width, src.height, + src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, + src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); } else { - process_pitch_linear(false, impl->tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, + process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); } @@ -221,20 +220,18 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, } const size_t dst_size = get_surface_size(dst, dst_bytes_per_pixel); - impl->tmp_buffer.resize_destructive(dst_size); - memory_manager.ReadBlock(dst.Address(), impl->tmp_buffer.data(), dst_size); + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::SafeReadWrite> + tmp_buffer2(memory_manager, dst.Address(), dst_size, &impl->tmp_buffer); if (dst.linear == Fermi2D::MemoryLayout::BlockLinear) { - SwizzleSubrect(impl->tmp_buffer, impl->dst_buffer, dst_bytes_per_pixel, dst.width, - dst.height, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, - dst_extent_y, dst.block_height, dst.block_depth, - dst_extent_x * dst_bytes_per_pixel); + SwizzleSubrect(tmp_buffer2, impl->dst_buffer, dst_bytes_per_pixel, dst.width, dst.height, + dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, + dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); } else { - process_pitch_linear(true, impl->dst_buffer, impl->tmp_buffer, dst_extent_x, dst_extent_y, + process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, dst.pitch, config.dst_x0, config.dst_y0, static_cast<size_t>(dst_bytes_per_pixel)); } - memory_manager.WriteBlock(dst.Address(), impl->tmp_buffer.data(), dst_size); return true; } diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 45141e488..d16040613 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -10,13 +10,13 @@ #include "core/device_memory.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" -#include "core/memory.h" #include "video_core/invalidation_accumulator.h" #include "video_core/memory_manager.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_base.h" namespace Tegra { +using Core::Memory::GuestMemoryFlags; std::atomic<size_t> MemoryManager::unique_identifier_generator{}; @@ -587,13 +587,10 @@ void MemoryManager::InvalidateRegion(GPUVAddr gpu_addr, size_t size, void MemoryManager::CopyBlock(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_addr, std::size_t size, VideoCommon::CacheType which) { - tmp_buffer.resize_destructive(size); - ReadBlock(gpu_src_addr, tmp_buffer.data(), size, which); - - // The output block must be flushed in case it has data modified from the GPU. - // Fixes NPC geometry in Zombie Panic in Wonderland DX + Core::Memory::GpuGuestMemoryScoped<u8, GuestMemoryFlags::SafeReadWrite> data( + *this, gpu_src_addr, size); + data.SetAddressAndSize(gpu_dest_addr, size); FlushRegion(gpu_dest_addr, size, which); - WriteBlock(gpu_dest_addr, tmp_buffer.data(), size, which); } bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const { @@ -758,4 +755,23 @@ void MemoryManager::FlushCaching() { accumulator->Clear(); } +const u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) const { + auto cpu_addr = GpuToCpuAddress(src_addr); + if (cpu_addr) { + return memory.GetSpan(*cpu_addr, size); + } + return nullptr; +} + +u8* MemoryManager::GetSpan(const GPUVAddr src_addr, const std::size_t size) { + if (!IsContinuousRange(src_addr, size)) { + return nullptr; + } + auto cpu_addr = GpuToCpuAddress(src_addr); + if (cpu_addr) { + return memory.GetSpan(*cpu_addr, size); + } + return nullptr; +} + } // namespace Tegra diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 4202c26ff..9b311b9e5 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -15,6 +15,7 @@ #include "common/range_map.h" #include "common/scratch_buffer.h" #include "common/virtual_buffer.h" +#include "core/memory.h" #include "video_core/cache_types.h" #include "video_core/pte_kind.h" @@ -62,6 +63,20 @@ public: [[nodiscard]] u8* GetPointer(GPUVAddr addr); [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const; + template <typename T> + [[nodiscard]] T* GetPointer(GPUVAddr addr) { + const auto address{GpuToCpuAddress(addr)}; + if (!address) { + return {}; + } + return memory.GetPointer(*address); + } + + template <typename T> + [[nodiscard]] const T* GetPointer(GPUVAddr addr) const { + return GetPointer<T*>(addr); + } + /** * ReadBlock and WriteBlock are full read and write operations over virtual * GPU Memory. It's important to use these when GPU memory may not be continuous @@ -139,6 +154,9 @@ public: void FlushCaching(); + const u8* GetSpan(const GPUVAddr src_addr, const std::size_t size) const; + u8* GetSpan(const GPUVAddr src_addr, const std::size_t size); + private: template <bool is_big_pages, typename FuncMapped, typename FuncReserved, typename FuncUnmapped> inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped, diff --git a/src/video_core/renderer_base.cpp b/src/video_core/renderer_base.cpp index 2d3f58201..4002fa72b 100644 --- a/src/video_core/renderer_base.cpp +++ b/src/video_core/renderer_base.cpp @@ -38,8 +38,8 @@ void RendererBase::RequestScreenshot(void* data, std::function<void(bool)> callb LOG_ERROR(Render, "A screenshot is already requested or in progress, ignoring the request"); return; } - auto async_callback{[callback = std::move(callback)](bool invert_y) { - std::thread t{callback, invert_y}; + auto async_callback{[callback_ = std::move(callback)](bool invert_y) { + std::thread t{callback_, invert_y}; t.detach(); }}; renderer_settings.screenshot_bits = data; diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp index 23a48c6fe..71f720c63 100644 --- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp +++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp @@ -231,24 +231,25 @@ GraphicsPipeline::GraphicsPipeline(const Device& device, TextureCache& texture_c } const bool in_parallel = thread_worker != nullptr; const auto backend = device.GetShaderBackend(); - auto func{[this, sources = std::move(sources), sources_spirv = std::move(sources_spirv), + auto func{[this, sources_ = std::move(sources), sources_spirv_ = std::move(sources_spirv), shader_notify, backend, in_parallel, force_context_flush](ShaderContext::Context*) mutable { for (size_t stage = 0; stage < 5; ++stage) { switch (backend) { case Settings::ShaderBackend::GLSL: - if (!sources[stage].empty()) { - source_programs[stage] = CreateProgram(sources[stage], Stage(stage)); + if (!sources_[stage].empty()) { + source_programs[stage] = CreateProgram(sources_[stage], Stage(stage)); } break; case Settings::ShaderBackend::GLASM: - if (!sources[stage].empty()) { - assembly_programs[stage] = CompileProgram(sources[stage], AssemblyStage(stage)); + if (!sources_[stage].empty()) { + assembly_programs[stage] = + CompileProgram(sources_[stage], AssemblyStage(stage)); } break; case Settings::ShaderBackend::SPIRV: - if (!sources_spirv[stage].empty()) { - source_programs[stage] = CreateProgram(sources_spirv[stage], Stage(stage)); + if (!sources_spirv_[stage].empty()) { + source_programs[stage] = CreateProgram(sources_spirv_[stage], Stage(stage)); } break; } diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 0329ed820..7e1d7f92e 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp @@ -288,9 +288,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, const auto load_compute{[&](std::ifstream& file, FileEnvironment env) { ComputePipelineKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - queue_work([this, key, env = std::move(env), &state, &callback](Context* ctx) mutable { + queue_work([this, key, env_ = std::move(env), &state, &callback](Context* ctx) mutable { ctx->pools.ReleaseContents(); - auto pipeline{CreateComputePipeline(ctx->pools, key, env, true)}; + auto pipeline{CreateComputePipeline(ctx->pools, key, env_, true)}; std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); @@ -305,9 +305,9 @@ void ShaderCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading, const auto load_graphics{[&](std::ifstream& file, std::vector<FileEnvironment> envs) { GraphicsPipelineKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - queue_work([this, key, envs = std::move(envs), &state, &callback](Context* ctx) mutable { + queue_work([this, key, envs_ = std::move(envs), &state, &callback](Context* ctx) mutable { boost::container::static_vector<Shader::Environment*, 5> env_ptrs; - for (auto& env : envs) { + for (auto& env : envs_) { env_ptrs.push_back(&env); } ctx->pools.ReleaseContents(); diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 51df18ec3..f8cd2a5d8 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -206,8 +206,8 @@ public: const size_t sub_first_offset = static_cast<size_t>(first % 4) * GetQuadsNum(num_indices); const size_t offset = (sub_first_offset + GetQuadsNum(first)) * 6ULL * BytesPerIndex(index_type); - scheduler.Record([buffer = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) { - cmdbuf.BindIndexBuffer(buffer, offset, index_type_); + scheduler.Record([buffer_ = *buffer, index_type_, offset](vk::CommandBuffer cmdbuf) { + cmdbuf.BindIndexBuffer(buffer_, offset, index_type_); }); } @@ -528,17 +528,18 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi buffer_handles.push_back(handle); } if (device.IsExtExtendedDynamicStateSupported()) { - scheduler.Record([bindings = std::move(bindings), - buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers2EXT( - bindings.min_index, bindings.max_index - bindings.min_index, buffer_handles.data(), - bindings.offsets.data(), bindings.sizes.data(), bindings.strides.data()); + scheduler.Record([bindings_ = std::move(bindings), + buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, + bindings_.max_index - bindings_.min_index, + buffer_handles_.data(), bindings_.offsets.data(), + bindings_.sizes.data(), bindings_.strides.data()); }); } else { - scheduler.Record([bindings = std::move(bindings), - buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { - cmdbuf.BindVertexBuffers(bindings.min_index, bindings.max_index - bindings.min_index, - buffer_handles.data(), bindings.offsets.data()); + scheduler.Record([bindings_ = std::move(bindings), + buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { + cmdbuf.BindVertexBuffers(bindings_.min_index, bindings_.max_index - bindings_.min_index, + buffer_handles_.data(), bindings_.offsets.data()); }); } } @@ -573,11 +574,11 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings< for (u32 index = 0; index < bindings.buffers.size(); ++index) { buffer_handles.push_back(bindings.buffers[index]->Handle()); } - scheduler.Record([bindings = std::move(bindings), - buffer_handles = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { - cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles.size()), - buffer_handles.data(), bindings.offsets.data(), - bindings.sizes.data()); + scheduler.Record([bindings_ = std::move(bindings), + buffer_handles_ = std::move(buffer_handles)](vk::CommandBuffer cmdbuf) { + cmdbuf.BindTransformFeedbackBuffersEXT(0, static_cast<u32>(buffer_handles_.size()), + buffer_handles_.data(), bindings_.offsets.data(), + bindings_.sizes.data()); }); } diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index d600c4e61..4f84d8497 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -469,9 +469,9 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading ComputePipelineCacheKey key; file.read(reinterpret_cast<char*>(&key), sizeof(key)); - workers.QueueWork([this, key, env = std::move(env), &state, &callback]() mutable { + workers.QueueWork([this, key, env_ = std::move(env), &state, &callback]() mutable { ShaderPools pools; - auto pipeline{CreateComputePipeline(pools, key, env, state.statistics.get(), false)}; + auto pipeline{CreateComputePipeline(pools, key, env_, state.statistics.get(), false)}; std::scoped_lock lock{state.mutex}; if (pipeline) { compute_cache.emplace(key, std::move(pipeline)); @@ -500,10 +500,10 @@ void PipelineCache::LoadDiskResources(u64 title_id, std::stop_token stop_loading (key.state.dynamic_vertex_input != 0) != dynamic_features.has_dynamic_vertex_input) { return; } - workers.QueueWork([this, key, envs = std::move(envs), &state, &callback]() mutable { + workers.QueueWork([this, key, envs_ = std::move(envs), &state, &callback]() mutable { ShaderPools pools; boost::container::static_vector<Shader::Environment*, 5> env_ptrs; - for (auto& env : envs) { + for (auto& env : envs_) { env_ptrs.push_back(&env); } auto pipeline{CreateGraphicsPipeline(pools, key, MakeSpan(env_ptrs), @@ -702,8 +702,8 @@ std::unique_ptr<ComputePipeline> PipelineCache::CreateComputePipeline( if (!pipeline || pipeline_cache_filename.empty()) { return pipeline; } - serialization_thread.QueueWork([this, key, env = std::move(env)] { - SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env}, + serialization_thread.QueueWork([this, key, env_ = std::move(env)] { + SerializePipeline(key, std::array<const GenericEnvironment*, 1>{&env_}, pipeline_cache_filename, CACHE_VERSION); }); return pipeline; diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index d67490449..29e0b797b 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -98,10 +98,10 @@ HostCounter::HostCounter(QueryCache& cache_, std::shared_ptr<HostCounter> depend : HostCounterBase{std::move(dependency_)}, cache{cache_}, type{type_}, query{cache_.AllocateQuery(type_)}, tick{cache_.GetScheduler().CurrentTick()} { const vk::Device* logical = &cache.GetDevice().GetLogical(); - cache.GetScheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) { + cache.GetScheduler().Record([logical, query_ = query](vk::CommandBuffer cmdbuf) { const bool use_precise = Settings::IsGPULevelHigh(); - logical->ResetQueryPool(query.first, query.second, 1); - cmdbuf.BeginQuery(query.first, query.second, + logical->ResetQueryPool(query_.first, query_.second, 1); + cmdbuf.BeginQuery(query_.first, query_.second, use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); }); } @@ -111,8 +111,9 @@ HostCounter::~HostCounter() { } void HostCounter::EndQuery() { - cache.GetScheduler().Record( - [query = query](vk::CommandBuffer cmdbuf) { cmdbuf.EndQuery(query.first, query.second); }); + cache.GetScheduler().Record([query_ = query](vk::CommandBuffer cmdbuf) { + cmdbuf.EndQuery(query_.first, query_.second); + }); } u64 HostCounter::BlockingQuery(bool async) const { diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 3aac3cfab..bf6ad6c79 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1412,7 +1412,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS } scheduler->RequestOutsideRenderPassOperationContext(); scheduler->Record([buffers = std::move(buffers_vector), image = *original_image, - aspect_mask = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { + aspect_mask_ = aspect_mask, vk_copies](vk::CommandBuffer cmdbuf) { const VkImageMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .pNext = nullptr, @@ -1424,7 +1424,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange{ - .aspectMask = aspect_mask, + .aspectMask = aspect_mask_, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, @@ -1456,7 +1456,7 @@ void Image::DownloadMemory(std::span<VkBuffer> buffers_span, std::span<VkDeviceS .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange{ - .aspectMask = aspect_mask, + .aspectMask = aspect_mask_, .baseMipLevel = 0, .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 3a859139c..4457b366f 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -8,6 +8,7 @@ #include "common/alignment.h" #include "common/settings.h" +#include "core/memory.h" #include "video_core/control/channel_state.h" #include "video_core/dirty_flags.h" #include "video_core/engines/kepler_compute.h" @@ -1026,19 +1027,19 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging) runtime.AccelerateImageUpload(image, staging, uploads); return; } - const size_t guest_size_bytes = image.guest_size_bytes; - swizzle_data_buffer.resize_destructive(guest_size_bytes); - gpu_memory->ReadBlockUnsafe(gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); + + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data( + *gpu_memory, gpu_addr, image.guest_size_bytes, &swizzle_data_buffer); if (True(image.flags & ImageFlagBits::Converted)) { unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); - auto copies = UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, - unswizzle_data_buffer); + auto copies = + UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, unswizzle_data_buffer); ConvertImage(unswizzle_data_buffer, image.info, mapped_span, copies); image.UploadMemory(staging, copies); } else { const auto copies = - UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data_buffer, mapped_span); + UnswizzleImage(*gpu_memory, gpu_addr, image.info, swizzle_data, mapped_span); image.UploadMemory(staging, copies); } } @@ -1231,11 +1232,12 @@ void TextureCache<P>::QueueAsyncDecode(Image& image, ImageId image_id) { decode->image_id = image_id; async_decodes.push_back(std::move(decode)); - Common::ScratchBuffer<u8> local_unswizzle_data_buffer(image.unswizzled_size_bytes); - const size_t guest_size_bytes = image.guest_size_bytes; - swizzle_data_buffer.resize_destructive(guest_size_bytes); - gpu_memory->ReadBlockUnsafe(image.gpu_addr, swizzle_data_buffer.data(), guest_size_bytes); - auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data_buffer, + static Common::ScratchBuffer<u8> local_unswizzle_data_buffer; + local_unswizzle_data_buffer.resize_destructive(image.unswizzled_size_bytes); + Core::Memory::GpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::UnsafeRead> swizzle_data( + *gpu_memory, image.gpu_addr, image.guest_size_bytes, &swizzle_data_buffer); + + auto copies = UnswizzleImage(*gpu_memory, image.gpu_addr, image.info, swizzle_data, local_unswizzle_data_buffer); const size_t out_size = MapSizeBytes(image); diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp index 0de6ed09d..a83f5d41c 100644 --- a/src/video_core/texture_cache/util.cpp +++ b/src/video_core/texture_cache/util.cpp @@ -20,6 +20,7 @@ #include "common/div_ceil.h" #include "common/scratch_buffer.h" #include "common/settings.h" +#include "core/memory.h" #include "video_core/compatible_formats.h" #include "video_core/engines/maxwell_3d.h" #include "video_core/memory_manager.h" @@ -544,17 +545,15 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr tile_size.height, info.tile_width_spacing); const size_t subresource_size = sizes[level]; - tmp_buffer.resize_destructive(subresource_size); - const std::span<u8> dst(tmp_buffer); - for (s32 layer = 0; layer < info.resources.layers; ++layer) { const std::span<const u8> src = input.subspan(host_offset); - gpu_memory.ReadBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes()); - - SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height, - num_tiles.depth, block.height, block.depth); + { + Core::Memory::GpuGuestMemoryScoped<u8, Core::Memory::GuestMemoryFlags::UnsafeReadWrite> + dst(gpu_memory, gpu_addr + guest_offset, subresource_size, &tmp_buffer); - gpu_memory.WriteBlockUnsafe(gpu_addr + guest_offset, dst.data(), dst.size_bytes()); + SwizzleTexture(dst, src, bytes_per_block, num_tiles.width, num_tiles.height, + num_tiles.depth, block.height, block.depth); + } host_offset += host_bytes_per_layer; guest_offset += layer_stride; @@ -837,6 +836,7 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory const Extent3D size = info.size; if (info.type == ImageType::Linear) { + ASSERT(output.size_bytes() >= guest_size_bytes); gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes); ASSERT((info.pitch >> bpp_log2) << bpp_log2 == info.pitch); @@ -904,16 +904,6 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory return copies; } -BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, - const ImageBase& image, std::span<u8> output) { - gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), image.guest_size_bytes); - return BufferCopy{ - .src_offset = 0, - .dst_offset = 0, - .size = image.guest_size_bytes, - }; -} - void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, std::span<BufferImageCopy> copies) { u32 output_offset = 0; diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h index ab45a43c4..5a0649d24 100644 --- a/src/video_core/texture_cache/util.h +++ b/src/video_core/texture_cache/util.h @@ -66,9 +66,6 @@ struct OverlapResult { Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info, std::span<const u8> input, std::span<u8> output); -[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, - const ImageBase& image, std::span<u8> output); - void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output, std::span<BufferImageCopy> copies); diff --git a/externals/vma/vma.cpp b/src/video_core/vulkan_common/vma.cpp index 1fe2cf52b..1fe2cf52b 100644 --- a/externals/vma/vma.cpp +++ b/src/video_core/vulkan_common/vma.cpp diff --git a/src/web_service/announce_room_json.cpp b/src/web_service/announce_room_json.cpp index 4c3195efd..f1020a5b8 100644 --- a/src/web_service/announce_room_json.cpp +++ b/src/web_service/announce_room_json.cpp @@ -135,11 +135,11 @@ void RoomJson::Delete() { LOG_ERROR(WebService, "Room must be registered to be deleted"); return; } - Common::DetachedTasks::AddTask( - [host{this->host}, username{this->username}, token{this->token}, room_id{this->room_id}]() { - // create a new client here because the this->client might be destroyed. - Client{host, username, token}.DeleteJson(fmt::format("/lobby/{}", room_id), "", false); - }); + Common::DetachedTasks::AddTask([host_{this->host}, username_{this->username}, + token_{this->token}, room_id_{this->room_id}]() { + // create a new client here because the this->client might be destroyed. + Client{host_, username_, token_}.DeleteJson(fmt::format("/lobby/{}", room_id_), "", false); + }); } } // namespace WebService diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 63326968b..5c910c9e0 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp @@ -235,7 +235,7 @@ GameListWorker::~GameListWorker() = default; void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { using namespace FileSys; - const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider()); + const auto& cache = system.GetContentProviderUnion(); auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application, ContentRecordType::Program); |