diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index d71f10523bc..2a94ef54eb2 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -967,6 +967,23 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +License for remote stack unwind (https://github.com/llvm/llvm-project/blob/main/lldb/source/Symbol/CompactUnwindInfo.cpp) +-------------------------------------- + +Copyright 2019 LLVM Project + +Licensed under the Apache License, Version 2.0 (the "License") with LLVM Exceptions; +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +https://llvm.org/LICENSE.txt + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + License notice for Apple header files ------------------------------------- diff --git a/src/coreclr/pal/src/exception/remote-unwind.cpp b/src/coreclr/pal/src/exception/remote-unwind.cpp index e8f3ac005e4..514851fa224 100644 --- a/src/coreclr/pal/src/exception/remote-unwind.cpp +++ b/src/coreclr/pal/src/exception/remote-unwind.cpp @@ -1310,6 +1310,14 @@ GetProcInfo(unw_word_t ip, unw_proc_info_t *pip, libunwindInfo* info, bool* step return false; } +//===-- CompactUnwindInfo.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + #if defined(TARGET_AMD64) static bool @@ -1381,6 +1389,163 @@ StepWithCompactEncodingRBPFrame(const libunwindInfo* info, compact_unwind_encodi return true; } +static bool +StepWithCompactEncodingFrameless(const libunwindInfo* info, compact_unwind_encoding_t compactEncoding, unw_word_t functionStart) +{ + int mode = compactEncoding & UNWIND_X86_64_MODE_MASK; + CONTEXT* context = info->Context; + + uint32_t stack_size = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t register_count = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + + if (mode == UNWIND_X86_64_MODE_STACK_IND) + { + _ASSERTE(functionStart != 0); + unw_word_t addr = functionStart + stack_size; + if (!ReadValue32(info, &addr, &stack_size)) { + return false; + } + uint32_t stack_adjust = EXTRACT_BITS(compactEncoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + stack_size += stack_adjust * 8; + } + else + { + stack_size *= 8; + } + + TRACE("Frameless function: encoding %08x stack size %d register count %d\n", compactEncoding, stack_size, register_count); + + // We need to include (up to) 6 registers in 10 bits. + // That would be 18 bits if we just used 3 bits per reg to indicate + // the order they're saved on the stack. + // + // This is done with Lehmer code permutation, e.g. see + // http://stackoverflow.com/questions/1506078/fast-permutation-number-permutation-mapping-algorithms + int permunreg[6]; + + // This decodes the variable-base number in the 10 bits + // and gives us the Lehmer code sequence which can then + // be decoded. + switch (register_count) { + case 6: + permunreg[0] = permutation / 120; // 120 == 5! + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; // 24 == 4! + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; // 6 == 3! + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; // 2 == 2! + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; // 1 == 1! + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + + // Decode the Lehmer code for this permutation of + // the registers v. http://en.wikipedia.org/wiki/Lehmer_code + int registers[6] = {UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, + UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE, + UNWIND_X86_64_REG_NONE, UNWIND_X86_64_REG_NONE}; + bool used[7] = {false, false, false, false, false, false, false}; + for (int i = 0; i < register_count; i++) + { + int renum = 0; + for (int j = 1; j < 7; j++) + { + if (!used[j]) + { + if (renum == permunreg[i]) + { + registers[i] = j; + used[j] = true; + break; + } + renum++; + } + } + } + + uint64_t savedRegisters = context->Rsp + stack_size - 8 - (8 * register_count); + for (int i = 0; i < register_count; i++) + { + uint64_t reg; + if (!ReadValue64(info, &savedRegisters, ®)) { + return false; + } + switch (registers[i]) { + case UNWIND_X86_64_REG_RBX: + context->Rbx = reg; + break; + case UNWIND_X86_64_REG_R12: + context->R12 = reg; + break; + case UNWIND_X86_64_REG_R13: + context->R13 = reg; + break; + case UNWIND_X86_64_REG_R14: + context->R14 = reg; + break; + case UNWIND_X86_64_REG_R15: + context->R15 = reg; + break; + case UNWIND_X86_64_REG_RBP: + context->Rbp = reg; + break; + default: + ERROR("Bad register for frameless\n"); + break; + } + } + + // Now unwind the frame + uint64_t ip; + if (!ReadValue64(info, &savedRegisters, &ip)) { + return false; + } + context->Rip = ip; + context->Rsp = savedRegisters; + + TRACE("SUCCESS: frameless encoding %08x rip %p rsp %p rbp %p\n", + compactEncoding, (void*)context->Rip, (void*)context->Rsp, (void*)context->Rbp); + return true; +} + #define AMD64_SYSCALL_OPCODE 0x050f static bool @@ -1602,7 +1767,7 @@ StepWithCompactEncoding(const libunwindInfo* info, compact_unwind_encoding_t com case UNWIND_X86_64_MODE_STACK_IMMD: case UNWIND_X86_64_MODE_STACK_IND: - break; + return StepWithCompactEncodingFrameless(info, compactEncoding, functionStart); case UNWIND_X86_64_MODE_DWARF: return false;