RPM Package Manager, CVS Repository http://rpm5.org/cvs/ ____________________________________________________________________________
Server: rpm5.org Name: Jeff Johnson Root: /v/rpm/cvs Email: j...@rpm5.org Module: rpm Date: 06-Mar-2017 18:52:55 Branch: rpm-5_4 Handle: 2017030617525401 Modified files: (Branch: rpm-5_4) rpm CHANGES configure.ac rpm/rpmio digest.c librpmio.vers rpmiotypes.h rpm/tools debugedit.c Log: - debugedit: upgrade to handle resizable elf section. Summary: Revision Changes Path 1.3501.2.525+1 -0 rpm/CHANGES 2.472.2.158 +18 -2 rpm/configure.ac 2.93.2.10 +5 -0 rpm/rpmio/digest.c 2.199.2.65 +1 -0 rpm/rpmio/librpmio.vers 1.47.2.23 +10 -0 rpm/rpmio/rpmiotypes.h 2.23.2.20 +1405 -399 rpm/tools/debugedit.c ____________________________________________________________________________ patch -p0 <<'@@ .' Index: rpm/CHANGES ============================================================================ $ cvs diff -u -r1.3501.2.524 -r1.3501.2.525 CHANGES --- rpm/CHANGES 5 Mar 2017 18:06:24 -0000 1.3501.2.524 +++ rpm/CHANGES 6 Mar 2017 17:52:54 -0000 1.3501.2.525 @@ -1,4 +1,5 @@ 5.4.17 -> 5.4.18: + - jbj: debugedit: upgrade to handle resizable elf section. - jbj: debugedit: sync changes before upgrading. - jbj: header: fix: test args before immutable region trailer memcpy (PLD). - jbj: fix: filter EEXIST and use rpmlog on tmpdir creation (ticket #97). @@ . patch -p0 <<'@@ .' Index: rpm/configure.ac ============================================================================ $ cvs diff -u -r2.472.2.157 -r2.472.2.158 configure.ac --- rpm/configure.ac 16 Jan 2017 18:58:06 -0000 2.472.2.157 +++ rpm/configure.ac 6 Mar 2017 17:52:54 -0000 2.472.2.158 @@ -1728,7 +1728,7 @@ AC_ARG_WITH(libelf, AS_HELP_STRING([--with-libelf], [use libelf/gelf API]), [ if test ".$withval" = .yes; then AC_MSG_RESULT(yes) - AC_CHECK_HEADERS([libelf.h gelf.h]) + AC_CHECK_HEADERS([libelf.h gelf.h dwarf.h elfutils/libdwelf.h]) if test ".$ac_cv_header_libelf_h" = .no; then dnl # <libelf.h> on Solaris is incompatible with LFS. If we couldn't dnl # include <libelf.h>, see if we can when _FILE_OFFSET_BITS is @@ -1778,7 +1778,8 @@ AC_MSG_RESULT(no) ]) fi - if test ".$ac_cv_header_gelf_h" = .yes; then + if test \( ".$ac_cv_header_gelf_h" = .yes \) -a \ + \( ".$ac_cv_header_dwarf_h" = .yes \); then AC_CHECK_LIB([elf], [elf_version], [ AC_CHECK_LIB([elf], [gelf_getvernaux], [ AC_DEFINE(HAVE_GELF_GETVERNAUX, 1, [Define to 1 if you have the gelf_getvernaux() function.]) @@ -1790,6 +1791,21 @@ WITH_LIBELF_DEBUGEDIT="debugedit" LIBS="$LIBS -lelf" ]) + if test ".$ac_cv_header_elfutils_libdwelf_h" = .yes; then +dnl # dwelf_elf_gnu_build_id was introduced in elfutils 0.159 + AC_CHECK_LIB([dw], [dwelf_elf_gnu_build_id], [ + # If possible we also want the strtab functions from elfutils 0.167. + # But we can fall back on the (unsupported) ebl alternatives if not. + LIBS="$LIBS -ldw" + AC_CHECK_LIB([dw], [dwelf_strtab_init], [ + AC_DEFINE(HAVE_LIBDW_STRTAB, 1, [Define to 1 if you have the dwelf_strtab_init() function.]) + ],[ + LIBS="$LIBS -lebl" + ]) + ],[ + AC_MSG_ERROR([elfutils does not have dwelf_elf_gnu_build_id()]) + ]) + fi else AC_MSG_ERROR([libelf/gelf API requested but not found or not usable]) fi @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/digest.c ============================================================================ $ cvs diff -u -r2.93.2.9 -r2.93.2.10 digest.c --- rpm/rpmio/digest.c 16 Jul 2016 14:40:26 -0000 2.93.2.9 +++ rpm/rpmio/digest.c 6 Mar 2017 17:52:54 -0000 2.93.2.10 @@ -182,6 +182,11 @@ return (ctx != NULL ? ctx->name : "UNKNOWN"); } +size_t rpmDigestSize(DIGEST_CTX ctx) +{ + return (ctx != NULL ? ctx->digestsize : 0); +} + const char * rpmDigestASN1(DIGEST_CTX ctx) { return (ctx != NULL ? ctx->asn1 : NULL); @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/librpmio.vers ============================================================================ $ cvs diff -u -r2.199.2.64 -r2.199.2.65 librpmio.vers --- rpm/rpmio/librpmio.vers 23 Dec 2016 17:00:02 -0000 2.199.2.64 +++ rpm/rpmio/librpmio.vers 6 Mar 2017 17:52:54 -0000 2.199.2.65 @@ -420,6 +420,7 @@ rpmDigestHashAlgo; rpmDigestInit; rpmDigestName; + rpmDigestSize; rpmDigestPoptTable; _rpmdir_debug; rpmDigestUpdate; @@ . patch -p0 <<'@@ .' Index: rpm/rpmio/rpmiotypes.h ============================================================================ $ cvs diff -u -r1.47.2.22 -r1.47.2.23 rpmiotypes.h --- rpm/rpmio/rpmiotypes.h 7 Jul 2016 12:17:31 -0000 1.47.2.22 +++ rpm/rpmio/rpmiotypes.h 6 Mar 2017 17:52:55 -0000 1.47.2.23 @@ -297,6 +297,8 @@ PGPHASHALGO_SALSA10 = 113, /*!< (private) SALSA-10 */ PGPHASHALGO_SALSA20 = 114, /*!< (private) SALSA-20 */ + PGPHASHALGO_WHIRLPOOL = 115, /*!< (private) WHIRLPOOL */ + PGPHASHALGO_MD6_224 = 128+0,/*!< (private) MD6-224 */ PGPHASHALGO_MD6_256 = 128+1,/*!< (private) MD6-256 */ PGPHASHALGO_MD6_384 = 128+2,/*!< (private) MD6-384 */ @@ -445,6 +447,14 @@ RPM_GNUC_PURE; /** \ingroup rpmpgp + * Return digest size in bytes. + * @param ctx digest context + * @return digest size in bytes + */ +size_t rpmDigestSize(DIGEST_CTX ctx) + RPM_GNUC_PURE; + +/** \ingroup rpmpgp * Return digest ASN1 oid string. * Values from PKCS#1 v2.1 (aka RFC-3447). * @param ctx digest context @@ . patch -p0 <<'@@ .' Index: rpm/tools/debugedit.c ============================================================================ $ cvs diff -u -r2.23.2.19 -r2.23.2.20 debugedit.c --- rpm/tools/debugedit.c 5 Mar 2017 18:06:24 -0000 2.23.2.19 +++ rpm/tools/debugedit.c 6 Mar 2017 17:52:55 -0000 2.23.2.20 @@ -1,6 +1,7 @@ -/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016 Red Hat, Inc. +/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016, 2017 Red Hat, Inc. Written by Alexander Larsson <al...@redhat.com>, 2002 Based on code by Jakub Jelinek <ja...@redhat.com>, 2001. + String/Line table rewriting by Mark Wielaard <m...@redhat.com>, 2017. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ /* Needed for libelf */ #define _FILE_OFFSET_BITS 64 +#include <assert.h> #include <byteswap.h> #include <endian.h> #include <errno.h> @@ -28,6 +30,8 @@ #include <limits.h> #include <string.h> #include <stdlib.h> +#include <stdint.h> +#include <inttypes.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> @@ -35,45 +39,39 @@ #include <popt.h> #include <gelf.h> +#include <dwarf.h> -#ifndef EM_AARCH64 -#define EM_AARCH64 183 /* ARM AARCH64 */ +/* Unfortunately strtab manipulation functions were only officially added + to elfutils libdw in 0.167. Before that there were internal unsupported + ebl variants. While libebl.h isn't supported we'll try to use it anyway + if the elfutils we build against is too old. */ +#include <elfutils/version.h> +#if _ELFUTILS_PREREQ (0, 167) +#include <elfutils/libdwelf.h> +typedef Dwelf_Strent Strent; +typedef Dwelf_Strtab Strtab; +#define strtab_init dwelf_strtab_init +#define strtab_add(X,Y) dwelf_strtab_add(X,Y) +#define strtab_add_len(X,Y,Z) dwelf_strtab_add_len(X,Y,Z) +#define strtab_free dwelf_strtab_free +#define strtab_finalize dwelf_strtab_finalize +#define strent_offset dwelf_strent_off +#else +#include <elfutils/libebl.h> +typedef struct Ebl_Strent Strent; +typedef struct Ebl_Strtab Strtab; +#define strtab_init ebl_strtabinit +#define strtab_add(X,Y) ebl_strtabadd(X,Y,0) +#define strtab_add_len(X,Y,Z) ebl_strtabadd(X,Y,Z) +#define strtab_free ebl_strtabfree +#define strtab_finalize ebl_strtabfinalize +#define strent_offset ebl_strtaboffset #endif -#ifndef R_AARCH64_ABS32 -#define R_AARCH64_ABS32 258 -#endif - -/* some defines taken from the dwarf standard */ - -#define DW_TAG_compile_unit 0x11 - -#define DW_AT_name 0x03 -#define DW_AT_stmt_list 0x10 -#define DW_AT_comp_dir 0x1b - -#define DW_FORM_addr 0x01 -#define DW_FORM_block2 0x03 -#define DW_FORM_block4 0x04 -#define DW_FORM_data2 0x05 -#define DW_FORM_data4 0x06 -#define DW_FORM_data8 0x07 -#define DW_FORM_string 0x08 -#define DW_FORM_block 0x09 -#define DW_FORM_block1 0x0a -#define DW_FORM_data1 0x0b -#define DW_FORM_flag 0x0c -#define DW_FORM_sdata 0x0d -#define DW_FORM_strp 0x0e -#define DW_FORM_udata 0x0f -#define DW_FORM_ref_addr 0x10 -#define DW_FORM_ref1 0x11 -#define DW_FORM_ref2 0x12 -#define DW_FORM_ref4 0x13 -#define DW_FORM_ref8 0x14 -#define DW_FORM_ref_udata 0x15 -#define DW_FORM_indirect 0x16 -#include <beecrypt/beecrypt.h> +#include <search.h> + +#include <rpmio.h> +#include <rpmpgp.h> #include "hashtab.h" #define DW_TAG_partial_unit 0x3c @@ -89,6 +87,99 @@ int do_build_id = 0; char *build_id_seed = NULL; +/* We go over the debug sections in two phases. In phase zero we keep + track of any needed changes and collect strings, indexes and + sizes. In phase one we do the actual replacements updating the + strings, indexes and writing out new debug sections. The following + keep track of various changes that might be needed. */ + +/* Whether we need to do any literal string (DW_FORM_string) replacements + in debug_info. */ +static bool need_string_replacement = false; +/* Whether we need to do any updates of the string indexes (DW_FORM_strp) + in debug_info for string indexes. */ +static bool need_strp_update = false; +/* If the debug_line changes size we will need to update the + DW_AT_stmt_list attributes indexes in the debug_info. */ +static bool need_stmt_update = false; + +/* Storage for dynamically allocated strings to put into string + table. Keep together in memory blocks of 16K. */ +#define STRMEMSIZE (16 * 1024) +struct strmemblock +{ + struct strmemblock *next; + char memory[0]; +}; + +/* We keep track of each index in the original string table and the + associated entry in the new table so we don't insert identical + strings into the new string table. If constructed correctly the + original strtab shouldn't contain duplicate strings anyway. Any + actual identical strings could be deduplicated, but searching for + and comparing the indexes is much faster than comparing strings + (and we don't have to construct replacement strings). */ +struct stridxentry +{ + uint32_t idx; /* Original index in the string table. */ + Strent *entry; /* Entry in the new table. */ +}; + +/* Storage for new string table entries. Keep together in memory to + quickly search through them with tsearch. */ +#define STRIDXENTRIES ((16 * 1024) / sizeof (struct stridxentry)) +struct strentblock +{ + struct strentblock *next; + struct stridxentry entry[0]; +}; + +/* All data to keep track of the existing and new string table. */ +struct strings +{ + Strtab *str_tab; /* The new string table. */ + char *str_buf; /* New Elf_Data d_buf. */ + struct strmemblock *blocks; /* The first strmemblock. */ + struct strmemblock *last_block; /* The currently used strmemblock. */ + size_t stridx; /* Next free byte in last block. */ + struct strentblock *entries; /* The first string index block. */ + struct strentblock *last_entries; /* The currently used strentblock. */ + size_t entryidx; /* Next free entry in the last block. */ + void *strent_root; /* strent binary search tree root. */ +}; + +struct line_table +{ + size_t old_idx; /* Original offset. */ + size_t new_idx; /* Offset in new debug_line section. */ + ssize_t size_diff; /* Difference in (header) size. */ + bool replace_dirs; /* Whether to replace any dir paths. */ + bool replace_files; /* Whether to replace any file paths. */ + + /* Header fields. */ + uint32_t unit_length; + uint16_t version; + uint32_t header_length; + uint8_t min_instr_len; + uint8_t max_op_per_instr; /* Only if version >= 4 */ + uint8_t default_is_stmt; + int8_t line_base; + uint8_t line_range; + uint8_t opcode_base; +}; + +struct debug_lines +{ + struct line_table *table; /* Malloc/Realloced. */ + size_t size; /* Total number of line_tables. + Updated by get_line_table. */ + size_t used; /* Used number of line_tables. + Updated by get_line_table. */ + size_t debug_lines_len; /* Total size of new debug_line section. + updated by edit_dwarf2_line. */ + char *line_buf; /* New Elf_Data d_buf. */ +}; + typedef struct { Elf *elf; @@ -96,15 +187,42 @@ Elf_Scn **scn; const char *filename; int lastscn; + size_t phnum; + struct strings strings; + struct debug_lines lines; GElf_Shdr shdr[0]; } DSO; +static void +setup_lines (struct debug_lines *lines) +{ + lines->table = NULL; + lines->size = 0; + lines->used = 0; + lines->debug_lines_len = 0; + lines->line_buf = NULL; +} + +static void +destroy_lines (struct debug_lines *lines) +{ + free (lines->table); + free (lines->line_buf); +} + typedef struct { unsigned char *ptr; uint32_t addend; + int ndx; } REL; +typedef struct +{ + Elf64_Addr r_offset; + int ndx; +} LINE_REL; + #define read_uleb128(ptr) ({ \ unsigned int ret = 0; \ unsigned int c; \ @@ -121,9 +239,23 @@ ret; \ }) +#define write_uleb128(ptr,val) ({ \ + uint32_t valv = (val); \ + do \ + { \ + unsigned char c = valv & 0x7f; \ + valv >>= 7; \ + if (valv) \ + c |= 0x80; \ + *ptr++ = c; \ + } \ + while (valv); \ +}) + static uint16_t (*do_read_16) (unsigned char *ptr); static uint32_t (*do_read_32) (unsigned char *ptr); -static void (*write_32) (unsigned char *ptr, GElf_Addr val); +static void (*do_write_16) (unsigned char *ptr, uint16_t val); +static void (*do_write_32) (unsigned char *ptr, uint32_t val); static int ptr_size; static int cu_version; @@ -162,11 +294,11 @@ if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size) { data = NULL; - while ((data = elf_rawdata (scn, data)) != NULL) + while ((data = elf_getdata (scn, data)) != NULL) { if (data->d_buf && offset >= data->d_off - && offset < data->d_off + (off_t)data->d_size) + && offset < (off_t) (data->d_off + data->d_size)) return (const char *) data->d_buf + (offset - data->d_off); } } @@ -175,7 +307,7 @@ } -#define read_1(ptr) *ptr++ +#define read_8(ptr) *ptr++ #define read_16(ptr) ({ \ uint16_t ret = do_read_16 (ptr); \ @@ -216,28 +348,73 @@ }) static void -dwarf2_write_le32 (unsigned char *p, GElf_Addr val) +dwarf2_write_le16 (unsigned char *p, uint16_t v) { - uint32_t v = (uint32_t) val; + p[0] = v; + p[1] = v >> 8; +} +static void +dwarf2_write_le32 (unsigned char *p, uint32_t v) +{ p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; } - static void -dwarf2_write_be32 (unsigned char *p, GElf_Addr val) +dwarf2_write_be16 (unsigned char *p, uint16_t v) { - uint32_t v = (uint32_t) val; + p[1] = v; + p[0] = v >> 8; +} +static void +dwarf2_write_be32 (unsigned char *p, uint32_t v) +{ p[3] = v; p[2] = v >> 8; p[1] = v >> 16; p[0] = v >> 24; } +#define write_8(ptr,val) ({ \ + *ptr++ = (val); \ +}) + +#define write_16(ptr,val) ({ \ + do_write_16 (ptr,val); \ + ptr += 2; \ +}) + +#define write_32(ptr,val) ({ \ + do_write_32 (ptr,val); \ + ptr += 4; \ +}) + +/* relocated writes can only be called immediately after + do_read_32_relocated. ptr must be equal to relptr->ptr (or + relend). Might just update the addend. So relocations need to be + updated at the end. */ + +#define do_write_32_relocated(ptr,val) ({ \ + if (relptr && relptr < relend && relptr->ptr == ptr) \ + { \ + if (reltype == SHT_REL) \ + do_write_32 (ptr, val - relptr->addend); \ + else \ + relptr->addend = val; \ + } \ + else \ + do_write_32 (ptr,val); \ +}) + +#define write_32_relocated(ptr,val) ({ \ + do_write_32_relocated (ptr,val); \ + ptr += 4; \ +}) + static struct { const char *name; @@ -481,90 +658,639 @@ return rv; } +/* Returns the rest of PATH if it starts with DIR_PREFIX, skipping any + / path separators, or NULL if PATH doesn't start with + DIR_PREFIX. Might return the empty string if PATH equals DIR_PREFIX + (modulo trailing slashes). Never returns path starting with '/'. */ +RPM_GNUC_PURE +static const char * +skip_dir_prefix (const char *path, const char *dir_prefix) +{ + size_t prefix_len = strlen (dir_prefix); + if (strncmp (path, dir_prefix, prefix_len) == 0) + { + path += prefix_len; + while (IS_DIR_SEPARATOR (path[0])) + path++; + return path; + } + + return 0; +} + +/* Most strings will be in the existing debug string table. But to + replace the base/dest directory prefix we need some new storage. + Keep new strings somewhat close together for faster comparison and + copying. SIZE should be at least one (and includes space for the + zero terminator). The returned pointer points to uninitialized + data. */ +static char * +new_string_storage (struct strings *strings, size_t size) +{ + assert (size > 0); + + /* If the string is extra long just create a whole block for + it. Normally strings are much smaller than STRMEMSIZE. */ + if (strings->last_block == NULL + || size > STRMEMSIZE + || strings->stridx > STRMEMSIZE + || (STRMEMSIZE - strings->stridx) < size) + { + struct strmemblock *newblock = malloc (sizeof (struct strmemblock) + + MAX (STRMEMSIZE, size)); + if (newblock == NULL) + return NULL; + + newblock->next = NULL; + + if (strings->blocks == NULL) + strings->blocks = newblock; + + if (strings->last_block != NULL) + strings->last_block->next = newblock; + + strings->last_block = newblock; + strings->stridx = 0; + } + + size_t stridx = strings->stridx; + strings->stridx += size + 1; + return &strings->last_block->memory[stridx]; +} + +/* Comparison function used for tsearch. */ static int -has_prefix (const char *str, - const char *prefix) +strent_compare (const void *a, const void *b) { - size_t str_len; - size_t prefix_len; - - str_len = strlen (str); - prefix_len = strlen (prefix); - - if (str_len < prefix_len) - return 0; - - return strncmp (str, prefix, prefix_len) == 0; + struct stridxentry *entry_a = (struct stridxentry *)a; + struct stridxentry *entry_b = (struct stridxentry *)b; + size_t idx_a = entry_a->idx; + size_t idx_b = entry_b->idx; + + if (idx_a < idx_b) + return -1; + + if (idx_a > idx_b) + return 1; + + return 0; } -static int dirty_elf; +/* Allocates and inserts a new entry for the old index if not yet + seen. Returns a stridxentry if the given index has not yet been + seen and needs to be filled in with the associated string (either + the original string or the replacement string). Returns NULL if the + idx is already known. Use in phase 0 to add all strings seen. In + phase 1 use string_find_entry instead to get existing entries. */ +static struct stridxentry * +string_find_new_entry (struct strings *strings, size_t old_idx) +{ + /* Use next entry in the pool for lookup so we can use it directly + if this is a new index. */ + struct stridxentry *entry; + + /* Keep entries close together to make key comparison fast. */ + if (strings->last_entries == NULL || strings->entryidx >= STRIDXENTRIES) + { + size_t entriessz = (sizeof (struct strentblock) + + (STRIDXENTRIES * sizeof (struct stridxentry))); + struct strentblock *newentries = malloc (entriessz); + if (newentries == NULL) + error (1, errno, "Couldn't allocate new string entries block"); + else + { + if (strings->entries == NULL) + strings->entries = newentries; + + if (strings->last_entries != NULL) + strings->last_entries->next = newentries; + + strings->last_entries = newentries; + strings->last_entries->next = NULL; + strings->entryidx = 0; + } + } + + entry = &strings->last_entries->entry[strings->entryidx]; + entry->idx = old_idx; + struct stridxentry **tres = tsearch (entry, &strings->strent_root, + strent_compare); + if (tres == NULL) + error (1, ENOMEM, "Couldn't insert new strtab idx"); + else if (*tres == entry) + { + /* idx not yet seen, must add actual str. */ + strings->entryidx++; + return entry; + } + + return NULL; /* We already know about this idx, entry already complete. */ +} + +static struct stridxentry * +string_find_entry (struct strings *strings, size_t old_idx) +{ + struct stridxentry **ret; + struct stridxentry key; + key.idx = old_idx; + ret = tfind (&key, &strings->strent_root, strent_compare); + assert (ret != NULL); /* Can only happen for a bad/non-existing old_idx. */ + return *ret; +} + +/* Adds a string_idx_entry given an index into the old/existing string + table. Should be used in phase 0. Does nothing if the index was + already registered. Otherwise it checks the string associated with + the index. If the old string doesn't start with base_dir an entry + will be recorded for the index with the same string. Otherwise a + string will be recorded where the base_dir prefix will be replaced + by dest_dir. Returns true if this is a not yet seen index and there + a replacement file string has been recorded for it, otherwise + returns false. */ +static bool +record_file_string_entry_idx (struct strings *strings, size_t old_idx) +{ + bool ret = false; + struct stridxentry *entry = string_find_new_entry (strings, old_idx); + if (entry != NULL) + { + Strent *strent; + const char *old_str = (char *)debug_sections[DEBUG_STR].data + old_idx; + const char *file = skip_dir_prefix (old_str, base_dir); + if (file == NULL) + { + /* Just record the existing string. */ + strent = strtab_add_len (strings->str_tab, old_str, + strlen (old_str) + 1); + } + else + { + /* Create and record the altered file path. */ + size_t dest_len = strlen (dest_dir); + size_t file_len = strlen (file); + size_t nsize = dest_len + 1; /* + '\0' */ + if (file_len > 0) + nsize += 1 + file_len; /* + '/' */ + char *nname = new_string_storage (strings, nsize); + if (nname == NULL) + error (1, ENOMEM, "Couldn't allocate new string storage"); + memcpy (nname, dest_dir, dest_len); + if (file_len > 0) + { + nname[dest_len] = '/'; + memcpy (nname + dest_len + 1, file, file_len + 1); + } + else + nname[dest_len] = '\0'; + + strent = strtab_add_len (strings->str_tab, nname, nsize); + ret = true; + } + if (strent == NULL) + error (1, ENOMEM, "Could not create new string table entry"); + else + entry->entry = strent; + } + + return ret; +} + +/* Same as record_new_string_file_string_entry_idx but doesn't replace + base_dir with dest_dir, just records the existing string associated + with the index. */ static void -dirty_section (unsigned int sec) +record_existing_string_entry_idx (struct strings *strings, size_t old_idx) { - elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY); - dirty_elf = 1; + struct stridxentry *entry = string_find_new_entry (strings, old_idx); + if (entry != NULL) + { + const char *str = (char *)debug_sections[DEBUG_STR].data + old_idx; + Strent *strent = strtab_add_len (strings->str_tab, + str, strlen (str) + 1); + if (strent == NULL) + error (1, ENOMEM, "Could not create new string table entry"); + else + entry->entry = strent; + } } -static int -edit_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir, int phase) +static void +setup_strings (struct strings *strings) { - unsigned char *ptr = debug_sections[DEBUG_LINE].data, *dir; - unsigned char **dirt; - unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; - unsigned char *endcu, *endprol; - unsigned char opcode_base; - uint32_t value, dirt_cnt; - size_t comp_dir_len = comp_dir ? strlen (comp_dir) : 0; - size_t abs_file_cnt = 0, abs_dir_cnt = 0; + strings->str_tab = strtab_init (false); + strings->str_buf = NULL; + strings->blocks = NULL; + strings->last_block = NULL; + strings->entries = NULL; + strings->last_entries = NULL; + strings->strent_root = NULL; +} - if (phase != 0) - return 0; +/* Noop for tdestroy. */ +static void free_node (void *p __attribute__((__unused__))) { } - /* XXX: RhBug:929365, should we error out instead of ignoring? */ +static void +destroy_strings (struct strings *strings) +{ + struct strmemblock *smb = strings->blocks; + while (smb != NULL) + { + void *old = smb; + smb = smb->next; + free (old); + } + + struct strentblock *emb = strings->entries; + while (emb != NULL) + { + void *old = emb; + emb = emb->next; + free (old); + } + + strtab_free (strings->str_tab); + tdestroy (strings->strent_root, &free_node); + free (strings->str_buf); +} + +/* The minimum number of line tables we pre-allocate. */ +#define MIN_LINE_TABLES 64 + +/* Gets a line_table at offset. Returns true if not yet know and + successfully read, false otherwise. Sets *table to NULL and + outputs a warning if there was a problem reading the table at the + given offset. */ +static bool +get_line_table (DSO *dso, size_t off, struct line_table **table) +{ + struct debug_lines *lines = &dso->lines; + /* Assume there aren't that many, just do a linear search. The + array is probably already sorted because the stmt_lists are + probably inserted in order. But we cannot rely on that (maybe we + should check that to make searching quicker if possible?). Once + we have all line tables for phase 1 (rewriting) we do explicitly + sort the array.*/ + for (unsigned i = 0; i < lines->used; i++) + if (lines->table[i].old_idx == off) + { + *table = &lines->table[i]; + return false; + } + + if (lines->size == lines->used) + { + struct line_table *new_table = realloc (lines->table, + (sizeof (struct line_table) + * (lines->size + + MIN_LINE_TABLES))); + if (new_table == NULL) + { + error (0, ENOMEM, "Couldn't add more debug_line tables"); + *table = NULL; + return false; + } + lines->table = new_table; + lines->size += MIN_LINE_TABLES; + } + + struct line_table *t = &lines->table[lines->used]; + *table = NULL; + + t->old_idx = off; + t->size_diff = 0; + t->replace_dirs = false; + t->replace_files = false; + + unsigned char *ptr = debug_sections[DEBUG_LINE].data; + unsigned char *endsec = ptr + debug_sections[DEBUG_LINE].size; if (ptr == NULL) - return 0; + { + error (0, 0, "%s: No .line_table section", dso->filename); + return false; + } + if (off > debug_sections[DEBUG_LINE].size) + { + error (0, 0, "%s: Invalid .line_table offset 0x%zx", + dso->filename, off); + return false; + } ptr += off; - - endcu = ptr + 4; - endcu += read_32 (ptr); + + /* unit_length */ + unsigned char *endcu = ptr + 4; + t->unit_length = read_32 (ptr); + endcu += t->unit_length; if (endcu == ptr + 0xffffffff) { error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); - return 1; + return false; } if (endcu > endsec) { error (0, 0, "%s: .debug_line CU does not fit into section", dso->filename); - return 1; + return false; } - value = read_16 (ptr); - if (value != 2 && value != 3 && value != 4) + /* version */ + t->version = read_16 (ptr); + if (t->version != 2 && t->version != 3 && t->version != 4) { error (0, 0, "%s: DWARF version %d unhandled", dso->filename, - value); - return 1; + t->version); + return false; } - - endprol = ptr + 4; - endprol += read_32 (ptr); + + /* header_length */ + unsigned char *endprol = ptr + 4; + t->header_length = read_32 (ptr); + endprol += t->header_length; if (endprol > endcu) { error (0, 0, "%s: .debug_line CU prologue does not fit into CU", dso->filename); - return 1; + return false; + } + + /* min instr len */ + t->min_instr_len = *ptr++; + + /* max op per instr, if version >= 4 */ + if (t->version >= 4) + t->max_op_per_instr = *ptr++; + + /* default is stmt */ + t->default_is_stmt = *ptr++; + + /* line base */ + t->line_base = (*(int8_t *)ptr++); + + /* line range */ + t->line_range = *ptr++; + + /* opcode base */ + t->opcode_base = *ptr++; + + if (ptr + t->opcode_base - 1 >= endcu) + { + error (0, 0, "%s: .debug_line opcode table does not fit into CU", + dso->filename); + return false; + } + lines->used++; + *table = t; + return true; +} + +static int dirty_elf; +static void +dirty_section (unsigned int sec) +{ + elf_flagdata (debug_sections[sec].elf_data, ELF_C_SET, ELF_F_DIRTY); + dirty_elf = 1; +} + +static int +line_table_cmp (const void *a, const void *b) +{ + struct line_table *ta = (struct line_table *) a; + struct line_table *tb = (struct line_table *) b; + + if (ta->old_idx < tb->old_idx) + return -1; + + if (ta->old_idx > tb->old_idx) + return 1; + + return 0; +} + + +/* Called after phase zero (which records all adjustments needed for + the line tables referenced from debug_info) and before phase one + starts (phase one will adjust the .debug_line section stmt + references using the updated data structures). */ +static void +edit_dwarf2_line (DSO *dso) +{ + Elf_Data *linedata = debug_sections[DEBUG_LINE].elf_data; + int linendx = debug_sections[DEBUG_LINE].sec; + Elf_Scn *linescn = dso->scn[linendx]; + unsigned char *old_buf = linedata->d_buf; + + /* Out with the old. */ + linedata->d_size = 0; + + /* In with the new. */ + linedata = elf_newdata (linescn); + + dso->lines.line_buf = malloc (dso->lines.debug_lines_len); + if (dso->lines.line_buf == NULL) + error (1, ENOMEM, "No memory for new .debug_line table (0x%zx bytes)", + dso->lines.debug_lines_len); + + linedata->d_size = dso->lines.debug_lines_len; + linedata->d_buf = dso->lines.line_buf; + debug_sections[DEBUG_LINE].size = linedata->d_size; + + /* Make sure the line tables are sorted on the old index. */ + qsort (dso->lines.table, dso->lines.used, sizeof (struct line_table), + line_table_cmp); + + unsigned char *ptr = linedata->d_buf; + for (unsigned ldx = 0; ldx < dso->lines.used; ldx++) + { + struct line_table *t = &dso->lines.table[ldx]; + unsigned char *optr = old_buf + t->old_idx; + t->new_idx = ptr - (unsigned char *) linedata->d_buf; + + /* Just copy the whole table if nothing needs replacing. */ + if (! t->replace_dirs && ! t->replace_files) + { + assert (t->size_diff == 0); + memcpy (ptr, optr, t->unit_length + 4); + ptr += t->unit_length + 4; + continue; + } + + /* Header fields. */ + write_32 (ptr, t->unit_length + t->size_diff); + write_16 (ptr, t->version); + write_32 (ptr, t->header_length + t->size_diff); + write_8 (ptr, t->min_instr_len); + if (t->version >= 4) + write_8 (ptr, t->max_op_per_instr); + write_8 (ptr, t->default_is_stmt); + write_8 (ptr, t->line_base); + write_8 (ptr, t->line_range); + write_8 (ptr, t->opcode_base); + + optr += (4 /* unit len */ + + 2 /* version */ + + 4 /* header len */ + + 1 /* min instr len */ + + (t->version >= 4) /* max op per instr, if version >= 4 */ + + 1 /* default is stmt */ + + 1 /* line base */ + + 1 /* line range */ + + 1); /* opcode base */ + + /* opcode len table. */ + memcpy (ptr, optr, t->opcode_base - 1); + optr += t->opcode_base - 1; + ptr += t->opcode_base - 1; + + /* directory table. We need to find the end (start of file + table) anyway, so loop over all dirs, even if replace_dirs is + false. */ + while (*optr != 0) + { + const char *dir = (const char *) optr; + const char *file_path = NULL; + if (t->replace_dirs) + { + file_path = skip_dir_prefix (dir, base_dir); + if (file_path != NULL) + { + size_t dest_len = strlen (dest_dir); + size_t file_len = strlen (file_path); + memcpy (ptr, dest_dir, dest_len); + ptr += dest_len; + if (file_len > 0) + { + *ptr++ = '/'; + memcpy (ptr, file_path, file_len); + ptr += file_len; + } + *ptr++ = '\0'; + } + } + if (file_path == NULL) + { + size_t dir_len = strlen (dir); + memcpy (ptr, dir, dir_len + 1); + ptr += dir_len + 1; + } + + optr = (unsigned char *) strchr (dir, 0) + 1; + } + optr++; + *ptr++ = '\0'; + + /* file table */ + if (t->replace_files) + { + while (*optr != 0) + { + const char *file = (const char *) optr; + const char *file_path = NULL; + if (t->replace_dirs) + { + file_path = skip_dir_prefix (file, base_dir); + if (file_path != NULL) + { + size_t dest_len = strlen (dest_dir); + size_t file_len = strlen (file_path); + memcpy (ptr, dest_dir, dest_len); + ptr += dest_len; + if (file_len > 0) + { + *ptr++ = '/'; + memcpy (ptr, file_path, file_len); + ptr += file_len; + } + *ptr++ = '\0'; + } + } + if (file_path == NULL) + { + size_t file_len = strlen (file); + memcpy (ptr, file, file_len + 1); + ptr += file_len + 1; + } + + optr = (unsigned char *) strchr (file, 0) + 1; + + /* dir idx, time, len */ + uint32_t dir_idx = read_uleb128 (optr); + write_uleb128 (ptr, dir_idx); + uint32_t time = read_uleb128 (optr); + write_uleb128 (ptr, time); + uint32_t len = read_uleb128 (optr); + write_uleb128 (ptr, len); + } + optr++; + *ptr++ = '\0'; + } + + /* line number program (and file table if not copied above). */ + size_t remaining = (t->unit_length + 4 + - (optr - (old_buf + t->old_idx))); + memcpy (ptr, optr, remaining); + ptr += remaining; } - - opcode_base = ptr[4 + (value >= 4)]; - ptr = dir = ptr + 4 + (value >= 4) + opcode_base; - +} + +/* Called during phase zero for each debug_line table referenced from + .debug_info. Outputs all source files seen and records any + adjustments needed in the debug_list data structures. Returns true + if line_table needs to be rewrite either the dir or file paths. */ +static bool +read_dwarf2_line (DSO *dso, uint32_t off, char *comp_dir) +{ + unsigned char *ptr, *dir; + unsigned char **dirt; + uint32_t value, dirt_cnt; + size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); + struct line_table *table; + + if (get_line_table (dso, off, &table) == false + || table == NULL) + { + if (table != NULL) + error (0, 0, ".debug_line offset 0x%x referenced multiple times", + off); + return false; + } + + /* Skip to the directory table. The rest of the header has already + been read and checked by get_line_table. */ + ptr = debug_sections[DEBUG_LINE].data + off; + ptr += (4 /* unit len */ + + 2 /* version */ + + 4 /* header len */ + + 1 /* min instr len */ + + (table->version >= 4) /* max op per instr, if version >= 4 */ + + 1 /* default is stmt */ + + 1 /* line base */ + + 1 /* line range */ + + 1 /* opcode base */ + + table->opcode_base - 1); /* opcode len table */ + dir = ptr; + /* dir table: */ value = 1; while (*ptr != 0) { + if (base_dir && dest_dir) + { + /* Do we need to replace any of the dirs? Calculate new size. */ + const char *file_path = skip_dir_prefix ((const char *)ptr, + base_dir); + if (file_path != NULL) + { + size_t old_size = strlen ((const char *)ptr) + 1; + size_t file_len = strlen (file_path); + size_t new_size = strlen (dest_dir) + 1; + if (file_len > 0) + new_size += 1 + file_len; + table->size_diff += (new_size - old_size); + table->replace_dirs = true; + } + } + ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; ++value; } @@ -594,21 +1320,34 @@ { error (0, 0, "%s: Wrong directory table index %u", dso->filename, value); - return 1; + return false; } file_len = strlen (file); + if (base_dir && dest_dir) + { + /* Do we need to replace any of the files? Calculate new size. */ + const char *file_path = skip_dir_prefix (file, base_dir); + if (file_path != NULL) + { + size_t old_size = file_len + 1; + size_t file_len = strlen (file_path); + size_t new_size = strlen (dest_dir) + 1; + if (file_len > 0) + new_size += 1 + file_len; + table->size_diff += (new_size - old_size); + table->replace_files = true; + } + } dir_len = strlen ((char *)dirt[value]); s = malloc (comp_dir_len + 1 + file_len + 1 + dir_len + 1); if (s == NULL) { error (0, ENOMEM, "%s: Reading file table", dso->filename); - return 1; + return false; } if (*file == '/') { memcpy (s, file, file_len + 1); - if (dest_dir && has_prefix (file, base_dir)) - ++abs_file_cnt; } else if (*dirt[value] == '/') { @@ -632,13 +1371,15 @@ canonicalize_path (s, s); if (list_file_fd != -1) { - char *p = NULL; + const char *p = NULL; if (base_dir == NULL) p = s; - else if (has_prefix (s, base_dir)) - p = s + strlen (base_dir); - else if (has_prefix (s, dest_dir)) - p = s + strlen (dest_dir); + else + { + p = skip_dir_prefix (s, base_dir); + if (p == NULL && dest_dir != NULL) + p = skip_dir_prefix (s, dest_dir); + } if (p) { @@ -659,112 +1400,29 @@ read_uleb128 (ptr); read_uleb128 (ptr); } - ++ptr; - - if (dest_dir) - { - unsigned char *srcptr, *buf = NULL; - size_t base_len = strlen (base_dir); - size_t dest_len = strlen (dest_dir); - size_t shrank = 0; - - if (dest_len == base_len) - abs_file_cnt = 0; - if (abs_file_cnt) - { - srcptr = buf = malloc (ptr - dir); - memcpy (srcptr, dir, ptr - dir); - ptr = dir; - } - else - ptr = srcptr = dir; - while (*srcptr != 0) - { - size_t len = strlen ((char *)srcptr) + 1; - const unsigned char *readptr = srcptr; - - char *orig = strdup ((const char *) srcptr); - - if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir)) - { - if (dest_len < base_len) - ++abs_dir_cnt; - memcpy (ptr, dest_dir, dest_len); - ptr += dest_len; - readptr += base_len; - } - srcptr += len; - - shrank += srcptr - readptr; - canonicalize_path ((char *)readptr, (char *)ptr); - len = strlen ((char *)ptr) + 1; - shrank -= len; - ptr += len; - - if (memcmp (orig, ptr - len, len)) - dirty_section (DEBUG_STR); - free (orig); - } - - if (shrank > 0) - { - if (--shrank == 0) - error (EXIT_FAILURE, 0, - "canonicalization unexpectedly shrank by one character"); - else - { - memset (ptr, 'X', shrank); - ptr += shrank; - *ptr++ = '\0'; - } - } - - if (abs_dir_cnt + abs_file_cnt != 0) - { - size_t len = (abs_dir_cnt + abs_file_cnt) * (base_len - dest_len); - - if (len == 1) - error (EXIT_FAILURE, 0, "-b arg has to be either the same length as -d arg, or more than 1 char longer"); - memset (ptr, 'X', len - 1); - ptr += len - 1; - *ptr++ = '\0'; - } - *ptr++ = '\0'; - ++srcptr; - - while (*srcptr != 0) - { - size_t len = strlen ((char *)srcptr) + 1; - if (*srcptr == '/' && has_prefix ((char *)srcptr, base_dir)) - { - memcpy (ptr, dest_dir, dest_len); - if (dest_len < base_len) - { - memmove (ptr + dest_len, srcptr + base_len, - len - base_len); - ptr += dest_len - base_len; - } - dirty_section (DEBUG_STR); - } - else if (ptr != srcptr) - memmove (ptr, srcptr, len); - srcptr += len; - ptr += len; - dir = srcptr; - read_uleb128 (srcptr); - read_uleb128 (srcptr); - read_uleb128 (srcptr); - if (ptr != dir) - memmove (ptr, dir, srcptr - dir); - ptr += srcptr - dir; - } - *ptr = '\0'; - free (buf); - } - return 0; + dso->lines.debug_lines_len += 4 + table->unit_length + table->size_diff; + return table->replace_dirs || table->replace_files; } +/* Called during phase one, after the table has been sorted. */ +RPM_GNUC_PURE +static size_t +find_new_list_offs (struct debug_lines *lines, size_t idx) +{ + struct line_table key; + key.old_idx = idx; + struct line_table *table = bsearch (&key, lines->table, + lines->used, + sizeof (struct line_table), + line_table_cmp); + return table->new_idx; +} + +/* This scans the attributes of one DIE described by the given abbrev_tag. + PTR points to the data in the debug_info. It will be advanced till all + abbrev data is consumed. In phase zero data is collected, in phase one + data might be replaced/updated. */ static unsigned char * edit_attributes (DSO *dso, unsigned char *ptr, struct abbrev_tag *t, int phase) { @@ -772,7 +1430,7 @@ uint32_t list_offs; int found_list_offs; char *comp_dir; - + comp_dir = NULL; list_offs = 0; found_list_offs = 0; @@ -780,65 +1438,102 @@ { uint32_t form = t->attr[i].form; size_t len = 0; - size_t base_len, dest_len; - while (1) { + /* Whether we already handled a string as file for this + attribute. If we did then we don't need to handle/record + it again when handling the DW_FORM_strp later. */ + bool handled_strp = false; + + /* A stmt_list points into the .debug_line section. In + phase zero record all offsets. Then in phase one replace + them with the new offsets if we rewrote the line + tables. */ if (t->attr[i].attr == DW_AT_stmt_list) { if (form == DW_FORM_data4 || form == DW_FORM_sec_offset) { list_offs = do_read_32_relocated (ptr); - found_list_offs = 1; + if (phase == 0) + found_list_offs = 1; + else if (need_stmt_update) /* phase one */ + { + size_t idx, new_idx; + idx = do_read_32_relocated (ptr); + new_idx = find_new_list_offs (&dso->lines, idx); + do_write_32_relocated (ptr, new_idx); + } } } + /* DW_AT_comp_dir is the current working directory. */ if (t->attr[i].attr == DW_AT_comp_dir) { if (form == DW_FORM_string) { free (comp_dir); comp_dir = strdup ((char *)ptr); - - if (phase == 1 && dest_dir && has_prefix ((char *)ptr, base_dir)) - { - base_len = strlen (base_dir); - dest_len = strlen (dest_dir); - - memcpy (ptr, dest_dir, dest_len); - if (dest_len < base_len) - { - memset(ptr + dest_len, '/', - base_len - dest_len); + if (dest_dir) + { + /* In phase zero we are just collecting dir/file + names and check whether any need to be + adjusted. If so, in phase one we replace + those dir/files. */ + const char *file = skip_dir_prefix (comp_dir, base_dir); + if (file != NULL && phase == 0) + need_string_replacement = true; + else if (file != NULL && phase == 1) + { + size_t orig_len = strlen (comp_dir); + size_t dest_len = strlen (dest_dir); + size_t file_len = strlen (file); + size_t new_len = dest_len; + if (file_len > 0) + new_len += 1 + file_len; /* + '/' */ + + /* We don't want to rewrite the whole + debug_info section, so we only replace + the comp_dir with something equal or + smaller, possibly adding some slashes + at the end of the new compdir. This + normally doesn't happen since most + producers will use DW_FORM_strp which is + more efficient. */ + if (orig_len < new_len) + fprintf (stderr, "Warning, not replacing comp_dir " + "'%s' prefix ('%s' -> '%s') encoded as " + "DW_FORM_string. " + "Replacement too large.\n", + comp_dir, base_dir, dest_dir); + else + { + /* Add one or more slashes in between to + fill up all space (replacement must be + of the same length). */ + memcpy (ptr, dest_dir, dest_len); + memset (ptr + dest_len, '/', + orig_len - new_len + 1); + } } - dirty_section (DEBUG_INFO); } } else if (form == DW_FORM_strp && debug_sections[DEBUG_STR].data) { - char *dir; - - dir = (char *) debug_sections[DEBUG_STR].data - + do_read_32_relocated (ptr); + const char *dir; + size_t idx = do_read_32_relocated (ptr); + dir = (char *) debug_sections[DEBUG_STR].data + idx; free (comp_dir); comp_dir = strdup (dir); - if (phase == 1 && dest_dir && has_prefix (dir, base_dir)) - { - base_len = strlen (base_dir); - dest_len = strlen (dest_dir); - - memcpy (dir, dest_dir, dest_len); - if (dest_len < base_len) - { - memmove (dir + dest_len, dir + base_len, - strlen (dir + base_len) + 1); - } - dirty_section (DEBUG_STR); + if (dest_dir != NULL && phase == 0) + { + if (record_file_string_entry_idx (&dso->strings, idx)) + need_strp_update = true; + handled_strp = true; } } } @@ -848,10 +1543,13 @@ && form == DW_FORM_strp && debug_sections[DEBUG_STR].data) { + /* DW_AT_name is the primary file for this compile + unit. If starting with / it is a full path name. + Note that we don't handle DW_FORM_string in this + case. */ char *name; - - name = (char *) debug_sections[DEBUG_STR].data - + do_read_32_relocated (ptr); + size_t idx = do_read_32_relocated (ptr); + name = (char *) debug_sections[DEBUG_STR].data + idx; if (*name == '/' && comp_dir == NULL) { char *enddir = strrchr (name, '/'); @@ -866,18 +1564,14 @@ comp_dir = strdup ("/"); } - if (phase == 1 && dest_dir && has_prefix (name, base_dir)) - { - base_len = strlen (base_dir); - dest_len = strlen (dest_dir); - - memcpy (name, dest_dir, dest_len); - if (dest_len < base_len) - { - memmove (name + dest_len, name + base_len, - strlen (name + base_len) + 1); - } - dirty_section (DEBUG_STR); + /* First pass (0) records the new name to be + added to the debug string pool, the second + pass (1) stores it (the new index). */ + if (dest_dir && phase == 0) + { + if (record_file_string_entry_idx (&dso->strings, idx)) + need_strp_update = true; + handled_strp = true; } } @@ -919,6 +1613,29 @@ read_uleb128 (ptr); break; case DW_FORM_strp: + /* In the first pass we collect all strings, in the + second we put the new references back (if there are + any changes). */ + if (phase == 0) + { + /* handled_strp is set for attributes refering to + files. If it is set the string is already + recorded. */ + if (! handled_strp) + { + size_t idx = do_read_32_relocated (ptr); + record_existing_string_entry_idx (&dso->strings, idx); + } + } + else if (need_strp_update) /* && phase == 1 */ + { + struct stridxentry *entry; + size_t idx, new_idx; + idx = do_read_32_relocated (ptr); + entry = string_find_entry (&dso->strings, idx); + new_idx = strent_offset (entry->entry); + do_write_32_relocated (ptr, new_idx); + } ptr += 4; break; case DW_FORM_string: @@ -963,14 +1680,17 @@ CU current dir subdirectories. */ if (comp_dir && list_file_fd != -1) { - char *p; + const char *p = NULL; size_t size; - if (base_dir && has_prefix (comp_dir, base_dir)) - p = comp_dir + strlen (base_dir); - else if (dest_dir && has_prefix (comp_dir, dest_dir)) - p = comp_dir + strlen (dest_dir); - else + if (base_dir) + { + p = skip_dir_prefix (comp_dir, base_dir); + if (p == NULL && dest_dir != NULL) + p = skip_dir_prefix (comp_dir, dest_dir); + } + + if (p == NULL) p = comp_dir; size = strlen (p) + 1; @@ -984,8 +1704,13 @@ } } - if (found_list_offs && comp_dir) - edit_dwarf2_line (dso, list_offs, comp_dir, phase); + /* In phase zero we collect all file names (we need the comp_dir for + that). Note that calculating the new size and offsets is done + separately (at the end of phase zero after all CUs have been + scanned in dwarf2_edit). */ + if (phase == 0 && found_list_offs + && read_dwarf2_line (dso, list_offs, comp_dir)) + need_stmt_update = true; free (comp_dir); @@ -1007,6 +1732,20 @@ } static int +line_rel_cmp (const void *a, const void *b) +{ + LINE_REL *rela = (LINE_REL *) a, *relb = (LINE_REL *) b; + + if (rela->r_offset < relb->r_offset) + return -1; + + if (rela->r_offset > relb->r_offset) + return 1; + + return 0; +} + +static int edit_dwarf2 (DSO *dso) { Elf_Data *data; @@ -1042,9 +1781,9 @@ } scn = dso->scn[i]; - data = elf_rawdata (scn, NULL); + data = elf_getdata (scn, NULL); assert (data != NULL && data->d_buf != NULL); - assert (elf_rawdata (scn, data) == NULL); + assert (elf_getdata (scn, data) == NULL); assert (data->d_off == 0); assert (data->d_size == dso->shdr[i].sh_size); debug_sections[j].data = data->d_buf; @@ -1083,13 +1822,15 @@ { do_read_16 = buf_read_ule16; do_read_32 = buf_read_ule32; - write_32 = dwarf2_write_le32; + do_write_16 = dwarf2_write_le16; + do_write_32 = dwarf2_write_le32; } else if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) { do_read_16 = buf_read_ube16; do_read_32 = buf_read_ube32; - write_32 = dwarf2_write_be32; + do_write_16 = dwarf2_write_be16; + do_write_32 = dwarf2_write_be32; } else { @@ -1212,6 +1953,7 @@ relend->ptr = debug_sections[DEBUG_INFO].data + (rela.r_offset - base); relend->addend = rela.r_addend; + relend->ndx = ndx; ++relend; } if (relbuf == relend) @@ -1226,6 +1968,13 @@ for (phase = 0; phase < 2; phase++) { + /* If we don't need to update anyhing, skip phase 1. */ + if (phase == 1 + && !need_strp_update + && !need_string_replacement + && !need_stmt_update) + break; + ptr = debug_sections[DEBUG_INFO].data; relptr = relbuf; endsec = ptr + debug_sections[DEBUG_INFO].size; @@ -1273,7 +2022,7 @@ if (ptr_size == 0) { - ptr_size = read_1 (ptr); + ptr_size = read_8 (ptr); if (ptr_size != 4 && ptr_size != 8) { error (0, 0, "%s: Invalid DWARF pointer size %d", @@ -1281,7 +2030,7 @@ return 1; } } - else if (read_1 (ptr) != ptr_size) + else if (read_8 (ptr) != ptr_size) { error (0, 0, "%s: DWARF pointer size differs between CUs", dso->filename); @@ -1314,7 +2063,185 @@ htab_delete (abbrev); } + + /* We might have to recalculate/rewrite the debug_line + section. We need to do that before going into phase one + so we have all new offsets. We do this separately from + scanning the dirs/file names because the DW_AT_stmt_lists + might not be in order or skip some padding we might have + to (re)move. */ + if (phase == 0 && need_stmt_update) + { + edit_dwarf2_line (dso); + + /* The line table programs will be moved + forward/backwards a bit in the new data. Update the + debug_line relocations to the new offsets. */ + int rndx = debug_sections[DEBUG_LINE].relsec; + if (rndx != 0) + { + LINE_REL *rbuf; + size_t rels; + Elf_Data *rdata = elf_getdata (dso->scn[rndx], NULL); + int rtype = dso->shdr[rndx].sh_type; + rels = dso->shdr[rndx].sh_size / dso->shdr[rndx].sh_entsize; + rbuf = malloc (rels * sizeof (LINE_REL)); + if (rbuf == NULL) + error (1, errno, "%s: Could not allocate line relocations", + dso->filename); + + /* Sort them by offset into section. */ + for (size_t i = 0; i < rels; i++) + { + if (rtype == SHT_RELA) + { + GElf_Rela rela; + if (gelf_getrela (rdata, i, &rela) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + rbuf[i].r_offset = rela.r_offset; + rbuf[i].ndx = i; + } + else + { + GElf_Rel rel; + if (gelf_getrel (rdata, i, &rel) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + rbuf[i].r_offset = rel.r_offset; + rbuf[i].ndx = i; + } + } + qsort (rbuf, rels, sizeof (LINE_REL), line_rel_cmp); + + size_t lndx = 0; + for (size_t i = 0; i < rels; i++) + { + /* These relocations only happen in ET_REL files + and are section offsets. */ + GElf_Addr r_offset; + size_t ndx = rbuf[i].ndx; + + GElf_Rel rel; + GElf_Rela rela; + if (rtype == SHT_RELA) + { + if (gelf_getrela (rdata, ndx, &rela) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + r_offset = rela.r_offset; + } + else + { + if (gelf_getrel (rdata, ndx, &rel) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + r_offset = rel.r_offset; + } + + while (r_offset > (dso->lines.table[lndx].old_idx + + 4 + + dso->lines.table[lndx].unit_length) + && lndx < dso->lines.used) + lndx++; + + if (lndx >= dso->lines.used) + error (1, 0, + ".debug_line relocation offset out of range"); + + /* Offset (pointing into the line program) moves + from old to new index including the header + size diff. */ + r_offset += ((dso->lines.table[lndx].new_idx + - dso->lines.table[lndx].old_idx) + + dso->lines.table[lndx].size_diff); + + if (rtype == SHT_RELA) + { + rela.r_offset = r_offset; + if (gelf_update_rela (rdata, ndx, &rela) == 0) + error (1, 0, "Couldn't update relocation: %s", + elf_errmsg (-1)); + } + else + { + rel.r_offset = r_offset; + if (gelf_update_rel (rdata, ndx, &rel) == 0) + error (1, 0, "Couldn't update relocation: %s", + elf_errmsg (-1)); + } + } + + elf_flagdata (rdata, ELF_C_SET, ELF_F_DIRTY); + free (rbuf); + } + } + + /* Same for the debug_str section. Make sure everything is + in place for phase 1 updating of debug_info + references. */ + if (phase == 0 && need_strp_update) + { + Strtab *strtab = dso->strings.str_tab; + Elf_Data *strdata = debug_sections[DEBUG_STR].elf_data; + int strndx = debug_sections[DEBUG_STR].sec; + Elf_Scn *strscn = dso->scn[strndx]; + + /* Out with the old. */ + strdata->d_size = 0; + /* In with the new. */ + strdata = elf_newdata (strscn); + + /* We really should check whether we had enough memory, + but the old ebl version will just abort on out of + memory... */ + strtab_finalize (strtab, strdata); + debug_sections[DEBUG_STR].size = strdata->d_size; + dso->strings.str_buf = strdata->d_buf; + } + } + + /* After phase 1 we might have rewritten the debug_info with + new strp, strings and/or linep offsets. */ + if (need_strp_update || need_string_replacement || need_stmt_update) + dirty_section (DEBUG_INFO); + + /* Update any debug_info relocations addends we might have touched. */ + if (relbuf != NULL && reltype == SHT_RELA) + { + Elf_Data *symdata; + int relsec_ndx = debug_sections[DEBUG_INFO].relsec; + data = elf_getdata (dso->scn[relsec_ndx], NULL); + symdata = elf_getdata (dso->scn[dso->shdr[relsec_ndx].sh_link], + NULL); + + relptr = relbuf; + while (relptr < relend) + { + GElf_Sym sym; + GElf_Rela rela; + int ndx = relptr->ndx; + + if (gelf_getrela (data, ndx, &rela) == NULL) + error (1, 0, "Couldn't get relocation: %s", + elf_errmsg (-1)); + + if (gelf_getsym (symdata, GELF_R_SYM (rela.r_info), + &sym) == NULL) + error (1, 0, "Couldn't get symbol: %s", elf_errmsg (-1)); + + rela.r_addend = relptr->addend - sym.st_value; + + if (gelf_update_rela (data, ndx, &rela) == 0) + error (1, 0, "Couldn't update relocations: %s", + elf_errmsg (-1)); + + ++relptr; + } + elf_flagdata (data, ELF_C_SET, ELF_F_DIRTY); + } + free (relbuf); } @@ -1322,19 +2249,18 @@ } static struct poptOption optionsTable[] = { - { "base-dir", 'b', POPT_ARG_STRING, &base_dir, 0, - N_("base build DIRECTORY of objects"), N_("DIRECTORY") }, - { "dest-dir", 'd', POPT_ARG_STRING, &dest_dir, 0, - N_("DIRECTORY to rewrite base-dir into"), N_("DIRECTORY") }, - { "list-file", 'l', POPT_ARG_STRING, &list_file, 0, - N_("FILE where to put list of source and header file names"), N_("FILE") }, - { "build-id", 'i', POPT_ARG_NONE, &do_build_id, 0, - N_("recompute build-id note and print on stdout"), NULL }, - { "build-id-seed", 's', POPT_ARG_STRING, &build_id_seed, 0, - N_("if recomputing the build-id note, use this string as hash SEED"), N_("SEED") }, - - POPT_AUTOHELP - { NULL, 0, 0, NULL, 0, NULL, NULL } + { "base-dir", 'b', POPT_ARG_STRING, &base_dir, 0, + "base build directory of objects", NULL }, + { "dest-dir", 'd', POPT_ARG_STRING, &dest_dir, 0, + "directory to rewrite base-dir into", NULL }, + { "list-file", 'l', POPT_ARG_STRING, &list_file, 0, + "file where to put list of source and header file names", NULL }, + { "build-id", 'i', POPT_ARG_NONE, &do_build_id, 0, + "recompute build ID note and print ID on stdout", NULL }, + { "build-id-seed", 's', POPT_ARG_STRING, &build_id_seed, 0, + "if recomputing the build ID note use this string as hash seed", NULL }, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0, NULL, NULL } }; static DSO * @@ -1344,8 +2270,9 @@ GElf_Ehdr ehdr; int i; DSO *dso = NULL; + size_t phnum; - elf = elf_begin (fd, ELF_C_RDWR_MMAP, NULL); + elf = elf_begin (fd, ELF_C_RDWR, NULL); if (elf == NULL) { error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); @@ -1382,10 +2309,20 @@ goto error_out; } - elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT); + if (elf_getphdrnum (elf, &phnum) != 0) + { + error (0, 0, "Couldn't get number of phdrs: %s", elf_errmsg (-1)); + goto error_out; + } + + /* If there are phdrs we want to maintain the layout of the + allocated sections in the file. */ + if (phnum != 0) + elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT); memset (dso, 0, sizeof(DSO)); dso->elf = elf; + dso->phnum = phnum; dso->ehdr = ehdr; dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20]; @@ -1396,12 +2333,16 @@ } dso->filename = (const char *) strdup (name); + setup_strings (&dso->strings); + setup_lines (&dso->lines); return dso; error_out: if (dso) { free ((char *) dso->filename); + destroy_strings (&dso->strings); + destroy_lines (&dso->lines); free (dso); } if (elf) @@ -1411,53 +2352,55 @@ return NULL; } -static inline void process (hashFunctionContext * ctx, - const void *data, size_t size) +static const pgpHashAlgo algorithms[] = { PGPHASHALGO_MD5, + PGPHASHALGO_SHA1, PGPHASHALGO_SHA256, PGPHASHALGO_SHA384, PGPHASHALGO_SHA512 }; + +#ifdef DYING +static size_t _rpmDigestLength(pgpHashAlgo algorithm) { - memchunk chunk = { .data = (void *) data, .size = size }; - if (data != NULL && size != 0) - hashFunctionContextUpdateMC (ctx, &chunk); + switch (algorithm) { + default: return 0; + case PGPHASHALGO_MD5: return 128/8; + case PGPHASHALGO_SHA1: return 160/8; + case PGPHASHALGO_SHA256: return 256/8; + case PGPHASHALGO_SHA384: return 384/8; + case PGPHASHALGO_SHA512: return 512/8; + } } +#endif /* Compute a fresh build ID bit-string from the editted file contents. */ static void handle_build_id (DSO *dso, Elf_Data *build_id, size_t build_id_offset, size_t build_id_size) { - hashFunctionContext ctx; - const hashFunction *hf = NULL; - int i = hashFunctionCount (); - - while (i-- > 0) - { - hf = hashFunctionGet (i); - if (hf != NULL && hf->digestsize == build_id_size) - break; - } - if (hf == NULL) - { - fprintf (stderr, "Cannot handle %u-byte build ID\n", (unsigned) build_id_size); - exit (1); - } + DIGEST_CTX ctx = NULL; + void *digest = NULL; + size_t len; if (!dirty_elf && build_id_seed == NULL) goto print; - if (elf_update (dso->elf, ELF_C_NULL) < 0) + for (int i = sizeof(algorithms)/sizeof(algorithms[0]); i > 0; i--) { - fprintf (stderr, "Failed to update file: %s\n", - elf_errmsg (elf_errno ())); + ctx = rpmDigestInit(algorithms[i], RPMDIGEST_NONE); + if (rpmDigestSize(ctx) == build_id_size) + break; + (void) rpmDigestFinal(ctx, NULL, NULL, 0); + ctx = NULL; + } + if (ctx == NULL) + { + fprintf (stderr, "Cannot handle %Zu-byte build ID\n", build_id_size); exit (1); } /* Clear the old bits so they do not affect the new hash. */ memset ((char *) build_id->d_buf + build_id_offset, 0, build_id_size); - hashFunctionContextInit (&ctx, hf); - /* If a seed string was given use it to prime the hash. */ if (build_id_seed != NULL) - process(&ctx, build_id_seed, strlen(build_id_seed)); + rpmDigestUpdate(ctx, build_id_seed, strlen (build_id_seed)); /* Slurp the relevant header bits and section contents and feed them into the hash function. The only bits we ignore are the offset @@ -1473,16 +2416,14 @@ GElf_Ehdr ehdr; GElf_Phdr phdr; GElf_Shdr shdr; - } u1, u2; - Elf_Data src = { .d_version = EV_CURRENT, .d_buf = &u1 }; - Elf_Data dest = { .d_version = EV_CURRENT, .d_buf = &u2 }; - - src.d_type = ELF_T_EHDR; - src.d_size = sizeof u1.ehdr; - dest.d_size = sizeof u2.ehdr; - u1.ehdr = dso->ehdr; - u1.ehdr.e_phoff = u1.ehdr.e_shoff = 0; - if (elf64_xlatetom (&dest, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + } u; + Elf_Data x = { .d_version = EV_CURRENT, .d_buf = &u }; + + x.d_type = ELF_T_EHDR; + x.d_size = sizeof u.ehdr; + u.ehdr = dso->ehdr; + u.ehdr.e_phoff = u.ehdr.e_shoff = 0; + if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL) { bad: fprintf (stderr, "Failed to compute header checksum: %s\n", @@ -1490,42 +2431,41 @@ exit (1); } - src.d_type = ELF_T_PHDR; - src.d_size = sizeof u1.phdr; - dest.d_size = sizeof u2.phdr; - for (i = 0; i < dso->ehdr.e_phnum; ++i) + x.d_type = ELF_T_PHDR; + x.d_size = sizeof u.phdr; + for (int i = 0; i < dso->ehdr.e_phnum; ++i) { - if (gelf_getphdr (dso->elf, i, &u1.phdr) == NULL) + if (gelf_getphdr (dso->elf, i, &u.phdr) == NULL) goto bad; - if (elf64_xlatetom (&dest, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL) goto bad; - process (&ctx, dest.d_buf, dest.d_size); + rpmDigestUpdate(ctx, x.d_buf, x.d_size); } - src.d_type = ELF_T_SHDR; - src.d_size = sizeof u1.shdr; - dest.d_size = sizeof u2.shdr; - for (i = 0; i < dso->ehdr.e_shnum; ++i) + x.d_type = ELF_T_SHDR; + x.d_size = sizeof u.shdr; + for (int i = 0; i < dso->ehdr.e_shnum; ++i) if (dso->scn[i] != NULL) { - u1.shdr = dso->shdr[i]; - u1.shdr.sh_offset = 0; - if (elf64_xlatetom (&dest, &src, dso->ehdr.e_ident[EI_DATA]) == NULL) + u.shdr = dso->shdr[i]; + u.shdr.sh_offset = 0; + if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL) goto bad; - process (&ctx, dest.d_buf, dest.d_size); + rpmDigestUpdate(ctx, x.d_buf, x.d_size); - if (u1.shdr.sh_type != SHT_NOBITS) + if (u.shdr.sh_type != SHT_NOBITS) { - Elf_Data *d = elf_rawdata (dso->scn[i], NULL); + Elf_Data *d = elf_getdata (dso->scn[i], NULL); if (d == NULL) goto bad; - process (&ctx, d->d_buf, d->d_size); + rpmDigestUpdate(ctx, d->d_buf, d->d_size); } } } - hashFunctionContextDigest (&ctx, (byte *) build_id->d_buf + build_id_offset); - hashFunctionContextFree (&ctx); + rpmDigestFinal(ctx, &digest, &len, 0); + memcpy((unsigned char *)build_id->d_buf + build_id_offset, digest, build_id_size); + free(digest); elf_flagdata (build_id, ELF_C_SET, ELF_F_DIRTY); @@ -1533,40 +2473,11 @@ /* Now format the build ID bits in hex to print out. */ { const uint8_t * id = (uint8_t *)build_id->d_buf + build_id_offset; - char hex[build_id_size * 2 + 1]; - int n = snprintf (hex, 3, "%02" PRIx8, id[0]); - assert (n == 2); - for (i = 1; i < (int)build_id_size; ++i) - { - n = snprintf (&hex[i * 2], 3, "%02" PRIx8, id[i]); - assert (n == 2); - } + char *hex = pgpHexStr(id, build_id_size); puts (hex); } } -/* It avoided the segment fault while file's bss offset have a large number. - See https://bugzilla.redhat.com/show_bug.cgi?id=1019707 - https://bugzilla.redhat.com/show_bug.cgi?id=1020842 for detail. */ -void valid_file(int fd) -{ - Elf *elf = elf_begin (fd, ELF_C_RDWR, NULL); - if (elf == NULL) - { - error (1, 0, "elf_begin: %s", elf_errmsg (-1)); - return; - } - - elf_flagelf (elf, ELF_C_SET, ELF_F_LAYOUT); - - if (elf_update (elf, ELF_C_WRITE) < 0) - error (1, 0, "elf_update: %s", elf_errmsg (-1)); - - elf_end (elf); - - return; -} - int main (int argc, char *argv[]) { @@ -1577,12 +2488,11 @@ int nextopt; const char **args; struct stat stat_buf; - char *p; Elf_Data *build_id = NULL; size_t build_id_offset = 0, build_id_size = 0; optCon = poptGetContext("debugedit", argc, (const char **)argv, optionsTable, 0); - + while ((nextopt = poptGetNextOpt (optCon)) > 0 || nextopt == POPT_ERROR_BADOPT) /* do nothing */ ; @@ -1609,11 +2519,6 @@ fprintf (stderr, "You must specify a base dir if you specify a dest dir\n"); exit (1); } - if (strlen (dest_dir) > strlen (base_dir)) - { - fprintf (stderr, "Dest dir longer than base dir is not supported\n"); - exit (1); - } } if (build_id_seed != NULL && do_build_id == 0) @@ -1625,34 +2530,17 @@ if (build_id_seed != NULL && strlen (build_id_seed) < 1) { fprintf (stderr, - "--build-id-seed (-s) string should be at least 1 char\n"); + "--build-id-seed (-s) string should be at least 1 char\n"); exit (1); } - /* Ensure clean paths, users can muck with these */ + /* Ensure clean paths, users can muck with these. Also removes any + trailing '/' from the paths. */ if (base_dir) canonicalize_path(base_dir, base_dir); if (dest_dir) canonicalize_path(dest_dir, dest_dir); - /* Make sure there are trailing slashes in dirs */ - if (base_dir != NULL && base_dir[strlen (base_dir)-1] != '/') - { - p = malloc (strlen (base_dir) + 2); - strcpy (p, base_dir); - strcat (p, "/"); - free (base_dir); - base_dir = p; - } - if (dest_dir != NULL && dest_dir[strlen (dest_dir)-1] != '/') - { - p = malloc (strlen (dest_dir) + 2); - strcpy (p, dest_dir); - strcat (p, "/"); - free (dest_dir); - dest_dir = p; - } - if (list_file != NULL) { list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644); @@ -1682,9 +2570,6 @@ exit (1); } - /* Make sure the file is valid. */ - valid_file(fd); - dso = fdopen_dso (fd, file); if (dso == NULL) exit (1); @@ -1696,7 +2581,6 @@ switch (dso->shdr[i].sh_type) { case SHT_PROGBITS: - case SHT_MIPS_DWARF: name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); /* TODO: Handle stabs */ if (strcmp (name, ".stab") == 0) @@ -1713,7 +2597,7 @@ && build_id == NULL && (dso->shdr[i].sh_flags & SHF_ALLOC)) { /* Look for a build-ID note here. */ - Elf_Data *data = elf_rawdata (elf_getscn (dso->elf, i), NULL); + Elf_Data *data = elf_getdata (elf_getscn (dso->elf, i), NULL); Elf32_Nhdr nh; Elf_Data dst = { @@ -1751,6 +2635,123 @@ } } + /* We might have changed the size of some debug sections. If so make + sure the section headers are updated and the data offsets are + correct. We set ELF_F_LAYOUT above because we don't want libelf + to move any allocated sections around itself if there are any + phdrs. Which means we are reponsible for setting the section size + and offset fields. Plus the shdr offsets. We don't want to change + anything for the phdrs allocated sections. Keep the offset of + allocated sections so they are at the same place in the file. Add + unallocated ones after the allocated ones. */ + if (dso->phnum != 0 && (need_strp_update || need_stmt_update)) + { + Elf *elf = dso->elf; + GElf_Off last_offset; + /* We position everything after the phdrs (which normally would + be at the start of the ELF file after the ELF header. */ + last_offset = (dso->ehdr.e_phoff + gelf_fsize (elf, ELF_T_PHDR, + dso->phnum, EV_CURRENT)); + + /* First find the last allocated section. */ + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1)); + + /* Any sections we have changed aren't allocated sections, + so we don't need to lookup any changed section sizes. */ + if ((shdr->sh_flags & SHF_ALLOC) != 0) + { + GElf_Off off = shdr->sh_offset + (shdr->sh_type != SHT_NOBITS + ? shdr->sh_size : 0); + if (last_offset < off) + last_offset = off; + } + } + + /* Now adjust any sizes and offsets for the unallocated sections. */ + scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + if (shdr == NULL) + error (1, 0, "Couldn't get shdr: %s\n", elf_errmsg (-1)); + + /* A bug in elfutils before 0.169 means we have to write out + all section data, even when nothing changed. + <A HREF="https://sourceware.org/bugzilla/show_bug.cgi?id=21199">https://sourceware.org/bugzilla/show_bug.cgi?id=21199</A> */ +#if !_ELFUTILS_PREREQ (0, 169) + if (shdr->sh_type != SHT_NOBITS) + { + Elf_Data *d = elf_getdata (scn, NULL); + elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY); + } +#endif + if ((shdr->sh_flags & SHF_ALLOC) == 0) + { + GElf_Off sec_offset = shdr->sh_offset; + GElf_Xword sec_size = shdr->sh_size; + + /* We might have changed the size (and content) of the + debug_str or debug_line section. */ + size_t secnum = elf_ndxscn (scn); + if (secnum == (size_t) debug_sections[DEBUG_STR].sec) + sec_size = debug_sections[DEBUG_STR].size; + if (secnum == (size_t) debug_sections[DEBUG_LINE].sec) + sec_size = debug_sections[DEBUG_LINE].size; + + /* Zero means one. No alignment constraints. */ + size_t addralign = shdr->sh_addralign ?: 1; + last_offset = (last_offset + addralign - 1) & ~(addralign - 1); + sec_offset = last_offset; + if (shdr->sh_type != SHT_NOBITS) + last_offset += sec_size; + + if (shdr->sh_size != sec_size + || shdr->sh_offset != sec_offset) + { + /* Make sure unchanged section data is written out + at the new location. */ + if (shdr->sh_offset != sec_offset + && shdr->sh_type != SHT_NOBITS) + { + Elf_Data *d = elf_getdata (scn, NULL); + elf_flagdata (d, ELF_C_SET, ELF_F_DIRTY); + } + + shdr->sh_size = sec_size; + shdr->sh_offset = sec_offset; + if (gelf_update_shdr (scn, shdr) == 0) + error (1, 0, "Couldn't update shdr: %s\n", + elf_errmsg (-1)); + } + } + } + + /* Position the shdrs after the last (unallocated) section. */ + const size_t offsize = gelf_fsize (elf, ELF_T_OFF, 1, EV_CURRENT); + GElf_Off new_offset = ((last_offset + offsize - 1) + & ~((GElf_Off) (offsize - 1))); + if (dso->ehdr.e_shoff != new_offset) + { + dso->ehdr.e_shoff = new_offset; + if (gelf_update_ehdr (elf, &dso->ehdr) == 0) + error (1, 0, "Couldn't update ehdr: %s\n", elf_errmsg (-1)); + } + } + + if (elf_update (dso->elf, ELF_C_NULL) < 0) + { + fprintf (stderr, "Failed to update file: %s\n", + elf_errmsg (elf_errno ())); + exit (1); + } + if (do_build_id && build_id != NULL) handle_build_id (dso, build_id, build_id_offset, build_id_size); @@ -1769,6 +2770,11 @@ /* Restore old access rights */ chmod (file, stat_buf.st_mode); + free ((char *) dso->filename); + destroy_strings (&dso->strings); + destroy_lines (&dso->lines); + free (dso); + poptFreeContext (optCon); return 0; @@ . ______________________________________________________________________ RPM Package Manager http://rpm5.org CVS Sources Repository rpm-cvs@rpm5.org