be_i386.c 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866
  1. /*
  2. * Debugger i386 specific functions
  3. *
  4. * Copyright 2004 Eric Pouech
  5. *
  6. * This library is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 2.1 of the License, or (at your option) any later version.
  10. *
  11. * This library is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with this library; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #include "debugger.h"
  21. #include "wine/debug.h"
  22. #if defined(__i386__) || defined(__x86_64__)
  23. WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
  24. /* db_disasm.c */
  25. extern void be_i386_disasm_one_insn(ADDRESS64* addr, int display);
  26. #define STEP_FLAG 0x00000100 /* single step flag */
  27. #define V86_FLAG 0x00020000
  28. #define IS_VM86_MODE(ctx) (ctx->EFlags & V86_FLAG)
  29. static ADDRESS_MODE get_selector_type(HANDLE hThread, const WOW64_CONTEXT *ctx, WORD sel)
  30. {
  31. LDT_ENTRY le;
  32. if (IS_VM86_MODE(ctx)) return AddrModeReal;
  33. /* null or system selector */
  34. if (!(sel & 4) || ((sel >> 3) < 17)) return AddrModeFlat;
  35. if (dbg_curr_process->process_io->get_selector(hThread, sel, &le))
  36. return le.HighWord.Bits.Default_Big ? AddrMode1632 : AddrMode1616;
  37. /* selector doesn't exist */
  38. return -1;
  39. }
  40. static void* be_i386_linearize(HANDLE hThread, const ADDRESS64* addr)
  41. {
  42. LDT_ENTRY le;
  43. switch (addr->Mode)
  44. {
  45. case AddrModeReal:
  46. return (void*)((DWORD_PTR)(LOWORD(addr->Segment) << 4) + (DWORD_PTR)addr->Offset);
  47. case AddrMode1632:
  48. if (!(addr->Segment & 4) || ((addr->Segment >> 3) < 17))
  49. return (void*)(DWORD_PTR)addr->Offset;
  50. /* fall through */
  51. case AddrMode1616:
  52. if (!dbg_curr_process->process_io->get_selector(hThread, addr->Segment, &le)) return NULL;
  53. return (void*)((le.HighWord.Bits.BaseHi << 24) +
  54. (le.HighWord.Bits.BaseMid << 16) + le.BaseLow +
  55. (DWORD_PTR)addr->Offset);
  56. case AddrModeFlat:
  57. return (void*)(DWORD_PTR)addr->Offset;
  58. }
  59. return NULL;
  60. }
  61. static BOOL be_i386_build_addr(HANDLE hThread, const dbg_ctx_t *ctx, ADDRESS64* addr,
  62. unsigned seg, DWORD64 offset)
  63. {
  64. addr->Mode = AddrModeFlat;
  65. addr->Segment = seg;
  66. addr->Offset = offset;
  67. if (seg)
  68. {
  69. addr->Mode = get_selector_type(hThread, &ctx->x86, seg);
  70. switch (addr->Mode)
  71. {
  72. case AddrModeReal:
  73. case AddrMode1616:
  74. addr->Offset &= 0xffff;
  75. break;
  76. case AddrModeFlat:
  77. case AddrMode1632:
  78. break;
  79. default:
  80. addr->Mode = -1;
  81. return FALSE;
  82. }
  83. }
  84. return TRUE;
  85. }
  86. static BOOL be_i386_get_addr(HANDLE hThread, const dbg_ctx_t *ctx,
  87. enum be_cpu_addr bca, ADDRESS64* addr)
  88. {
  89. switch (bca)
  90. {
  91. case be_cpu_addr_pc:
  92. return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegCs, ctx->x86.Eip);
  93. case be_cpu_addr_stack:
  94. return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegSs, ctx->x86.Esp);
  95. case be_cpu_addr_frame:
  96. return be_i386_build_addr(hThread, ctx, addr, ctx->x86.SegSs, ctx->x86.Ebp);
  97. }
  98. return FALSE;
  99. }
  100. static BOOL be_i386_get_register_info(int regno, enum be_cpu_addr* kind)
  101. {
  102. switch (regno)
  103. {
  104. case CV_REG_EIP: *kind = be_cpu_addr_pc; return TRUE;
  105. case CV_REG_EBP: *kind = be_cpu_addr_frame; return TRUE;
  106. case CV_REG_ESP: *kind = be_cpu_addr_stack; return TRUE;
  107. }
  108. return FALSE;
  109. }
  110. static void be_i386_single_step(dbg_ctx_t *ctx, BOOL enable)
  111. {
  112. if (enable) ctx->x86.EFlags |= STEP_FLAG;
  113. else ctx->x86.EFlags &= ~STEP_FLAG;
  114. }
  115. static void be_i386_all_print_context(HANDLE hThread, const dbg_ctx_t *pctx)
  116. {
  117. static const char mxcsr_flags[16][4] = { "IE", "DE", "ZE", "OE", "UE", "PE", "DAZ", "IM",
  118. "DM", "ZM", "OM", "UM", "PM", "R-", "R+", "FZ" };
  119. const WOW64_CONTEXT *ctx = &pctx->x86;
  120. XSAVE_FORMAT *xmm_area;
  121. int cnt;
  122. /* Break out the FPU state and the floating point registers */
  123. dbg_printf("Floating Point Unit status:\n");
  124. dbg_printf(" FLCW:%04x ", LOWORD(ctx->FloatSave.ControlWord));
  125. dbg_printf(" FLTW:%04x ", LOWORD(ctx->FloatSave.TagWord));
  126. dbg_printf(" FLEO:%08x ", (unsigned int) ctx->FloatSave.ErrorOffset);
  127. dbg_printf(" FLSW:%04x", LOWORD(ctx->FloatSave.StatusWord));
  128. /* Isolate the condition code bits - note they are not contiguous */
  129. dbg_printf("(CC:%d%d%d%d", (ctx->FloatSave.StatusWord & 0x00004000) >> 14,
  130. (ctx->FloatSave.StatusWord & 0x00000400) >> 10,
  131. (ctx->FloatSave.StatusWord & 0x00000200) >> 9,
  132. (ctx->FloatSave.StatusWord & 0x00000100) >> 8);
  133. /* Now pull out the 3 bit of the TOP stack pointer */
  134. dbg_printf(" TOP:%01x", (unsigned int) (ctx->FloatSave.StatusWord & 0x00003800) >> 11);
  135. /* Lets analyse the error bits and indicate the status
  136. * the Invalid Op flag has sub status which is tested as follows */
  137. if (ctx->FloatSave.StatusWord & 0x00000001) { /* Invalid Fl OP */
  138. if (ctx->FloatSave.StatusWord & 0x00000040) { /* Stack Fault */
  139. if (ctx->FloatSave.StatusWord & 0x00000200) /* C1 says Overflow */
  140. dbg_printf(" #IE(Stack Overflow)");
  141. else
  142. dbg_printf(" #IE(Stack Underflow)"); /* Underflow */
  143. }
  144. else dbg_printf(" #IE(Arthimetic error)"); /* Invalid Fl OP */
  145. }
  146. if (ctx->FloatSave.StatusWord & 0x00000002) dbg_printf(" #DE"); /* Denormalised OP */
  147. if (ctx->FloatSave.StatusWord & 0x00000004) dbg_printf(" #ZE"); /* Zero Divide */
  148. if (ctx->FloatSave.StatusWord & 0x00000008) dbg_printf(" #OE"); /* Overflow */
  149. if (ctx->FloatSave.StatusWord & 0x00000010) dbg_printf(" #UE"); /* Underflow */
  150. if (ctx->FloatSave.StatusWord & 0x00000020) dbg_printf(" #PE"); /* Precision error */
  151. if (ctx->FloatSave.StatusWord & 0x00000040)
  152. if (!(ctx->FloatSave.StatusWord & 0x00000001))
  153. dbg_printf(" #SE"); /* Stack Fault (don't think this can occur) */
  154. if (ctx->FloatSave.StatusWord & 0x00000080) dbg_printf(" #ES"); /* Error Summary */
  155. if (ctx->FloatSave.StatusWord & 0x00008000) dbg_printf(" #FB"); /* FPU Busy */
  156. dbg_printf(")\n");
  157. /* Here are the rest of the registers */
  158. dbg_printf(" FLES:%08x FLDO:%08x FLDS:%08x FLCNS:%08x\n",
  159. ctx->FloatSave.ErrorSelector,
  160. ctx->FloatSave.DataOffset,
  161. ctx->FloatSave.DataSelector,
  162. ctx->FloatSave.Cr0NpxState);
  163. /* Now for the floating point registers */
  164. dbg_printf("Floating Point Registers:\n");
  165. for (cnt = 0; cnt < 8; cnt++)
  166. {
  167. const BYTE *p = &ctx->FloatSave.RegisterArea[cnt * 10];
  168. if (cnt == 4) dbg_printf("\n");
  169. dbg_printf(" ST%d:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x ", cnt,
  170. p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9] );
  171. }
  172. xmm_area = (XSAVE_FORMAT *) &ctx->ExtendedRegisters;
  173. dbg_printf(" mxcsr: %04x (", xmm_area->MxCsr );
  174. for (cnt = 0; cnt < 16; cnt++)
  175. if (xmm_area->MxCsr & (1 << cnt)) dbg_printf( " %s", mxcsr_flags[cnt] );
  176. dbg_printf(" )\n");
  177. for (cnt = 0; cnt < 8; cnt++)
  178. {
  179. dbg_printf( " xmm%u: uint=%08x%08x%08x%08x", cnt,
  180. *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 3),
  181. *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 2),
  182. *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 1),
  183. *((unsigned int *)&xmm_area->XmmRegisters[cnt] + 0));
  184. dbg_printf( " double={%g; %g}", *(double *)&xmm_area->XmmRegisters[cnt].Low,
  185. *(double *)&xmm_area->XmmRegisters[cnt].High );
  186. dbg_printf( " float={%g; %g; %g; %g}\n",
  187. (double)*((float *)&xmm_area->XmmRegisters[cnt] + 0),
  188. (double)*((float *)&xmm_area->XmmRegisters[cnt] + 1),
  189. (double)*((float *)&xmm_area->XmmRegisters[cnt] + 2),
  190. (double)*((float *)&xmm_area->XmmRegisters[cnt] + 3) );
  191. }
  192. dbg_printf("\n");
  193. }
  194. static void be_i386_print_context(HANDLE hThread, const dbg_ctx_t *pctx, int all_regs)
  195. {
  196. static const char flags[] = "aVR-N--ODITSZ-A-P-C";
  197. const WOW64_CONTEXT *ctx = &pctx->x86;
  198. int i;
  199. char buf[33];
  200. dbg_printf("Register dump:\n");
  201. /* First get the segment registers out of the way */
  202. dbg_printf(" CS:%04x SS:%04x DS:%04x ES:%04x FS:%04x GS:%04x",
  203. (WORD)ctx->SegCs, (WORD)ctx->SegSs,
  204. (WORD)ctx->SegDs, (WORD)ctx->SegEs,
  205. (WORD)ctx->SegFs, (WORD)ctx->SegGs);
  206. strcpy(buf, flags);
  207. for (i = 0; buf[i]; i++)
  208. if (buf[i] != '-' && !(ctx->EFlags & (1 << (sizeof(flags) - 2 - i))))
  209. buf[i] = ' ';
  210. switch (get_selector_type(hThread, ctx, ctx->SegCs))
  211. {
  212. case AddrMode1616:
  213. case AddrModeReal:
  214. dbg_printf("\n IP:%04x SP:%04x BP:%04x FLAGS:%04x(%s)\n",
  215. LOWORD(ctx->Eip), LOWORD(ctx->Esp),
  216. LOWORD(ctx->Ebp), LOWORD(ctx->EFlags), buf);
  217. dbg_printf(" AX:%04x BX:%04x CX:%04x DX:%04x SI:%04x DI:%04x\n",
  218. LOWORD(ctx->Eax), LOWORD(ctx->Ebx),
  219. LOWORD(ctx->Ecx), LOWORD(ctx->Edx),
  220. LOWORD(ctx->Esi), LOWORD(ctx->Edi));
  221. break;
  222. case AddrModeFlat:
  223. case AddrMode1632:
  224. dbg_printf("\n EIP:%08x ESP:%08x EBP:%08x EFLAGS:%08x(%s)\n",
  225. ctx->Eip, ctx->Esp, ctx->Ebp, ctx->EFlags, buf);
  226. dbg_printf(" EAX:%08x EBX:%08x ECX:%08x EDX:%08x\n",
  227. ctx->Eax, ctx->Ebx, ctx->Ecx, ctx->Edx);
  228. dbg_printf(" ESI:%08x EDI:%08x\n",
  229. ctx->Esi, ctx->Edi);
  230. break;
  231. }
  232. if (all_regs) be_i386_all_print_context(hThread, pctx);
  233. }
  234. static void be_i386_print_segment_info(HANDLE hThread, const dbg_ctx_t *ctx)
  235. {
  236. if (get_selector_type(hThread, &ctx->x86, ctx->x86.SegCs) == AddrMode1616)
  237. {
  238. info_win32_segments(ctx->x86.SegDs >> 3, 1);
  239. if (ctx->x86.SegEs != ctx->x86.SegDs)
  240. info_win32_segments(ctx->x86.SegEs >> 3, 1);
  241. }
  242. info_win32_segments(ctx->x86.SegFs >> 3, 1);
  243. }
  244. static struct dbg_internal_var be_i386_ctx[] =
  245. {
  246. {CV_REG_AL, "AL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_char_int},
  247. {CV_REG_CL, "CL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_char_int},
  248. {CV_REG_DL, "DL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_char_int},
  249. {CV_REG_BL, "BL", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_char_int},
  250. {CV_REG_AH, "AH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Eax)+1), dbg_itype_unsigned_char_int},
  251. {CV_REG_CH, "CH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Ecx)+1), dbg_itype_unsigned_char_int},
  252. {CV_REG_DH, "DH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Edx)+1), dbg_itype_unsigned_char_int},
  253. {CV_REG_BH, "BH", (void*)(FIELD_OFFSET(WOW64_CONTEXT, Ebx)+1), dbg_itype_unsigned_char_int},
  254. {CV_REG_AX, "AX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_short_int},
  255. {CV_REG_CX, "CX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_short_int},
  256. {CV_REG_DX, "DX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_short_int},
  257. {CV_REG_BX, "BX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_short_int},
  258. {CV_REG_SP, "SP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esp), dbg_itype_unsigned_short_int},
  259. {CV_REG_BP, "BP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebp), dbg_itype_unsigned_short_int},
  260. {CV_REG_SI, "SI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esi), dbg_itype_unsigned_short_int},
  261. {CV_REG_DI, "DI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edi), dbg_itype_unsigned_short_int},
  262. {CV_REG_EAX, "EAX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eax), dbg_itype_unsigned_int},
  263. {CV_REG_ECX, "ECX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ecx), dbg_itype_unsigned_int},
  264. {CV_REG_EDX, "EDX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edx), dbg_itype_unsigned_int},
  265. {CV_REG_EBX, "EBX", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebx), dbg_itype_unsigned_int},
  266. {CV_REG_ESP, "ESP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esp), dbg_itype_unsigned_int},
  267. {CV_REG_EBP, "EBP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Ebp), dbg_itype_unsigned_int},
  268. {CV_REG_ESI, "ESI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Esi), dbg_itype_unsigned_int},
  269. {CV_REG_EDI, "EDI", (void*)FIELD_OFFSET(WOW64_CONTEXT, Edi), dbg_itype_unsigned_int},
  270. {CV_REG_ES, "ES", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegEs), dbg_itype_unsigned_short_int},
  271. {CV_REG_CS, "CS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegCs), dbg_itype_unsigned_short_int},
  272. {CV_REG_SS, "SS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegSs), dbg_itype_unsigned_short_int},
  273. {CV_REG_DS, "DS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegDs), dbg_itype_unsigned_short_int},
  274. {CV_REG_FS, "FS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegFs), dbg_itype_unsigned_short_int},
  275. {CV_REG_GS, "GS", (void*)FIELD_OFFSET(WOW64_CONTEXT, SegGs), dbg_itype_unsigned_short_int},
  276. {CV_REG_IP, "IP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eip), dbg_itype_unsigned_short_int},
  277. {CV_REG_FLAGS, "FLAGS", (void*)FIELD_OFFSET(WOW64_CONTEXT, EFlags), dbg_itype_unsigned_short_int},
  278. {CV_REG_EIP, "EIP", (void*)FIELD_OFFSET(WOW64_CONTEXT, Eip), dbg_itype_unsigned_int},
  279. {CV_REG_EFLAGS, "EFLAGS", (void*)FIELD_OFFSET(WOW64_CONTEXT, EFlags), dbg_itype_unsigned_int},
  280. {CV_REG_ST0, "ST0", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[ 0]), dbg_itype_long_real},
  281. {CV_REG_ST0+1, "ST1", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[10]), dbg_itype_long_real},
  282. {CV_REG_ST0+2, "ST2", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[20]), dbg_itype_long_real},
  283. {CV_REG_ST0+3, "ST3", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[30]), dbg_itype_long_real},
  284. {CV_REG_ST0+4, "ST4", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[40]), dbg_itype_long_real},
  285. {CV_REG_ST0+5, "ST5", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[50]), dbg_itype_long_real},
  286. {CV_REG_ST0+6, "ST6", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[60]), dbg_itype_long_real},
  287. {CV_REG_ST0+7, "ST7", (void*)FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[70]), dbg_itype_long_real},
  288. {CV_AMD64_XMM0, "XMM0", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[0])), dbg_itype_m128a},
  289. {CV_AMD64_XMM0+1, "XMM1", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[1])), dbg_itype_m128a},
  290. {CV_AMD64_XMM0+2, "XMM2", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[2])), dbg_itype_m128a},
  291. {CV_AMD64_XMM0+3, "XMM3", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[3])), dbg_itype_m128a},
  292. {CV_AMD64_XMM0+4, "XMM4", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[4])), dbg_itype_m128a},
  293. {CV_AMD64_XMM0+5, "XMM5", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[5])), dbg_itype_m128a},
  294. {CV_AMD64_XMM0+6, "XMM6", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[6])), dbg_itype_m128a},
  295. {CV_AMD64_XMM0+7, "XMM7", (void*)(FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[7])), dbg_itype_m128a},
  296. {0, NULL, 0, dbg_itype_none}
  297. };
  298. static BOOL be_i386_is_step_over_insn(const void* insn)
  299. {
  300. BYTE ch;
  301. for (;;)
  302. {
  303. if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
  304. switch (ch)
  305. {
  306. /* Skip all prefixes */
  307. case 0x2e: /* cs: */
  308. case 0x36: /* ss: */
  309. case 0x3e: /* ds: */
  310. case 0x26: /* es: */
  311. case 0x64: /* fs: */
  312. case 0x65: /* gs: */
  313. case 0x66: /* opcode size prefix */
  314. case 0x67: /* addr size prefix */
  315. case 0xf0: /* lock */
  316. case 0xf2: /* repne */
  317. case 0xf3: /* repe */
  318. insn = (const char*)insn + 1;
  319. continue;
  320. /* Handle call instructions */
  321. case 0xcd: /* int <intno> */
  322. case 0xe8: /* call <offset> */
  323. case 0x9a: /* lcall <seg>:<off> */
  324. return TRUE;
  325. case 0xff: /* call <regmodrm> */
  326. if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
  327. return FALSE;
  328. return (((ch & 0x38) == 0x10) || ((ch & 0x38) == 0x18));
  329. /* Handle string instructions */
  330. case 0x6c: /* insb */
  331. case 0x6d: /* insw */
  332. case 0x6e: /* outsb */
  333. case 0x6f: /* outsw */
  334. case 0xa4: /* movsb */
  335. case 0xa5: /* movsw */
  336. case 0xa6: /* cmpsb */
  337. case 0xa7: /* cmpsw */
  338. case 0xaa: /* stosb */
  339. case 0xab: /* stosw */
  340. case 0xac: /* lodsb */
  341. case 0xad: /* lodsw */
  342. case 0xae: /* scasb */
  343. case 0xaf: /* scasw */
  344. return TRUE;
  345. default:
  346. return FALSE;
  347. }
  348. }
  349. }
  350. static BOOL be_i386_is_function_return(const void* insn)
  351. {
  352. BYTE ch;
  353. if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
  354. if (ch == 0xF3) /* REP */
  355. {
  356. insn = (const char*)insn + 1;
  357. if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
  358. }
  359. return (ch == 0xC2) || (ch == 0xC3);
  360. }
  361. static BOOL be_i386_is_break_insn(const void* insn)
  362. {
  363. BYTE c;
  364. if (!dbg_read_memory(insn, &c, sizeof(c))) return FALSE;
  365. return c == 0xCC;
  366. }
  367. static unsigned get_size(ADDRESS_MODE am)
  368. {
  369. if (am == AddrModeReal || am == AddrMode1616) return 16;
  370. return 32;
  371. }
  372. static BOOL fetch_value(const char* addr, unsigned sz, int* value)
  373. {
  374. char value8;
  375. short value16;
  376. switch (sz)
  377. {
  378. case 8:
  379. if (!dbg_read_memory(addr, &value8, sizeof(value8)))
  380. return FALSE;
  381. *value = value8;
  382. break;
  383. case 16:
  384. if (!dbg_read_memory(addr, &value16, sizeof(value16)))
  385. return FALSE;
  386. *value = value16;
  387. break;
  388. case 32:
  389. if (!dbg_read_memory(addr, value, sizeof(*value)))
  390. return FALSE;
  391. break;
  392. default: return FALSE;
  393. }
  394. return TRUE;
  395. }
  396. static BOOL be_i386_is_func_call(const void* insn, ADDRESS64* callee)
  397. {
  398. BYTE ch;
  399. int delta;
  400. short segment;
  401. unsigned dst = 0;
  402. unsigned operand_size;
  403. ADDRESS_MODE cs_addr_mode;
  404. cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
  405. dbg_context.x86.SegCs);
  406. operand_size = get_size(cs_addr_mode);
  407. /* get operand_size (also getting rid of the various prefixes */
  408. do
  409. {
  410. if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
  411. if (ch == 0x66)
  412. {
  413. operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */
  414. insn = (const char*)insn + 1;
  415. }
  416. } while (ch == 0x66 || ch == 0x67);
  417. switch (ch)
  418. {
  419. case 0xe8: /* relative near call */
  420. callee->Mode = cs_addr_mode;
  421. if (!fetch_value((const char*)insn + 1, operand_size, &delta))
  422. return FALSE;
  423. callee->Segment = dbg_context.x86.SegCs;
  424. callee->Offset = (DWORD_PTR)insn + 1 + (operand_size / 8) + delta;
  425. return TRUE;
  426. case 0x9a: /* absolute far call */
  427. if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8,
  428. &segment, sizeof(segment)))
  429. return FALSE;
  430. callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
  431. segment);
  432. if (!fetch_value((const char*)insn + 1, operand_size, &delta))
  433. return FALSE;
  434. callee->Segment = segment;
  435. callee->Offset = delta;
  436. return TRUE;
  437. case 0xff:
  438. if (!dbg_read_memory((const char*)insn + 1, &ch, sizeof(ch)))
  439. return FALSE;
  440. /* keep only the CALL and LCALL insn:s */
  441. switch ((ch >> 3) & 0x07)
  442. {
  443. case 0x02:
  444. segment = dbg_context.x86.SegCs;
  445. break;
  446. case 0x03:
  447. if (!dbg_read_memory((const char*)insn + 1 + operand_size / 8,
  448. &segment, sizeof(segment)))
  449. return FALSE;
  450. break;
  451. default: return FALSE;
  452. }
  453. /* FIXME: we only support the 32 bit far calls for now */
  454. if (operand_size != 32)
  455. {
  456. WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) with 16 bit operand-size at %p\n", ch, insn);
  457. return FALSE;
  458. }
  459. switch (ch & 0xC7) /* keep Mod R/M only (skip reg) */
  460. {
  461. case 0x04:
  462. case 0x44:
  463. case 0x84:
  464. WINE_FIXME("Unsupported yet call insn (0xFF 0x%02x) (SIB bytes) at %p\n", ch, insn);
  465. return FALSE;
  466. case 0x05: /* addr32 */
  467. if ((ch & 0x38) == 0x10 || /* call */
  468. (ch & 0x38) == 0x18) /* lcall */
  469. {
  470. void *addr;
  471. if (!dbg_read_memory((const char *)insn + 2, &addr, sizeof(addr)))
  472. return FALSE;
  473. if ((ch & 0x38) == 0x18) /* lcall */
  474. {
  475. if (!dbg_read_memory((const char*)addr + operand_size, &segment, sizeof(segment)))
  476. return FALSE;
  477. }
  478. else segment = dbg_context.x86.SegCs;
  479. if (!dbg_read_memory((const char*)addr, &dst, sizeof(dst)))
  480. return FALSE;
  481. callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86, segment);
  482. callee->Segment = segment;
  483. callee->Offset = dst;
  484. return TRUE;
  485. }
  486. return FALSE;
  487. default:
  488. switch (ch & 0x07)
  489. {
  490. case 0x00: dst = dbg_context.x86.Eax; break;
  491. case 0x01: dst = dbg_context.x86.Ecx; break;
  492. case 0x02: dst = dbg_context.x86.Edx; break;
  493. case 0x03: dst = dbg_context.x86.Ebx; break;
  494. case 0x04: dst = dbg_context.x86.Esp; break;
  495. case 0x05: dst = dbg_context.x86.Ebp; break;
  496. case 0x06: dst = dbg_context.x86.Esi; break;
  497. case 0x07: dst = dbg_context.x86.Edi; break;
  498. }
  499. if ((ch >> 6) != 0x03) /* indirect address */
  500. {
  501. if (ch >> 6) /* we got a displacement */
  502. {
  503. if (!fetch_value((const char*)insn + 2, (ch >> 6) == 0x01 ? 8 : 32, &delta))
  504. return FALSE;
  505. dst += delta;
  506. }
  507. if (((ch >> 3) & 0x07) == 0x03) /* LCALL */
  508. {
  509. if (!dbg_read_memory((const char*)(UINT_PTR)dst + operand_size, &segment, sizeof(segment)))
  510. return FALSE;
  511. }
  512. else segment = dbg_context.x86.SegCs;
  513. if (!dbg_read_memory((const char*)(UINT_PTR)dst, &delta, sizeof(delta)))
  514. return FALSE;
  515. callee->Mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
  516. segment);
  517. callee->Segment = segment;
  518. callee->Offset = delta;
  519. }
  520. else
  521. {
  522. callee->Mode = cs_addr_mode;
  523. callee->Segment = dbg_context.x86.SegCs;
  524. callee->Offset = dst;
  525. }
  526. }
  527. return TRUE;
  528. default:
  529. return FALSE;
  530. }
  531. }
  532. static BOOL be_i386_is_jump(const void* insn, ADDRESS64* jumpee)
  533. {
  534. BYTE ch;
  535. int delta;
  536. unsigned operand_size;
  537. ADDRESS_MODE cs_addr_mode;
  538. cs_addr_mode = get_selector_type(dbg_curr_thread->handle, &dbg_context.x86,
  539. dbg_context.x86.SegCs);
  540. operand_size = get_size(cs_addr_mode);
  541. /* get operand_size (also getting rid of the various prefixes */
  542. do
  543. {
  544. if (!dbg_read_memory(insn, &ch, sizeof(ch))) return FALSE;
  545. if (ch == 0x66)
  546. {
  547. operand_size = 48 - operand_size; /* 16 => 32, 32 => 16 */
  548. insn = (const char*)insn + 1;
  549. }
  550. } while (ch == 0x66 || ch == 0x67);
  551. switch (ch)
  552. {
  553. case 0xe9: /* jmp near */
  554. jumpee->Mode = cs_addr_mode;
  555. if (!fetch_value((const char*)insn + 1, operand_size, &delta))
  556. return FALSE;
  557. jumpee->Segment = dbg_context.x86.SegCs;
  558. jumpee->Offset = (DWORD_PTR)insn + 1 + (operand_size / 8) + delta;
  559. return TRUE;
  560. default: WINE_FIXME("unknown %x\n", ch); return FALSE;
  561. }
  562. return FALSE;
  563. }
  564. #define DR7_CONTROL_SHIFT 16
  565. #define DR7_CONTROL_SIZE 4
  566. #define DR7_RW_EXECUTE (0x0)
  567. #define DR7_RW_WRITE (0x1)
  568. #define DR7_RW_READ (0x3)
  569. #define DR7_LEN_1 (0x0)
  570. #define DR7_LEN_2 (0x4)
  571. #define DR7_LEN_4 (0xC)
  572. #define DR7_LOCAL_ENABLE_SHIFT 0
  573. #define DR7_GLOBAL_ENABLE_SHIFT 1
  574. #define DR7_ENABLE_SIZE 2
  575. #define DR7_LOCAL_ENABLE_MASK (0x55)
  576. #define DR7_GLOBAL_ENABLE_MASK (0xAA)
  577. #define DR7_CONTROL_RESERVED (0xFC00)
  578. #define DR7_LOCAL_SLOWDOWN (0x100)
  579. #define DR7_GLOBAL_SLOWDOWN (0x200)
  580. #define DR7_ENABLE_MASK(dr) (1<<(DR7_LOCAL_ENABLE_SHIFT+DR7_ENABLE_SIZE*(dr)))
  581. #define IS_DR7_SET(ctrl,dr) ((ctrl)&DR7_ENABLE_MASK(dr))
  582. static inline int be_i386_get_unused_DR(dbg_ctx_t *pctx, DWORD** r)
  583. {
  584. WOW64_CONTEXT *ctx = &pctx->x86;
  585. if (!IS_DR7_SET(ctx->Dr7, 0))
  586. {
  587. *r = &ctx->Dr0;
  588. return 0;
  589. }
  590. if (!IS_DR7_SET(ctx->Dr7, 1))
  591. {
  592. *r = &ctx->Dr1;
  593. return 1;
  594. }
  595. if (!IS_DR7_SET(ctx->Dr7, 2))
  596. {
  597. *r = &ctx->Dr2;
  598. return 2;
  599. }
  600. if (!IS_DR7_SET(ctx->Dr7, 3))
  601. {
  602. *r = &ctx->Dr3;
  603. return 3;
  604. }
  605. dbg_printf("All hardware registers have been used\n");
  606. return -1;
  607. }
  608. static BOOL be_i386_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
  609. dbg_ctx_t *ctx, enum be_xpoint_type type,
  610. void* addr, unsigned *val, unsigned size)
  611. {
  612. unsigned char ch;
  613. SIZE_T sz;
  614. DWORD *pr;
  615. int reg;
  616. unsigned int bits;
  617. switch (type)
  618. {
  619. case be_xpoint_break:
  620. if (size != 0) return FALSE;
  621. if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
  622. *val = ch;
  623. ch = 0xcc;
  624. if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
  625. break;
  626. case be_xpoint_watch_exec:
  627. bits = DR7_RW_EXECUTE;
  628. goto hw_bp;
  629. case be_xpoint_watch_read:
  630. bits = DR7_RW_READ;
  631. goto hw_bp;
  632. case be_xpoint_watch_write:
  633. bits = DR7_RW_WRITE;
  634. hw_bp:
  635. if ((reg = be_i386_get_unused_DR(ctx, &pr)) == -1) return FALSE;
  636. *pr = (DWORD_PTR)addr;
  637. if (type != be_xpoint_watch_exec) switch (size)
  638. {
  639. case 4: bits |= DR7_LEN_4; break;
  640. case 2: bits |= DR7_LEN_2; break;
  641. case 1: bits |= DR7_LEN_1; break;
  642. default: return FALSE;
  643. }
  644. *val = reg;
  645. /* clear old values */
  646. ctx->x86.Dr7 &= ~(0x0F << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg));
  647. /* set the correct ones */
  648. ctx->x86.Dr7 |= bits << (DR7_CONTROL_SHIFT + DR7_CONTROL_SIZE * reg);
  649. ctx->x86.Dr7 |= DR7_ENABLE_MASK(reg) | DR7_LOCAL_SLOWDOWN;
  650. break;
  651. default:
  652. dbg_printf("Unknown bp type %c\n", type);
  653. return FALSE;
  654. }
  655. return TRUE;
  656. }
  657. static BOOL be_i386_remove_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
  658. dbg_ctx_t *ctx, enum be_xpoint_type type,
  659. void* addr, unsigned val, unsigned size)
  660. {
  661. SIZE_T sz;
  662. unsigned char ch;
  663. switch (type)
  664. {
  665. case be_xpoint_break:
  666. if (size != 0) return FALSE;
  667. if (!pio->read(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
  668. if (ch != (unsigned char)0xCC)
  669. WINE_FIXME("Cannot get back %02x instead of 0xCC at %p\n", ch, addr);
  670. ch = (unsigned char)val;
  671. if (!pio->write(hProcess, addr, &ch, 1, &sz) || sz != 1) return FALSE;
  672. break;
  673. case be_xpoint_watch_exec:
  674. case be_xpoint_watch_read:
  675. case be_xpoint_watch_write:
  676. /* simply disable the entry */
  677. ctx->x86.Dr7 &= ~DR7_ENABLE_MASK(val);
  678. break;
  679. default:
  680. dbg_printf("Unknown bp type %c\n", type);
  681. return FALSE;
  682. }
  683. return TRUE;
  684. }
  685. static BOOL be_i386_is_watchpoint_set(const dbg_ctx_t *ctx, unsigned idx)
  686. {
  687. return ctx->x86.Dr6 & (1 << idx);
  688. }
  689. static void be_i386_clear_watchpoint(dbg_ctx_t *ctx, unsigned idx)
  690. {
  691. ctx->x86.Dr6 &= ~(1 << idx);
  692. }
  693. static int be_i386_adjust_pc_for_break(dbg_ctx_t *ctx, BOOL way)
  694. {
  695. if (way)
  696. {
  697. ctx->x86.Eip--;
  698. return -1;
  699. }
  700. ctx->x86.Eip++;
  701. return 1;
  702. }
  703. static BOOL be_i386_get_context(HANDLE thread, dbg_ctx_t *ctx)
  704. {
  705. ctx->x86.ContextFlags = WOW64_CONTEXT_ALL;
  706. return Wow64GetThreadContext(thread, &ctx->x86);
  707. }
  708. static BOOL be_i386_set_context(HANDLE thread, const dbg_ctx_t *ctx)
  709. {
  710. return Wow64SetThreadContext(thread, &ctx->x86);
  711. }
  712. #define REG(f,n,t,r) {f, n, t, FIELD_OFFSET(WOW64_CONTEXT, r), sizeof(((WOW64_CONTEXT*)NULL)->r)}
  713. static struct gdb_register be_i386_gdb_register_map[] = {
  714. REG("core", "eax", NULL, Eax),
  715. REG(NULL, "ecx", NULL, Ecx),
  716. REG(NULL, "edx", NULL, Edx),
  717. REG(NULL, "ebx", NULL, Ebx),
  718. REG(NULL, "esp", "data_ptr", Esp),
  719. REG(NULL, "ebp", "data_ptr", Ebp),
  720. REG(NULL, "esi", NULL, Esi),
  721. REG(NULL, "edi", NULL, Edi),
  722. REG(NULL, "eip", "code_ptr", Eip),
  723. REG(NULL, "eflags", "i386_eflags", EFlags),
  724. REG(NULL, "cs", NULL, SegCs),
  725. REG(NULL, "ss", NULL, SegSs),
  726. REG(NULL, "ds", NULL, SegDs),
  727. REG(NULL, "es", NULL, SegEs),
  728. REG(NULL, "fs", NULL, SegFs),
  729. REG(NULL, "gs", NULL, SegGs),
  730. { NULL, "st0", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[ 0]), 10},
  731. { NULL, "st1", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[10]), 10},
  732. { NULL, "st2", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[20]), 10},
  733. { NULL, "st3", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[30]), 10},
  734. { NULL, "st4", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[40]), 10},
  735. { NULL, "st5", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[50]), 10},
  736. { NULL, "st6", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[60]), 10},
  737. { NULL, "st7", "i387_ext", FIELD_OFFSET(WOW64_CONTEXT, FloatSave.RegisterArea[70]), 10},
  738. { NULL, "fctrl", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ControlWord), 2},
  739. { NULL, "fstat", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.StatusWord), 2},
  740. { NULL, "ftag", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.TagWord), 2},
  741. { NULL, "fiseg", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ErrorSelector), 2},
  742. REG(NULL, "fioff", NULL, FloatSave.ErrorOffset),
  743. { NULL, "foseg", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.DataSelector), 2},
  744. REG(NULL, "fooff", NULL, FloatSave.DataOffset),
  745. { NULL, "fop", NULL, FIELD_OFFSET(WOW64_CONTEXT, FloatSave.ErrorSelector)+2, 2},
  746. { "sse", "xmm0", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[0]), 16},
  747. { NULL, "xmm1", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[1]), 16},
  748. { NULL, "xmm2", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[2]), 16},
  749. { NULL, "xmm3", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[3]), 16},
  750. { NULL, "xmm4", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[4]), 16},
  751. { NULL, "xmm5", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[5]), 16},
  752. { NULL, "xmm6", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[6]), 16},
  753. { NULL, "xmm7", "vec128", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, XmmRegisters[7]), 16},
  754. { NULL, "mxcsr", "i386_mxcsr", FIELD_OFFSET(WOW64_CONTEXT, ExtendedRegisters) + FIELD_OFFSET(XSAVE_FORMAT, MxCsr), 4},
  755. };
  756. struct backend_cpu be_i386 =
  757. {
  758. IMAGE_FILE_MACHINE_I386,
  759. 4,
  760. be_i386_linearize,
  761. be_i386_build_addr,
  762. be_i386_get_addr,
  763. be_i386_get_register_info,
  764. be_i386_single_step,
  765. be_i386_print_context,
  766. be_i386_print_segment_info,
  767. be_i386_ctx,
  768. be_i386_is_step_over_insn,
  769. be_i386_is_function_return,
  770. be_i386_is_break_insn,
  771. be_i386_is_func_call,
  772. be_i386_is_jump,
  773. be_i386_disasm_one_insn,
  774. be_i386_insert_Xpoint,
  775. be_i386_remove_Xpoint,
  776. be_i386_is_watchpoint_set,
  777. be_i386_clear_watchpoint,
  778. be_i386_adjust_pc_for_break,
  779. be_i386_get_context,
  780. be_i386_set_context,
  781. be_i386_gdb_register_map,
  782. ARRAY_SIZE(be_i386_gdb_register_map),
  783. };
  784. #endif