From 843f6f57d7c89a49479ee4567aedd0d5069f102e Mon Sep 17 00:00:00 2001 From: Roland McGrath Date: Sun, 11 Sep 2005 08:49:19 +0000 Subject: [PATCH] 2005-09-09 Roland McGrath * elf_update.c (write_file): Stat the file and fchmod it after update if its mode had S_ISUID or S_ISGID bits set. --- libdw/ChangeLog | 11 +++ libdw/Makefile.am | 6 +- libdw/dwarf_entry_breakpoints.c | 200 ++++++++++++++++++++++++++++++++++++++++ libdw/dwarf_entrypc.c | 35 +++++++ libdw/libdw.h | 10 ++ libdw/libdwP.h | 1 + libelf/ChangeLog | 5 + libelf/elf_update.c | 21 +++++ 8 files changed, 286 insertions(+), 3 deletions(-) create mode 100644 libdw/dwarf_entry_breakpoints.c create mode 100644 libdw/dwarf_entrypc.c diff --git a/libdw/ChangeLog b/libdw/ChangeLog index db069c9..006da6d 100644 --- a/libdw/ChangeLog +++ b/libdw/ChangeLog @@ -1,3 +1,14 @@ +2005-09-07 Roland McGrath + + * dwarf_entry_breakpoints.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * libdw.h: Declare dwarf_entry_breakpoints. + + * dwarf_entrypc.c: New file. + * Makefile.am (libdw_a_SOURCES): Add it. + * libdw.h: Declare dwarf_entrypc. + * libdwP.h: Add INTDECL. + 2005-08-28 Ulrich Drepper * Makefile.am: Use $(LINK) not $(CC) when creating DSO. diff --git a/libdw/Makefile.am b/libdw/Makefile.am index a35f5dc..4121b0f 100644 --- a/libdw/Makefile.am +++ b/libdw/Makefile.am @@ -42,7 +42,7 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_attr_integrate.c dwarf_hasattr_integrate.c \ dwarf_child.c dwarf_haschildren.c dwarf_formaddr.c \ dwarf_formudata.c dwarf_formsdata.c dwarf_lowpc.c \ - dwarf_haspc.c dwarf_highpc.c \ + dwarf_entrypc.c dwarf_haspc.c dwarf_highpc.c \ dwarf_formref.c dwarf_formref_die.c dwarf_siblingof.c \ dwarf_dieoffset.c dwarf_cuoffset.c dwarf_hasattr.c \ dwarf_hasform.c dwarf_whatform.c dwarf_whatattr.c \ @@ -68,8 +68,8 @@ libdw_a_SOURCES = dwarf_begin.c dwarf_begin_elf.c dwarf_end.c dwarf_getelf.c \ dwarf_func_line.c dwarf_func_col.c dwarf_func_die.c \ dwarf_func_inline.c dwarf_getsrc_file.c \ libdw_findcu.c libdw_form.c libdw_alloc.c memory-access.c \ - libdw_visit_scopes.c - + libdw_visit_scopes.c \ + dwarf_entry_breakpoints.c if !MUDFLAP libdw_pic_a_SOURCES = diff --git a/libdw/dwarf_entry_breakpoints.c b/libdw/dwarf_entry_breakpoints.c new file mode 100644 index 0000000..a8fb294 --- /dev/null +++ b/libdw/dwarf_entry_breakpoints.c @@ -0,0 +1,200 @@ +/* Find entry breakpoint locations for a function. + Copyright (C) 2005 Red Hat, Inc. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif +#include "libdwP.h" +#include +#include + + +int +dwarf_entry_breakpoints (die, bkpts) + Dwarf_Die *die; + Dwarf_Addr **bkpts; +{ + int nbkpts = 0; + *bkpts = NULL; + + /* Add one breakpoint location to the result vector. */ + inline int add_bkpt (Dwarf_Addr pc) + { + Dwarf_Addr *newlist = realloc (*bkpts, ++nbkpts * sizeof newlist[0]); + if (newlist == NULL) + { + free (*bkpts); + *bkpts = NULL; + __libdw_seterrno (DWARF_E_NOMEM); + return -1; + } + newlist[nbkpts - 1] = pc; + *bkpts = newlist; + return nbkpts; + } + + /* Fallback result, break at the entrypc/lowpc value. */ + inline int entrypc_bkpt (void) + { + Dwarf_Addr pc; + return INTUSE(dwarf_entrypc) (die, &pc) < 0 ? -1 : add_bkpt (pc); + } + + /* Fetch the CU's line records to look for this DIE's addresses. */ + Dwarf_Die cudie = + { + .cu = die->cu, + .addr = ((char *) die->cu->dbg->sectiondata[IDX_debug_info]->d_buf + + die->cu->start + 3 * die->cu->offset_size - 4 + 3), + }; + Dwarf_Lines *lines; + size_t nlines; + if (INTUSE(dwarf_getsrclines) (&cudie, &lines, &nlines) < 0) + { + int error = INTUSE (dwarf_errno) (); + if (error == DWARF_E_NO_DEBUG_LINE) + return entrypc_bkpt (); + __libdw_seterrno (error); + return -1; + } + + /* Search a contiguous PC range for prologue-end markers. + If DWARF, look for proper markers. + Failing that, if ADHOC, look for the ad hoc convention. */ + inline int search_range (Dwarf_Addr low, Dwarf_Addr high, + bool dwarf, bool adhoc) + { + size_t l = 0, u = nlines; + while (l < u) + { + size_t idx = (l + u) / 2; + if (low < lines->info[idx].addr) + u = idx; + else + { + l = idx; + if (low > lines->info[idx].addr) + { + if (lines->info[idx].addr < high) + break; + ++l; + } + else + break; + } + } + if (l < u) + { + if (dwarf) + while (l < u && lines->info[l].addr < high) + if (lines->info[l].prologue_end + && add_bkpt (lines->info[l].addr) < 0) + return -1; + if (adhoc && nbkpts == 0 + && l + 1 < nlines + && lines->info[l + 1].line == lines->info[l].line + && lines->info[l + 1].file == lines->info[l].file + && lines->info[l + 1].column == lines->info[l].column) + return add_bkpt (lines->info[l + 1].addr); + return nbkpts; + } + __libdw_seterrno (DWARF_E_INVALID_DWARF); + return -1; + } + + /* Most often there is a single contiguous PC range for the DIE. */ + Dwarf_Addr lowpc; + Dwarf_Addr highpc; + if (INTUSE(dwarf_lowpc) (die, &lowpc) == 0) + return (INTUSE(dwarf_highpc) (die, &highpc) + ?: search_range (lowpc, highpc, true, true)); + + + /* We have to look for a noncontiguous range. */ + Dwarf_Attribute attr_mem; + Dwarf_Attribute *attr = INTUSE(dwarf_attr) (die, DW_AT_ranges, &attr_mem); + if (attr == NULL) + return -1; + + /* Must have the form data4 or data8 which act as an offset. */ + Dwarf_Word offset; + if (INTUSE(dwarf_formudata) (attr, &offset) != 0) + return -1; + + const Elf_Data *d = die->cu->dbg->sectiondata[IDX_debug_ranges]; + if (d == NULL) + { + __libdw_seterrno (DWARF_E_NO_DEBUG_RANGES); + return -1; + } + + /* Fetch the CU's base address. */ + Dwarf_Addr base; + if (INTUSE(dwarf_lowpc) (&cudie, &base) != 0) + return -1; + + /* Search each contiguous address range for DWARF prologue_end markers. */ + unsigned char *readp = d->d_buf + offset; + Dwarf_Addr begin; + Dwarf_Addr end; + lowpc = highpc = (Dwarf_Addr) -1l; + do + { + next: + if ((unsigned char *) d->d_buf + d->d_size - readp + < die->cu->address_size * 2) + { + __libdw_seterrno (DWARF_E_INVALID_DWARF); + free (*bkpts); + *bkpts = NULL; + return -1; + } + + if (die->cu->address_size == 8) + { + begin = read_8ubyte_unaligned_inc (die->cu->dbg, readp); + end = read_8ubyte_unaligned_inc (die->cu->dbg, readp); + } + else + { + begin = (Dwarf_Sword) read_4sbyte_unaligned_inc (die->cu->dbg, + readp); + end = read_4ubyte_unaligned_inc (die->cu->dbg, readp); + } + + if (begin == (Dwarf_Addr) -1l) /* Base address entry. */ + { + base = end; + goto next; + } + + if (begin == 0 && end == 0) /* End of list entry. */ + break; + + /* We have an address range entry. */ + if (begin < lowpc) + { + lowpc = begin; + highpc = end; + } + } + while (search_range (begin, end, true, false) >= 0); + + /* If we didn't find any proper DWARF markers, then look in the + lowest-addressed range for an ad hoc marker. Failing that, + fall back to just using the entrypc value. */ + return (nbkpts + ?: (lowpc == (Dwarf_Addr) -1l ? 0 + : search_range (lowpc, highpc, false, true)) + ?: entrypc_bkpt ()); +} diff --git a/libdw/dwarf_entrypc.c b/libdw/dwarf_entrypc.c new file mode 100644 index 0000000..61a1d4b --- /dev/null +++ b/libdw/dwarf_entrypc.c @@ -0,0 +1,35 @@ +/* Return entry PC attribute of DIE. + Copyright (C) 2003, 2005 Red Hat, Inc. + + This program is Open Source software; you can redistribute it and/or + modify it under the terms of the Open Software License version 1.0 as + published by the Open Source Initiative. + + You should have received a copy of the Open Software License along + with this program; if not, you may obtain a copy of the Open Software + License version 1.0 from http://www.opensource.org/licenses/osl.php or + by writing the Open Source Initiative c/o Lawrence Rosen, Esq., + 3001 King Ranch Road, Ukiah, CA 95482. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "libdwP.h" + + +int +dwarf_entrypc (die, return_addr) + Dwarf_Die *die; + Dwarf_Addr *return_addr; +{ + Dwarf_Attribute attr_mem; + + return INTUSE(dwarf_formaddr) (INTUSE(dwarf_attr) (die, DW_AT_entry_pc, + &attr_mem) + ?: INTUSE(dwarf_attr) (die, DW_AT_low_pc, + &attr_mem), + return_addr); +} +INTDEF(dwarf_entrypc) diff --git a/libdw/libdw.h b/libdw/libdw.h index 350aa44..b76d432 100644 --- a/libdw/libdw.h +++ b/libdw/libdw.h @@ -292,6 +292,10 @@ extern int dwarf_highpc (Dwarf_Die *die, Dwarf_Addr *return_addr) extern int dwarf_lowpc (Dwarf_Die *die, Dwarf_Addr *return_addr) __nonnull_attribute__ (2); +/* Return entry_pc or low_pc attribute of DIE. */ +extern int dwarf_entrypc (Dwarf_Die *die, Dwarf_Addr *return_addr) + __nonnull_attribute__ (2); + /* Return 1 if DIE's lowpc/highpc or ranges attributes match the PC address, 0 if not, or -1 for errors. */ extern int dwarf_haspc (Dwarf_Die *die, Dwarf_Addr pc); @@ -530,6 +534,12 @@ extern int dwarf_func_inline_instances (Dwarf_Func *func, void *arg); +/* Find the appropriate PC location or locations for function entry + breakpoints for the given DW_TAG_subprogram DIE. Returns -1 for errors. + On success, returns the number of breakpoint locations (never zero) + and sets *BKPTS to a malloc'd vector of addresses. */ +extern int dwarf_entry_breakpoints (Dwarf_Die *die, Dwarf_Addr **bkpts); + /* Call callback function for each of the macro information entry for the CU. */ diff --git a/libdw/libdwP.h b/libdw/libdwP.h index 37f2872..d7ae776 100644 --- a/libdw/libdwP.h +++ b/libdw/libdwP.h @@ -373,6 +373,7 @@ INTDECL (dwarf_child) INTDECL (dwarf_dieoffset) INTDECL (dwarf_diename) INTDECL (dwarf_end) +INTDECL (dwarf_entrypc) INTDECL (dwarf_errmsg) INTDECL (dwarf_formaddr) INTDECL (dwarf_formblock) diff --git a/libelf/ChangeLog b/libelf/ChangeLog index bd710ae..35667ce 100644 --- a/libelf/ChangeLog +++ b/libelf/ChangeLog @@ -1,3 +1,8 @@ +2005-09-09 Roland McGrath + + * elf_update.c (write_file): Stat the file and fchmod it after update + if its mode had S_ISUID or S_ISGID bits set. + 2005-08-28 Ulrich Drepper * elf32_getphdr.c: Include . Use pread_retry instead of diff --git a/libelf/elf_update.c b/libelf/elf_update.c index 5d6c6b7..f380e2b 100644 --- a/libelf/elf_update.c +++ b/libelf/elf_update.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "libelfP.h" @@ -31,6 +32,14 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum) { int class = elf->class; + /* Check the mode bits now, before modification might change them. */ + struct stat st; + if (unlikely (fstat (elf->fildes, &st) != 0)) + { + __libelf_seterrno (ELF_E_WRITE_ERROR); + return -1; + } + /* Adjust the size in any case. We do this even if we use `write'. We cannot do this if this file is in an archive. We also don't do it *now* if we are shortening the file since this would @@ -87,6 +96,18 @@ write_file (Elf *elf, off_t size, int change_bo, size_t shnum) size = -1; } + /* POSIX says that ftruncate and write may clear the S_ISUID and S_ISGID + mode bits. So make sure we restore them afterwards if they were set. + This is not atomic if someone else chmod's the file while we operate. */ + if (size != -1 + && unlikely (st.st_mode & (S_ISUID | S_ISGID)) + /* fchmod ignores the bits we cannot change. */ + && unlikely (fchmod (elf->fildes, st.st_mode) != 0)) + { + __libelf_seterrno (ELF_E_WRITE_ERROR); + size = -1; + } + if (size != -1 && elf->parent == NULL) elf->maximum_size = size; -- 2.7.4