X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fdwarf2-frame.c;h=52ca337cc8b8545b4809b90c7e9bc2266a997eaf;hb=2d92b4e1632aaedb76f94c43b04ca0551f66204b;hp=999cef7e6cdb6114d8d16ef88145d9f9c1b23ba4;hpb=b9362cc7a8079dd0809070cfd94e94097fa7b6d0;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/dwarf2-frame.c b/gdb/dwarf2-frame.c index 999cef7e6c..52ca337cc8 100644 --- a/gdb/dwarf2-frame.c +++ b/gdb/dwarf2-frame.c @@ -1,6 +1,6 @@ /* Frame unwinder for frames with DWARF Call Frame Information. - Copyright 2003 Free Software Foundation, Inc. + Copyright 2003, 2004 Free Software Foundation, Inc. Contributed by Mark Kettenis. @@ -36,6 +36,7 @@ #include "gdb_assert.h" #include "gdb_string.h" +#include "complaints.h" #include "dwarf2-frame.h" /* Call Frame Information (CFI). */ @@ -102,22 +103,7 @@ struct dwarf2_frame_state another register, or a location expression. */ struct dwarf2_frame_state_reg_info { - struct dwarf2_frame_state_reg - { - union { - LONGEST offset; - ULONGEST reg; - unsigned char *exp; - } loc; - ULONGEST exp_len; - enum { - REG_UNSAVED, - REG_SAVED_OFFSET, - REG_SAVED_REG, - REG_SAVED_EXP, - REG_UNMODIFIED - } how; - } *reg; + struct dwarf2_frame_state_reg *reg; int num_regs; /* Used to implement DW_CFA_remember_state. */ @@ -220,12 +206,13 @@ static CORE_ADDR read_reg (void *baton, int reg) { struct frame_info *next_frame = (struct frame_info *) baton; + struct gdbarch *gdbarch = get_frame_arch (next_frame); int regnum; char *buf; regnum = DWARF2_REG_TO_REGNUM (reg); - buf = (char *) alloca (register_size (current_gdbarch, regnum)); + buf = (char *) alloca (register_size (gdbarch, regnum)); frame_unwind_register (next_frame, regnum, buf); return extract_typed_address (buf, builtin_type_void_data_ptr); } @@ -299,7 +286,7 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp); offset = utmp * fs->data_align; dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); - fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_OFFSET; fs->regs.reg[reg].loc.offset = offset; } else if ((insn & 0xc0) == DW_CFA_restore) @@ -339,7 +326,7 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp); offset = utmp * fs->data_align; dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); - fs->regs.reg[reg].how = REG_SAVED_OFFSET; + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_OFFSET; fs->regs.reg[reg].loc.offset = offset; break; @@ -353,19 +340,20 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, case DW_CFA_undefined: insn_ptr = read_uleb128 (insn_ptr, insn_end, ®); dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); - fs->regs.reg[reg].how = REG_UNSAVED; + fs->regs.reg[reg].how = DWARF2_FRAME_REG_UNDEFINED; break; case DW_CFA_same_value: insn_ptr = read_uleb128 (insn_ptr, insn_end, ®); dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); - fs->regs.reg[reg].how = REG_UNMODIFIED; + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAME_VALUE; break; case DW_CFA_register: insn_ptr = read_uleb128 (insn_ptr, insn_end, ®); insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp); dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_REG; fs->regs.reg[reg].loc.reg = utmp; break; @@ -384,11 +372,17 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, { struct dwarf2_frame_state_reg_info *old_rs = fs->regs.prev; - gdb_assert (old_rs); - - xfree (fs->regs.reg); - fs->regs = *old_rs; - xfree (old_rs); + if (old_rs == NULL) + { + complaint (&symfile_complaints, "\ +bad CFI data; mismatched DW_CFA_restore_state at 0x%s", paddr (fs->pc)); + } + else + { + xfree (fs->regs.reg); + fs->regs = *old_rs; + xfree (old_rs); + } } break; @@ -409,6 +403,9 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, /* cfa_how deliberately not set. */ break; + case DW_CFA_nop: + break; + case DW_CFA_def_cfa_expression: insn_ptr = read_uleb128 (insn_ptr, insn_end, &fs->cfa_exp_len); fs->cfa_exp = insn_ptr; @@ -422,11 +419,30 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, insn_ptr = read_uleb128 (insn_ptr, insn_end, &utmp); fs->regs.reg[reg].loc.exp = insn_ptr; fs->regs.reg[reg].exp_len = utmp; - fs->regs.reg[reg].how = REG_SAVED_EXP; + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_EXP; insn_ptr += utmp; break; - case DW_CFA_nop: + case DW_CFA_offset_extended_sf: + insn_ptr = read_uleb128 (insn_ptr, insn_end, ®); + insn_ptr = read_sleb128 (insn_ptr, insn_end, &offset); + offset += fs->data_align; + dwarf2_frame_state_alloc_regs (&fs->regs, reg + 1); + fs->regs.reg[reg].how = DWARF2_FRAME_REG_SAVED_OFFSET; + fs->regs.reg[reg].loc.offset = offset; + break; + + case DW_CFA_def_cfa_sf: + insn_ptr = read_uleb128 (insn_ptr, insn_end, &fs->cfa_reg); + insn_ptr = read_sleb128 (insn_ptr, insn_end, &offset); + fs->cfa_offset = offset * fs->data_align; + fs->cfa_how = CFA_REG_OFFSET; + break; + + case DW_CFA_def_cfa_offset_sf: + insn_ptr = read_sleb128 (insn_ptr, insn_end, &offset); + fs->cfa_offset = offset * fs->data_align; + /* cfa_how deliberately not set. */ break; case DW_CFA_GNU_args_size: @@ -444,6 +460,94 @@ execute_cfa_program (unsigned char *insn_ptr, unsigned char *insn_end, dwarf2_frame_state_free_regs (fs->regs.prev); fs->regs.prev = NULL; } + + +/* Architecture-specific operations. */ + +/* Per-architecture data key. */ +static struct gdbarch_data *dwarf2_frame_data; + +struct dwarf2_frame_ops +{ + /* Pre-initialize the register state REG for register REGNUM. */ + void (*init_reg) (struct gdbarch *, int, struct dwarf2_frame_state_reg *); +}; + +/* Default architecture-specific register state initialization + function. */ + +static void +dwarf2_frame_default_init_reg (struct gdbarch *gdbarch, int regnum, + struct dwarf2_frame_state_reg *reg) +{ + /* If we have a register that acts as a program counter, mark it as + a destination for the return address. If we have a register that + serves as the stack pointer, arrange for it to be filled with the + call frame address (CFA). The other registers are marked as + unspecified. + + We copy the return address to the program counter, since many + parts in GDB assume that it is possible to get the return address + by unwinding the program counter register. However, on ISA's + with a dedicated return address register, the CFI usually only + contains information to unwind that return address register. + + The reason we're treating the stack pointer special here is + because in many cases GCC doesn't emit CFI for the stack pointer + and implicitly assumes that it is equal to the CFA. This makes + some sense since the DWARF specification (version 3, draft 8, + p. 102) says that: + + "Typically, the CFA is defined to be the value of the stack + pointer at the call site in the previous frame (which may be + different from its value on entry to the current frame)." + + However, this isn't true for all platforms supported by GCC + (e.g. IBM S/390 and zSeries). Those architectures should provide + their own architecture-specific initialization function. */ + + if (regnum == PC_REGNUM) + reg->how = DWARF2_FRAME_REG_RA; + else if (regnum == SP_REGNUM) + reg->how = DWARF2_FRAME_REG_CFA; +} + +/* Return a default for the architecture-specific operations. */ + +static void * +dwarf2_frame_init (struct obstack *obstack) +{ + struct dwarf2_frame_ops *ops; + + ops = OBSTACK_ZALLOC (obstack, struct dwarf2_frame_ops); + ops->init_reg = dwarf2_frame_default_init_reg; + return ops; +} + +/* Set the architecture-specific register state initialization + function for GDBARCH to INIT_REG. */ + +void +dwarf2_frame_set_init_reg (struct gdbarch *gdbarch, + void (*init_reg) (struct gdbarch *, int, + struct dwarf2_frame_state_reg *)) +{ + struct dwarf2_frame_ops *ops = gdbarch_data (gdbarch, dwarf2_frame_data); + + ops->init_reg = init_reg; +} + +/* Pre-initialize the register state REG for register REGNUM. */ + +static void +dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, + struct dwarf2_frame_state_reg *reg) +{ + struct dwarf2_frame_ops *ops = gdbarch_data (gdbarch, dwarf2_frame_data); + + ops->init_reg (gdbarch, regnum, reg); +} + struct dwarf2_frame_cache { @@ -459,11 +563,11 @@ static struct dwarf2_frame_cache * dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache) { struct cleanup *old_chain; - int num_regs = NUM_REGS + NUM_PSEUDO_REGS; + struct gdbarch *gdbarch = get_frame_arch (next_frame); + const int num_regs = NUM_REGS + NUM_PSEUDO_REGS; struct dwarf2_frame_cache *cache; struct dwarf2_frame_state *fs; struct dwarf2_fde *fde; - int reg; if (*this_cache) return *this_cache; @@ -483,22 +587,16 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache) to abort), the compiler might optimize away the instruction at NEXT_FRAME's return address. As a result the return address will point at some random instruction, and the CFI for that - instruction is probably wortless to us. GCC's unwinder solves + instruction is probably worthless to us. GCC's unwinder solves this problem by substracting 1 from the return address to get an address in the middle of a presumed call instruction (or the instruction in the associated delay slot). This should only be done for "normal" frames and not for resume-type frames (signal - handlers, sentinel frames, dummy frames). - - We don't do what GCC's does here (yet). It's not clear how - reliable the method is. There's also a problem with finding the - right FDE; see the comment in dwarf_frame_p. If dwarf_frame_p - selected this frame unwinder because it found the FDE for the - next function, using the adjusted return address might not yield - a FDE at all. The problem isn't specific to DWARF CFI; other - unwinders loose in similar ways. Therefore it's probably - acceptable to leave things slightly broken for now. */ - fs->pc = frame_pc_unwind (next_frame); + handlers, sentinel frames, dummy frames). The function + frame_unwind_address_in_block does just this. It's not clear how + reliable the method is though; there is the potential for the + register state pre-call being different to that on return. */ + fs->pc = frame_unwind_address_in_block (next_frame); /* Find the correct FDE. */ fde = dwarf2_frame_find_fde (&fs->pc); @@ -537,54 +635,83 @@ dwarf2_frame_cache (struct frame_info *next_frame, void **this_cache) internal_error (__FILE__, __LINE__, "Unknown CFA rule."); } - /* Save the register info in the cache. */ - for (reg = 0; reg < fs->regs.num_regs; reg++) - { - int regnum; - - /* Skip the return address column. */ - if (reg == fs->retaddr_column) - /* NOTE: cagney/2003-06-07: Is this right? What if the - RETADDR_COLUM corresponds to a real register (and, worse, - that isn't the PC_REGNUM)? I'm guessing that the PC_REGNUM - further down is trying to handle this. That can't be right - though - PC_REGNUM may not be valid (it can be -ve). I - think, instead when RETADDR_COLUM isn't a real register, it - should map itself onto frame_pc_unwind. */ - continue; - - /* Use the GDB register number as index. */ - regnum = DWARF2_REG_TO_REGNUM (reg); - - if (regnum >= 0 && regnum < num_regs) - cache->reg[regnum] = fs->regs.reg[reg]; - } - - /* Store the location of the return addess. If the return address - column (adjusted) is not the same as gdb's PC_REGNUM, then this - implies a copy from the ra column register. */ - if (fs->retaddr_column < fs->regs.num_regs - && fs->regs.reg[fs->retaddr_column].how != REG_UNSAVED) - { - /* See comment above about a possibly -ve PC_REGNUM. If this - assertion fails, it's a problem with this code and not the - architecture. */ - gdb_assert (PC_REGNUM >= 0); - cache->reg[PC_REGNUM] = fs->regs.reg[fs->retaddr_column]; - } - else - { - reg = DWARF2_REG_TO_REGNUM (fs->retaddr_column); - if (reg != PC_REGNUM) - { - /* See comment above about PC_REGNUM being -ve. If this - assertion fails, it's a problem with this code and not - the architecture. */ - gdb_assert (PC_REGNUM >= 0); - cache->reg[PC_REGNUM].loc.reg = reg; - cache->reg[PC_REGNUM].how = REG_SAVED_REG; - } - } + /* Initialize the register state. */ + { + int regnum; + + for (regnum = 0; regnum < num_regs; regnum++) + dwarf2_frame_init_reg (gdbarch, regnum, &cache->reg[regnum]); + } + + /* Go through the DWARF2 CFI generated table and save its register + location information in the cache. Note that we don't skip the + return address column; it's perfectly all right for it to + correspond to a real register. If it doesn't correspond to a + real register, or if we shouldn't treat it as such, + DWARF2_REG_TO_REGNUM should be defined to return a number outside + the range [0, NUM_REGS). */ + { + int column; /* CFI speak for "register number". */ + + for (column = 0; column < fs->regs.num_regs; column++) + { + /* Use the GDB register number as the destination index. */ + int regnum = DWARF2_REG_TO_REGNUM (column); + + /* If there's no corresponding GDB register, ignore it. */ + if (regnum < 0 || regnum >= num_regs) + continue; + + /* NOTE: cagney/2003-09-05: CFI should specify the disposition + of all debug info registers. If it doesn't, complain (but + not too loudly). It turns out that GCC assumes that an + unspecified register implies "same value" when CFI (draft + 7) specifies nothing at all. Such a register could equally + be interpreted as "undefined". Also note that this check + isn't sufficient; it only checks that all registers in the + range [0 .. max column] are specified, and won't detect + problems when a debug info register falls outside of the + table. We need a way of iterating through all the valid + DWARF2 register numbers. */ + if (fs->regs.reg[column].how == DWARF2_FRAME_REG_UNSPECIFIED) + complaint (&symfile_complaints, + "Incomplete CFI data; unspecified registers at 0x%s", + paddr (fs->pc)); + else + cache->reg[regnum] = fs->regs.reg[column]; + } + } + + /* Eliminate any DWARF2_FRAME_REG_RA rules. */ + { + int regnum; + + for (regnum = 0; regnum < num_regs; regnum++) + { + if (cache->reg[regnum].how == DWARF2_FRAME_REG_RA) + { + struct dwarf2_frame_state_reg *retaddr_reg = + &fs->regs.reg[fs->retaddr_column]; + + /* It seems rather bizarre to specify an "empty" column as + the return adress column. However, this is exactly + what GCC does on some targets. It turns out that GCC + assumes that the return address can be found in the + register corresponding to the return address column. + Incidentally, that's how should treat a return address + column specifying "same value" too. */ + if (fs->retaddr_column < fs->regs.num_regs + && retaddr_reg->how != DWARF2_FRAME_REG_UNSPECIFIED + && retaddr_reg->how != DWARF2_FRAME_REG_SAME_VALUE) + cache->reg[regnum] = *retaddr_reg; + else + { + cache->reg[regnum].loc.reg = fs->retaddr_column; + cache->reg[regnum].how = DWARF2_FRAME_REG_SAVED_REG; + } + } + } + } do_cleanups (old_chain); @@ -608,45 +735,20 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache, enum lval_type *lvalp, CORE_ADDR *addrp, int *realnump, void *valuep) { + struct gdbarch *gdbarch = get_frame_arch (next_frame); struct dwarf2_frame_cache *cache = dwarf2_frame_cache (next_frame, this_cache); switch (cache->reg[regnum].how) { - case REG_UNSAVED: + case DWARF2_FRAME_REG_UNDEFINED: + /* If CFI explicitly specified that the value isn't defined, + mark it as optimized away; the value isn't available. */ *optimizedp = 1; *lvalp = not_lval; *addrp = 0; *realnump = -1; - if (regnum == SP_REGNUM) - { - /* GCC defines the CFA as the value of the stack pointer - just before the call instruction is executed. Do other - compilers use the same definition? */ - /* DWARF V3 Draft 7 p102: Typically, the CFA is defined to - be the value of the stack pointer at the call site in the - previous frame (which may be different from its value on - entry to the current frame). */ - /* DWARF V3 Draft 7 p103: The first column of the rules - defines the rule which computes the CFA value; it may be - either a register and a signed offset that are added - together or a DWARF expression that is evaluated. */ - /* FIXME: cagney/2003-07-07: I don't understand this. The - CFI info should have provided unwind information for the - SP register and then pointed ->cfa_reg at it, not the - reverse. Assuming that SP_REGNUM is !-ve, there is a - very real posibility that CFA is an offset from some - other register, having nothing to do with the unwound SP - value. */ - *optimizedp = 0; - if (valuep) - { - /* Store the value. */ - store_typed_address (valuep, builtin_type_void_data_ptr, - cache->cfa); - } - } - else if (valuep) + if (valuep) { /* In some cases, for example %eflags on the i386, we have to provide a sane value, even though this register wasn't @@ -655,7 +757,7 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache, } break; - case REG_SAVED_OFFSET: + case DWARF2_FRAME_REG_SAVED_OFFSET: *optimizedp = 0; *lvalp = lval_memory; *addrp = cache->cfa + cache->reg[regnum].loc.offset; @@ -663,18 +765,17 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache, if (valuep) { /* Read the value in from memory. */ - read_memory (*addrp, valuep, - register_size (current_gdbarch, regnum)); + read_memory (*addrp, valuep, register_size (gdbarch, regnum)); } break; - case REG_SAVED_REG: + case DWARF2_FRAME_REG_SAVED_REG: regnum = DWARF2_REG_TO_REGNUM (cache->reg[regnum].loc.reg); frame_register_unwind (next_frame, regnum, optimizedp, lvalp, addrp, realnump, valuep); break; - case REG_SAVED_EXP: + case DWARF2_FRAME_REG_SAVED_EXP: *optimizedp = 0; *lvalp = lval_memory; *addrp = execute_stack_op (cache->reg[regnum].loc.exp, @@ -684,16 +785,39 @@ dwarf2_frame_prev_register (struct frame_info *next_frame, void **this_cache, if (valuep) { /* Read the value in from memory. */ - read_memory (*addrp, valuep, - register_size (current_gdbarch, regnum)); + read_memory (*addrp, valuep, register_size (gdbarch, regnum)); } break; - case REG_UNMODIFIED: + case DWARF2_FRAME_REG_UNSPECIFIED: + /* GCC, in its infinite wisdom decided to not provide unwind + information for registers that are "same value". Since + DWARF2 (3 draft 7) doesn't define such behavior, said + registers are actually undefined (which is different to CFI + "undefined"). Code above issues a complaint about this. + Here just fudge the books, assume GCC, and that the value is + more inner on the stack. */ frame_register_unwind (next_frame, regnum, optimizedp, lvalp, addrp, realnump, valuep); break; + case DWARF2_FRAME_REG_SAME_VALUE: + frame_register_unwind (next_frame, regnum, + optimizedp, lvalp, addrp, realnump, valuep); + break; + + case DWARF2_FRAME_REG_CFA: + *optimizedp = 0; + *lvalp = not_lval; + *addrp = 0; + *realnump = -1; + if (valuep) + { + /* Store the value. */ + store_typed_address (valuep, builtin_type_void_data_ptr, cache->cfa); + } + break; + default: internal_error (__FILE__, __LINE__, "Unknown register rule."); } @@ -707,16 +831,13 @@ static const struct frame_unwind dwarf2_frame_unwind = }; const struct frame_unwind * -dwarf2_frame_p (CORE_ADDR pc) +dwarf2_frame_sniffer (struct frame_info *next_frame) { - /* The way GDB works, this function can be called with PC just after - the last instruction of the function we're supposed to return the - unwind methods for. In that case we won't find the correct FDE; - instead we find the FDE for the next function, or we won't find - an FDE at all. There is a possible solution (see the comment in - dwarf2_frame_cache), GDB doesn't pass us enough information to - implement it. */ - if (dwarf2_frame_find_fde (&pc)) + /* Grab an address that is guarenteed to reside somewhere within the + function. frame_pc_unwind(), for a no-return next function, can + end up returning something past the end of this function's body. */ + CORE_ADDR block_addr = frame_unwind_address_in_block (next_frame); + if (dwarf2_frame_find_fde (&block_addr)) return &dwarf2_frame_unwind; return NULL; @@ -747,8 +868,9 @@ static const struct frame_base dwarf2_frame_base = }; const struct frame_base * -dwarf2_frame_base_p (CORE_ADDR pc) +dwarf2_frame_base_sniffer (struct frame_info *next_frame) { + CORE_ADDR pc = frame_pc_unwind (next_frame); if (dwarf2_frame_find_fde (&pc)) return &dwarf2_frame_base; @@ -782,8 +904,13 @@ struct comp_unit /* Base for DW_EH_PE_datarel encodings. */ bfd_vma dbase; + + /* Base for DW_EH_PE_textrel encodings. */ + bfd_vma tbase; }; +const struct objfile_data *dwarf2_frame_objfile_data; + static unsigned int read_1_byte (bfd *bfd, char *buf) { @@ -934,6 +1061,8 @@ static CORE_ADDR read_encoded_value (struct comp_unit *unit, unsigned char encoding, char *buf, unsigned int *bytes_read_ptr) { + int ptr_len = size_of_encoded_value (DW_EH_PE_absptr); + ptrdiff_t offset; CORE_ADDR base; /* GCC currently doesn't generate DW_EH_PE_indirect encodings for @@ -942,6 +1071,8 @@ read_encoded_value (struct comp_unit *unit, unsigned char encoding, internal_error (__FILE__, __LINE__, "Unsupported encoding: DW_EH_PE_indirect"); + *bytes_read_ptr = 0; + switch (encoding & 0x70) { case DW_EH_PE_absptr: @@ -954,32 +1085,52 @@ read_encoded_value (struct comp_unit *unit, unsigned char encoding, case DW_EH_PE_datarel: base = unit->dbase; break; + case DW_EH_PE_textrel: + base = unit->tbase; + break; + case DW_EH_PE_funcrel: + /* FIXME: kettenis/20040501: For now just pretend + DW_EH_PE_funcrel is equivalent to DW_EH_PE_absptr. For + reading the initial location of an FDE it should be treated + as such, and currently that's the only place where this code + is used. */ + base = 0; + break; + case DW_EH_PE_aligned: + base = 0; + offset = buf - unit->dwarf_frame_buffer; + if ((offset % ptr_len) != 0) + { + *bytes_read_ptr = ptr_len - (offset % ptr_len); + buf += *bytes_read_ptr; + } + break; default: internal_error (__FILE__, __LINE__, "Invalid or unsupported encoding"); } if ((encoding & 0x0f) == 0x00) - encoding |= encoding_for_size (TYPE_LENGTH(builtin_type_void_data_ptr)); + encoding |= encoding_for_size (ptr_len); switch (encoding & 0x0f) { case DW_EH_PE_udata2: - *bytes_read_ptr = 2; + *bytes_read_ptr += 2; return (base + bfd_get_16 (unit->abfd, (bfd_byte *) buf)); case DW_EH_PE_udata4: - *bytes_read_ptr = 4; + *bytes_read_ptr += 4; return (base + bfd_get_32 (unit->abfd, (bfd_byte *) buf)); case DW_EH_PE_udata8: - *bytes_read_ptr = 8; + *bytes_read_ptr += 8; return (base + bfd_get_64 (unit->abfd, (bfd_byte *) buf)); case DW_EH_PE_sdata2: - *bytes_read_ptr = 2; + *bytes_read_ptr += 2; return (base + bfd_get_signed_16 (unit->abfd, (bfd_byte *) buf)); case DW_EH_PE_sdata4: - *bytes_read_ptr = 4; + *bytes_read_ptr += 4; return (base + bfd_get_signed_32 (unit->abfd, (bfd_byte *) buf)); case DW_EH_PE_sdata8: - *bytes_read_ptr = 8; + *bytes_read_ptr += 8; return (base + bfd_get_signed_64 (unit->abfd, (bfd_byte *) buf)); default: internal_error (__FILE__, __LINE__, "Invalid or unsupported encoding"); @@ -1026,9 +1177,13 @@ dwarf2_frame_find_fde (CORE_ADDR *pc) struct dwarf2_fde *fde; CORE_ADDR offset; + fde = objfile_data (objfile, dwarf2_frame_objfile_data); + if (fde == NULL) + continue; + + gdb_assert (objfile->section_offsets); offset = ANOFFSET (objfile->section_offsets, SECT_OFF_TEXT (objfile)); - - fde = objfile->sym_private; + while (fde) { if (*pc >= fde->initial_location + offset @@ -1048,8 +1203,8 @@ dwarf2_frame_find_fde (CORE_ADDR *pc) static void add_fde (struct comp_unit *unit, struct dwarf2_fde *fde) { - fde->next = unit->objfile->sym_private; - unit->objfile->sym_private = fde; + fde->next = objfile_data (unit->objfile, dwarf2_frame_objfile_data); + set_objfile_data (unit->objfile, dwarf2_frame_objfile_data, fde); } #ifdef CC_HAS_LONG_LONG @@ -1058,35 +1213,44 @@ add_fde (struct comp_unit *unit, struct dwarf2_fde *fde) #define DW64_CIE_ID ~0 #endif -/* Read a CIE or FDE in BUF and decode it. */ +static char *decode_frame_entry (struct comp_unit *unit, char *start, + int eh_frame_p); +/* Decode the next CIE or FDE. Return NULL if invalid input, otherwise + the next byte to be processed. */ static char * -decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) +decode_frame_entry_1 (struct comp_unit *unit, char *start, int eh_frame_p) { + char *buf; LONGEST length; unsigned int bytes_read; - int dwarf64_p = 0; - ULONGEST cie_id = DW_CIE_ID; + int dwarf64_p; + ULONGEST cie_id; ULONGEST cie_pointer; - char *start = buf; char *end; + buf = start; length = read_initial_length (unit->abfd, buf, &bytes_read); buf += bytes_read; end = buf + length; + /* Are we still within the section? */ + if (end > unit->dwarf_frame_buffer + unit->dwarf_frame_size) + return NULL; + if (length == 0) return end; - if (bytes_read == 12) - dwarf64_p = 1; + /* Distinguish between 32 and 64-bit encoded frame info. */ + dwarf64_p = (bytes_read == 12); - /* In a .eh_frame section, zero is used to distinguish CIEs from - FDEs. */ + /* In a .eh_frame section, zero is used to distinguish CIEs from FDEs. */ if (eh_frame_p) cie_id = 0; else if (dwarf64_p) cie_id = DW64_CIE_ID; + else + cie_id = DW_CIE_ID; if (dwarf64_p) { @@ -1104,6 +1268,7 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) /* This is a CIE. */ struct dwarf2_cie *cie; char *augmentation; + unsigned int cie_version; /* Record the offset into the .debug_frame section of this CIE. */ cie_pointer = start - unit->dwarf_frame_buffer; @@ -1113,7 +1278,7 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) return end; cie = (struct dwarf2_cie *) - obstack_alloc (&unit->objfile->psymbol_obstack, + obstack_alloc (&unit->objfile->objfile_obstack, sizeof (struct dwarf2_cie)); cie->initial_instructions = NULL; cie->cie_pointer = cie_pointer; @@ -1124,7 +1289,9 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) cie->encoding = encoding_for_size (unit->addr_size); /* Check version number. */ - gdb_assert (read_1_byte (unit->abfd, buf) == DW_CIE_VERSION); + cie_version = read_1_byte (unit->abfd, buf); + if (cie_version != 1 && cie_version != 3) + return NULL; buf += 1; /* Interpret the interesting bits of the augmentation. */ @@ -1149,8 +1316,15 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) read_signed_leb128 (unit->abfd, buf, &bytes_read); buf += bytes_read; - cie->return_address_register = read_1_byte (unit->abfd, buf); - buf += 1; + if (cie_version == 1) + { + cie->return_address_register = read_1_byte (unit->abfd, buf); + bytes_read = 1; + } + else + cie->return_address_register = read_unsigned_leb128 (unit->abfd, buf, + &bytes_read); + buf += bytes_read; cie->saw_z_augmentation = (*augmentation == 'z'); if (cie->saw_z_augmentation) @@ -1159,6 +1333,8 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) length = read_unsigned_leb128 (unit->abfd, buf, &bytes_read); buf += bytes_read; + if (buf > end) + return NULL; cie->initial_instructions = buf + length; augmentation++; } @@ -1211,18 +1387,22 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) /* This is a FDE. */ struct dwarf2_fde *fde; + /* In an .eh_frame section, the CIE pointer is the delta between the + address within the FDE where the CIE pointer is stored and the + address of the CIE. Convert it to an offset into the .eh_frame + section. */ if (eh_frame_p) { - /* In an .eh_frame section, the CIE pointer is the delta - between the address within the FDE where the CIE pointer - is stored and the address of the CIE. Convert it to an - offset into the .eh_frame section. */ cie_pointer = buf - unit->dwarf_frame_buffer - cie_pointer; cie_pointer -= (dwarf64_p ? 8 : 4); } + /* In either case, validate the result is still within the section. */ + if (cie_pointer >= unit->dwarf_frame_size) + return NULL; + fde = (struct dwarf2_fde *) - obstack_alloc (&unit->objfile->psymbol_obstack, + obstack_alloc (&unit->objfile->objfile_obstack, sizeof (struct dwarf2_fde)); fde->cie = find_cie (unit, cie_pointer); if (fde->cie == NULL) @@ -1252,6 +1432,8 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) length = read_unsigned_leb128 (unit->abfd, buf, &bytes_read); buf += bytes_read + length; + if (buf > end) + return NULL; } fde->instructions = buf; @@ -1262,22 +1444,109 @@ decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p) return end; } + +/* Read a CIE or FDE in BUF and decode it. */ +static char * +decode_frame_entry (struct comp_unit *unit, char *start, int eh_frame_p) +{ + enum { NONE, ALIGN4, ALIGN8, FAIL } workaround = NONE; + char *ret; + const char *msg; + ptrdiff_t start_offset; + + while (1) + { + ret = decode_frame_entry_1 (unit, start, eh_frame_p); + if (ret != NULL) + break; + + /* We have corrupt input data of some form. */ + + /* ??? Try, weakly, to work around compiler/assembler/linker bugs + and mismatches wrt padding and alignment of debug sections. */ + /* Note that there is no requirement in the standard for any + alignment at all in the frame unwind sections. Testing for + alignment before trying to interpret data would be incorrect. + + However, GCC traditionally arranged for frame sections to be + sized such that the FDE length and CIE fields happen to be + aligned (in theory, for performance). This, unfortunately, + was done with .align directives, which had the side effect of + forcing the section to be aligned by the linker. + + This becomes a problem when you have some other producer that + creates frame sections that are not as strictly aligned. That + produces a hole in the frame info that gets filled by the + linker with zeros. + + The GCC behaviour is arguably a bug, but it's effectively now + part of the ABI, so we're now stuck with it, at least at the + object file level. A smart linker may decide, in the process + of compressing duplicate CIE information, that it can rewrite + the entire output section without this extra padding. */ + + start_offset = start - unit->dwarf_frame_buffer; + if (workaround < ALIGN4 && (start_offset & 3) != 0) + { + start += 4 - (start_offset & 3); + workaround = ALIGN4; + continue; + } + if (workaround < ALIGN8 && (start_offset & 7) != 0) + { + start += 8 - (start_offset & 7); + workaround = ALIGN8; + continue; + } + + /* Nothing left to try. Arrange to return as if we've consumed + the entire input section. Hopefully we'll get valid info from + the other of .debug_frame/.eh_frame. */ + workaround = FAIL; + ret = unit->dwarf_frame_buffer + unit->dwarf_frame_size; + break; + } + + switch (workaround) + { + case NONE: + break; + + case ALIGN4: + complaint (&symfile_complaints, + "Corrupt data in %s:%s; align 4 workaround apparently succeeded", + unit->dwarf_frame_section->owner->filename, + unit->dwarf_frame_section->name); + break; + + case ALIGN8: + complaint (&symfile_complaints, + "Corrupt data in %s:%s; align 8 workaround apparently succeeded", + unit->dwarf_frame_section->owner->filename, + unit->dwarf_frame_section->name); + break; + + default: + complaint (&symfile_complaints, + "Corrupt data in %s:%s", + unit->dwarf_frame_section->owner->filename, + unit->dwarf_frame_section->name); + break; + } + + return ret; +} /* FIXME: kettenis/20030504: This still needs to be integrated with dwarf2read.c in a better way. */ /* Imported from dwarf2read.c. */ -extern file_ptr dwarf_frame_offset; -extern unsigned int dwarf_frame_size; extern asection *dwarf_frame_section; -extern file_ptr dwarf_eh_frame_offset; -extern unsigned int dwarf_eh_frame_size; extern asection *dwarf_eh_frame_section; /* Imported from dwarf2read.c. */ -extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset, - unsigned int size, asection *sectp); +extern char *dwarf2_read_section (struct objfile *objfile, asection *sectp); void dwarf2_build_frame_info (struct objfile *objfile) @@ -1290,43 +1559,48 @@ dwarf2_build_frame_info (struct objfile *objfile) unit.objfile = objfile; unit.addr_size = objfile->obfd->arch_info->bits_per_address / 8; unit.dbase = 0; + unit.tbase = 0; /* First add the information from the .eh_frame section. That way, the FDEs from that section are searched last. */ - if (dwarf_eh_frame_offset) + if (dwarf_eh_frame_section) { - asection *got; + asection *got, *txt; unit.cie = NULL; unit.dwarf_frame_buffer = dwarf2_read_section (objfile, - dwarf_eh_frame_offset, - dwarf_eh_frame_size, dwarf_eh_frame_section); - unit.dwarf_frame_size = dwarf_eh_frame_size; + unit.dwarf_frame_size + = bfd_get_section_size_before_reloc (dwarf_eh_frame_section); unit.dwarf_frame_section = dwarf_eh_frame_section; /* FIXME: kettenis/20030602: This is the DW_EH_PE_datarel base - that for the i386/amd64 target, which currently is the only - target in GCC that supports/uses the DW_EH_PE_datarel - encoding. */ + that is used for the i386/amd64 target, which currently is + the only target in GCC that supports/uses the + DW_EH_PE_datarel encoding. */ got = bfd_get_section_by_name (unit.abfd, ".got"); if (got) unit.dbase = got->vma; + /* GCC emits the DW_EH_PE_textrel encoding type on sh and ia64 + so far. */ + txt = bfd_get_section_by_name (unit.abfd, ".text"); + if (txt) + unit.tbase = txt->vma; + frame_ptr = unit.dwarf_frame_buffer; while (frame_ptr < unit.dwarf_frame_buffer + unit.dwarf_frame_size) frame_ptr = decode_frame_entry (&unit, frame_ptr, 1); } - if (dwarf_frame_offset) + if (dwarf_frame_section) { unit.cie = NULL; unit.dwarf_frame_buffer = dwarf2_read_section (objfile, - dwarf_frame_offset, - dwarf_frame_size, dwarf_frame_section); - unit.dwarf_frame_size = dwarf_frame_size; + unit.dwarf_frame_size + = bfd_get_section_size_before_reloc (dwarf_frame_section); unit.dwarf_frame_section = dwarf_frame_section; frame_ptr = unit.dwarf_frame_buffer; @@ -1334,3 +1608,13 @@ dwarf2_build_frame_info (struct objfile *objfile) frame_ptr = decode_frame_entry (&unit, frame_ptr, 0); } } + +/* Provide a prototype to silence -Wmissing-prototypes. */ +void _initialize_dwarf2_frame (void); + +void +_initialize_dwarf2_frame (void) +{ + dwarf2_frame_data = gdbarch_data_register_pre_init (dwarf2_frame_init); + dwarf2_frame_objfile_data = register_objfile_data (); +}