cpu_x86_64.c 35.8 KB
Newer Older
1 2 3
/*
 * File cpu_x86_64.c
 *
4
 * Copyright (C) 1999, 2005 Alexandre Julliard
5
 * Copyright (C) 2009, 2011 Eric Pouech.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <assert.h>

24 25
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
26 27 28 29 30 31 32 33
#include "ntstatus.h"
#define WIN32_NO_STATUS
#include "dbghelp_private.h"
#include "winternl.h"
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dbghelp);

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/* x86-64 unwind information, for PE modules, as described on MSDN */

typedef enum _UNWIND_OP_CODES
{
    UWOP_PUSH_NONVOL = 0,
    UWOP_ALLOC_LARGE,
    UWOP_ALLOC_SMALL,
    UWOP_SET_FPREG,
    UWOP_SAVE_NONVOL,
    UWOP_SAVE_NONVOL_FAR,
    UWOP_SAVE_XMM128,
    UWOP_SAVE_XMM128_FAR,
    UWOP_PUSH_MACHFRAME
} UNWIND_CODE_OPS;

typedef union _UNWIND_CODE
{
    struct
    {
        BYTE CodeOffset;
        BYTE UnwindOp : 4;
        BYTE OpInfo   : 4;
56
    } u;
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
    USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;

typedef struct _UNWIND_INFO
{
    BYTE Version       : 3;
    BYTE Flags         : 5;
    BYTE SizeOfProlog;
    BYTE CountOfCodes;
    BYTE FrameRegister : 4;
    BYTE FrameOffset   : 4;
    UNWIND_CODE UnwindCode[1]; /* actually CountOfCodes (aligned) */
/*
 *  union
 *  {
 *      OPTIONAL ULONG ExceptionHandler;
 *      OPTIONAL ULONG FunctionEntry;
 *  };
 *  OPTIONAL ULONG ExceptionData[];
 */
} UNWIND_INFO, *PUNWIND_INFO;

79 80
static BOOL x86_64_get_addr(HANDLE hThread, const CONTEXT* ctx,
                            enum cpu_addr ca, ADDRESS64* addr)
81 82 83 84 85 86 87 88 89 90 91 92 93 94
{
    addr->Mode = AddrModeFlat;
    switch (ca)
    {
#ifdef __x86_64__
    case cpu_addr_pc:    addr->Segment = ctx->SegCs; addr->Offset = ctx->Rip; return TRUE;
    case cpu_addr_stack: addr->Segment = ctx->SegSs; addr->Offset = ctx->Rsp; return TRUE;
    case cpu_addr_frame: addr->Segment = ctx->SegSs; addr->Offset = ctx->Rbp; return TRUE;
#endif
    default: addr->Mode = -1;
        return FALSE;
    }
}

95 96
#ifdef __x86_64__

97 98 99 100
enum st_mode {stm_start, stm_64bit, stm_done};

/* indexes in Reserved array */
#define __CurrentMode     0
101 102
#define __CurrentCount    1
/* #define __     2 (unused) */
103 104

#define curr_mode   (frame->Reserved[__CurrentMode])
105 106
#define curr_count  (frame->Reserved[__CurrentCount])
/* #define ??? (frame->Reserved[__]) (unused) */
107

108 109 110 111 112 113
union handler_data
{
    RUNTIME_FUNCTION chain;
    ULONG handler;
};

114
static void dump_unwind_info(struct cpu_stack_walk* csw, ULONG64 base, RUNTIME_FUNCTION *function)
115 116 117 118 119
{
    static const char * const reg_names[16] =
        { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
          "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15" };

120
    union handler_data handler_data;
121 122 123
    char buffer[sizeof(UNWIND_INFO) + 256 * sizeof(UNWIND_CODE)];
    UNWIND_INFO* info = (UNWIND_INFO*)buffer;
    unsigned int i, count;
124 125
    RUNTIME_FUNCTION snext;
    ULONG64 addr;
126 127 128 129 130 131

    TRACE("**** func %x-%x\n", function->BeginAddress, function->EndAddress);
    for (;;)
    {
        if (function->UnwindData & 1)
        {
132 133
            if (!sw_read_mem(csw, base + function->UnwindData, &snext, sizeof(snext)))
            {
134
                TRACE("Couldn't unwind RUNTIME_INFO at %lx\n", base + function->UnwindData);
135 136
                return;
            }
137 138
            TRACE("unwind info for function %p-%p chained to function %p-%p\n",
                  (char*)base + function->BeginAddress, (char*)base + function->EndAddress,
139 140
                  (char*)base + snext.BeginAddress, (char*)base + snext.EndAddress);
            function = &snext;
141 142
            continue;
        }
143 144 145 146 147
        addr = base + function->UnwindData;
        if (!sw_read_mem(csw, addr, info, FIELD_OFFSET(UNWIND_INFO, UnwindCode)) ||
            !sw_read_mem(csw, addr + FIELD_OFFSET(UNWIND_INFO, UnwindCode),
                         info->UnwindCode, info->CountOfCodes * sizeof(UNWIND_CODE)))
        {
148
            FIXME("couldn't read memory for UNWIND_INFO at %lx\n", addr);
149 150
            return;
        }
151
        TRACE("unwind info at %p flags %x prolog 0x%x bytes function %p-%p\n",
152
              (char*)addr, info->Flags, info->SizeOfProlog,
153 154 155 156 157 158 159 160
              (char*)base + function->BeginAddress, (char*)base + function->EndAddress);

        if (info->FrameRegister)
            TRACE("    frame register %s offset 0x%x(%%rsp)\n",
                  reg_names[info->FrameRegister], info->FrameOffset * 16);

        for (i = 0; i < info->CountOfCodes; i++)
        {
161 162
            TRACE("    0x%x: ", info->UnwindCode[i].u.CodeOffset);
            switch (info->UnwindCode[i].u.UnwindOp)
163 164
            {
            case UWOP_PUSH_NONVOL:
165
                TRACE("pushq %%%s\n", reg_names[info->UnwindCode[i].u.OpInfo]);
166 167
                break;
            case UWOP_ALLOC_LARGE:
168
                if (info->UnwindCode[i].u.OpInfo)
169 170 171 172 173 174 175 176 177 178 179 180
                {
                    count = *(DWORD*)&info->UnwindCode[i+1];
                    i += 2;
                }
                else
                {
                    count = *(USHORT*)&info->UnwindCode[i+1] * 8;
                    i++;
                }
                TRACE("subq $0x%x,%%rsp\n", count);
                break;
            case UWOP_ALLOC_SMALL:
181
                count = (info->UnwindCode[i].u.OpInfo + 1) * 8;
182 183 184 185 186 187 188 189
                TRACE("subq $0x%x,%%rsp\n", count);
                break;
            case UWOP_SET_FPREG:
                TRACE("leaq 0x%x(%%rsp),%s\n",
                      info->FrameOffset * 16, reg_names[info->FrameRegister]);
                break;
            case UWOP_SAVE_NONVOL:
                count = *(USHORT*)&info->UnwindCode[i+1] * 8;
190
                TRACE("movq %%%s,0x%x(%%rsp)\n", reg_names[info->UnwindCode[i].u.OpInfo], count);
191 192 193 194
                i++;
                break;
            case UWOP_SAVE_NONVOL_FAR:
                count = *(DWORD*)&info->UnwindCode[i+1];
195
                TRACE("movq %%%s,0x%x(%%rsp)\n", reg_names[info->UnwindCode[i].u.OpInfo], count);
196 197 198 199
                i += 2;
                break;
            case UWOP_SAVE_XMM128:
                count = *(USHORT*)&info->UnwindCode[i+1] * 16;
200
                TRACE("movaps %%xmm%u,0x%x(%%rsp)\n", info->UnwindCode[i].u.OpInfo, count);
201 202 203 204
                i++;
                break;
            case UWOP_SAVE_XMM128_FAR:
                count = *(DWORD*)&info->UnwindCode[i+1];
205
                TRACE("movaps %%xmm%u,0x%x(%%rsp)\n", info->UnwindCode[i].u.OpInfo, count);
206 207 208
                i += 2;
                break;
            case UWOP_PUSH_MACHFRAME:
209
                TRACE("PUSH_MACHFRAME %u\n", info->UnwindCode[i].u.OpInfo);
210 211
                break;
            default:
212
                FIXME("unknown code %u\n", info->UnwindCode[i].u.UnwindOp);
213 214 215 216
                break;
            }
        }

217 218
        addr += FIELD_OFFSET(UNWIND_INFO, UnwindCode) +
            ((info->CountOfCodes + 1) & ~1) * sizeof(UNWIND_CODE);
219 220
        if (info->Flags & UNW_FLAG_CHAININFO)
        {
221 222 223 224 225
            if (!sw_read_mem(csw, addr, &handler_data, sizeof(handler_data.chain)))
            {
                FIXME("couldn't read memory for handler_data.chain\n");
                return;
            }
226
            TRACE("    chained to function %p-%p\n",
227 228 229
                  (char*)base + handler_data.chain.BeginAddress,
                  (char*)base + handler_data.chain.EndAddress);
            function = &handler_data.chain;
230 231 232
            continue;
        }
        if (info->Flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
233 234 235 236 237 238
        {
            if (!sw_read_mem(csw, addr, &handler_data, sizeof(handler_data.handler)))
            {
                FIXME("couldn't read memory for handler_data.handler\n");
                return;
            }
239
            TRACE("    handler %p data at %p\n",
240 241
                  (char*)base + handler_data.handler, (char*)addr + sizeof(handler_data.handler));
        }
242 243 244 245
        break;
    }
}

246
/* highly derived from dlls/ntdll/signal_x86_64.c */
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
static ULONG64 get_int_reg(CONTEXT *context, int reg)
{
    return *(&context->Rax + reg);
}

static void set_int_reg(CONTEXT *context, int reg, ULONG64 val)
{
    *(&context->Rax + reg) = val;
}

static void set_float_reg(CONTEXT *context, int reg, M128A val)
{
    *(&context->u.s.Xmm0 + reg) = val;
}

static int get_opcode_size(UNWIND_CODE op)
263
{
264
    switch (op.u.UnwindOp)
265 266
    {
    case UWOP_ALLOC_LARGE:
267
        return 2 + (op.u.OpInfo != 0);
268 269 270 271 272 273 274 275 276 277 278
    case UWOP_SAVE_NONVOL:
    case UWOP_SAVE_XMM128:
        return 2;
    case UWOP_SAVE_NONVOL_FAR:
    case UWOP_SAVE_XMM128_FAR:
        return 3;
    default:
        return 1;
    }
}

279 280
static BOOL is_inside_epilog(struct cpu_stack_walk* csw, DWORD64 pc,
                             DWORD64 base, const RUNTIME_FUNCTION *function )
281
{
282 283
    BYTE op0, op1, op2;
    LONG val32;
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301

    if (!sw_read_mem(csw, pc, &op0, 1)) return FALSE;

    /* add or lea must be the first instruction, and it must have a rex.W prefix */
    if ((op0 & 0xf8) == 0x48)
    {
        if (!sw_read_mem(csw, pc + 1, &op1, 1)) return FALSE;
        switch (op1)
        {
        case 0x81: /* add $nnnn,%rsp */
            if (!sw_read_mem(csw, pc + 2, &op2, 1)) return FALSE;
            if (op0 == 0x48 && op2 == 0xc4)
            {
                pc += 7;
                break;
            }
            return FALSE;
        case 0x83: /* add $n,%rsp */
302
            if (!sw_read_mem(csw, pc + 2, &op2, 1)) return FALSE;
303 304 305 306 307 308 309
            if (op0 == 0x48 && op2 == 0xc4)
            {
                pc += 4;
                break;
            }
            return FALSE;
        case 0x8d: /* lea n(reg),%rsp */
310
            if (!sw_read_mem(csw, pc + 2, &op2, 1)) return FALSE;
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
            if (op0 & 0x06) return FALSE;  /* rex.RX must be cleared */
            if (((op2 >> 3) & 7) != 4) return FALSE;  /* dest reg mus be %rsp */
            if ((op2 & 7) == 4) return FALSE;  /* no SIB byte allowed */
            if ((op2 >> 6) == 1)  /* 8-bit offset */
            {
                pc += 4;
                break;
            }
            if ((op2 >> 6) == 2)  /* 32-bit offset */
            {
                pc += 7;
                break;
            }
            return FALSE;
        }
    }

    /* now check for various pop instructions */
    for (;;)
    {
        if (!sw_read_mem(csw, pc, &op0, 1)) return FALSE;
332
        if ((op0 & 0xf0) == 0x40)  /* rex prefix */
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351
        {
            if (!sw_read_mem(csw, ++pc, &op0, 1)) return FALSE;
        }

        switch (op0)
        {
        case 0x58: /* pop %rax/%r8 */
        case 0x59: /* pop %rcx/%r9 */
        case 0x5a: /* pop %rdx/%r10 */
        case 0x5b: /* pop %rbx/%r11 */
        case 0x5c: /* pop %rsp/%r12 */
        case 0x5d: /* pop %rbp/%r13 */
        case 0x5e: /* pop %rsi/%r14 */
        case 0x5f: /* pop %rdi/%r15 */
            pc++;
            continue;
        case 0xc2: /* ret $nn */
        case 0xc3: /* ret */
            return TRUE;
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        case 0xe9: /* jmp nnnn */
            if (!sw_read_mem(csw, pc + 1, &val32, sizeof(LONG))) return FALSE;
            pc += 5 + val32;
            if (pc - base >= function->BeginAddress && pc - base < function->EndAddress)
                continue;
            break;
        case 0xeb: /* jmp n */
            if (!sw_read_mem(csw, pc + 1, &op1, 1)) return FALSE;
            pc += 2 + (signed char)op1;
            if (pc - base >= function->BeginAddress && pc - base < function->EndAddress)
                continue;
            break;
        case 0xf3: /* rep; ret (for amd64 prediction bug) */
            if (!sw_read_mem(csw, pc + 1, &op1, 1)) return FALSE;
            return op1 == 0xc3;
367 368 369 370 371
        }
        return FALSE;
    }
}

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
static BOOL interpret_epilog(struct cpu_stack_walk* csw, ULONG64 pc, CONTEXT *context )
{
    BYTE        insn, val8;
    WORD        val16;
    LONG        val32;
    DWORD64     val64;

    for (;;)
    {
        BYTE rex = 0;

        if (!sw_read_mem(csw, pc, &insn, 1)) return FALSE;
        if ((insn & 0xf0) == 0x40)
        {
            rex = insn & 0x0f;  /* rex prefix */
            if (!sw_read_mem(csw, ++pc, &insn, 1)) return FALSE;
        }

        switch (insn)
        {
        case 0x58: /* pop %rax/r8 */
        case 0x59: /* pop %rcx/r9 */
        case 0x5a: /* pop %rdx/r10 */
        case 0x5b: /* pop %rbx/r11 */
        case 0x5c: /* pop %rsp/r12 */
        case 0x5d: /* pop %rbp/r13 */
        case 0x5e: /* pop %rsi/r14 */
        case 0x5f: /* pop %rdi/r15 */
            if (!sw_read_mem(csw, context->Rsp, &val64, sizeof(DWORD64))) return FALSE;
            set_int_reg(context, insn - 0x58 + (rex & 1) * 8, val64);
            context->Rsp += sizeof(ULONG64);
            pc++;
            continue;
        case 0x81: /* add $nnnn,%rsp */
406 407
            if (!sw_read_mem(csw, pc + 2, &val32, sizeof(LONG))) return FALSE;
            context->Rsp += val32;
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
            pc += 2 + sizeof(LONG);
            continue;
        case 0x83: /* add $n,%rsp */
            if (!sw_read_mem(csw, pc + 2, &val8, sizeof(BYTE))) return FALSE;
            context->Rsp += (signed char)val8;
            pc += 3;
            continue;
        case 0x8d:
            if (!sw_read_mem(csw, pc + 1, &insn, sizeof(BYTE))) return FALSE;
            if ((insn >> 6) == 1)  /* lea n(reg),%rsp */
            {
                if (!sw_read_mem(csw, pc + 2, &val8, sizeof(BYTE))) return FALSE;
                context->Rsp = get_int_reg( context, (insn & 7) + (rex & 1) * 8 ) + (signed char)val8;
                pc += 3;
            }
            else  /* lea nnnn(reg),%rsp */
            {
425 426
                if (!sw_read_mem(csw, pc + 2, &val32, sizeof(LONG))) return FALSE;
                context->Rsp = get_int_reg( context, (insn & 7) + (rex & 1) * 8 ) + val32;
427 428 429 430 431 432 433 434 435 436
                pc += 2 + sizeof(LONG);
            }
            continue;
        case 0xc2: /* ret $nn */
            if (!sw_read_mem(csw, context->Rsp, &val64, sizeof(DWORD64))) return FALSE;
            if (!sw_read_mem(csw, pc + 1, &val16, sizeof(WORD))) return FALSE;
            context->Rip = val64;
            context->Rsp += sizeof(ULONG64) + val16;
            return TRUE;
        case 0xc3: /* ret */
437
        case 0xf3: /* rep; ret */
438 439 440 441
            if (!sw_read_mem(csw, context->Rsp, &val64, sizeof(DWORD64))) return FALSE;
            context->Rip = val64;
            context->Rsp += sizeof(ULONG64);
            return TRUE;
442 443 444 445 446 447 448 449
        case 0xe9: /* jmp nnnn */
            if (!sw_read_mem(csw, pc + 1, &val32, sizeof(LONG))) return FALSE;
            pc += 5 + val32;
            continue;
        case 0xeb: /* jmp n */
            if (!sw_read_mem(csw, pc + 1, &val8, sizeof(BYTE))) return FALSE;
            pc += 2 + (signed char)val8;
            continue;
450 451 452 453 454 455
        }
        FIXME("unsupported insn %x\n", insn);
        return FALSE;
    }
}

456
static BOOL default_unwind(struct cpu_stack_walk* csw, CONTEXT* context)
457
{
458
    if (!sw_read_mem(csw, context->Rsp, &context->Rip, sizeof(DWORD64)))
459
    {
460
        WARN("Cannot read new frame offset %s\n", wine_dbgstr_longlong(context->Rsp));
461 462 463 464 465 466
        return FALSE;
    }
    context->Rsp += sizeof(DWORD64);
    return TRUE;
}

467
static BOOL interpret_function_table_entry(struct cpu_stack_walk* csw,
468 469 470 471 472 473 474 475 476 477 478
                                           CONTEXT* context, RUNTIME_FUNCTION* function, DWORD64 base)
{
    char                buffer[sizeof(UNWIND_INFO) + 256 * sizeof(UNWIND_CODE)];
    UNWIND_INFO*        info = (UNWIND_INFO*)buffer;
    unsigned            i;
    DWORD64             newframe, prolog_offset, off, value;
    M128A               floatvalue;
    union handler_data  handler_data;

    /* FIXME: we have some assumptions here */
    assert(context);
479
    dump_unwind_info(csw, sw_module_base(csw, context->Rip), function);
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
    newframe = context->Rsp;
    for (;;)
    {
        if (!sw_read_mem(csw, base + function->UnwindData, info, sizeof(*info)) ||
            !sw_read_mem(csw, base + function->UnwindData + FIELD_OFFSET(UNWIND_INFO, UnwindCode),
                         info->UnwindCode, info->CountOfCodes * sizeof(UNWIND_CODE)))
        {
            WARN("Couldn't read unwind_code at %lx\n", base + function->UnwindData);
            return FALSE;
        }

        if (info->Version != 1)
        {
            WARN("unknown unwind info version %u at %lx\n", info->Version, base + function->UnwindData);
            return FALSE;
        }

        if (info->FrameRegister)
            newframe = get_int_reg(context, info->FrameRegister) - info->FrameOffset * 16;

        /* check if in prolog */
501 502
        if (context->Rip >= base + function->BeginAddress &&
            context->Rip < base + function->BeginAddress + info->SizeOfProlog)
503
        {
504
            prolog_offset = context->Rip - base - function->BeginAddress;
505 506 507 508
        }
        else
        {
            prolog_offset = ~0;
509
            if (is_inside_epilog(csw, context->Rip, base, function))
510
            {
511
                interpret_epilog(csw, context->Rip, context);
512 513 514 515 516 517
                return TRUE;
            }
        }

        for (i = 0; i < info->CountOfCodes; i += get_opcode_size(info->UnwindCode[i]))
        {
518
            if (prolog_offset < info->UnwindCode[i].u.CodeOffset) continue; /* skip it */
519

520
            switch (info->UnwindCode[i].u.UnwindOp)
521 522 523
            {
            case UWOP_PUSH_NONVOL:  /* pushq %reg */
                if (!sw_read_mem(csw, context->Rsp, &value, sizeof(DWORD64))) return FALSE;
524
                set_int_reg(context, info->UnwindCode[i].u.OpInfo, value);
525 526 527
                context->Rsp += sizeof(ULONG64);
                break;
            case UWOP_ALLOC_LARGE:  /* subq $nn,%rsp */
528
                if (info->UnwindCode[i].u.OpInfo) context->Rsp += *(DWORD*)&info->UnwindCode[i+1];
529 530 531
                else context->Rsp += *(USHORT*)&info->UnwindCode[i+1] * 8;
                break;
            case UWOP_ALLOC_SMALL:  /* subq $n,%rsp */
532
                context->Rsp += (info->UnwindCode[i].u.OpInfo + 1) * 8;
533 534 535 536 537 538
                break;
            case UWOP_SET_FPREG:  /* leaq nn(%rsp),%framereg */
                context->Rsp = newframe;
                break;
            case UWOP_SAVE_NONVOL:  /* movq %reg,n(%rsp) */
                off = newframe + *(USHORT*)&info->UnwindCode[i+1] * 8;
539
                if (!sw_read_mem(csw, off, &value, sizeof(DWORD64))) return FALSE;
540
                set_int_reg(context, info->UnwindCode[i].u.OpInfo, value);
541 542 543
                break;
            case UWOP_SAVE_NONVOL_FAR:  /* movq %reg,nn(%rsp) */
                off = newframe + *(DWORD*)&info->UnwindCode[i+1];
544
                if (!sw_read_mem(csw, off, &value, sizeof(DWORD64))) return FALSE;
545
                set_int_reg(context, info->UnwindCode[i].u.OpInfo, value);
546 547 548
                break;
            case UWOP_SAVE_XMM128:  /* movaps %xmmreg,n(%rsp) */
                off = newframe + *(USHORT*)&info->UnwindCode[i+1] * 16;
549
                if (!sw_read_mem(csw, off, &floatvalue, sizeof(M128A))) return FALSE;
550
                set_float_reg(context, info->UnwindCode[i].u.OpInfo, floatvalue);
551 552 553
                break;
            case UWOP_SAVE_XMM128_FAR:  /* movaps %xmmreg,nn(%rsp) */
                off = newframe + *(DWORD*)&info->UnwindCode[i+1];
554
                if (!sw_read_mem(csw, off, &floatvalue, sizeof(M128A))) return FALSE;
555
                set_float_reg(context, info->UnwindCode[i].u.OpInfo, floatvalue);
556 557
                break;
            case UWOP_PUSH_MACHFRAME:
558
                FIXME("PUSH_MACHFRAME %u\n", info->UnwindCode[i].u.OpInfo);
559 560
                break;
            default:
561
                FIXME("unknown code %u\n", info->UnwindCode[i].u.UnwindOp);
562 563 564 565 566 567 568 569 570
                break;
            }
        }
        if (!(info->Flags & UNW_FLAG_CHAININFO)) break;
        if (!sw_read_mem(csw, base + function->UnwindData + FIELD_OFFSET(UNWIND_INFO, UnwindCode) +
                                   ((info->CountOfCodes + 1) & ~1) * sizeof(UNWIND_CODE),
                         &handler_data, sizeof(handler_data))) return FALSE;
        function = &handler_data.chain;  /* restart with the chained info */
    }
571
    return default_unwind(csw, context);
572 573
}

574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
/* fetch_next_frame()
 *
 * modify (at least) context.{rip, rsp, rbp} using unwind information
 * either out of PE exception handlers, debug info (dwarf), or simple stack unwind
 */
static BOOL fetch_next_frame(struct cpu_stack_walk* csw, CONTEXT* context,
                             DWORD_PTR curr_pc, void** prtf)
{
    DWORD_PTR               cfa;
    RUNTIME_FUNCTION*       rtf;
    DWORD64                 base;

    if (!curr_pc || !(base = sw_module_base(csw, curr_pc))) return FALSE;
    rtf = sw_table_access(csw, curr_pc);
    if (prtf) *prtf = rtf;
    if (rtf)
    {
        return interpret_function_table_entry(csw, context, rtf, base);
    }
    else if (dwarf2_virtual_unwind(csw, curr_pc, context, &cfa))
    {
        context->Rsp = cfa;
        TRACE("next function rip=%016lx\n", context->Rip);
        TRACE("  rax=%016lx rbx=%016lx rcx=%016lx rdx=%016lx\n",
              context->Rax, context->Rbx, context->Rcx, context->Rdx);
        TRACE("  rsi=%016lx rdi=%016lx rbp=%016lx rsp=%016lx\n",
              context->Rsi, context->Rdi, context->Rbp, context->Rsp);
        TRACE("   r8=%016lx  r9=%016lx r10=%016lx r11=%016lx\n",
              context->R8, context->R9, context->R10, context->R11);
        TRACE("  r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n",
              context->R12, context->R13, context->R14, context->R15);
        return TRUE;
    }
    else
        return default_unwind(csw, context);
}

611 612
static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
{
613
    unsigned    deltapc = curr_count <= 1 ? 0 : 1;
614

615 616 617 618
    /* sanity check */
    if (curr_mode >= stm_done) return FALSE;
    assert(!csw->is32);

619
    TRACE("Enter: PC=%s Frame=%s Return=%s Stack=%s Mode=%s Count=%s\n",
620 621 622 623
          wine_dbgstr_addr(&frame->AddrPC),
          wine_dbgstr_addr(&frame->AddrFrame),
          wine_dbgstr_addr(&frame->AddrReturn),
          wine_dbgstr_addr(&frame->AddrStack),
624 625
          curr_mode == stm_start ? "start" : "64bit",
          wine_dbgstr_longlong(curr_count));
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645

    if (curr_mode == stm_start)
    {
        if ((frame->AddrPC.Mode == AddrModeFlat) &&
            (frame->AddrFrame.Mode != AddrModeFlat))
        {
            WARN("Bad AddrPC.Mode / AddrFrame.Mode combination\n");
            goto done_err;
        }

        /* Init done */
        curr_mode = stm_64bit;
        frame->AddrReturn.Mode = frame->AddrStack.Mode = AddrModeFlat;
        /* don't set up AddrStack on first call. Either the caller has set it up, or
         * we will get it in the next frame
         */
        memset(&frame->AddrBStore, 0, sizeof(frame->AddrBStore));
    }
    else
    {
646
        if (context->Rsp != frame->AddrStack.Offset) FIXME("inconsistent Stack Pointer\n");
647
        if (context->Rip != frame->AddrPC.Offset) FIXME("inconsistent Instruction Pointer\n");
648

649
        if (frame->AddrReturn.Offset == 0) goto done_err;
650
        if (!fetch_next_frame(csw, context, frame->AddrPC.Offset - deltapc, &frame->FuncTableEntry))
651
            goto done_err;
652
        deltapc = 1;
653
    }
654

655 656
    memset(&frame->Params, 0, sizeof(frame->Params));

657 658 659
    /* set frame information */
    frame->AddrStack.Offset = context->Rsp;
    frame->AddrFrame.Offset = context->Rbp;
660 661 662 663 664 665 666 667 668 669
    frame->AddrPC.Offset = context->Rip;
    if (1)
    {
        CONTEXT         newctx = *context;

        if (!fetch_next_frame(csw, &newctx, frame->AddrPC.Offset - deltapc, NULL))
            goto done_err;
        frame->AddrReturn.Mode = AddrModeFlat;
        frame->AddrReturn.Offset = newctx.Rip;
    }
670

671 672
    frame->Far = TRUE;
    frame->Virtual = TRUE;
673
    curr_count++;
674

675
    TRACE("Leave: PC=%s Frame=%s Return=%s Stack=%s Mode=%s Count=%s FuncTable=%p\n",
676 677 678 679 680
          wine_dbgstr_addr(&frame->AddrPC),
          wine_dbgstr_addr(&frame->AddrFrame),
          wine_dbgstr_addr(&frame->AddrReturn),
          wine_dbgstr_addr(&frame->AddrStack),
          curr_mode == stm_start ? "start" : "64bit",
681
          wine_dbgstr_longlong(curr_count),
682 683 684 685 686 687 688
          frame->FuncTableEntry);

    return TRUE;
done_err:
    curr_mode = stm_done;
    return FALSE;
}
689 690 691 692 693 694
#else
static BOOL x86_64_stack_walk(struct cpu_stack_walk* csw, LPSTACKFRAME64 frame, CONTEXT* context)
{
    return FALSE;
}
#endif
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
static void*    x86_64_find_runtime_function(struct module* module, DWORD64 addr)
{
#ifdef __x86_64__
    RUNTIME_FUNCTION*   rtf;
    ULONG               size;
    int                 min, max;

    rtf = (RUNTIME_FUNCTION*)pe_map_directory(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size);
    if (rtf) for (min = 0, max = size / sizeof(*rtf); min <= max; )
    {
        int pos = (min + max) / 2;
        if (addr < module->module.BaseOfImage + rtf[pos].BeginAddress) max = pos - 1;
        else if (addr >= module->module.BaseOfImage + rtf[pos].EndAddress) min = pos + 1;
        else
        {
            rtf += pos;
            while (rtf->UnwindData & 1)  /* follow chained entry */
            {
                FIXME("RunTime_Function outside IMAGE_DIRECTORY_ENTRY_EXCEPTION unimplemented yet!\n");
                /* we need to read into the other process */
                /* rtf = (RUNTIME_FUNCTION*)(module->module.BaseOfImage + (rtf->UnwindData & ~1)); */
            }
            return rtf;
        }
    }
#endif
    return NULL;
}

725
static unsigned x86_64_map_dwarf_register(unsigned regno, BOOL eh_frame)
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
{
    unsigned    reg;

    if (regno >= 17 && regno <= 24)
        reg = CV_AMD64_XMM0 + regno - 17;
    else if (regno >= 25 && regno <= 32)
        reg = CV_AMD64_XMM8 + regno - 25;
    else if (regno >= 33 && regno <= 40)
        reg = CV_AMD64_ST0 + regno - 33;
    else switch (regno)
    {
    case  0: reg = CV_AMD64_RAX;    break;
    case  1: reg = CV_AMD64_RDX;    break;
    case  2: reg = CV_AMD64_RCX;    break;
    case  3: reg = CV_AMD64_RBX;    break;
    case  4: reg = CV_AMD64_RSI;    break;
    case  5: reg = CV_AMD64_RDI;    break;
    case  6: reg = CV_AMD64_RBP;    break;
    case  7: reg = CV_AMD64_RSP;    break;
    case  8: reg = CV_AMD64_R8;     break;
    case  9: reg = CV_AMD64_R9;     break;
    case 10: reg = CV_AMD64_R10;    break;
    case 11: reg = CV_AMD64_R11;    break;
    case 12: reg = CV_AMD64_R12;    break;
    case 13: reg = CV_AMD64_R13;    break;
    case 14: reg = CV_AMD64_R14;    break;
    case 15: reg = CV_AMD64_R15;    break;
    case 16: reg = CV_AMD64_RIP;    break;
    case 49: reg = CV_AMD64_EFLAGS; break;
    case 50: reg = CV_AMD64_ES;     break;
    case 51: reg = CV_AMD64_CS;     break;
    case 52: reg = CV_AMD64_SS;     break;
    case 53: reg = CV_AMD64_DS;     break;
    case 54: reg = CV_AMD64_FS;     break;
    case 55: reg = CV_AMD64_GS;     break;
    case 62: reg = CV_AMD64_TR;     break;
    case 63: reg = CV_AMD64_LDTR;   break;
    case 64: reg = CV_AMD64_MXCSR;  break;
    case 65: reg = CV_AMD64_CTRL;   break;
    case 66: reg = CV_AMD64_STAT;   break;
/*
 * 56-57 reserved
 * 58 %fs.base
 * 59 %gs.base
 * 60-61 reserved
 */
    default:
        FIXME("Don't know how to map register %d\n", regno);
        return 0;
    }
    return reg;
}

779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
static void* x86_64_fetch_context_reg(CONTEXT* ctx, unsigned regno, unsigned* size)
{
#ifdef __x86_64__
    switch (regno)
    {
    case CV_AMD64_RAX: *size = sizeof(ctx->Rax); return &ctx->Rax;
    case CV_AMD64_RDX: *size = sizeof(ctx->Rdx); return &ctx->Rdx;
    case CV_AMD64_RCX: *size = sizeof(ctx->Rcx); return &ctx->Rcx;
    case CV_AMD64_RBX: *size = sizeof(ctx->Rbx); return &ctx->Rbx;
    case CV_AMD64_RSI: *size = sizeof(ctx->Rsi); return &ctx->Rsi;
    case CV_AMD64_RDI: *size = sizeof(ctx->Rdi); return &ctx->Rdi;
    case CV_AMD64_RBP: *size = sizeof(ctx->Rbp); return &ctx->Rbp;
    case CV_AMD64_RSP: *size = sizeof(ctx->Rsp); return &ctx->Rsp;
    case CV_AMD64_R8:  *size = sizeof(ctx->R8);  return &ctx->R8;
    case CV_AMD64_R9:  *size = sizeof(ctx->R9);  return &ctx->R9;
    case CV_AMD64_R10: *size = sizeof(ctx->R10); return &ctx->R10;
    case CV_AMD64_R11: *size = sizeof(ctx->R11); return &ctx->R11;
    case CV_AMD64_R12: *size = sizeof(ctx->R12); return &ctx->R12;
    case CV_AMD64_R13: *size = sizeof(ctx->R13); return &ctx->R13;
    case CV_AMD64_R14: *size = sizeof(ctx->R14); return &ctx->R14;
    case CV_AMD64_R15: *size = sizeof(ctx->R15); return &ctx->R15;
    case CV_AMD64_RIP: *size = sizeof(ctx->Rip); return &ctx->Rip;

    case CV_AMD64_XMM0 + 0: *size = sizeof(ctx->u.s.Xmm0 ); return &ctx->u.s.Xmm0;
    case CV_AMD64_XMM0 + 1: *size = sizeof(ctx->u.s.Xmm1 ); return &ctx->u.s.Xmm1;
    case CV_AMD64_XMM0 + 2: *size = sizeof(ctx->u.s.Xmm2 ); return &ctx->u.s.Xmm2;
    case CV_AMD64_XMM0 + 3: *size = sizeof(ctx->u.s.Xmm3 ); return &ctx->u.s.Xmm3;
    case CV_AMD64_XMM0 + 4: *size = sizeof(ctx->u.s.Xmm4 ); return &ctx->u.s.Xmm4;
    case CV_AMD64_XMM0 + 5: *size = sizeof(ctx->u.s.Xmm5 ); return &ctx->u.s.Xmm5;
    case CV_AMD64_XMM0 + 6: *size = sizeof(ctx->u.s.Xmm6 ); return &ctx->u.s.Xmm6;
    case CV_AMD64_XMM0 + 7: *size = sizeof(ctx->u.s.Xmm7 ); return &ctx->u.s.Xmm7;
    case CV_AMD64_XMM8 + 0: *size = sizeof(ctx->u.s.Xmm8 ); return &ctx->u.s.Xmm8;
    case CV_AMD64_XMM8 + 1: *size = sizeof(ctx->u.s.Xmm9 ); return &ctx->u.s.Xmm9;
    case CV_AMD64_XMM8 + 2: *size = sizeof(ctx->u.s.Xmm10); return &ctx->u.s.Xmm10;
    case CV_AMD64_XMM8 + 3: *size = sizeof(ctx->u.s.Xmm11); return &ctx->u.s.Xmm11;
    case CV_AMD64_XMM8 + 4: *size = sizeof(ctx->u.s.Xmm12); return &ctx->u.s.Xmm12;
    case CV_AMD64_XMM8 + 5: *size = sizeof(ctx->u.s.Xmm13); return &ctx->u.s.Xmm13;
    case CV_AMD64_XMM8 + 6: *size = sizeof(ctx->u.s.Xmm14); return &ctx->u.s.Xmm14;
    case CV_AMD64_XMM8 + 7: *size = sizeof(ctx->u.s.Xmm15); return &ctx->u.s.Xmm15;

    case CV_AMD64_ST0 + 0: *size = sizeof(ctx->u.s.Legacy[0]); return &ctx->u.s.Legacy[0];
    case CV_AMD64_ST0 + 1: *size = sizeof(ctx->u.s.Legacy[1]); return &ctx->u.s.Legacy[1];
    case CV_AMD64_ST0 + 2: *size = sizeof(ctx->u.s.Legacy[2]); return &ctx->u.s.Legacy[2];
    case CV_AMD64_ST0 + 3: *size = sizeof(ctx->u.s.Legacy[3]); return &ctx->u.s.Legacy[3];
    case CV_AMD64_ST0 + 4: *size = sizeof(ctx->u.s.Legacy[4]); return &ctx->u.s.Legacy[4];
    case CV_AMD64_ST0 + 5: *size = sizeof(ctx->u.s.Legacy[5]); return &ctx->u.s.Legacy[5];
    case CV_AMD64_ST0 + 6: *size = sizeof(ctx->u.s.Legacy[6]); return &ctx->u.s.Legacy[6];
    case CV_AMD64_ST0 + 7: *size = sizeof(ctx->u.s.Legacy[7]); return &ctx->u.s.Legacy[7];

    case CV_AMD64_EFLAGS: *size = sizeof(ctx->EFlags); return &ctx->EFlags;
    case CV_AMD64_ES: *size = sizeof(ctx->SegEs); return &ctx->SegEs;
    case CV_AMD64_CS: *size = sizeof(ctx->SegCs); return &ctx->SegCs;
    case CV_AMD64_SS: *size = sizeof(ctx->SegSs); return &ctx->SegSs;
    case CV_AMD64_DS: *size = sizeof(ctx->SegDs); return &ctx->SegDs;
    case CV_AMD64_FS: *size = sizeof(ctx->SegFs); return &ctx->SegFs;
    case CV_AMD64_GS: *size = sizeof(ctx->SegGs); return &ctx->SegGs;

    }
#endif
    FIXME("Unknown register %x\n", regno);
    return NULL;
}

static const char* x86_64_fetch_regname(unsigned regno)
{
    switch (regno)
    {
    case CV_AMD64_RAX:          return "rax";
    case CV_AMD64_RDX:          return "rdx";
    case CV_AMD64_RCX:          return "rcx";
    case CV_AMD64_RBX:          return "rbx";
    case CV_AMD64_RSI:          return "rsi";
    case CV_AMD64_RDI:          return "rdi";
    case CV_AMD64_RBP:          return "rbp";
    case CV_AMD64_RSP:          return "rsp";
    case CV_AMD64_R8:           return "r8";
    case CV_AMD64_R9:           return "r9";
    case CV_AMD64_R10:          return "r10";
    case CV_AMD64_R11:          return "r11";
    case CV_AMD64_R12:          return "r12";
    case CV_AMD64_R13:          return "r13";
    case CV_AMD64_R14:          return "r14";
    case CV_AMD64_R15:          return "r15";
    case CV_AMD64_RIP:          return "rip";

    case CV_AMD64_XMM0 + 0:     return "xmm0";
    case CV_AMD64_XMM0 + 1:     return "xmm1";
    case CV_AMD64_XMM0 + 2:     return "xmm2";
    case CV_AMD64_XMM0 + 3:     return "xmm3";
    case CV_AMD64_XMM0 + 4:     return "xmm4";
    case CV_AMD64_XMM0 + 5:     return "xmm5";
    case CV_AMD64_XMM0 + 6:     return "xmm6";
    case CV_AMD64_XMM0 + 7:     return "xmm7";
    case CV_AMD64_XMM8 + 0:     return "xmm8";
    case CV_AMD64_XMM8 + 1:     return "xmm9";
    case CV_AMD64_XMM8 + 2:     return "xmm10";
    case CV_AMD64_XMM8 + 3:     return "xmm11";
    case CV_AMD64_XMM8 + 4:     return "xmm12";
    case CV_AMD64_XMM8 + 5:     return "xmm13";
    case CV_AMD64_XMM8 + 6:     return "xmm14";
    case CV_AMD64_XMM8 + 7:     return "xmm15";

    case CV_AMD64_ST0 + 0:      return "st0";
    case CV_AMD64_ST0 + 1:      return "st1";
    case CV_AMD64_ST0 + 2:      return "st2";
    case CV_AMD64_ST0 + 3:      return "st3";
    case CV_AMD64_ST0 + 4:      return "st4";
    case CV_AMD64_ST0 + 5:      return "st5";
    case CV_AMD64_ST0 + 6:      return "st6";
    case CV_AMD64_ST0 + 7:      return "st7";

    case CV_AMD64_EFLAGS:       return "eflags";
    case CV_AMD64_ES:           return "es";
    case CV_AMD64_CS:           return "cs";
    case CV_AMD64_SS:           return "ss";
    case CV_AMD64_DS:           return "ds";
    case CV_AMD64_FS:           return "fs";
    case CV_AMD64_GS:           return "gs";
    }
    FIXME("Unknown register %x\n", regno);
    return NULL;
}
901

902 903 904 905 906 907 908 909 910 911 912 913 914 915
static BOOL x86_64_fetch_minidump_thread(struct dump_context* dc, unsigned index, unsigned flags, const CONTEXT* ctx)
{
    if (ctx->ContextFlags && (flags & ThreadWriteInstructionWindow))
    {
        /* FIXME: crop values across module boundaries, */
#ifdef __x86_64__
        ULONG64 base = ctx->Rip <= 0x80 ? 0 : ctx->Rip - 0x80;
        minidump_add_memory_block(dc, base, ctx->Rip + 0x80 - base, 0);
#endif
    }

    return TRUE;
}

916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
static BOOL x86_64_fetch_minidump_module(struct dump_context* dc, unsigned index, unsigned flags)
{
    /* FIXME: not sure about the flags... */
    if (1)
    {
        /* FIXME: crop values across module boundaries, */
#ifdef __x86_64__
        struct process*         pcs;
        struct module*          module;
        const RUNTIME_FUNCTION* rtf;
        ULONG                   size;

        if (!(pcs = process_find_by_handle(dc->hProcess)) ||
            !(module = module_find_by_addr(pcs, dc->modules[index].base, DMT_UNKNOWN)))
            return FALSE;
        rtf = (const RUNTIME_FUNCTION*)pe_map_directory(module, IMAGE_DIRECTORY_ENTRY_EXCEPTION, &size);
        if (rtf)
        {
            const RUNTIME_FUNCTION* end = (const RUNTIME_FUNCTION*)((const char*)rtf + size);
            UNWIND_INFO ui;

            while (rtf + 1 < end)
            {
                while (rtf->UnwindData & 1)  /* follow chained entry */
                {
                    FIXME("RunTime_Function outside IMAGE_DIRECTORY_ENTRY_EXCEPTION unimplemented yet!\n");
                    return FALSE;
                    /* we need to read into the other process */
                    /* rtf = (RUNTIME_FUNCTION*)(module->module.BaseOfImage + (rtf->UnwindData & ~1)); */
                }
                if (ReadProcessMemory(dc->hProcess,
                                      (void*)(dc->modules[index].base + rtf->UnwindData),
                                      &ui, sizeof(ui), NULL))
                    minidump_add_memory_block(dc, dc->modules[index].base + rtf->UnwindData,
                                              FIELD_OFFSET(UNWIND_INFO, UnwindCode) + ui.CountOfCodes * sizeof(UNWIND_CODE), 0);
                rtf++;
            }
        }
#endif
    }

    return TRUE;
}

960
DECLSPEC_HIDDEN struct cpu cpu_x86_64 = {
961
    IMAGE_FILE_MACHINE_AMD64,
962
    8,
963
    CV_AMD64_RSP,
964
    x86_64_get_addr,
965
    x86_64_stack_walk,
966
    x86_64_find_runtime_function,
967
    x86_64_map_dwarf_register,
968 969
    x86_64_fetch_context_reg,
    x86_64_fetch_regname,
970
    x86_64_fetch_minidump_thread,
971
    x86_64_fetch_minidump_module,
972
};