diff options
author | Anthony G. Basile <blueness@gentoo.org> | 2012-12-28 20:55:35 -0500 |
---|---|---|
committer | Anthony G. Basile <blueness@gentoo.org> | 2012-12-28 20:55:35 -0500 |
commit | aee408c58d71f407fc92761aafee788db83603ff (patch) | |
tree | 55dab324da37f01537e4a8aae9b9da11ad3ebc0b | |
parent | ChangeLog: updated (diff) | |
download | elfix-aee408c58d71f407fc92761aafee788db83603ff.tar.gz elfix-aee408c58d71f407fc92761aafee788db83603ff.tar.bz2 elfix-aee408c58d71f407fc92761aafee788db83603ff.zip |
scripts/revdep-pax: update to alt-revdep-pax + link_map.py
-rw-r--r-- | TODO | 4 | ||||
-rwxr-xr-x | misc/alt-revdep-pax | 472 | ||||
-rwxr-xr-x | misc/revdep-pax-ng | 500 | ||||
-rw-r--r-- | scripts/paxmodule.c | 2 | ||||
-rwxr-xr-x | scripts/pypaxctl | 2 | ||||
-rwxr-xr-x | scripts/revdep-pax | 1084 | ||||
-rwxr-xr-x | scripts/setup.py | 2 |
7 files changed, 1124 insertions, 942 deletions
@@ -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 |