From 6258e7486eb3eed6e50005946795c5fbf73aa106 Mon Sep 17 00:00:00 2001 From: Ulrich Drepper Date: Tue, 13 Mar 2007 06:22:40 +0000 Subject: [PATCH] propagate from branch 'com.redhat.elfutils.roland.pending' (head e0c7abd450c9e49093cfae30af8a22782a74a403) to branch 'com.redhat.elfutils' (head 2c784d50eee72e33972c333138a3a28df304da63) --- NEWS | 4 + libdw/ChangeLog | 9 +++ libdw/libdw.map | 10 +++ libdwfl/ChangeLog | 44 ++++++++++ libdwfl/Makefile.am | 5 +- libdwfl/dwfl_module.c | 36 ++++++--- libdwfl/dwfl_module_addrname.c | 72 +---------------- libdwfl/dwfl_module_addrsym.c | 137 +++++++++++++++++++++++++++++++ libdwfl/dwfl_module_getdwarf.c | 19 +++-- libdwfl/elf-from-memory.c | 4 +- libdwfl/libdwfl.h | 14 ++++ libdwfl/libdwflP.h | 4 +- libdwfl/linux-kernel-modules.c | 142 ++++++++++++++++++++++++++------- libdwfl/offline.c | 2 +- tests/ChangeLog | 9 +++ tests/Makefile.am | 5 +- tests/dwfl-bug-fd-leak.c | 110 +++++++++++++++++++++++++ tests/dwflmodtest.c | 34 +++++++- 18 files changed, 540 insertions(+), 120 deletions(-) create mode 100644 libdwfl/dwfl_module_addrsym.c create mode 100644 tests/dwfl-bug-fd-leak.c diff --git a/NEWS b/NEWS index b1e968567..a7a5e3299 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +Version 0.127-UNFINISHED: + +libdwfl: new function dwfl_module_addrsym + Version 0.126: new program: ar diff --git a/libdw/ChangeLog b/libdw/ChangeLog index 61170d59f..76f04bb9f 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,12 @@ +2007-03-12 Roland McGrath + + * libdw.map (ELFUTILS_0.127): Add dwfl_report_begin_add. + +2007-03-04 Roland McGrath + + * libdw.map (ELFUTILS_0.127): New version set, inherits from + ELFUTILS_0.126. Add dwfl_module_addrsym. + 2007-02-10 Roland McGrath * dwarf.h (DW_OP_fbreg): Comment fix. diff --git a/libdw/libdw.map b/libdw/libdw.map index 71e9f50fe..e79b6e01a 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -145,6 +145,7 @@ ELFUTILS_0.122 { local: *; } ELFUTILS_0; + ELFUTILS_0.126 { global: dwarf_getelf; @@ -152,3 +153,12 @@ ELFUTILS_0.126 { local: *; } ELFUTILS_0.122; + +ELFUTILS_0.127 { + global: + dwfl_module_addrsym; + dwfl_report_begin_add; + + local: + *; +} ELFUTILS_0.126; diff --git a/libdwfl/ChangeLog b/libdwfl/ChangeLog index 71b167f35..02829e30f 100644 --- a/libdwfl/ChangeLog +++ b/libdwfl/ChangeLog @@ -1,3 +1,47 @@ +2007-03-12 Roland McGrath + + * dwfl_module.c (dwfl_report_begin_add): New function broken out of ... + (dwfl_report_begin): ... here. Call it. + * libdwfl.h: Declare it. + * libdwflP.h: Add INTDECL. + + * elf-from-memory.c (elf_from_remote_memory): Fix 32/64 typo. + + * offline.c: Comment typo fix. + +2007-03-04 Roland McGrath + + * linux-kernel-modules.c (KERNEL_MODNAME): New macro for "kernel". + (find_kernel_elf): New function, broken out of ... + (report_kernel): ... here. Call it. + (dwfl_linux_kernel_find_elf): Use it for module named KERNEL_MODNAME. + (intuit_kernel_bounds): New function, grovel /proc/kallsyms to guess + virtual address bounds of kernel from symbols rounded to page size. + (dwfl_linux_kernel_report_kernel): Use that if it works, before + resorting to report_kernel. + + * dwfl_module_getdwarf.c (open_elf): Set MOD->e_type to ET_DYN for an + ET_EXEC file with nonzero bias. + + * dwfl_module_addrname.c (dwfl_module_addrname): Just call + dwfl_module_addrsym. Guts moved to ... + * dwfl_module_addrsym.c: ... here; new file. + * Makefile.am (libdwfl_a_SOURCES): Add it. + * libdwfl.h: Declare dwfl_module_addrsym. + * libdwflP.h: Add INTDECL. + +2007-03-03 Roland McGrath + + * dwfl_module.c (free_file): New function, broken out of ... + (__libdwfl_module_free): ... here. In it, close fd after elf_end. + + * dwfl_module_getdwarf.c (open_elf): Close fd and reset to -1 + on libelf failure. + +2007-03-02 Roland McGrath + + * linux-kernel-modules.c: Fix bogus error test for asprintf call. + 2007-02-02 Roland McGrath * dwfl_addrmodule.c (dwfl_addrmodule): Match a module's high boundary diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index ee9efec93..e5cbb9794 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -2,7 +2,7 @@ ## ## Process this file with automake to create Makefile.in ## -## Copyright (C) 2005, 2006 Red Hat, Inc. +## Copyright (C) 2005, 2006, 2007 Red Hat, Inc. ## This file is part of Red Hat elfutils. ## ## Red Hat elfutils is free software; you can redistribute it and/or modify @@ -66,7 +66,8 @@ libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module_getsrc_file.c \ libdwfl_crc32.c libdwfl_crc32_file.c \ elf-from-memory.c \ - dwfl_module_getsym.c dwfl_module_addrname.c \ + dwfl_module_getsym.c \ + dwfl_module_addrname.c dwfl_module_addrsym.c \ dwfl_module_return_value_location.c \ dwfl_module_register_names.c diff --git a/libdwfl/dwfl_module.c b/libdwfl/dwfl_module.c index 16c616d26..08730ec64 100644 --- a/libdwfl/dwfl_module.c +++ b/libdwfl/dwfl_module.c @@ -1,5 +1,5 @@ /* Maintenance of module list in libdwfl. - Copyright (C) 2005, 2006 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -49,6 +49,7 @@ #include "libdwflP.h" #include +#include static void free_cu (struct dwfl_cu *cu) @@ -63,6 +64,17 @@ nofree (void *arg __attribute__ ((unused))) { } +static void +free_file (struct dwfl_file *file) +{ + if (file->elf != NULL) + { + elf_end (file->elf); + if (file->fd != -1) + close (file->fd); + } +} + void internal_function __libdwfl_module_free (Dwfl_Module *mod) @@ -86,24 +98,30 @@ __libdwfl_module_free (Dwfl_Module *mod) if (mod->ebl != NULL) ebl_closebackend (mod->ebl); - if (mod->debug.elf != mod->main.elf && mod->debug.elf != NULL) - elf_end (mod->debug.elf); - if (mod->main.elf != NULL) - elf_end (mod->main.elf); + if (mod->debug.elf != mod->main.elf) + free_file (&mod->debug); + free_file (&mod->main); free (mod->name); } void -dwfl_report_begin (Dwfl *dwfl) +dwfl_report_begin_add (Dwfl *dwfl) { - for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) - m->gc = true; - if (dwfl->modules != NULL) free (dwfl->modules); dwfl->modules = NULL; dwfl->nmodules = 0; +} +INTDEF (dwfl_report_begin_add) + +void +dwfl_report_begin (Dwfl *dwfl) +{ + INTUSE(dwfl_report_begin_add) (dwfl); + + for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) + m->gc = true; dwfl->offline_next_address = OFFLINE_REDZONE; } diff --git a/libdwfl/dwfl_module_addrname.c b/libdwfl/dwfl_module_addrname.c index b107448bc..7d365fe2c 100644 --- a/libdwfl/dwfl_module_addrname.c +++ b/libdwfl/dwfl_module_addrname.c @@ -52,74 +52,6 @@ const char * dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr addr) { - int syments = INTUSE(dwfl_module_getsymtab) (mod); - if (syments < 0) - return NULL; - - /* Return true iff we consider ADDR to lie in the same section as SYM. */ - GElf_Word addr_shndx = SHN_UNDEF; - inline bool same_section (const GElf_Sym *sym, GElf_Word shndx) - { - /* For absolute symbols and the like, only match exactly. */ - if (shndx >= SHN_LORESERVE) - return sym->st_value == addr; - - /* Ignore section and other special symbols. */ - switch (GELF_ST_TYPE (sym->st_info)) - { - case STT_SECTION: - case STT_FILE: - case STT_TLS: - return false; - } - - /* Figure out what section ADDR lies in. */ - if (addr_shndx == SHN_UNDEF) - { - GElf_Addr mod_addr = addr - mod->symfile->bias; - Elf_Scn *scn = NULL; - addr_shndx = SHN_ABS; - while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) - { - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (likely (shdr != NULL) - && mod_addr >= shdr->sh_addr - && mod_addr < shdr->sh_addr + shdr->sh_size) - { - addr_shndx = elf_ndxscn (scn); - break; - } - } - } - - return shndx == addr_shndx; - } - - /* Look through the symbol table for a matching symbol. */ - const char *closest = NULL; - GElf_Addr closest_value = 0; - for (int i = 1; i < syments; ++i) - { - GElf_Sym sym; - GElf_Word shndx; - const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx); - if (name != NULL && sym.st_value <= addr) - { - if (addr < sym.st_value + sym.st_size) - return name; - - /* Handwritten assembly symbols sometimes have no st_size. - If no symbol with proper size includes the address, we'll - use the closest one that is in the same section as ADDR. */ - if (sym.st_size == 0 && sym.st_value >= closest_value - && same_section (&sym, shndx)) - { - closest_value = sym.st_value; - closest = name; - } - } - } - - return closest; + GElf_Sym sym; + return INTUSE(dwfl_module_addrsym) (mod, addr, &sym, NULL); } diff --git a/libdwfl/dwfl_module_addrsym.c b/libdwfl/dwfl_module_addrsym.c new file mode 100644 index 000000000..98ab15a01 --- /dev/null +++ b/libdwfl/dwfl_module_addrsym.c @@ -0,0 +1,137 @@ +/* Find debugging and symbol information for a module in libdwfl. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + In addition, as a special exception, Red Hat, Inc. gives You the + additional right to link the code of Red Hat elfutils with code licensed + under any Open Source Initiative certified open source license + (http://www.opensource.org/licenses/index.php) which requires the + distribution of source code with any binary distribution and to + distribute linked combinations of the two. Non-GPL Code permitted under + this exception must only link to the code of Red Hat elfutils through + those well defined interfaces identified in the file named EXCEPTION + found in the source code files (the "Approved Interfaces"). The files + of Non-GPL Code may instantiate templates or use macros or inline + functions from the Approved Interfaces without causing the resulting + work to be covered by the GNU General Public License. Only Red Hat, + Inc. may make changes or additions to the list of Approved Interfaces. + Red Hat's grant of this exception is conditioned upon your not adding + any new exceptions. If you wish to add a new Approved Interface or + exception, please contact Red Hat. You must obey the GNU General Public + License in all respects for all of the Red Hat elfutils code and other + code used in conjunction with Red Hat elfutils except the Non-GPL Code + covered by this exception. If you modify this file, you may extend this + exception to your version of the file, but you are not obligated to do + so. If you do not wish to provide this exception without modification, + you must delete this exception statement from your version and license + this file solely under the GPL without exception. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include "libdwflP.h" + +const char * +dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, + GElf_Sym *closest_sym, GElf_Word *shndxp) +{ + int syments = INTUSE(dwfl_module_getsymtab) (mod); + if (syments < 0) + return NULL; + + /* Return true iff we consider ADDR to lie in the same section as SYM. */ + GElf_Word addr_shndx = SHN_UNDEF; + inline bool same_section (const GElf_Sym *sym, GElf_Word shndx) + { + /* For absolute symbols and the like, only match exactly. */ + if (shndx >= SHN_LORESERVE) + return sym->st_value == addr; + + /* Ignore section and other special symbols. */ + switch (GELF_ST_TYPE (sym->st_info)) + { + case STT_SECTION: + case STT_FILE: + case STT_TLS: + return false; + } + + /* Figure out what section ADDR lies in. */ + if (addr_shndx == SHN_UNDEF) + { + GElf_Addr mod_addr = addr - mod->symfile->bias; + Elf_Scn *scn = NULL; + addr_shndx = SHN_ABS; + while ((scn = elf_nextscn (mod->symfile->elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (likely (shdr != NULL) + && mod_addr >= shdr->sh_addr + && mod_addr < shdr->sh_addr + shdr->sh_size) + { + addr_shndx = elf_ndxscn (scn); + break; + } + } + } + + return shndx == addr_shndx; + } + + /* Look through the symbol table for a matching symbol. */ + const char *closest_name = NULL; + closest_sym->st_value = 0; + GElf_Word closest_shndx = SHN_UNDEF; + for (int i = 1; i < syments; ++i) + { + GElf_Sym sym; + GElf_Word shndx; + const char *name = INTUSE(dwfl_module_getsym) (mod, i, &sym, &shndx); + if (name != NULL && sym.st_value <= addr) + { + inline void closest (void) + { + *closest_sym = sym; + closest_shndx = shndx; + closest_name = name; + } + + if (addr < sym.st_value + sym.st_size) + { + closest (); + break; + } + + /* Handwritten assembly symbols sometimes have no st_size. + If no symbol with proper size includes the address, we'll + use the closest one that is in the same section as ADDR. */ + if (sym.st_size == 0 && sym.st_value >= closest_sym->st_value + && same_section (&sym, shndx)) + closest (); + } + } + + if (shndxp != NULL) + *shndxp = closest_shndx; + return closest_name; +} +INTDEF (dwfl_module_addrsym) diff --git a/libdwfl/dwfl_module_getdwarf.c b/libdwfl/dwfl_module_getdwarf.c index 07248bf78..3a22c3beb 100644 --- a/libdwfl/dwfl_module_getdwarf.c +++ b/libdwfl/dwfl_module_getdwarf.c @@ -1,5 +1,5 @@ /* Find debugging and symbol information for a module in libdwfl. - Copyright (C) 2005, 2006 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -69,9 +69,12 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (file->elf, &ehdr_mem); if (ehdr == NULL) - return DWFL_E (LIBELF, elf_errno ()); - - mod->e_type = ehdr->e_type; + { + elf_error: + close (file->fd); + file->fd = -1; + return DWFL_E_LIBELF; + } file->bias = 0; for (uint_fast16_t i = 0; i < ehdr->e_phnum; ++i) @@ -79,7 +82,7 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) GElf_Phdr ph_mem; GElf_Phdr *ph = gelf_getphdr (file->elf, i, &ph_mem); if (ph == NULL) - return DWFL_E_LIBELF; + goto elf_error; if (ph->p_type == PT_LOAD) { file->bias = ((mod->low_addr & -ph->p_align) @@ -88,6 +91,12 @@ open_elf (Dwfl_Module *mod, struct dwfl_file *file) } } + mod->e_type = ehdr->e_type; + + /* Relocatable Linux kernels are ET_EXEC but act like ET_DYN. */ + if (mod->e_type == ET_EXEC && file->bias != 0) + mod->e_type = ET_DYN; + return DWFL_E_NOERROR; } diff --git a/libdwfl/elf-from-memory.c b/libdwfl/elf-from-memory.c index 2a174759b..a292210ae 100644 --- a/libdwfl/elf-from-memory.c +++ b/libdwfl/elf-from-memory.c @@ -1,5 +1,5 @@ /* Reconstruct an ELF file by reading the segments out of remote memory. - Copyright (C) 2005, 2006 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -154,7 +154,7 @@ elf_from_remote_memory (GElf_Addr ehdr_vma, case ELFCLASS64: xlatefrom.d_size = sizeof (Elf64_Ehdr); - if (elf32_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL) + if (elf64_xlatetom (&xlateto, &xlatefrom, buffer[EI_DATA]) == NULL) goto libelf_error; phoff = ehdr.e64.e_phoff; phnum = ehdr.e64.e_phnum; diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index ec0065627..c36f9206b 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -156,6 +156,14 @@ extern int dwfl_report_end (Dwfl *dwfl, void *arg), void *arg); +/* Start reporting additional modules to the library. No calls but + dwfl_report_* can be made on DWFL until dwfl_report_end is called. + This is like dwfl_report_begin, but all the old modules are kept on. + More dwfl_report_* calls can follow to add more modules. + When dwfl_report_end is called, no old modules will be removed. */ +extern void dwfl_report_begin_add (Dwfl *dwfl); + + /* Return the name of the module, and for each non-null argument store interesting details: *USERDATA is a location for storing your own pointer, **USERDATA is initially null; *START and *END give the address @@ -332,6 +340,12 @@ extern const char *dwfl_module_getsym (Dwfl_Module *mod, int ndx, /* Find the symbol that ADDRESS lies inside, and return its name. */ extern const char *dwfl_module_addrname (Dwfl_Module *mod, GElf_Addr address); +/* Find the symbol that ADDRESS lies inside, and return detailed + information as for dwfl_module_getsym (above). */ +extern const char *dwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr address, + GElf_Sym *sym, GElf_Word *shndxp) + __nonnull_attribute__ (3); + /*** Dwarf access functions ***/ diff --git a/libdwfl/libdwflP.h b/libdwfl/libdwflP.h index 31da938db..25e9a4e86 100644 --- a/libdwfl/libdwflP.h +++ b/libdwfl/libdwflP.h @@ -1,5 +1,5 @@ /* Internal definitions for libdwfl. - Copyright (C) 2005, 2006 Red Hat, Inc. + Copyright (C) 2005, 2006, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -269,6 +269,7 @@ INTDECL (dwfl_addrmodule) INTDECL (dwfl_addrdwarf) INTDECL (dwfl_addrdie) INTDECL (dwfl_module_addrdie) +INTDECL (dwfl_module_addrsym) INTDECL (dwfl_module_getdwarf) INTDECL (dwfl_module_getelf) INTDECL (dwfl_module_getsym) @@ -276,6 +277,7 @@ INTDECL (dwfl_module_getsymtab) INTDECL (dwfl_module_getsrc) INTDECL (dwfl_report_elf) INTDECL (dwfl_report_begin) +INTDECL (dwfl_report_begin_add) INTDECL (dwfl_report_module) INTDECL (dwfl_report_offline) INTDECL (dwfl_report_end) diff --git a/libdwfl/linux-kernel-modules.c b/libdwfl/linux-kernel-modules.c index 4ea391c07..2aaa25acb 100644 --- a/libdwfl/linux-kernel-modules.c +++ b/libdwfl/linux-kernel-modules.c @@ -63,8 +63,11 @@ #include +#define KERNEL_MODNAME "kernel" + #define MODULEDIRFMT "/lib/modules/%s" +#define KSYMSFILE "/proc/kallsyms" #define MODULELIST "/proc/modules" #define SECADDRDIRFMT "/sys/module/%s/sections/" #define MODULE_SECT_NAME_LEN 32 /* Minimum any linux/module.h has had. */ @@ -115,6 +118,26 @@ kernel_release (void) return utsname.release; } +static int +find_kernel_elf (Dwfl *dwfl, const char *release, char **fname) +{ + if ((release[0] == '/' + ? asprintf (fname, "%s/vmlinux", release) + : asprintf (fname, "/boot/vmlinux-%s", release)) < 0) + return -1; + + int fd = try_kernel_name (dwfl, fname); + if (fd < 0 && release[0] != '/') + { + free (*fname); + if (asprintf (fname, MODULEDIRFMT "/vmlinux", release) < 0) + return -1; + fd = try_kernel_name (dwfl, fname); + } + + return fd; +} + static int report_kernel (Dwfl *dwfl, const char *release, int (*predicate) (const char *module, const char *file)) @@ -122,23 +145,19 @@ report_kernel (Dwfl *dwfl, const char *release, if (dwfl == NULL) return -1; - char *fname; - if ((release[0] == '/' - ? asprintf (&fname, "%s/vmlinux", release) - : asprintf (&fname, "/boot/vmlinux-%s", release)) < 0) - return -1; - int fd = try_kernel_name (dwfl, &fname); - if (fd < 0 && release[0] != '/') + if (release == NULL) { - free (fname); - if (asprintf (&fname, MODULEDIRFMT "/vmlinux", release) < 0) - return -1; - fd = try_kernel_name (dwfl, &fname); + release = kernel_release (); + if (release == NULL) + return errno; } + char *fname; + int fd = find_kernel_elf (dwfl, release, &fname); + int result = 0; if (fd < 0) - result = ((predicate != NULL && !(*predicate) ("kernel", NULL)) + result = ((predicate != NULL && !(*predicate) (KERNEL_MODNAME, NULL)) ? 0 : errno ?: ENOENT); else { @@ -147,14 +166,15 @@ report_kernel (Dwfl *dwfl, const char *release, if (predicate != NULL) { /* Let the predicate decide whether to use this one. */ - int want = (*predicate) ("kernel", fname); + int want = (*predicate) (KERNEL_MODNAME, fname); if (want < 0) result = errno; report = want > 0; } if (report - && INTUSE(dwfl_report_elf) (dwfl, "kernel", fname, fd, 0) == NULL) + && INTUSE(dwfl_report_elf) (dwfl, KERNEL_MODNAME, + fname, fd, 0) == NULL) { close (fd); result = -1; @@ -177,13 +197,6 @@ dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, int (*predicate) (const char *module, const char *file)) { - if (release == NULL) - { - release = kernel_release (); - if (release == NULL) - return errno; - } - /* First report the kernel. */ int result = report_kernel (dwfl, release, predicate); if (result == 0) @@ -279,15 +292,87 @@ dwfl_linux_kernel_report_offline (Dwfl *dwfl, const char *release, INTDEF (dwfl_linux_kernel_report_offline) -/* Find the ELF file for the running kernel and dwfl_report_elf it. */ +/* Grovel around to guess the bounds of the runtime kernel image. */ +static int +intuit_kernel_bounds (Dwarf_Addr *start, Dwarf_Addr *end) +{ + FILE *f = fopen (KSYMSFILE, "r"); + if (f == NULL) + return errno; + + (void) __fsetlocking (f, FSETLOCKING_BYCALLER); + + char *line = NULL; + size_t linesz = 0; + size_t n = getline (&line, &linesz, f); + Dwarf_Addr first; + char *p = NULL; + int result = 0; + if (n > 0 && (first = strtoull (line, &p, 16)) > 0 && p > line) + { + Dwarf_Addr last = 0; + while ((n = getline (&line, &linesz, f)) > 1 && line[n - 2] != ']') + { + p = NULL; + last = strtoull (line, &p, 16); + if (p == NULL || p == line || last == 0) + { + result = -1; + break; + } + } + if ((n == 0 && feof_unlocked (f)) || (n > 1 && line[n - 2] == ']')) + { + Dwarf_Addr round_kernel = sysconf (_SC_PAGE_SIZE); + first &= -(Dwarf_Addr) round_kernel; + last += round_kernel - 1; + last &= -(Dwarf_Addr) round_kernel; + *start = first; + *end = last; + result = 0; + } + } + free (line); + + if (result == -1) + result = ferror_unlocked (f) ? errno : ENOEXEC; + + fclose (f); + + return result; +} + int dwfl_linux_kernel_report_kernel (Dwfl *dwfl) { - const char *release = kernel_release (); - if (release == NULL) - return errno; + Dwarf_Addr start; + Dwarf_Addr end; + inline int report (void) + { + return INTUSE(dwfl_report_module) (dwfl, KERNEL_MODNAME, + start, end) == NULL ? -1 : 0; + } + + /* This is a bit of a kludge. If we already reported the kernel, + don't bother figuring it out again--it never changes. */ + for (Dwfl_Module *m = dwfl->modulelist; m != NULL; m = m->next) + if (!strcmp (m->name, KERNEL_MODNAME)) + { + start = m->low_addr; + end = m->high_addr; + return report (); + } + + /* Try to figure out the bounds of the kernel image without + looking for any vmlinux file. */ + int result = intuit_kernel_bounds (&start, &end); + if (result == 0) + return report (); + if (result != ENOENT) + return result; - return report_kernel (dwfl, release, NULL); + /* Find the ELF file for the running kernel and dwfl_report_elf it. */ + return report_kernel (dwfl, NULL, NULL); } INTDEF (dwfl_linux_kernel_report_kernel) @@ -306,6 +391,9 @@ dwfl_linux_kernel_find_elf (Dwfl_Module *mod __attribute__ ((unused)), if (release == NULL) return errno; + if (!strcmp (module_name, KERNEL_MODNAME)) + return find_kernel_elf (mod->dwfl, release, file_name); + /* Do "find /lib/modules/`uname -r`/kernel -name MODULE_NAME.ko". */ char *modulesdir[] = { NULL, NULL }; @@ -416,7 +504,7 @@ dwfl_linux_kernel_module_section_address Dwarf_Addr *addr) { char *sysfile; - if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname)) + if (asprintf (&sysfile, SECADDRDIRFMT "%s", modname, secname) < 0) return ENOMEM; FILE *f = fopen (sysfile, "r"); diff --git a/libdwfl/offline.c b/libdwfl/offline.c index bde2aeb91..beeb0abf2 100644 --- a/libdwfl/offline.c +++ b/libdwfl/offline.c @@ -52,7 +52,7 @@ /* Since dwfl_report_elf lays out the sections already, this will only be called when the section headers of the debuginfo file are being - consulted instead, or for a section located at zeron. With binutils + consulted instead, or for a section located at zero. With binutils strip-to-debug, the symbol table is in the debuginfo file and relocation looks there. */ int diff --git a/tests/ChangeLog b/tests/ChangeLog index 5b188f845..770f33751 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,12 @@ +2007-03-04 Roland McGrath + + * dwfl-bug-fd-leak.c: New file. + * Makefile.am (noinst_PROGRAMS, TESTS): Add it. + (dwfl_bug_fd_leak_LDADD): New variable. + + * dwflmodtest.c: Test dwfl_getmodules before and after getdwarf, + show what files have been located. + 2007-02-02 Roland McGrath * run-addrname-test.sh: New file. diff --git a/tests/Makefile.am b/tests/Makefile.am index bff5568b5..c72ea3ce6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -58,7 +58,7 @@ noinst_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ get-aranges allfcts line2addr addrscopes funcscopes \ show-abbrev hash newscn ecp dwflmodtest \ find-prologues funcretval allregs rdwrmmap \ - dwfl-bug-addr-overflow arls + dwfl-bug-addr-overflow arls dwfl-bug-fd-leak # get-ciefde asm_TESTS = asm-tst1 asm-tst2 asm-tst3 asm-tst4 asm-tst5 \ asm-tst6 asm-tst7 asm-tst8 asm-tst9 @@ -76,7 +76,7 @@ TESTS = run-arextract.sh run-arsymtest.sh newfile test-nlist \ run-addrscopes.sh run-strings-test.sh run-funcscopes.sh \ run-find-prologues.sh run-allregs.sh run-readelf-test1.sh \ run-native-test.sh run-bug1-test.sh \ - dwfl-bug-addr-overflow run-addrname-test.sh + dwfl-bug-addr-overflow run-addrname-test.sh dwfl-bug-fd-leak # run-show-ciefde.sh if !STANDALONE @@ -203,6 +203,7 @@ dwflmodtest_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl rdwrmmap_LDADD = $(libelf) dwfl_bug_addr_overflow_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl arls_LDADD = $(libelf) $(libmudflap) +dwfl_bug_fd_leak_LDADD = $(libdw) $(libebl) $(libelf) $(libmudflap) -ldl CLEANFILES = xxx *.gcno *.gcda *gconv diff --git a/tests/dwfl-bug-fd-leak.c b/tests/dwfl-bug-fd-leak.c new file mode 100644 index 000000000..c75a79b61 --- /dev/null +++ b/tests/dwfl-bug-fd-leak.c @@ -0,0 +1,110 @@ +/* Test program for libdwfl file decriptors leakage. + Copyright (C) 2007 Red Hat, Inc. + This file is part of Red Hat elfutils. + + Red Hat elfutils 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; version 2 of the License. + + Red Hat elfutils 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 Red Hat elfutils; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA. + + Red Hat elfutils is an included package of the Open Invention Network. + An included package of the Open Invention Network is a package for which + Open Invention Network licensees cross-license their patents. No patent + license is granted, either expressly or impliedly, by designation as an + included package. Should you wish to participate in the Open Invention + Network licensing program, please visit www.openinventionnetwork.com + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include ELFUTILS_HEADER(dwfl) + + +static Dwfl * +elfutils_open (pid_t pid, Dwarf_Addr address) +{ + static char *debuginfo_path; + static const Dwfl_Callbacks proc_callbacks = + { + .find_debuginfo = dwfl_standard_find_debuginfo, + .debuginfo_path = &debuginfo_path, + + .find_elf = dwfl_linux_proc_find_elf, + }; + Dwfl *dwfl = dwfl_begin (&proc_callbacks); + if (dwfl == NULL) + error (2, 0, "dwfl_begin: %s", dwfl_errmsg (-1)); + + int result = dwfl_linux_proc_report (dwfl, pid); + if (result < 0) + error (2, 0, "dwfl_linux_proc_report: %s", dwfl_errmsg (-1)); + else if (result > 0) + error (2, result, "dwfl_linux_proc_report"); + + if (dwfl_report_end (dwfl, NULL, NULL) != 0) + error (2, 0, "dwfl_report_end: %s", dwfl_errmsg (-1)); + + Dwarf_Addr bias; + Dwarf *dbg = dwfl_addrdwarf (dwfl, address, &bias); + if (dbg != NULL) + { + Elf *elf = dwarf_getelf (dbg); + if (elf == NULL) + error (2, 0, "dwarf_getelf: %s", dwarf_errmsg (-1)); + } + else + { + Elf *elf = dwfl_module_getelf (dwfl_addrmodule (dwfl, address), &bias); + if (elf == NULL) + error (2, 0, "dwfl_module_getelf: %s", dwfl_errmsg (-1)); + } + + return dwfl; +} + +static void +elfutils_close (Dwfl *dwfl) +{ + dwfl_end (dwfl); +} + +int +main (void) +{ + /* We use no threads here which can interfere with handling a stream. */ + (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER); + + /* Set locale. */ + (void) setlocale (LC_ALL, ""); + + struct rlimit fd_limit = { .rlim_cur = 32, .rlim_max = 32 }; + if (setrlimit (RLIMIT_NOFILE, &fd_limit) < 0) + error (2, errno, "setrlimit"); + + for (int i = 0; i < 5000; ++i) + { + Dwfl *dwfl = elfutils_open (getpid (), (Dwarf_Addr) main); + elfutils_close (dwfl); + } + + return 0; +} diff --git a/tests/dwflmodtest.c b/tests/dwflmodtest.c index 7e454e472..c34cac429 100644 --- a/tests/dwflmodtest.c +++ b/tests/dwflmodtest.c @@ -1,5 +1,5 @@ /* Test program for libdwfl basic module tracking, relocation. - Copyright (C) 2005 Red Hat, Inc. + Copyright (C) 2005, 2007 Red Hat, Inc. This file is part of Red Hat elfutils. Red Hat elfutils is free software; you can redistribute it and/or modify @@ -162,6 +162,25 @@ print_func (Dwarf_Die *func, void *arg) return DWARF_CB_OK; } +static int +list_module (Dwfl_Module *mod __attribute__ ((unused)), + void **userdata __attribute__ ((unused)), + const char *name, Dwarf_Addr base, + void *arg __attribute__ ((unused))) +{ + Dwarf_Addr start; + Dwarf_Addr end; + const char *file; + const char *debug; + if (dwfl_module_info (mod, NULL, &start, &end, + NULL, NULL, &file, &debug) != name + || start != base) + abort (); + printf ("module: %30s %08" PRIx64 "..%08" PRIx64 " %s %s\n", + name, start, end, file, debug); + return DWARF_CB_OK; +} + static int print_module (Dwfl_Module *mod __attribute__ ((unused)), void **userdata __attribute__ ((unused)), @@ -251,12 +270,25 @@ main (int argc, char **argv) assert (dwfl != NULL); ptrdiff_t p = 0; + do + p = dwfl_getmodules (dwfl, &list_module, NULL, p); + while (p > 0); + if (p < 0) + error (2, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); + do p = dwfl_getdwarf (dwfl, &print_module, &show_functions, p); while (p > 0); if (p < 0) error (2, 0, "dwfl_getdwarf: %s", dwfl_errmsg (-1)); + p = 0; + do + p = dwfl_getmodules (dwfl, &list_module, NULL, p); + while (p > 0); + if (p < 0) + error (2, 0, "dwfl_getmodules: %s", dwfl_errmsg (-1)); + dwfl_end (dwfl); return 0; -- 2.47.2