blob: dd700cbb0c93e8be07f18165486ac82ac40d4ac2 [file] [log] [blame]
Brad Bishopc342db32019-05-15 21:57:59 -04001#
2# SPDX-License-Identifier: GPL-2.0-only
3#
4
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08005import stat
6import mmap
7import subprocess
8
Patrick Williamsc124f4f2015-09-15 14:41:29 -05009def runstrip(arg):
10 # Function to strip a single file, called from split_and_strip_files below
11 # A working 'file' (one which works on the target architecture)
12 #
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080013 # The elftype is a bit pattern (explained in is_elf below) to tell
Patrick Williamsc124f4f2015-09-15 14:41:29 -050014 # us what type of file we're processing...
15 # 4 - executable
16 # 8 - shared library
17 # 16 - kernel module
18
Patrick Williamsc124f4f2015-09-15 14:41:29 -050019 (file, elftype, strip) = arg
20
21 newmode = None
22 if not os.access(file, os.W_OK) or os.access(file, os.R_OK):
23 origmode = os.stat(file)[stat.ST_MODE]
24 newmode = origmode | stat.S_IWRITE | stat.S_IREAD
25 os.chmod(file, newmode)
26
Brad Bishop6e60e8b2018-02-01 10:27:11 -050027 stripcmd = [strip]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080028 skip_strip = False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050029 # kernel module
30 if elftype & 16:
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080031 if is_kernel_module_signed(file):
32 bb.debug(1, "Skip strip on signed module %s" % file)
33 skip_strip = True
34 else:
35 stripcmd.extend(["--strip-debug", "--remove-section=.comment",
36 "--remove-section=.note", "--preserve-dates"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037 # .so and shared library
38 elif ".so" in file and elftype & 8:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050039 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note", "--strip-unneeded"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040 # shared or executable:
41 elif elftype & 8 or elftype & 4:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050042 stripcmd.extend(["--remove-section=.comment", "--remove-section=.note"])
Patrick Williamsc124f4f2015-09-15 14:41:29 -050043
Brad Bishop6e60e8b2018-02-01 10:27:11 -050044 stripcmd.append(file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050045 bb.debug(1, "runstrip: %s" % stripcmd)
46
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080047 if not skip_strip:
Brad Bishop6e60e8b2018-02-01 10:27:11 -050048 output = subprocess.check_output(stripcmd, stderr=subprocess.STDOUT)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050049
50 if newmode:
51 os.chmod(file, origmode)
52
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080053# Detect .ko module by searching for "vermagic=" string
54def is_kernel_module(path):
55 with open(path) as f:
56 return mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ).find(b"vermagic=") >= 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -050057
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080058# Detect if .ko module is signed
59def is_kernel_module_signed(path):
60 with open(path, "rb") as f:
61 f.seek(-28, 2)
62 module_tail = f.read()
63 return "Module signature appended" in "".join(chr(c) for c in bytearray(module_tail))
Patrick Williamsc124f4f2015-09-15 14:41:29 -050064
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080065# Return type (bits):
66# 0 - not elf
67# 1 - ELF
68# 2 - stripped
69# 4 - executable
70# 8 - shared library
71# 16 - kernel module
72def is_elf(path):
73 exec_type = 0
74 result = subprocess.check_output(["file", "-b", path], stderr=subprocess.STDOUT).decode("utf-8")
75
76 if "ELF" in result:
77 exec_type |= 1
78 if "not stripped" not in result:
79 exec_type |= 2
80 if "executable" in result:
81 exec_type |= 4
82 if "shared" in result:
83 exec_type |= 8
84 if "relocatable" in result:
85 if path.endswith(".ko") and path.find("/lib/modules/") != -1 and is_kernel_module(path):
86 exec_type |= 16
87 return (path, exec_type)
88
89def is_static_lib(path):
90 if path.endswith('.a') and not os.path.islink(path):
91 with open(path, 'rb') as fh:
92 # The magic must include the first slash to avoid
93 # matching golang static libraries
94 magic = b'!<arch>\x0a/'
95 start = fh.read(len(magic))
96 return start == magic
97 return False
98
99def strip_execs(pn, dstdir, strip_cmd, libdir, base_libdir, d, qa_already_stripped=False):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500100 """
101 Strip executable code (like executables, shared libraries) _in_place_
102 - Based on sysroot_strip in staging.bbclass
103 :param dstdir: directory in which to strip files
104 :param strip_cmd: Strip command (usually ${STRIP})
105 :param libdir: ${libdir} - strip .so files in this directory
106 :param base_libdir: ${base_libdir} - strip .so files in this directory
107 :param qa_already_stripped: Set to True if already-stripped' in ${INSANE_SKIP}
108 This is for proper logging and messages only.
109 """
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800110 import stat, errno, oe.path, oe.utils
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500111
112 elffiles = {}
113 inodes = {}
114 libdir = os.path.abspath(dstdir + os.sep + libdir)
115 base_libdir = os.path.abspath(dstdir + os.sep + base_libdir)
116 exec_mask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
117 #
118 # First lets figure out all of the files we may have to process
119 #
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800120 checkelf = []
121 inodecache = {}
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500122 for root, dirs, files in os.walk(dstdir):
123 for f in files:
124 file = os.path.join(root, f)
125
126 try:
127 ltarget = oe.path.realpath(file, dstdir, False)
128 s = os.lstat(ltarget)
129 except OSError as e:
130 (err, strerror) = e.args
131 if err != errno.ENOENT:
132 raise
133 # Skip broken symlinks
134 continue
135 if not s:
136 continue
137 # Check its an excutable
138 if s[stat.ST_MODE] & exec_mask \
139 or ((file.startswith(libdir) or file.startswith(base_libdir)) and ".so" in f) \
140 or file.endswith('.ko'):
141 # If it's a symlink, and points to an ELF file, we capture the readlink target
142 if os.path.islink(file):
143 continue
144
145 # It's a file (or hardlink), not a link
146 # ...but is it ELF, and is it already stripped?
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800147 checkelf.append(file)
148 inodecache[file] = s.st_ino
149 results = oe.utils.multiprocess_launch(is_elf, checkelf, d)
150 for (file, elf_file) in results:
151 #elf_file = is_elf(file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500152 if elf_file & 1:
153 if elf_file & 2:
154 if qa_already_stripped:
155 bb.note("Skipping file %s from %s for already-stripped QA test" % (file[len(dstdir):], pn))
156 else:
157 bb.warn("File '%s' from %s was already stripped, this will prevent future debugging!" % (file[len(dstdir):], pn))
158 continue
159
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800160 if inodecache[file] in inodes:
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500161 os.unlink(file)
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800162 os.link(inodes[inodecache[file]], file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500163 else:
164 # break hardlinks so that we do not strip the original.
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800165 inodes[inodecache[file]] = file
166 bb.utils.break_hardlinks(file)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500167 elffiles[file] = elf_file
168
169 #
170 # Now strip them (in parallel)
171 #
172 sfiles = []
173 for file in elffiles:
174 elf_file = int(elffiles[file])
175 sfiles.append((file, elf_file, strip_cmd))
176
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800177 oe.utils.multiprocess_launch(runstrip, sfiles, d)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500178
179
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500180def file_translate(file):
181 ft = file.replace("@", "@at@")
182 ft = ft.replace(" ", "@space@")
183 ft = ft.replace("\t", "@tab@")
184 ft = ft.replace("[", "@openbrace@")
185 ft = ft.replace("]", "@closebrace@")
186 ft = ft.replace("_", "@underscore@")
187 return ft
188
189def filedeprunner(arg):
190 import re, subprocess, shlex
191
192 (pkg, pkgfiles, rpmdeps, pkgdest) = arg
193 provides = {}
194 requires = {}
195
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 file_re = re.compile(r'\s+\d+\s(.*)')
197 dep_re = re.compile(r'\s+(\S)\s+(.*)')
198 r = re.compile(r'[<>=]+\s+\S*')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199
200 def process_deps(pipe, pkg, pkgdest, provides, requires):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500201 file = None
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500202 for line in pipe.split("\n"):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500203
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500204 m = file_re.match(line)
205 if m:
206 file = m.group(1)
207 file = file.replace(pkgdest + "/" + pkg, "")
208 file = file_translate(file)
209 continue
210
211 m = dep_re.match(line)
212 if not m or not file:
213 continue
214
215 type, dep = m.groups()
216
217 if type == 'R':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500218 i = requires
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500219 elif type == 'P':
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500220 i = provides
221 else:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500222 continue
223
224 if dep.startswith("python("):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500225 continue
226
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500227 # Ignore all perl(VMS::...) and perl(Mac::...) dependencies. These
228 # are typically used conditionally from the Perl code, but are
229 # generated as unconditional dependencies.
230 if dep.startswith('perl(VMS::') or dep.startswith('perl(Mac::'):
231 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500232
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500233 # Ignore perl dependencies on .pl files.
234 if dep.startswith('perl(') and dep.endswith('.pl)'):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 continue
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500236
237 # Remove perl versions and perl module versions since they typically
238 # do not make sense when used as package versions.
239 if dep.startswith('perl') and r.search(dep):
240 dep = dep.split()[0]
241
242 # Put parentheses around any version specifications.
243 dep = r.sub(r'(\g<0>)',dep)
244
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500245 if file not in i:
246 i[file] = []
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500247 i[file].append(dep)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500248
249 return provides, requires
250
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500251 output = subprocess.check_output(shlex.split(rpmdeps) + pkgfiles, stderr=subprocess.STDOUT).decode("utf-8")
252 provides, requires = process_deps(output, pkg, pkgdest, provides, requires)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500253
254 return (pkg, provides, requires)
255
256
257def read_shlib_providers(d):
258 import re
259
260 shlib_provider = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500261 shlibs_dirs = d.getVar('SHLIBSDIRS').split()
Brad Bishop19323692019-04-05 15:28:33 -0400262 list_re = re.compile(r'^(.*)\.list$')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500263 # Go from least to most specific since the last one found wins
264 for dir in reversed(shlibs_dirs):
265 bb.debug(2, "Reading shlib providers in %s" % (dir))
266 if not os.path.exists(dir):
267 continue
Brad Bishop08902b02019-08-20 09:16:51 -0400268 for file in sorted(os.listdir(dir)):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500269 m = list_re.match(file)
270 if m:
271 dep_pkg = m.group(1)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600272 try:
273 fd = open(os.path.join(dir, file))
274 except IOError:
275 # During a build unrelated shlib files may be deleted, so
276 # handle files disappearing between the listdirs and open.
277 continue
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500278 lines = fd.readlines()
279 fd.close()
280 for l in lines:
281 s = l.strip().split(":")
282 if s[0] not in shlib_provider:
283 shlib_provider[s[0]] = {}
284 shlib_provider[s[0]][s[1]] = (dep_pkg, s[2])
285 return shlib_provider