aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnthony G. Basile <blueness@gentoo.org>2012-12-28 20:55:35 -0500
committerAnthony G. Basile <blueness@gentoo.org>2012-12-28 20:55:35 -0500
commitaee408c58d71f407fc92761aafee788db83603ff (patch)
tree55dab324da37f01537e4a8aae9b9da11ad3ebc0b
parentChangeLog: updated (diff)
downloadelfix-aee408c58d71f407fc92761aafee788db83603ff.tar.gz
elfix-aee408c58d71f407fc92761aafee788db83603ff.tar.bz2
elfix-aee408c58d71f407fc92761aafee788db83603ff.zip
scripts/revdep-pax: update to alt-revdep-pax + link_map.py
-rw-r--r--TODO4
-rwxr-xr-xmisc/alt-revdep-pax472
-rwxr-xr-xmisc/revdep-pax-ng500
-rw-r--r--scripts/paxmodule.c2
-rwxr-xr-xscripts/pypaxctl2
-rwxr-xr-xscripts/revdep-pax1084
-rwxr-xr-xscripts/setup.py2
7 files changed, 1124 insertions, 942 deletions
diff --git a/TODO b/TODO
index 007e7db..3a16c66 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,4 @@
- * src/paxctl-ng.c: add verbose error reporting for xattr (like for set/get phdr)
* src/paxctl-ng.c, scripts/paxmodule.c: reduce the shared code to a convenience library
- * scripts/revdep-pax: don't use /var/db/pkg but some $PATH to find all exes.
+ * misc/revdep-pax-np: (non-gentoo) use $PATH to find all exe's and ldd for libraries
+
diff --git a/misc/alt-revdep-pax b/misc/alt-revdep-pax
deleted file mode 100755
index dda9025..0000000
--- a/misc/alt-revdep-pax
+++ /dev/null
@@ -1,472 +0,0 @@
-#!/usr/bin/env python
-#
-# alt-revdep-pax: this file is part of the elfix package
-# Copyright (C) 2011 Anthony G. Basile
-#
-# 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 <http://www.gnu.org/licenses/>.
-#
-
-#
-# Note: This alternative way of doing revdep-pax only
-# works on Gentoo systems where NEEDED.ELF.2 all the
-# information we need generated by scanelf during emerge.
-#
-# See /usr/lib/portage/bin/misc-functions.sh ~line 520
-# echo "${arch:3};${obj};${soname};${rpath};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2
-#
-
-import getopt
-import os
-import sys
-import pax
-
-from link_map import LinkMap
-
-def get_input(prompt):
- """ python2/3 compat input """
- if sys.hexversion > 0x03000000:
- return input(prompt)
- else:
- return raw_input(prompt)
-
-
-def print_problems(sonames_missing_library):
- sonames_missing_library = set(sonames_missing_library)
- print('\n**** SONAMES without any library files ****')
- for m in sonames_missing_library:
- print('\t%s' % m)
-
-
-def run_forward(verbose):
- (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
-
- sonames_missing_library = []
-
- for abi in object_linkings:
- for elf in object_linkings[abi]:
- try:
- (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
- sv = '%s :%s ( %s )' % (elf, abi, elf_str_flags)
- s = sv
- except pax.PaxError:
- sv = '%s :%s ( %s )' % (elf, abi, '****')
- s = sv
- continue
-
- count = 0
- for soname in object_linkings[abi][elf]:
- try:
- library = soname2library[(soname, abi)]
- try:
- (library_str_flags, library_bin_flags) = pax.getflags(library)
- except pax.PaxError:
- library_str_flags = '****'
- sv = '%s\n\t%s\t%s ( %s )' % (sv, soname, library, library_str_flags)
- if elf_str_flags != library_str_flags:
- s = '%s\n\t%s\t%s ( %s )' % (s, soname, library, library_str_flags)
- count = count + 1
- except KeyError:
- sonames_missing_library.append(soname)
-
- if verbose:
- print('%s\n' % sv)
- if count == 0:
- print('\tNo mismatches\n\n')
- else:
- print('\tMismatches\n\n')
- else:
- if count != 0:
- print('%s\n\n' % s)
-
- if verbose:
- print_problems(sonames_missing_library)
-
-
-def run_reverse(verbose, executable_only):
- (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
-
- shell_path = path = os.getenv('PATH').split(':')
-
- sonames_missing_library = []
-
- for abi in object_reverse_linkings:
- for soname in object_reverse_linkings[abi]:
- try:
- library = soname2library[(soname, abi)]
- try:
- (library_str_flags, library_bin_flags) = pax.getflags(library)
- except pax.PaxError:
- library_str_flags = '****'
- sv = '%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags)
- s = sv
- except KeyError:
- sonames_missing_library.append(soname)
-
- count = 0
- for elf in object_reverse_linkings[abi][soname]:
- try:
- (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
- except pax.PaxError:
- elf_str_flags = '****'
- if executable_only:
- if os.path.dirname(elf) in shell_path:
- sv = '%s\n\t%s ( %s )' % (sv, elf, elf_str_flags)
- if library_str_flags != elf_str_flags:
- s = '%s\n\t%s ( %s )' % (s, elf, elf_str_flags)
- count = count + 1
- else:
- sv = '%s\n\t%s ( %s )' % (sv, elf, elf_str_flags)
- if library_str_flags != elf_str_flags:
- s = '%s\n\t%s ( %s )' % (s, elf, elf_str_flags)
- count = count + 1
-
- if verbose:
- print('%s\n' % sv)
- if count == 0:
- print('\tNo mismatches\n\n')
- else:
- print('\tMismatches\n\n')
- else:
- if count != 0:
- print('%s\n\n' % s)
-
- if verbose:
- print_problems(sonames_missing_library)
-
-
-def migrate_flags(importer, exporter_str_flags, exporter_bin_flags):
- # We implement the following logic for setting the pax flags
- # on the target elf object, the IMPORTER, given that the flags
- # from the elf object we want it to match to, the EXPORTER.
- #
- # EXPORTER IMPORTER RESULT
- # On On On
- # On Off On + Warn
- # On - On
- # Off On On + Warn
- # Off Off Off
- # Off - Off
- # - On On
- # - Off Off
- # - - -
-
- #See /usr/include/elf.h for these values
- pf_flags = {
- 'P':1<<4, 'p':1<<5,
- 'S':1<<6, 's':1<<7,
- 'M':1<<8, 'm':1<<9,
- 'X':1<<10, 'x':1<<11,
- 'E':1<<12, 'e':1<<13,
- 'R':1<<14, 'r':1<<15
- }
-
- try:
- (importer_str_flags, importer_bin_flags) = pax.getflags(importer)
- except pax.PaxError:
- # The importer has no flags, so just set them
- pax.setbinflags(importer, exporter_bin_flags)
- return
-
- # Start with the exporter's flags
- result_bin_flags = exporter_bin_flags
-
- for i in range(len(importer_str_flags)):
-
- # The exporter's flag contradicts the importer's flag, so do nothing
- if (exporter_str_flags[i].isupper() and importer_str_flags[i].islower()) or \
- (exporter_str_flags[i].islower() and importer_str_flags[i].isupper()):
-
- # Revert the exporter's flag, use the importer's flag and warn
- result_bin_flags = result_bin_flags ^ pf_flags[exporter_str_flags[i]]
- result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
- print('\t\tWarning: %s has %s, refusing to set to %s' % (
- importer, importer_str_flags[i], exporter_str_flags[i] )),
-
- # The exporter's flags is off, so use the importer's flag
- if (exporter_str_flags[i] == '-' and importer_str_flags[i] != '-'):
- result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
-
- pax.setbinflags(importer, result_bin_flags)
-
-
-def run_elf(elf, verbose, mark, allyes):
- if not os.path.exists(elf):
- print('%s\tNo such OBJECT' % elf)
- return
-
- try:
- (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
- print('%s (%s)\n' % (elf, elf_str_flags))
- except pax.PaxError:
- print('%s: No PAX flags found\n' % elf)
- return
-
- (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
-
- mismatched_libraries = []
-
- for abi in object_linkings:
- if not elf in object_linkings[abi]: # There may be no elf for that abi
- continue
- for soname in object_linkings[abi][elf]:
- try:
- library = soname2library[(soname,abi)]
- try:
- (library_str_flags, library_bin_flags) = pax.getflags(library)
- except pax.PaxError:
- library_str_flags = '****'
- if verbose:
- print('\t%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags))
- if elf_str_flags != library_str_flags:
- mismatched_libraries.append(library)
- if not verbose:
- print('\t%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags))
- except KeyError:
- print('%s :%s: file for soname not found' % (soname, abi))
-
- if len(mismatched_libraries) == 0:
- if not verbose:
- print('\tNo mismatches\n')
- else:
- print('')
- if mark:
- print('\tWill mark libraries with %s\n' % elf_str_flags)
- for library in mismatched_libraries:
- do_marking = False
- while True:
- if allyes:
- ans = 'y'
- else:
- ans = get_input('\tSet flags for %s :%s (y/n): ' % (library, abi))
- if ans == 'y':
- do_marking = True
- break
- elif ans == 'n':
- do_marking = False
- break
- else:
- print('\t\tPlease enter y or n')
-
- if do_marking:
- try:
- migrate_flags(library, elf_str_flags, elf_bin_flags)
- except pax.PaxError:
- print('\n\tCould not set PAX flags on %s, text maybe busy' % (library, abi))
-
- try:
- (library_str_flags, library_bin_flags) = pax.getflags(library)
- print('\n\t\t%s ( %s )\n' % (library, library_str_flags))
- except pax.PaxError:
- print('\n\t\t%s: Could not read PAX flags')
-
-
-def run_soname(name, verbose, use_soname, mark, allyes, executable_only):
- shell_path = path = os.getenv('PATH').split(':')
-
- (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
-
- if use_soname:
- soname = name
- abi_list = object_reverse_linkings.keys()
- for abi in abi_list:
- # There must be at least on abi with that soname
- if soname in object_reverse_linkings[abi]:
- break
- else:
- print('%s\tNo such SONAME' % soname)
- return
- else:
- try:
- (soname, abi) = library2soname[name]
- abi_list = [abi]
- except KeyError:
- print('%s\tNo such LIBRARY' % name)
- return
-
-
- mismatched_elfs = []
-
- for abi in abi_list:
- # An soname can belong to one or more abis
- if not soname in object_reverse_linkings[abi]:
- continue
-
- library = soname2library[(soname, abi)]
-
- try:
- (library_str_flags, library_bin_flags) = pax.getflags(library)
- print('%s\t%s :%s (%s)\n' % (soname, library, abi, library_str_flags))
- except pax.PaxError:
- print('%s :%s : No PAX flags found\n' % (library, abi))
- continue
-
- for elf in object_reverse_linkings[abi][soname]:
- try:
- (elf_str_flags, elf_bin_flags ) = pax.getflags(elf)
- except pax.PaxError:
- elf_str_flags = '****'
- if verbose:
- if executable_only:
- if os.path.dirname(elf) in shell_path:
- print('\t%s ( %s )' % (elf, elf_str_flags))
- else:
- print('\t%s ( %s )' % (elf, elf_str_flags))
- if library_str_flags != elf_str_flags:
- if executable_only:
- if os.path.dirname(elf) in shell_path:
- mismatched_elfs.append(elf)
- if not verbose:
- print('\t%s ( %s )' % (elf, elf_str_flags))
- else:
- mismatched_elfs.append(elf)
- if not verbose:
- print('\t%s ( %s )' % (elf, elf_str_flags))
-
- if len(mismatched_elfs) == 0:
- if not verbose:
- print('\tNo mismatches\n')
- else:
- print('')
- if mark:
- print('\tWill mark elf with %s\n' % library_str_flags)
- for elf in mismatched_elfs:
- if executable_only:
- if not os.path.dirname(elf) in shell_path:
- continue
- do_marking = False
- while True:
- if allyes:
- ans = 'y'
- else:
- ans = get_input('\tSet flags for %s (y/n): ' % elf)
- if ans == 'y':
- do_marking = True
- break
- elif ans == 'n':
- do_marking = False
- break
- else:
- print('\t\tPlease enter y or n')
- if do_marking:
- try:
- migrate_flags(elf, library_str_flags, library_bin_flags)
- except pax.PaxError:
- print('\n\tCould not set pax flags on %s, file is probably busy' % elf)
- print('\tShut down all processes that use it and try again')
- (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
- print('\n\t\t%s ( %s )\n' % (elf, elf_str_flags))
-
-
-def run_usage():
- print('Package Name : elfix')
- print('Bug Reports : http://bugs.gentoo.org/')
- print('Program Name : revdep-pax')
- print('Description : Get or set pax flags on an ELF object')
- print('')
- print('Usage : revdep-pax -f [-v] print out all forward mappings for all system ELF objects')
- print(' : revdep-pax -r [-ve] print out all reverse mappings for all system sonames')
- print(' : revdep-pax -b OBJECT [-myv] print all forward mappings only for OBJECT')
- print(' : revdep-pax -s SONAME [-myve] print all reverse mappings only for SONAME')
- print(' : revdep-pax -l LIBRARY [-myve] print all reverse mappings only for LIBRARY file')
- print(' : revdep-pax [-h] print out this help')
- print(' : -v verbose, otherwise just print mismatching objects')
- print(' : -e only print out executables in shell $PATH')
- print(' : -m don\'t just report, but mark the mismatching objects')
- print(' : -y assume "yes" to all prompts for marking (USE CAREFULLY!)')
- print('')
-
-
-def main():
- # Are we root?
- uid = os.getuid()
- if uid != 0:
- print('This program must be run as root')
- sys.exit(1)
-
- try:
- opts, args = getopt.getopt(sys.argv[1:], 'hfrb:s:l:vemy')
- except getopt.GetoptError as err:
- print(str(err)) # will print something like 'option -a not recognized'
- run_usage()
- sys.exit(1)
-
- if len(opts) == 0:
- run_usage()
- sys.exit(1)
-
- do_usage = False
- do_forward = False
- do_reverse = False
-
- elf = None
- soname = None
- library = None
-
- verbose = False
- executable_only = False
- mark = False
- allyes = False
-
- opt_count = 0
-
- for o, a in opts:
- if o == '-h':
- do_usage = True
- opt_count += 1
- elif o == '-f':
- do_forward = True
- opt_count += 1
- elif o == '-r':
- do_reverse = True
- opt_count += 1
- elif o == '-b':
- elf = a
- opt_count += 1
- elif o == '-s':
- soname = a
- opt_count += 1
- elif o == '-l':
- library = a
- opt_count += 1
- elif o == '-v':
- verbose = True
- elif o == '-e':
- executable_only = True
- elif o == '-m':
- mark = True
- elif o == '-y':
- allyes = True
- else:
- print('Option included in getopt but not handled here!')
- print('Please file a bug')
- sys.exit(1)
-
- # Only allow one of -h, -f -r -b -s
- if opt_count > 1 or do_usage:
- run_usage()
- elif do_forward:
- run_forward(verbose)
- elif do_reverse:
- run_reverse(verbose, executable_only)
- elif elf != None:
- run_elf(elf, verbose, mark, allyes)
- elif soname != None:
- run_soname(soname, verbose, True, mark, allyes, executable_only)
- elif library != None:
- library = os.path.realpath(library)
- run_soname(library, verbose, False, mark, allyes, executable_only)
-
-
-if __name__ == '__main__':
- main()
diff --git a/misc/revdep-pax-ng b/misc/revdep-pax-ng
new file mode 100755
index 0000000..7767563
--- /dev/null
+++ b/misc/revdep-pax-ng
@@ -0,0 +1,500 @@
+#!/usr/bin/env python
+#
+# revdep-pax-ng: this file is part of the elfix package
+# Copyright (C) 2011, 2012 Anthony G. Basile
+#
+# 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 <http://www.gnu.org/licenses/>.
+#
+
+
+import sys
+import getopt
+import os
+import subprocess
+import re
+import pax
+
+
+#python2/3 compat input
+def get_input(prompt):
+ if sys.hexversion > 0x03000000:
+ return input(prompt)
+ else:
+ return raw_input(prompt)
+
+
+def get_ldd_linkings(binary):
+ ldd_output = subprocess.Popen(['/usr/bin/ldd', binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ ldd_lines = ldd_output.stdout.read().decode().split('\n')
+
+ linkings = []
+ mappings = {}
+ for m in range(0,len(ldd_lines)):
+ if not re.search('=>', ldd_lines[m]):
+ continue
+ ldd_lines[m] = ldd_lines[m].strip()
+ mapp = re.split('=>', ldd_lines[m] )
+ soname = mapp[0].strip()
+ soname = os.path.basename(soname) # This is for ./libSDL-1.2.so.0
+ library = re.sub('\(.*$', '', mapp[1]).strip()
+ if library == '':
+ continue
+ library = os.path.realpath(library)
+ linkings.append(soname)
+ mappings[soname] = library
+
+ return ( linkings, mappings )
+
+
+def get_forward_linkings():
+ #TODO: I'm still not sure we want to use /var/db/pkg vs some path of binaries
+ var_db_pkg = '/var/db/pkg'
+
+ forward_linkings = {}
+ so2library_mappings = {}
+ for cat in os.listdir(var_db_pkg):
+ catdir = '%s/%s' % (var_db_pkg, cat)
+ for pkg in os.listdir(catdir):
+ pkgdir = '%s/%s' % (catdir, pkg)
+ need = '%s/%s' % (pkgdir, 'NEEDED')
+ try:
+ g = open(need, 'r')
+ needs = g.readlines()
+ for line in needs:
+ line = line.strip()
+ link = re.split('\s', line)
+ binary = link[0]
+ ( linkings, mappings ) = get_ldd_linkings(binary)
+ forward_linkings[binary] = linkings
+ so2library_mappings.update(mappings)
+ except IOError:
+ continue #File probably doesn't exist, which is okay
+
+ return ( forward_linkings, so2library_mappings )
+
+
+def invert_linkings( forward_linkings ):
+ reverse_linkings = {}
+
+ for binary in forward_linkings:
+ for library in forward_linkings[binary]:
+ reverse_linkings.setdefault(library,[]).append(binary)
+
+ return reverse_linkings
+
+
+def print_forward_linkings( forward_linkings, so2library_mappings, verbose ):
+ missing_binaries = []
+ missing_links = []
+ for binary in forward_linkings:
+
+ try:
+ ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
+ sv = '%s ( %s )\n' % ( binary, binary_str_flags )
+ s = sv
+ except pax.PaxError:
+ missing_binaries.append(binary)
+ continue
+
+ count = 0
+ for soname in forward_linkings[binary]:
+ try:
+ library = so2library_mappings[soname]
+ ( library_str_flags, library_bin_flags ) = pax.getflags(library)
+ sv = '%s\n\t%s\t%s ( %s )' % ( sv, soname, library, library_str_flags )
+ if binary_str_flags != library_str_flags:
+ s = '%s\n\t%s\t%s ( %s )' % ( s, soname, library, library_str_flags )
+ count = count + 1
+ except pax.PaxError:
+ missing_links.append(soname)
+
+ if verbose:
+ print('%s\n' % sv)
+ if count == 0:
+ print('\tNo mismatches\n\n')
+ else:
+ print('\tMismatches\n\n')
+ else:
+ if count != 0:
+ print('%s\n\n' % s)
+
+ missing_binaries = set(missing_binaries)
+ print('\n**** Missing binaries ****\n')
+ for m in missing_binaries:
+ print('\t%s\n' % m)
+
+ missing_links = set(missing_links)
+ print('\n**** Missing forward linkings ****\n')
+ for m in missing_links:
+ print('\t%s\n' % m)
+
+
+def print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only ):
+ shell_path = path = os.getenv('PATH').split(':')
+ missing_sonames = []
+ missing_links = []
+
+ for soname in reverse_linkings:
+ try:
+ library = so2library_mappings[soname]
+ ( library_str_flags, library_bin_flags ) = pax.getflags(library)
+ sv = '%s\t%s ( %s )\n' % ( soname, library, library_str_flags )
+ s = sv
+ except pax.PaxError:
+ missing_sonames.append(soname)
+ continue
+
+ count = 0
+ for binary in reverse_linkings[soname]:
+ try:
+ ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
+ if executable_only:
+ if os.path.dirname(binary) in shell_path:
+ sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags )
+ if library_str_flags != binary_str_flags:
+ s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags )
+ count = count + 1
+ else:
+ sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags )
+ if library_str_flags != binary_str_flags:
+ s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags )
+ count = count + 1
+ except pax.PaxError:
+ missing_links.append(binary)
+
+ if verbose:
+ print('%s\n' % sv)
+ if count == 0:
+ print('\tNo mismatches\n\n')
+ else:
+ print('\tMismatches\n\n')
+ else:
+ if count != 0:
+ print('%s\n\n' % s)
+
+ missing_sonames = set(missing_sonames)
+ print('\n**** Missing sonames ****\n')
+ for m in missing_sonames:
+ print('\t%s\n' % m)
+
+ missing_links = set(missing_links)
+ print('\n**** Missing reverse linkings ****\n')
+ for m in missing_links:
+ print('\t%s\n' % m)
+
+
+def run_forward(verbose):
+ ( forward_linkings, so2library_mappings ) = get_forward_linkings()
+ print_forward_linkings( forward_linkings, so2library_mappings, verbose)
+
+
+def run_reverse(verbose, executable_only):
+ ( forward_linkings, so2library_mappings ) = get_forward_linkings()
+ reverse_linkings = invert_linkings( forward_linkings )
+ print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only)
+
+
+def migrate_flags(importer, exporter_str_flags, exporter_bin_flags):
+ # We implement the following logic for setting the pax flags
+ # on the target elf object, the IMPORTER, given that the flags
+ # from the elf object we want it to match to, the EXPORTER.
+ #
+ # EXPORTER IMPORTER RESULT
+ # On On On
+ # On Off On + Warn
+ # On - On
+ # Off On On + Warn
+ # Off Off Off
+ # Off - Off
+ # - On On
+ # - Off Off
+ # - - -
+
+ #See /usr/include/elf.h for these values
+ pf_flags = {
+ 'P':1<<4, 'p':1<<5,
+ 'S':1<<6, 's':1<<7,
+ 'M':1<<8, 'm':1<<9,
+ 'X':1<<10, 'x':1<<11,
+ 'E':1<<12, 'e':1<<13,
+ 'R':1<<14, 'r':1<<15
+ }
+
+ ( importer_str_flags, importer_bin_flags ) = pax.getflags(importer)
+
+ # Start with the exporter's flags
+ result_bin_flags = exporter_bin_flags
+
+ for i in range(len(importer_str_flags)):
+
+ # The exporter's flag contradicts the importer's flag, so do nothing
+ if (exporter_str_flags[i].isupper() and importer_str_flags[i].islower()) or \
+ (exporter_str_flags[i].islower() and importer_str_flags[i].isupper()):
+
+ # Revert the exporter's flag, use the importer's flag and warn
+ result_bin_flags = result_bin_flags ^ pf_flags[exporter_str_flags[i]]
+ result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
+ print('\t\tWarning: %s has %s, refusing to set to %s' % (
+ importer, importer_str_flags[i], exporter_str_flags[i] )),
+
+ # The exporter's flags is off, so use the importer's flag
+ if (exporter_str_flags[i] == '-' and importer_str_flags[i] != '-'):
+ result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
+
+ pax.setbinflags(importer, result_bin_flags)
+
+
+def run_binary(binary, verbose, mark, allyes):
+ if not os.path.exists(binary):
+ print('%s\tNo such OBJECT' % binary)
+ return
+ ( linkings, mappings ) = get_ldd_linkings(binary)
+ ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
+ print('%s (%s)\n' % ( binary, binary_str_flags ))
+
+ mismatched_libraries = []
+
+ for soname in linkings:
+ try:
+ library = mappings[soname]
+ ( library_str_flags, library_bin_flags ) = pax.getflags(library)
+ if verbose:
+ print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags ))
+ if binary_str_flags != library_str_flags:
+ mismatched_libraries.append(library)
+ if not verbose:
+ print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags ))
+ except pax.PaxError:
+ print('file for soname %s not found' % soname)
+
+ if len(mismatched_libraries) == 0:
+ if not verbose:
+ print('\tNo mismatches\n')
+ else:
+ print('\n'),
+ if mark:
+ print('\tWill mark libraries with %s\n' % binary_str_flags)
+ for library in mismatched_libraries:
+ do_marking = False
+ while True:
+ if allyes:
+ ans = 'y'
+ else:
+ ans = get_input('\tSet flags for %s (y/n): ' % library)
+ if ans == 'y':
+ do_marking = True
+ break
+ elif ans == 'n':
+ do_marking = False
+ break
+ else:
+ print('\t\tPlease enter y or n')
+
+ if do_marking:
+ try:
+ migrate_flags(library, binary_str_flags, binary_bin_flags)
+ except pax.PaxError:
+ print("\n\tCould not set pax flags on %s, file is probably busy" % library)
+ print("\tShut down all processes that use it and try again")
+ ( library_str_flags, library_bin_flags ) = pax.getflags(library)
+ print('\n\t\t%s ( %s )\n' % ( library, library_str_flags ))
+
+
+def invert_so2library_mappings( so2library_mappings ):
+ library2soname_mappings = {}
+ for soname, library in so2library_mappings.items():
+ library2soname_mappings[library] = soname
+ return library2soname_mappings
+
+
+def run_soname(name, verbose, use_soname, mark, allyes, executable_only):
+ shell_path = path = os.getenv('PATH').split(':')
+
+ ( forward_linkings, so2library_mappings ) = get_forward_linkings()
+ reverse_linkings = invert_linkings( forward_linkings )
+
+ if use_soname:
+ soname = name
+ else:
+ library2soname_mappings = invert_so2library_mappings(so2library_mappings)
+ try:
+ soname = library2soname_mappings[name]
+ except KeyError:
+ print('%s\tNo such LIBRARY' % name)
+ return
+
+ try:
+ linkings = reverse_linkings[soname]
+ except KeyError:
+ print('%s\tNo such SONAME' % soname)
+ return
+
+ library = so2library_mappings[soname]
+
+ ( library_str_flags, library_bin_flags ) = pax.getflags(library)
+ print('%s\t%s (%s)\n' % ( soname, library, library_str_flags ))
+
+ mismatched_binaries = []
+ for binary in linkings:
+ try:
+ ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
+ if verbose:
+ if executable_only:
+ if os.path.dirname(binary) in shell_path:
+ print('\t%s ( %s )' % ( binary, binary_str_flags ))
+ else:
+ print('\t%s ( %s )' % ( binary, binary_str_flags ))
+ if library_str_flags != binary_str_flags:
+ if executable_only:
+ if os.path.dirname(binary) in shell_path:
+ mismatched_binaries.append(binary)
+ if not verbose:
+ print('\t%s ( %s )' % ( binary, binary_str_flags ))
+ else:
+ mismatched_binaries.append(binary)
+ if not verbose:
+ print('\t%s ( %s )' % ( binary, binary_str_flags ))
+ except pax.PaxError:
+ print('cannot obtain pax flags for %s' % binary)
+
+ if len(mismatched_binaries) == 0:
+ if not verbose:
+ print('\tNo mismatches\n')
+ else:
+ print('\n'),
+ if mark:
+ print('\tWill mark binaries with %s\n' % library_str_flags)
+ for binary in mismatched_binaries:
+ if executable_only:
+ if not os.path.dirname(binary) in shell_path:
+ continue
+ do_marking = False
+ while True:
+ if allyes:
+ ans = 'y'
+ else:
+ ans = get_input('\tSet flags for %s (y/n): ' % binary)
+ if ans == 'y':
+ do_marking = True
+ break
+ elif ans == 'n':
+ do_marking = False
+ break
+ else:
+ print('\t\tPlease enter y or n')
+ if do_marking:
+ try:
+ migrate_flags(binary, library_str_flags, library_bin_flags)
+ except pax.PaxError:
+ print('\n\tCould not set pax flags on %s, file is probably busy' % binary)
+ print('\tShut down all processes that use it and try again')
+ ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
+ print('\n\t\t%s ( %s )\n' % ( binary, binary_str_flags ))
+
+
+def run_usage():
+ print('Package Name : elfix')
+ print('Bug Reports : http://bugs.gentoo.org/')
+ print('Program Name : revdep-pax')
+ print('Description : Get or set pax flags on an ELF object')
+ print('')
+ print('Usage : revdep-pax -f [-v] print out all forward mappings for all system binaries')
+ print(' : revdep-pax -r [-ve] print out all reverse mappings for all system sonames')
+ print(' : revdep-pax -b OBJECT [-myv] print all forward mappings only for OBJECT')
+ print(' : revdep-pax -s SONAME [-myve] print all reverse mappings only for SONAME')
+ print(' : revdep-pax -l LIBRARY [-myve] print all reverse mappings only for LIBRARY file')
+ print(' : revdep-pax [-h] print out this help')
+ print(' : -v verbose, otherwise just print mismatching objects')
+ print(' : -e only print out executables in shell $PATH')
+ print(' : -m don\'t just report, but mark the mismatching objects')
+ print(' : -y assume "yes" to all prompts for marking (USE CAREFULLY!)')
+ print('')
+
+
+def main():
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'hfrb:s:l:vemy')
+ except getopt.GetoptError as err:
+ print(str(err)) # will print something like 'option -a not recognized'
+ run_usage()
+ sys.exit(1)
+
+ if len(opts) == 0:
+ run_usage()
+ sys.exit(1)
+
+ do_usage = False
+ do_forward = False
+ do_reverse = False
+
+ binary = None
+ soname = None
+ library = None
+
+ verbose = False
+ executable_only = False
+ mark = False
+ allyes = False
+
+ opt_count = 0
+
+ for o, a in opts:
+ if o == '-h':
+ do_usage = True
+ opt_count += 1
+ elif o == '-f':
+ do_forward = True
+ opt_count += 1
+ elif o == '-r':
+ do_reverse = True
+ opt_count += 1
+ elif o == '-b':
+ binary = a
+ opt_count += 1
+ elif o == '-s':
+ soname = a
+ opt_count += 1
+ elif o == '-l':
+ library = a
+ opt_count += 1
+ elif o == '-v':
+ verbose = True
+ elif o == '-e':
+ executable_only = True
+ elif o == '-m':
+ mark = True
+ elif o == '-y':
+ allyes = True
+ else:
+ print('Option included in getopt but not handled here!')
+ print('Please file a bug')
+ sys.exit(1)
+
+ # Only allow one of -h, -f -r -b -s
+ if opt_count > 1 or do_usage:
+ run_usage()
+ elif do_forward:
+ run_forward(verbose)
+ elif do_reverse:
+ run_reverse(verbose, executable_only)
+ elif binary != None:
+ run_binary(binary, verbose, mark, allyes)
+ elif soname != None:
+ run_soname(soname, verbose, True, mark, allyes, executable_only)
+ elif library != None:
+ library = os.path.realpath(library)
+ run_soname(library, verbose, False, mark, allyes, executable_only)
+
+if __name__ == '__main__':
+ main()
diff --git a/scripts/paxmodule.c b/scripts/paxmodule.c
index f329df8..4ba32df 100644
--- a/scripts/paxmodule.c
+++ b/scripts/paxmodule.c
@@ -1,6 +1,6 @@
/*
paxmodule.c: this file is part of the elfix package
- Copyright (C) 2011 Anthony G. Basile
+ Copyright (C) 2011, 2012 Anthony G. Basile
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
diff --git a/scripts/pypaxctl b/scripts/pypaxctl
index 0e8f3fe..cca3d2c 100755
--- a/scripts/pypaxctl
+++ b/scripts/pypaxctl
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# pypaxctl: this file is part of the elfix package
-# Copyright (C) 2011 Anthony G. Basile
+# Copyright (C) 2011, 2012 Anthony G. Basile
#
# 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
diff --git a/scripts/revdep-pax b/scripts/revdep-pax
index 4bb3f36..b919dbf 100755
--- a/scripts/revdep-pax
+++ b/scripts/revdep-pax
@@ -1,500 +1,654 @@
#!/usr/bin/env python
#
-# revdep-pax: this file is part of the elfix package
-# Copyright (C) 2011 Anthony G. Basile
+# revdep-pax: this file is part of the elfix package
+# Copyright (C) 2011, 2012 Anthony G. Basile
#
-# 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 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.
+# 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 <http://www.gnu.org/licenses/>.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
+#
+# Note: This alternative way of doing revdep-pax only
+# works on Gentoo systems where NEEDED.ELF.2 all the
+# information we need generated by scanelf during emerge.
+#
+# See /usr/lib/portage/bin/misc-functions.sh ~line 520
+# echo "${arch:3};${obj};${soname};${rpath};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2
+#
-import sys
import getopt
import os
-import subprocess
-import re
+import sys
import pax
+import re
+import portage
+
-#python2/3 compat input
def get_input(prompt):
- if sys.hexversion > 0x03000000:
- return input(prompt)
- else:
- return raw_input(prompt)
-
-
-def get_ldd_linkings(binary):
- ldd_output = subprocess.Popen(['/usr/bin/ldd', binary], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- ldd_lines = ldd_output.stdout.read().decode().split('\n')
-
- linkings = []
- mappings = {}
- for m in range(0,len(ldd_lines)):
- if not re.search('=>', ldd_lines[m]):
- continue
- ldd_lines[m] = ldd_lines[m].strip()
- mapp = re.split('=>', ldd_lines[m] )
- soname = mapp[0].strip()
- soname = os.path.basename(soname) # This is for ./libSDL-1.2.so.0
- library = re.sub('\(.*$', '', mapp[1]).strip()
- if library == '':
- continue
- library = os.path.realpath(library)
- linkings.append(soname)
- mappings[soname] = library
-
- return ( linkings, mappings )
-
-
-def get_forward_linkings():
- #TODO: I'm still not sure we want to use /var/db/pkg vs some path of binaries
- var_db_pkg = '/var/db/pkg'
-
- forward_linkings = {}
- so2library_mappings = {}
- for cat in os.listdir(var_db_pkg):
- catdir = '%s/%s' % (var_db_pkg, cat)
- for pkg in os.listdir(catdir):
- pkgdir = '%s/%s' % (catdir, pkg)
- need = '%s/%s' % (pkgdir, 'NEEDED')
- try:
- g = open(need, 'r')
- needs = g.readlines()
- for line in needs:
- line = line.strip()
- link = re.split('\s', line)
- binary = link[0]
- ( linkings, mappings ) = get_ldd_linkings(binary)
- forward_linkings[binary] = linkings
- so2library_mappings.update(mappings)
- except IOError:
- continue #File probably doesn't exist, which is okay
-
- return ( forward_linkings, so2library_mappings )
-
-
-def invert_linkings( forward_linkings ):
- reverse_linkings = {}
-
- for binary in forward_linkings:
- for library in forward_linkings[binary]:
- reverse_linkings.setdefault(library,[]).append(binary)
-
- return reverse_linkings
-
-
-def print_forward_linkings( forward_linkings, so2library_mappings, verbose ):
- missing_binaries = []
- missing_links = []
- for binary in forward_linkings:
-
- try:
- ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
- sv = '%s ( %s )\n' % ( binary, binary_str_flags )
- s = sv
- except pax.PaxError:
- missing_binaries.append(binary)
- continue
-
- count = 0
- for soname in forward_linkings[binary]:
- try:
- library = so2library_mappings[soname]
- ( library_str_flags, library_bin_flags ) = pax.getflags(library)
- sv = '%s\n\t%s\t%s ( %s )' % ( sv, soname, library, library_str_flags )
- if binary_str_flags != library_str_flags:
- s = '%s\n\t%s\t%s ( %s )' % ( s, soname, library, library_str_flags )
- count = count + 1
- except pax.PaxError:
- missing_links.append(soname)
-
- if verbose:
- print('%s\n' % sv)
- if count == 0:
- print('\tNo mismatches\n\n')
- else:
- print('\tMismatches\n\n')
- else:
- if count != 0:
- print('%s\n\n' % s)
-
- missing_binaries = set(missing_binaries)
- print('\n**** Missing binaries ****\n')
- for m in missing_binaries:
- print('\t%s\n' % m)
-
- missing_links = set(missing_links)
- print('\n**** Missing forward linkings ****\n')
- for m in missing_links:
- print('\t%s\n' % m)
-
-
-def print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only ):
- shell_path = path = os.getenv('PATH').split(':')
- missing_sonames = []
- missing_links = []
-
- for soname in reverse_linkings:
- try:
- library = so2library_mappings[soname]
- ( library_str_flags, library_bin_flags ) = pax.getflags(library)
- sv = '%s\t%s ( %s )\n' % ( soname, library, library_str_flags )
- s = sv
- except pax.PaxError:
- missing_sonames.append(soname)
- continue
-
- count = 0
- for binary in reverse_linkings[soname]:
- try:
- ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
- if executable_only:
- if os.path.dirname(binary) in shell_path:
- sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags )
- if library_str_flags != binary_str_flags:
- s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags )
- count = count + 1
- else:
- sv = '%s\n\t%s ( %s )' % ( sv, binary, binary_str_flags )
- if library_str_flags != binary_str_flags:
- s = '%s\n\t%s ( %s )' % ( s, binary, binary_str_flags )
- count = count + 1
- except pax.PaxError:
- missing_links.append(binary)
-
- if verbose:
- print('%s\n' % sv)
- if count == 0:
- print('\tNo mismatches\n\n')
- else:
- print('\tMismatches\n\n')
- else:
- if count != 0:
- print('%s\n\n' % s)
-
- missing_sonames = set(missing_sonames)
- print('\n**** Missing sonames ****\n')
- for m in missing_sonames:
- print('\t%s\n' % m)
-
- missing_links = set(missing_links)
- print('\n**** Missing reverse linkings ****\n')
- for m in missing_links:
- print('\t%s\n' % m)
+ """ python2/3 compat input """
+ if sys.hexversion > 0x03000000:
+ return input(prompt)
+ else:
+ return raw_input(prompt)
+
+
+class LinkMap:
+
+ def __init__(self):
+ """ Put all the NEEDED.ELF.2 files for all installed packages
+ into a dictionary of the form
+
+ { pkg : line_from_NEEDED.ELF.2, ... }
+
+ where the line has the following form:
+
+ echo "${arch:3};${obj};${soname};${rpath};${needed}" >> \
+ "${PORTAGE_BUILDDIR}"/build-info/NEEDED.ELF.2
+
+ See /usr/lib/portage/bin/misc-functions.sh ~line 520
+ """
+
+ vardb = portage.db[portage.root]["vartree"].dbapi
+
+ self.pkgs = []
+ self.pkgs_needed = {}
+
+ for pkg in vardb.cpv_all():
+ needed = vardb.aux_get(pkg, ['NEEDED.ELF.2'])[0].strip()
+ if needed: # Some packages have no NEEDED.ELF.2
+ self.pkgs.append(pkg)
+ for line in re.split('\n', needed):
+ self.pkgs_needed.setdefault(pkg,[]).append(re.split(';', line))
+
+
+ def get_object_needed(self):
+ """ Return object_needed dictionary which has structure
+
+ {
+ abi1 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... },
+ abi2 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... },
+ ....
+ }
+
+ Here the sonames were obtained from the ELF object by scanelf -nm
+ (like readelf -d) during emerge.
+ """
+ object_needed = {}
+
+ for pkg in self.pkgs:
+ for link in self.pkgs_needed[pkg]:
+ abi = link[0]
+ elf = link[1]
+ sonames = re.split(',', link[4])
+ object_needed.setdefault(abi,{}).update({elf:sonames})
+
+ return object_needed
+
+
+ def get_libraries(self):
+ """ Return library2soname dictionary which has structure
+
+ { full_path_to_library : (soname, abi), ... }
+
+ and its inverse which has structure
+
+ { (soname, abi) : full_path_to_library, ... }
+ """
+ library2soname = {}
+ soname2library = {}
+
+ for pkg in self.pkgs:
+ for link in self.pkgs_needed[pkg]:
+ abi = link[0]
+ elf = link[1]
+ soname = link[2]
+ if soname: #no soname => executable
+ library2soname[elf] = (soname,abi)
+ soname2library[(soname,abi)] = elf
+
+ return ( library2soname, soname2library )
+
+
+ def get_soname_needed(self, object_needed, library2soname ):
+ """ Return soname_needed dictionary which has structure:
+
+ {
+ abi1: { soname: [ soname1, soname2, ... ], .... },
+ abi2: { soname: [ soname1, soname2, ... ], .... },
+ }
+
+ Here the soname1, soname2,... were obtained from soname's corresponding
+ ELF object by scanelf -n during emerge.
+ """
+ soname_needed = {}
+
+ for abi in object_needed:
+ for elf in object_needed[abi]:
+ try:
+ (soname, abi_check) = library2soname[elf]
+ assert abi == abi_check # We should get the same abi associated with the soname
+ soname_needed.setdefault(abi,{}).update({soname:object_needed[abi][elf]})
+ except KeyError:
+ continue # no soname, its probably an executable
+
+ return soname_needed
+
+
+ def expand_linkings(self, object_needed, soname2library):
+ """ Expands the object_needed dictionary which has structure
+
+ {
+ abi1 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... },
+ abi2 : { full_path_to_ELF_object : [ soname1, soname2, ... ], ... },
+ ....
+ }
+
+ such that the soname's are traced all the way to the end of
+ the link chain. Here the sonames should be the same as those
+ obtained from the ELF object by ldd.
+ """
+ for abi in object_needed:
+ for elf in object_needed[abi]:
+ while True:
+ found_new_soname = False
+ for so in object_needed[abi][elf]: # For all the first links ...
+ try:
+ for sn in object_needed[abi][soname2library[(so,abi)]]: # go to the next links ...
+ if sn in object_needed[abi][elf]: # skip if already included ...
+ continue
+ if not (sn,abi) in soname2library: # skip if vdso ...
+ continue
+
+ # This appends to the object_needed and soname_needed lists. No copy was
+ # done so its the same lists in memory for both, and its modified for both.
+
+ object_needed[abi][elf].append(sn) # otherwise collapse it back into
+ found_new_soname = True # first links of the chain.
+
+ except KeyError: # Not all nodes in the chain have a next node
+ continue
+
+ if not found_new_soname: # We're done, that last iteration found
+ break # no new nodes
+
+
+ def get_object_reverse_linkings(self, object_linkings):
+ """ Return object_reverse_linkings dictionary which has structure
+
+ {
+ abi1 : { soname : [ path_to_elf1, path_to_elf2, ... ], ... },
+ abi2 : { soname : [ path_to_elf3, path_to_elf4, ... ], ... },
+ ....
+ }
+ """
+ object_reverse_linkings = {}
+
+ for abi in object_linkings:
+ for elf in object_linkings[abi]:
+ for soname in object_linkings[abi][elf]:
+ object_reverse_linkings.setdefault(abi,{}).setdefault(soname,[]).append(elf)
+
+ return object_reverse_linkings
+
+
+ def get_maps(self):
+ """ Generate the full forward and reverse links using the above functions """
+
+ # After get_object_needed() and get_soname_needed(), both object_linkings and
+ # soname_linkings are only one step into the entire link chain.
+
+ object_linkings = self.get_object_needed()
+ ( library2soname, soname2library ) = self.get_libraries()
+ soname_linkings = self.get_soname_needed( object_linkings, library2soname )
+
+ # After the appending in expand_linkings(), forward_linkings and soname_linkings
+ # have been extended through the entire chain of linking. expand_linkings() is
+ # a "side-effect" function, so we note it here.
+ self.expand_linkings( soname_linkings, soname2library )
+ object_reverse_linkings = self.get_object_reverse_linkings( object_linkings )
+
+ return ( object_linkings, object_reverse_linkings, library2soname, soname2library )
+
+
+
+
+def print_problems(sonames_missing_library):
+ sonames_missing_library = set(sonames_missing_library)
+ print('\n**** SONAMES without any library files ****')
+ for m in sonames_missing_library:
+ print('\t%s' % m)
def run_forward(verbose):
- ( forward_linkings, so2library_mappings ) = get_forward_linkings()
- print_forward_linkings( forward_linkings, so2library_mappings, verbose)
+ (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
+
+ sonames_missing_library = []
+
+ for abi in object_linkings:
+ for elf in object_linkings[abi]:
+ try:
+ (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
+ sv = '%s :%s ( %s )' % (elf, abi, elf_str_flags)
+ s = sv
+ except pax.PaxError:
+ sv = '%s :%s ( %s )' % (elf, abi, '****')
+ s = sv
+ continue
+
+ count = 0
+ for soname in object_linkings[abi][elf]:
+ try:
+ library = soname2library[(soname, abi)]
+ try:
+ (library_str_flags, library_bin_flags) = pax.getflags(library)
+ except pax.PaxError:
+ library_str_flags = '****'
+ sv = '%s\n\t%s\t%s ( %s )' % (sv, soname, library, library_str_flags)
+ if elf_str_flags != library_str_flags:
+ s = '%s\n\t%s\t%s ( %s )' % (s, soname, library, library_str_flags)
+ count = count + 1
+ except KeyError:
+ sonames_missing_library.append(soname)
+
+ if verbose:
+ print('%s\n' % sv)
+ if count == 0:
+ print('\tNo mismatches\n\n')
+ else:
+ print('\tMismatches\n\n')
+ else:
+ if count != 0:
+ print('%s\n\n' % s)
+
+ if verbose:
+ print_problems(sonames_missing_library)
def run_reverse(verbose, executable_only):
- ( forward_linkings, so2library_mappings ) = get_forward_linkings()
- reverse_linkings = invert_linkings( forward_linkings )
- print_reverse_linkings( reverse_linkings, so2library_mappings, verbose, executable_only)
+ (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
+
+ shell_path = path = os.getenv('PATH').split(':')
+
+ sonames_missing_library = []
+
+ for abi in object_reverse_linkings:
+ for soname in object_reverse_linkings[abi]:
+ try:
+ library = soname2library[(soname, abi)]
+ try:
+ (library_str_flags, library_bin_flags) = pax.getflags(library)
+ except pax.PaxError:
+ library_str_flags = '****'
+ sv = '%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags)
+ s = sv
+ except KeyError:
+ sonames_missing_library.append(soname)
+
+ count = 0
+ for elf in object_reverse_linkings[abi][soname]:
+ try:
+ (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
+ except pax.PaxError:
+ elf_str_flags = '****'
+ if executable_only:
+ if os.path.dirname(elf) in shell_path:
+ sv = '%s\n\t%s ( %s )' % (sv, elf, elf_str_flags)
+ if library_str_flags != elf_str_flags:
+ s = '%s\n\t%s ( %s )' % (s, elf, elf_str_flags)
+ count = count + 1
+ else:
+ sv = '%s\n\t%s ( %s )' % (sv, elf, elf_str_flags)
+ if library_str_flags != elf_str_flags:
+ s = '%s\n\t%s ( %s )' % (s, elf, elf_str_flags)
+ count = count + 1
+
+ if verbose:
+ print('%s\n' % sv)
+ if count == 0:
+ print('\tNo mismatches\n\n')
+ else:
+ print('\tMismatches\n\n')
+ else:
+ if count != 0:
+ print('%s\n\n' % s)
+
+ if verbose:
+ print_problems(sonames_missing_library)
def migrate_flags(importer, exporter_str_flags, exporter_bin_flags):
- # We implement the following logic for setting the pax flags
- # on the target elf object, the IMPORTER, given that the flags
- # from the elf object we want it to match to, the EXPORTER.
- #
- # EXPORTER IMPORTER RESULT
- # On On On
- # On Off On + Warn
- # On - On
- # Off On On + Warn
- # Off Off Off
- # Off - Off
- # - On On
- # - Off Off
- # - - -
-
- #See /usr/include/elf.h for these values
- pf_flags = {
- 'P':1<<4, 'p':1<<5,
- 'S':1<<6, 's':1<<7,
- 'M':1<<8, 'm':1<<9,
- 'X':1<<10, 'x':1<<11,
- 'E':1<<12, 'e':1<<13,
- 'R':1<<14, 'r':1<<15
- }
-
- ( importer_str_flags, importer_bin_flags ) = pax.getflags(importer)
-
- # Start with the exporter's flags
- result_bin_flags = exporter_bin_flags
-
- for i in range(len(importer_str_flags)):
-
- # The exporter's flag contradicts the importer's flag, so do nothing
- if (exporter_str_flags[i].isupper() and importer_str_flags[i].islower()) or \
- (exporter_str_flags[i].islower() and importer_str_flags[i].isupper()):
-
- # Revert the exporter's flag, use the importer's flag and warn
- result_bin_flags = result_bin_flags ^ pf_flags[exporter_str_flags[i]]
- result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
- print('\t\tWarning: %s has %s, refusing to set to %s' % (
- importer, importer_str_flags[i], exporter_str_flags[i] )),
-
- # The exporter's flags is off, so use the importer's flag
- if (exporter_str_flags[i] == '-' and importer_str_flags[i] != '-'):
- result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
-
- pax.setbinflags(importer, result_bin_flags)
-
-
-def run_binary(binary, verbose, mark, allyes):
- if not os.path.exists(binary):
- print('%s\tNo such OBJECT' % binary)
- return
- ( linkings, mappings ) = get_ldd_linkings(binary)
- ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
- print('%s (%s)\n' % ( binary, binary_str_flags ))
-
- mismatched_libraries = []
-
- for soname in linkings:
- try:
- library = mappings[soname]
- ( library_str_flags, library_bin_flags ) = pax.getflags(library)
- if verbose:
- print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags ))
- if binary_str_flags != library_str_flags:
- mismatched_libraries.append(library)
- if not verbose:
- print('\t%s\t%s ( %s )' % ( soname, library, library_str_flags ))
- except pax.PaxError:
- print('file for soname %s not found' % soname)
-
- if len(mismatched_libraries) == 0:
- if not verbose:
- print('\tNo mismatches\n')
- else:
- print('\n'),
- if mark:
- print('\tWill mark libraries with %s\n' % binary_str_flags)
- for library in mismatched_libraries:
- do_marking = False
- while True:
- if allyes:
- ans = 'y'
- else:
- ans = get_input('\tSet flags for %s (y/n): ' % library)
- if ans == 'y':
- do_marking = True
- break
- elif ans == 'n':
- do_marking = False
- break
- else:
- print('\t\tPlease enter y or n')
-
- if do_marking:
- try:
- migrate_flags(library, binary_str_flags, binary_bin_flags)
- except pax.PaxError:
- print("\n\tCould not set pax flags on %s, file is probably busy" % library)
- print("\tShut down all processes that use it and try again")
- ( library_str_flags, library_bin_flags ) = pax.getflags(library)
- print('\n\t\t%s ( %s )\n' % ( library, library_str_flags ))
-
-
-def invert_so2library_mappings( so2library_mappings ):
- library2soname_mappings = {}
- for soname, library in so2library_mappings.items():
- library2soname_mappings[library] = soname
- return library2soname_mappings
+ # We implement the following logic for setting the pax flags
+ # on the target elf object, the IMPORTER, given that the flags
+ # from the elf object we want it to match to, the EXPORTER.
+ #
+ # EXPORTER IMPORTER RESULT
+ # On On On
+ # On Off On + Warn
+ # On - On
+ # Off On On + Warn
+ # Off Off Off
+ # Off - Off
+ # - On On
+ # - Off Off
+ # - - -
+
+ #See /usr/include/elf.h for these values
+ pf_flags = {
+ 'P':1<<4, 'p':1<<5,
+ 'S':1<<6, 's':1<<7,
+ 'M':1<<8, 'm':1<<9,
+ 'X':1<<10, 'x':1<<11,
+ 'E':1<<12, 'e':1<<13,
+ 'R':1<<14, 'r':1<<15
+ }
+
+ try:
+ (importer_str_flags, importer_bin_flags) = pax.getflags(importer)
+ except pax.PaxError:
+ # The importer has no flags, so just set them
+ pax.setbinflags(importer, exporter_bin_flags)
+ return
+
+ # Start with the exporter's flags
+ result_bin_flags = exporter_bin_flags
+
+ for i in range(len(importer_str_flags)):
+
+ # The exporter's flag contradicts the importer's flag, so do nothing
+ if (exporter_str_flags[i].isupper() and importer_str_flags[i].islower()) or \
+ (exporter_str_flags[i].islower() and importer_str_flags[i].isupper()):
+
+ # Revert the exporter's flag, use the importer's flag and warn
+ result_bin_flags = result_bin_flags ^ pf_flags[exporter_str_flags[i]]
+ result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
+ print('\t\tWarning: %s has %s, refusing to set to %s' % (
+ importer, importer_str_flags[i], exporter_str_flags[i] )),
+
+ # The exporter's flags is off, so use the importer's flag
+ if (exporter_str_flags[i] == '-' and importer_str_flags[i] != '-'):
+ result_bin_flags = result_bin_flags | pf_flags[importer_str_flags[i]]
+
+ pax.setbinflags(importer, result_bin_flags)
+
+
+def run_elf(elf, verbose, mark, allyes):
+ if not os.path.exists(elf):
+ print('%s\tNo such OBJECT' % elf)
+ return
+
+ try:
+ (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
+ print('%s (%s)\n' % (elf, elf_str_flags))
+ except pax.PaxError:
+ print('%s: No PAX flags found\n' % elf)
+ return
+
+ (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
+
+ mismatched_libraries = []
+
+ for abi in object_linkings:
+ if not elf in object_linkings[abi]: # There may be no elf for that abi
+ continue
+ for soname in object_linkings[abi][elf]:
+ try:
+ library = soname2library[(soname,abi)]
+ try:
+ (library_str_flags, library_bin_flags) = pax.getflags(library)
+ except pax.PaxError:
+ library_str_flags = '****'
+ if verbose:
+ print('\t%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags))
+ if elf_str_flags != library_str_flags:
+ mismatched_libraries.append(library)
+ if not verbose:
+ print('\t%s\t%s :%s ( %s )' % (soname, library, abi, library_str_flags))
+ except KeyError:
+ print('%s :%s: file for soname not found' % (soname, abi))
+
+ if len(mismatched_libraries) == 0:
+ if not verbose:
+ print('\tNo mismatches\n')
+ else:
+ print('')
+ if mark:
+ print('\tWill mark libraries with %s\n' % elf_str_flags)
+ for library in mismatched_libraries:
+ do_marking = False
+ while True:
+ if allyes:
+ ans = 'y'
+ else:
+ ans = get_input('\tSet flags for %s :%s (y/n): ' % (library, abi))
+ if ans == 'y':
+ do_marking = True
+ break
+ elif ans == 'n':
+ do_marking = False
+ break
+ else:
+ print('\t\tPlease enter y or n')
+
+ if do_marking:
+ try:
+ migrate_flags(library, elf_str_flags, elf_bin_flags)
+ except pax.PaxError:
+ print('\n\tCould not set PAX flags on %s, text maybe busy' % (library, abi))
+
+ try:
+ (library_str_flags, library_bin_flags) = pax.getflags(library)
+ print('\n\t\t%s ( %s )\n' % (library, library_str_flags))
+ except pax.PaxError:
+ print('\n\t\t%s: Could not read PAX flags')
def run_soname(name, verbose, use_soname, mark, allyes, executable_only):
- shell_path = path = os.getenv('PATH').split(':')
-
- ( forward_linkings, so2library_mappings ) = get_forward_linkings()
- reverse_linkings = invert_linkings( forward_linkings )
-
- if use_soname:
- soname = name
- else:
- library2soname_mappings = invert_so2library_mappings(so2library_mappings)
- try:
- soname = library2soname_mappings[name]
- except KeyError:
- print('%s\tNo such LIBRARY' % name)
- return
-
- try:
- linkings = reverse_linkings[soname]
- except KeyError:
- print('%s\tNo such SONAME' % soname)
- return
-
- library = so2library_mappings[soname]
-
- ( library_str_flags, library_bin_flags ) = pax.getflags(library)
- print('%s\t%s (%s)\n' % ( soname, library, library_str_flags ))
-
- mismatched_binaries = []
- for binary in linkings:
- try:
- ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
- if verbose:
- if executable_only:
- if os.path.dirname(binary) in shell_path:
- print('\t%s ( %s )' % ( binary, binary_str_flags ))
- else:
- print('\t%s ( %s )' % ( binary, binary_str_flags ))
- if library_str_flags != binary_str_flags:
- if executable_only:
- if os.path.dirname(binary) in shell_path:
- mismatched_binaries.append(binary)
- if not verbose:
- print('\t%s ( %s )' % ( binary, binary_str_flags ))
- else:
- mismatched_binaries.append(binary)
- if not verbose:
- print('\t%s ( %s )' % ( binary, binary_str_flags ))
- except pax.PaxError:
- print('cannot obtain pax flags for %s' % binary)
-
- if len(mismatched_binaries) == 0:
- if not verbose:
- print('\tNo mismatches\n')
- else:
- print('\n'),
- if mark:
- print('\tWill mark binaries with %s\n' % library_str_flags)
- for binary in mismatched_binaries:
- if executable_only:
- if not os.path.dirname(binary) in shell_path:
- continue
- do_marking = False
- while True:
- if allyes:
- ans = 'y'
- else:
- ans = get_input('\tSet flags for %s (y/n): ' % binary)
- if ans == 'y':
- do_marking = True
- break
- elif ans == 'n':
- do_marking = False
- break
- else:
- print('\t\tPlease enter y or n')
- if do_marking:
- try:
- migrate_flags(binary, library_str_flags, library_bin_flags)
- except pax.PaxError:
- print('\n\tCould not set pax flags on %s, file is probably busy' % binary)
- print('\tShut down all processes that use it and try again')
- ( binary_str_flags, binary_bin_flags ) = pax.getflags(binary)
- print('\n\t\t%s ( %s )\n' % ( binary, binary_str_flags ))
+ shell_path = path = os.getenv('PATH').split(':')
+
+ (object_linkings, object_reverse_linkings, library2soname, soname2library) = LinkMap().get_maps()
+
+ if use_soname:
+ soname = name
+ abi_list = object_reverse_linkings.keys()
+ for abi in abi_list:
+ # There must be at least on abi with that soname
+ if soname in object_reverse_linkings[abi]:
+ break
+ else:
+ print('%s\tNo such SONAME' % soname)
+ return
+ else:
+ try:
+ (soname, abi) = library2soname[name]
+ abi_list = [abi]
+ except KeyError:
+ print('%s\tNo such LIBRARY' % name)
+ return
+
+
+ mismatched_elfs = []
+
+ for abi in abi_list:
+ # An soname can belong to one or more abis
+ if not soname in object_reverse_linkings[abi]:
+ continue
+
+ library = soname2library[(soname, abi)]
+
+ try:
+ (library_str_flags, library_bin_flags) = pax.getflags(library)
+ print('%s\t%s :%s (%s)\n' % (soname, library, abi, library_str_flags))
+ except pax.PaxError:
+ print('%s :%s : No PAX flags found\n' % (library, abi))
+ continue
+
+ for elf in object_reverse_linkings[abi][soname]:
+ try:
+ (elf_str_flags, elf_bin_flags ) = pax.getflags(elf)
+ except pax.PaxError:
+ elf_str_flags = '****'
+ if verbose:
+ if executable_only:
+ if os.path.dirname(elf) in shell_path:
+ print('\t%s ( %s )' % (elf, elf_str_flags))
+ else:
+ print('\t%s ( %s )' % (elf, elf_str_flags))
+ if library_str_flags != elf_str_flags:
+ if executable_only:
+ if os.path.dirname(elf) in shell_path:
+ mismatched_elfs.append(elf)
+ if not verbose:
+ print('\t%s ( %s )' % (elf, elf_str_flags))
+ else:
+ mismatched_elfs.append(elf)
+ if not verbose:
+ print('\t%s ( %s )' % (elf, elf_str_flags))
+
+ if len(mismatched_elfs) == 0:
+ if not verbose:
+ print('\tNo mismatches\n')
+ else:
+ print('')
+ if mark:
+ print('\tWill mark elf with %s\n' % library_str_flags)
+ for elf in mismatched_elfs:
+ if executable_only:
+ if not os.path.dirname(elf) in shell_path:
+ continue
+ do_marking = False
+ while True:
+ if allyes:
+ ans = 'y'
+ else:
+ ans = get_input('\tSet flags for %s (y/n): ' % elf)
+ if ans == 'y':
+ do_marking = True
+ break
+ elif ans == 'n':
+ do_marking = False
+ break
+ else:
+ print('\t\tPlease enter y or n')
+ if do_marking:
+ try:
+ migrate_flags(elf, library_str_flags, library_bin_flags)
+ except pax.PaxError:
+ print('\n\tCould not set pax flags on %s, file is probably busy' % elf)
+ print('\tShut down all processes that use it and try again')
+ (elf_str_flags, elf_bin_flags) = pax.getflags(elf)
+ print('\n\t\t%s ( %s )\n' % (elf, elf_str_flags))
def run_usage():
- print('Package Name : elfix')
- print('Bug Reports : http://bugs.gentoo.org/')
- print('Program Name : revdep-pax')
- print('Description : Get or set pax flags on an ELF object')
- print('')
- print('Usage : revdep-pax -f [-v] print out all forward mappings for all system binaries')
- print(' : revdep-pax -r [-ve] print out all reverse mappings for all system sonames')
- print(' : revdep-pax -b OBJECT [-myv] print all forward mappings only for OBJECT')
- print(' : revdep-pax -s SONAME [-myve] print all reverse mappings only for SONAME')
- print(' : revdep-pax -l LIBRARY [-myve] print all reverse mappings only for LIBRARY file')
- print(' : revdep-pax [-h] print out this help')
- print(' : -v verbose, otherwise just print mismatching objects')
- print(' : -e only print out executables in shell $PATH')
- print(' : -m don\'t just report, but mark the mismatching objects')
- print(' : -y assume "yes" to all prompts for marking (USE CAREFULLY!)')
- print('')
+ print('Package Name : elfix')
+ print('Bug Reports : http://bugs.gentoo.org/')
+ print('Program Name : revdep-pax')
+ print('Description : Get or set pax flags on an ELF object')
+ print('')
+ print('Usage : revdep-pax -f [-v] print out all forward mappings for all system ELF objects')
+ print(' : revdep-pax -r [-ve] print out all reverse mappings for all system sonames')
+ print(' : revdep-pax -b OBJECT [-myv] print all forward mappings only for OBJECT')
+ print(' : revdep-pax -s SONAME [-myve] print all reverse mappings only for SONAME')
+ print(' : revdep-pax -l LIBRARY [-myve] print all reverse mappings only for LIBRARY file')
+ print(' : revdep-pax [-h] print out this help')
+ print(' : -v verbose, otherwise just print mismatching objects')
+ print(' : -e only print out executables in shell $PATH')
+ print(' : -m don\'t just report, but mark the mismatching objects')
+ print(' : -y assume "yes" to all prompts for marking (USE CAREFULLY!)')
+ print('')
def main():
- try:
- opts, args = getopt.getopt(sys.argv[1:], 'hfrb:s:l:vemy')
- except getopt.GetoptError as err:
- print(str(err)) # will print something like 'option -a not recognized'
- run_usage()
- sys.exit(1)
-
- if len(opts) == 0:
- run_usage()
- sys.exit(1)
-
- do_usage = False
- do_forward = False
- do_reverse = False
-
- binary = None
- soname = None
- library = None
-
- verbose = False
- executable_only = False
- mark = False
- allyes = False
-
- opt_count = 0
-
- for o, a in opts:
- if o == '-h':
- do_usage = True
- opt_count += 1
- elif o == '-f':
- do_forward = True
- opt_count += 1
- elif o == '-r':
- do_reverse = True
- opt_count += 1
- elif o == '-b':
- binary = a
- opt_count += 1
- elif o == '-s':
- soname = a
- opt_count += 1
- elif o == '-l':
- library = a
- opt_count += 1
- elif o == '-v':
- verbose = True
- elif o == '-e':
- executable_only = True
- elif o == '-m':
- mark = True
- elif o == '-y':
- allyes = True
- else:
- print('Option included in getopt but not handled here!')
- print('Please file a bug')
- sys.exit(1)
-
- # Only allow one of -h, -f -r -b -s
- if opt_count > 1 or do_usage:
- run_usage()
- elif do_forward:
- run_forward(verbose)
- elif do_reverse:
- run_reverse(verbose, executable_only)
- elif binary != None:
- run_binary(binary, verbose, mark, allyes)
- elif soname != None:
- run_soname(soname, verbose, True, mark, allyes, executable_only)
- elif library != None:
- library = os.path.realpath(library)
- run_soname(library, verbose, False, mark, allyes, executable_only)
+ # Are we root?
+ uid = os.getuid()
+ if uid != 0:
+ print('This program must be run as root')
+ sys.exit(1)
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'hfrb:s:l:vemy')
+ except getopt.GetoptError as err:
+ print(str(err)) # will print something like 'option -a not recognized'
+ run_usage()
+ sys.exit(1)
+
+ if len(opts) == 0:
+ run_usage()
+ sys.exit(1)
+
+ do_usage = False
+ do_forward = False
+ do_reverse = False
+
+ elf = None
+ soname = None
+ library = None
+
+ verbose = False
+ executable_only = False
+ mark = False
+ allyes = False
+
+ opt_count = 0
+
+ for o, a in opts:
+ if o == '-h':
+ do_usage = True
+ opt_count += 1
+ elif o == '-f':
+ do_forward = True
+ opt_count += 1
+ elif o == '-r':
+ do_reverse = True
+ opt_count += 1
+ elif o == '-b':
+ elf = a
+ opt_count += 1
+ elif o == '-s':
+ soname = a
+ opt_count += 1
+ elif o == '-l':
+ library = a
+ opt_count += 1
+ elif o == '-v':
+ verbose = True
+ elif o == '-e':
+ executable_only = True
+ elif o == '-m':
+ mark = True
+ elif o == '-y':
+ allyes = True
+ else:
+ print('Option included in getopt but not handled here!')
+ print('Please file a bug')
+ sys.exit(1)
+
+ # Only allow one of -h, -f -r -b -s
+ if opt_count > 1 or do_usage:
+ run_usage()
+ elif do_forward:
+ run_forward(verbose)
+ elif do_reverse:
+ run_reverse(verbose, executable_only)
+ elif elf != None:
+ run_elf(elf, verbose, mark, allyes)
+ elif soname != None:
+ run_soname(soname, verbose, True, mark, allyes, executable_only)
+ elif library != None:
+ library = os.path.realpath(library)
+ run_soname(library, verbose, False, mark, allyes, executable_only)
+
if __name__ == '__main__':
main()
diff --git a/scripts/setup.py b/scripts/setup.py
index ccb6989..f428db2 100755
--- a/scripts/setup.py
+++ b/scripts/setup.py
@@ -1,7 +1,7 @@
#!/usr/bin/env python
#
# setup.py: this file is part of the elfix package
-# Copyright (C) 2011 Anthony G. Basile
+# Copyright (C) 2011, 2012 Anthony G. Basile
#
# 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