blob: 6e83f01f1488cf7c222166c30339a09931af90b1 [file] [log] [blame]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001import stat
2import mmap
3import subprocess
4
Patrick Williamsc124f4f2015-09-15 14:41:29 -05005def runstrip(arg):
6 # Function to strip a single file, called from split_and_strip_files below
7 # A working 'file' (one which works on the target architecture)
8 #
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08009 # The elftype is a bit pattern (explained in is_elf below) to tell
Patrick Williamsc124f4f2015-09-15 14:41:29 -050010 # us what type of file we're processing...
11 # 4 - executable
12 # 8 - shared library
13 # 16 - kernel module
14
Patrick Williamsc124f4f2015-09-15 14:41:29 -050015 (file, elftype, strip) = arg
16
17 newmode = None
18 if not os.access(file, os.W_OK) or os.access(file, os.R_OK):
19 origmode = os.stat(file)[stat.ST_MODE]
20 newmode = origmode | stat.S_IWRITE | stat.S_IREAD
21 os.chmod(file, newmode)
22
Brad Bishop6e60e8b2018-02-01 10:27:11 -050023 stripcmd = [strip]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080024 skip_strip = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050025 # kernel module
26 if elftype & 16:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080027 if is_kernel_module_signed(file):
28 bb.debug(1, "Skip strip on signed module %s" % file)
29 skip_strip = True
30 else:
31 stripcmd.extend(["--strip-debug", "--remove-section=.comment",
32 "--remove-section=.note", "--preserve-dates"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050033 # .so and shared library
34 elif ".so" in file and elftype & 8:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050035 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note", "--strip-unneeded"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050036 # shared or executable:
37 elif elftype & 8 or elftype & 4:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050038 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050039
Brad Bishop6e60e8b2018-02-01 10:27:11 -050040 stripcmd.append(file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050041 bb.debug(1, "runstrip: %s" % stripcmd)
42
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080043 if not skip_strip:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050044 output = subprocess.check_output(stripcmd, stderr=subprocess.STDOUT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045
46 if newmode:
47 os.chmod(file, origmode)
48
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080049# Detect .ko module by searching for "vermagic=" string
50def is_kernel_module(path):
51 with open(path) as f:
52 return mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ).find(b"vermagic=") >= 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080054# Detect if .ko module is signed
55def is_kernel_module_signed(path):
56 with open(path, "rb") as f:
57 f.seek(-28, 2)
58 module_tail = f.read()
59 return "Module signature appended" in "".join(chr(c) for c in bytearray(module_tail))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080061# Return type (bits):
62# 0 - not elf
63# 1 - ELF
64# 2 - stripped
65# 4 - executable
66# 8 - shared library
67# 16 - kernel module
68def is_elf(path):
69 exec_type = 0
70 result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
71
72 if "ELF" in result:
73 exec_type |= 1
74 if "not stripped" not in result:
75 exec_type |= 2
76 if "executable" in result:
77 exec_type |= 4
78 if "shared" in result:
79 exec_type |= 8
80 if "relocatable" in result:
81 if path.endswith(".ko") and path.find("/lib/modules/") != -1 and is_kernel_module(path):
82 exec_type |= 16
83 return (path, exec_type)
84
85def is_static_lib(path):
86 if path.endswith('.a') and not os.path.islink(path):
87 with open(path, 'rb') as fh:
88 # The magic must include the first slash to avoid
89 # matching golang static libraries
90 magic = b'!<arch>\x0a/'
91 start = fh.read(len(magic))
92 return start == magic
93 return False
94
95def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, d, qa_already_stripped=False):
Brad Bishopd7bf8c12018-02-25 22:55:05 -050096 """
97 Strip executable code (like executables, shared libraries) _in_place_
98 - Based on sysroot_strip in staging.bbclass
99 :param dstdir: directory in which to strip files
100 :param strip_cmd: Strip command (usually ${STRIP})
101 :param libdir: ${libdir} - strip .so files in this directory
102 :param base_libdir: ${base_libdir} - strip .so files in this directory
103 :param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
104 This is for proper logging and messages only.
105 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800106 import stat, errno, oe.path, oe.utils
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500107
108 elffiles = {}
109 inodes = {}
110 libdir = os.path.abspath(dstdir + os.sep + libdir)
111 base_libdir = os.path.abspath(dstdir + os.sep + base_libdir)
112 exec_mask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
113 #
114 # First lets figure out all of the files we may have to process
115 #
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800116 checkelf = []
117 inodecache = {}
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500118 for root, dirs, files in os.walk(dstdir):
119 for f in files:
120 file = os.path.join(root, f)
121
122 try:
123 ltarget = oe.path.realpath(file, dstdir, False)
124 s = os.lstat(ltarget)
125 except OSError as e:
126 (err, strerror) = e.args
127 if err != errno.ENOENT:
128 raise
129 # Skip broken symlinks
130 continue
131 if not s:
132 continue
133 # Check its an excutable
134 if s[stat.ST_MODE] & exec_mask \
135 or ((file.startswith(libdir) or file.startswith(base_libdir)) and ".so" in f) \
136 or file.endswith('.ko'):
137 # If it's a symlink, and points to an ELF file, we capture the readlink target
138 if os.path.islink(file):
139 continue
140
141 # It's a file (or hardlink), not a link
142 # ...but is it ELF, and is it already stripped?
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800143 checkelf.append(file)
144 inodecache[file] = s.st_ino
145 results = oe.utils.multiprocess_launch(is_elf, checkelf, d)
146 for (file, elf_file) in results:
147 #elf_file = is_elf(file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500148 if elf_file & 1:
149 if elf_file & 2:
150 if qa_already_stripped:
151 bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dstdir):], pn))
152 else:
153 bb.warn("File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dstdir):], pn))
154 continue
155
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800156 if inodecache[file] in inodes:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500157 os.unlink(file)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800158 os.link(inodes[inodecache[file]], file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500159 else:
160 # break hardlinks so that we do not strip the original.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800161 inodes[inodecache[file]] = file
162 bb.utils.break_hardlinks(file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500163 elffiles[file] = elf_file
164
165 #
166 # Now strip them (in parallel)
167 #
168 sfiles = []
169 for file in elffiles:
170 elf_file = int(elffiles[file])
171 sfiles.append((file, elf_file, strip_cmd))
172
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800173 oe.utils.multiprocess_launch(runstrip, sfiles, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500174
175
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500176def file_translate(file):
177 ft = file.replace("@", "@at@")
178 ft = ft.replace(" ", "@space@")
179 ft = ft.replace("\t", "@tab@")
180 ft = ft.replace("[", "@openbrace@")
181 ft = ft.replace("]", "@closebrace@")
182 ft = ft.replace("_", "@underscore@")
183 return ft
184
185def filedeprunner(arg):
186 import re, subprocess, shlex
187
188 (pkg, pkgfiles, rpmdeps, pkgdest) = arg
189 provides = {}
190 requires = {}
191
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500192 file_re = re.compile(r'\s+\d+\s(.*)')
193 dep_re = re.compile(r'\s+(\S)\s+(.*)')
194 r = re.compile(r'[<>=]+\s+\S*')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195
196 def process_deps(pipe, pkg, pkgdest, provides, requires):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500197 file = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500198 for line in pipe.split("\n"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 m = file_re.match(line)
201 if m:
202 file = m.group(1)
203 file = file.replace(pkgdest + "/" + pkg, "")
204 file = file_translate(file)
205 continue
206
207 m = dep_re.match(line)
208 if not m or not file:
209 continue
210
211 type, dep = m.groups()
212
213 if type == 'R':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500214 i = requires
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500215 elif type == 'P':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500216 i = provides
217 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500218 continue
219
220 if dep.startswith("python("):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500221 continue
222
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500223 # Ignore all perl(VMS::...) and perl(Mac::...) dependencies. These
224 # are typically used conditionally from the Perl code, but are
225 # generated as unconditional dependencies.
226 if dep.startswith('perl(VMS::') or dep.startswith('perl(Mac::'):
227 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500229 # Ignore perl dependencies on .pl files.
230 if dep.startswith('perl(') and dep.endswith('.pl)'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500231 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500232
233 # Remove perl versions and perl module versions since they typically
234 # do not make sense when used as package versions.
235 if dep.startswith('perl') and r.search(dep):
236 dep = dep.split()[0]
237
238 # Put parentheses around any version specifications.
239 dep = r.sub(r'(\g<0>)',dep)
240
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241 if file not in i:
242 i[file] = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500243 i[file].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500244
245 return provides, requires
246
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500247 output = subprocess.check_output(shlex.split(rpmdeps) + pkgfiles, stderr=subprocess.STDOUT).decode("utf-8")
248 provides, requires = process_deps(output, pkg, pkgdest, provides, requires)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500249
250 return (pkg, provides, requires)
251
252
253def read_shlib_providers(d):
254 import re
255
256 shlib_provider = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500257 shlibs_dirs = d.getVar('SHLIBSDIRS').split()
Brad Bishop19323692019-04-05 15:28:33 -0400258 list_re = re.compile(r'^(.*)\.list$')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500259 # Go from least to most specific since the last one found wins
260 for dir in reversed(shlibs_dirs):
261 bb.debug(2, "Reading shlib providers in %s" % (dir))
262 if not os.path.exists(dir):
263 continue
264 for file in os.listdir(dir):
265 m = list_re.match(file)
266 if m:
267 dep_pkg = m.group(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600268 try:
269 fd = open(os.path.join(dir, file))
270 except IOError:
271 # During a build unrelated shlib files may be deleted, so
272 # handle files disappearing between the listdirs and open.
273 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500274 lines = fd.readlines()
275 fd.close()
276 for l in lines:
277 s = l.strip().split(":")
278 if s[0] not in shlib_provider:
279 shlib_provider[s[0]] = {}
280 shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
281 return shlib_provider
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500282
283
284def npm_split_package_dirs(pkgdir):
285 """
286 Work out the packages fetched and unpacked by BitBake's npm fetcher
287 Returns a dict of packagename -> (relpath, package.json) ordered
288 such that it is suitable for use in PACKAGES and FILES
289 """
290 from collections import OrderedDict
291 import json
292 packages = {}
293 for root, dirs, files in os.walk(pkgdir):
294 if os.path.basename(root) == 'node_modules':
295 for dn in dirs:
296 relpth = os.path.relpath(os.path.join(root, dn), pkgdir)
297 pkgitems = ['${PN}']
298 for pathitem in relpth.split('/'):
299 if pathitem == 'node_modules':
300 continue
301 pkgitems.append(pathitem)
302 pkgname = '-'.join(pkgitems).replace('_', '-')
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500303 pkgname = pkgname.replace('@', '')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500304 pkgfile = os.path.join(root, dn, 'package.json')
305 data = None
306 if os.path.exists(pkgfile):
307 with open(pkgfile, 'r') as f:
308 data = json.loads(f.read())
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600309 packages[pkgname] = (relpth, data)
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500310 # We want the main package for a module sorted *after* its subpackages
311 # (so that it doesn't otherwise steal the files for the subpackage), so
312 # this is a cheap way to do that whilst still having an otherwise
313 # alphabetical sort
314 return OrderedDict((key, packages[key]) for key in sorted(packages, key=lambda pkg: pkg + '~'))