diff --git a/LuaDkmDebuggerComponent/Bytecode.cs b/LuaDkmDebuggerComponent/Bytecode.cs index da29cff..f503360 100644 --- a/LuaDkmDebuggerComponent/Bytecode.cs +++ b/LuaDkmDebuggerComponent/Bytecode.cs @@ -9,39 +9,103 @@ namespace LuaDkmDebuggerComponent { - public enum LuaBaseType + static class LuaBaseTypes { - Nil, - Boolean, - LightUserData, - Number, - String, - Table, - Function, - UserData, - Thread + // TODO: setup dynamically + static bool hasVector = true; + + internal static int Nil() + { + return 0; + } + internal static int Boolean() + { + return 1; + } + internal static int LightUserData() + { + return 2; + } + internal static int Number() + { + return 3; + } + internal static int String() + { + return 4 + (hasVector ? 1 : 0); + } + internal static int Table() + { + return 5 + (hasVector ? 1 : 0); + } + internal static int Function() + { + return 6 + (hasVector ? 1 : 0); + } + internal static int UserData() + { + return 7 + (hasVector ? 1 : 0); + } + internal static int Thread() + { + return 8 + (hasVector ? 1 : 0); + } } - public enum LuaExtendedType + static class LuaExtendedTypes { - Nil = LuaBaseType.Nil, - - Boolean = LuaBaseType.Boolean, - BooleanTrue = LuaBaseType.Boolean + 16, // Lua 5.4 - - LightUserData = LuaBaseType.LightUserData, - - ShortString = LuaBaseType.String, - LongString = LuaBaseType.String + 16, - - Table = LuaBaseType.Table, - - LuaFunction = LuaBaseType.Function, - ExternalFunction = LuaBaseType.Function + 16, - ExternalClosure = LuaBaseType.Function + 32, - - UserData = LuaBaseType.UserData, - Thread = LuaBaseType.Thread, + internal static int Nil() + { + return LuaBaseTypes.Nil(); + } + internal static int Boolean() + { + return LuaBaseTypes.Boolean(); + } + internal static int BooleanTrue() + { + return LuaBaseTypes.Boolean() + 16; + } + internal static int LightUserData() + { + return LuaBaseTypes.LightUserData(); + } + internal static int Number() + { + return LuaBaseTypes.Number(); + } + internal static int ShortString() + { + return LuaBaseTypes.String(); + } + internal static int LongString() + { + return LuaBaseTypes.String() + 16; + } + internal static int Table() + { + return LuaBaseTypes.Table(); + } + internal static int LuaFunction() + { + return LuaBaseTypes.Function(); + } + internal static int ExternalFunction() + { + return LuaBaseTypes.Function() + 16; + } + internal static int ExternalClosure() + { + return LuaBaseTypes.Function() + 32; + } + internal static int UserData() + { + return LuaBaseTypes.UserData(); + } + internal static int Thread() + { + return LuaBaseTypes.Thread(); + } } static class LuaHelpers @@ -50,14 +114,14 @@ static class LuaHelpers public static int luaVersion = 0; - internal static LuaBaseType GetBaseType(int typeTag) + internal static int GetBaseType(int typeTag) { - return (LuaBaseType)(typeTag & 0xf); + return typeTag & 0xf; } - internal static LuaExtendedType GetExtendedType(int typeTag) + internal static int GetExtendedType(int typeTag) { - return (LuaExtendedType)(typeTag & 0x3f); + return typeTag & 0x3f; } internal static ulong GetStringDataOffset(DkmProcess process) @@ -76,6 +140,9 @@ internal static ulong GetStringDataOffset(DkmProcess process) if (Schema.LuaStringData.offsetToContent_5_4.HasValue) return Schema.LuaStringData.offsetToContent_5_4.Value; + if (Schema.LuaStringData.offsetToContent_Luau.HasValue) + return Schema.LuaStringData.offsetToContent_Luau.Value; + return (ulong)Schema.LuaStringData.structSize; } @@ -128,20 +195,20 @@ internal static ulong GetNodeSize(DkmProcess process) return 24u; } - internal static LuaExtendedType GetFloatNumberExtendedType() + internal static int GetFloatNumberExtendedType() { if (LuaHelpers.luaVersion == 504) - return (LuaExtendedType)(LuaBaseType.Number + 16); + return LuaBaseTypes.Number() + 16; - return (LuaExtendedType)LuaBaseType.Number; + return LuaBaseTypes.Number(); } - internal static LuaExtendedType GetIntegerNumberExtendedType() + internal static int GetIntegerNumberExtendedType() { if (LuaHelpers.luaVersion == 504) - return (LuaExtendedType)LuaBaseType.Number; + return LuaBaseTypes.Number(); - return (LuaExtendedType)(LuaBaseType.Number + 16); + return LuaBaseTypes.Number() + 16; } internal static bool HasIntegerNumberExtendedType() @@ -219,16 +286,16 @@ internal static void GetValueAddressParts(DkmProcess process, ulong address, out } // upvalue, proto, trace and cdata are not supported as values - LuaExtendedType[] ljTypeTagMapping = { LuaExtendedType.Nil, LuaExtendedType.Boolean, LuaExtendedType.BooleanTrue, LuaExtendedType.LightUserData, LuaExtendedType.ShortString, LuaExtendedType.Nil, LuaExtendedType.Thread, LuaExtendedType.Nil, LuaExtendedType.LuaFunction, LuaExtendedType.Nil, LuaExtendedType.Nil, LuaExtendedType.Table, LuaExtendedType.UserData }; + int[] ljTypeTagMapping = { LuaExtendedTypes.Nil(), LuaExtendedTypes.Boolean(), LuaExtendedTypes.BooleanTrue(), LuaExtendedTypes.LightUserData(), LuaExtendedTypes.ShortString(), LuaExtendedTypes.Nil(), LuaExtendedTypes.Thread(), LuaExtendedTypes.Nil(), LuaExtendedTypes.LuaFunction(), LuaExtendedTypes.Nil(), LuaExtendedTypes.Nil(), LuaExtendedTypes.Table(), LuaExtendedTypes.UserData() }; if (ljTypeTag >= ljTypeTagMapping.Length) return null; - typeTag = (int)ljTypeTagMapping[ljTypeTag]; + typeTag = ljTypeTagMapping[ljTypeTag]; } else { - typeTag = (int)GetFloatNumberExtendedType(); + typeTag = GetFloatNumberExtendedType(); } } else if (Schema.LuaValueData.available) @@ -249,7 +316,7 @@ internal static void GetValueAddressParts(DkmProcess process, ulong address, out } else { - typeTag = (int)GetFloatNumberExtendedType(); + typeTag = GetFloatNumberExtendedType(); } } else @@ -276,7 +343,7 @@ internal static void GetValueAddressParts(DkmProcess process, ulong address, out } else { - typeTag = (int)GetFloatNumberExtendedType(); + typeTag = GetFloatNumberExtendedType(); } } else @@ -317,7 +384,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag { var extenedType = GetExtendedType(typeTag); - if (extenedType == LuaExtendedType.Nil) + if (extenedType == LuaExtendedTypes.Nil()) { return new LuaValueDataNil() { @@ -329,7 +396,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.Boolean) + if (extenedType == LuaExtendedTypes.Boolean()) { if (LuaHelpers.luaVersion == 504 || LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit) { @@ -369,7 +436,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.BooleanTrue) + if (extenedType == LuaExtendedTypes.BooleanTrue()) { if (LuaHelpers.luaVersion == 504 || LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit) { @@ -394,7 +461,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.LightUserData) + if (extenedType == LuaExtendedTypes.LightUserData()) { var value = DebugHelpers.ReadPointerVariable(process, address, batch); @@ -475,7 +542,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.ShortString) + if (extenedType == LuaExtendedTypes.ShortString()) { var value = ReadGCobjAddress(process, address, batch); @@ -510,7 +577,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.LongString) + if (extenedType == LuaExtendedTypes.LongString()) { var value = ReadGCobjAddress(process, address, batch); @@ -545,7 +612,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.Table) + if (extenedType == LuaExtendedTypes.Table()) { var value = ReadGCobjAddress(process, address, batch); @@ -577,7 +644,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.LuaFunction) + if (extenedType == LuaExtendedTypes.LuaFunction()) { var value = ReadGCobjAddress(process, address, batch); @@ -597,7 +664,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag return new LuaValueDataExternalClosure() { baseType = GetBaseType(typeTag), - extendedType = LuaExtendedType.ExternalClosure, + extendedType = LuaExtendedTypes.ExternalClosure(), evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly, tagAddress = tagAddress, originalAddress = address, @@ -628,7 +695,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.ExternalFunction) + if (extenedType == LuaExtendedTypes.ExternalFunction()) { var value = ReadGCobjAddress(process, address, batch); @@ -655,7 +722,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.ExternalClosure) + if (extenedType == LuaExtendedTypes.ExternalClosure()) { var value = ReadGCobjAddress(process, address, batch); @@ -687,7 +754,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.UserData) + if (extenedType == LuaExtendedTypes.UserData()) { var value = ReadGCobjAddress(process, address, batch); @@ -719,7 +786,7 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag }; } - if (extenedType == LuaExtendedType.Thread) + if (extenedType == LuaExtendedTypes.Thread()) { var value = ReadGCobjAddress(process, address, batch); @@ -751,56 +818,56 @@ internal static LuaValueDataBase ReadValueOfType(DkmProcess process, int typeTag internal static uint? GetLuajitTag(int tagValue) { - uint ljTypeTag = 0; - - switch ((LuaExtendedType)(tagValue & ~64)) - { - case LuaExtendedType.Nil: - break; - case LuaExtendedType.Boolean: - ljTypeTag = 1; - break; - case LuaExtendedType.BooleanTrue: - ljTypeTag = 2; - break; - case LuaExtendedType.LightUserData: - ljTypeTag = 3; - break; - case LuaExtendedType.ShortString: - case LuaExtendedType.LongString: - ljTypeTag = 4; - break; - case LuaExtendedType.Table: - ljTypeTag = 11; - break; - case LuaExtendedType.LuaFunction: - case LuaExtendedType.ExternalClosure: - ljTypeTag = 8; - break; - case LuaExtendedType.ExternalFunction: - return null; - case LuaExtendedType.UserData: - ljTypeTag = 12; - break; - case LuaExtendedType.Thread: - ljTypeTag = 6; - break; - default: - return null; - } + int value = tagValue & ~64; + + if (value == LuaExtendedTypes.Nil()) + return 0; + + if (value == LuaExtendedTypes.Boolean()) + return 1; - return ljTypeTag; + if (value == LuaExtendedTypes.BooleanTrue()) + return 2; + + if (value == LuaExtendedTypes.LightUserData()) + return 3; + + if (value == LuaExtendedTypes.ShortString()) + return 4; + + if (value == LuaExtendedTypes.LongString()) + return 4; + + if (value == LuaExtendedTypes.Table()) + return 11; + + if (value == LuaExtendedTypes.LuaFunction()) + return 8; + + if (value == LuaExtendedTypes.ExternalClosure()) + return 8; + + if (value == LuaExtendedTypes.ExternalFunction()) + return null; + + if (value == LuaExtendedTypes.UserData()) + return 12; + + if (value == LuaExtendedTypes.Thread()) + return 6; + + return null; } internal static bool WriteTypeTag(DkmProcess process, ulong tagAddress, int tagValue) { if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit) { - if ((LuaExtendedType)tagValue == GetFloatNumberExtendedType()) + if (tagValue == GetFloatNumberExtendedType()) return true; // Integer writes are not supported - if ((LuaExtendedType)tagValue == GetIntegerNumberExtendedType()) + if (tagValue == GetIntegerNumberExtendedType()) return false; // GC64 encoding is not supported @@ -864,7 +931,7 @@ bool Failed(string text, out string errorText_) { if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit && Schema.Luajit.fullPointer) { - uint? ljTypeTag = GetLuajitTag((int)LuaExtendedType.Nil).GetValueOrDefault(0); + uint? ljTypeTag = GetLuajitTag(LuaExtendedTypes.Nil()).GetValueOrDefault(0); ulong encodedValue = ((ulong)~ljTypeTag.Value << 47) | 0x7fffffffffful; @@ -873,7 +940,7 @@ bool Failed(string text, out string errorText_) } else { - if (!WriteTypeTag(process, tagAddress, (int)LuaExtendedType.Nil)) + if (!WriteTypeTag(process, tagAddress, LuaExtendedTypes.Nil())) return Failed("Failed to modify target process memory (tag)", out errorText); if (!DebugHelpers.TryWriteIntVariable(process, valueAddress, 0)) @@ -887,7 +954,7 @@ bool Failed(string text, out string errorText_) { if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit && Schema.Luajit.fullPointer) { - uint? ljTypeTag = GetLuajitTag((int)(sourceBool.value ? LuaExtendedType.BooleanTrue : LuaExtendedType.Boolean)).GetValueOrDefault(0); + uint? ljTypeTag = GetLuajitTag(sourceBool.value ? LuaExtendedTypes.BooleanTrue() : LuaExtendedTypes.Boolean()).GetValueOrDefault(0); ulong encodedValue = ((ulong)~ljTypeTag.Value << 47) | 0x7fffffffffful; @@ -898,7 +965,7 @@ bool Failed(string text, out string errorText_) { if (LuaHelpers.luaVersion == 504 || LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit) { - if (!WriteTypeTag(process, tagAddress, (int)(sourceBool.value ? LuaExtendedType.BooleanTrue : LuaExtendedType.Boolean))) + if (!WriteTypeTag(process, tagAddress, sourceBool.value ? LuaExtendedTypes.BooleanTrue() : LuaExtendedTypes.Boolean())) return Failed("Failed to modify target process memory (tag)", out errorText); if (!DebugHelpers.TryWriteIntVariable(process, valueAddress, 0)) @@ -906,7 +973,7 @@ bool Failed(string text, out string errorText_) } else { - if (!WriteTypeTag(process, tagAddress, (int)LuaExtendedType.Boolean)) + if (!WriteTypeTag(process, tagAddress, LuaExtendedTypes.Boolean())) return Failed("Failed to modify target process memory (tag)", out errorText); if (!DebugHelpers.TryWriteIntVariable(process, valueAddress, sourceBool.value ? 1 : 0)) @@ -922,7 +989,7 @@ bool Failed(string text, out string errorText_) if (sourceNumber.extendedType == GetFloatNumberExtendedType() || !LuaHelpers.HasIntegerNumberExtendedType()) { // Write tag first here, unioned value will go over it when neccessary - if (!WriteTypeTag(process, tagAddress, (int)GetFloatNumberExtendedType())) + if (!WriteTypeTag(process, tagAddress, GetFloatNumberExtendedType())) return Failed("Failed to modify target process memory (tag)", out errorText); if (!DebugHelpers.TryWriteDoubleVariable(process, valueAddress, sourceNumber.value)) @@ -934,7 +1001,7 @@ bool Failed(string text, out string errorText_) if (sourceNumber.extendedType == GetIntegerNumberExtendedType() && LuaHelpers.HasIntegerNumberExtendedType()) { - if (!WriteTypeTag(process, tagAddress, (int)GetIntegerNumberExtendedType())) + if (!WriteTypeTag(process, tagAddress, GetIntegerNumberExtendedType())) return Failed("Failed to modify target process memory (tag)", out errorText); if (!DebugHelpers.TryWriteIntVariable(process, valueAddress, (int)sourceNumber.value)) @@ -987,7 +1054,7 @@ bool Failed(string text, out string errorText_) if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit && Schema.Luajit.fullPointer) { - uint? ljTypeTag = GetLuajitTag((int)value.extendedType).GetValueOrDefault(0); + uint? ljTypeTag = GetLuajitTag(value.extendedType).GetValueOrDefault(0); ulong encodedPointer = ((ulong)~ljTypeTag.Value << 47) | (stringAddress.Value & 0x7fffffffffful); @@ -996,7 +1063,7 @@ bool Failed(string text, out string errorText_) } else { - int finalTag = LuaHelpers.luaVersion == 501 ? (int)value.baseType : (int)value.extendedType | 64; + int finalTag = LuaHelpers.luaVersion == 501 ? value.baseType : value.extendedType | 64; if (!WriteTypeTag(process, tagAddress, finalTag)) return Failed("Failed to modify target process memory (tag)", out errorText); @@ -1007,7 +1074,7 @@ bool Failed(string text, out string errorText_) } else { - int finalTag = LuaHelpers.luaVersion == 501 ? (int)value.baseType : (int)value.extendedType | 64; + int finalTag = LuaHelpers.luaVersion == 501 ? value.baseType : value.extendedType | 64; if (!WriteTypeTag(process, tagAddress, finalTag)) return Failed("Failed to modify target process memory (tag)", out errorText); @@ -1046,7 +1113,7 @@ bool Failed(string text, out string errorText_) if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit && Schema.Luajit.fullPointer) { - uint? ljTypeTag = GetLuajitTag((int)value.extendedType); + uint? ljTypeTag = GetLuajitTag(value.extendedType); if (!ljTypeTag.HasValue) return Failed("Unsupported type tag", out errorText); @@ -1058,7 +1125,7 @@ bool Failed(string text, out string errorText_) } else { - int finalTag = LuaHelpers.luaVersion == 501 ? (int)value.baseType : (int)value.extendedType | (collectable ? 64 : 0); + int finalTag = LuaHelpers.luaVersion == 501 ? value.baseType : value.extendedType | (collectable ? 64 : 0); if (!WriteTypeTag(process, tagAddress, finalTag)) return Failed("Failed to modify target process memory (tag)", out errorText); @@ -1080,6 +1147,8 @@ public class LuaLocalVariableData public int lifetimeStartInstruction; public int lifetimeEndInstruction; + public byte? targetRegister_Luau; + public static int StructSize(DkmProcess process) { Debug.Assert(LuaHelpers.luaVersion != LuaHelpers.luaVersionLuajit, "LuaLocalVariableData size is variable in Luajit"); @@ -1099,6 +1168,9 @@ public void ReadFrom(DkmProcess process, ulong address, BatchRead batch = null) nameAddress = DebugHelpers.ReadPointerVariable(process, address + Schema.LuaLocalVariableData.nameAddress.GetValueOrDefault(0), batch).GetValueOrDefault(0); lifetimeStartInstruction = DebugHelpers.ReadIntVariable(process, address + Schema.LuaLocalVariableData.lifetimeStartInstruction.GetValueOrDefault(0), batch).GetValueOrDefault(0); lifetimeEndInstruction = DebugHelpers.ReadIntVariable(process, address + Schema.LuaLocalVariableData.lifetimeEndInstruction.GetValueOrDefault(0), batch).GetValueOrDefault(0); + + if (Schema.LuaLocalVariableData.targetRegister_Luau.HasValue) + targetRegister_Luau = DebugHelpers.ReadByteVariable(process, address + Schema.LuaLocalVariableData.targetRegister_Luau.GetValueOrDefault(0), batch); } else { @@ -1256,6 +1328,7 @@ public class LuaFunctionData public int codeSize; public int lineInfoSize; public int? absLineInfoSize_5_4; + public int? linegaplog2_Luau; public int localFunctionSize; public int localVariableSize; public int definitionStartLine_opt; @@ -1349,6 +1422,9 @@ public void ReadFrom(DkmProcess process, ulong address) if (Schema.LuaFunctionData.absLineInfoSize_5_4.HasValue) absLineInfoSize_5_4 = DebugHelpers.ReadIntVariable(process, address + Schema.LuaFunctionData.absLineInfoSize_5_4.GetValueOrDefault(0)); + if (Schema.LuaFunctionData.linegaplog2_Luau.HasValue) + linegaplog2_Luau = DebugHelpers.ReadIntVariable(process, address + Schema.LuaFunctionData.linegaplog2_Luau.GetValueOrDefault(0)); + localFunctionSize = DebugHelpers.ReadIntVariable(process, address + Schema.LuaFunctionData.localFunctionSize.GetValueOrDefault(0)).GetValueOrDefault(0); localVariableSize = DebugHelpers.ReadIntVariable(process, address + Schema.LuaFunctionData.localVariableSize.GetValueOrDefault(0)).GetValueOrDefault(0); @@ -1366,6 +1442,11 @@ public void ReadFrom(DkmProcess process, ulong address) if (Schema.LuaFunctionData.maxStackSize_opt.HasValue) maxStackSize_opt = DebugHelpers.ReadByteVariable(process, address + Schema.LuaFunctionData.maxStackSize_opt.GetValueOrDefault(0)).GetValueOrDefault(0); + if (Schema.LuaFunctionData.linegaplog2_Luau.HasValue) + { + definitionStartLine_opt = DebugHelpers.ReadIntVariable(process, absLineInfoDataAddress_5_4).GetValueOrDefault(0); + } + hasDefinitionLineInfo = Schema.LuaFunctionData.definitionStartLine_opt.HasValue && Schema.LuaFunctionData.definitionEndLine_opt.HasValue; } else if (LuaHelpers.luaVersion == 501) @@ -1604,7 +1685,7 @@ public void ReadLocals(DkmProcess process, int instructionPointer) locals.Add(local); - if (i < argumentCount || instructionPointer == -1) + if ((i < argumentCount && !linegaplog2_Luau.HasValue) || instructionPointer == -1) { activeLocals.Add(local); } @@ -1642,7 +1723,7 @@ public void UpdateLocals(DkmProcess process, int instructionPointer) } else { - if (i < argumentCount || instructionPointer == -1) + if ((i < argumentCount && !linegaplog2_Luau.HasValue) || instructionPointer == -1) { activeLocals.Add(local); } @@ -1694,6 +1775,12 @@ public int[] ReadLineInfo(DkmProcess process) for (int i = 0; i < lineInfoSize; i++) lineInfo[i] = DebugHelpers.ReadByteVariable(process, lineInfoDataAddress + (ulong)i).GetValueOrDefault(0); } + else if (linegaplog2_Luau.HasValue) + { + // TODO: block read + for (int i = 0; i < lineInfoSize; i++) + lineInfo[i] = DebugHelpers.ReadByteVariable(process, lineInfoDataAddress + (ulong)i).GetValueOrDefault(0); + } else { // TODO: block read @@ -1709,16 +1796,31 @@ public int[] ReadAbsoluteLineInfo(DkmProcess process) if (absLineInfo != null) return absLineInfo; - if (!absLineInfoSize_5_4.HasValue) - return null; + if (absLineInfoSize_5_4.HasValue) + { + absLineInfo = new int[absLineInfoSize_5_4.Value * 2]; - absLineInfo = new int[absLineInfoSize_5_4.Value * 2]; + // TODO: block read + for (int i = 0; i < absLineInfoSize_5_4.Value * 2; i++) + absLineInfo[i] = DebugHelpers.ReadIntVariable(process, absLineInfoDataAddress_5_4 + (ulong)i * 4u).GetValueOrDefault(0); - // TODO: block read - for (int i = 0; i < absLineInfoSize_5_4.Value * 2; i++) - absLineInfo[i] = DebugHelpers.ReadIntVariable(process, absLineInfoDataAddress_5_4 + (ulong)i * 4u).GetValueOrDefault(0); + return absLineInfo; + } - return absLineInfo; + if (linegaplog2_Luau.HasValue) + { + int absoluteLineCount = ((codeSize - 1) >> linegaplog2_Luau.Value) + 1; + + absLineInfo = new int[absoluteLineCount]; + + // TODO: block read + for (int i = 0; i < absoluteLineCount; i++) + absLineInfo[i] = DebugHelpers.ReadIntVariable(process, absLineInfoDataAddress_5_4 + (ulong)i * 4u).GetValueOrDefault(0); + + return absLineInfo; + } + + return null; } public int ReadLineInfoFor(DkmProcess process, int instructionPointer) @@ -1765,6 +1867,18 @@ public int ReadLineInfoFor(DkmProcess process, int instructionPointer) return baseLine; } + if (linegaplog2_Luau.HasValue) + { + if (lineInfoSize == 0) + return 0; + + // We need all data for this + ReadLineInfo(process); + ReadAbsoluteLineInfo(process); + + return absLineInfo[instructionPointer >> linegaplog2_Luau.Value] + lineInfo[instructionPointer]; + } + return DebugHelpers.ReadIntVariable(process, lineInfoDataAddress + (ulong)instructionPointer * 4).GetValueOrDefault(0); } @@ -1944,7 +2058,7 @@ public void ReadFunction(DkmProcess process) if (LuaHelpers.luaVersion == LuaHelpers.luaVersionLuajit) { - func = LuaHelpers.ReadValueOfType(process, (int)LuaExtendedType.LuaFunction, 0, funcAddress); + func = LuaHelpers.ReadValueOfType(process, LuaExtendedTypes.LuaFunction(), 0, funcAddress); } else { @@ -2152,8 +2266,16 @@ public LuaValueDataBase LoadKey(DkmProcess process, BatchRead batch = null) if (keyValueDataAddress == 0) return null; - // Same in Lua 5.1, 5.2, 5.3 and 5.4 - key = LuaHelpers.ReadValueOfType(process, keyTypeTag.GetValueOrDefault(0), keyValueTagAddress, keyValueDataAddress, batch); + if (Schema.LuaStringData.offsetToContent_Luau.HasValue) // TODO: explicit version check + { + // TODO: we need to mark result somehow so that updates will not break linking + key = LuaHelpers.ReadValueOfType(process, keyTypeTag.GetValueOrDefault(0) & 15, keyValueTagAddress, keyValueDataAddress, batch); + } + else + { + // Same in Lua 5.1, 5.2, 5.3 and 5.4 + key = LuaHelpers.ReadValueOfType(process, keyTypeTag.GetValueOrDefault(0), keyValueTagAddress, keyValueDataAddress, batch); + } return key; } @@ -2449,7 +2571,7 @@ public void LoadNodeLazyElements(DkmProcess process) node.ReadFromMetaOnly(process, address, batchNodeElementData); - if (LuaHelpers.GetBaseType(node.keyTypeTag.GetValueOrDefault(0)) != LuaBaseType.Nil) + if (LuaHelpers.GetBaseType(node.keyTypeTag.GetValueOrDefault(0)) != LuaBaseTypes.Nil()) nodeLazyElements.Add(node); } } @@ -2651,6 +2773,19 @@ public LuaUpvalueData ReadUpvalue(DkmProcess process, int index, int expectedCou { upvalueAddress = LuaHelpers.ReadGCobjAddress(process, firstUpvaluePointerAddress + (ulong)(index * DebugHelpers.GetPointerSize(process))).GetValueOrDefault(0); } + else if (Schema.LuaStringData.offsetToContent_Luau.HasValue) // TODO: explicit version check + { + ulong address = firstUpvaluePointerAddress + (ulong)index * LuaHelpers.GetValueSize(process); + + LuaUpvalueData data = new LuaUpvalueData(); + + data.valueAddress = address; + data.value = LuaHelpers.ReadValue(process, address); + + upvalues[index] = data; + + return data; + } else { upvalueAddress = DebugHelpers.ReadPointerVariable(process, firstUpvaluePointerAddress + (ulong)(index * DebugHelpers.GetPointerSize(process))).GetValueOrDefault(0); diff --git a/LuaDkmDebuggerComponent/BytecodeSchema.cs b/LuaDkmDebuggerComponent/BytecodeSchema.cs index 9480e9f..970b634 100644 --- a/LuaDkmDebuggerComponent/BytecodeSchema.cs +++ b/LuaDkmDebuggerComponent/BytecodeSchema.cs @@ -109,6 +109,7 @@ public static class LuaStringData public static long structSize = 0; public static ulong? offsetToContent_5_4; + public static ulong? offsetToContent_Luau; public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread thread, DkmStackWalkFrame frame) { @@ -120,6 +121,7 @@ public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread structSize = Helper.GetSize(inspectionSession, thread, frame, "TString", ref available); offsetToContent_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, "TString", "contents", "used in 5.4", ref optional); + offsetToContent_Luau = Helper.ReadOptional(inspectionSession, thread, frame, "TString", "data", "used in Luau", ref optional); if (Log.instance != null) Log.instance.Debug($"LuaStringData schema {(available ? "available" : "not available")} with {success} successes and {failure} failures and {optional} optional"); @@ -169,6 +171,7 @@ public static class LuaLocalVariableData public static ulong? nameAddress; public static ulong? lifetimeStartInstruction; public static ulong? lifetimeEndInstruction; + public static ulong? targetRegister_Luau; public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread thread, DkmStackWalkFrame frame) { @@ -182,6 +185,7 @@ public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread nameAddress = Helper.Read(inspectionSession, thread, frame, "LocVar", "varname", ref available, ref success, ref failure); lifetimeStartInstruction = Helper.Read(inspectionSession, thread, frame, "LocVar", "startpc", ref available, ref success, ref failure); lifetimeEndInstruction = Helper.Read(inspectionSession, thread, frame, "LocVar", "endpc", ref available, ref success, ref failure); + targetRegister_Luau = Helper.ReadOptional(inspectionSession, thread, frame, "LocVar", "reg", "used in Luau", ref optional); if (Log.instance != null) Log.instance.Debug($"LuaLocalVariableData schema {(available ? "available" : "not available")} with {success} successes and {failure} failures and {optional} optional"); @@ -259,6 +263,7 @@ public static class LuaFunctionData public static ulong? codeSize; public static ulong? lineInfoSize; public static ulong? absLineInfoSize_5_4; + public static ulong? linegaplog2_Luau; public static ulong? localFunctionSize; public static ulong? localVariableSize; public static ulong? definitionStartLine_opt; @@ -290,6 +295,7 @@ public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread codeSize = Helper.Read(inspectionSession, thread, frame, "Proto", "sizecode", ref available, ref success, ref failure); lineInfoSize = Helper.Read(inspectionSession, thread, frame, "Proto", "sizelineinfo", ref available, ref success, ref failure); absLineInfoSize_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, "Proto", "sizeabslineinfo", "used in 5.4", ref optional); + linegaplog2_Luau = Helper.ReadOptional(inspectionSession, thread, frame, "Proto", "linegaplog2", "used in Luau", ref optional); localFunctionSize = Helper.Read(inspectionSession, thread, frame, "Proto", "sizep", ref available, ref success, ref failure); localVariableSize = Helper.Read(inspectionSession, thread, frame, "Proto", "sizelocvars", ref available, ref success, ref failure); definitionStartLine_opt = Helper.ReadOptional(inspectionSession, thread, frame, "Proto", "linedefined", "used to detect main function", ref optional); @@ -377,15 +383,24 @@ public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread failure = 0; optional = 0; - structSize = Helper.GetSize(inspectionSession, thread, frame, "Node", ref available); + string name = "Node"; - valueDataAddress = Helper.Read(inspectionSession, thread, frame, "Node", "i_val", ref available, ref success, ref failure); + structSize = Helper.GetSize(inspectionSession, thread, frame, name, ref available); - keyDataTypeAddress_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, "Node", "u.key_tt", "used in Lua 5.4", ref optional); - keyDataValueAddress_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, "Node", "u.key_val", "used in Lua 5.4", ref optional); + // TODO: explicit Luau detection + if (!available || structSize == 0) + { + name = "LuaNode"; + structSize = Helper.GetSize(inspectionSession, thread, frame, name, ref available); + } + + valueDataAddress = Helper.Read(inspectionSession, thread, frame, name, new[] { "i_val", "val" }, ref available, ref success, ref failure); + + keyDataTypeAddress_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, name, "u.key_tt", "used in Lua 5.4", ref optional); + keyDataValueAddress_5_4 = Helper.ReadOptional(inspectionSession, thread, frame, name, "u.key_val", "used in Lua 5.4", ref optional); if (!keyDataTypeAddress_5_4.HasValue || !keyDataValueAddress_5_4.HasValue) - keyDataAddress_5_123 = Helper.Read(inspectionSession, thread, frame, "Node", "i_key", ref available, ref success, ref failure); + keyDataAddress_5_123 = Helper.Read(inspectionSession, thread, frame, name, new[] { "i_key", "key" }, ref available, ref success, ref failure); else keyDataAddress_5_123 = null; @@ -457,13 +472,22 @@ public static void LoadSchema(DkmInspectionSession inspectionSession, DkmThread failure = 0; optional = 0; - structSize = Helper.GetSize(inspectionSession, thread, frame, "LClosure", ref available); + string name = "LClosure"; + + structSize = Helper.GetSize(inspectionSession, thread, frame, name, ref available); + + // TODO: explicit Luau detection + if (!available || structSize == 0) + { + name = "Closure"; + structSize = Helper.GetSize(inspectionSession, thread, frame, name, ref available); + } - isC_5_1 = Helper.ReadOptional(inspectionSession, thread, frame, "LClosure", "isC", "used in 5.1", ref optional); - upvalueSize_opt = Helper.ReadOptional(inspectionSession, thread, frame, "LClosure", "nupvalues", "used if avaiable", ref optional); - envTableDataAddress_5_1 = Helper.ReadOptional(inspectionSession, thread, frame, "LClosure", "env", "used in 5.1", ref optional); - functionAddress = Helper.Read(inspectionSession, thread, frame, "LClosure", "p", ref available, ref success, ref failure); - firstUpvaluePointerAddress = Helper.Read(inspectionSession, thread, frame, "LClosure", "upvals", ref available, ref success, ref failure); + isC_5_1 = Helper.ReadOptional(inspectionSession, thread, frame, name, "isC", "used in 5.1", ref optional); + upvalueSize_opt = Helper.ReadOptional(inspectionSession, thread, frame, name, "nupvalues", "used if avaiable", ref optional); + envTableDataAddress_5_1 = Helper.ReadOptional(inspectionSession, thread, frame, name, "env", "used in 5.1", ref optional); + functionAddress = Helper.Read(inspectionSession, thread, frame, name, new[] { "p", "l.p" }, ref available, ref success, ref failure); + firstUpvaluePointerAddress = Helper.Read(inspectionSession, thread, frame, name, new[] { "upvals", "l.uprefs" }, ref available, ref success, ref failure); if (Log.instance != null) Log.instance.Debug($"LuaClosureData schema {(available ? "available" : "not available")} with {success} successes and {failure} failures and {optional} optional"); diff --git a/LuaDkmDebuggerComponent/EvaluationHelpers.cs b/LuaDkmDebuggerComponent/EvaluationHelpers.cs index fe1b0e1..79bd9df 100644 --- a/LuaDkmDebuggerComponent/EvaluationHelpers.cs +++ b/LuaDkmDebuggerComponent/EvaluationHelpers.cs @@ -208,7 +208,7 @@ internal static string EvaluateValueAtLuaValue(DkmProcess process, LuaValueDataB { var value = valueBase as LuaValueDataString; - type = value.extendedType == LuaExtendedType.ShortString ? "short_string" : "long_string"; + type = value.extendedType == LuaExtendedTypes.ShortString() ? "short_string" : "long_string"; flags |= DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.RawString; editableValue = $"{value.value}"; @@ -473,8 +473,8 @@ internal static DkmEvaluationResult GetTableChildAtIndex(DkmInspectionContext in { var metaTableValue = new LuaValueDataTable { - baseType = LuaBaseType.Table, - extendedType = LuaExtendedType.Table, + baseType = LuaBaseTypes.Table(), + extendedType = LuaExtendedTypes.Table(), evaluationFlags = DkmEvaluationResultFlags.ReadOnly, originalAddress = 0, // Not available as TValue value = value.GetMetaTable(process), diff --git a/LuaDkmDebuggerComponent/ExpressionEvaluation.cs b/LuaDkmDebuggerComponent/ExpressionEvaluation.cs index 9f9febe..9d524cd 100644 --- a/LuaDkmDebuggerComponent/ExpressionEvaluation.cs +++ b/LuaDkmDebuggerComponent/ExpressionEvaluation.cs @@ -279,6 +279,9 @@ public LuaValueDataBase LookupVariable(string name) { ulong address = frameBaseAddress + (ulong)i * LuaHelpers.GetValueSize(process); + if (local.targetRegister_Luau.HasValue) + address = frameBaseAddress + (ulong)local.targetRegister_Luau.Value * LuaHelpers.GetValueSize(process); + var result = LuaHelpers.ReadValue(process, address); if (result == null) @@ -744,7 +747,7 @@ public LuaValueDataBase EvaluateUnary() for (int i = start; i < arrayElements.Count; i++) { - if (arrayElements[i] == null || arrayElements[i].baseType == LuaBaseType.Nil) + if (arrayElements[i] == null || arrayElements[i].baseType == LuaBaseTypes.Nil()) return new LuaValueDataNumber(i - start); } diff --git a/LuaDkmDebuggerComponent/LocalComponent.cs b/LuaDkmDebuggerComponent/LocalComponent.cs index f158e37..264ea97 100644 --- a/LuaDkmDebuggerComponent/LocalComponent.cs +++ b/LuaDkmDebuggerComponent/LocalComponent.cs @@ -624,7 +624,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio functionAddress = callLuaFunction.value.functionAddress, functionName = functionName, - instructionLine = (int)currLine, + instructionLine = currLine, instructionPointer = prevInstructionPointer, source = sourceName @@ -720,7 +720,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio continue; } - if (currCallInfoData.func.baseType != LuaBaseType.Function) + if (currCallInfoData.func.baseType != LuaBaseTypes.Function()) break; var currCallLuaFunction = currCallInfoData.func as LuaValueDataLuaFunction; @@ -838,7 +838,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio if (currCallInfoData.func == null) break; - if (currCallInfoData.func.baseType == LuaBaseType.Function) + if (currCallInfoData.func.baseType == LuaBaseTypes.Function()) { if (stackContextData.skipFrames != 0) { @@ -861,11 +861,11 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio LuaValueDataExternalFunction currCallExternalFunction = null; LuaValueDataExternalClosure currCallExternalClosure = null; - if (currCallInfoData.func.extendedType == LuaExtendedType.LuaFunction) + if (currCallInfoData.func.extendedType == LuaExtendedTypes.LuaFunction()) currCallLuaFunction = currCallInfoData.func as LuaValueDataLuaFunction; - else if (currCallInfoData.func.extendedType == LuaExtendedType.ExternalFunction) + else if (currCallInfoData.func.extendedType == LuaExtendedTypes.ExternalFunction()) currCallExternalFunction = currCallInfoData.func as LuaValueDataExternalFunction; - else if (currCallInfoData.func.extendedType == LuaExtendedType.ExternalClosure) + else if (currCallInfoData.func.extendedType == LuaExtendedTypes.ExternalClosure()) currCallExternalClosure = currCallInfoData.func as LuaValueDataExternalClosure; string currFunctionName = "[name unavailable]"; @@ -886,7 +886,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio else { // Check that it's safe to cast previous call info to a Lua Closure - if (prevCallInfoData.func.extendedType != LuaExtendedType.LuaFunction) + if (prevCallInfoData.func.extendedType != LuaExtendedTypes.LuaFunction()) break; LuaStateSymbols stateSymbols; @@ -947,7 +947,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio } } - if (currCallInfoData.func.extendedType == LuaExtendedType.LuaFunction) + if (currCallInfoData.func.extendedType == LuaExtendedTypes.LuaFunction()) { Debug.Assert(currCallLuaFunction != null); @@ -984,7 +984,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio break; } } - else if (currCallInfoData.func.extendedType == LuaExtendedType.ExternalFunction) + else if (currCallInfoData.func.extendedType == LuaExtendedTypes.ExternalFunction()) { if (stackContextData.seenLuaFrame) { @@ -1000,7 +1000,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio luaFrameFlags &= ~DkmStackWalkFrameFlags.TopFrame; } - else if (currCallInfoData.func.extendedType == LuaExtendedType.ExternalClosure) + else if (currCallInfoData.func.extendedType == LuaExtendedTypes.ExternalClosure()) { if (stackContextData.seenLuaFrame) { @@ -1125,7 +1125,7 @@ DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctio { ulong functionAddress = Schema.Luajit.fullPointer ? frameAddress - LuaHelpers.GetValueSize(process) : frameAddress; - LuaValueDataBase callFunction = LuaHelpers.ReadValueOfType(process, (int)LuaExtendedType.LuaFunction, 0, functionAddress); + LuaValueDataBase callFunction = LuaHelpers.ReadValueOfType(process, LuaExtendedTypes.LuaFunction(), 0, functionAddress); long? status; @@ -1306,6 +1306,383 @@ void RegisterMissingScript() } } + if (methodName == "luau_execute") + { + log.Verbose($"IDkmCallStackFilter.FilterNextFrame Got 'luau_execute' stack frame"); + + bool fromHook = stackContextData.hideTopLuaLibraryFrames; + + stackContextData.hideTopLuaLibraryFrames = false; + stackContextData.hideInternalLuaLibraryFrames = false; + + if (!OnFoundLuaCallStack(process, processData, stackContext, input)) + return new DkmStackWalkFrame[1] { input }; + + bool isTopFrame = (input.Flags & DkmStackWalkFrameFlags.TopFrame) != 0; + + List luaFrames = new List(); + + var luaFrameFlags = input.Flags; + + luaFrameFlags &= ~(DkmStackWalkFrameFlags.NonuserCode | DkmStackWalkFrameFlags.UserStatusNotDetermined); + + if (isTopFrame) + luaFrameFlags |= DkmStackWalkFrameFlags.TopFrame; + + ulong? stateAddress = EvaluationHelpers.TryEvaluateAddressExpression($"L", stackContext.InspectionSession, stackContext.Thread, input, DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects); + + // Reset Lua frame skip data if we have switched Lua state + if (stackContextData.stateAddress != stateAddress.GetValueOrDefault(0)) + { + stackContextData.stateAddress = stateAddress.GetValueOrDefault(0); + stackContextData.seenLuaFrame = false; + stackContextData.skipFrames = 0; + stackContextData.seenFrames = 0; + } + + ulong? registryAddress = EvaluationHelpers.TryEvaluateAddressExpression($"&L->l_gt", stackContext.InspectionSession, stackContext.Thread, input, DkmEvaluationFlags.TreatAsExpression | DkmEvaluationFlags.NoSideEffects); + + if (LuaHelpers.luaVersion == 0) + { + LuaHelpers.luaVersion = 501; + + SendVersionNotification(process, processData); + } + + string GetLuaFunctionName(ulong currCallInfoAddress, ulong prevCallInfoAddress, ulong closureAddress) + { + if (processData.scratchMemory == 0) + return null; + + string functionNameType = null; + + DkmCustomMessage.Create(process.Connection, process, MessageToRemote.guid, MessageToRemote.pauseBreakpoints, null, null).SendLower(); + + functionNameType = EvaluationHelpers.TryEvaluateStringExpression($"getfuncname(L, ((CallInfo*){currCallInfoAddress}), (const char**){processData.scratchMemory})", stackContext.InspectionSession, stackContext.Thread, input, DkmEvaluationFlags.None); + + if (functionNameType == null && closureAddress != 0) + { + long? result = EvaluationHelpers.TryEvaluateNumberExpression($"auxgetinfo(L, \"n\", {processData.scratchMemory}, {closureAddress}, {currCallInfoAddress})", stackContext.InspectionSession, stackContext.Thread, input, DkmEvaluationFlags.None); + + if (result.GetValueOrDefault(0) == 1) + { + string functionName = EvaluationHelpers.TryEvaluateStringExpression($"((lua_Debug*){processData.scratchMemory})->name", stackContext.InspectionSession, stackContext.Thread, input, DkmEvaluationFlags.None); + + if (functionName != null && functionName != "?") + { + DkmCustomMessage.Create(process.Connection, process, MessageToRemote.guid, MessageToRemote.resumeBreakpoints, null, null).SendLower(); + + return functionName; + } + } + } + + DkmCustomMessage.Create(process.Connection, process, MessageToRemote.guid, MessageToRemote.resumeBreakpoints, null, null).SendLower(); + + if (functionNameType != null) + { + ulong? functionNameAddress = DebugHelpers.ReadPointerVariable(process, processData.scratchMemory); + + if (functionNameAddress.HasValue && functionNameAddress.Value != 0) + return DebugHelpers.ReadStringVariable(process, functionNameAddress.Value, 1024); + } + + return null; + } + + DkmStackWalkFrame GetLuaFunctionStackWalkFrame(ulong callInfoAddress, LuaFunctionCallInfoData callInfoData, LuaValueDataLuaFunction callLuaFunction, string functionName) + { + var currFunctionData = callLuaFunction.value.ReadFunction(process); + + if (currFunctionData == null) + { + log.Error("IDkmCallStackFilter.FilterNextFrame Failed to read Lua function data (Proto)"); + + return null; + } + + long currInstructionPointer = 0; + + // Invalid value is possible in bad break locations + if (callInfoData.savedInstructionPointerAddress >= currFunctionData.codeDataAddress) + currInstructionPointer = ((long)callInfoData.savedInstructionPointerAddress - (long)currFunctionData.codeDataAddress) / 4; // unsigned size instructions + + // If the call was already made, savedpc will be offset by 1 (return location) + int prevInstructionPointer = currInstructionPointer == 0 ? 0 : (int)currInstructionPointer - 1; + + int currLine = currFunctionData.ReadLineInfoFor(process, prevInstructionPointer); + + string sourceName = currFunctionData.ReadSource(process); + + lock (processData.symbolStore) + { + LuaStateSymbols stateSymbols = processData.symbolStore.FetchOrCreate(stateAddress.Value); + + if (stateSymbols.unnamedScriptMapping.ContainsKey(sourceName)) + sourceName = stateSymbols.unnamedScriptMapping[sourceName]; + } + + if (sourceName != null) + { + if (currFunctionData.hasDefinitionLineInfo && currFunctionData.definitionStartLine_opt == 0) + functionName = "main"; + + LuaFunctionData functionData = currFunctionData; + + lock (processData.symbolStore) + { + processData.symbolStore.FetchOrCreate(stateAddress.Value).AddSourceFromFunction(process, functionData); + } + + functionData.ReadLocals(process, prevInstructionPointer); + + string argumentList = ""; + + if (functionData.activeLocals != null && functionData.activeLocals.Count >= functionData.argumentCount) + { + for (int i = 0; i < functionData.activeLocals.Count; i++) + { + LuaLocalVariableData argument = functionData.activeLocals[i]; + + if (argument.lifetimeStartInstruction != 0) + continue; + + argumentList += (argumentList.Length == 0 ? "" : ", ") + argument.name; + } + } + + LuaAddressEntityData entityData = new LuaAddressEntityData + { + source = sourceName, + line = currLine, + + functionAddress = callLuaFunction.value.functionAddress, + functionInstructionPointer = prevInstructionPointer, + }; + + LuaFrameData frameData = new LuaFrameData + { + state = stateAddress.Value, + + registryAddress = registryAddress.GetValueOrDefault(0), + version = LuaHelpers.luaVersion, + + callInfo = callInfoAddress, + + functionAddress = callLuaFunction.value.functionAddress, + functionName = functionName, + + instructionLine = currLine, + instructionPointer = prevInstructionPointer, + + source = sourceName + }; + + var entityDataBytes = entityData.Encode(); + var frameDataBytes = frameData.Encode(); + + DkmInstructionAddress instructionAddress = DkmCustomInstructionAddress.Create(processData.runtimeInstance, processData.moduleInstance, entityDataBytes, (ulong)((currLine << 16) + prevInstructionPointer), frameDataBytes, null); + + var description = $"{sourceName} {functionName}({argumentList}) Line {currLine}"; + + var parentFrameData = DkmStackWalkFrameData.Create(stackContext.InspectionSession, new LuaStackWalkFrameParentData { originalFrame = input, stateAddress = stateAddress.Value }); + + return DkmStackWalkFrame.Create(stackContext.Thread, instructionAddress, input.FrameBase, input.FrameSize, luaFrameFlags, description, input.Registers, input.Annotations, null, null, parentFrameData); + } + + return null; + } + + if (LuaHelpers.luaVersion == 501) + { + ulong callInfoAddress = 0; + ulong? savedProgramCounterAddress = null; + ulong baseCallInfoAddress = 0; + + if (Schema.LuaStateData.available && stateAddress.HasValue && Schema.LuaStateData.baseCallInfoAddress_5_1.HasValue) + { + callInfoAddress = DebugHelpers.ReadPointerVariable(process, stateAddress.Value + Schema.LuaStateData.callInfoAddress.GetValueOrDefault(0)).GetValueOrDefault(0); + baseCallInfoAddress = DebugHelpers.ReadPointerVariable(process, stateAddress.Value + Schema.LuaStateData.baseCallInfoAddress_5_1.GetValueOrDefault(0)).GetValueOrDefault(0); + + if (Schema.LuaStateData.savedProgramCounterAddress_5_1_opt.HasValue) + savedProgramCounterAddress = DebugHelpers.ReadPointerVariable(process, stateAddress.Value + Schema.LuaStateData.savedProgramCounterAddress_5_1_opt.GetValueOrDefault(0)).GetValueOrDefault(0); + } + else if (stateAddress.HasValue) + { + // Read lua_State + ulong temp = stateAddress.Value; + + // CommonHeader + DebugHelpers.SkipStructPointer(process, ref temp); + DebugHelpers.SkipStructByte(process, ref temp); + DebugHelpers.SkipStructByte(process, ref temp); + + DebugHelpers.SkipStructByte(process, ref temp); // status + DebugHelpers.SkipStructPointer(process, ref temp); // top + DebugHelpers.SkipStructPointer(process, ref temp); // base + DebugHelpers.SkipStructPointer(process, ref temp); // l_G + callInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); + savedProgramCounterAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); + DebugHelpers.SkipStructPointer(process, ref temp); // stack_last + DebugHelpers.SkipStructPointer(process, ref temp); // stack + DebugHelpers.SkipStructPointer(process, ref temp); // end_ci + baseCallInfoAddress = DebugHelpers.ReadStructPointer(process, ref temp).GetValueOrDefault(0); + } + + ulong currCallInfoAddress = callInfoAddress; + + while (currCallInfoAddress > baseCallInfoAddress) + { + LuaFunctionCallInfoData currCallInfoData = new LuaFunctionCallInfoData(); + + currCallInfoData.ReadFrom(process, currCallInfoAddress); + currCallInfoData.ReadFunction(process); + + // Last function call info program counter is saved in lua_State + if (currCallInfoAddress == callInfoAddress && savedProgramCounterAddress.HasValue) + currCallInfoData.savedInstructionPointerAddress = savedProgramCounterAddress.Value; + + if (currCallInfoData.func == null) + break; + + ulong prevCallInfoDataAddress; + + if (Schema.LuaFunctionCallInfoData.available) + prevCallInfoDataAddress = currCallInfoAddress - (ulong)Schema.LuaFunctionCallInfoData.structSize; + else + prevCallInfoDataAddress = currCallInfoAddress - (DebugHelpers.Is64Bit(process) ? 40ul : 24ul); + + LuaFunctionCallInfoData prevCallInfoData = new LuaFunctionCallInfoData(); + + prevCallInfoData.ReadFrom(process, prevCallInfoDataAddress); + prevCallInfoData.ReadFunction(process); + + if (prevCallInfoData.func == null) + break; + + if (stackContextData.skipFrames != 0) + { + stackContextData.skipFrames--; + + currCallInfoAddress = prevCallInfoDataAddress; + continue; + } + + if (currCallInfoData.func.baseType != LuaBaseTypes.Function()) + break; + + var currCallLuaFunction = currCallInfoData.func as LuaValueDataLuaFunction; + var currCallExternalClosure = currCallInfoData.func as LuaValueDataExternalClosure; + + Debug.Assert(currCallLuaFunction != null || currCallExternalClosure != null); + + if (currCallLuaFunction == null && currCallExternalClosure == null) + break; + + var prevCallLuaFunction = prevCallInfoData.func as LuaValueDataLuaFunction; + + string currFunctionName = "[name unavailable]"; + + // Can't get function name if calling function is unknown because of a tail call or if call was not from Lua + if (currCallInfoData.tailCallCount_5_1 > 0) + { + currFunctionName = $"[name unavailable - tail call]"; + } + else if (prevCallLuaFunction == null) + { + currFunctionName = $"[name unavailable - not called from Lua]"; + } + else + { + LuaStateSymbols stateSymbols; + + lock (processData.symbolStore) + { + stateSymbols = processData.symbolStore.FetchOrCreate(stateAddress.Value); + } + + if (currCallLuaFunction != null) + { + string functionName = stateSymbols.FetchFunctionName(currCallLuaFunction.value.functionAddress); + + if (functionName == null) + { + functionName = GetLuaFunctionName(currCallInfoAddress, prevCallInfoDataAddress, currCallLuaFunction.targetAddress); + + if (functionName != null) + stateSymbols.AddFunctionName(currCallLuaFunction.value.functionAddress, functionName); + } + + if (functionName != null) + currFunctionName = functionName; + } + else if (currCallExternalClosure != null) + { + string functionName = stateSymbols.FetchFunctionName(currCallExternalClosure.value.functionAddress); + + if (functionName == null) + { + functionName = GetLuaFunctionName(currCallInfoAddress, prevCallInfoDataAddress, currCallExternalClosure.targetAddress); + + if (functionName != null) + stateSymbols.AddFunctionName(currCallExternalClosure.value.functionAddress, functionName); + } + + if (functionName != null) + currFunctionName = functionName; + } + } + + if (currCallLuaFunction != null) + { + stackContextData.seenLuaFrame = true; + stackContextData.seenFrames++; + + var frame = GetLuaFunctionStackWalkFrame(currCallInfoAddress, currCallInfoData, currCallLuaFunction, currFunctionName); + + if (frame != null) + { + luaFrames.Add(frame); + + luaFrameFlags &= ~DkmStackWalkFrameFlags.TopFrame; + } + } + else + { + if (stackContextData.seenLuaFrame) + { + stackContextData.seenLuaFrame = false; + stackContextData.skipFrames = stackContextData.seenFrames; + break; + } + + stackContextData.seenFrames++; + + if (!fromHook) + luaFrames.Add(DkmStackWalkFrame.Create(stackContext.Thread, input.InstructionAddress, input.FrameBase, input.FrameSize, luaFrameFlags, $"[{currFunctionName} C closure]", input.Registers, input.Annotations)); + + luaFrameFlags &= ~DkmStackWalkFrameFlags.TopFrame; + } + + currCallInfoAddress = prevCallInfoDataAddress; + } + } + + if (!stackContextData.hideInternalLuaLibraryFrames) + luaFrames.Add(DkmStackWalkFrame.Create(stackContext.Thread, null, input.FrameBase, input.FrameSize, DkmStackWalkFrameFlags.NonuserCode, "[Transition to Lua]", input.Registers, input.Annotations)); + + log.Verbose($"IDkmCallStackFilter.FilterNextFrame Completed 'luaV_execute' stack frame"); + + return luaFrames.ToArray(); + } + + if (methodName.StartsWith("luau_execute") && !showHiddenFrames) + { + var flags = (input.Flags & ~DkmStackWalkFrameFlags.UserStatusNotDetermined) | DkmStackWalkFrameFlags.NonuserCode | DkmStackWalkFrameFlags.Hidden; + + return new DkmStackWalkFrame[1] { DkmStackWalkFrame.Create(stackContext.Thread, input.InstructionAddress, input.FrameBase, input.FrameSize, flags, input.Description, input.Registers, input.Annotations) }; + } + return new DkmStackWalkFrame[1] { input }; } @@ -1560,7 +1937,7 @@ void GetEvaluationSessionData(DkmProcess process, DkmInspectionSession inspectio evaluationSession.functionDataMap.Add(frameData.functionAddress, functionData); } - if (callInfoData.func != null && callInfoData.func.extendedType == LuaExtendedType.LuaFunction) + if (callInfoData.func != null && callInfoData.func.extendedType == LuaExtendedTypes.LuaFunction()) closureData = (callInfoData.func as LuaValueDataLuaFunction).value; else closureData = null; @@ -1954,7 +2331,7 @@ void IDkmLanguageExpressionEvaluator.GetFrameLocals(DkmInspectionContext inspect int count = 1 + functionData.activeLocals.Count; // 1 pseudo variable for '[registry]' table // Add upvalue list for Lua functions - if (callInfoData.func != null && callInfoData.func.extendedType == LuaExtendedType.LuaFunction) + if (callInfoData.func != null && callInfoData.func.extendedType == LuaExtendedTypes.LuaFunction()) count += functionData.upvalues.Count; completionRoutine(new DkmGetFrameLocalsAsyncResult(DkmEvaluationResultEnumContext.Create(count, stackFrame, inspectionContext, frameLocalsEnumData))); @@ -1982,7 +2359,7 @@ void IDkmLanguageExpressionEvaluator.GetItems(DkmEvaluationResultEnumContext enu LuaClosureData closureData = null; - if (frameLocalsEnumData.callInfo.func != null && frameLocalsEnumData.callInfo.func.extendedType == LuaExtendedType.LuaFunction) + if (frameLocalsEnumData.callInfo.func != null && frameLocalsEnumData.callInfo.func.extendedType == LuaExtendedTypes.LuaFunction()) { closureData = (frameLocalsEnumData.callInfo.func as LuaValueDataLuaFunction).value; @@ -2013,8 +2390,8 @@ void IDkmLanguageExpressionEvaluator.GetItems(DkmEvaluationResultEnumContext enu LuaValueDataTable envTable = new LuaValueDataTable() { - baseType = LuaBaseType.Table, - extendedType = LuaExtendedType.Table, + baseType = LuaBaseTypes.Table(), + extendedType = LuaExtendedTypes.Table(), evaluationFlags = DkmEvaluationResultFlags.ReadOnly | DkmEvaluationResultFlags.Expandable, tagAddress = 0, originalAddress = 0, @@ -2038,7 +2415,12 @@ void IDkmLanguageExpressionEvaluator.GetItems(DkmEvaluationResultEnumContext enu // Base stack contains arguments and locals that are live at the current instruction ulong address = frameLocalsEnumData.callInfo.stackBaseAddress + (ulong)index * LuaHelpers.GetValueSize(process); - string name = function.activeLocals[index].name; + var local = function.activeLocals[index]; + + if (local.targetRegister_Luau.HasValue) + address = frameLocalsEnumData.callInfo.stackBaseAddress + (ulong)local.targetRegister_Luau.Value * LuaHelpers.GetValueSize(process); + + string name = local.name; for (int k = index + 1; k < function.activeLocals.Count; k++) { @@ -2275,7 +2657,7 @@ void IDkmLanguageExpressionEvaluator.SetValueAsString(DkmEvaluationResult result if (LuaHelpers.luaVersion == 504 && evalData.luaValueData.tagAddress != 0) { - if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, (int)LuaExtendedType.BooleanTrue)) + if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, LuaExtendedTypes.BooleanTrue())) { errorText = "Failed to modify target process memory"; return; @@ -2299,7 +2681,7 @@ void IDkmLanguageExpressionEvaluator.SetValueAsString(DkmEvaluationResult result if (LuaHelpers.luaVersion == 504 && evalData.luaValueData.tagAddress != 0) { - if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, (int)LuaExtendedType.Boolean)) + if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, LuaExtendedTypes.Boolean())) { errorText = "Failed to modify target process memory"; return; @@ -2323,7 +2705,7 @@ void IDkmLanguageExpressionEvaluator.SetValueAsString(DkmEvaluationResult result if (LuaHelpers.luaVersion == 504 && evalData.luaValueData.tagAddress != 0) { - if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, (int)(intValue != 0 ? LuaExtendedType.BooleanTrue : LuaExtendedType.Boolean))) + if (!DebugHelpers.TryWriteIntVariable(process, evalData.luaValueData.tagAddress, intValue != 0 ? LuaExtendedTypes.BooleanTrue() : LuaExtendedTypes.Boolean())) { errorText = "Failed to modify target process memory"; return; @@ -2633,7 +3015,7 @@ DkmResolvedDocument[] IDkmSymbolDocumentCollectionQuery.FindDocuments(DkmModule bool FindFunctionInstructionForLine(DkmProcess process, LuaFunctionData function, int startLine, int endLine, out LuaFunctionData targetFunction, out int targetInstructionPointer, out int targetLine) { // TODO: Reverse search in line map - if (function.absLineInfoSize_5_4.HasValue) + if (function.absLineInfoSize_5_4.HasValue || function.linegaplog2_Luau.HasValue) { targetFunction = null; targetInstructionPointer = 0; diff --git a/LuaDkmDebuggerComponent/LuaValues.cs b/LuaDkmDebuggerComponent/LuaValues.cs index dcdbb99..cb32aa1 100644 --- a/LuaDkmDebuggerComponent/LuaValues.cs +++ b/LuaDkmDebuggerComponent/LuaValues.cs @@ -5,8 +5,8 @@ namespace LuaDkmDebuggerComponent { public abstract class LuaValueDataBase { - public LuaBaseType baseType; - public LuaExtendedType extendedType; + public int baseType; + public int extendedType; public DkmEvaluationResultFlags evaluationFlags; public ulong tagAddress; public ulong originalAddress; @@ -25,8 +25,8 @@ public LuaValueDataError() public LuaValueDataError(string value, bool stoppedOnSideEffect = false) { - baseType = LuaBaseType.Nil; - extendedType = LuaExtendedType.Nil; + baseType = LuaBaseTypes.Nil(); + extendedType = LuaExtendedTypes.Nil(); evaluationFlags = DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; originalAddress = 0; @@ -63,8 +63,8 @@ public class LuaValueDataNil : LuaValueDataBase { public LuaValueDataNil() { - baseType = LuaBaseType.Nil; - extendedType = LuaExtendedType.Nil; + baseType = LuaBaseTypes.Nil(); + extendedType = LuaExtendedTypes.Nil(); evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; originalAddress = 0; @@ -100,12 +100,12 @@ public LuaValueDataBool() public LuaValueDataBool(bool value) { - baseType = LuaBaseType.Boolean; + baseType = LuaBaseTypes.Boolean(); if (LuaHelpers.luaVersion == 504) - extendedType = value ? LuaExtendedType.BooleanTrue : LuaExtendedType.Boolean; + extendedType = value ? LuaExtendedTypes.BooleanTrue() : LuaExtendedTypes.Boolean(); else - extendedType = LuaExtendedType.Boolean; + extendedType = LuaExtendedTypes.Boolean(); evaluationFlags = (value ? DkmEvaluationResultFlags.BooleanTrue : DkmEvaluationResultFlags.None) | DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.Boolean | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; @@ -145,8 +145,8 @@ public LuaValueDataLightUserData() public LuaValueDataLightUserData(ulong value) { - baseType = LuaBaseType.LightUserData; - extendedType = LuaExtendedType.LightUserData; + baseType = LuaBaseTypes.LightUserData(); + extendedType = LuaExtendedTypes.LightUserData(); evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; originalAddress = 0; @@ -185,7 +185,7 @@ public LuaValueDataNumber() public LuaValueDataNumber(int value) { - baseType = LuaBaseType.Number; + baseType = LuaBaseTypes.Number(); extendedType = LuaHelpers.GetIntegerNumberExtendedType(); evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; @@ -195,7 +195,7 @@ public LuaValueDataNumber(int value) public LuaValueDataNumber(double value) { - baseType = LuaBaseType.Number; + baseType = LuaBaseTypes.Number(); extendedType = LuaHelpers.GetFloatNumberExtendedType(); evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; @@ -243,8 +243,8 @@ public LuaValueDataString() public LuaValueDataString(string value) { - baseType = LuaBaseType.String; - extendedType = LuaExtendedType.ShortString; + baseType = LuaBaseTypes.String(); + extendedType = LuaExtendedTypes.ShortString(); evaluationFlags = DkmEvaluationResultFlags.IsBuiltInType | DkmEvaluationResultFlags.ReadOnly; tagAddress = 0; originalAddress = 0; @@ -263,7 +263,7 @@ public override bool LuaCompare(LuaValueDataBase rhs) public override string GetLuaType() { - return extendedType == LuaExtendedType.ShortString ? "short_string" : "long_string"; + return extendedType == LuaExtendedTypes.ShortString() ? "short_string" : "long_string"; } public override string AsSimpleDisplayString(uint radix) diff --git a/LuaDkmDebuggerComponent/RemoteComponent.cs b/LuaDkmDebuggerComponent/RemoteComponent.cs index 56bc2b0..9446e47 100644 --- a/LuaDkmDebuggerComponent/RemoteComponent.cs +++ b/LuaDkmDebuggerComponent/RemoteComponent.cs @@ -709,7 +709,7 @@ void IDkmLanguageConditionEvaluator.EvaluateCondition(DkmEvaluationBreakpointCon return; } - if (callInfoData.func.extendedType != LuaExtendedType.LuaFunction) + if (callInfoData.func.extendedType != LuaExtendedTypes.LuaFunction()) { inspectionSession.Close(); @@ -773,7 +773,7 @@ void IDkmLanguageConditionEvaluator.EvaluateCondition(DkmEvaluationBreakpointCon return; } - if (result.baseType == LuaBaseType.Nil) + if (result.baseType == LuaBaseTypes.Nil()) { inspectionSession.Close();