From 64924abe6863ab65a2ad5ca792b6df54f3a40b0f Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Thu, 27 May 2021 13:58:58 +0300 Subject: [PATCH 1/2] Macroize find-debuginfo script location Makes it easier to handle varying paths, mainly in preparation for the next step. --- macros.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/macros.in b/macros.in index 626d377887..dc3e055967 100644 --- a/macros.in +++ b/macros.in @@ -68,6 +68,8 @@ %__objdump @__OBJDUMP@ %__strip @__STRIP@ +%__find_debuginfo %{_rpmconfigdir}/find-debuginfo.sh + #============================================================================== # Conditional build stuff. @@ -158,7 +160,7 @@ # the script. See the script for details. # %__debug_install_post \ - %{_rpmconfigdir}/find-debuginfo.sh \\\ + %{__find_debuginfo} \\\ %{?_smp_build_ncpus:-j%{_smp_build_ncpus}} \\\ %{?_missing_build_ids_terminate_build:--strict-build-id} \\\ %{?_no_recompute_build_ids:-n} \\\ From 4226ddee6722ebd2a1cbe96e78ff6ae315fe8830 Mon Sep 17 00:00:00 2001 From: Panu Matilainen Date: Fri, 18 Jun 2021 10:17:14 +0300 Subject: [PATCH 2/2] Use external debuginfo tooling There's been an increasing interest in the wider community to use the debuginfo tooling outside rpm context, and deep ELF format internals are not rpm's core business anyhow, the reasons for it being here are entirely historical. So without further ado, remove the debuginfo tooling from rpm and rely on the external debugedit project from now on. Update INSTALL to document the new dependency, and add conditionals to relevant debuginfo build tests. The lower-level debugedit and sepdebugcrcfix tools are tested in the external project, no need to duplicate that here. --- INSTALL | 4 + Makefile.am | 19 - configure.ac | 11 +- macros.in | 2 +- scripts/Makefile.am | 2 +- scripts/find-debuginfo.sh | 688 ------- tests/Makefile.am | 2 +- tests/atlocal.in | 5 + tests/debugedit.at | 652 ------- tests/rpmbuild.at | 49 +- tests/rpmbuildid.at | 18 + tests/rpmtests.at | 1 - tools/debugedit.c | 3569 ------------------------------------- tools/hashtab.c | 523 ------ tools/hashtab.h | 143 -- tools/sepdebugcrcfix.c | 370 ---- 16 files changed, 50 insertions(+), 6008 deletions(-) delete mode 100755 scripts/find-debuginfo.sh delete mode 100644 tests/debugedit.at delete mode 100644 tools/debugedit.c delete mode 100644 tools/hashtab.c delete mode 100644 tools/hashtab.h delete mode 100644 tools/sepdebugcrcfix.c diff --git a/INSTALL b/INSTALL index 0b682874cc..1ddbb4c235 100644 --- a/INSTALL +++ b/INSTALL @@ -5,6 +5,10 @@ The popt library for option parsing, must be version 1.13 or later. It is available from http://ftp.rpm.org/popt/ +The debugedit >= 0.3 tools for producing debuginfo sub-packages. +It is available from + https://sourceware.org/debugedit/ + Lua >= 5.2 library + development environment. Note that only the library is needed at runtime, RPM never calls external Lua interpreter for anything. Lua is available from diff --git a/Makefile.am b/Makefile.am index 8fa0d531bb..c15fbb6c91 100644 --- a/Makefile.am +++ b/Makefile.am @@ -159,29 +159,10 @@ rpm2archive_LDADD += @WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@ if LIBELF -if LIBDWARF -if LIBDW -rpmconfig_SCRIPTS += scripts/find-debuginfo.sh - -rpmlibexec_PROGRAMS += debugedit -debugedit_SOURCES = tools/debugedit.c tools/hashtab.c tools/hashtab.h -debugedit_LDADD = rpmio/librpmio.la -debugedit_LDADD += @WITH_LIBELF_LIB@ @WITH_POPT_LIB@ -if HAVE_LIBDW_STRTAB -debugedit_LDADD += @WITH_LIBDW_LIB@ -else -debugedit_LDADD += @WITH_LIBDW_LIB@ -lebl -endif # HAVE_LIBDW_STRTAB -endif # LIBDW -endif # LIBDWARF rpmlibexec_PROGRAMS += elfdeps elfdeps_SOURCES = tools/elfdeps.c elfdeps_LDADD = rpmio/librpmio.la elfdeps_LDADD += @WITH_LIBELF_LIB@ @WITH_POPT_LIB@ - -rpmlibexec_PROGRAMS += sepdebugcrcfix -sepdebugcrcfix_SOURCES = tools/sepdebugcrcfix.c -sepdebugcrcfix_LDADD = @WITH_LIBELF_LIB@ endif #LIBELF rpmlibexec_PROGRAMS += rpmdeps diff --git a/configure.ac b/configure.ac index f003b2bf70..92610cdfb7 100644 --- a/configure.ac +++ b/configure.ac @@ -105,6 +105,7 @@ AC_PATH_PROG(__ZSTD, zstd, /usr/bin/zstd, $MYPATH) AC_PATH_PROG(__LD, ld, /usr/bin/ld, $MYPATH) AC_PATH_PROG(__OBJDUMP, objdump, /usr/bin/objdump, $MYPATH) AC_PATH_PROG(__STRIP, strip, /usr/bin/strip, $MYPATH) +AC_PATH_PROG(__FIND_DEBUGINFO, find-debuginfo, /usr/bin/find-debuginfo, $MYPATH) AC_PATH_PROG(__GIT, git, /usr/bin/git, $MYPATH) AC_PATH_PROG(__HG, hg, /usr/bin/hg, $MYPATH) @@ -259,11 +260,6 @@ AC_CHECK_HEADERS([gelf.h], [ AC_SUBST(WITH_LIBELF_LIB) AM_CONDITIONAL(LIBELF,[test "$WITH_LIBELF" = yes]) -AC_CHECK_HEADERS([dwarf.h], [ - WITH_LIBDWARF=yes -]) -AM_CONDITIONAL(LIBDWARF,[test "$WITH_LIBDWARF" = yes]) - #================= # Select crypto library AC_ARG_WITH(crypto, @@ -444,7 +440,6 @@ AM_CONDITIONAL(WITH_ARCHIVE,[test "$with_archive" = yes]) #================= # Check for elfutils libdw library with dwelf_elf_gnu_build_id. WITH_LIBDW_LIB= -HAVE_LIBDW_STRTAB= AS_IF([test "$WITH_LIBELF" = yes],[ AC_CHECK_HEADERS([elfutils/libdwelf.h],[ # dwelf_elf_gnu_build_id was introduced in elfutils 0.159 @@ -453,9 +448,6 @@ AS_IF([test "$WITH_LIBELF" = yes],[ [Define to 1 if you have elfutils libdw library]) WITH_LIBDW_LIB="-ldw" WITH_LIBDW=yes - # If possible we also want the strtab functions from elfutils 0.167. - # But we can fall back on the (unsupported) ebl alternatives if not. - AC_CHECK_LIB(dw, dwelf_strtab_init, [HAVE_LIBDW_STRTAB=yes]) # whether libdw supports compressed ELF objects AC_CHECK_LIB(dw, dwelf_elf_begin, [ AC_DEFINE(HAVE_DWELF_ELF_BEGIN, 1, [Have dwelf_elf_begin?]) @@ -465,7 +457,6 @@ AS_IF([test "$WITH_LIBELF" = yes],[ ]) AC_SUBST(WITH_LIBDW_LIB) AM_CONDITIONAL(LIBDW,[test "$WITH_LIBDW" = yes]) -AM_CONDITIONAL(HAVE_LIBDW_STRTAB,[test "$HAVE_LIBDW_STRTAB" = yes]) #================= # Process --enable-ndb diff --git a/macros.in b/macros.in index dc3e055967..2fe71da468 100644 --- a/macros.in +++ b/macros.in @@ -68,7 +68,7 @@ %__objdump @__OBJDUMP@ %__strip @__STRIP@ -%__find_debuginfo %{_rpmconfigdir}/find-debuginfo.sh +%__find_debuginfo @__FIND_DEBUGINFO@ #============================================================================== # Conditional build stuff. diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 65d1db7c30..4aed76be02 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -12,7 +12,7 @@ EXTRA_DIST = \ brp-remove-la-files \ check-files check-prereqs \ check-buildroot check-rpaths check-rpaths-worker \ - find-debuginfo.sh find-lang.sh \ + find-lang.sh \ perl.prov perl.req \ rpmdb_dump rpmdb_load \ rpm.daily rpm.log rpm.supp rpm2cpio.sh \ diff --git a/scripts/find-debuginfo.sh b/scripts/find-debuginfo.sh deleted file mode 100755 index e0c36ebb64..0000000000 --- a/scripts/find-debuginfo.sh +++ /dev/null @@ -1,688 +0,0 @@ -#!/bin/bash -#find-debuginfo.sh - automagically generate debug info and file list -#for inclusion in an rpm spec file. -# -# Usage: find-debuginfo.sh [--strict-build-id] [-g] [-r] [-m] [-i] [-n] -# [--keep-section SECTION] [--remove-section SECTION] -# [--g-libs] -# [-j N] [--jobs N] -# [-o debugfiles.list] -# [-S debugsourcefiles.list] -# [--run-dwz] [--dwz-low-mem-die-limit N] -# [--dwz-max-die-limit N] -# [--build-id-seed SEED] -# [--unique-debug-suffix SUFFIX] -# [--unique-debug-src-base BASE] -# [[-l filelist]... [-p 'pattern'] -o debuginfo.list] -# [builddir] -# -# The -g flag says to use strip -g instead of full strip on DSOs or EXEs. -# The --g-libs flag says to use strip -g instead of full strip ONLY on DSOs. -# Options -g and --g-libs are mutually exclusive. -# The -r flag says to use eu-strip --reloc-debug-sections. -# Use --keep-section SECTION or --remove-section SECTION to explicitly -# keep a (non-allocated) section in the main executable or explicitly -# remove it into the .debug file. SECTION is an extended wildcard pattern. -# Both options can be given more than once. -# -# The --strict-build-id flag says to exit with failure status if -# any ELF binary processed fails to contain a build-id note. -# The -m flag says to include a .gnu_debugdata section in the main binary. -# The -i flag says to include a .gdb_index section in the .debug file. -# The -n flag says to not recompute the build-id. -# -# The -j, --jobs N option will spawn N processes to do the debuginfo -# extraction in parallel. -# -# A single -o switch before any -l or -p switches simply renames -# the primary output file from debugfiles.list to something else. -# A -o switch that follows a -p switch or some -l switches produces -# an additional output file with the debuginfo for the files in -# the -l filelist file, or whose names match the -p pattern. -# The -p argument is an grep -E -style regexp matching the a file name, -# and must not use anchors (^ or $). -# -# The --run-dwz flag instructs find-debuginfo.sh to run the dwz utility -# if available, and --dwz-low-mem-die-limit and --dwz-max-die-limit -# provide detailed limits. See dwz(1) -l and -L option for details. -# -# If --build-id-seed SEED is given then debugedit is called to -# update the build-ids it finds adding the SEED as seed to recalculate -# the build-id hash. This makes sure the build-ids in the ELF files -# are unique between versions and releases of the same package. -# (Use --build-id-seed "%{VERSION}-%{RELEASE}".) -# -# If --unique-debug-suffix SUFFIX is given then the debug files created -# for will be named -.debug. This makes sure .debug -# are unique between package version, release and architecture. -# (Use --unique-debug-suffix "-%{VERSION}-%{RELEASE}.%{_arch}".) -# -# If --unique-debug-src-base BASE is given then the source directory -# will be called /usr/debug/src/. This makes sure the debug source -# directories are unique between package version, release and architecture. -# (Use --unique-debug-src-base "%{name}-%{VERSION}-%{RELEASE}.%{_arch}".) -# -# All file names in switches are relative to builddir (. if not given). -# - -# Figure out where we are installed so we can call other helper scripts. -lib_rpm_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# With -g arg, pass it to strip on libraries or executables. -strip_g=false - -# With --g-libs arg, pass it to strip on libraries. -strip_glibs=false - -# with -r arg, pass --reloc-debug-sections to eu-strip. -strip_r=false - -# keep or remove arguments to eu-strip. -keep_remove_args= - -# with -m arg, add minimal debuginfo to binary. -include_minidebug=false - -# with -i arg, add GDB index to .debug file. -include_gdb_index=false - -# Barf on missing build IDs. -strict=false - -# Do not recompute build IDs. -no_recompute_build_id=false - -# DWZ parameters. -run_dwz=false -dwz_low_mem_die_limit= -dwz_max_die_limit= - -# build id seed given by the --build-id-seed option -build_id_seed= - -# Arch given by --unique-debug-arch -unique_debug_suffix= - -# Base given by --unique-debug-src-base -unique_debug_src_base= - -# Number of parallel jobs to spawn -n_jobs=1 - -BUILDDIR=. -out=debugfiles.list -srcout= -nout=0 -while [ $# -gt 0 ]; do - case "$1" in - --strict-build-id) - strict=true - ;; - --run-dwz) - run_dwz=true - ;; - --dwz-low-mem-die-limit) - dwz_low_mem_die_limit=$2 - shift - ;; - --dwz-max-die-limit) - dwz_max_die_limit=$2 - shift - ;; - --build-id-seed) - build_id_seed=$2 - shift - ;; - --unique-debug-suffix) - unique_debug_suffix=$2 - shift - ;; - --unique-debug-src-base) - unique_debug_src_base=$2 - shift - ;; - --g-libs) - strip_glibs=true - ;; - -g) - strip_g=true - ;; - -m) - include_minidebug=true - ;; - -n) - no_recompute_build_id=true - ;; - -i) - include_gdb_index=true - ;; - -o) - if [ -z "${lists[$nout]}" ] && [ -z "${ptns[$nout]}" ]; then - out=$2 - else - outs[$nout]=$2 - ((nout++)) - fi - shift - ;; - -l) - lists[$nout]="${lists[$nout]} $2" - shift - ;; - -p) - ptns[$nout]=$2 - shift - ;; - -r) - strip_r=true - ;; - --keep-section) - keep_remove_args="${keep_remove_args} --keep-section $2" - shift - ;; - --remove-section) - keep_remove_args="${keep_remove_args} --remove-section $2" - shift - ;; - -j) - n_jobs=$2 - shift - ;; - -j*) - n_jobs=${1#-j} - ;; - --jobs) - n_jobs=$2 - shift - ;; - -S) - srcout=$2 - shift - ;; - *) - BUILDDIR=$1 - shift - break - ;; - esac - shift -done - -if test -n "$build_id_seed" -a "$no_recompute_build_id" = "true"; then - echo >&2 "*** ERROR: --build-id-seed (unique build-ids) and -n (do not recompute build-id) cannot be used together" - exit 2 -fi - -if [ "$strip_g" = "true" ] && [ "$strip_glibs" = "true" ]; then - echo >&2 "*** ERROR: -g and --g-libs cannot be used together" - exit 2 -fi - -i=0 -while ((i < nout)); do - outs[$i]="$BUILDDIR/${outs[$i]}" - l='' - for f in ${lists[$i]}; do - l="$l $BUILDDIR/$f" - done - lists[$i]=$l - ((++i)) -done - -LISTFILE="$BUILDDIR/$out" -SOURCEFILE="$BUILDDIR/debugsources.list" -LINKSFILE="$BUILDDIR/debuglinks.list" -ELFBINSFILE="$BUILDDIR/elfbins.list" - -> "$SOURCEFILE" -> "$LISTFILE" -> "$LINKSFILE" -> "$ELFBINSFILE" - -debugdir="${RPM_BUILD_ROOT}/usr/lib/debug" - -strip_to_debug() -{ - local g= - local r= - $strip_r && r=--reloc-debug-sections - $strip_g && case "$(file -bi "$2")" in - application/x-sharedlib*) g=-g ;; - application/x-executable*) g=-g ;; - application/x-pie-executable*) g=-g ;; - esac - $strip_glibs && case "$(file -bi "$2")" in - application/x-sharedlib*) g=-g ;; - esac - eu-strip --remove-comment $r $g ${keep_remove_args} -f "$1" "$2" || exit - chmod 444 "$1" || exit -} - -add_minidebug() -{ - local debuginfo="$1" - local binary="$2" - - local dynsyms=`mktemp` - local funcsyms=`mktemp` - local keep_symbols=`mktemp` - local mini_debuginfo=`mktemp` - - # In the minisymtab we don't need the .debug_ sections (already removed - # by -S) but also not other non-allocated PROGBITS, NOTE or NOBITS sections. - # List and remove them explicitly. We do want to keep the allocated, - # symbol and NOBITS sections so cannot use --keep-only because that is - # too aggressive. Field $2 is the section name, $3 is the section type - # and $8 are the section flags. - local remove_sections=`readelf -W -S "$debuginfo" \ - | awk '{ if (index($2,".debug_") != 1 \ - && ($3 == "PROGBITS" || $3 == "NOTE" || $3 == "NOBITS") \ - && index($8,"A") == 0) \ - printf "--remove-section "$2" " }'` - - # Extract the dynamic symbols from the main binary, there is no need to also have these - # in the normal symbol table - nm -D "$binary" --format=posix --defined-only | awk '{ print $1 }' | sort > "$dynsyms" - # Extract all the text (i.e. function) symbols from the debuginfo - # Use format sysv to make sure we can match against the actual ELF FUNC - # symbol type. The binutils nm posix format symbol type chars are - # ambigous for architectures that might use function descriptors. - nm "$debuginfo" --format=sysv --defined-only | awk -F \| '{ if ($4 ~ "FUNC") print $1 }' | sort > "$funcsyms" - # Keep all the function symbols not already in the dynamic symbol table - comm -13 "$dynsyms" "$funcsyms" > "$keep_symbols" - # Copy the full debuginfo, keeping only a minumal set of symbols and removing some unnecessary sections - objcopy -S $remove_sections --keep-symbols="$keep_symbols" "$debuginfo" "$mini_debuginfo" &> /dev/null - #Inject the compressed data into the .gnu_debugdata section of the original binary - xz "$mini_debuginfo" - mini_debuginfo="${mini_debuginfo}.xz" - objcopy --add-section .gnu_debugdata="$mini_debuginfo" "$binary" - rm -f "$dynsyms" "$funcsyms" "$keep_symbols" "$mini_debuginfo" -} - -# Make a relative symlink to $1 called $3$2 -shopt -s extglob -link_relative() -{ - local t="$1" f="$2" pfx="$3" - local fn="${f#/}" tn="${t#/}" - local fd td d - - while fd="${fn%%/*}"; td="${tn%%/*}"; [ "$fd" = "$td" ]; do - fn="${fn#*/}" - tn="${tn#*/}" - done - - d="${fn%/*}" - if [ "$d" != "$fn" ]; then - d="${d//+([!\/])/..}" - tn="${d}/${tn}" - fi - - mkdir -p "$(dirname "$pfx$f")" && ln -snf "$tn" "$pfx$f" -} - -# Make a symlink in /usr/lib/debug/$2 to $1 -debug_link() -{ - local l="/usr/lib/debug$2" - local t="$1" - echo >> "$LINKSFILE" "$l $t" - link_relative "$t" "$l" "$RPM_BUILD_ROOT" -} - -get_debugfn() -{ - dn=$(dirname "${1#$RPM_BUILD_ROOT}") - bn=$(basename "$1" .debug)${unique_debug_suffix}.debug - debugdn=${debugdir}${dn} - debugfn=${debugdn}/${bn} -} - -set -o pipefail - -strict_error=ERROR -$strict || strict_error=WARNING - -temp=$(mktemp -d ${TMPDIR:-/tmp}/find-debuginfo.XXXXXX) -trap 'rm -rf "$temp"' EXIT - -# Build a list of unstripped ELF files and their hardlinks -touch "$temp/primary" -find "$RPM_BUILD_ROOT" ! -path "${debugdir}/*.debug" -type f \ - \( -perm -0100 -or -perm -0010 -or -perm -0001 \) \ - -print | LC_ALL=C sort | -file -N -f - | sed -n -e 's/^\(.*\):[ ]*.*ELF.*, not stripped.*/\1/p' | -xargs --no-run-if-empty stat -c '%h %D_%i %n' | -while read nlinks inum f; do - if [ $nlinks -gt 1 ]; then - var=seen_$inum - if test -n "${!var}"; then - echo "$inum $f" >>"$temp/linked" - continue - else - read "$var" < <(echo 1) - fi - fi - echo "$nlinks $inum $f" >>"$temp/primary" -done - -# Strip ELF binaries -do_file() -{ - local nlinks=$1 inum=$2 f=$3 id link linked - - get_debugfn "$f" - [ -f "${debugfn}" ] && return - - echo "extracting debug info from $f" - # See also cpio SOURCEFILE copy. Directories must match up. - debug_base_name="$RPM_BUILD_DIR" - debug_dest_name="/usr/src/debug" - if [ ! -z "$unique_debug_src_base" ]; then - debug_base_name="$BUILDDIR" - debug_dest_name="/usr/src/debug/${unique_debug_src_base}" - fi - no_recompute= - if [ "$no_recompute_build_id" = "true" ]; then - no_recompute="-n" - fi - id=$(${lib_rpm_dir}/debugedit -b "$debug_base_name" -d "$debug_dest_name" \ - $no_recompute -i \ - ${build_id_seed:+--build-id-seed="$build_id_seed"} \ - -l "$SOURCEFILE" "$f") || exit - if [ -z "$id" ]; then - echo >&2 "*** ${strict_error}: No build ID note found in $f" - $strict && exit 2 - fi - - # Add .gdb_index if requested. - if $include_gdb_index; then - if type gdb-add-index >/dev/null 2>&1; then - gdb-add-index "$f" - else - echo >&2 "*** ERROR: GDB index requested, but no gdb-add-index installed" - exit 2 - fi - fi - - # Compress any annobin notes in the original binary. - # Ignore any errors, since older objcopy don't support --merge-notes. - objcopy --merge-notes "$f" 2>/dev/null || true - - # A binary already copied into /usr/lib/debug doesn't get stripped, - # just has its file names collected and adjusted. - case "$dn" in - /usr/lib/debug/*) - return ;; - esac - - mkdir -p "${debugdn}" - if test -w "$f"; then - strip_to_debug "${debugfn}" "$f" - else - chmod u+w "$f" - strip_to_debug "${debugfn}" "$f" - chmod u-w "$f" - fi - - # strip -g implies we have full symtab, don't add mini symtab in that case. - # It only makes sense to add a minisymtab for executables and shared - # libraries. Other executable ELF files (like kernel modules) don't need it. - if [ "$include_minidebug" = "true" ] && [ "$strip_g" = "false" ]; then - skip_mini=true - if [ "$strip_glibs" = "false" ]; then - case "$(file -bi "$f")" in - application/x-sharedlib*) skip_mini=false ;; - esac - fi - case "$(file -bi "$f")" in - application/x-executable*) skip_mini=false ;; - application/x-pie-executable*) skip_mini=false ;; - esac - $skip_mini || add_minidebug "${debugfn}" "$f" - fi - - echo "./${f#$RPM_BUILD_ROOT}" >> "$ELFBINSFILE" - - # If this file has multiple links, make the corresponding .debug files - # all links to one file too. - if [ $nlinks -gt 1 ]; then - grep "^$inum " "$temp/linked" | while read inum linked; do - link=$debugfn - get_debugfn "$linked" - echo "hard linked $link to $debugfn" - mkdir -p "$(dirname "$debugfn")" && ln -nf "$link" "$debugfn" - done - fi -} - -# 16^6 - 1 or about 16 million files -FILENUM_DIGITS=6 -run_job() -{ - local jobid=$1 filenum - local SOURCEFILE=$temp/debugsources.$jobid ELFBINSFILE=$temp/elfbins.$jobid - - >"$SOURCEFILE" - >"$ELFBINSFILE" - # can't use read -n , because it reads bytes one by one, allowing for - # races - while :; do - filenum=$(dd bs=$(( FILENUM_DIGITS + 1 )) count=1 status=none) - if test -z "$filenum"; then - break - fi - do_file $(sed -n "$(( 0x$filenum )) p" "$temp/primary") - done - echo 0 >"$temp/res.$jobid" -} - -n_files=$(wc -l <"$temp/primary") -if [ $n_jobs -gt $n_files ]; then - n_jobs=$n_files -fi -if [ $n_jobs -le 1 ]; then - while read nlinks inum f; do - do_file "$nlinks" "$inum" "$f" - done <"$temp/primary" -else - for ((i = 1; i <= n_files; i++)); do - printf "%0${FILENUM_DIGITS}x\\n" $i - done | ( - exec 3<&0 - for ((i = 0; i < n_jobs; i++)); do - # The shell redirects stdin to /dev/null for background jobs. Work - # around this by duplicating fd 0 - run_job $i <&3 & - done - wait - ) - for f in "$temp"/res.*; do - test -f "$f" || continue - res=$(< "$f") - if [ "$res" != "0" ]; then - exit 1 - fi - done - cat "$temp"/debugsources.* >"$SOURCEFILE" - cat "$temp"/elfbins.* >"$ELFBINSFILE" -fi - -# Invoke the DWARF Compressor utility. -if $run_dwz \ - && [ -d "${RPM_BUILD_ROOT}/usr/lib/debug" ]; then - readarray dwz_files < <(cd "${RPM_BUILD_ROOT}/usr/lib/debug"; find -type f -name \*.debug | LC_ALL=C sort) - if [ ${#dwz_files[@]} -gt 0 ]; then - size_before=$(du -sk ${RPM_BUILD_ROOT}/usr/lib/debug | cut -f1) - dwz_multifile_name="${RPM_PACKAGE_NAME}-${RPM_PACKAGE_VERSION}-${RPM_PACKAGE_RELEASE}.${RPM_ARCH}" - dwz_multifile_suffix= - dwz_multifile_idx=0 - while [ -f "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz/${dwz_multifile_name}${dwz_multifile_suffix}" ]; do - let ++dwz_multifile_idx - dwz_multifile_suffix=".${dwz_multifile_idx}" - done - dwz_multifile_name="${dwz_multifile_name}${dwz_multifile_suffix}" - dwz_opts="-h -q -r" - [ ${#dwz_files[@]} -gt 1 ] \ - && dwz_opts="${dwz_opts} -m .dwz/${dwz_multifile_name}" - mkdir -p "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz" - [ -n "${dwz_low_mem_die_limit}" ] \ - && dwz_opts="${dwz_opts} -l ${dwz_low_mem_die_limit}" - [ -n "${dwz_max_die_limit}" ] \ - && dwz_opts="${dwz_opts} -L ${dwz_max_die_limit}" - if type dwz >/dev/null 2>&1; then - ( cd "${RPM_BUILD_ROOT}/usr/lib/debug" && dwz $dwz_opts ${dwz_files[@]} ) - else - echo >&2 "*** ERROR: DWARF compression requested, but no dwz installed" - exit 2 - fi - size_after=$(du -sk ${RPM_BUILD_ROOT}/usr/lib/debug | cut -f1) - echo "original debug info size: ${size_before}kB, size after compression: ${size_after}kB" - # Remove .dwz directory if empty - rmdir "${RPM_BUILD_ROOT}/usr/lib/debug/.dwz" 2>/dev/null - - # dwz invalidates .gnu_debuglink CRC32 in the main files. - cat "$ELFBINSFILE" | - (cd "$RPM_BUILD_ROOT"; \ - tr '\n' '\0' | xargs -0 ${lib_rpm_dir}/sepdebugcrcfix usr/lib/debug) - fi -fi - -# For each symlink whose target has a .debug file, -# make a .debug symlink to that file. -find "$RPM_BUILD_ROOT" ! -path "${debugdir}/*" -type l -print | -while read f -do - t=$(readlink -m "$f").debug - f=${f#$RPM_BUILD_ROOT} - t=${t#$RPM_BUILD_ROOT} - if [ -f "$debugdir$t" ]; then - echo "symlinked /usr/lib/debug$t to /usr/lib/debug${f}.debug" - debug_link "/usr/lib/debug$t" "${f}.debug" - fi -done - -if [ -s "$SOURCEFILE" ]; then - # See also debugedit invocation. Directories must match up. - debug_base_name="$RPM_BUILD_DIR" - debug_dest_name="/usr/src/debug" - if [ ! -z "$unique_debug_src_base" ]; then - debug_base_name="$BUILDDIR" - debug_dest_name="/usr/src/debug/${unique_debug_src_base}" - fi - - mkdir -p "${RPM_BUILD_ROOT}${debug_dest_name}" - # Filter out anything compiler generated which isn't a source file. - # e.g. , , <__thread_local_inner macros>. - # Some compilers generate them as if they are part of the working - # directory (which is why we match against ^ or /). - LC_ALL=C sort -z -u "$SOURCEFILE" | grep -E -v -z '(^|/)<[a-z _-]+>$' | - (cd "${debug_base_name}"; cpio -pd0mL "${RPM_BUILD_ROOT}${debug_dest_name}") - # stupid cpio creates new directories in mode 0700, - # and non-standard modes may be inherented from original directories, fixup - find "${RPM_BUILD_ROOT}${debug_dest_name}" -type d -print0 | - xargs --no-run-if-empty -0 chmod 0755 -fi - -if [ -d "${RPM_BUILD_ROOT}/usr/lib" ] || [ -d "${RPM_BUILD_ROOT}/usr/src" ]; then - ((nout > 0)) || - test ! -d "${RPM_BUILD_ROOT}/usr/lib" || - (cd "${RPM_BUILD_ROOT}/usr/lib"; find debug -type d) | - sed 's,^,%dir /usr/lib/,' >> "$LISTFILE" - - (cd "${RPM_BUILD_ROOT}/usr" - test ! -d lib/debug || find lib/debug ! -type d - test ! -d src/debug -o -n "$srcout" || find src/debug -mindepth 1 -maxdepth 1 - ) | sed 's,^,/usr/,' >> "$LISTFILE" -fi - -if [ -n "$srcout" ]; then - srcout="$BUILDDIR/$srcout" - > "$srcout" - if [ -d "${RPM_BUILD_ROOT}/usr/src/debug" ]; then - (cd "${RPM_BUILD_ROOT}/usr" - find src/debug -mindepth 1 -maxdepth 1 - ) | sed 's,^,/usr/,' >> "$srcout" - fi -fi - -# Append to $1 only the lines from stdin not already in the file. -append_uniq() -{ - grep -F -f "$1" -x -v >> "$1" -} - -# Helper to generate list of corresponding .debug files from a file list. -filelist_debugfiles() -{ - local extra="$1" - shift - sed 's/^%[a-z0-9_][a-z0-9_]*([^)]*) *// -s/^%[a-z0-9_][a-z0-9_]* *// -/^$/d -'"$extra" "$@" -} - -# Write an output debuginfo file list based on given input file lists. -filtered_list() -{ - local out="$1" - shift - test $# -gt 0 || return - grep -F -f <(filelist_debugfiles 's,^.*$,/usr/lib/debug&.debug,' "$@") \ - -x $LISTFILE >> $out - sed -n -f <(filelist_debugfiles 's/[\\.*+#]/\\&/g -h -s,^.*$,s# &$##p,p -g -s,^.*$,s# /usr/lib/debug&.debug$##p,p -' "$@") "$LINKSFILE" | append_uniq "$out" -} - -# Write an output debuginfo file list based on an grep -E -style regexp. -pattern_list() -{ - local out="$1" ptn="$2" - test -n "$ptn" || return - grep -E -x -e "$ptn" "$LISTFILE" >> "$out" - sed -n -r "\#^$ptn #s/ .*\$//p" "$LINKSFILE" | append_uniq "$out" -} - -# -# When given multiple -o switches, split up the output as directed. -# -i=0 -while ((i < nout)); do - > ${outs[$i]} - filtered_list ${outs[$i]} ${lists[$i]} - pattern_list ${outs[$i]} "${ptns[$i]}" - grep -Fvx -f ${outs[$i]} "$LISTFILE" > "${LISTFILE}.new" - mv "${LISTFILE}.new" "$LISTFILE" - ((++i)) -done -if ((nout > 0)); then - # Generate %dir lines for each output list. - generate_percent_dir() - { - while read -r line; do - while test "${line:0:15}" = "/usr/lib/debug/"; do - line="${line%/*}" - printf '%s\n' "$line" - done - done | \ - sort -u | \ - while read -r line; do - test -d "${RPM_BUILD_ROOT}$line" && printf '%%dir %s\n' "$line" - done - } - i=0 - while ((i < nout)); do - generate_percent_dir < "${outs[$i]}" > "${outs[$i]}.new" - cat "${outs[$i]}" >> "${outs[$i]}.new" - mv -f "${outs[$i]}.new" "${outs[$i]}" - ((++i)) - done - generate_percent_dir < "${LISTFILE}" > "${LISTFILE}.new" - cat "$LISTFILE" >> "${LISTFILE}.new" - mv "${LISTFILE}.new" "$LISTFILE" -fi diff --git a/tests/Makefile.am b/tests/Makefile.am index 5a5a1e7427..3bda68093e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,7 +18,6 @@ TESTSUITE_AT += rpmverify.at TESTSUITE_AT += rpmdb.at TESTSUITE_AT += rpmbuild.at TESTSUITE_AT += rpmbuildid.at -TESTSUITE_AT += debugedit.at TESTSUITE_AT += rpmi.at TESTSUITE_AT += rpmvercmp.at TESTSUITE_AT += rpmdeps.at @@ -152,6 +151,7 @@ atlocal: atlocal.in Makefile -e "s,[@]usrlibexecdir[@],$(rpmlibexecdir),g" \ -e "s,[@]RPMCONFIGDIR[@],$(rpmconfigdir)," \ -e "s,[@]PYTHON[@],$(PYTHON)," \ + -e "s,[@]__FIND_DEBUGINFO[@],$(__FIND_DEBUGINFO)," \ < $(srcdir)/atlocal.in > atlocal DISTCLEANFILES = atlocal EXTRA_DIST += atlocal.in diff --git a/tests/atlocal.in b/tests/atlocal.in index 6bd4066d8c..c3189d327c 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -34,6 +34,11 @@ unset SOURCE_DATE_EPOCH TOPDIR="${HOME}/build" RPM_XFAIL=${RPM_XFAIL-1} +if test -x "@__FIND_DEBUGINFO@"; then + DEBUGINFO_DISABLED=false; +else + DEBUGINFO_DISABLED=true; +fi if test "${PYTHON}"; then PYTHON_DISABLED=false; else diff --git a/tests/debugedit.at b/tests/debugedit.at deleted file mode 100644 index a78180e13f..0000000000 --- a/tests/debugedit.at +++ /dev/null @@ -1,652 +0,0 @@ -# debugedit.at: Tests for the debugedit tool -# -# Copyright (C) 2019 Mark J. Wielaard -# -# 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 -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 this program; if not, see see . - -# Tests for the tools/debugedit program. -AT_BANNER([RPM debugedit]) - -# Show which debugedit binary we are testing. -AT_TESTED([debugedit]) - -# Helper to create some test binaries. -# Optional parameter can specify additional gcc parameters. -m4_define([RPM_DEBUGEDIT_SETUP],[[ -# Create some test binaries. Create and build them in different subdirs -# to make sure they produce different relative/absolute paths. - -export HOME=${PWD} -mkdir subdir_foo -cp "${abs_srcdir}"/data/SOURCES/foo.c subdir_foo -mkdir subdir_bar -cp "${abs_srcdir}"/data/SOURCES/bar.c subdir_bar -mkdir subdir_headers -cp "${abs_srcdir}"/data/SOURCES/foobar.h subdir_headers -cp "${abs_srcdir}"/data/SOURCES/baz.c . - -# First three object files (foo.o subdir_bar/bar.o and baz.o) -gcc -g3 -Isubdir_headers $1 -c subdir_foo/foo.c -cd subdir_bar -gcc -g3 -I../subdir_headers $1 -c bar.c -cd .. -gcc -g3 -I$(pwd)/subdir_headers $1 -c $(pwd)/baz.c - -# Then a partially linked object file (somewhat like a kernel module). -# This will still have relocations between the debug sections. -ld -r -o foobarbaz.part.o foo.o subdir_bar/bar.o baz.o - -# Create an executable. Relocations between debug sections will -# have been resolved. -gcc -g3 -o foobarbaz.exe foo.o subdir_bar/bar.o baz.o -]]) - -# === -# Check debugedit --help doesn't crash and burn. -# === -AT_SETUP([debugedit help]) -AT_KEYWORDS([debuginfo] [debugedit]) -AT_CHECK([debugedit --help],[0],[ignore],[ignore]) -AT_CLEANUP - -# === -# Make sure that an executable still runs after debugedit munged it. -# === -AT_SETUP([debugedit executable]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -AT_CHECK([[./foobarbaz.exe]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[./foobarbaz.exe]]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str directory paths -# in the objects. -# === -AT_SETUP([debugedit .debug_str objects DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -readelf -p.debug_str foo.o subdir_bar/bar.o baz.o | cut -c13- \ - | grep ^$(pwd) | sort \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf -p.debug_str foo.o subdir_bar/bar.o baz.o | cut -c13- \ - | grep ^/foo/bar/baz | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str/line_str directory paths -# in the objects. -# === -AT_SETUP([debugedit .debug_str/line_str objects DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -readelf -p.debug_str -p.debug_line_str foo.o subdir_bar/bar.o baz.o \ - | cut -c13- \ - | grep ^$(pwd) | sort | uniq \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf -p.debug_str -p.debug_line_str foo.o subdir_bar/bar.o baz.o \ - | cut -c13- \ - | grep ^/foo/bar/baz | sort | uniq -]],[0],[expout],[ignore]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str directory paths -# also in partially linked files. -# === -AT_SETUP([debugedit .debug_str partial DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -# Note that partially linked files, might have multiple duplicate -# strings, but debugedit will merge them. So use sort -u. -readelf -p.debug_str ./foobarbaz.part.o | cut -c13- \ - | grep ^$(pwd) | sort -u \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf -p.debug_str ./foobarbaz.part.o | cut -c13- \ - | grep ^/foo/bar/baz | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str/line_str directory paths -# also in partially linked files. -# === -AT_SETUP([debugedit .debug_str/line_str partial DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -# Note that partially linked files, might have multiple duplicate -# strings, but debugedit will merge them. So use sort -u. -readelf -p.debug_str -p.debug_line_str ./foobarbaz.part.o | cut -c13- \ - | grep ^$(pwd) | sort -u | uniq \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf -p.debug_str -p.debug_line_str ./foobarbaz.part.o | cut -c13- \ - | grep ^/foo/bar/baz | sort | uniq -]],[0],[expout],[ignore]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str directory paths -# and in the executable. -# === -AT_SETUP([debugedit .debug_str exe DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -readelf -p.debug_str foobarbaz.exe | cut -c13- \ - | grep ^$(pwd) | sort \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -# The linker will have merged unique strings, so no need for sort -u. -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf -p.debug_str foobarbaz.exe | cut -c13- \ - | grep ^/foo/bar/baz | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# debugedit should at least replace the .debug_str/line_str directory paths -# and in the executable. -# === -AT_SETUP([debugedit .debug_str/line_str exe DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -# Capture strings that start with the testdir (pwd) directory path -# (and replace that textually with /foo/bar/baz) -readelf -p.debug_str -p.debug_line_str foobarbaz.exe | cut -c13- \ - | grep ^$(pwd) | sort | uniq \ - | sed -e "s@$(pwd)@/foo/bar/baz@" > expout - -# Make sure there is at least some output -# The linker will have merged unique strings, so no need for sort -u. -expout_lines=$(wc --lines expout | cut -f1 -d\ ) -if test $expout_lines -lt 3; then - echo "Expecting at least 3 debug strings starting with ${testdir}" >> expout -fi - -# Check the replaced strings are all there. -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf -p.debug_str -p.debug_line_str foobarbaz.exe | cut -c13- \ - | grep ^/foo/bar/baz | sort | uniq -]],[0],[expout],[ignore]) - -AT_CLEANUP - -# For .debug_info we expect the following DW_AT_name and DW_AT_comp_dir -# strings for the DW_TAG_compile_unit: -# - foo.o -# DW_AT_name: subdir_foo/foo.c -# DW_AT_comp_dir: /foo/baz/baz -# - bar.o -# DW_AT_name: bar.c -# DW_AT_comp_dir: /foo/bar/baz/subdir_bar -# - baz.o -# DW_AT_name: /foo/bar/baz/baz.c -# DW_AT_comp_dir: /foo/baz/baz -# -# Older gcc (before 7) don't emit the DW_AT_comp_dir for baz.o. -# But because it is similar to the comp_dir of foo.o, just sort -u. - -# === -# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced -# in objects. -# === -AT_SETUP([debugedit .debug_info objects]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/baz.c -/foo/bar/baz/subdir_bar -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf --debug-dump=info foo.o subdir_bar/bar.o baz.o \ - | grep -E 'DW_AT_(name|comp_dir)' \ - | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced -# in partial linked object. -# === -AT_SETUP([debugedit .debug_info partial]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/baz.c -/foo/bar/baz/subdir_bar -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf --debug-dump=info ./foobarbaz.part.o \ - | grep -E 'DW_AT_(name|comp_dir)' \ - | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure DW_AT_name and DW_AT_comp_dir strings are replaced -# in executable. -# === -AT_SETUP([debugedit .debug_info exe]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/baz.c -/foo/bar/baz/subdir_bar -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf --debug-dump=info ./foobarbaz.exe | grep -E 'DW_AT_(name|comp_dir)' \ - | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo/bar/baz | sort -u -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure -fdebug-types-section has updated strings in objects. -# Currently only works with DWARF4 -# === -AT_SETUP([debugedit .debug_types objects]) -AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_SETUP([-fdebug-types-section -gdwarf-4]) - -AT_DATA([expout], -[st1 -stf -stringp1 -stringp_foo -st1 -stb -stringp1 -stringp_bar -st1 -stringp1 -stringp_baz -stz -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -for i in ./foo.o ./subdir_bar/bar.o ./baz.o;do \ - readelf --debug-dump=info $i \ - | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ - | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ - | sort; -done -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure -fdebug-types-section has updated strings in partial linked object. -# Currently only works with DWARF4 -# === -AT_SETUP([debugedit .debug_types partial]) -AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_SETUP([-fdebug-types-section -gdwarf-4]) - -AT_DATA([expout], -[st1 -stb -stf -stringp1 -stringp_bar -stringp_baz -stringp_foo -stz -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf --debug-dump=info ./foobarbaz.part.o \ - | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ - | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ - | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure -fdebug-types-section has updated strings in executable. -# Currently only works with DWARF4 -# === -AT_SETUP([debugedit .debug_types exe]) -AT_KEYWORDS([debugtypes] [debugedit]) -RPM_DEBUGEDIT_SETUP([-fdebug-types-section -gdwarf-4]) - -AT_DATA([expout], -[st1 -stb -stf -stringp1 -stringp_bar -stringp_baz -stringp_foo -stz -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf --debug-dump=info ./foobarbaz.exe \ - | awk '/Abbrev Number:.*DW_TAG_type_unit/{p=1}{if(p)print}/^$/{p=0}' \ - | sed -n 's/^.*> *DW_AT_name *:.* \(stringp[^ ]*\|st.\)$/\1/p' \ - | sort -]],[0],[expout]) - -AT_CLEANUP - -# foo.o and bar.o are build with relative paths and so will use the -# comp_dir (from .debug_info). But bar.o is build from sources with -# an absolute path, so the .debug_line Directory Table should contain -# /foo/bar/baz and /foo/bar/baz/subdir_headers. - -# === -# Make sure .debug_line Directory Table entries are replaced -# in objects. -# === -AT_SETUP([debugedit .debug_line objects DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf --debug-dump=line foo.o subdir_bar/bar.o baz.o \ - | grep -A3 "The Directory Table" | grep "^ [123]" \ - | grep /foo/ | cut -c5- | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_line Directory Table entries are replaced -# in objects. -# === -AT_SETUP([debugedit .debug_line objects DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -AT_DATA([expout], -[foo/bar/baz -foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf --debug-dump=line foo.o subdir_bar/bar.o baz.o \ - | grep -A5 "The Directory Table" | grep "^ [123]" \ - | cut -f2- -d/ | grep ^foo/ | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_line Directory Table entries are replaced -# in partial linked object. -# === -AT_SETUP([debugedit .debug_line partial DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf --debug-dump=line ./foobarbaz.part.o \ - | grep -A3 "The Directory Table" | grep "^ [123]" \ - | grep /foo/ | cut -c5- | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_line Directory Table entries are replaced -# in partial linked object. -# === -AT_SETUP([debugedit .debug_line partial DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -AT_DATA([expout], -[foo/bar/baz -foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf --debug-dump=line ./foobarbaz.part.o \ - | grep -A5 "The Directory Table" | grep "^ [123]" \ - | cut -f2- -d/ | grep ^foo/ | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_line Directory Table entries are replaced -# in executable. -# === -AT_SETUP([debugedit .debug_line exe DWARF4]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-4]) - -AT_DATA([expout], -[/foo/bar/baz -/foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf --debug-dump=line ./foobarbaz.exe \ - | grep -A3 "The Directory Table" | grep "^ [123]" \ - | grep /foo/ | cut -c5- | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_line Directory Table entries are replaced -# in executable. -# === -AT_SETUP([debugedit .debug_line exe DWARF5]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP([-gdwarf-5]) - -AT_DATA([expout], -[foo/bar/baz -foo/bar/baz/subdir_headers -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf --debug-dump=line ./foobarbaz.exe \ - | grep -A5 "The Directory Table" | grep "^ [123]" \ - | cut -f2- -d/ | grep ^foo/ | sort -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_macro strings are still there -# in objects. -# === -AT_SETUP([debugedit .debug_macro objects]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -# We expect 3 for each compile unit. -AT_DATA([expout], -[NUMBER 42 -NUMBER 42 -NUMBER 42 -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foo.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./subdir_bar/bar.o]]) -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./baz.o]]) -AT_CHECK([[ -readelf --debug-dump=macro foo.o subdir_bar/bar.o baz.o \ - | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_macro strings are still there -# in partial linked object. -# === -AT_SETUP([debugedit .debug_macro partial]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -# We expect 3 for each compile unit. -AT_DATA([expout], -[NUMBER 42 -NUMBER 42 -NUMBER 42 -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.part.o]]) -AT_CHECK([[ -readelf --debug-dump=macro ./foobarbaz.part.o \ - | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- -]],[0],[expout]) - -AT_CLEANUP - -# === -# Make sure .debug_macro strings are still there -# in executable. -# === -AT_SETUP([debugedit .debug_macro exe]) -AT_KEYWORDS([debuginfo] [debugedit]) -RPM_DEBUGEDIT_SETUP - -# We expect 3 for each compile unit. -AT_DATA([expout], -[NUMBER 42 -NUMBER 42 -NUMBER 42 -]) - -AT_CHECK([[debugedit -b $(pwd) -d /foo/bar/baz ./foobarbaz.exe]]) -AT_CHECK([[ -readelf --debug-dump=macro ./foobarbaz.exe \ - | grep NUMBER | rev | cut -d: -f1 | rev | cut -c2- -]],[0],[expout]) - -AT_CLEANUP diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at index 09fbea1402..730b79b3a4 100644 --- a/tests/rpmbuild.at +++ b/tests/rpmbuild.at @@ -620,6 +620,7 @@ AT_CLEANUP # Check if rpmbuild creates the minisymtab section in the main hello binary AT_SETUP([rpmbuild debuginfo minisymtab]) AT_KEYWORDS([build] [debuginfo]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -657,6 +658,7 @@ AT_CLEANUP # but the debug symbols/info in the debuginfo package. AT_SETUP([rpmbuild debuginfo minisymtab strip -g]) AT_KEYWORDS([build] [debuginfo]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -696,6 +698,7 @@ AT_CLEANUP # Test the case without unique debug file names. AT_SETUP([rpmbuild debuginfo dwz]) AT_KEYWORDS([build] [debuginfo] [dwz]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -782,6 +785,7 @@ AT_CLEANUP # Check that rpmbuild creates no debuginfo when --nodebuginfo is passed AT_SETUP([rpmbuild no debuginfo]) AT_KEYWORDS([build] [debuginfo]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -815,6 +819,7 @@ AT_CLEANUP # Test with unique debug file names. AT_SETUP([rpmbuild debuginfo dwz unique debug names]) AT_KEYWORDS([build] [debuginfo] [dwz]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -896,41 +901,12 @@ test "$canonmultiref" = "$canonmultifile" \ [ignore]) AT_CLEANUP -# ------------------------------ -# Check that old style gnu_debuglink CRC checksums are correct even after -# using dwz to compress the debuginfo files. -AT_SETUP([rpmbuild debuginfo dwz gnu_debuglink crc]) -AT_KEYWORDS([build] [debuginfo]) -AT_CHECK([ -RPMDB_INIT - -rundebug rpmbuild --quiet \ - -ba "${abs_srcdir}"/data/SPECS/hello2.spec - -# Unpack the main and debuginfo rpms so we can check binaries and .debug files. -rpm2cpio ${RPMTEST}/build/RPMS/*/hello2-debuginfo-1.0-1.*.rpm \ - | cpio -diu -rpm2cpio ${RPMTEST}/build/RPMS/*/hello2-1.0-1.*.rpm \ - | cpio -diu - -# Check that dwz has ran and a multi file has been produced -test -f ./usr/lib/debug/.dwz/hello2-1.0-1.* || exit 1 - -# Run sepdbugcrcfix on the binaries, both should have correct CRC already. -${abs_top_builddir}/sepdebugcrcfix ./usr/lib/debug \ - ./usr/local/bin/hello ./usr/local/bin/hello2 | grep CRC32 | cut -f2 -d: -], -[0], -[ Updated 0 CRC32s, 2 CRC32s did match. -], -[ignore]) -AT_CLEANUP - # ------------------------------ # Check that an implicit suid binary get included with the suid bit set. # We explicitly build with all debug.macros to test those helpers. AT_SETUP([rpmbuild implicit suid binary]) AT_KEYWORDS([build] [debuginfo] [dwz] [suid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -962,6 +938,7 @@ AT_CLEANUP # Check that a GDB index is included when requested. AT_SETUP([rpmbuild debuginfo gdb index included]) AT_KEYWORDS([build] [debuginfo] [gdb]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -988,6 +965,7 @@ AT_CLEANUP # Check that a GDB index is NOT included when not requested. AT_SETUP([rpmbuild debuginfo no gdb index included]) AT_KEYWORDS([build] [debuginfo] [gdb]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1013,6 +991,7 @@ AT_CLEANUP # Check that a -g3 (macros) build creates a valid .debug file. AT_SETUP([rpmbuild debuginfo -g3 .debug_macro]) AT_KEYWORDS([build] [debuginfo] [gdb]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1043,6 +1022,7 @@ AT_CLEANUP # Check that a debug source is in a "unique" directory when requested. AT_SETUP([rpmbuild debuginfo unique debug src dir]) AT_KEYWORDS([build] [debuginfo]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1079,6 +1059,7 @@ AT_CLEANUP # It will be in the "build directory" name under /usr/src/debug. AT_SETUP([rpmbuild debuginfo no unique debug src dir]) AT_KEYWORDS([build] [debuginfo]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1112,6 +1093,7 @@ AT_CLEANUP # Check that defining _debugsource_packages creates -debugsource package AT_SETUP([rpmbuild debugsource]) AT_KEYWORDS([build] [debuginfo] [debugsource]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1143,6 +1125,7 @@ AT_CLEANUP # should be in expected build dir). AT_SETUP([rpmbuild debugsource debugsourcefiles.list path]) AT_KEYWORDS([build] [debuginfo] [debugsource]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1167,6 +1150,7 @@ AT_CLEANUP # Check that undefining _debuginfo_subpackages creates one single -debuginfo. AT_SETUP([rpmbuild debuginfo subpackages single]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1205,6 +1189,7 @@ AT_CLEANUP # Check that defining _debuginfo_subpackages creates multiple -debuginfos. AT_SETUP([rpmbuild debuginfo subpackages multiple]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1285,6 +1270,7 @@ AT_CLEANUP # With unique debug and source names AT_SETUP([rpmbuild debuginfo subpackages multiple unique]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1365,6 +1351,7 @@ AT_CLEANUP # Unique with debugsources. AT_SETUP([rpmbuild debuginfo subpackages multiple unique debugsource]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage] [debugsource]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1448,6 +1435,7 @@ AT_CLEANUP # Check that defining _debuginfo_subpackages works with excluded files. AT_SETUP([rpmbuild debuginfo subpackages multiple excluded]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage] [debugsource]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1508,6 +1496,7 @@ AT_CLEANUP # Check that defining _debuginfo_subpackages works with RemovePathPostfixes. AT_SETUP([rpmbuild debuginfo subpackages multiple excluded]) AT_KEYWORDS([build] [debuginfo] [debugsubpackage] [debugsource]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT diff --git a/tests/rpmbuildid.at b/tests/rpmbuildid.at index c29b48e255..a476270381 100644 --- a/tests/rpmbuildid.at +++ b/tests/rpmbuildid.at @@ -22,6 +22,7 @@ AT_BANNER([RPM buildid tests]) # Check if rpmbuild "none" doesn't generates buildid symlinks for hello program AT_SETUP([rpmbuild buildid none]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -52,6 +53,7 @@ AT_CLEANUP # Without unique debug file names. AT_SETUP([rpmbuild buildid alldebug]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -140,6 +142,7 @@ AT_CLEANUP # With unique debug file names. AT_SETUP([rpmbuild buildid alldebug unique debug names]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -227,6 +230,7 @@ AT_CLEANUP # Without unique debug file names AT_SETUP([rpmbuild buildid separate]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -314,6 +318,7 @@ AT_CLEANUP # With unique debug file names AT_SETUP([rpmbuild buildid separate unique debug names]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -400,6 +405,7 @@ AT_CLEANUP # Without unique debug file names AT_SETUP([rpmbuild buildid compat]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -500,6 +506,7 @@ AT_CLEANUP # With unique debug file names AT_SETUP([rpmbuild buildid compat unique debug names]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -600,6 +607,7 @@ AT_CLEANUP # This is simply the hello example with one binary copied. AT_SETUP([rpmbuild buildid duplicate alldebug]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # Should create two warnings @@ -664,6 +672,7 @@ AT_CLEANUP # This is simply the hello example with one binary hard linked. AT_SETUP([rpmbuild buildid hardlink alldebug]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # No warnings for hard links @@ -725,6 +734,7 @@ AT_CLEANUP # This is simply the hello example with one binary copied. AT_SETUP([rpmbuild buildid duplicate separate]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # Should create two warnings @@ -786,6 +796,7 @@ AT_CLEANUP # This is simply the hello example with one binary hard linked. AT_SETUP([rpmbuild buildid hardlink separate]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # No warnings for hard links @@ -844,6 +855,7 @@ AT_CLEANUP # This is simply the hello example with one binary copied. AT_SETUP([rpmbuild buildid duplicate compat]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # Should create two warnings @@ -917,6 +929,7 @@ AT_CLEANUP # This is simply the hello example with one binary hard linked. AT_SETUP([rpmbuild buildid hardlink compat]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # No warnings for hard links @@ -986,6 +999,7 @@ AT_CLEANUP # but not with _no_recompute_build_ids AT_SETUP([rpmbuild buildid recompute]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # Make sure we get debuginfo @@ -1096,6 +1110,7 @@ AT_CLEANUP # with _unique_build_ids defined. AT_SETUP([rpmbuild buildid unique r1 r2]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # No warnings for hard links @@ -1140,6 +1155,7 @@ AT_CLEANUP # with _unique_build_ids undefined (and exact same sources). AT_SETUP([rpmbuild buildid non-unique r1 r2]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT # No warnings for hard links @@ -1188,6 +1204,7 @@ AT_CLEANUP # even if the spec file sets attrs explicitly. AT_SETUP([rpmbuild buildid attrs]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT @@ -1218,6 +1235,7 @@ AT_CLEANUP # even if the spec file sets config explicitly. AT_SETUP([rpmbuild buildid config attrs]) AT_KEYWORDS([build] [debuginfo] [buildid]) +AT_SKIP_IF([$DEBUGINFO_DISABLED]) AT_CHECK([ RPMDB_INIT diff --git a/tests/rpmtests.at b/tests/rpmtests.at index 48b86bdf24..a1adab8e0c 100644 --- a/tests/rpmtests.at +++ b/tests/rpmtests.at @@ -14,7 +14,6 @@ m4_include([rpmi.at]) m4_include([rpmorder.at]) m4_include([rpmbuild.at]) m4_include([rpmbuildid.at]) -m4_include([debugedit.at]) m4_include([rpmscript.at]) m4_include([rpmdeps.at]) m4_include([rpmconflict.at]) diff --git a/tools/debugedit.c b/tools/debugedit.c deleted file mode 100644 index 0fe1156a9c..0000000000 --- a/tools/debugedit.c +++ /dev/null @@ -1,3569 +0,0 @@ -/* Copyright (C) 2001-2003, 2005, 2007, 2009-2011, 2016, 2017 Red Hat, Inc. - Written by Alexander Larsson , 2002 - Based on code by Jakub Jelinek , 2001. - String/Line table rewriting by Mark Wielaard , 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 - the Free Software Foundation; either version 2, or (at your option) - any later version. - - This program 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 this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -#include "system.h" - -/* Needed for libelf */ -#define _FILE_OFFSET_BITS 64 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - - -/* 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 -#if _ELFUTILS_PREREQ (0, 167) -#include -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 -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 - -#include - -#include -#include -#include "tools/hashtab.h" - -#define DW_TAG_partial_unit 0x3c -#define DW_FORM_sec_offset 0x17 -#define DW_FORM_exprloc 0x18 -#define DW_FORM_flag_present 0x19 -#define DW_FORM_ref_sig8 0x20 - -char *base_dir = NULL; -char *dest_dir = NULL; -char *list_file = NULL; -int list_file_fd = -1; -int do_build_id = 0; -int no_recompute_build_id = 0; -char *build_id_seed = NULL; - -int show_version = 0; - -/* 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; -/* Likewise for DW_FORM_line_strp. */ -static bool need_line_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; - GElf_Ehdr ehdr; - Elf_Scn **scn; - const char *filename; - int lastscn; - size_t phnum; - struct strings debug_str, debug_line_str; - 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; \ - int shift = 0; \ - do \ - { \ - c = *(ptr)++; \ - ret |= (c & 0x7f) << shift; \ - shift += 7; \ - } while (c & 0x80); \ - \ - if (shift >= 35) \ - ret = UINT_MAX; \ - 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 (*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; - -static inline uint16_t -buf_read_ule16 (unsigned char *data) -{ - return data[0] | (data[1] << 8); -} - -static inline uint16_t -buf_read_ube16 (unsigned char *data) -{ - return data[1] | (data[0] << 8); -} - -static inline uint32_t -buf_read_ule32 (unsigned char *data) -{ - return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); -} - -static inline uint32_t -buf_read_ube32 (unsigned char *data) -{ - return data[3] | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); -} - -static const char * -strptr (DSO *dso, int sec, off_t offset) -{ - Elf_Scn *scn; - Elf_Data *data; - - scn = dso->scn[sec]; - if (offset >= 0 && (GElf_Addr) offset < dso->shdr[sec].sh_size) - { - data = NULL; - while ((data = elf_getdata (scn, data)) != NULL) - { - if (data->d_buf - && offset >= data->d_off - && offset < data->d_off + data->d_size) - return (const char *) data->d_buf + (offset - data->d_off); - } - } - - return NULL; -} - - -#define read_8(ptr) *(ptr)++ - -#define read_16(ptr) ({ \ - uint16_t ret = do_read_16 (ptr); \ - ptr += 2; \ - ret; \ -}) - -#define read_32(ptr) ({ \ - uint32_t ret = do_read_32 (ptr); \ - ptr += 4; \ - ret; \ -}) - -REL *relptr, *relend; -int reltype; - -#define do_read_32_relocated(xptr) ({ \ - uint32_t dret = do_read_32 (xptr); \ - if (relptr) \ - { \ - while (relptr < relend && relptr->ptr < (xptr)) \ - ++relptr; \ - if (relptr < relend && relptr->ptr == (xptr)) \ - { \ - if (reltype == SHT_REL) \ - dret += relptr->addend; \ - else \ - dret = relptr->addend; \ - } \ - } \ - dret; \ -}) - -#define read_32_relocated(ptr) ({ \ - uint32_t ret = do_read_32_relocated (ptr); \ - ptr += 4; \ - ret; \ -}) - -static void -dwarf2_write_le16 (unsigned char *p, uint16_t v) -{ - 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_be16 (unsigned char *p, uint16_t v) -{ - 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. */ - -static bool rel_updated; - -#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; \ - rel_updated = true; \ - } \ - } \ - else \ - do_write_32 (ptr,val); \ -}) - -#define write_32_relocated(ptr,val) ({ \ - do_write_32_relocated (ptr,val); \ - ptr += 4; \ -}) - -typedef struct debug_section - { - const char *name; - unsigned char *data; - Elf_Data *elf_data; - size_t size; - int sec, relsec; - REL *relbuf; - REL *relend; - /* Only happens for COMDAT .debug_macro and .debug_types. */ - struct debug_section *next; - } debug_section; - -static debug_section debug_sections[] = - { -#define DEBUG_INFO 0 -#define DEBUG_ABBREV 1 -#define DEBUG_LINE 2 -#define DEBUG_ARANGES 3 -#define DEBUG_PUBNAMES 4 -#define DEBUG_PUBTYPES 5 -#define DEBUG_MACINFO 6 -#define DEBUG_LOC 7 -#define DEBUG_STR 8 -#define DEBUG_FRAME 9 -#define DEBUG_RANGES 10 -#define DEBUG_TYPES 11 -#define DEBUG_MACRO 12 -#define DEBUG_GDB_SCRIPT 13 -#define DEBUG_RNGLISTS 14 -#define DEBUG_LINE_STR 15 -#define DEBUG_ADDR 16 -#define DEBUG_STR_OFFSETS 17 -#define DEBUG_LOCLISTS 18 - { ".debug_info", NULL, NULL, 0, 0, 0 }, - { ".debug_abbrev", NULL, NULL, 0, 0, 0 }, - { ".debug_line", NULL, NULL, 0, 0, 0 }, - { ".debug_aranges", NULL, NULL, 0, 0, 0 }, - { ".debug_pubnames", NULL, NULL, 0, 0, 0 }, - { ".debug_pubtypes", NULL, NULL, 0, 0, 0 }, - { ".debug_macinfo", NULL, NULL, 0, 0, 0 }, - { ".debug_loc", NULL, NULL, 0, 0, 0 }, - { ".debug_str", NULL, NULL, 0, 0, 0 }, - { ".debug_frame", NULL, NULL, 0, 0, 0 }, - { ".debug_ranges", NULL, NULL, 0, 0, 0 }, - { ".debug_types", NULL, NULL, 0, 0, 0 }, - { ".debug_macro", NULL, NULL, 0, 0, 0 }, - { ".debug_gdb_scripts", NULL, NULL, 0, 0, 0 }, - { ".debug_rnglists", NULL, NULL, 0, 0, 0 }, - { ".debug_line_str", NULL, NULL, 0, 0, 0 }, - { ".debug_addr", NULL, NULL, 0, 0, 0 }, - { ".debug_str_offsets", NULL, NULL, 0, 0, 0 }, - { ".debug_loclists", NULL, NULL, 0, 0, 0 }, - { NULL, NULL, NULL, 0, 0, 0 } - }; - -static int -rel_cmp (const void *a, const void *b) -{ - REL *rela = (REL *) a, *relb = (REL *) b; - - if (rela->ptr < relb->ptr) - return -1; - - if (rela->ptr > relb->ptr) - return 1; - - return 0; -} - -/* Returns a malloced REL array, or NULL when there are no relocations - for this section. When there are relocations, will setup relend, - as the last REL, and reltype, as SHT_REL or SHT_RELA. */ -static void -setup_relbuf (DSO *dso, debug_section *sec, int *reltype) -{ - int ndx, maxndx; - GElf_Rel rel; - GElf_Rela rela; - GElf_Sym sym; - GElf_Addr base = dso->shdr[sec->sec].sh_addr; - Elf_Data *symdata = NULL; - int rtype; - REL *relbuf; - Elf_Scn *scn; - Elf_Data *data; - int i = sec->relsec; - - /* No relocations, or did we do this already? */ - if (i == 0 || sec->relbuf != NULL) - { - relptr = sec->relbuf; - relend = sec->relend; - return; - } - - scn = dso->scn[i]; - data = elf_getdata (scn, NULL); - assert (data != NULL && data->d_buf != NULL); - assert (elf_getdata (scn, data) == NULL); - assert (data->d_off == 0); - assert (data->d_size == dso->shdr[i].sh_size); - maxndx = dso->shdr[i].sh_size / dso->shdr[i].sh_entsize; - relbuf = malloc (maxndx * sizeof (REL)); - *reltype = dso->shdr[i].sh_type; - if (relbuf == NULL) - error (1, errno, "%s: Could not allocate memory", dso->filename); - - symdata = elf_getdata (dso->scn[dso->shdr[i].sh_link], NULL); - assert (symdata != NULL && symdata->d_buf != NULL); - assert (elf_getdata (dso->scn[dso->shdr[i].sh_link], symdata) == NULL); - assert (symdata->d_off == 0); - assert (symdata->d_size == dso->shdr[dso->shdr[i].sh_link].sh_size); - - for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx) - { - if (dso->shdr[i].sh_type == SHT_REL) - { - gelf_getrel (data, ndx, &rel); - rela.r_offset = rel.r_offset; - rela.r_info = rel.r_info; - rela.r_addend = 0; - } - else - gelf_getrela (data, ndx, &rela); - gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym); - /* Relocations against section symbols are uninteresting in REL. */ - if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0) - continue; - /* Only consider relocations against .debug_str, .debug_line, - .debug_line_str, and .debug_abbrev. */ - if (sym.st_shndx != debug_sections[DEBUG_STR].sec - && sym.st_shndx != debug_sections[DEBUG_LINE].sec - && sym.st_shndx != debug_sections[DEBUG_LINE_STR].sec - && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec) - continue; - rela.r_addend += sym.st_value; - rtype = ELF64_R_TYPE (rela.r_info); - switch (dso->ehdr.e_machine) - { - case EM_SPARC: - case EM_SPARC32PLUS: - case EM_SPARCV9: - if (rtype != R_SPARC_32 && rtype != R_SPARC_UA32) - goto fail; - break; - case EM_386: - if (rtype != R_386_32) - goto fail; - break; - case EM_PPC: - case EM_PPC64: - if (rtype != R_PPC_ADDR32 && rtype != R_PPC_UADDR32) - goto fail; - break; - case EM_S390: - if (rtype != R_390_32) - goto fail; - break; - case EM_IA_64: - if (rtype != R_IA64_SECREL32LSB) - goto fail; - break; - case EM_X86_64: - if (rtype != R_X86_64_32) - goto fail; - break; - case EM_ALPHA: - if (rtype != R_ALPHA_REFLONG) - goto fail; - break; -#if defined(EM_AARCH64) && defined(R_AARCH64_ABS32) - case EM_AARCH64: - if (rtype != R_AARCH64_ABS32) - goto fail; - break; -#endif - case EM_68K: - if (rtype != R_68K_32) - goto fail; - break; -#if defined(EM_RISCV) && defined(R_RISCV_32) - case EM_RISCV: - if (rtype != R_RISCV_32) - goto fail; - break; -#endif - default: - fail: - error (1, 0, "%s: Unhandled relocation %d in %s section", - dso->filename, rtype, sec->name); - } - relend->ptr = sec->data - + (rela.r_offset - base); - relend->addend = rela.r_addend; - relend->ndx = ndx; - ++(relend); - } - if (relbuf == relend) - { - free (relbuf); - relbuf = NULL; - relend = NULL; - } - else - qsort (relbuf, relend - relbuf, sizeof (REL), rel_cmp); - - sec->relbuf = relbuf; - sec->relend = relend; - relptr = relbuf; -} - -/* Updates SHT_RELA section associated with the given section based on - the relbuf data. The relbuf data is freed at the end. */ -static void -update_rela_data (DSO *dso, struct debug_section *sec) -{ - Elf_Data *symdata; - int relsec_ndx = sec->relsec; - Elf_Data *data = elf_getdata (dso->scn[relsec_ndx], NULL); - symdata = elf_getdata (dso->scn[dso->shdr[relsec_ndx].sh_link], - NULL); - - relptr = sec->relbuf; - relend = sec->relend; - 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 (sec->relbuf); -} - -struct abbrev_attr - { - unsigned int attr; - unsigned int form; - }; - -struct abbrev_tag - { - unsigned int entry; - unsigned int tag; - int nattr; - struct abbrev_attr attr[0]; - }; - -static hashval_t -abbrev_hash (const void *p) -{ - struct abbrev_tag *t = (struct abbrev_tag *)p; - - return t->entry; -} - -static int -abbrev_eq (const void *p, const void *q) -{ - struct abbrev_tag *t1 = (struct abbrev_tag *)p; - struct abbrev_tag *t2 = (struct abbrev_tag *)q; - - return t1->entry == t2->entry; -} - -static void -abbrev_del (void *p) -{ - free (p); -} - -static htab_t -read_abbrev (DSO *dso, unsigned char *ptr) -{ - htab_t h = htab_try_create (50, abbrev_hash, abbrev_eq, abbrev_del); - unsigned int attr, form; - struct abbrev_tag *t; - int size; - void **slot; - - if (h == NULL) - { -no_memory: - error (0, ENOMEM, "%s: Could not read .debug_abbrev", dso->filename); - if (h) - htab_delete (h); - return NULL; - } - - while ((attr = read_uleb128 (ptr)) != 0) - { - size = 10; - t = malloc (sizeof (*t) + size * sizeof (struct abbrev_attr)); - if (t == NULL) - goto no_memory; - t->entry = attr; - t->nattr = 0; - slot = htab_find_slot (h, t, INSERT); - if (slot == NULL) - { - free (t); - goto no_memory; - } - if (*slot != NULL) - { - error (0, 0, "%s: Duplicate DWARF abbreviation %d", dso->filename, - t->entry); - free (t); - htab_delete (h); - return NULL; - } - t->tag = read_uleb128 (ptr); - ++ptr; /* skip children flag. */ - while ((attr = read_uleb128 (ptr)) != 0) - { - if (t->nattr == size) - { - size += 10; - t = realloc (t, sizeof (*t) + size * sizeof (struct abbrev_attr)); - if (t == NULL) - goto no_memory; - } - form = read_uleb128 (ptr); - if (form == 2 - || (form > DW_FORM_flag_present - && !(form == DW_FORM_ref_sig8 - || form == DW_FORM_data16 - || form == DW_FORM_line_strp - || form == DW_FORM_implicit_const - || form == DW_FORM_addrx - || form == DW_FORM_loclistx - || form == DW_FORM_rnglistx - || form == DW_FORM_addrx1 - || form == DW_FORM_addrx2 - || form == DW_FORM_addrx3 - || form == DW_FORM_addrx4))) - { - error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, - form); - htab_delete (h); - return NULL; - } - if (form == DW_FORM_implicit_const) - { - /* It is SLEB128 but the value is dropped anyway. */ - read_uleb128 (ptr); - } - - t->attr[t->nattr].attr = attr; - t->attr[t->nattr++].form = form; - } - if (read_uleb128 (ptr) != 0) - { - error (0, 0, "%s: DWARF abbreviation does not end with 2 zeros", - dso->filename); - htab_delete (h); - return NULL; - } - *slot = t; - } - - return h; -} - -#define IS_DIR_SEPARATOR(c) ((c)=='/') - -static char * -canonicalize_path (const char *s, char *d) -{ - char *rv = d; - char *droot; - - if (IS_DIR_SEPARATOR (*s)) - { - *d++ = *s++; - if (IS_DIR_SEPARATOR (*s) && !IS_DIR_SEPARATOR (s[1])) - { - /* Special case for "//foo" meaning a Posix namespace - escape. */ - *d++ = *s++; - } - while (IS_DIR_SEPARATOR (*s)) - s++; - } - droot = d; - - while (*s) - { - /* At this point, we're always at the beginning of a path - segment. */ - - if (s[0] == '.' && (s[1] == 0 || IS_DIR_SEPARATOR (s[1]))) - { - s++; - if (*s) - while (IS_DIR_SEPARATOR (*s)) - ++s; - } - - else if (s[0] == '.' && s[1] == '.' - && (s[2] == 0 || IS_DIR_SEPARATOR (s[2]))) - { - char *pre = d - 1; /* includes slash */ - while (droot < pre && IS_DIR_SEPARATOR (*pre)) - pre--; - if (droot <= pre && ! IS_DIR_SEPARATOR (*pre)) - { - while (droot < pre && ! IS_DIR_SEPARATOR (*pre)) - pre--; - /* pre now points to the slash */ - if (droot < pre) - pre++; - if (pre + 3 == d && pre[0] == '.' && pre[1] == '.') - { - *d++ = *s++; - *d++ = *s++; - } - else - { - d = pre; - s += 2; - if (*s) - while (IS_DIR_SEPARATOR (*s)) - s++; - } - } - else - { - *d++ = *s++; - *d++ = *s++; - } - } - else - { - while (*s && ! IS_DIR_SEPARATOR (*s)) - *d++ = *s++; - } - - if (IS_DIR_SEPARATOR (*s)) - { - *d++ = *s++; - while (IS_DIR_SEPARATOR (*s)) - s++; - } - } - while (droot < d && IS_DIR_SEPARATOR (d[-1])) - --d; - if (d == rv) - *d++ = '.'; - *d = 0; - - 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 '/'. - Note that DIR_PREFIX itself should NOT end with a '/'. */ -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; - /* Unless path == dir_prefix there should be at least one '/' - in the path (which we will skip). Otherwise the path has - a different (longer) directory prefix. */ - if (*path != '\0' && !IS_DIR_SEPARATOR (*path)) - return NULL; - while (IS_DIR_SEPARATOR (path[0])) - path++; - return path; - } - - return NULL; -} - -/* 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 -strent_compare (const void *a, const void *b) -{ - 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; -} - -/* 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 (bool line_strp, DSO *dso, size_t old_idx) -{ - struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str; - bool ret = false; - struct stridxentry *entry = string_find_new_entry (strings, old_idx); - if (entry != NULL) - { - debug_section *sec = &debug_sections[line_strp - ? DEBUG_LINE_STR : DEBUG_STR]; - if (old_idx >= sec->size) - error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name); - - Strent *strent; - const char *old_str = (char *)sec->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 -record_existing_string_entry_idx (bool line_strp, DSO *dso, size_t old_idx) -{ - struct strings *strings = line_strp ? &dso->debug_line_str : &dso->debug_str; - struct stridxentry *entry = string_find_new_entry (strings, old_idx); - if (entry != NULL) - { - debug_section *sec = &debug_sections[line_strp - ? DEBUG_LINE_STR : DEBUG_STR]; - if (old_idx >= sec->size) - error (1, 0, "Bad string pointer index %zd (%s)", old_idx, sec->name); - - const char *str = (char *)sec->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 void -setup_strings (struct strings *strings) -{ - 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; -} - -/* Noop for tdestroy. */ -static void free_node (void *p __attribute__((__unused__))) { } - -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 (int 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->new_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) - { - 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; - - /* 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 false; - } - - if (endcu > endsec) - { - error (0, 0, "%s: .debug_line CU does not fit into section", - dso->filename); - return false; - } - - /* version */ - t->version = read_16 (ptr); - if (t->version != 2 && t->version != 3 && t->version != 4 && t->version != 5) - { - error (0, 0, "%s: DWARF version %d unhandled", dso->filename, - t->version); - return false; - } - - if (t->version >= 5) - { - /* address_size */ - assert (ptr_size != 0); - if (ptr_size != read_8 (ptr)) - { - error (0, 0, "%s: .debug_line address size differs from .debug_info", - dso->filename); - return false; - } - - /* segment_selector_size */ - (void) read_8 (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 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) -{ - for (struct debug_section *secp = &debug_sections[sec]; secp != NULL; - secp = secp->next) - elf_flagdata (secp->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].data = linedata->d_buf; - debug_sections[DEBUG_LINE].size = linedata->d_size; - debug_sections[DEBUG_LINE].elf_data = linedata; - - /* 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 (int 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_files) - { - 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; - } -} - -/* Record or adjust (according to phase) DW_FORM_strp or DW_FORM_line_strp. */ -static void -edit_strp (DSO *dso, bool line_strp, unsigned char *ptr, int phase, - bool handled_strp) -{ - unsigned char *ptr_orig = ptr; - - /* 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 referring 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 (line_strp, dso, idx); - } - } - else if (line_strp - ? need_line_strp_update : need_strp_update) /* && phase == 1 */ - { - struct stridxentry *entry; - size_t idx, new_idx; - struct strings *strings = (line_strp - ? &dso->debug_line_str : &dso->debug_str); - idx = do_read_32_relocated (ptr); - entry = string_find_entry (strings, idx); - new_idx = strent_offset (entry->entry); - do_write_32_relocated (ptr, new_idx); - } - - assert (ptr == ptr_orig); -} - -/* Adjust *PTRP after the current *FORMP, update *FORMP for FORM_INDIRECT. */ -static enum { FORM_OK, FORM_ERROR, FORM_INDIRECT } -skip_form (DSO *dso, uint32_t *formp, unsigned char **ptrp) -{ - size_t len = 0; - - switch (*formp) - { - case DW_FORM_ref_addr: - if (cu_version == 2) - *ptrp += ptr_size; - else - *ptrp += 4; - break; - case DW_FORM_flag_present: - case DW_FORM_implicit_const: - break; - case DW_FORM_addr: - *ptrp += ptr_size; - break; - case DW_FORM_ref1: - case DW_FORM_flag: - case DW_FORM_data1: - case DW_FORM_strx1: - case DW_FORM_addrx1: - ++*ptrp; - break; - case DW_FORM_ref2: - case DW_FORM_data2: - case DW_FORM_strx2: - case DW_FORM_addrx2: - *ptrp += 2; - break; - case DW_FORM_strx3: - case DW_FORM_addrx3: - *ptrp += 3; - break; - case DW_FORM_ref4: - case DW_FORM_data4: - case DW_FORM_strx4: - case DW_FORM_addrx4: - case DW_FORM_sec_offset: - *ptrp += 4; - break; - case DW_FORM_ref8: - case DW_FORM_data8: - case DW_FORM_ref_sig8: - *ptrp += 8; - break; - case DW_FORM_data16: - *ptrp += 16; - break; - case DW_FORM_sdata: - case DW_FORM_ref_udata: - case DW_FORM_udata: - case DW_FORM_strx: - case DW_FORM_loclistx: - case DW_FORM_rnglistx: - case DW_FORM_addrx: - read_uleb128 (*ptrp); - break; - case DW_FORM_strp: - case DW_FORM_line_strp: - *ptrp += 4; - break; - case DW_FORM_string: - *ptrp = (unsigned char *) strchr ((char *)*ptrp, '\0') + 1; - break; - case DW_FORM_indirect: - *formp = read_uleb128 (*ptrp); - return FORM_INDIRECT; - case DW_FORM_block1: - len = *(*ptrp)++; - break; - case DW_FORM_block2: - len = read_16 (*ptrp); - *formp = DW_FORM_block1; - break; - case DW_FORM_block4: - len = read_32 (*ptrp); - *formp = DW_FORM_block1; - break; - case DW_FORM_block: - case DW_FORM_exprloc: - len = read_uleb128 (*ptrp); - *formp = DW_FORM_block1; - assert (len < UINT_MAX); - break; - default: - error (0, 0, "%s: Unknown DWARF DW_FORM_0x%x", dso->filename, *formp); - return FORM_ERROR; - } - - if (*formp == DW_FORM_block1) - *ptrp += len; - - return FORM_OK; -} - -/* Part of read_dwarf2_line processing DWARF-4. */ -static bool -read_dwarf4_line (DSO *dso, unsigned char *ptr, char *comp_dir, - struct line_table *table) -{ - unsigned char **dirt; - uint32_t value, dirt_cnt; - size_t comp_dir_len = !comp_dir ? 0 : strlen (comp_dir); - unsigned char *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; - } - - dirt = (unsigned char **) alloca (value * sizeof (unsigned char *)); - dirt[0] = (unsigned char *) "."; - dirt_cnt = 1; - ptr = dir; - while (*ptr != 0) - { - dirt[dirt_cnt++] = ptr; - ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; - } - ptr++; - - /* file table: */ - while (*ptr != 0) - { - char *s, *file; - size_t file_len, dir_len; - - file = (char *) ptr; - ptr = (unsigned char *) strchr ((char *)ptr, 0) + 1; - value = read_uleb128 (ptr); - - if (value >= dirt_cnt) - { - error (0, 0, "%s: Wrong directory table index %u", - dso->filename, value); - 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 false; - } - if (*file == '/') - { - memcpy (s, file, file_len + 1); - } - else if (*dirt[value] == '/') - { - memcpy (s, dirt[value], dir_len); - s[dir_len] = '/'; - memcpy (s + dir_len + 1, file, file_len + 1); - } - else - { - char *p = s; - if (comp_dir_len != 0) - { - memcpy (s, comp_dir, comp_dir_len); - s[comp_dir_len] = '/'; - p += comp_dir_len + 1; - } - memcpy (p, dirt[value], dir_len); - p[dir_len] = '/'; - memcpy (p + dir_len + 1, file, file_len + 1); - } - canonicalize_path (s, s); - if (list_file_fd != -1) - { - const char *p = NULL; - if (base_dir == NULL) - p = s; - else - { - p = skip_dir_prefix (s, base_dir); - if (p == NULL && dest_dir != NULL) - p = skip_dir_prefix (s, dest_dir); - } - - if (p) - { - size_t size = strlen (p) + 1; - while (size > 0) - { - ssize_t ret = write (list_file_fd, p, size); - if (ret == -1) - break; - size -= ret; - p += ret; - } - } - } - - free (s); - - read_uleb128 (ptr); - read_uleb128 (ptr); - } - - return true; -} - -/* Called by read_dwarf5_line first for directories and then file - names as they both have the same format. */ -static bool -read_dwarf5_line_entries (DSO *dso, unsigned char **ptrp, - struct line_table *table, int phase, - char ***dirs, int *ndir, - const char *entry_name) -{ - /* directory_entry_format_count */ - /* file_name_entry_format_count */ - unsigned format_count = read_8 (*ptrp); - - unsigned char *formats = *ptrp; - - /* directory_entry_format */ - /* file_name_entry_format */ - for (unsigned formati = 0; formati < format_count; ++formati) - { - read_uleb128 (*ptrp); - read_uleb128 (*ptrp); - } - - /* directories_count */ - /* file_names_count */ - unsigned entry_count = read_uleb128 (*ptrp); - - bool collecting_dirs = dest_dir && phase == 0 && *dirs == NULL; - bool writing_files = dest_dir && phase == 0 && *dirs != NULL; - if (collecting_dirs) - { - *ndir = entry_count; - *dirs = malloc (entry_count * sizeof (char *)); - if (*dirs == NULL) - error (1, errno, "%s: Could not allocate debug_line dirs", - dso->filename); - } - - /* directories */ - /* file_names */ - for (unsigned entryi = 0; entryi < entry_count; ++entryi) - { - char *dir = NULL; - char *file = NULL;; - unsigned char *format_ptr = formats; - for (unsigned formati = 0; formati < format_count; ++formati) - { - unsigned lnct = read_uleb128 (format_ptr); - unsigned form = read_uleb128 (format_ptr); - bool handled_form = false; - bool handled_strp = false; - bool line_strp = form == DW_FORM_line_strp; - if (lnct == DW_LNCT_path) - { - switch (form) - { - case DW_FORM_strp: - case DW_FORM_line_strp: - if (dest_dir && phase == 0) - { - size_t idx = do_read_32_relocated (*ptrp); - if (record_file_string_entry_idx (line_strp, dso, idx)) - { - if (line_strp) - need_line_strp_update = true; - else - need_strp_update = true; - } - handled_strp = true; - if (collecting_dirs || writing_files) - { - debug_section *sec = &debug_sections[line_strp - ? DEBUG_LINE_STR : DEBUG_STR]; - if (collecting_dirs) - dir = (char *)sec->data + idx; - if (writing_files) - file = (char *)sec->data + idx; - } - } - break; - default: - error (0, 0, "%s: Unsupported " - ".debug_line %s %u path DW_FORM_0x%x", - dso->filename, entry_name, entryi, form); - return false; - } - } - if (writing_files && lnct == DW_LNCT_directory_index) - { - int dirndx; - switch (form) - { - case DW_FORM_udata: - handled_form = true; - dirndx = read_uleb128 (*ptrp); - break; - case DW_FORM_data1: - dirndx = **ptrp; - break; - case DW_FORM_data2: - dirndx = do_read_16 (*ptrp); - break; - case DW_FORM_data4: - dirndx = do_read_32 (*ptrp); - break; - default: - error (0, 0, "%s: Unsupported " - ".debug_line %s %u dirndx DW_FORM_0x%x", - dso->filename, entry_name, entryi, form); - return false; - } - - if (dirndx > *ndir) - { - error (0, 0, "%s: Bad dir number %u in .debug_line %s", - dso->filename, entryi, entry_name); - return false; - } - dir = (*dirs)[dirndx]; - } - - switch (form) - { - case DW_FORM_strp: - case DW_FORM_line_strp: - edit_strp (dso, line_strp, *ptrp, phase, handled_strp); - break; - } - - if (!handled_form) - { - switch (skip_form (dso, &form, ptrp)) - { - case FORM_OK: - break; - case FORM_ERROR: - return false; - case FORM_INDIRECT: - error (0, 0, "%s: Unsupported " - ".debug_line %s %u DW_FORM_indirect", - dso->filename, entry_name, entryi); - return false; - } - } - } - - if (collecting_dirs) - (*dirs)[entryi] = dir; - - if (writing_files) - { - char *comp_dir = (*dirs)[0]; - size_t comp_dir_len = strlen(comp_dir); - size_t file_len = strlen (file); - size_t dir_len = strlen (dir); - - char *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 false; - } - if (file[0] == '/') - { - memcpy (s, file, file_len + 1); - } - else if (dir[0] == '/') - { - memcpy (s, dir, dir_len); - s[dir_len] = '/'; - memcpy (s + dir_len + 1, file, file_len + 1); - } - else - { - char *p = s; - if (comp_dir_len != 0) - { - memcpy (s, comp_dir, comp_dir_len); - s[comp_dir_len] = '/'; - p += comp_dir_len + 1; - } - memcpy (p, dir, dir_len); - p[dir_len] = '/'; - memcpy (p + dir_len + 1, file, file_len + 1); - } - canonicalize_path (s, s); - if (list_file_fd != -1) - { - const char *p = NULL; - if (base_dir == NULL) - p = s; - else - { - p = skip_dir_prefix (s, base_dir); - if (p == NULL && dest_dir != NULL) - p = skip_dir_prefix (s, dest_dir); - } - - if (p) - { - size_t size = strlen (p) + 1; - while (size > 0) - { - ssize_t ret = write (list_file_fd, p, size); - if (ret == -1) - break; - size -= ret; - p += ret; - } - } - } - - free (s); - } - } - - return true; -} - -/* Part of read_dwarf2_line processing DWARF-5. */ -static bool -read_dwarf5_line (DSO *dso, unsigned char *ptr, struct line_table *table, - int phase) -{ - char **dirs = NULL; - int ndir; - /* Skip header. */ - ptr += (4 /* unit len */ - + 2 /* version */ - + (table->version < 5 ? 0 : 0 - + 1 /* address_size */ - + 1 /* segment_selector*/) - + 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 */ - - bool retval = (read_dwarf5_line_entries (dso, &ptr, table, phase, - &dirs, &ndir, "directory") - && read_dwarf5_line_entries (dso, &ptr, table, phase, - &dirs, &ndir, "file name")); - free (dirs); - return retval; -} - -/* 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; - struct line_table *table; - - if (get_line_table (dso, off, &table) == false - || table == NULL) - 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 */ - + (table->version < 5 ? 0 : 0 - + 1 /* address_size */ - + 1 /* segment_selector*/) - + 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 */ - - /* DWARF version 5 line tables won't change size. But they might need - [line]strp recording/updates. Handle that part later. */ - if (table->version < 5) - { - if (! read_dwarf4_line (dso, ptr, comp_dir, table)) - return false; - } - - 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. */ -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; -} - -/* Read DW_FORM_strp or DW_FORM_line_strp collecting compilation directory. */ -static void -edit_attributes_str_comp_dir (bool line_strp, DSO *dso, unsigned char **ptrp, - int phase, char **comp_dirp, bool *handled_strpp) -{ - const char *dir; - size_t idx = do_read_32_relocated (*ptrp); - /* In phase zero we collect the comp_dir. */ - if (phase == 0) - { - debug_section *sec = &debug_sections[line_strp - ? DEBUG_LINE_STR : DEBUG_STR]; - if (sec->data == NULL || idx >= sec->size) - error (1, 0, "%s: Bad string pointer index %zd for comp_dir (%s)", - dso->filename, idx, sec->name); - dir = (char *) sec->data + idx; - - free (*comp_dirp); - *comp_dirp = strdup (dir); - } - - if (dest_dir != NULL && phase == 0) - { - if (record_file_string_entry_idx (line_strp, dso, idx)) - { - if (line_strp) - need_line_strp_update = true; - else - need_strp_update = true; - } - *handled_strpp = true; - } -} - -/* 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) -{ - int i; - uint32_t list_offs; - int found_list_offs; - char *comp_dir; - - comp_dir = NULL; - list_offs = 0; - found_list_offs = 0; - for (i = 0; i < t->nattr; ++i) - { - uint32_t form = t->attr[i].form; - 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); - 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 (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 zero (if no file part), one or more - slashes in between the new dest_dir and the - file name to fill up all space (replacement - DW_FORM_string must be of the same length). - We don't need to copy the old file name (if - any) or the zero terminator, because those - are already at the end of the string. */ - memcpy (ptr, dest_dir, dest_len); - memset (ptr + dest_len, '/', - orig_len - new_len); - } - } - } - } - else if (form == DW_FORM_strp) - edit_attributes_str_comp_dir (false /* line_strp */, dso, - &ptr, phase, &comp_dir, - &handled_strp); - else if (form == DW_FORM_line_strp) - edit_attributes_str_comp_dir (true /* line_strp */, dso, &ptr, - phase, &comp_dir, &handled_strp); - } - else if ((t->tag == DW_TAG_compile_unit - || t->tag == DW_TAG_partial_unit) - && ((form == DW_FORM_strp - && debug_sections[DEBUG_STR].data) - || (form == DW_FORM_line_strp - && debug_sections[DEBUG_LINE_STR].data)) - && t->attr[i].attr == DW_AT_name) - { - bool line_strp = form == DW_FORM_line_strp; - - /* 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. */ - size_t idx = do_read_32_relocated (ptr); - - /* In phase zero we will look for a comp_dir to use. */ - if (phase == 0) - { - debug_section *sec = &debug_sections[line_strp - ? DEBUG_LINE_STR - : DEBUG_STR]; - if (idx >= sec->size) - error (1, 0, - "%s: Bad string pointer index %zd for unit name (%s)", - dso->filename, idx, sec->name); - char *name = (char *) sec->data + idx; - if (*name == '/' && comp_dir == NULL) - { - char *enddir = strrchr (name, '/'); - - if (enddir != name) - { - comp_dir = malloc (enddir - name + 1); - memcpy (comp_dir, name, enddir - name); - comp_dir [enddir - name] = '\0'; - } - else - comp_dir = strdup ("/"); - } - } - - /* 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 (line_strp, dso, idx)) - { - if (line_strp) - need_line_strp_update = true; - else - need_strp_update = true; - } - handled_strp = true; - } - } - - switch (form) - { - case DW_FORM_strp: - edit_strp (dso, false /* line_strp */, ptr, phase, handled_strp); - break; - case DW_FORM_line_strp: - edit_strp (dso, true /* line_strp */, ptr, phase, handled_strp); - break; - } - - switch (skip_form (dso, &form, &ptr)) - { - case FORM_OK: - break; - case FORM_ERROR: - return NULL; - case FORM_INDIRECT: - continue; - } - - break; - } - } - - /* Ensure the CU current directory will exist even if only empty. Source - filenames possibly located in its parent directories refer relatively to - it and the debugger (GDB) cannot safely optimize out the missing - CU current dir subdirectories. Only do this once in phase one. And - only do this for dirs under our build/base_dir. Don't output the - empty string (in case the comp_dir == base_dir). */ - if (phase == 0 && base_dir && comp_dir && list_file_fd != -1) - { - const char *p = skip_dir_prefix (comp_dir, base_dir); - if (p != NULL && p[0] != '\0') - { - size_t size = strlen (p); - while (size > 0) - { - ssize_t ret = write (list_file_fd, p, size); - if (ret == -1) - break; - size -= ret; - p += ret; - } - /* Output trailing dir separator to distinguish them quickly from - regular files. */ - if (size == 0) - { - if (*(p - 1) != '/') - write (list_file_fd, "/", 2); - else - write (list_file_fd, "", 1); - } - } - } - - /* 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); - - return ptr; -} - -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_info (DSO *dso, int phase, struct debug_section *sec) -{ - unsigned char *ptr, *endcu, *endsec; - uint32_t value; - htab_t abbrev; - struct abbrev_tag tag, *t; - - ptr = sec->data; - if (ptr == NULL) - return 0; - - setup_relbuf(dso, sec, &reltype); - endsec = ptr + sec->size; - while (ptr < endsec) - { - unsigned char *cu_start = ptr; - - /* header size, version, unit_type, ptr_size. */ - if (ptr + 4 + 2 + 1 + 1 > endsec) - { - error (0, 0, "%s: %s CU header too small", - dso->filename, sec->name); - return 1; - } - - endcu = ptr + 4; - endcu += read_32 (ptr); - if (endcu == ptr + 0xffffffff) - { - error (0, 0, "%s: 64-bit DWARF not supported", dso->filename); - return 1; - } - - if (endcu > endsec) - { - error (0, 0, "%s: %s too small", dso->filename, sec->name); - return 1; - } - - cu_version = read_16 (ptr); - if (cu_version != 2 && cu_version != 3 && cu_version != 4 - && cu_version != 5) - { - error (0, 0, "%s: DWARF version %d unhandled", dso->filename, - cu_version); - return 1; - } - - int cu_ptr_size = 0; - - if (cu_version >= 5) - { - uint8_t unit_type = read_8 (ptr); - if (unit_type != DW_UT_compile && unit_type != DW_UT_partial) - { - error (0, 0, "%s: Unit type %u unhandled", dso->filename, - unit_type); - return 1; - } - - cu_ptr_size = read_8 (ptr); - } - - unsigned char *header_end = (cu_start + 23 + (cu_version < 5 ? 0 : 1)); - if (header_end > endsec) - { - error (0, 0, "%s: %s CU header too small", dso->filename, sec->name); - return 1; - } - - value = read_32_relocated (ptr); - if (value >= debug_sections[DEBUG_ABBREV].size) - { - if (debug_sections[DEBUG_ABBREV].data == NULL) - error (0, 0, "%s: .debug_abbrev not present", dso->filename); - else - error (0, 0, "%s: DWARF CU abbrev offset too large", - dso->filename); - return 1; - } - - if (cu_version < 5) - cu_ptr_size = read_8 (ptr); - - if (ptr_size == 0) - { - ptr_size = cu_ptr_size; - if (ptr_size != 4 && ptr_size != 8) - { - error (0, 0, "%s: Invalid DWARF pointer size %d", - dso->filename, ptr_size); - return 1; - } - } - else if (cu_ptr_size != ptr_size) - { - error (0, 0, "%s: DWARF pointer size differs between CUs", - dso->filename); - return 1; - } - - if (sec != &debug_sections[DEBUG_INFO]) - ptr += 12; /* Skip type_signature and type_offset. */ - - abbrev = read_abbrev (dso, - debug_sections[DEBUG_ABBREV].data + value); - if (abbrev == NULL) - return 1; - - while (ptr < endcu) - { - tag.entry = read_uleb128 (ptr); - if (tag.entry == 0) - continue; - t = htab_find_with_hash (abbrev, &tag, tag.entry); - if (t == NULL) - { - error (0, 0, "%s: Could not find DWARF abbreviation %d", - dso->filename, tag.entry); - htab_delete (abbrev); - return 1; - } - - ptr = edit_attributes (dso, ptr, t, phase); - if (ptr == NULL) - break; - } - - htab_delete (abbrev); - } - - return 0; -} - -/* Rebuild .debug_str. */ -static void -edit_dwarf2_any_str (DSO *dso, struct strings *strings, debug_section *secp) -{ - Strtab *strtab = strings->str_tab; - Elf_Data *strdata = secp->elf_data; - int strndx = secp->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); - secp->size = strdata->d_size; - strings->str_buf = strdata->d_buf; -} - -static int -edit_dwarf2 (DSO *dso) -{ - Elf_Data *data; - Elf_Scn *scn; - int i, j; - - for (i = 0; debug_sections[i].name; ++i) - { - debug_sections[i].data = NULL; - debug_sections[i].size = 0; - debug_sections[i].sec = 0; - debug_sections[i].relsec = 0; - } - ptr_size = 0; - - for (i = 1; i < dso->ehdr.e_shnum; ++i) - if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR)) - && dso->shdr[i].sh_size) - { - const char *name = strptr (dso, dso->ehdr.e_shstrndx, - dso->shdr[i].sh_name); - - if (strncmp (name, ".debug_", sizeof (".debug_") - 1) == 0) - { - for (j = 0; debug_sections[j].name; ++j) - if (strcmp (name, debug_sections[j].name) == 0) - { - struct debug_section *debug_sec = &debug_sections[j]; - if (debug_sections[j].data) - { - if (j != DEBUG_MACRO && j != DEBUG_TYPES) - { - error (0, 0, "%s: Found two copies of %s section", - dso->filename, name); - return 1; - } - else - { - /* In relocatable files .debug_macro and .debug_types - might appear multiple times as COMDAT section. */ - struct debug_section *sec; - sec = calloc (sizeof (struct debug_section), 1); - if (sec == NULL) - error (1, errno, - "%s: Could not allocate more %s sections", - dso->filename, name); - sec->name = name; - - struct debug_section *multi_sec = debug_sec; - while (multi_sec->next != NULL) - multi_sec = multi_sec->next; - - multi_sec->next = sec; - debug_sec = sec; - } - } - - scn = dso->scn[i]; - data = elf_getdata (scn, NULL); - assert (data != NULL && data->d_buf != NULL); - assert (elf_getdata (scn, data) == NULL); - assert (data->d_off == 0); - assert (data->d_size == dso->shdr[i].sh_size); - debug_sec->data = data->d_buf; - debug_sec->elf_data = data; - debug_sec->size = data->d_size; - debug_sec->sec = i; - break; - } - - if (debug_sections[j].name == NULL) - { - error (0, 0, "%s: Unknown debugging section %s", - dso->filename, name); - } - } - else if (dso->ehdr.e_type == ET_REL - && ((dso->shdr[i].sh_type == SHT_REL - && strncmp (name, ".rel.debug_", - sizeof (".rel.debug_") - 1) == 0) - || (dso->shdr[i].sh_type == SHT_RELA - && strncmp (name, ".rela.debug_", - sizeof (".rela.debug_") - 1) == 0))) - { - for (j = 0; debug_sections[j].name; ++j) - if (strcmp (name + sizeof (".rel") - 1 - + (dso->shdr[i].sh_type == SHT_RELA), - debug_sections[j].name) == 0) - { - if (j == DEBUG_MACRO || j == DEBUG_TYPES) - { - /* Pick the correct one. */ - int rel_target = dso->shdr[i].sh_info; - struct debug_section *multi_sec = &debug_sections[j]; - while (multi_sec != NULL) - { - if (multi_sec->sec == rel_target) - { - multi_sec->relsec = i; - break; - } - multi_sec = multi_sec->next; - } - if (multi_sec == NULL) - error (0, 1, "No %s reloc section: %s", - debug_sections[j].name, dso->filename); - } - else - debug_sections[j].relsec = i; - break; - } - } - } - - if (dso->ehdr.e_ident[EI_DATA] == ELFDATA2LSB) - { - do_read_16 = buf_read_ule16; - do_read_32 = buf_read_ule32; - 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; - do_write_16 = dwarf2_write_be16; - do_write_32 = dwarf2_write_be32; - } - else - { - error (0, 0, "%s: Wrong ELF data enconding", dso->filename); - return 1; - } - - if (debug_sections[DEBUG_INFO].data == NULL) - return 0; - - unsigned char *ptr, *endsec; - int phase; - bool info_rel_updated = false; - bool types_rel_updated = false; - bool macro_rel_updated = false; - bool line_rel_updated = false; - - for (phase = 0; phase < 2; phase++) - { - /* If we don't need to update anyhing, skip phase 1. */ - if (phase == 1 - && !need_strp_update - && !need_line_strp_update - && !need_string_replacement - && !need_stmt_update) - break; - - rel_updated = false; - if (edit_info (dso, phase, &debug_sections[DEBUG_INFO])) - return 1; - - /* Remember whether any .debug_info relocations might need - to be updated. */ - info_rel_updated = rel_updated; - - rel_updated = false; - struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; - while (types_sec != NULL) - { - if (edit_info (dso, phase, types_sec)) - return 1; - types_sec = types_sec->next; - } - - /* Remember whether any .debug_types relocations might need - to be updated. */ - types_rel_updated = rel_updated; - - /* 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 (lndx < dso->lines.used - && r_offset > (dso->lines.table[lndx].old_idx - + 4 - + dso->lines.table[lndx].unit_length)) - 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 += (ssize_t)((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); - } - } - - /* The .debug_macro section also contains offsets into the - .debug_str section and references to the .debug_line - tables, so we need to update those as well if we update - the strings or the stmts. */ - if ((need_strp_update || need_stmt_update) - && debug_sections[DEBUG_MACRO].data) - { - /* There might be multiple (COMDAT) .debug_macro sections. */ - struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; - while (macro_sec != NULL) - { - setup_relbuf(dso, macro_sec, &reltype); - rel_updated = false; - - ptr = macro_sec->data; - endsec = ptr + macro_sec->size; - int op = 0, macro_version, macro_flags; - int offset_len = 4, line_offset = 0; - - while (ptr < endsec) - { - if (!op) - { - macro_version = read_16 (ptr); - macro_flags = read_8 (ptr); - if (macro_version < 4 || macro_version > 5) - error (1, 0, "unhandled .debug_macro version: %d", - macro_version); - if ((macro_flags & ~2) != 0) - error (1, 0, "unhandled .debug_macro flags: 0x%x", - macro_flags); - - offset_len = (macro_flags & 0x01) ? 8 : 4; - line_offset = (macro_flags & 0x02) ? 1 : 0; - - if (offset_len != 4) - error (0, 1, - "Cannot handle 8 byte macro offsets: %s", - dso->filename); - - /* Update the line_offset if it is there. */ - if (line_offset) - { - if (phase == 0) - ptr += offset_len; - else - { - size_t idx, new_idx; - idx = do_read_32_relocated (ptr); - new_idx = find_new_list_offs (&dso->lines, - idx); - write_32_relocated (ptr, new_idx); - } - } - } - - op = read_8 (ptr); - if (!op) - continue; - switch(op) - { - case DW_MACRO_GNU_define: - case DW_MACRO_GNU_undef: - read_uleb128 (ptr); - ptr = ((unsigned char *) strchr ((char *) ptr, '\0') - + 1); - break; - case DW_MACRO_GNU_start_file: - read_uleb128 (ptr); - read_uleb128 (ptr); - break; - case DW_MACRO_GNU_end_file: - break; - case DW_MACRO_GNU_define_indirect: - case DW_MACRO_GNU_undef_indirect: - read_uleb128 (ptr); - if (phase == 0) - { - size_t idx = read_32_relocated (ptr); - record_existing_string_entry_idx (false, dso, idx); - } - else - { - struct stridxentry *entry; - size_t idx, new_idx; - idx = do_read_32_relocated (ptr); - entry = string_find_entry (&dso->debug_str, idx); - new_idx = strent_offset (entry->entry); - write_32_relocated (ptr, new_idx); - } - break; - case DW_MACRO_GNU_transparent_include: - ptr += offset_len; - break; - default: - error (1, 0, "Unhandled DW_MACRO op 0x%x", op); - break; - } - } - - if (rel_updated) - macro_rel_updated = true; - macro_sec = macro_sec->next; - } - } - - - /* Now handle all the DWARF5 line tables, they contain strp - and/or line_strp entries that need to be registered/rewritten. */ - setup_relbuf(dso, &debug_sections[DEBUG_LINE], &reltype); - rel_updated = false; - - /* edit_dwarf2_line will have set this up, unless there are no - moved/resized (DWARF4) lines. In which case we can just use - the original section data. new_idx will have been setup - correctly, even if it is the same as old_idx. */ - unsigned char *line_buf = (unsigned char *)dso->lines.line_buf; - if (line_buf == NULL) - line_buf = debug_sections[DEBUG_LINE].data; - for (int ldx = 0; ldx < dso->lines.used; ldx++) - { - struct line_table *t = &dso->lines.table[ldx]; - if (t->version >= 5) - read_dwarf5_line (dso, line_buf + t->new_idx, t, phase); - } - if (rel_updated) - line_rel_updated = true; - - /* Same for the debug_str and debug_line_str sections. - Make sure everything is in place for phase 1 updating of debug_info - references. */ - if (phase == 0 && need_strp_update) - edit_dwarf2_any_str (dso, &dso->debug_str, - &debug_sections[DEBUG_STR]); - if (phase == 0 && need_line_strp_update) - edit_dwarf2_any_str (dso, &dso->debug_line_str, - &debug_sections[DEBUG_LINE_STR]); - } - - /* After phase 1 we might have rewritten the debug_info with - new strp, strings and/or linep offsets. */ - if (need_strp_update || need_line_strp_update - || need_string_replacement || need_stmt_update) { - dirty_section (DEBUG_INFO); - if (debug_sections[DEBUG_TYPES].data != NULL) - dirty_section (DEBUG_TYPES); - } - if (need_strp_update || need_stmt_update) - dirty_section (DEBUG_MACRO); - if (need_stmt_update || need_line_strp_update) - dirty_section (DEBUG_LINE); - - /* Update any relocations addends we might have touched. */ - if (info_rel_updated) - update_rela_data (dso, &debug_sections[DEBUG_INFO]); - if (types_rel_updated) - { - struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; - while (types_sec != NULL) - { - update_rela_data (dso, types_sec); - types_sec = types_sec->next; - } - } - - if (macro_rel_updated) - { - struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; - while (macro_sec != NULL) - { - update_rela_data (dso, macro_sec); - macro_sec = macro_sec->next; - } - } - - if (line_rel_updated) - update_rela_data (dso, &debug_sections[DEBUG_LINE]); - - return 0; -} - -static struct poptOption optionsTable[] = { - { "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 }, - { "no-recompute-build-id", 'n', POPT_ARG_NONE, &no_recompute_build_id, 0, - "do not recompute build ID note even when -i or -s are given", NULL }, - { "version", '\0', POPT_ARG_NONE, &show_version, 0, - "print the debugedit version", NULL }, - POPT_AUTOHELP - { NULL, 0, 0, NULL, 0, NULL, NULL } -}; - -static DSO * -fdopen_dso (int fd, const char *name) -{ - Elf *elf = NULL; - GElf_Ehdr ehdr; - int i; - DSO *dso = NULL; - size_t phnum; - - elf = elf_begin (fd, ELF_C_RDWR, NULL); - if (elf == NULL) - { - error (0, 0, "cannot open ELF file: %s", elf_errmsg (-1)); - goto error_out; - } - - if (elf_kind (elf) != ELF_K_ELF) - { - error (0, 0, "\"%s\" is not an ELF file", name); - goto error_out; - } - - if (gelf_getehdr (elf, &ehdr) == NULL) - { - error (0, 0, "cannot get the ELF header: %s", - elf_errmsg (-1)); - goto error_out; - } - - if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC && ehdr.e_type != ET_REL) - { - error (0, 0, "\"%s\" is not a shared library", name); - goto error_out; - } - - /* Allocate DSO structure. Leave place for additional 20 new section - headers. */ - dso = (DSO *) - malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr) - + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *)); - if (!dso) - { - error (0, ENOMEM, "Could not open DSO"); - goto error_out; - } - - 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]; - - for (i = 0; i < ehdr.e_shnum; ++i) - { - dso->scn[i] = elf_getscn (elf, i); - gelf_getshdr (dso->scn[i], dso->shdr + i); - } - - dso->filename = (const char *) strdup (name); - setup_strings (&dso->debug_str); - setup_strings (&dso->debug_line_str); - setup_lines (&dso->lines); - return dso; - -error_out: - if (dso) - { - free ((char *) dso->filename); - destroy_strings (&dso->debug_str); - destroy_strings (&dso->debug_line_str); - destroy_lines (&dso->lines); - free (dso); - } - if (elf) - elf_end (elf); - if (fd != -1) - close (fd); - return NULL; -} - -static const pgpHashAlgo algorithms[] = { PGPHASHALGO_MD5, - PGPHASHALGO_SHA1, PGPHASHALGO_SHA256, PGPHASHALGO_SHA384, PGPHASHALGO_SHA512 }; - -/* 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) -{ - DIGEST_CTX ctx; - pgpHashAlgo algorithm; - int i = sizeof(algorithms)/sizeof(algorithms[0]); - void *digest = NULL; - size_t len; - - while (i-- > 0) - { - algorithm = algorithms[i]; - if (rpmDigestLength(algorithm) == build_id_size) - break; - } - if (i < 0) - { - fprintf (stderr, "Cannot handle %Zu-byte build ID\n", build_id_size); - exit (1); - } - - if (no_recompute_build_id - || (! dirty_elf && build_id_seed == NULL)) - goto print; - - /* 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); - - ctx = rpmDigestInit(algorithm, 0); - - /* If a seed string was given use it to prime the hash. */ - if (build_id_seed != NULL) - 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 - fields in ehdr and shdrs, since the semantically identical ELF file - could be written differently if it doesn't change the phdr layout. - We always use the GElf (i.e. Elf64) formats for the bits to hash - since it is convenient. It doesn't matter whether this is an Elf32 - or Elf64 object, only that we are consistent in what bits feed the - hash so it comes out the same for the same file contents. */ - { - union - { - GElf_Ehdr ehdr; - GElf_Phdr phdr; - GElf_Shdr shdr; - } 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", - elf_errmsg (elf_errno ())); - exit (1); - } - - x.d_type = ELF_T_PHDR; - x.d_size = sizeof u.phdr; - for (i = 0; i < dso->ehdr.e_phnum; ++i) - { - if (gelf_getphdr (dso->elf, i, &u.phdr) == NULL) - goto bad; - if (elf64_xlatetom (&x, &x, dso->ehdr.e_ident[EI_DATA]) == NULL) - goto bad; - rpmDigestUpdate(ctx, x.d_buf, x.d_size); - } - - x.d_type = ELF_T_SHDR; - x.d_size = sizeof u.shdr; - for (i = 0; i < dso->ehdr.e_shnum; ++i) - if (dso->scn[i] != 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; - rpmDigestUpdate(ctx, x.d_buf, x.d_size); - - if (u.shdr.sh_type != SHT_NOBITS) - { - Elf_Data *d = elf_getdata (dso->scn[i], NULL); - if (d == NULL) - goto bad; - rpmDigestUpdate(ctx, d->d_buf, d->d_size); - } - } - } - - 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); - - print: - /* 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 = pgpHexStr(id, build_id_size); - puts (hex); - free(hex); - } -} - -int -main (int argc, char *argv[]) -{ - DSO *dso; - int fd, i; - const char *file; - poptContext optCon; /* context for parsing command-line options */ - int nextopt; - const char **args; - struct stat stat_buf; - 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 */ ; - - if (nextopt != -1) - { - fprintf (stderr, "Error on option %s: %s.\nRun '%s --help' to see a full list of available command line options.\n", - poptBadOption (optCon, 0), - poptStrerror (nextopt), - argv[0]); - exit (1); - } - - if (show_version) - { - printf("RPM debugedit %s\n", VERSION); - exit(EXIT_SUCCESS); - } - - args = poptGetArgs (optCon); - if (args == NULL || args[0] == NULL || args[1] != NULL) - { - poptPrintHelp(optCon, stdout, 0); - exit (1); - } - - if (dest_dir != NULL) - { - if (base_dir == NULL) - { - fprintf (stderr, "You must specify a base dir if you specify a dest dir\n"); - exit (1); - } - } - - if (build_id_seed != NULL && do_build_id == 0) - { - fprintf (stderr, "--build-id-seed (-s) needs --build-id (-i)\n"); - exit (1); - } - - if (build_id_seed != NULL && strlen (build_id_seed) < 1) - { - fprintf (stderr, - "--build-id-seed (-s) string should be at least 1 char\n"); - exit (1); - } - - /* 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); - - if (list_file != NULL) - { - list_file_fd = open (list_file, O_WRONLY|O_CREAT|O_APPEND, 0644); - } - - file = args[0]; - - if (elf_version(EV_CURRENT) == EV_NONE) - { - fprintf (stderr, "library out of date\n"); - exit (1); - } - - if (stat(file, &stat_buf) < 0) - { - fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno)); - exit (1); - } - - /* Make sure we can read and write */ - chmod (file, stat_buf.st_mode | S_IRUSR | S_IWUSR); - - fd = open (file, O_RDWR); - if (fd < 0) - { - fprintf (stderr, "Failed to open input file '%s': %s\n", file, strerror(errno)); - exit (1); - } - - dso = fdopen_dso (fd, file); - if (dso == NULL) - exit (1); - - for (i = 1; i < dso->ehdr.e_shnum; i++) - { - const char *name; - - switch (dso->shdr[i].sh_type) - { - case SHT_PROGBITS: - name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name); - /* TODO: Handle stabs */ - if (strcmp (name, ".stab") == 0) - { - fprintf (stderr, "Stabs debuginfo not supported: %s\n", file); - break; - } - if (!(do_build_id && no_recompute_build_id && !base_dir && !dest_dir) - && strcmp (name, ".debug_info") == 0) - edit_dwarf2 (dso); - - break; - case SHT_NOTE: - if (do_build_id - && build_id == 0 && (dso->shdr[i].sh_flags & SHF_ALLOC)) - { - /* Look for a build-ID note here. */ - size_t off = 0; - GElf_Nhdr nhdr; - size_t name_off; - size_t desc_off; - Elf_Data *data = elf_getdata (elf_getscn (dso->elf, i), NULL); - while ((off = gelf_getnote (data, off, - &nhdr, &name_off, &desc_off)) > 0) - if (nhdr.n_type == NT_GNU_BUILD_ID - && nhdr.n_namesz == sizeof "GNU" - && (memcmp ((char *)data->d_buf + name_off, "GNU", - sizeof "GNU") == 0)) - { - build_id = data; - build_id_offset = desc_off; - build_id_size = nhdr.n_descsz; - } - } - break; - default: - break; - } - } - - /* Normally we only need to explicitly update the section headers - and data when any section data changed size. But because of a bug - in elfutils before 0.169 we will have to update and write out all - section data if any data has changed (when ELF_F_LAYOUT was - set). https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */ - bool need_update = (need_strp_update - || need_line_strp_update - || need_stmt_update); - -#if !_ELFUTILS_PREREQ (0, 169) - /* string replacements or build_id updates don't change section size. */ - need_update = (need_update - || need_string_replacement - || (do_build_id && build_id != NULL)); -#endif - - /* 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 responsible 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_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. - https://sourceware.org/bugzilla/show_bug.cgi?id=21199 */ -#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, debug_line_str or debug_line section. */ - size_t secnum = elf_ndxscn (scn); - if (secnum == debug_sections[DEBUG_STR].sec) - sec_size = debug_sections[DEBUG_STR].size; - if (secnum == debug_sections[DEBUG_LINE_STR].sec) - sec_size = debug_sections[DEBUG_LINE_STR].size; - if (secnum == 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); - - if (elf_update (dso->elf, ELF_C_WRITE) < 0) - { - fprintf (stderr, "Failed to write file: %s\n", elf_errmsg (elf_errno())); - exit (1); - } - if (elf_end (dso->elf) < 0) - { - fprintf (stderr, "elf_end failed: %s\n", elf_errmsg (elf_errno())); - exit (1); - } - close (fd); - - /* Restore old access rights */ - chmod (file, stat_buf.st_mode); - - free ((char *) dso->filename); - destroy_strings (&dso->debug_str); - destroy_strings (&dso->debug_line_str); - destroy_lines (&dso->lines); - free (dso); - - /* In case there were multiple (COMDAT) .debug_macro sections, - free them. */ - struct debug_section *macro_sec = &debug_sections[DEBUG_MACRO]; - macro_sec = macro_sec->next; - while (macro_sec != NULL) - { - struct debug_section *next = macro_sec->next; - free (macro_sec); - macro_sec = next; - } - - /* In case there were multiple (COMDAT) .debug_types sections, - free them. */ - struct debug_section *types_sec = &debug_sections[DEBUG_TYPES]; - types_sec = types_sec->next; - while (types_sec != NULL) - { - struct debug_section *next = types_sec->next; - free (types_sec); - types_sec = next; - } - - poptFreeContext (optCon); - - return 0; -} diff --git a/tools/hashtab.c b/tools/hashtab.c deleted file mode 100644 index 7bd576df27..0000000000 --- a/tools/hashtab.c +++ /dev/null @@ -1,523 +0,0 @@ -/* An expandable hash tables datatype. - Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. - Contributed by Vladimir Makarov (vmakarov@cygnus.com). - -This file is part of the libiberty library. -Libiberty is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public -License as published by the Free Software Foundation; either -version 2 of the License, or (at your option) any later version. - -Libiberty 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 -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with libiberty; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ - -/* This package implements basic hash table functionality. It is possible - to search for an entry, create an entry and destroy an entry. - - Elements in the table are generic pointers. - - The size of the table is not fixed; if the occupancy of the table - grows too high the hash table will be expanded. - - The abstract data implementation is based on generalized Algorithm D - from Knuth's book "The art of computer programming". Hash table is - expanded by creation of new hash table and transferring elements from - the old table to the new table. */ - -#include -#include -#include -#include -#include "tools/hashtab.h" - -/* This macro defines reserved value for empty table entry. */ - -#define EMPTY_ENTRY ((void *) 0) - -/* This macro defines reserved value for table entry which contained - a deleted element. */ - -#define DELETED_ENTRY ((void *) 1) - -static unsigned long higher_prime_number (unsigned long); -static hashval_t hash_pointer (const void *); -static int eq_pointer (const void *, const void *); -static int htab_expand (htab_t); -static void **find_empty_slot_for_expand (htab_t, hashval_t); - -/* At some point, we could make these be NULL, and modify the - hash-table routines to handle NULL specially; that would avoid - function-call overhead for the common case of hashing pointers. */ -htab_hash htab_hash_pointer = hash_pointer; -htab_eq htab_eq_pointer = eq_pointer; - -/* The following function returns a nearest prime number which is - greater than N, and near a power of two. */ - -static unsigned long -higher_prime_number (n) - unsigned long n; -{ - /* These are primes that are near, but slightly smaller than, a - power of two. */ - static unsigned long primes[] = { - (unsigned long) 2, - (unsigned long) 7, - (unsigned long) 13, - (unsigned long) 31, - (unsigned long) 61, - (unsigned long) 127, - (unsigned long) 251, - (unsigned long) 509, - (unsigned long) 1021, - (unsigned long) 2039, - (unsigned long) 4093, - (unsigned long) 8191, - (unsigned long) 16381, - (unsigned long) 32749, - (unsigned long) 65521, - (unsigned long) 131071, - (unsigned long) 262139, - (unsigned long) 524287, - (unsigned long) 1048573, - (unsigned long) 2097143, - (unsigned long) 4194301, - (unsigned long) 8388593, - (unsigned long) 16777213, - (unsigned long) 33554393, - (unsigned long) 67108859, - (unsigned long) 134217689, - (unsigned long) 268435399, - (unsigned long) 536870909, - (unsigned long) 1073741789, - (unsigned long) 2147483647, - /* 4294967291L */ - ((unsigned long) 2147483647) + ((unsigned long) 2147483644), - }; - - unsigned long* low = &primes[0]; - unsigned long* high = &primes[sizeof(primes) / sizeof(primes[0])]; - - while (low != high) - { - unsigned long* mid = low + (high - low) / 2; - if (n > *mid) - low = mid + 1; - else - high = mid; - } - - /* If we've run out of primes, abort. */ - if (n > *low) - { - fprintf (stderr, "Cannot find prime bigger than %lu\n", n); - abort (); - } - - return *low; -} - -/* Returns a hash code for P. */ - -static hashval_t -hash_pointer (p) - const void * p; -{ - return (hashval_t) ((long)p >> 3); -} - -/* Returns non-zero if P1 and P2 are equal. */ - -static int -eq_pointer (p1, p2) - const void * p1; - const void * p2; -{ - return p1 == p2; -} - -/* This function creates table with length slightly longer than given - source length. The created hash table is initiated as empty (all the - hash table entries are EMPTY_ENTRY). The function returns the created - hash table. Memory allocation may fail; it may return NULL. */ - -htab_t -htab_try_create (size, hash_f, eq_f, del_f) - size_t size; - htab_hash hash_f; - htab_eq eq_f; - htab_del del_f; -{ - htab_t result; - - size = higher_prime_number (size); - result = (htab_t) calloc (1, sizeof (struct htab)); - if (result == NULL) - return NULL; - - result->entries = (void **) calloc (size, sizeof (void *)); - if (result->entries == NULL) - { - free (result); - return NULL; - } - - result->size = size; - result->hash_f = hash_f; - result->eq_f = eq_f; - result->del_f = del_f; - result->return_allocation_failure = 1; - return result; -} - -/* This function frees all memory allocated for given hash table. - Naturally the hash table must already exist. */ - -void -htab_delete (htab) - htab_t htab; -{ - int i; - - if (htab->del_f) - for (i = htab->size - 1; i >= 0; i--) - if (htab->entries[i] != EMPTY_ENTRY - && htab->entries[i] != DELETED_ENTRY) - (*htab->del_f) (htab->entries[i]); - - free (htab->entries); - free (htab); -} - -/* This function clears all entries in the given hash table. */ - -void -htab_empty (htab) - htab_t htab; -{ - int i; - - if (htab->del_f) - for (i = htab->size - 1; i >= 0; i--) - if (htab->entries[i] != EMPTY_ENTRY - && htab->entries[i] != DELETED_ENTRY) - (*htab->del_f) (htab->entries[i]); - - memset (htab->entries, 0, htab->size * sizeof (void *)); -} - -/* Similar to htab_find_slot, but without several unwanted side effects: - - Does not call htab->eq_f when it finds an existing entry. - - Does not change the count of elements/searches/collisions in the - hash table. - This function also assumes there are no deleted entries in the table. - HASH is the hash value for the element to be inserted. */ - -static void ** -find_empty_slot_for_expand (htab, hash) - htab_t htab; - hashval_t hash; -{ - size_t size = htab->size; - hashval_t hash2 = 1 + hash % (size - 2); - unsigned int index = hash % size; - - for (;;) - { - void **slot = htab->entries + index; - - if (*slot == EMPTY_ENTRY) - return slot; - else if (*slot == DELETED_ENTRY) - abort (); - - index += hash2; - if (index >= size) - index -= size; - } -} - -/* The following function changes size of memory allocated for the - entries and repeatedly inserts the table elements. The occupancy - of the table after the call will be about 50%. Naturally the hash - table must already exist. Remember also that the place of the - table entries is changed. If memory allocation failures are allowed, - this function will return zero, indicating that the table could not be - expanded. If all goes well, it will return a non-zero value. */ - -static int -htab_expand (htab) - htab_t htab; -{ - void **oentries; - void **olimit; - void **p; - - oentries = htab->entries; - olimit = oentries + htab->size; - - htab->size = higher_prime_number (htab->size * 2); - - if (htab->return_allocation_failure) - { - void **nentries = (void **) calloc (htab->size, sizeof (void **)); - if (nentries == NULL) - return 0; - htab->entries = nentries; - } - - htab->n_elements -= htab->n_deleted; - htab->n_deleted = 0; - - p = oentries; - do - { - void * x = *p; - - if (x != EMPTY_ENTRY && x != DELETED_ENTRY) - { - void **q = find_empty_slot_for_expand (htab, (*htab->hash_f) (x)); - - *q = x; - } - - p++; - } - while (p < olimit); - - free (oentries); - return 1; -} - -/* This function searches for a hash table entry equal to the given - element. It cannot be used to insert or delete an element. */ - -void * -htab_find_with_hash (htab, element, hash) - htab_t htab; - const void * element; - hashval_t hash; -{ - unsigned int index; - hashval_t hash2; - size_t size; - void * entry; - - htab->searches++; - size = htab->size; - index = hash % size; - - entry = htab->entries[index]; - if (entry == EMPTY_ENTRY - || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) - return entry; - - hash2 = 1 + hash % (size - 2); - - for (;;) - { - htab->collisions++; - index += hash2; - if (index >= size) - index -= size; - - entry = htab->entries[index]; - if (entry == EMPTY_ENTRY - || (entry != DELETED_ENTRY && (*htab->eq_f) (entry, element))) - return entry; - } -} - -/* Like htab_find_slot_with_hash, but compute the hash value from the - element. */ - -void * -htab_find (htab, element) - htab_t htab; - const void * element; -{ - return htab_find_with_hash (htab, element, (*htab->hash_f) (element)); -} - -/* This function searches for a hash table slot containing an entry - equal to the given element. To delete an entry, call this with - INSERT = 0, then call htab_clear_slot on the slot returned (possibly - after doing some checks). To insert an entry, call this with - INSERT = 1, then write the value you want into the returned slot. - When inserting an entry, NULL may be returned if memory allocation - fails. */ - -void ** -htab_find_slot_with_hash (htab, element, hash, insert) - htab_t htab; - const void * element; - hashval_t hash; - enum insert_option insert; -{ - void **first_deleted_slot; - unsigned int index; - hashval_t hash2; - size_t size; - - if (insert == INSERT && htab->size * 3 <= htab->n_elements * 4 - && htab_expand (htab) == 0) - return NULL; - - size = htab->size; - hash2 = 1 + hash % (size - 2); - index = hash % size; - - htab->searches++; - first_deleted_slot = NULL; - - for (;;) - { - void * entry = htab->entries[index]; - if (entry == EMPTY_ENTRY) - { - if (insert == NO_INSERT) - return NULL; - - htab->n_elements++; - - if (first_deleted_slot) - { - *first_deleted_slot = EMPTY_ENTRY; - return first_deleted_slot; - } - - return &htab->entries[index]; - } - - if (entry == DELETED_ENTRY) - { - if (!first_deleted_slot) - first_deleted_slot = &htab->entries[index]; - } - else if ((*htab->eq_f) (entry, element)) - return &htab->entries[index]; - - htab->collisions++; - index += hash2; - if (index >= size) - index -= size; - } -} - -/* Like htab_find_slot_with_hash, but compute the hash value from the - element. */ - -void ** -htab_find_slot (htab, element, insert) - htab_t htab; - const void * element; - enum insert_option insert; -{ - return htab_find_slot_with_hash (htab, element, (*htab->hash_f) (element), - insert); -} - -/* This function deletes an element with the given value from hash - table. If there is no matching element in the hash table, this - function does nothing. */ - -void -htab_remove_elt (htab, element) - htab_t htab; - void * element; -{ - void **slot; - - slot = htab_find_slot (htab, element, NO_INSERT); - if (*slot == EMPTY_ENTRY) - return; - - if (htab->del_f) - (*htab->del_f) (*slot); - - *slot = DELETED_ENTRY; - htab->n_deleted++; -} - -/* This function clears a specified slot in a hash table. It is - useful when you've already done the lookup and don't want to do it - again. */ - -void -htab_clear_slot (htab, slot) - htab_t htab; - void **slot; -{ - if (slot < htab->entries || slot >= htab->entries + htab->size - || *slot == EMPTY_ENTRY || *slot == DELETED_ENTRY) - abort (); - - if (htab->del_f) - (*htab->del_f) (*slot); - - *slot = DELETED_ENTRY; - htab->n_deleted++; -} - -/* This function scans over the entire hash table calling - CALLBACK for each live entry. If CALLBACK returns false, - the iteration stops. INFO is passed as CALLBACK's second - argument. */ - -void -htab_traverse (htab, callback, info) - htab_t htab; - htab_trav callback; - void * info; -{ - void **slot = htab->entries; - void **limit = slot + htab->size; - - do - { - void * x = *slot; - - if (x != EMPTY_ENTRY && x != DELETED_ENTRY) - if (!(*callback) (slot, info)) - break; - } - while (++slot < limit); -} - -/* Return the current size of given hash table. */ - -size_t -htab_size (htab) - htab_t htab; -{ - return htab->size; -} - -/* Return the current number of elements in given hash table. */ - -size_t -htab_elements (htab) - htab_t htab; -{ - return htab->n_elements - htab->n_deleted; -} - -/* Return the fraction of fixed collisions during all work with given - hash table. */ - -double -htab_collisions (htab) - htab_t htab; -{ - if (htab->searches == 0) - return 0.0; - - return (double) htab->collisions / (double) htab->searches; -} diff --git a/tools/hashtab.h b/tools/hashtab.h deleted file mode 100644 index 9ed18aeaab..0000000000 --- a/tools/hashtab.h +++ /dev/null @@ -1,143 +0,0 @@ -/* An expandable hash tables datatype. - Copyright (C) 1999, 2000 Free Software Foundation, Inc. - Contributed by Vladimir Makarov (vmakarov@cygnus.com). - -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 -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program 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 this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ - -/* This package implements basic hash table functionality. It is possible - to search for an entry, create an entry and destroy an entry. - - Elements in the table are generic pointers. - - The size of the table is not fixed; if the occupancy of the table - grows too high the hash table will be expanded. - - The abstract data implementation is based on generalized Algorithm D - from Knuth's book "The art of computer programming". Hash table is - expanded by creation of new hash table and transferring elements from - the old table to the new table. */ - -#ifndef __HASHTAB_H__ -#define __HASHTAB_H__ - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/* The type for a hash code. */ -typedef unsigned int hashval_t; - -/* Callback function pointer types. */ - -/* Calculate hash of a table entry. */ -typedef hashval_t (*htab_hash) (const void *); - -/* Compare a table entry with a possible entry. The entry already in - the table always comes first, so the second element can be of a - different type (but in this case htab_find and htab_find_slot - cannot be used; instead the variants that accept a hash value - must be used). */ -typedef int (*htab_eq) (const void *, const void *); - -/* Cleanup function called whenever a live element is removed from - the hash table. */ -typedef void (*htab_del) (void *); - -/* Function called by htab_traverse for each live element. The first - arg is the slot of the element (which can be passed to htab_clear_slot - if desired), the second arg is the auxiliary pointer handed to - htab_traverse. Return 1 to continue scan, 0 to stop. */ -typedef int (*htab_trav) (void **, void *); - -/* Hash tables are of the following type. The structure - (implementation) of this type is not needed for using the hash - tables. All work with hash table should be executed only through - functions mentioned below. */ - -struct htab -{ - /* Pointer to hash function. */ - htab_hash hash_f; - - /* Pointer to comparison function. */ - htab_eq eq_f; - - /* Pointer to cleanup function. */ - htab_del del_f; - - /* Table itself. */ - void **entries; - - /* Current size (in entries) of the hash table */ - size_t size; - - /* Current number of elements including also deleted elements */ - size_t n_elements; - - /* Current number of deleted elements in the table */ - size_t n_deleted; - - /* The following member is used for debugging. Its value is number - of all calls of `htab_find_slot' for the hash table. */ - unsigned int searches; - - /* The following member is used for debugging. Its value is number - of collisions fixed for time of work with the hash table. */ - unsigned int collisions; - - /* This is non-zero if we are allowed to return NULL for function calls - that allocate memory. */ - int return_allocation_failure; -}; - -typedef struct htab *htab_t; - -/* An enum saying whether we insert into the hash table or not. */ -enum insert_option {NO_INSERT, INSERT}; - -/* The prototypes of the package functions. */ - -/* This function is like htab_create, but may return NULL if memory - allocation fails, and also signals that htab_find_slot_with_hash and - htab_find_slot are allowed to return NULL when inserting. */ -extern htab_t htab_try_create (size_t, htab_hash, htab_eq, htab_del); -extern void htab_delete (htab_t); -extern void htab_empty (htab_t); - -extern void *htab_find (htab_t, const void *); -extern void **htab_find_slot (htab_t, const void *, enum insert_option); -extern void *htab_find_with_hash (htab_t, const void *, hashval_t); -extern void **htab_find_slot_with_hash (htab_t, const void *, hashval_t, - enum insert_option); -extern void htab_clear_slot (htab_t, void **); -extern void htab_remove_elt (htab_t, void *); - -extern void htab_traverse (htab_t, htab_trav, void *); - -extern size_t htab_size (htab_t); -extern size_t htab_elements (htab_t); -extern double htab_collisions (htab_t); - -/* A hash function for pointers. */ -extern htab_hash htab_hash_pointer; - -/* An equality function for pointers. */ -extern htab_eq htab_eq_pointer; - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* __HASHTAB_H */ diff --git a/tools/sepdebugcrcfix.c b/tools/sepdebugcrcfix.c deleted file mode 100644 index fba460014f..0000000000 --- a/tools/sepdebugcrcfix.c +++ /dev/null @@ -1,370 +0,0 @@ -/* Copyright (C) 2013 Free Software Foundation, Inc. - - 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 - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program 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 this program. If not, see . */ - -/* Version 2013-06-24. */ - -#define _GNU_SOURCE - -#include "system.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef _ -#define _(x) x -#endif -#define static_assert(expr) \ - extern int never_defined_just_used_for_checking[(expr) ? 1 : -1] -#ifndef min -# define min(a, b) ((a) < (b) ? (a) : (b)) -#endif - -static_assert (sizeof (unsigned long) >= sizeof (uint32_t)); - -typedef int bool; -static const bool false = 0, true = 1; - -/* This is bfd_calc_gnu_debuglink_crc32 from bfd/opncls.c. */ -static unsigned long - calc_gnu_debuglink_crc32 (unsigned long crc, - const unsigned char *buf, - size_t len) -{ - static const unsigned long crc32_table[256] = - { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, - 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, - 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, - 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, - 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, - 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, - 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, - 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, - 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, - 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, - 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, - 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, - 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, - 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, - 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, - 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, - 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, - 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, - 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, - 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, - 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, - 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, - 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, - 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, - 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, - 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, - 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, - 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, - 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, - 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, - 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, - 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, - 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, - 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, - 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, - 0x2d02ef8d - }; - const unsigned char *end; - - crc = ~crc & 0xffffffff; - for (end = buf + len; buf < end; ++ buf) - crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8); - return ~crc & 0xffffffff; -} - -static size_t updated_count, matched_count, failed_count; - -static const char *usr_lib_debug; - -static bool -crc32 (const char *fname, const char *base_fname, uint32_t *crcp) -{ - char *reldir = strdup (base_fname); - if (reldir == NULL) - error (1, 0, _("out of memory")); - char *s = reldir + strlen (reldir); - while (s > reldir && s[-1] != '/') - *--s = '\0'; - char *debugname; - if (asprintf (&debugname, "%s/%s/%s", usr_lib_debug, reldir, fname) <= 0) - error (1, 0, _("out of memory")); - free (reldir); - int fd = open (debugname, O_RDONLY); - if (fd == -1) - { - error (0, errno, _("cannot open \"%s\""), debugname); - return false; - } - off64_t size = lseek64 (fd, 0, SEEK_END); - if (size == -1) - { - error (0, errno, _("cannot get size of \"%s\""), debugname); - return false; - } - off_t offset = 0; - uint32_t crc = 0; - void *buf = NULL; - while (offset < size) - { - const size_t maplen = min (0x10000, size - offset); - void *map = NULL; - if (buf == NULL) - { - map = mmap (NULL, maplen, PROT_READ, MAP_PRIVATE -#ifdef MAP_POPULATE - | MAP_POPULATE -#endif - , - fd, offset); - if (map == MAP_FAILED) - { - error (0, errno, _("cannot map 0x%llx bytes at offset 0x%llx " - "of file \"%s\""), - (unsigned long long) maplen, (unsigned long long) offset, - debugname); - map = NULL; - } - } - if (map == NULL) - { - if (buf == NULL) - { - buf = malloc (maplen); - if (buf == NULL) - error (1, 0, _("out of memory")); - } - ssize_t got = pread (fd, buf, maplen, offset); - if (got != maplen) - { - error (0, errno, _("cannot read 0x%llx bytes at offset 0x%llx " - "of file \"%s\""), - (unsigned long long) maplen, (unsigned long long) offset, - debugname); - free (buf); - free (debugname); - return false; - } - } - crc = calc_gnu_debuglink_crc32 (crc, map ?: buf, maplen); - if (map && munmap (map, maplen) != 0) - error (1, errno, _("cannot unmap 0x%llx bytes at offset 0x%llx " - "of file \"%s\""), - (unsigned long long) maplen, (unsigned long long) offset, - debugname); - offset += maplen; - } - free (buf); - if (close (fd) != 0) - { - error (0, errno, _("cannot close \"%s\""), debugname); - free (debugname); - return false; - } - free (debugname); - *crcp = crc; - return true; -} - -static bool -process (Elf *elf, int fd, const char *fname) -{ - GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); - if (ehdr == NULL) - { - error (0, 0, _("cannot get ELF header of \"%s\""), fname); - return false; - } - if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB - && ehdr->e_ident[EI_DATA] != ELFDATA2MSB) - { - error (0, 0, _("invalid ELF endianity of \"%s\""), fname); - return false; - } - Elf_Scn *scn = NULL; - const char scnname[] = ".gnu_debuglink"; - while ((scn = elf_nextscn (elf, scn)) != NULL) - { - GElf_Shdr shdr_mem, *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr == NULL) - { - error (0, 0, _("cannot get section # %zu in \"%s\""), - elf_ndxscn (scn), fname); - continue; - } - const char *sname = elf_strptr (elf, ehdr->e_shstrndx, shdr->sh_name); - if (sname == NULL) - { - error (0, 0, _("cannot get name of section # %zu in \"%s\""), - elf_ndxscn (scn), fname); - continue; - } - if (strcmp (sname, scnname) != 0) - continue; - Elf_Data *data = elf_getdata (scn, NULL); - if (data == NULL) - { - error (0, 0, _("cannot get data of section \"%s\" # %zu in \"%s\""), - scnname, elf_ndxscn (scn), fname); - continue; - } - if ((data->d_size & 3) != 0) - { - error (0, 0, _("invalid size of section \"%s\" # %zu in \"%s\""), - scnname, elf_ndxscn (scn), fname); - continue; - } - const uint8_t *zerop = memchr (data->d_buf, '\0', data->d_size); - const uint8_t *crcp = (zerop == NULL - ? NULL - : (const uint8_t *) ((uintptr_t) (zerop + 1 + 3) - & -4)); - if (crcp + 4 != (uint8_t *) data->d_buf + data->d_size) - { - error (0, 0, _("invalid format of section \"%s\" # %zu in \"%s\""), - scnname, elf_ndxscn (scn), fname); - continue; - } - uint32_t had_crc_targetendian = *(const uint32_t *) crcp; - uint32_t had_crc = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB - ? le32toh (had_crc_targetendian) - : be32toh (had_crc_targetendian)); - uint32_t crc; - if (! crc32 (data->d_buf, fname, &crc)) - return false; - if (crc == had_crc) - { - matched_count++; - return true; - } - updated_count++; - off64_t seekto = (shdr->sh_offset + data->d_off - + (crcp - (const uint8_t *) data->d_buf)); - uint32_t crc_targetendian = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB - ? htole32 (crc) : htobe32 (crc)); - ssize_t wrote = pwrite (fd, &crc_targetendian, sizeof (crc_targetendian), - seekto); - if (wrote != sizeof (crc_targetendian)) - { - error (0, 0, _("cannot write new CRC to 0x%llx " - "inside section \"%s\" # %zu in \"%s\""), - (unsigned long long) seekto, scnname, elf_ndxscn (scn), fname); - return false; - } - return true; - } - error (0, 0, _("cannot find section \"%s\" in \"%s\""), scnname, fname); - return false; -} - -int -main (int argc, char **argv) -{ - if (argc < 2) - error (1, 0, _("usr/lib/debug [...]")); - usr_lib_debug = argv[1]; - if (elf_version (EV_CURRENT) == EV_NONE) - error (1, 0, _("error initializing libelf: %s"), elf_errmsg (-1)); - for (int argi = 2; argi < argc; argi++) - { - const char *fname = argv[argi]; - struct stat stat_buf; - if (stat(fname, &stat_buf) < 0) - { - error (0, errno, _("cannot stat input \"%s\""), fname); - failed_count++; - continue; - } - - /* Make sure we can read and write */ - chmod (fname, stat_buf.st_mode | S_IRUSR | S_IWUSR); - - bool failed = false; - int fd = open64 (fname, O_RDWR); - if (fd == -1) - { - error (0, errno, _("cannot open \"%s\""), fname); - failed = true; - } - else - { - Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); - if (elf == NULL) - { - error (0, 0, _("cannot open \"%s\" as ELF: %s"), fname, - elf_errmsg (-1)); - failed = true; - } - else - { - if (! process (elf, fd, fname)) - failed = true; - if (elf_end (elf) != 0) - { - error (0, 0, _("cannot close \"%s\" as ELF: %s"), fname, - elf_errmsg (-1)); - failed = true; - } - } - if (close (fd) != 0) - { - error (0, errno, _("cannot close \"%s\""), fname); - failed = true; - } - } - - /* Restore old access rights. Including any suid bits reset. */ - chmod (fname, stat_buf.st_mode); - - if (failed) - failed_count++; - } - printf ("%s: Updated %zu CRC32s, %zu CRC32s did match.\n", argv[0], - updated_count, matched_count); - if (failed_count) - printf ("%s: Failed for %zu files.\n", argv[0], failed_count); - return failed_count == 0 ? EXIT_SUCCESS : EXIT_FAILURE; -}