ARM64UnwindEncoder.h 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #pragma once
  6. class Arm64XdataGenerator
  7. {
  8. public:
  9. Arm64XdataGenerator();
  10. void Generate(PULONG prologStart, PULONG prologEnd, PULONG epilogStart, PULONG epilogEnd, PULONG functionEnd, ULONG exceptionHandlerRva = 0, ULONG exceptionData = 0);
  11. const VOID *GetXdata() const { return &this->m_xdata[0]; }
  12. ULONG GetXdataBytes() const { return this->m_xdataBytes; }
  13. // xdata opcodes map 1:1 with instructions; compute an estimated maximum
  14. // instructions for prolog/epilog:
  15. // 4: 2xMOV + BL + SUB for allocation (worst case assumes _chkstk call)
  16. // 4: STP for homing parameters (x0-x7)
  17. // 6: STP for saving integer non-vols in pairs (x19-x28 plus fp/lr)
  18. // 4: STP for saving FP non-vols in pairs (d8-d16)
  19. // =18, round up to 20 instructions for flotsam (extra no-ops)
  20. // Assume 2 bytes/opcode on average (one opcode is 4 bytes; many are only 1)
  21. // Plus a factor of 2 because prolog and epilog can each have a list
  22. static const ULONG maxOpcodeBytes = 20 * 2 * 2;
  23. // xdata consists of a few header words plus opcodes for prolog and epilog:
  24. // 8: up to 2 header words
  25. // 4: possibly 1 exception scope
  26. // MOB: opcode bytes
  27. // 8: exception handler + data
  28. static const ULONG maxXdataSize = 8 + 4 + maxOpcodeBytes + 8;
  29. private:
  30. void SafeAppendDword(DWORD value);
  31. ULONG m_xdata[(maxXdataSize + 3) / 4];
  32. ULONG m_xdataBytes;
  33. };
  34. struct OpcodeMatcher
  35. {
  36. bool Matches(ULONG opcode) const { return (opcode & this->mask) == this->pattern; }
  37. ULONG mask;
  38. ULONG pattern;
  39. };
  40. struct OpcodeList
  41. {
  42. OpcodeMatcher subSpSpImm; // sub sp, sp, #imm / add sp, sp, #imm [epilog]
  43. OpcodeMatcher subSpSpReg; // sub sp, sp, reg / add sp, sp, reg [epilog]
  44. OpcodeMatcher addFpSpImm; // add fp, sp, #imm
  45. OpcodeMatcher stpRtRt2SpOffs; // stp rt, rt2, [sp, #offs] / ldp rt, rt2, [sp, #offs]
  46. OpcodeMatcher stpRtRt2SpOffsBang; // stp rt, rt2, [sp, #offs]! / ldp rt, rt2, [sp], #offs
  47. OpcodeMatcher strRtSpOffs; // str rt, [sp, #offs] / ldr rt, [sp, #offs]
  48. OpcodeMatcher strRtSpOffsBang; // str rt, [sp, #offs]! / ldr rt, [sp], #offs
  49. OpcodeMatcher stpDtDt2SpOffs; // stp dt, dt2, [sp, #offs] / ldp dt, dt2, [sp, #offs]
  50. OpcodeMatcher stpDtDt2SpOffsBang; // stp dt, dt2, [sp, #offs]! / ldp dt, dt2, [sp], #offs
  51. OpcodeMatcher strDtSpOffs; // str dt, [sp, #offs] / ldr dt, [sp, #offs]
  52. OpcodeMatcher strDtSpOffsBang; // str dt, [sp, #offs]! / ldr dt, [sp], #offs
  53. };
  54. class Arm64UnwindCodeGenerator
  55. {
  56. // opcodes as defined by the Windows ARM64 Exception Data document
  57. static const ULONG op_alloc_s = 0x00; // 000xxxxx: allocate small stack with size < 512 (2^5 * 16).
  58. static const ULONG op_save_r19r20_x = 0x20; // 001zzzzz: save <r19,r20> pair at [sp-#Z*8]!, pre-indexed offset >= -248
  59. static const ULONG op_save_fplr = 0x40; // 01zzzzzz: save <r29,lr> pair at [sp+#Z*8], offset <= 504.
  60. static const ULONG op_save_fplr_x = 0x80; // 10zzzzzz: save <r29,lr> pair at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
  61. static const ULONG op_alloc_m = 0xc000; // 11000xxx|xxxxxxxx: allocate large stack with size < 32k (2^11 * 16).
  62. static const ULONG op_save_regp = 0xc800; // 110010xx|xzzzzzz: save r(19+#X) pair at [sp+#Z*8], offset <= 504
  63. static const ULONG op_save_regp_x = 0xcc00; // 110011xx|xxzzzzzz: save pair r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
  64. static const ULONG op_save_reg = 0xd000; // 110100xx|xxzzzzzz: save reg r(19+#X) at [sp+#Z*8], offset <=504
  65. static const ULONG op_save_reg_x = 0xd400; // 1101010x|xxxzzzzz: save reg r(19+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256
  66. static const ULONG op_save_lrpair = 0xd600; // 1101011x|xxzzzzzz: save pair <r19+2*#X,rl> at [sp+#Z*8], offset <= 504
  67. static const ULONG op_save_fregp = 0xd800; // 1101100x|xxzzzzzz: save pair d(8+#X) at [sp+#Z*8], offset <=504
  68. static const ULONG op_save_fregp_x = 0xda00; // 1101101x|xxzzzzzz: save pair d(8+#X), at [sp-(#Z+1)*8]!, pre-indexed offset >= -512
  69. static const ULONG op_save_freg = 0xdc00; // 1101110x|xxzzzzzz: save reg d(9+#X) at [sp+#Z*8], offset <=504
  70. static const ULONG op_save_freg_x = 0xde00; // 11011110|xxxzzzzz: save reg d(8+#X) at [sp-(#Z+1)*8]!, pre-indexed offset >= -256
  71. static const ULONG op_alloc_l = 0xe0000000; // 11100000|xxxxxxxx|xxxxxxxx|xxxxxxxx: allocate large stack with size < 256M
  72. static const ULONG op_set_fp = 0xe1; // 11100001: set up r29: with: mov r29,sp
  73. static const ULONG op_add_fp = 0xe200; // 11100010|xxxxxxxx: set up r29 with: add r29,sp,#x*8
  74. static const ULONG op_nop = 0xe3; // 11100011: no unwind operation is required.
  75. static const ULONG op_end = 0xe4; // 11100100: end of unwind code
  76. static const ULONG op_end_c = 0xe5; // 11100101: end of unwind code in current chained scope.
  77. static const ULONG op_save_next = 0xe6; // 11100110: save next non-volatile Int or FP register pair.**
  78. // maximum instructions in the internal buffer, figuring:
  79. // +4 for worst-case stack allocation (via chkstk)
  80. // +6 for saving integer non-volatiles
  81. // +4 for saving FP non-volatiles
  82. // +4 for homing paramters
  83. // +1 for setting up/recovering frame pointer
  84. // +1 for return
  85. // +12 for random things and to be safe and to make a nice power of 2
  86. static const ULONG MAX_INSTRUCTIONS = 32;
  87. public:
  88. Arm64UnwindCodeGenerator();
  89. // generate the code for a prolog or epilog as appropriate
  90. ULONG GeneratePrologCodes(PBYTE buffer, ULONG bufferSize, PULONG &prologStart, PULONG &prologEnd);
  91. ULONG GenerateEpilogCodes(PBYTE buffer, ULONG bufferSize, PULONG &epilogStart, PULONG &epilogEnd);
  92. private:
  93. // internal helpers
  94. ULONG GenerateSingleOpcode(PULONG pOpcode, PULONG pRegionStart, const OpcodeList &opcodeList);
  95. ULONG TrimNops(PULONG opcodeList, ULONG numOpcodes);
  96. void ReverseCodes(PULONG opcodeList, ULONG numOpcodes);
  97. ULONG64 FindRegisterImmediate(int regNum, PULONG registerStart, PULONG regionEnd);
  98. ULONG EmitFinalCodes(PBYTE buffer, ULONG bufferSize, PULONG opcodes, ULONG count);
  99. // encode an opcode and parameters, up to 4 bytes depending on the opcode
  100. ULONG SafeEncode(ULONG opcode, ULONG params);
  101. // encode an opcode and parameters for a register pair saving operation
  102. ULONG SafeEncodeRegPair(ULONG opcode, ULONG params, int regBase, int rebasedOffset);
  103. // rebase registers and offets as needed for opcodes
  104. int RebaseReg(int reg);
  105. int RebaseRegPair(int reg1, int reg2);
  106. int RebaseFpReg(int reg);
  107. int RebaseFpRegPair(int reg1, int reg2);
  108. int RebaseOffset(int offset, int maxOffset);
  109. // specific encoders
  110. ULONG EncodeAlloc(ULONG bytes);
  111. ULONG EncodeStoreReg(int reg, int offset);
  112. ULONG EncodeStoreRegPredec(int reg, int offset);
  113. ULONG EncodeStorePair(int reg1, int reg2, int offset);
  114. ULONG EncodeStorePairPredec(int reg1, int reg2, int offset);
  115. ULONG EncodeStoreFpReg(int reg, int offset);
  116. ULONG EncodeStoreFpRegPredec(int reg, int offset);
  117. ULONG EncodeStoreFpPair(int reg1, int reg2, int offset);
  118. ULONG EncodeStoreFpPairPredec(int reg1, int reg2, int offset);
  119. ULONG EncodeAddFp(int offset);
  120. // simple inline encoders
  121. ULONG EncodeSetFp() { return this->SafeEncode(op_set_fp, 0); }
  122. ULONG EncodeNop() { return this->SafeEncode(op_nop, 0); }
  123. ULONG EncodeEnd() { return this->SafeEncode(op_end, 0); }
  124. // save_next tracking
  125. int m_lastPair;
  126. int m_lastPairOffset;
  127. // immediate tracking
  128. int64 m_pendingImmediate;
  129. int m_pendingImmediateReg;
  130. // fixed opcodes
  131. static const OpcodeMatcher MovkOpcode;
  132. static const OpcodeMatcher BlrOpcode;
  133. static const OpcodeMatcher RetOpcode;
  134. static const OpcodeMatcher SubSpSpX15Uxtx4Opcode;
  135. // specific opcodes for prologs vs. epilogs
  136. static const OpcodeList PrologOpcodes;
  137. static const OpcodeList EpilogOpcodes;
  138. };