This is the mail archive of the
gdb-patches@sources.redhat.com
mailing list for the GDB project.
Re: [branch patch] dwarf-frame.c support for .eh_frame_hdr
- From: Elena Zannoni <ezannoni at redhat dot com>
- To: Roland McGrath <roland at redhat dot com>
- Cc: Mark Kettenis <kettenis at gnu dot org>, gdb-patches at sources dot redhat dot com
- Date: Mon, 12 May 2003 23:32:39 -0400
- Subject: Re: [branch patch] dwarf-frame.c support for .eh_frame_hdr
- References: <200305120946.h4C9ksj17912@magilla.sf.frob.com>
Roland McGrath writes:
> [Please note that I am not on the mailing list, and keep me CC'd.]
>
> These patches to kettenis_i386newframe-20030419-branch gdb make
> dwarf-frame.c support .eh_frame_hdr format.
>
> This is more complex than what I had anticipated writing. I had expected to
> decode the .eh_frame_hdr contents only to locate the .eh_frame section and
> then just read that as is done when the section is known by name. However,
> AFAICT there is no way to tell from the contents of .eh_frame where it ends.
> When you don't know the exact bounds of the section, you can try to read off
> the end of it and become confused. When you have only .eh_frame_hdr
> information to go on, you know where .eh_frame starts but not where it ends,
> so you don't know how much to decode. The only thing I can find that works
> is to follow the FDE quick-lookup table in the .eh_frame_hdr section,
> reading the FDEs pointed to and the CIEs they point to, rather than reading
> the whole .eh_frame section from start to finish. I implemented that.
> (Though the description of the .eh_frame_hdr format calls the lookup table
> "optional", from looking at the libgcc code, I think it may well wind up
> crashing in some cases if there is an .eh_frame_hdr section without a lookup
> table, because it can read off the end of the .eh_frame section.)
Is there a way to tell if there is a table? I think that if the
fde_count_enc has a value of 0xff (DW_EH_PE_omit) it means that there
is no table. Same for the table_enc field. (If that's what you mean,
that is).
>
> The symfile.c change is necessary for "add-symbol-file" to be usable, which
> is how I tested the new code. The following snippet of a gdb session using
> this patch demonstrates how adding the core file as a symfile and thus
> reading its .eh_frame_hdr table makes backtraces from vsyscall PCs suddenly
> start working right. (I editted out some boring messges.)
>
I see that the symfile.c change is a good way to test the code, and to
verify that the stuff that ends up in the core file makes sense. This
new add-symbol-file command required to read the symbols info from the
core file is however a bit of a departure from the standard way core
files are treated in gdb. It also not entirely clear to a user that
it would be necessary to issue it.
elena
>
> (gdb) core ./core.23363
> Core was generated by `/home/roland/build/newthreads-libc/elf/ld-linux.so.2 --library-path /home/rolan'.
> Program terminated with signal 3, Quit.
> #0 0xffffe410 in ?? ()
> (gdb) bt
> #0 0xffffe410 in ?? ()
> (gdb) i fr 0
> Stack frame at 0x0:
> eip = 0xffffe410; saved eip 0xbffff698
> Arglist at 0xbffff664, args:
> Locals at 0xbffff664, Previous frame's sp is 0xbffff66c
> Saved registers:
> eip at 0xbffff668
> (gdb) add-symbol-file core.23363
> add symbol table from file "core.23363" at
> (y or n) y
> Reading symbols from core.23363...done.
> (gdb) bt
> #0 0xffffe410 in _r_debug ()
> #1 0x400cd473 in read () at ctype.h:51
> #2 0x40070012 in _IO_file_read (fp=0xfffffe00, buf=0xfffffe00, size=-512) at fileops.c:1208
> #3 0x4006ed9c in _IO_new_file_underflow (fp=0x401294a0) at fileops.c:591
> #4 0x4007122d in _IO_default_uflow (fp=0x401294a0) at genops.c:430
> #5 0x40071076 in *__GI___uflow (fp=0x401294a0) at genops.c:384
> #6 0x4006af9f in getchar () at getchar.c:44
> #7 0x0804834d in main () at loser.c:1
> (gdb) i fr 0
> Stack frame at 0xbffff678:
> eip = 0xffffe410 in _r_debug; saved eip 0x400cd473
> called by frame at 0xbffff680
> Arglist at 0xbffff664, args:
> Locals at 0xbffff664, Previous frame's sp is 0xbffff678
> Saved registers:
> ebp at 0xbffff668, eip at 0xbffff674
> (gdb)
>
>
> (Of course "_r_debug" is bogus, it's just the highest-addressed candidate
> symbol or something or other. gdb really ought to notice that it's not in
> the same symfile section and not use that symbol for that address.)
>
>
> Thanks,
> Roland
>
>
> 2003-05-12 Roland McGrath <roland@redhat.com>
>
> * dwarf2read.c (dwarf_eh_frame_hdr_offset, dwarf_eh_frame_hdr_size,
> dwarf_eh_frame_hdr_section): New variables.
> (dwarf2_locate_sections): Match a section whose name starts with
> "eh_frame_hdr", and set those.
> (dwarf2_has_info): Clear dwarf_eh_frame_hdr_offset here.
> * dwarf-frame.c (decode_eh_frame_hdr): New function.
> (dwarf2_build_frame_info): Call it when dwarf_eh_frame_hdr_offset
> is set but dwarf_eh_frame_offset is not.
>
> * symfile.c (symfile_bfd_open): Try bfd_check_format with bfd_core
> if bfd_object fails.
>
> Index: dwarf-frame.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/Attic/dwarf-frame.c,v
> retrieving revision 1.1.2.3
> diff -b -B -p -u -r1.1.2.3 dwarf-frame.c
> --- dwarf-frame.c 10 May 2003 15:49:09 -0000 1.1.2.3
> +++ dwarf-frame.c 12 May 2003 09:17:02 -0000
> @@ -697,8 +697,9 @@ struct comp_unit
> /* Length of the loaded .debug_frame section. */
> unsigned long dwarf_frame_size;
>
> - /* Pointer to the .debug_frame section. */
> - asection *dwarf_frame_section;
> + /* VMA corresponding to the beginning of the buffer,
> + for relative addressing calculations. */
> + bfd_vma dwarf_frame_vma;
> };
>
> static unsigned int
> @@ -851,8 +852,7 @@ read_encoded_value (struct comp_unit *un
> base = 0;
> break;
> case DW_EH_PE_pcrel:
> - base = bfd_get_section_vma (unit->bfd, unit->dwarf_frame_section);
> - base += (buf - unit->dwarf_frame_buffer);
> + base = unit->dwarf_frame_vma + (buf - unit->dwarf_frame_buffer);
> break;
> default:
> internal_error (__FILE__, __LINE__, "Invalid or unsupported encoding");
> @@ -958,7 +958,7 @@ add_fde (struct comp_unit *unit, struct
> #define DW64_CIE_ID ~0
> #endif
>
> -/* Read and the CIE or FDE in BUF and decode it. */
> +/* Read the CIE or FDE in BUF and decode it. */
>
> static char *
> decode_frame_entry (struct comp_unit *unit, char *buf, int eh_frame_p)
> @@ -1161,11 +1161,170 @@ 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;
> +extern file_ptr dwarf_eh_frame_hdr_offset;
> +extern unsigned int dwarf_eh_frame_hdr_size;
> +extern asection *dwarf_eh_frame_hdr_section;
>
> /* Imported from dwarf2read.c. */
> extern char *dwarf2_read_section (struct objfile *objfile, file_ptr offset,
> unsigned int size, asection *sectp);
>
> +/* Decode .eh_frame_hdr format in dwarf_eh_frame_hdr_section. */
> +static void
> +decode_eh_frame_hdr (struct comp_unit *unit)
> +{
> + char hdr[4], buf[16];
> + ufile_ptr posn;
> + CORE_ADDR eh_frame_addr;
> + CORE_ADDR fde_count;
> + unsigned int len, element_size;
> + asection *sec;
> + struct comp_unit dummy;
> +
> + if (bfd_seek (unit->abfd, dwarf_eh_frame_hdr_offset, SEEK_SET)
> + || bfd_bread (hdr, sizeof hdr, unit->abfd) != sizeof hdr)
> + {
> + error ("Dwarf Error: Can't read DWARF data from '%s'",
> + bfd_get_filename (unit->abfd));
> + return;
> + }
> + if (hdr[0] != 1)
> + {
> + warning ("Dwarf Error: %s: %s header version %d (expected %d)",
> + bfd_get_filename (unit->abfd),
> + dwarf_eh_frame_hdr_section->name,
> + hdr[0], 1);
> + return;
> + }
> +
> + len = size_of_encoded_value (hdr[1]);
> + gdb_assert (len <= sizeof buf);
> + if (bfd_bread (buf, len, unit->abfd) != len)
> + {
> + error ("Dwarf Error: Can't read DWARF data from '%s'",
> + bfd_get_filename (unit->abfd));
> + return;
> + }
> +
> + /* read_encoded_value uses these to calculate the vma. */
> + dummy.abfd = unit->abfd;
> + dummy.dwarf_frame_vma = (bfd_get_section_vma (unit->abfd,
> + dwarf_eh_frame_hdr_section)
> + + sizeof hdr);
> + dummy.dwarf_frame_buffer = buf;
> +
> + eh_frame_addr = read_encoded_value (&dummy, hdr[1], buf, &len);
> + gdb_assert (len <= sizeof buf);
> +
> + len = size_of_encoded_value (hdr[2]);
> + if (len == 0)
> + /* No FDE table, and no way to know where .eh_frame ends!
> + We cannot use this. */
> + return;
> +
> + dummy.dwarf_frame_vma += len;
> + gdb_assert (len <= sizeof buf);
> + if (bfd_bread (buf, len, unit->abfd) != len)
> + {
> + error ("Dwarf Error: Can't read DWARF data from '%s'",
> + bfd_get_filename (unit->abfd));
> + return;
> + }
> + fde_count = read_encoded_value (&dummy, hdr[2], buf, &len);
> + gdb_assert (len <= sizeof buf);
> + dummy.dwarf_frame_vma += len;
> +
> + element_size = size_of_encoded_value (hdr[3]);
> + if (element_size == 0 || fde_count == 0)
> + /* No FDE table, and no way to know where .eh_frame ends!
> + We cannot use this. */
> + return;
> +
> + /* Save the position of the FDE table. */
> + posn = bfd_tell (unit->abfd);
> +
> + /* Now EH_FRAME_ADDR tells us where a .eh_frame section lies. We
> + presumably don't have real section headers or we would have seen
> + it by the name ".eh_frame" already. The BFD sections we have are
> + probably synthesized from ELF phdrs, as for a core file or a
> + stripped executable or DSO, i.e. the section we find corresponds
> + to our whole text segment. We have no way of knowing how large
> + the .eh_frame section should be, so we have to read through til
> + the end of the segment. Fortunately, .eh_frame_hdr is usually
> + the last thing in text, so we likely won't read much extra. */
> + for (sec = unit->abfd->sections; sec != NULL; sec = sec->next)
> + if ((sec->flags & SEC_ALLOC)
> + && sec->vma <= eh_frame_addr
> + && (sec->vma + bfd_get_section_size_before_reloc (sec)
> + > eh_frame_addr))
> + break;
> + if (sec == NULL)
> + {
> + warning ("\
> +Dwarf Error: No section contains eh_frame address 0x%08lx [in module %s]",
> + (long) eh_frame_addr, bfd_get_filename (unit->abfd));
> + return;
> + }
> +
> + unit->dwarf_frame_vma = eh_frame_addr;
> + eh_frame_addr -= bfd_get_section_vma (unit->abfd, sec);
> + unit->dwarf_frame_size = (bfd_get_section_size_before_reloc (sec)
> + - eh_frame_addr);
> + unit->dwarf_frame_buffer = dwarf2_read_section (unit->objfile,
> + sec->filepos + eh_frame_addr,
> + unit->dwarf_frame_size,
> + sec);
> +
> +
> + /* Now that we have read in the whole .eh_frame section, we cannot
> + just feed it all to decode_frame_entry, unfortunately. We don't
> + actually know where the .eh_frame section ends, and it contains
> + no internal end marker. So instead we have to work from the FDE
> + quick-lookup table in .eh_frame_hdr, whose size we do know. */
> + if (bfd_seek (unit->abfd, posn, SEEK_SET))
> + {
> + error ("Dwarf Error: Can't read DWARF data from '%s'",
> + bfd_get_filename (unit->abfd));
> + return;
> + }
> + while (fde_count-- > 0)
> + {
> + CORE_ADDR fde_pointer;
> +
> + if (bfd_bread (buf, element_size * 2, unit->abfd) != element_size * 2)
> + {
> + error ("Dwarf Error: Can't read DWARF data from '%s'",
> + bfd_get_filename (unit->abfd));
> + return;
> + }
> +
> + if ((hdr[3] & 0x70) == DW_EH_PE_datarel)
> + /* In this case, it's relative to the vma of .eh_frame_hdr. */
> + fde_pointer = (bfd_get_section_vma (unit->abfd,
> + dwarf_eh_frame_hdr_section)
> + + read_encoded_value (&dummy,
> + DW_EH_PE_absptr | (hdr[3] & 0x0f),
> + &buf[element_size], &len));
> + else
> + fde_pointer = read_encoded_value (&dummy, hdr[3],
> + &buf[element_size], &len);
> + gdb_assert (len == element_size);
> + dummy.dwarf_frame_vma += element_size * 2;
> +
> + if (fde_pointer < unit->dwarf_frame_vma
> + || fde_pointer > unit->dwarf_frame_vma + unit->dwarf_frame_size)
> + {
> + warning ("Dwarf Error: \
> +Bogus FDE pointer 0x%08lx from .eh_frame_hdr table [in module %s]",
> + (long) fde_pointer, bfd_get_filename (unit->abfd));
> + continue;
> + }
> + fde_pointer -= unit->dwarf_frame_vma;
> + (void) decode_frame_entry (unit,
> + unit->dwarf_frame_buffer + fde_pointer, 1);
> + }
> +}
> +
> void
> dwarf2_build_frame_info (struct objfile *objfile)
> {
> @@ -1176,34 +1335,39 @@ dwarf2_build_frame_info (struct objfile
> unit.abfd = objfile->obfd;
> unit.objfile = objfile;
> unit.addr_size = objfile->obfd->arch_info->bits_per_address / 8;
> + unit.cie = NULL;
>
> /* First add the information from the .eh_frame section. That way,
> the FDEs from that section are searched last. */
> if (dwarf_eh_frame_offset)
> {
> - 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_section = dwarf_eh_frame_section;
> + unit.dwarf_frame_vma = bfd_get_section_vma (unit.bfd,
> + dwarf_eh_frame_section);
>
> 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);
> }
> + else if (dwarf_eh_frame_hdr_offset)
> + /* We didn't find the .eh_frame section but found .eh_frame_hdr,
> + decode it to locate the .eh_frame section that actually exists. */
> + decode_eh_frame_hdr (&unit);
>
> if (dwarf_frame_offset)
> {
> - 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_section = dwarf_frame_section;
> + unit.dwarf_frame_vma = bfd_get_section_vma (unit.bfd,
> + dwarf_frame_section);
>
> frame_ptr = unit.dwarf_frame_buffer;
> while (frame_ptr < unit.dwarf_frame_buffer + unit.dwarf_frame_size)
> Index: dwarf2read.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/dwarf2read.c,v
> retrieving revision 1.90
> diff -b -B -p -u -r1.90 dwarf2read.c
> --- dwarf2read.c 15 Apr 2003 23:07:11 -0000 1.90
> +++ dwarf2read.c 12 May 2003 09:17:06 -0000
> @@ -139,6 +139,7 @@ static file_ptr dwarf_str_offset;
> static file_ptr dwarf_ranges_offset;
> file_ptr dwarf_frame_offset;
> file_ptr dwarf_eh_frame_offset;
> +file_ptr dwarf_eh_frame_hdr_offset;
>
> static unsigned int dwarf_info_size;
> static unsigned int dwarf_abbrev_size;
> @@ -151,6 +152,7 @@ static unsigned int dwarf_str_size;
> static unsigned int dwarf_ranges_size;
> unsigned int dwarf_frame_size;
> unsigned int dwarf_eh_frame_size;
> +unsigned int dwarf_eh_frame_hdr_size;
>
> static asection *dwarf_info_section;
> static asection *dwarf_abbrev_section;
> @@ -163,6 +165,7 @@ static asection *dwarf_str_section;
> static asection *dwarf_ranges_section;
> asection *dwarf_frame_section;
> asection *dwarf_eh_frame_section;
> +asection *dwarf_eh_frame_hdr_section;
>
> /* names of the debugging sections */
>
> @@ -944,6 +947,7 @@ dwarf2_has_info (bfd *abfd)
> dwarf_macinfo_offset = 0;
> dwarf_frame_offset = 0;
> dwarf_eh_frame_offset = 0;
> + dwarf_eh_frame_hdr_offset = 0;
> dwarf_ranges_offset = 0;
> dwarf_loc_offset = 0;
>
> @@ -1025,6 +1029,12 @@ dwarf2_locate_sections (bfd *ignore_abfd
> dwarf_eh_frame_size = bfd_get_section_size_before_reloc (sectp);
> dwarf_eh_frame_section = sectp;
> }
> + else if (!strncmp (sectp->name, "eh_frame_hdr", 12))
> + {
> + dwarf_eh_frame_hdr_offset = sectp->filepos;
> + dwarf_eh_frame_hdr_size = bfd_get_section_size_before_reloc (sectp);
> + dwarf_eh_frame_hdr_section = sectp;
> + }
> else if (STREQ (sectp->name, RANGES_SECTION))
> {
> dwarf_ranges_offset = sectp->filepos;
> Index: symfile.c
> ===================================================================
> RCS file: /cvs/src/src/gdb/symfile.c,v
> retrieving revision 1.93
> diff -b -B -p -u -r1.93 symfile.c
> --- symfile.c 1 Apr 2003 14:17:20 -0000 1.93
> +++ symfile.c 12 May 2003 09:17:08 -0000
> @@ -1338,7 +1338,9 @@ symfile_bfd_open (char *name)
> }
> sym_bfd->cacheable = 1;
>
> - if (!bfd_check_format (sym_bfd, bfd_object))
> + if (!bfd_check_format (sym_bfd, bfd_object)
> + /* Yes, Virginia, core files can have symbols! */
> + && !bfd_check_format (sym_bfd, bfd_core))
> {
> /* FIXME: should be checking for errors from bfd_close (for one thing,
> on error it does not free all the storage associated with the