diff options
Diffstat (limited to 'gdb/prologue-value.c')
-rw-r--r-- | gdb/prologue-value.c | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/gdb/prologue-value.c b/gdb/prologue-value.c new file mode 100644 index 00000000000..4ad4d6c828f --- /dev/null +++ b/gdb/prologue-value.c @@ -0,0 +1,591 @@ +/* Prologue value handling for GDB. + Copyright 2003, 2004, 2005 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to: + + Free Software Foundation, Inc. + 51 Franklin St - Fifth Floor + Boston, MA 02110-1301 + USA */ + +#include "defs.h" +#include "gdb_string.h" +#include "gdb_assert.h" +#include "prologue-value.h" +#include "regcache.h" + + +/* Constructors. */ + +pv_t +pv_unknown (void) +{ + pv_t v = { pvk_unknown, 0, 0 }; + + return v; +} + + +pv_t +pv_constant (CORE_ADDR k) +{ + pv_t v; + + v.kind = pvk_constant; + v.reg = -1; /* for debugging */ + v.k = k; + + return v; +} + + +pv_t +pv_register (int reg, CORE_ADDR k) +{ + pv_t v; + + v.kind = pvk_register; + v.reg = reg; + v.k = k; + + return v; +} + + + +/* Arithmetic operations. */ + +/* If one of *A and *B is a constant, and the other isn't, swap the + values as necessary to ensure that *B is the constant. This can + reduce the number of cases we need to analyze in the functions + below. */ +static void +constant_last (pv_t *a, pv_t *b) +{ + if (a->kind == pvk_constant + && b->kind != pvk_constant) + { + pv_t temp = *a; + *a = *b; + *b = temp; + } +} + + +pv_t +pv_add (pv_t a, pv_t b) +{ + constant_last (&a, &b); + + /* We can add a constant to a register. */ + if (a.kind == pvk_register + && b.kind == pvk_constant) + return pv_register (a.reg, a.k + b.k); + + /* We can add a constant to another constant. */ + else if (a.kind == pvk_constant + && b.kind == pvk_constant) + return pv_constant (a.k + b.k); + + /* Anything else we don't know how to add. We don't have a + representation for, say, the sum of two registers, or a multiple + of a register's value (adding a register to itself). */ + else + return pv_unknown (); +} + + +pv_t +pv_add_constant (pv_t v, CORE_ADDR k) +{ + /* Rather than thinking of all the cases we can and can't handle, + we'll just let pv_add take care of that for us. */ + return pv_add (v, pv_constant (k)); +} + + +pv_t +pv_subtract (pv_t a, pv_t b) +{ + /* This isn't quite the same as negating B and adding it to A, since + we don't have a representation for the negation of anything but a + constant. For example, we can't negate { pvk_register, R1, 10 }, + but we do know that { pvk_register, R1, 10 } minus { pvk_register, + R1, 5 } is { pvk_constant, <ignored>, 5 }. + + This means, for example, that we could subtract two stack + addresses; they're both relative to the original SP. Since the + frame pointer is set based on the SP, its value will be the + original SP plus some constant (probably zero), so we can use its + value just fine, too. */ + + constant_last (&a, &b); + + /* We can subtract two constants. */ + if (a.kind == pvk_constant + && b.kind == pvk_constant) + return pv_constant (a.k - b.k); + + /* We can subtract a constant from a register. */ + else if (a.kind == pvk_register + && b.kind == pvk_constant) + return pv_register (a.reg, a.k - b.k); + + /* We can subtract a register from itself, yielding a constant. */ + else if (a.kind == pvk_register + && b.kind == pvk_register + && a.reg == b.reg) + return pv_constant (a.k - b.k); + + /* We don't know how to subtract anything else. */ + else + return pv_unknown (); +} + + +pv_t +pv_logical_and (pv_t a, pv_t b) +{ + constant_last (&a, &b); + + /* We can 'and' two constants. */ + if (a.kind == pvk_constant + && b.kind == pvk_constant) + return pv_constant (a.k & b.k); + + /* We can 'and' anything with the constant zero. */ + else if (b.kind == pvk_constant + && b.k == 0) + return pv_constant (0); + + /* We can 'and' anything with ~0. */ + else if (b.kind == pvk_constant + && b.k == ~ (CORE_ADDR) 0) + return a; + + /* We can 'and' a register with itself. */ + else if (a.kind == pvk_register + && b.kind == pvk_register + && a.reg == b.reg + && a.k == b.k) + return a; + + /* Otherwise, we don't know. */ + else + return pv_unknown (); +} + + + +/* Examining prologue values. */ + +int +pv_is_identical (pv_t a, pv_t b) +{ + if (a.kind != b.kind) + return 0; + + switch (a.kind) + { + case pvk_unknown: + return 1; + case pvk_constant: + return (a.k == b.k); + case pvk_register: + return (a.reg == b.reg && a.k == b.k); + default: + gdb_assert (0); + } +} + + +int +pv_is_constant (pv_t a) +{ + return (a.kind == pvk_constant); +} + + +int +pv_is_register (pv_t a, int r) +{ + return (a.kind == pvk_register + && a.reg == r); +} + + +int +pv_is_register_k (pv_t a, int r, CORE_ADDR k) +{ + return (a.kind == pvk_register + && a.reg == r + && a.k == k); +} + + +enum pv_boolean +pv_is_array_ref (pv_t addr, CORE_ADDR size, + pv_t array_addr, CORE_ADDR array_len, + CORE_ADDR elt_size, + int *i) +{ + /* Note that, since .k is a CORE_ADDR, and CORE_ADDR is unsigned, if + addr is *before* the start of the array, then this isn't going to + be negative... */ + pv_t offset = pv_subtract (addr, array_addr); + + if (offset.kind == pvk_constant) + { + /* This is a rather odd test. We want to know if the SIZE bytes + at ADDR don't overlap the array at all, so you'd expect it to + be an || expression: "if we're completely before || we're + completely after". But with unsigned arithmetic, things are + different: since it's a number circle, not a number line, the + right values for offset.k are actually one contiguous range. */ + if (offset.k <= -size + && offset.k >= array_len * elt_size) + return pv_definite_no; + else if (offset.k % elt_size != 0 + || size != elt_size) + return pv_maybe; + else + { + *i = offset.k / elt_size; + return pv_definite_yes; + } + } + else + return pv_maybe; +} + + + +/* Areas. */ + + +/* A particular value known to be stored in an area. + + Entries form a ring, sorted by unsigned offset from the area's base + register's value. Since entries can straddle the wrap-around point, + unsigned offsets form a circle, not a number line, so the list + itself is structured the same way --- there is no inherent head. + The entry with the lowest offset simply follows the entry with the + highest offset. Entries may abut, but never overlap. The area's + 'entry' pointer points to an arbitrary node in the ring. */ +struct area_entry +{ + /* Links in the doubly-linked ring. */ + struct area_entry *prev, *next; + + /* Offset of this entry's address from the value of the base + register. */ + CORE_ADDR offset; + + /* The size of this entry. Note that an entry may wrap around from + the end of the address space to the beginning. */ + CORE_ADDR size; + + /* The value stored here. */ + pv_t value; +}; + + +struct pv_area +{ + /* This area's base register. */ + int base_reg; + + /* The mask to apply to addresses, to make the wrap-around happen at + the right place. */ + CORE_ADDR addr_mask; + + /* An element of the doubly-linked ring of entries, or zero if we + have none. */ + struct area_entry *entry; +}; + + +struct pv_area * +make_pv_area (int base_reg) +{ + struct pv_area *a = (struct pv_area *) xmalloc (sizeof (*a)); + + memset (a, 0, sizeof (*a)); + + a->base_reg = base_reg; + a->entry = 0; + + /* Remember that shift amounts equal to the type's width are + undefined. */ + a->addr_mask = ((((CORE_ADDR) 1 << (TARGET_ADDR_BIT - 1)) - 1) << 1) | 1; + + return a; +} + + +/* Delete all entries from AREA. */ +static void +clear_entries (struct pv_area *area) +{ + struct area_entry *e = area->entry; + + if (e) + { + /* This needs to be a do-while loop, in order to actually + process the node being checked for in the terminating + condition. */ + do + { + struct area_entry *next = e->next; + xfree (e); + } + while (e != area->entry); + + area->entry = 0; + } +} + + +void +free_pv_area (struct pv_area *area) +{ + clear_entries (area); + xfree (area); +} + + +static void +do_free_pv_area_cleanup (void *arg) +{ + free_pv_area ((struct pv_area *) arg); +} + + +struct cleanup * +make_cleanup_free_pv_area (struct pv_area *area) +{ + return make_cleanup (do_free_pv_area_cleanup, (void *) area); +} + + +int +pv_area_store_would_trash (struct pv_area *area, pv_t addr) +{ + /* It may seem odd that pvk_constant appears here --- after all, + that's the case where we know the most about the address! But + pv_areas are always relative to a register, and we don't know the + value of the register, so we can't compare entry addresses to + constants. */ + return (addr.kind == pvk_unknown + || addr.kind == pvk_constant + || (addr.kind == pvk_register && addr.reg != area->base_reg)); +} + + +/* Return a pointer to the first entry we hit in AREA starting at + OFFSET and going forward. + + This may return zero, if AREA has no entries. + + And since the entries are a ring, this may return an entry that + entirely preceeds OFFSET. This is the correct behavior: depending + on the sizes involved, we could still overlap such an area, with + wrap-around. */ +static struct area_entry * +find_entry (struct pv_area *area, CORE_ADDR offset) +{ + struct area_entry *e = area->entry; + + if (! e) + return 0; + + /* If the next entry would be better than the current one, then scan + forward. Since we use '<' in this loop, it always terminates. + + Note that, even setting aside the addr_mask stuff, we must not + simplify this, in high school algebra fashion, to + (e->next->offset < e->offset), because of the way < interacts + with wrap-around. We have to subtract offset from both sides to + make sure both things we're comparing are on the same side of the + discontinuity. */ + while (((e->next->offset - offset) & area->addr_mask) + < ((e->offset - offset) & area->addr_mask)) + e = e->next; + + /* If the previous entry would be better than the current one, then + scan backwards. */ + while (((e->prev->offset - offset) & area->addr_mask) + < ((e->offset - offset) & area->addr_mask)) + e = e->prev; + + /* In case there's some locality to the searches, set the area's + pointer to the entry we've found. */ + area->entry = e; + + return e; +} + + +/* Return non-zero if the SIZE bytes at OFFSET would overlap ENTRY; + return zero otherwise. AREA is the area to which ENTRY belongs. */ +static int +overlaps (struct pv_area *area, + struct area_entry *entry, + CORE_ADDR offset, + CORE_ADDR size) +{ + /* Think carefully about wrap-around before simplifying this. */ + return (((entry->offset - offset) & area->addr_mask) < size + || ((offset - entry->offset) & area->addr_mask) < entry->size); +} + + +void +pv_area_store (struct pv_area *area, + pv_t addr, + CORE_ADDR size, + pv_t value) +{ + /* Remove any (potentially) overlapping entries. */ + if (pv_area_store_would_trash (area, addr)) + clear_entries (area); + else + { + CORE_ADDR offset = addr.k; + struct area_entry *e = find_entry (area, offset); + + /* Delete all entries that we would overlap. */ + while (e && overlaps (area, e, offset, size)) + { + struct area_entry *next = (e->next == e) ? 0 : e->next; + e->prev->next = e->next; + e->next->prev = e->prev; + + xfree (e); + e = next; + } + + /* Move the area's pointer to the next remaining entry. This + will also zero the pointer if we've deleted all the entries. */ + area->entry = e; + } + + /* Now, there are no entries overlapping us, and area->entry is + either zero or pointing at the closest entry after us. We can + just insert ourselves before that. + + But if we're storing an unknown value, don't bother --- that's + the default. */ + if (value.kind == pvk_unknown) + return; + else + { + CORE_ADDR offset = addr.k; + struct area_entry *e = (struct area_entry *) xmalloc (sizeof (*e)); + e->offset = offset; + e->size = size; + e->value = value; + + if (area->entry) + { + e->prev = area->entry->prev; + e->next = area->entry; + e->prev->next = e->next->prev = e; + } + else + { + e->prev = e->next = e; + area->entry = e; + } + } +} + + +pv_t +pv_area_fetch (struct pv_area *area, pv_t addr, CORE_ADDR size) +{ + /* If we have no entries, or we can't decide how ADDR relates to the + entries we do have, then the value is unknown. */ + if (! area->entry + || pv_area_store_would_trash (area, addr)) + return pv_unknown (); + else + { + CORE_ADDR offset = addr.k; + struct area_entry *e = find_entry (area, offset); + + /* If this entry exactly matches what we're looking for, then + we're set. Otherwise, say it's unknown. */ + if (e->offset == offset && e->size == size) + return e->value; + else + return pv_unknown (); + } +} + + +int +pv_area_find_reg (struct pv_area *area, + struct gdbarch *gdbarch, + int reg, + CORE_ADDR *offset_p) +{ + struct area_entry *e = area->entry; + + if (e) + do + { + if (e->value.kind == pvk_register + && e->value.reg == reg + && e->value.k == 0 + && e->size == register_size (gdbarch, reg)) + { + if (offset_p) + *offset_p = e->offset; + return 1; + } + + e = e->next; + } + while (e != area->entry); + + return 0; +} + + +void +pv_area_scan (struct pv_area *area, + void (*func) (void *closure, + pv_t addr, + CORE_ADDR size, + pv_t value), + void *closure) +{ + struct area_entry *e = area->entry; + pv_t addr; + + addr.kind = pvk_register; + addr.reg = area->base_reg; + + if (e) + do + { + addr.k = e->offset; + func (closure, addr, e->size, e->value); + e = e->next; + } + while (e != area->entry); +} |