version 1.10, 2019/10/05 01:01:23 |
version 1.11, 2022/01/03 03:40:48 |
|
|
#!/bin/ksh |
#!/bin/ksh |
# $OpenBSD$ |
# $OpenBSD$ |
# |
# |
# Copyright (c) 2016,2019 Philip Guenther <guenther@openbsd.org> |
# Copyright (c) 2016,2019,2022 Philip Guenther <guenther@openbsd.org> |
# |
# |
# Permission to use, copy, modify, and distribute this software for any |
# Permission to use, copy, modify, and distribute this software for any |
# purpose with or without fee is hereby granted, provided that the above |
# purpose with or without fee is hereby granted, provided that the above |
|
|
# versions of a shared library |
# versions of a shared library |
# |
# |
# SYNOPSIS |
# SYNOPSIS |
# check_sym [-ch] [old [new]] |
# check_sym [-chkv] [old [new]] |
# |
# |
# DESCRIPTION |
# DESCRIPTION |
# Library developers need to be aware when they have changed the |
# Library developers need to be aware when they have changed the |
|
|
# *current* directory, or the highest version of that library in |
# *current* directory, or the highest version of that library in |
# /usr/lib if it wasn't present in the current directory. |
# /usr/lib if it wasn't present in the current directory. |
# |
# |
# check_sym uses fixed names in /tmp for its intermediate files, |
# By default, check_sym places all its intermediate files in a |
# as they contain useful details for those trying to understand |
# temporary directory and removed it on exit. They contain useful |
# what changed. If any of them cannot be created by the user, |
# details for understanding what changed, so if the -k option is used |
# the command will fail. The files can be cleaned up using |
# they will instead be placed in /tmp/ and left behind. If any of |
# the -c option. |
# them cannot be created by the user, the command will fail. The |
|
# files left behind by the -k option can be cleaned up by invoking |
|
# check_syms with the -c option. |
# |
# |
|
# The -v option enables verbose output. |
# |
# |
# The *basic* rules of thumb for library versions are: if you |
# The *basic* rules of thumb for library versions are: if you |
# * stop exporting a symbol, or |
# * stop exporting a symbol, or |
|
|
|
|
usage() |
usage() |
{ |
{ |
usage="usage: check_sym [-chv] [old [new]]" |
usage="usage: check_sym [-chkv] [old [new]]" |
if [[ $# -gt 0 ]] |
if [[ $# -gt 0 ]] |
then |
then |
echo "check_sym: $@ |
echo "check_sym: $@ |
|
|
exit 0 |
exit 0 |
} |
} |
|
|
file_list=/tmp/{D{,S,W,O},J,S,U,d,j,r,s}{1,2} |
unset odir |
|
file_list={D{,S,W,O},J,S,U,d,j,r,s}{1,2} |
|
|
|
keep_temp=false |
verbose=false |
verbose=false |
while getopts :chv opt "$@" |
while getopts :chkv opt "$@" |
do |
do |
case $opt in |
case $opt in |
h) usage;; |
c) rm -f /tmp/$file_list |
c) rm -f $file_list |
|
exit 0;; |
exit 0;; |
|
h) usage;; |
|
k) keep_temp=true;; |
v) verbose=true;; |
v) verbose=true;; |
\?) usage "unknown option -- $OPTARG";; |
\?) usage "unknown option -- $OPTARG";; |
esac |
esac |
|
|
sed 's/\[<other>: [0-9a-f]*\]//' |
sed 's/\[<other>: [0-9a-f]*\]//' |
} |
} |
|
|
# precreate all the files we'll use, but with noclobber set to avoid |
if $keep_temp |
# symlink attacks |
then |
|
# precreate all the files we'll use, but with noclobber set to avoid |
|
# symlink attacks |
|
odir=/tmp |
|
files= |
|
trap 'ret=$?; rm -f $files; exit $ret' 1 2 15 ERR |
|
else |
|
trap 'ret=$?; rm -rf "$odir"; exit $ret' 0 1 2 15 ERR |
|
odir=$(mktemp -dt check_sym.XXXXXXXXXX) |
|
fi |
set -C |
set -C |
files= |
for i in $odir/$file_list |
trap 'rm -f $files' 1 2 15 ERR |
|
for i in $file_list |
|
do |
do |
rm -f $i |
rm -f $i |
3>$i |
3>$i |
|
|
done |
done |
set +C |
set +C |
|
|
readelf -rW $old > /tmp/r1 |
readelf -rW $old > $odir/r1 |
readelf -rW $new > /tmp/r2 |
readelf -rW $new > $odir/r2 |
|
|
readelf -sW $old | filt_symtab > /tmp/s1 |
readelf -sW $old | filt_symtab > $odir/s1 |
readelf -sW $new | filt_symtab > /tmp/s2 |
readelf -sW $new | filt_symtab > $odir/s2 |
|
|
|
|
case $(readelf -h $new | grep '^ *Machine:') in |
case $(readelf -h $new | grep '^ *Machine:') in |
|
|
gotsym2=$(readelf -d $new | awk '$2 ~ /MIPS_GOTSYM/{print $3}') |
gotsym2=$(readelf -d $new | awk '$2 ~ /MIPS_GOTSYM/{print $3}') |
fi |
fi |
|
|
|
# Now that we're done accessing $old and $new (which could be |
|
# relative paths), chdir into our work directory, whatever it is |
|
cd $odir |
|
|
jump_slots() { |
jump_slots() { |
case $cpu in |
case $cpu in |
hppa) awk '/IPLT/ && $5 != ""{print $5}' /tmp/r$1 |
hppa) awk '/IPLT/ && $5 != ""{print $5}' r$1 |
;; |
;; |
mips64) # the $((gotsym$1)) converts hex to decimal |
mips64) # the $((gotsym$1)) converts hex to decimal |
awk -v g=$((gotsym$1)) \ |
awk -v g=$((gotsym$1)) \ |
'/^Symbol table ..symtab/{exit} |
'/^Symbol table ..symtab/{exit} |
$6 == "PROTECTED" { next } |
$6 == "PROTECTED" { next } |
$1+0 >= g && $4 == "FUNC" {print $8}' /tmp/s$1 |
$1+0 >= g && $4 == "FUNC" {print $8}' s$1 |
;; |
;; |
*) awk '/JU*MP_SL/ && $5 != ""{print $5}' /tmp/r$1 |
*) awk '/JU*MP_SL/ && $5 != ""{print $5}' r$1 |
;; |
;; |
esac | sort -o /tmp/j$1 |
esac | sort -o j$1 |
} |
} |
|
|
dynamic_sym() { |
dynamic_sym() { |
awk -v s=$1 '/^Symbol table ..symtab/{exit} |
awk -v s=$1 '/^Symbol table ..symtab/{exit} |
! /^ *[1-9]/ {next} |
! /^ *[1-9]/ {next} |
$7 == "UND" {print $8 | ("sort -o /tmp/U" s); next } |
$7 == "UND" {print $8 | ("sort -o U" s); next } |
$5 == "GLOBAL" {print $8 | ("sort -o /tmp/DS" s) } |
$5 == "GLOBAL" {print $8 | ("sort -o DS" s) } |
$5 == "WEAK" {print $8 | ("sort -o /tmp/DW" s) } |
$5 == "WEAK" {print $8 | ("sort -o DW" s) } |
$5 != "LOCAL" {print $8 | ("sort -o /tmp/D" s) } |
$5 != "LOCAL" {print $8 | ("sort -o D" s) } |
$5 != "LOCAL" && $4 == "OBJECT" { |
$5 != "LOCAL" && $4 == "OBJECT" { |
print $8, $3 | ("sort -o /tmp/DO" s) } |
print $8, $3 | ("sort -o DO" s) } |
{print $4, $5, $6, $8}' /tmp/s$1 | sort -o /tmp/d$1 |
{print $4, $5, $6, $8}' s$1 | sort -o d$1 |
} |
} |
|
|
static_sym() { |
static_sym() { |
awk '/^Symbol table ..symtab/{s=1} |
awk '/^Symbol table ..symtab/{s=1} |
/LOCAL/{next} |
/LOCAL/{next} |
s&&/^ *[1-9]/{print $4, $5, $6, $8}' /tmp/s$1 | sort -o /tmp/S$1 |
s&&/^ *[1-9]/{print $4, $5, $6, $8}' s$1 | sort -o S$1 |
} |
} |
|
|
data_sym_changes() { |
data_sym_changes() { |
|
|
jump_slots $i |
jump_slots $i |
dynamic_sym $i |
dynamic_sym $i |
static_sym $i |
static_sym $i |
comm -23 /tmp/j$i /tmp/U$i >/tmp/J$i |
comm -23 j$i U$i >J$i |
done |
done |
|
|
echo "$old --> $new" |
echo "$old --> $new" |
if cmp -s /tmp/d[12] && cmp -s /tmp/DO[12] |
if cmp -s d[12] && cmp -s DO[12] |
then |
then |
printf "No dynamic export changes\n" |
printf "No dynamic export changes\n" |
else |
else |
printf "Dynamic export changes:\n" |
printf "Dynamic export changes:\n" |
output_if_not_empty "added:" comm -13 /tmp/D[12] |
output_if_not_empty "added:" comm -13 D[12] |
output_if_not_empty "removed:" comm -23 /tmp/D[12] |
output_if_not_empty "removed:" comm -23 D[12] |
output_if_not_empty "weakened:" comm -12 /tmp/DS1 /tmp/DW2 |
output_if_not_empty "weakened:" comm -12 DS1 DW2 |
output_if_not_empty "strengthened:" comm -12 /tmp/DW1 /tmp/DS2 |
output_if_not_empty "strengthened:" comm -12 DW1 DS2 |
output_if_not_empty "data object sizes changes:" \ |
output_if_not_empty "data object sizes changes:" \ |
data_sym_changes /tmp/DO[12] |
data_sym_changes DO[12] |
fi |
fi |
if ! cmp -s /tmp/U[12] |
if ! cmp -s U[12] |
then |
then |
printf "External reference changes:\n" |
printf "External reference changes:\n" |
output_if_not_empty "added:" comm -13 /tmp/U[12] |
output_if_not_empty "added:" comm -13 U[12] |
output_if_not_empty "removed:" comm -23 /tmp/U[12] |
output_if_not_empty "removed:" comm -23 U[12] |
fi |
fi |
|
|
if $verbose; then |
if $verbose; then |
printf "\nReloc counts:\nbefore:\n" |
printf "\nReloc counts:\nbefore:\n" |
grep ^R /tmp/r1 |
grep ^R r1 |
printf "\nafter:\n" |
printf "\nafter:\n" |
grep ^R /tmp/r2 |
grep ^R r2 |
fi |
fi |
|
|
output_if_not_empty "PLT added:" comm -13 /tmp/J1 /tmp/J2 |
output_if_not_empty "PLT added:" comm -13 J1 J2 |
output_if_not_empty "PLT removed:" comm -23 /tmp/J1 /tmp/J2 |
output_if_not_empty "PLT removed:" comm -23 J1 J2 |