elf.py revision 1516
1516N/A#!/usr/bin/python
1231N/A#
1231N/A# CDDL HEADER START
1231N/A#
1231N/A# The contents of this file are subject to the terms of the
1231N/A# Common Development and Distribution License (the "License").
1231N/A# You may not use this file except in compliance with the License.
1231N/A#
1231N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1231N/A# or http://www.opensolaris.org/os/licensing.
1231N/A# See the License for the specific language governing permissions
1231N/A# and limitations under the License.
1231N/A#
1231N/A# When distributing Covered Code, include this CDDL HEADER in each
1231N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1231N/A# If applicable, add the following below this CDDL HEADER, with the
1231N/A# fields enclosed by brackets "[]" replaced with your own identifying
1231N/A# information: Portions Copyright [yyyy] [name of copyright owner]
1231N/A#
1231N/A# CDDL HEADER END
1231N/A#
1231N/A
1231N/A#
1231N/A# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
1231N/A# Use is subject to license terms.
1231N/A#
1231N/A
1231N/Aimport os
1231N/A
1231N/Aimport pkg.elf as elf
1231N/Aimport pkg.flavor.base as base
1231N/A
1500N/Afrom pkg.portable import PD_LOCAL_PATH
1500N/A
1231N/Aclass BadElfFile(base.DependencyAnalysisError):
1231N/A """Exception that is raised when the elf dependency checker is given
1231N/A a file that errors when it tries to get the dynamic section from the
1231N/A file."""
1231N/A
1231N/A def __init__(self, fp, ex):
1231N/A base.DependencyAnalysisError.__init__(self)
1231N/A self.fp = fp
1231N/A self.ex = ex
1231N/A
1231N/A def __str__(self):
1231N/A return _("%s had this elf error:%s") % (self.fp, self.ex)
1231N/A
1231N/Aclass UnsupportedDynamicToken(base.DependencyAnalysisError):
1231N/A """Exception that is used for elf dependencies which have a dynamic
1231N/A token in their path that we're unable to decode."""
1231N/A
1231N/A def __init__(self, file_path, run_path, token):
1231N/A base.DependencyAnalysisError.__init__(self)
1231N/A self.fp = file_path
1231N/A self.rp = run_path
1231N/A self.tok = token
1231N/A
1231N/A def __str__(self):
1231N/A return _("%s had this token, %s, in its run path:%s. We are "
1231N/A "unable to handle this token at this time.") % \
1293N/A (self.fp, self.tok, self.rp)
1231N/A
1231N/A
1231N/Aclass ElfDependency(base.MultiplePathDependency):
1231N/A """Class representing a dependency from one file to another library
1231N/A as determined by elf."""
1231N/A
1231N/A def __init__(self, *args, **kwargs):
1231N/A self.err_type = self.ERROR
1231N/A attrs = kwargs.get("attrs", {})
1231N/A attrs["%s.type" % self.DEPEND_DEBUG_PREFIX] = "elf"
1231N/A
1231N/A base.MultiplePathDependency.__init__(self, attrs=attrs, *args,
1231N/A **kwargs)
1231N/A
1231N/A def is_error(self):
1231N/A return self.err_type == self.ERROR
1231N/A
1231N/A def resolve_internal(self, delivered_base_names, **kwargs):
1231N/A """Checks whether this dependency has been delivered. If the
1231N/A full path has not been delivered, check whether the base name
1231N/A has. If it has, it's likely that the run path is being set
1231N/A externally. Report a warning, but not an error in this case."""
1231N/A err, vars = base.MultiplePathDependency.resolve_internal(self,
1231N/A delivered_base_names=delivered_base_names, **kwargs)
1231N/A # If the none of the paths pointed to a file with the desired
1231N/A # basename, but a file with that basename was delivered by this
1231N/A # package, then treat the dependency as a warning instead of
1231N/A # an error. The failure to find the path to the right file
1231N/A # may be due to the library search path being set outside the
1231N/A # file that generates the dependency.
1231N/A if err == self.ERROR and vars is None and \
1231N/A self.base_name in delivered_base_names:
1231N/A self.err_type = self.WARNING
1231N/A self.attrs["%s.severity" % self.DEPEND_DEBUG_PREFIX] =\
1231N/A "warning"
1231N/A return self.WARNING, self.get_var_diff(
1231N/A delivered_base_names[self.base_name])
1231N/A else:
1231N/A return err, vars
1231N/A
1231N/A def __repr__(self):
1231N/A return "ElfDep(%s, %s, %s, %s)" % (self.action, self.base_name,
1231N/A self.run_paths, self.pkg_vars)
1231N/A
1231N/Adef process_elf_dependencies(action, proto_dir, pkg_vars, **kwargs):
1231N/A """Given a file action and proto directory, produce the elf dependencies
1231N/A for that file."""
1231N/A
1231N/A if not action.name == "file":
1231N/A return []
1231N/A
1231N/A installed_path = action.attrs[action.key_attr]
1231N/A
1500N/A proto_file = action.attrs[PD_LOCAL_PATH]
1231N/A
1231N/A if not os.path.exists(proto_file):
1231N/A raise base.MissingFile(proto_file)
1231N/A
1231N/A if not elf.is_elf_object(proto_file):
1231N/A return []
1231N/A
1231N/A try:
1231N/A ei = elf.get_info(proto_file)
1231N/A ed = elf.get_dynamic(proto_file)
1231N/A except elf.ElfError, e:
1231N/A raise BadElfFile(proto_file, e)
1231N/A deps = [
1231N/A d[0]
1231N/A for d in ed.get("deps", [])
1231N/A ]
1231N/A rp = ed.get("runpath", "").split(":")
1231N/A if len(rp) == 1 and rp[0] == "":
1231N/A rp = []
1231N/A
1231N/A rp = [
1384N/A os.path.normpath(p.replace("$ORIGIN",
1384N/A os.path.join("/", os.path.dirname(installed_path))))
1231N/A for p in rp
1231N/A ]
1231N/A
1231N/A kernel64 = None
1231N/A
1231N/A # For kernel modules, default path resolution is /platform/<platform>,
1231N/A # /kernel, /usr/kernel. But how do we know what <platform> would be for
1231N/A # a given module? Does it do fallbacks to, say, sun4u?
1231N/A if installed_path.startswith("kernel") or \
1231N/A installed_path.startswith("usr/kernel") or \
1231N/A (installed_path.startswith("platform") and \
1231N/A installed_path.split("/")[2] == "kernel"):
1231N/A if rp:
1231N/A raise RuntimeError("RUNPATH set for kernel module "
1293N/A "(%s): %s" % (installed_path, rp))
1231N/A # Add this platform to the search path.
1293N/A if installed_path.startswith("platform"):
1293N/A rp.append("/platform/%s/kernel" %
1293N/A installed_path.split("/")[1])
1231N/A # Default kernel search path
1231N/A rp.extend(("/kernel", "/usr/kernel"))
1231N/A # What subdirectory should we look in for 64-bit kernel modules?
1231N/A if ei["bits"] == 64:
1231N/A if ei["arch"] == "i386":
1231N/A kernel64 = "amd64"
1231N/A elif ei["arch"] == "sparc":
1231N/A kernel64 = "sparcv9"
1231N/A else:
1231N/A raise RuntimeError("Unknown arch:%s" %
1231N/A ei["arch"])
1231N/A else:
1231N/A if "/lib" not in rp:
1231N/A rp.append("/lib")
1231N/A if "/usr/lib" not in rp:
1231N/A rp.append("/usr/lib")
1231N/A
1231N/A res = []
1231N/A elist = []
1231N/A
1231N/A for p in rp:
1231N/A if "$" in p:
1231N/A tok = p[p.find("$"):]
1231N/A if "/" in tok:
1231N/A tok = tok[:tok.find("/")]
1293N/A elist.append(UnsupportedDynamicToken(installed_path, p,
1293N/A tok))
1231N/A
1231N/A rp = [p for p in rp[:] if "$" not in p]
1231N/A
1231N/A return [
1231N/A ElfDependency(action, d, rp, pkg_vars, proto_dir)
1231N/A for d in deps
1231N/A ], elist