5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
24 # Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
30 import pkg
.flavor
.base
as base
32 from pkg
.portable
import PD_LOCAL_PATH
, PD_PROTO_DIR
, PD_DEFAULT_RUNPATH
34 class BadElfFile(base
.DependencyAnalysisError
):
35 """Exception that is raised when the elf dependency checker is given
36 a file that errors when it tries to get the dynamic section from the
39 def __init__(self
, fp
, ex
):
40 base
.DependencyAnalysisError
.__init
__(self
)
45 return _("{file} had this elf error:{err}").format(
46 file="self.fp", err
=self
.ex
)
48 class UnsupportedDynamicToken(base
.DependencyAnalysisError
):
49 """Exception that is used for elf dependencies which have a dynamic
50 token in their path that we're unable to decode."""
52 def __init__(self
, proto_path
, installed_path
, run_path
, token
):
53 base
.DependencyAnalysisError
.__init
__(self
)
55 self
.ip
= installed_path
60 return _("{pp} (which will be installed at {ip}) had this "
61 "token, {tok}, in its run path: {rp}. It is not "
62 "currently possible to automatically expand this token. "
63 "Please specify its value on the command line.").format(
67 class ElfDependency(base
.PublishingDependency
):
68 """Class representing a dependency from one file to another library
69 as determined by elf."""
71 def __init__(self
, action
, base_name
, run_paths
, pkg_vars
, proto_dir
):
72 self
.err_type
= self
.ERROR
74 base
.PublishingDependency
.__init
__(self
, action
,
75 [base_name
], run_paths
, pkg_vars
, proto_dir
, "elf")
78 """Because elf dependencies can be either warnings or errors,
79 it's necessary to check whether this dependency is an error
82 return self
.err_type
== self
.ERROR
84 def resolve_internal(self
, delivered_base_names
, **kwargs
):
85 """Checks whether this dependency has been delivered. If the
86 full path has not been delivered, check whether the base name
87 has. If it has, it's likely that the run path is being set
88 externally. Report a warning, but not an error in this case."""
89 err
, vars = base
.PublishingDependency
.resolve_internal(
90 self
, delivered_base_names
=delivered_base_names
, **kwargs
)
91 # If the none of the paths pointed to a file with the desired
92 # basename, but a file with that basename was delivered by this
93 # package, then treat the dependency as a warning instead of
94 # an error. The failure to find the path to the right file
95 # may be due to the library search path being set outside the
96 # file that generates the dependency.
97 if err
== self
.ERROR
and vars.is_satisfied() and \
98 self
.base_names
[0] in delivered_base_names
:
99 self
.err_type
= self
.WARNING
100 self
.attrs
["{0}.severity".format(self
.DEPEND_DEBUG_PREFIX
)] =\
102 missing_vars
= self
.get_variant_combinations()
103 missing_vars
.mark_as_satisfied(
104 delivered_base_names
[self
.base_names
[0]])
105 return self
.WARNING
, missing_vars
110 return "ElfDep({0}, {1}, {2}, {3})".format(self
.action
,
111 self
.base_names
[0], self
.run_paths
, self
.pkg_vars
)
113 def expand_variables(paths
, dyn_tok_conv
):
114 """Replace dynamic tokens, such as $PLATFORM, in the paths in the
115 paramter 'paths' with the values for that token provided in the
116 dictionary 'dyn_tok_conv.'
122 tok_start
= p
.find("$")
125 tok_end
= tok
.find("/")
128 if tok
not in dyn_tok_conv
:
129 elist
.append((p
, tok
))
132 p
[:tok_start
] + dc
+ \
133 p
[tok_start
+ len(tok
):]
134 for dc
in dyn_tok_conv
[tok
]
136 # The first dynamic token has been replaced, but
137 # more may remain so process the path again.
138 rec_res
, rec_elist
= expand_variables(np
,
141 elist
.extend(rec_elist
)
146 default_run_paths
= ["/lib", "/usr/lib"]
148 def process_elf_dependencies(action
, pkg_vars
, dyn_tok_conv
, run_paths
,
150 """Produce the elf dependencies for the file delivered in the action
153 'action' is the file action to analyze.
155 'pkg_vars' is the list of variants against which the package delivering
156 the action was published.
158 'dyn_tok_conv' is the dictionary which maps the dynamic tokens, like
159 $PLATFORM, to the values they should be expanded to.
161 'run_paths' contains the run paths which elf binaries should use.
164 if not action
.name
== "file":
167 installed_path
= action
.attrs
[action
.key_attr
]
169 proto_file
= action
.attrs
[PD_LOCAL_PATH
]
171 if not os
.path
.exists(proto_file
):
172 raise base
.MissingFile(proto_file
)
174 if not elf
.is_elf_object(proto_file
):
178 ei
= elf
.get_info(proto_file
)
179 ed
= elf
.get_dynamic(proto_file
, sha1
=False, sha256
=False)
180 except elf
.ElfError
as e
:
181 raise BadElfFile(proto_file
, e
)
184 for d
in ed
.get("deps", [])
186 rp
= ed
.get("runpath", "").split(":")
187 if len(rp
) == 1 and rp
[0] == "":
190 dyn_tok_conv
["$ORIGIN"] = [os
.path
.join("/",
191 os
.path
.dirname(installed_path
))]
193 # For kernel modules, default path resolution is /platform/<platform>,
194 # /kernel, /usr/kernel. But how do we know what <platform> would be for
195 # a given module? Does it do fallbacks to, say, sun4u?
196 if installed_path
.startswith("kernel") or \
197 installed_path
.startswith("usr/kernel") or \
198 (installed_path
.startswith("platform") and \
199 installed_path
.split("/")[2] == "kernel"):
201 raise RuntimeError("RUNPATH set for kernel module "
202 "({0}): {1}".format(installed_path
, rp
))
203 # Add this platform to the search path.
204 if installed_path
.startswith("platform"):
205 rp
.append("/platform/{0}/kernel".format(
206 installed_path
.split("/")[1]))
208 for p
in dyn_tok_conv
.get("$PLATFORM", []):
209 rp
.append("/platform/{0}/kernel".format(p
))
210 # Default kernel search path
211 rp
.extend(["/kernel", "/usr/kernel"])
213 for p
in default_run_paths
:
221 # add our detected runpaths into the user-supplied one (if any)
222 rp
= base
.insert_default_runpath(rp
, run_paths
)
224 rp
, errs
= expand_variables(rp
, dyn_tok_conv
)
227 UnsupportedDynamicToken(proto_file
, installed_path
, p
, tok
)
234 pn
, fn
= os
.path
.split(d
)
237 deppath
= os
.path
.join(p
, d
).lstrip(os
.path
.sep
)
238 # deppath includes filename; remove that.
239 head
, tail
= os
.path
.split(deppath
)
241 pathlist
.append(head
)
242 res
.append(ElfDependency(action
, fn
, pathlist
, pkg_vars
,
243 action
.attrs
[PD_PROTO_DIR
]))
244 del dyn_tok_conv
["$ORIGIN"]
245 return res
, elist
, {}