Add preserve-libs support for AIX' XCOFF, patch by Michael Haubenwallner
authorFabian Groffen <grobian@gentoo.org>
Wed, 28 Oct 2009 19:38:53 +0000 (19:38 -0000)
committerFabian Groffen <grobian@gentoo.org>
Wed, 28 Oct 2009 19:38:53 +0000 (19:38 -0000)
svn path=/main/branches/prefix/; revision=14745

bin/misc-functions.sh
pym/portage/__init__.py
pym/portage/dbapi/vartree.py

index 8be1176dc8f9ec8c5d99562582059d7ee46ffef8..53808a6ebc107dea7b852905a00470edf59560a8 100644 (file)
@@ -436,6 +436,114 @@ install_qa_check() {
                PORTAGE_QUIET=${tmp_quiet}
        fi
 
+       if [[ ${CHOST} == *-aix* ]] && ! hasq binchecks ${RESTRICT}; then
+               local tmp_quiet=${PORTAGE_QUIET}
+               local queryline deplib
+               local insecure_rpath_list= undefined_symbols_list=
+
+               # display warnings when using stricter because we die afterwards
+               if has stricter ${FEATURES} ; then
+                       unset PORTAGE_QUIET
+               fi
+
+               rm -f "${PORTAGE_BUILDDIR}"/build-info/NEEDED.XCOFF.1
+               find "${ED}" -not -type d -exec \
+                       "${EPREFIX}/usr/bin/aixdll-query" '{}' FILE MEMBER FLAGS FORMAT RUNPATH DEPLIBS ';' \
+                       > "${T}"/needed 2>/dev/null
+
+               # Symlinking archive libraries is not a good idea on aix,
+               # as there is nothing like "soname" on pure filesystem level.
+               # So we create a copy instead of the symlink.
+               local prev_FILE=
+               while read queryline
+               do
+                       local FILE= MEMBER= FLAGS= FORMAT= RUNPATH= DEPLIBS=
+                       eval ${queryline}
+
+                       if [[ ${prev_FILE} != ${FILE} ]]; then
+                               prev_FILE=${FILE}
+                               if [[ -n ${MEMBER} || " ${FLAGS} " == *" SHROBJ "* ]] && [[ -h ${FILE} ]]; then
+                                       local target=$(readlink "${FILE}")
+                                       if [[ ${target} == /* ]]; then
+                                               target=${D}${target}
+                                       else
+                                               target=${FILE%/*}/${target}
+                                       fi
+                                       rm -f "${FILE}" || die "cannot prune ${FILE#${ED}}"
+                                       cp -f "${target}" "${FILE}" || die "cannot copy ${target#${ED}} to ${FILE#${ED}}"
+                               fi
+                       fi
+               done <"${T}"/needed
+
+               prev_FILE=
+               while read queryline
+               do
+                       local FILE= MEMBER= FLAGS= FORMAT= RUNPATH= DEPLIBS=
+                       eval ${queryline}
+
+                       if [[ ${prev_FILE} != ${FILE} ]]; then
+                               # Save NEEDED information for the archive library stub
+                               echo "${FORMAT##* }${FORMAT%%-*};${FILE#${D%/}};${FILE##*/};;" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.XCOFF.1
+                       fi
+
+                       # Make sure we disallow insecure RUNPATH's
+                       # Don't want paths that point to the tree where the package was built
+                       # (older, broken libtools would do this).  Also check for null paths
+                       # because the loader will search $PWD when it finds null paths.
+                       # And we really want absolute paths only.
+                       if [[ -n $(echo ":${RUNPATH}:" | grep -E "(${PORTAGE_BUILDDIR}|::|:[^/])") ]]; then
+                               insecure_rpath_list="${insecure_rpath_list}\n${FILE}"
+                       fi
+
+                       # Although we do have runtime linking, we don't want undefined symbols.
+                       # AIX does indicate this by needing either '.' or '..'
+                       local needed=${FILE##*/}
+                       for deplib in ${DEPLIBS}; do
+                               eval deplib=${deplib}
+                               if [[ ${deplib} == '.' || ${deplib} == '..' ]]; then
+                                       undefined_symbols_list="${undefined_symbols_list}\n${FILE}"
+                               else
+                                       needed="${needed},${deplib}"
+                               fi
+                       done
+
+                       FILE=${FILE#${D%/}}
+
+                       [[ -n ${MEMBER} ]] && MEMBER="[${MEMBER}]"
+                       # Save NEEDED information
+                       echo "${FORMAT##* }${FORMAT%%-*};${FILE}${MEMBER};${FILE##*/}${MEMBER};${RUNPATH};${needed}" >> "${PORTAGE_BUILDDIR}"/build-info/NEEDED.XCOFF.1
+               done <"${T}"/needed
+
+               if [[ -n ${undefined_symbols_list} ]]; then
+                       vecho -ne '\a\n'
+                       eqawarn "QA Notice: The following files contain undefined symbols."
+                       eqawarn " Please file a bug about this at http://bugs.gentoo.org/"
+                       eqawarn " with 'prefix' as the maintaining herd of the package."
+                       eqawarn "${undefined_symbols_list}"
+                       vecho -ne '\a\n'
+               fi
+
+               if [[ -n ${insecure_rpath_list} ]] ; then
+                       vecho -ne '\a\n'
+                       eqawarn "QA Notice: The following files contain insecure RUNPATH's"
+                       eqawarn " Please file a bug about this at http://bugs.gentoo.org/"
+                       eqawarn " with 'prefix' as the maintaining herd of the package."
+                       eqawarn "${insecure_rpath_list}"
+                       vecho -ne '\a\n'
+                       if [[ -n ${x} ]] || has stricter ${FEATURES} ; then
+                               insecure_rpath=1
+                       fi
+               fi
+
+               if [[ ${insecure_rpath} -eq 1 ]] ; then
+                       die "Aborting due to serious QA concerns with RUNPATH/RPATH"
+               elif [[ -n ${die_msg} ]] && has stricter ${FEATURES} ; then
+                       die "Aborting due to QA concerns: ${die_msg}"
+               fi
+
+               PORTAGE_QUIET=${tmp_quiet}
+       fi
+
        local unsafe_files=$(find "${ED}" -type f '(' -perm -2002 -o -perm -4002 ')')
        if [[ -n ${unsafe_files} ]] ; then
                eqawarn "QA Notice: Unsafe files detected (set*id and world writable)"
@@ -862,6 +970,167 @@ postinst_bsdflags() {
        mtree -e -p "${EROOT}" -U -k flags < "${T}/bsdflags.mtree" &> /dev/null
 }
 
+preinst_aix() {
+       if [[ ${CHOST} != *-aix* ]] || hasq binchecks ${RESTRICT}; then
+               return 0
+       fi
+       local ar strip
+       if type ${CHOST}-ar >/dev/null 2>&1 && type ${CHOST}-strip >/dev/null 2>&1; then
+               ar=${CHOST}-ar
+               strip=${CHOST}-strip
+       elif [[ ${CBUILD} == "${CHOST}" ]] && type ar >/dev/null 2>&1 && type strip >/dev/null 2>&1; then
+               ar=ar
+               strip=strip
+       elif [[ -x /usr/ccs/bin/ar && -x /usr/ccs/bin/strip ]]; then
+               ar=/usr/ccs/bin/ar
+               strip=/usr/ccs/bin/strip
+       else
+               die "cannot find where to use 'ar' and 'strip' from"
+       fi
+
+       local archive prev_archive= archives=()
+       while read archive; do
+               archive=${archive#*;}
+               archive=${archive%%;*}
+               if [[ ${archive} == *'['*']' ]]; then
+                       archive=${archive%[*}
+                       [[ ${prev_archive} == ${archive} ]] && continue
+                       prev_archive=${archive}
+                       archives[${#archives[@]}]=${archive#${EPREFIX}/}
+               fi
+       done < "${PORTAGE_BUILDDIR}"/build-info/NEEDED.XCOFF.1
+       unset prev_archive
+
+       local libmetadir members preservemembers member contentmember chmod400files=() prunedirs=()
+       for archive in "${archives[@]}"; do
+               libmetadir=${ED}${archive%/*}/.${archive##*/}
+               [[ ! -e ${libmetadir} ]] || rm -rf "${libmetadir}" || die "cannot prune ${libmetadir}"
+               mkdir "${libmetadir}" || die "cannot create ${libmetadir}"
+               pushd "${libmetadir}" >/dev/null || die "cannot cd to ${libmetadir}"
+               ${ar} -X32_64 -x "${ED}${archive}" || die "cannot unpack ${archive}"
+               members=" $(echo *) "
+               preservemembers=
+               if [[ -e ${EROOT}${archive} ]]; then
+                       for member in $(${ar} -X32_64 -t "${EROOT}${archive}"); do
+                               [[ ${members} == *" ${member} "* ]] && continue
+                               preservemembers="${preservemembers} ${member}"
+                       done
+                       if [[ -n ${preservemembers} ]]; then
+                               einfo "preserving (on spec)${preservemembers}"
+                               ${ar} -X32_64 -x "${EROOT}${archive}" ${preservemembers} || die "cannot extract ${preservemembers} from ${EROOT}${archive}"
+                               chmod u+w ${preservemembers} || die "cannot chmod${preservedmembers}"
+                               ${strip} -X32_64 -e ${preservemembers} || die "cannot strip${preservemembers}"
+                               ${ar} -X32_64 -q "${ED}${archive}" ${preservemembers} || die "cannot update ${archive}"
+                               eend $?
+                       fi
+               fi
+               for member in ${members}; do
+                       contentmember="${archive%/*}/.${archive##*/}[${member}]"
+                       # portage does os.lstat() on merged files every now
+                       # and then, so keep stamp-files for archive members
+                       # around to get the preserve-libs feature working.
+                       {       echo "Please leave this file alone, it is an important helper"
+                               echo "for portage to implement the 'preserve-libs' feature on AIX." 
+                       } > "${ED}${contentmember}" || die "cannot create ${contentmember}"
+                       chmod400files[${#chmod400files[@]}]=${ED}${contentmember}
+               done
+               popd >/dev/null || die "cannot leave ${libmetadir}"
+               prunedirs[${#prunedirs[@]}]=${libmetadir}
+       done
+       [[ ${#chmod400files[@]} == 0 ]] ||
+       chmod 0400 "${chmod400files[@]}" || die "cannot chmod ${chmod400files[@]}"
+       [[ ${#prunedirs[@]} == 0 ]] ||
+       rm -rf "${prunedirs[@]}" || die "cannot prune ${prunedirs[@]}"
+       return 0
+}
+
+postinst_aix() {
+       if [[ ${CHOST} != *-aix* ]] || hasq binchecks ${RESTRICT}; then
+               return 0
+       fi
+       local MY_PR=${PR%r0}
+       local ar strip
+       if type ${CHOST}-ar >/dev/null 2>&1 && type ${CHOST}-strip >/dev/null 2>&1; then
+               ar=${CHOST}-ar
+               strip=${CHOST}-strip
+       elif [[ ${CBUILD} == "${CHOST}" ]] && type ar >/dev/null 2>&1 && type strip >/dev/null 2>&1; then
+               ar=ar
+               strip=strip
+       elif [[ -x /usr/ccs/bin/ar && -x /usr/ccs/bin/strip ]]; then
+               ar=/usr/ccs/bin/ar
+               strip=/usr/ccs/bin/strip
+       else
+               die "cannot find where to use 'ar' and 'strip' from"
+       fi
+
+       local member contentmember activecontentmembers= prev_archive= archive activearchives=
+       while read member; do
+               member=${member#*;} # drop "^type;"
+               member=${member%%;*} # drop ";soname;runpath;needed$"
+               [[ ${member##*/} == *'['*']' ]] || continue
+               contentmember=${member%/*}/.${member##*/}
+               activecontentmembers="${activecontentmembers}:(${contentmember}):"
+               archive=${member%[*}
+               [[ ${prev_archive} != ${archive} ]] || continue
+               prev_archive=${archive}
+               activearchives="${activearchives}:(${archive}):"
+       done < "${PORTAGE_BUILDDIR}"/build-info/NEEDED.XCOFF.1
+
+       local type allcontentmembers= all_archives=()
+       prev_archive=
+       while read type contentmember; do
+               contentmember=${contentmember% *} # drop " timestamp$"
+               contentmember=${contentmember% *} # drop " hash$"
+               [[ ${contentmember##*/} == *'['*']' ]] || continue
+               allcontentmembers="${allcontentmembers}:(${contentmember}):"
+               member=${contentmember%/.*}/${contentmember##*/.}
+               archive=${member%[*}
+               [[ ${prev_archive} != ${archive} ]] || continue
+               prev_archive=${archive}
+               all_archives[${#all_archives[@]}]=${archive}
+       done < "${EPREFIX}/var/db/pkg/${CATEGORY}/${P}${MY_PR:+-}${MY_PR}/CONTENTS"
+
+       local delmembers oldmembers libmetadir prunemembers=() prunedirs=()
+       for archive in "${all_archives[@]}"; do
+               [[ -r ${ROOT}${archive} && -w ${ROOT}${archive} ]] ||
+               chmod a+r,u+w "${ROOT}${archive}" || die "cannot chmod ${archive}"
+               delmembers= oldmembers=
+               for member in $(${ar} -X32_64 -t "${ROOT}${archive}"); do
+                       contentmember="${archive%/*}/.${archive##*/}[${member}]"
+                       if [[ ${allcontentmembers} != *":(${contentmember}):"* ]]; then
+                               # not existent any more, just drop it
+                               delmembers="${delmembers} ${member}"
+                               prunemembers[${#prunemembers[@]}]=${ROOT}${contentmember}
+                       elif [[ ${activecontentmembers} != *":(${contentmember}):"* ]]; then
+                               oldmembers="${oldmembers} ${member}"
+                       fi
+               done
+               if [[ -n ${delmembers} ]]; then
+                       einfo "dropping${delmembers}"
+                       ${ar} -X32_64 -z -o -d "${ROOT}${archive}" ${delmembers} || die "cannot remove${delmembers} from ${archive}"
+                       eend $?
+               fi
+               if [[ -n ${oldmembers} && ${activearchives} != *":(${archive}):"* ]]; then
+                       einfo "preserving (extra)${oldmembers}"
+                       libmetadir=${ROOT}${archive%/*}/.${archive##*/}
+                       [[ ! -e ${libmetadir} ]] || rm -rf "${libmetadir}" || die "cannot prune ${libmetadir}"
+                       mkdir "${libmetadir}" || die "cannot create ${libmetadir}"
+                       pushd "${libmetadir}" >/dev/null || die "cannot cd to ${libmetadir}"
+                       ${ar} -X32_64 -x "${ROOT}${archive}" ${oldmembers} || die "cannot unpack ${archive}"
+                       ${strip} -e ${oldmembers} || die "cannot strip ${oldmembers}"
+                       ${ar} -X32_64 -z -o -r "${ROOT}${archive}" ${oldmembers} || die "cannot update${oldmembers} in ${archive}"
+                       popd > /dev/null || die "cannot leave ${libmetadir}"
+                       prunedirs[${#prunedirs[@]}]=${libmetadir}
+                       eend $?
+               fi
+       done
+       [[ ${#prunedirs[@]} == 0 ]] ||
+       rm -rf "${prunedirs[@]}" || die "cannot prune ${prunedirs[@]}"
+       [[ ${#prunemembers[@]} == 0 ]] ||
+       rm -f "${prunemembers[@]}" || die "cannot prune ${contentmenbers[@]}"
+       return 0
+}
+
 preinst_mask() {
        if [ -z "${D}" ]; then
                 eerror "${FUNCNAME}: D is unset"
index 165db71b418d5c2e3de84d7f5aa50d2fec981232..6d24a8321ce82d695f165d757edc3913715d96d5 100644 (file)
@@ -5655,6 +5655,7 @@ _post_phase_cmds = {
                "install_symlink_html_docs"],
 
        "preinst" : [
+               "preinst_aix",
                "preinst_bsdflags",
                "preinst_sfperms",
                "preinst_selinux_labels",
@@ -5662,6 +5663,7 @@ _post_phase_cmds = {
                "preinst_mask"],
 
        "postinst" : [
+               "postinst_aix",
                "postinst_bsdflags"]
 }
 
index 210a1d5530d172b07c103f10c774fcf23df3eeee..c3da0fecbc60530308cc6217a95e24daa4934bf8 100644 (file)
@@ -5,7 +5,7 @@
 from __future__ import print_function
 
 __all__ = ["PreservedLibsRegistry", "LinkageMap",
-       "LinkageMapMachO", "LinkageMapPeCoff",
+       "LinkageMapMachO", "LinkageMapPeCoff", "LinkageMapXCoff"
        "vardbapi", "vartree", "dblink"] + \
        ["write_contents", "tar_contents"]
 
@@ -26,7 +26,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
 )
 
 from portage.const import CACHE_PATH, CONFIG_MEMORY_FILE, \
-       PORTAGE_PACKAGE_ATOM, PRIVATE_PATH, VDB_PATH, EPREFIX, EPREFIX_LSTRIP
+       PORTAGE_PACKAGE_ATOM, PRIVATE_PATH, VDB_PATH, EPREFIX, EPREFIX_LSTRIP, BASH_BINARY
 from portage.data import portage_gid, portage_uid, secpass
 from portage.dbapi import dbapi
 from portage.exception import CommandNotFound, \
@@ -1479,6 +1479,252 @@ class LinkageMapPeCoff(LinkageMap):
                                        arch_map[needed_soname] = soname_map
                                soname_map.consumers.add(obj_key)
 
+class LinkageMapXCoff(LinkageMap):
+
+       """Models dynamic linker dependencies."""
+
+       _needed_aux_key = "NEEDED.XCOFF.1"
+
+       class _ObjectKey(LinkageMap._ObjectKey):
+
+               def __init__(self, obj, root):
+                       LinkageMap._ObjectKey.__init__(self, obj, root)
+
+               def _generate_object_key(self, obj, root):
+                       """
+                       Generate object key for a given object.
+
+                       @param object: path to a file
+                       @type object: string (example: '/usr/bin/bar')
+                       @rtype: 2-tuple of types (long, int) if object exists. string if
+                               object does not exist.
+                       @return:
+                               1. 2-tuple of object's inode and device from a stat call, if object
+                                       exists.
+                               2. realpath of object if object does not exist.
+
+                       """
+
+                       os = _os_merge
+
+                       try:
+                               _unicode_encode(obj,
+                                       encoding=_encodings['merge'], errors='strict')
+                       except UnicodeEncodeError:
+                               # The package appears to have been merged with a 
+                               # different value of sys.getfilesystemencoding(),
+                               # so fall back to utf_8 if appropriate.
+                               try:
+                                       _unicode_encode(obj,
+                                               encoding=_encodings['fs'], errors='strict')
+                               except UnicodeEncodeError:
+                                       pass
+                               else:
+                                       os = portage.os
+
+                       abs_path = os.path.join(root, obj.lstrip(os.sep))
+                       try:
+                               object_stat = os.stat(abs_path)
+                       except OSError:
+                               # Use the realpath as the key if the file does not exists on the
+                               # filesystem.
+                               return os.path.realpath(abs_path)
+                       # Return a tuple of the device and inode, as well as the basename,
+                       # because of hardlinks the device and inode might be identical.
+                       return (object_stat.st_dev, object_stat.st_ino, os.path.basename(abs_path.rstrip(os.sep)))
+
+               def file_exists(self):
+                       """
+                       Determine if the file for this key exists on the filesystem.
+
+                       @rtype: Boolean
+                       @return:
+                               1. True if the file exists.
+                               2. False if the file does not exist or is a broken symlink.
+
+                       """
+                       return isinstance(self._key, tuple)
+
+       class _LibGraphNode(_ObjectKey):
+               __slots__ = ("alt_paths",)
+
+               def __init__(self, obj, root):
+                       LinkageMapXCoff._ObjectKey.__init__(self, obj, root)
+                       self.alt_paths = set()
+
+               def __str__(self):
+                       return str(sorted(self.alt_paths))
+
+       def rebuild(self, exclude_pkgs=None, include_file=None):
+               """
+               Raises CommandNotFound if there are preserved libs
+               and the scanelf binary is not available.
+               """
+
+               os = _os_merge
+               root = self._root
+               root_len = len(root) - 1
+               self._clear_cache()
+               self._defpath.update(getlibpaths(self._root))
+               libs = self._libs
+               obj_key_cache = self._obj_key_cache
+               obj_properties = self._obj_properties
+
+               lines = []
+
+               # Data from include_file is processed first so that it
+               # overrides any data from previously installed files.
+               if include_file is not None:
+                       lines += grabfile(include_file)
+
+               aux_keys = [self._needed_aux_key]
+               for cpv in self._dbapi.cpv_all():
+                       if exclude_pkgs is not None and cpv in exclude_pkgs:
+                               continue
+                       lines += self._dbapi.aux_get(cpv, aux_keys)[0].split('\n')
+               # Cache NEEDED.* files avoid doing excessive IO for every rebuild.
+               self._dbapi.flush_cache()
+
+               # have to call scanelf for preserved libs here as they aren't 
+               # registered in NEEDED.XCOFF.1 files
+               if self._dbapi.plib_registry and self._dbapi.plib_registry.getPreservedLibs():
+                       for items in self._dbapi.plib_registry.getPreservedLibs().values():
+                               for x in items:
+                                       args = [BASH_BINARY, "-c", ':'
+                                               + '; member="' + x + '"'
+                                               + '; archive=${member}'
+                                               + '; if [[ ${member##*/} == .*"["*"]" ]]'
+                                               + '; then member=${member%/.*}/${member##*/.}'
+                                                        + '; archive=${member%[*}'
+                                               + '; fi'
+                                               + '; member=${member#${archive}}'
+                                               + '; [[ -r ${archive} ]] || chmod a+r "${archive}"'
+                                               + '; eval $(aixdll-query "${archive}${member}" FILE MEMBER FLAGS FORMAT RUNPATH DEPLIBS)'
+                                               + '; [[ -n ${member} ]] && needed=${FILE##*/} || needed='
+                                               + '; for deplib in ${DEPLIBS}'
+                                               + '; do eval deplib=${deplib}'
+                                                  + '; if [[ ${deplib} != "." && ${deplib} != ".." ]]'
+                                                  + '; then needed="${needed}${needed:+,}${deplib}"'
+                                                  + '; fi'
+                                               + '; done'
+                                               + '; [[ -n ${MEMBER} ]] && MEMBER="[${MEMBER}]"'
+                                               + '; [[ " ${FLAGS} " == *" SHROBJ "* ]] && soname=${FILE##*/}${MEMBER} || soname='
+                                               + '; echo "${FORMAT##* }${FORMAT%%-*};${FILE#${ROOT%/}}${MEMBER};${soname};${RUNPATH};${needed}"'
+                                               + '; [[ -n ${member} ]] || echo "${FORMAT##* }${FORMAT%%-*};${FILE#${ROOT%/}};${FILE##*/};;"'
+                                       ]
+                                       try:
+                                               proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+                                       except EnvironmentError as e:
+                                               if e.errno != errno.ENOENT:
+                                                       raise
+                                               raise CommandNotFound("aixdll-query via " + argv[0])
+                                       else:
+                                               for l in proc.stdout:
+                                                       try:
+                                                               l = _unicode_decode(l,
+                                                                       encoding=_encodings['content'], errors='strict')
+                                                       except UnicodeDecodeError:
+                                                               l = _unicode_decode(l,
+                                                                       encoding=_encodings['content'], errors='replace')
+                                                               writemsg_level(_("\nError decoding characters " \
+                                                                       "returned from aixdll-query: %s\n\n") % (l,),
+                                                                       level=logging.ERROR, noiselevel=-1)
+                                                       l = l.rstrip("\n")
+                                                       if not l:
+                                                               continue
+                                                       fields = l.split(";")
+                                                       if len(fields) < 5:
+                                                               writemsg_level(_("\nWrong number of fields " \
+                                                                       "returned from aixdll-query: %s\n\n") % (l,),
+                                                                       level=logging.ERROR, noiselevel=-1)
+                                                               continue
+                                                       fields[1] = fields[1][root_len:]
+                                                       lines.append(";".join(fields))
+                                               proc.wait()
+
+               for l in lines:
+                       l = l.rstrip("\n")
+                       if not l:
+                               continue
+                       fields = l.split(";")
+                       if len(fields) < 5:
+                               writemsg_level(_("\nWrong number of fields " \
+                                       "in %s: %s\n\n") % (self._needed_aux_key, l),
+                                       level=logging.ERROR, noiselevel=-1)
+                               continue
+                       arch = fields[0]
+
+                       def as_contentmember(obj):
+                               if obj.endswith("]"):
+                                       if obj.find("/") >= 0:
+                                               return obj[:obj.rfind("/")] + "/." + obj[obj.rfind("/")+1:]
+                                       return "." + obj
+                               return obj
+
+                       obj = as_contentmember(fields[1])
+                       soname = as_contentmember(fields[2])
+                       path = set([normalize_path(x) \
+                               for x in filter(None, fields[3].replace(
+                               "${ORIGIN}", os.path.dirname(obj)).replace(
+                               "$ORIGIN", os.path.dirname(obj)).split(":"))])
+                       needed = [as_contentmember(x) for x in fields[4].split(",") if x]
+
+                       obj_key = self._obj_key(obj)
+                       indexed = True
+                       myprops = obj_properties.get(obj_key)
+                       if myprops is None:
+                               indexed = False
+                               myprops = (arch, needed, path, soname, set())
+                               obj_properties[obj_key] = myprops
+                       # All object paths are added into the obj_properties tuple.
+                       myprops[4].add(obj)
+
+                       # Don't index the same file more that once since only one
+                       # set of data can be correct and therefore mixing data
+                       # may corrupt the index (include_file overrides previously
+                       # installed).
+                       if indexed:
+                               continue
+
+                       arch_map = libs.get(arch)
+                       if arch_map is None:
+                               arch_map = {}
+                               libs[arch] = arch_map
+                       if soname:
+                               soname_map = arch_map.get(soname)
+                               if soname_map is None:
+                                       soname_map = self._soname_map_class(
+                                               providers=set(), consumers=set())
+                                       arch_map[soname] = soname_map
+                               soname_map.providers.add(obj_key)
+                       for needed_soname in needed:
+                               soname_map = arch_map.get(needed_soname)
+                               if soname_map is None:
+                                       soname_map = self._soname_map_class(
+                                               providers=set(), consumers=set())
+                                       arch_map[needed_soname] = soname_map
+                               soname_map.consumers.add(obj_key)
+
+       def getSoname(self, obj):
+               """
+               Return the soname associated with an object.
+
+               @param obj: absolute path to an object
+               @type obj: string (example: '/usr/bin/bar')
+               @rtype: string
+               @return: soname as a string
+
+               """
+               if not self._libs:
+                       self.rebuild()
+               if isinstance(obj, self._ObjectKey):
+                       obj_key = obj
+                       if obj_key not in self._obj_properties:
+                               raise KeyError("%s not in object list" % obj_key)
+                       return self._obj_properties[obj_key][3]
+               if obj not in self._obj_key_cache:
+                       raise KeyError("%s not in object list" % obj)
+               return self._obj_properties[self._obj_key_cache[obj]][3]
 
 class vardbapi(dbapi):
 
@@ -1552,6 +1798,8 @@ class vardbapi(dbapi):
                        self.linkmap = LinkageMapMachO(self)
                elif chost.find('interix') >= 0 or chost.find('winnt') >= 0:
                        self.linkmap = LinkageMapPeCoff(self)
+               elif chost.find('aix'):
+                       self.linkmap = LinkageMapXCoff(self)
                else:
                        self.linkmap = LinkageMap(self)
                self._owners = self._owners_db(self)
@@ -3698,6 +3946,8 @@ class dblink(object):
                                        node = LinkageMapMachO._LibGraphNode(path, root)
                                elif chost.find('interix') >= 0 or chost.find('winnt') >= 0:
                                        node = LinkageMapPeCoff._LibGraphNode(path, root)
+                               elif chost.find('aix') >= 0:
+                                       node = LinkageMapXCoff._LibGraphNode(path, root)
                                else:
                                        node = LinkageMap._LibGraphNode(path, root)
                                alt_path_node = lib_graph.get(node)