memory.c 18.6 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4 5
/*
 * Debugger memory handling
 *
 * Copyright 1993 Eric Youngdale
 * Copyright 1995 Alexandre Julliard
6
 * Copyright 2000-2005 Eric Pouech
7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Alexandre Julliard's avatar
Alexandre Julliard committed
21 22
 */

23
#include "config.h"
24 25
#include "wine/port.h"

Alexandre Julliard's avatar
Alexandre Julliard committed
26
#include <stdlib.h>
27
#include <string.h>
28
#include <stdio.h>
Patrik Stridvall's avatar
Patrik Stridvall committed
29

Alexandre Julliard's avatar
Alexandre Julliard committed
30
#include "debugger.h"
31
#include "wine/debug.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
32

33
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
Alexandre Julliard's avatar
Alexandre Julliard committed
34

35
void* be_cpu_linearize(HANDLE hThread, const ADDRESS* addr)
Alexandre Julliard's avatar
Alexandre Julliard committed
36
{
37 38
    assert(addr->Mode == AddrModeFlat);
    return (void*)addr->Offset;
39
}
40

41 42
unsigned be_cpu_build_addr(HANDLE hThread, const CONTEXT* ctx, ADDRESS* addr, 
                           unsigned seg, unsigned long offset)
43
{
44 45 46 47
    addr->Mode    = AddrModeFlat;
    addr->Segment = 0; /* don't need segment */
    addr->Offset  = offset;
    return TRUE;
48
}
49

50
void* memory_to_linear_addr(const ADDRESS* addr)
51
{
52
    return be_cpu->linearize(dbg_curr_thread->handle, addr);
53
}
54

55
BOOL memory_get_current_pc(ADDRESS* addr)
56
{
57 58 59
    assert(be_cpu->get_addr);
    return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context, 
                            be_cpu_addr_pc, addr);
60
}
Alexandre Julliard's avatar
Alexandre Julliard committed
61

62
BOOL memory_get_current_stack(ADDRESS* addr)
63
{
64 65 66
    assert(be_cpu->get_addr);
    return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context, 
                            be_cpu_addr_stack, addr);
67 68
}

69
BOOL memory_get_current_frame(ADDRESS* addr)
Alexandre Julliard's avatar
Alexandre Julliard committed
70
{
71 72 73
    assert(be_cpu->get_addr);
    return be_cpu->get_addr(dbg_curr_thread->handle, &dbg_context, 
                            be_cpu_addr_frame, addr);
74
}
75

76
static void	memory_report_invalid_addr(const void* addr)
77
{
78 79 80 81 82 83 84 85
    ADDRESS     address;

    address.Mode    = AddrModeFlat;
    address.Segment = 0;
    address.Offset  = (unsigned long)addr;
    dbg_printf("*** Invalid address ");
    print_address(&address, FALSE);
    dbg_printf("\n");
Alexandre Julliard's avatar
Alexandre Julliard committed
86 87
}

88 89 90 91 92 93
/***********************************************************************
 *           memory_read_value
 *
 * Read a memory value.
 */
BOOL memory_read_value(const struct dbg_lvalue* lvalue, DWORD size, void* result)
94
{
95 96
    BOOL ret = FALSE;

97 98
    if (lvalue->cookie == DLV_TARGET)
    {
99 100 101
        void*   linear = memory_to_linear_addr(&lvalue->addr);
        if (!(ret = dbg_read_memory(linear, result, size)))
            memory_report_invalid_addr(linear);
102 103 104
    }
    else
    {
105 106 107 108 109
        if (lvalue->addr.Offset)
        {
            memcpy(result, (void*)lvalue->addr.Offset, size);
            ret = TRUE;
        }
110 111
    }
    return TRUE;
112
}
Alexandre Julliard's avatar
Alexandre Julliard committed
113

114 115 116 117 118 119
/***********************************************************************
 *           memory_write_value
 *
 * Store a value in memory.
 */
BOOL memory_write_value(const struct dbg_lvalue* lvalue, DWORD size, void* value)
120
{
121
    BOOL        ret = TRUE;
122
    DWORD64     os;
123

124
    os = ~(DWORD64)size;
125
    types_get_info(&lvalue->type, TI_GET_LENGTH, &os);
126
    assert(size == os);
127

128 129 130
    /* FIXME: only works on little endian systems */
    if (lvalue->cookie == DLV_TARGET)
    {
131 132 133
        void*       linear = memory_to_linear_addr(&lvalue->addr);
        if (!(ret = dbg_write_memory(linear, value, size)))
            memory_report_invalid_addr(linear);
134 135 136 137 138 139
    }
    else 
    {
        memcpy((void*)lvalue->addr.Offset, value, size);
    }
    return ret;
140 141
}

142 143 144 145 146
/***********************************************************************
 *           memory_examine
 *
 * Implementation of the 'x' command.
 */
147
void memory_examine(const struct dbg_lvalue *lvalue, int count, char format)
Alexandre Julliard's avatar
Alexandre Julliard committed
148
{
149 150
    int			i;
    char                buffer[256];
151
    ADDRESS             addr;
152
    void               *linear;
153

154 155
    types_extract_as_address(lvalue, &addr);
    linear = memory_to_linear_addr(&addr);
Alexandre Julliard's avatar
Alexandre Julliard committed
156

157 158
    if (format != 'i' && count > 1)
    {
159
        print_address(&addr, FALSE);
160 161 162 163 164 165 166
        dbg_printf(": ");
    }

    switch (format)
    {
    case 'u':
        if (count == 1) count = 256;
167
        memory_get_string(dbg_curr_process, linear, 
168
                          TRUE, TRUE, buffer, min(count, sizeof(buffer)));
169 170 171 172
        dbg_printf("%s\n", buffer);
        return;
    case 's':
        if (count == 1) count = 256;
173
        memory_get_string(dbg_curr_process, linear,
174
                          TRUE, FALSE, buffer, min(count, sizeof(buffer)));
175 176 177
        dbg_printf("%s\n", buffer);
        return;
    case 'i':
178
        while (count-- && memory_disasm_one_insn(&addr));
179 180 181 182 183
        return;
    case 'g':
        while (count--)
        {
            GUID guid;
184 185 186 187 188
            if (!dbg_read_memory(linear, &guid, sizeof(guid)))
            {
                memory_report_invalid_addr(linear);
                break;
            }
189 190 191 192
            dbg_printf("{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
                       guid.Data1, guid.Data2, guid.Data3,
                       guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
                       guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
193 194
            linear = (char*)linear + sizeof(guid);
            addr.Offset += sizeof(guid);
195 196
            if (count)
            {
197
                print_address(&addr, FALSE);
198 199 200 201 202 203 204 205
                dbg_printf(": ");
            }
        }
        return;

#define DO_DUMP2(_t,_l,_f,_vv) {                                        \
            _t _v;                                                      \
            for (i = 0; i < count; i++) {                               \
206 207
                if (!dbg_read_memory(linear, &_v, sizeof(_t)))          \
                { memory_report_invalid_addr(linear); break; }          \
208
                dbg_printf(_f, (_vv));                                  \
209 210 211 212
                addr.Offset += sizeof(_t);                              \
                linear = (char*)linear + sizeof(_t);                    \
                if ((i % (_l)) == (_l) - 1 && i != count - 1)           \
                {                                                       \
213
                    dbg_printf("\n");                                   \
214
                    print_address(&addr, FALSE);                        \
215 216 217 218 219 220 221 222 223 224 225 226 227 228
                    dbg_printf(": ");                                   \
                }                                                       \
            }                                                           \
            dbg_printf("\n");                                           \
        }                                                               \
	return
#define DO_DUMP(_t,_l,_f) DO_DUMP2(_t,_l,_f,_v)

    case 'x': DO_DUMP(int, 4, " %8.8x");
    case 'd': DO_DUMP(unsigned int, 4, " %10d");
    case 'w': DO_DUMP(unsigned short, 8, " %04x");
    case 'c': DO_DUMP2(char, 32, " %c", (_v < 0x20) ? ' ' : _v);
    case 'b': DO_DUMP2(char, 16, " %02x", (_v) & 0xff);
    }
229 230
}

231 232
BOOL memory_get_string(struct dbg_process* pcs, void* addr, BOOL in_debuggee,
                       BOOL unicode, char* buffer, int size)
233
{
234 235
    DWORD       sz;
    WCHAR*      buffW;
236

237 238
    buffer[0] = 0;
    if (!addr) return FALSE;
239
    if (in_debuggee)
240
    {
241
        BOOL ret;
242

243 244 245 246 247 248 249 250 251 252 253
        if (!unicode) ret = pcs->process_io->read(pcs->handle, addr, buffer, size, &sz);
        else
        {
            buffW = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR));
            ret = pcs->process_io->read(pcs->handle, addr, buffW, size * sizeof(WCHAR), &sz);
            WideCharToMultiByte(CP_ACP, 0, buffW, sz / sizeof(WCHAR), buffer, size,
                                NULL, NULL);
            HeapFree(GetProcessHeap(), 0, buffW);
        }
        if (size) buffer[size-1] = 0;
        return ret;
254 255 256
    }
    else
    {
257
        lstrcpynA(buffer, addr, size);
258
    }
259
    return TRUE;
260
}
Alexandre Julliard's avatar
Alexandre Julliard committed
261

262
BOOL memory_get_string_indirect(struct dbg_process* pcs, void* addr, BOOL unicode, char* buffer, int size)
Alexandre Julliard's avatar
Alexandre Julliard committed
263
{
264 265
    void*       ad;
    DWORD	sz;
266

267 268
    buffer[0] = 0;
    if (addr && 
269
        pcs->process_io->read(pcs->handle, addr, &ad, sizeof(ad), &sz) && sz == sizeof(ad) && ad)
270
    {
271
        return memory_get_string(pcs, ad, TRUE, unicode, buffer, size);
272 273 274
    }
    return FALSE;
}
275

276 277
static void print_typed_basic(const struct dbg_lvalue* lvalue)
{
278
    LONGLONG            val_int;
279 280
    void*               val_ptr;
    long double         val_real;
281
    DWORD64             size64;
282 283
    DWORD               tag, size, count, bt;
    struct dbg_type     rtype;
284

285 286
    if (lvalue->type.id == dbg_itype_none ||
        !types_get_info(&lvalue->type, TI_GET_SYMTAG, &tag))
287
        return;
288

289 290 291
    switch (tag)
    {
    case SymTagBaseType:
292
        if (!types_get_info(&lvalue->type, TI_GET_LENGTH, &size64) ||
293
            !types_get_info(&lvalue->type, TI_GET_BASETYPE, &bt))
294 295 296 297
        {
            WINE_ERR("Couldn't get information\n");
            RaiseException(DEBUG_STATUS_INTERNAL_ERROR, 0, 0, NULL);
        }
298
        size = (DWORD)size64;
299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
        switch (bt)
        {
        case btInt:
            if (!be_cpu->fetch_integer(lvalue, size, TRUE, &val_int)) return;
            dbg_printf("%lld", val_int);
            break;
        case btUInt:
            if (!be_cpu->fetch_integer(lvalue, size, FALSE, &val_int)) return;
            dbg_printf("%llu", val_int);
            break;
        case btFloat:
            if (!be_cpu->fetch_float(lvalue, size, &val_real)) return;
            dbg_printf("%Lf", val_real);
            break;
        case btChar:
            if (!be_cpu->fetch_integer(lvalue, size, TRUE, &val_int)) return;
            /* FIXME: should do the same for a Unicode character (size == 2) */
            if (size == 1 && (val_int < 0x20 || val_int > 0x80))
                dbg_printf("%d", (int)val_int);
            else
                dbg_printf("'%c'", (char)val_int);
            break;
        default:
            WINE_FIXME("Unsupported basetype %lu\n", bt);
            break;
        }
        break;
    case SymTagPointerType:
        if (!memory_read_value(lvalue, sizeof(void*), &val_ptr)) return;

329 330
        if (!types_get_info(&lvalue->type, TI_GET_TYPE, &rtype.id) ||
            rtype.id == dbg_itype_none)
331 332 333 334
        {
            dbg_printf("Internal symbol error: unable to access memory location %p", val_ptr);
            break;
        }
335 336 337
        rtype.module = lvalue->type.module;
        if (types_get_info(&rtype, TI_GET_SYMTAG, &tag) && tag == SymTagBaseType &&
            types_get_info(&rtype, TI_GET_BASETYPE, &bt) && bt == btChar &&
338
            types_get_info(&rtype, TI_GET_LENGTH, &size64))
339 340 341
        {
            char    buffer[1024];

342 343 344
            if (!val_ptr) dbg_printf("0x0");
            else if (memory_get_string(dbg_curr_process, val_ptr, 
                                       lvalue->cookie == DLV_TARGET,
345
                                       size64 == 2, buffer, sizeof(buffer)))
346 347 348
                dbg_printf("\"%s\"", buffer);
            else
                dbg_printf("*** invalid address %p ***", val_ptr);
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
        }
        else dbg_printf("%p", val_ptr);
        break;
    case SymTagArrayType:
    case SymTagUDT:
        assert(lvalue->cookie == DLV_TARGET);
        if (!memory_read_value(lvalue, sizeof(val_ptr), &val_ptr)) return;
        dbg_printf("%p", val_ptr);
        break;
    case SymTagEnum:
        {
            BOOL        ok = FALSE;

            assert(lvalue->cookie == DLV_TARGET);
            /* FIXME: it depends on underlying type for enums 
             * (not supported yet in dbghelp)
             * Assuming 4 as for an int
             */
            if (!be_cpu->fetch_integer(lvalue, 4, TRUE, &val_int)) return;

369
            if (types_get_info(&lvalue->type, TI_GET_CHILDRENCOUNT, &count))
370
            {
371 372 373 374 375 376 377
                char                    buffer[sizeof(TI_FINDCHILDREN_PARAMS) + 256 * sizeof(DWORD)];
                TI_FINDCHILDREN_PARAMS* fcp = (TI_FINDCHILDREN_PARAMS*)buffer;
                WCHAR*                  ptr;
                char                    tmp[256];
                VARIANT                 variant;
                int                     i;
                struct dbg_type         type;
378 379 380 381 382

                fcp->Start = 0;
                while (count)
                {
                    fcp->Count = min(count, 256);
383
                    if (types_get_info(&lvalue->type, TI_FINDCHILDREN, fcp))
384
                    {
385
                        type.module = lvalue->type.module;
386 387
                        for (i = 0; i < min(fcp->Count, count); i++)
                        {
388 389
                            type.id = fcp->ChildId[i];
                            if (!types_get_info(&type, TI_GET_VALUE, &variant)) 
390 391 392 393 394 395 396 397 398
                                continue;
                            switch (variant.n1.n2.vt)
                            {
                            case VT_I4: ok = (val_int == variant.n1.n2.n3.lVal); break;
                            default: WINE_FIXME("Unsupported variant type (%u)\n", variant.n1.n2.vt);
                            }
                            if (ok)
                            {
                                ptr = NULL;
399
                                types_get_info(&type, TI_GET_SYMNAME, &ptr);
400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
                                if (!ptr) continue;
                                WideCharToMultiByte(CP_ACP, 0, ptr, -1, tmp, sizeof(tmp), NULL, NULL);
                                HeapFree(GetProcessHeap(), 0, ptr);
                                dbg_printf("%s", tmp);
                                count = 0; /* so that we'll get away from outter loop */
                                break;
                            }
                        }
                    }
                }
                count -= min(count, 256);
                fcp->Start += 256;
            }
            if (!ok) dbg_printf("%lld", val_int);
        }
        break;
    default:
        WINE_FIXME("Unsupported tag %lu\n", tag);
        break;
419
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
420 421 422
}

/***********************************************************************
423
 *           print_basic
Alexandre Julliard's avatar
Alexandre Julliard committed
424
 *
425
 * Implementation of the 'print' command.
Alexandre Julliard's avatar
Alexandre Julliard committed
426
 */
427
void print_basic(const struct dbg_lvalue* lvalue, int count, char format)
Alexandre Julliard's avatar
Alexandre Julliard committed
428
{
429
    long int    res;
430

431
    if (lvalue->type.id == dbg_itype_none)
432 433 434 435
    {
        dbg_printf("Unable to evaluate expression\n");
        return;
    }
436

437
    res = types_extract_as_integer(lvalue);
Alexandre Julliard's avatar
Alexandre Julliard committed
438

439 440 441 442
    /* FIXME: this implies i386 byte ordering */
    switch (format)
    {
    case 'x':
443 444
        if (lvalue->addr.Mode == AddrMode1616 || 
            lvalue->addr.Mode == AddrModeReal)
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472
            dbg_printf("0x%04lx", res);
        else
            dbg_printf("0x%08lx", res);
        break;

    case 'd':
        dbg_printf("%ld\n", res);
        break;

    case 'c':
        dbg_printf("%d = '%c'", (char)(res & 0xff), (char)(res & 0xff));
        break;

    case 'u':
        {
            WCHAR wch = (WCHAR)(res & 0xFFFF);
            dbg_printf("%d = '", wch);
            dbg_outputW(&wch, 1);
            dbg_printf("'");
        }
        break;

    case 'i':
    case 's':
    case 'w':
    case 'b':
        dbg_printf("Format specifier '%c' is meaningless in 'print' command\n", format);
    case 0:
473 474 475 476
        if (lvalue->type.id == dbg_itype_segptr)
            dbg_printf("%ld", res);
        else 
            print_typed_basic(lvalue);
477
        break;
478
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
479 480
}

481
void print_bare_address(const ADDRESS* addr)
Alexandre Julliard's avatar
Alexandre Julliard committed
482
{
483 484 485 486 487 488 489 490 491 492 493 494
    switch (addr->Mode)
    {
    case AddrModeFlat: 
        dbg_printf("0x%08lx", addr->Offset); 
        break;
    case AddrModeReal:
    case AddrMode1616:
        dbg_printf("0x%04x:0x%04lx", addr->Segment, addr->Offset);
        break;
    case AddrMode1632:
        dbg_printf("0x%04x:0x%08lx", addr->Segment, addr->Offset);
        break;
495 496 497
    default:
        dbg_printf("Unknown mode %x\n", addr->Mode);
        break;
Alexandre Julliard's avatar
Alexandre Julliard committed
498
    }
499 500 501
}

/***********************************************************************
502
 *           print_address
503
 *
504
 * Print an 16- or 32-bit address, with the nearest symbol if any.
505
 */
506
void print_address(const ADDRESS* addr, BOOLEAN with_line)
507
{
508 509 510
    char                buffer[sizeof(SYMBOL_INFO) + 256];
    SYMBOL_INFO*        si = (SYMBOL_INFO*)buffer;
    void*               lin = memory_to_linear_addr(addr);
511 512
    DWORD64             disp64;
    DWORD               disp;
513

514
    print_bare_address(addr);
Alexandre Julliard's avatar
Alexandre Julliard committed
515

516 517
    si->SizeOfStruct = sizeof(*si);
    si->MaxNameLen   = 256;
518
    if (!SymFromAddr(dbg_curr_process->handle, (DWORD_PTR)lin, &disp64, si)) return;
519
    dbg_printf(" %s", si->Name);
520
    if (disp64) dbg_printf("+0x%lx", (DWORD_PTR)disp64);
521
    if (with_line)
Alexandre Julliard's avatar
Alexandre Julliard committed
522
    {
523 524 525 526
        IMAGEHLP_LINE               il;
        IMAGEHLP_MODULE             im;

        il.SizeOfStruct = sizeof(il);
527
        if (SymGetLineFromAddr(dbg_curr_process->handle, (DWORD_PTR)lin, &disp, &il))
528 529
            dbg_printf(" [%s:%lu]", il.FileName, il.LineNumber);
        im.SizeOfStruct = sizeof(im);
530
        if (SymGetModuleInfo(dbg_curr_process->handle, (DWORD_PTR)lin, &im))
531
            dbg_printf(" in %s", im.ModuleName);
Alexandre Julliard's avatar
Alexandre Julliard committed
532
    }
533
}
Alexandre Julliard's avatar
Alexandre Julliard committed
534

535 536 537
BOOL memory_disasm_one_insn(ADDRESS* addr)
{
    char        ch;
538

539 540 541
    print_address(addr, TRUE);
    dbg_printf(": ");
    if (!dbg_read_memory(memory_to_linear_addr(addr), &ch, sizeof(ch)))
542
    {
543 544
        dbg_printf("-- no code accessible --\n");
        return FALSE;
545
    }
546 547 548
    be_cpu->disasm_one_insn(addr, TRUE);
    dbg_printf("\n");
    return TRUE;
549 550
}

551
void memory_disassemble(const struct dbg_lvalue* xstart, 
552
                        const struct dbg_lvalue* xend, int instruction_count)
553
{
554
    static ADDRESS last = {0,0,0};
555 556
    int stop = 0;
    int i;
557

558 559 560 561 562
    if (!xstart && !xend) 
    {
        if (!last.Segment && !last.Offset) memory_get_current_pc(&last);
    }
    else
563
    {
564
        if (xstart)
565
            types_extract_as_address(xstart, &last);
566 567
        if (xend) 
            stop = types_extract_as_integer(xend);
568
    }
569 570 571
    for (i = 0; (instruction_count == 0 || i < instruction_count)  &&
                (stop == 0 || last.Offset <= stop); i++)
        memory_disasm_one_insn(&last);
572
}
573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594

BOOL memory_get_register(DWORD regno, DWORD** value, char* buffer, int len)
{
    const struct dbg_internal_var*  div;

    if (dbg_curr_thread->curr_frame != 0)
    {
        if (buffer) snprintf(buffer, len, "<register not in topmost frame>");
        return FALSE;
    }
    for (div = dbg_context_vars; div->name; div++)
    {
        if (div->val == regno)
        {
            *value = div->pval;
            snprintf(buffer, len, div->name);
            return TRUE;
        }
    }
    if (buffer) snprintf(buffer, len, "<unknown register %lu>", regno);
    return FALSE;
}