summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorL. E. Segovia <amy@centricular.com>2024-03-29 01:30:12 +0000
committerTim-Philipp Müller <tim@centricular.com>2024-04-02 09:53:53 +0100
commitdb49b4e59b5b286b409220985f8f2aa555aed3a5 (patch)
tree31ae1a869b876ee37ba26161490f183c2b26646b
parentf30584f417e28def0a6d714cdba13c1fff332835 (diff)
gst-plugins-rs: Fix superstripping for ELF breaking all plugins
As it turns out, superstripping was doing a complete(ly wrong) job out of the static libraries generated by rust. Using `strip` with `--keep-symbol` looked sensible, but the utility did not truly parse all the symbols and constructed a dependency chain. Instead, placeholders to the next address were generated in place of all the rodata symbols referenced in the functions to be kept. The result of this nightmare was crashes that looked completely senseless, until one checked the disassembly of the functions -- the `gst_plugin_xxx_register` function was there, but neither the call nor the parameters referenced anywhere valid in the data sections. The fix here is to perform a Clang-style "Single-Object Prelinking", which is in fact called relocatable partial linking -- meld all the objects into one, stripping the unreferenced cruft, then marking only the functions we desire as global with `objcopy`. I tried doing this with a version script, like FFmpeg, but it did not have any effect on the symbol visibility -- the `--export-dynamic-symbol-list` flag does not allow localising symbols' visibility. See: https://maskray.me/blog/2022-11-21-relocatable-linking Fixes gstreamer/gstreamer#3358 Part-of: <https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1437>
-rw-r--r--recipes/gst-plugins-rs.recipe114
1 files changed, 102 insertions, 12 deletions
diff --git a/recipes/gst-plugins-rs.recipe b/recipes/gst-plugins-rs.recipe
index d4972afb..6291a80e 100644
--- a/recipes/gst-plugins-rs.recipe
+++ b/recipes/gst-plugins-rs.recipe
@@ -185,12 +185,8 @@ class Recipe(recipe.Recipe):
libraries = [f for f in self.devel_files_list()
if f.endswith('.a')]
for f in libraries:
- if self.config.target_platform in (Platform.ANDROID, Platform.LINUX):
- shell.new_call([self.env['STRIP'], '--wildcard', '--strip-all',
- '--keep-symbol=gst_plugin_*', f], self.config.prefix, env=self.env)
- elif self.config.target_platform in (Platform.IOS, Platform.DARWIN):
- # Unlike ELF, for MachO Apple wants you to do
- # Single-Object Prelink
+ if self.config.target_platform in (Platform.IOS, Platform.DARWIN):
+ # Apple wants you to do Single-Object Prelink
source = Path(self.config.prefix) / f
# Only global symbols
@@ -276,28 +272,122 @@ class Recipe(recipe.Recipe):
# With the stripping done, all files now need to be rearchived
dest = Path(tmp) / source.name
- m.log(f"Repacking library to {dest.absolute()}", self.logfile)
+ m.log(f"Repacking library to {dest.absolute()}", self.logfile)
libtool = shutil.which("libtool")
if libtool is None:
raise FatalError(f'libtool not found')
- # (again) DO NOT split this into an array unless
+ shell.new_call([
+ libtool,
+ "-static",
+ prelinked_obj.absolute(),
+ "-o",
+ dest.absolute(),
+ ],
+ cmd_dir=tmp,
+ logfile=self.logfile,
+ )
+
+ # And now we paper over
+ os.replace(dest.absolute(), source.absolute())
+ elif self.config.target_platform in (Platform.LINUX, Platform.ANDROID):
+ # This is a very similar approach, however Clang
+ # will itself do a really bad job if one supplies
+ # a LD version script -- that'll suppress 99% of
+ # the things needed for a working .o file.
+ # The result, just like using
+ # `strip --wildcard --keep-symbol=gst_plugin_*`,
+ # is a .o that has all the symbols you want, but
+ # placeholders/duds/broken references for all unfortunate
+ # .rodata symbols referenced in the exported functions.
+ #
+ # See https://maskray.me/blog/2022-11-21-relocatable-linking
+ source = Path(self.config.prefix) / f
+
+ with tempfile.TemporaryDirectory(prefix='cerbero', dir=self.config.home_dir) as tmp:
+ # Unpack archive
+ m.log(f"Unpacking {source.absolute()} with ar", self.logfile)
+ if self.config.target_platform == Platform.ANDROID:
+ ar = shutil.which('llvm-ar',
+ path=self.config.env['ANDROID_NDK_TOOLCHAIN_BIN'])
+ else:
+ ar = shutil.which('llvm-ar')
+ if not ar:
+ ar = shutil.which('ar')
+ if ar is None:
+ raise FatalError('ar not found')
+ shell.new_call(
+ [ar, 'xv', source.absolute()], cmd_dir=tmp, logfile=self.logfile)
+
+ # Now everything is flat in the pwd
+ m.log("Performing Single-Object Prelinking", self.logfile)
+ prelinked_obj = (
+ Path(tmp) / source.name).with_suffix('.prelinked.o')
+
+ if self.config.target_platform == Platform.ANDROID:
+ ld = shutil.which("ld.lld",
+ path=self.config.env['ANDROID_NDK_TOOLCHAIN_BIN'])
+ else:
+ ld = shutil.which('ld')
+ if ld is None:
+ raise FatalError('ld not found')
+
+ # DO NOT split this into an array unless
# you wrap this into a 'sh -c' call.
# It needs the glob to be parsed by the shell!
shell.new_call(
' '.join([
- libtool,
- "-static",
- str(prelinked_obj.absolute()),
+ ld,
+ "--relocatable",
+ "--export-dynamic-symbol=gst_plugin_*",
"-o",
- str(dest.absolute()),
+ str(prelinked_obj.absolute()),
+ "*.o",
]),
cmd_dir=tmp,
logfile=self.logfile,
)
+ # WE ARE NOT DONE! ld.lld merged all the files,
+ # stripping those not referenced in the dynamic symbol
+ # glob, but we still need to hide all the Rust cruft.
+ if self.config.target_platform == Platform.ANDROID:
+ objcopy = shutil.which("llvm-objcopy",
+ path=self.config.env['ANDROID_NDK_TOOLCHAIN_BIN'])
+ else:
+ objcopy = shutil.which("llvm-objcopy")
+ if objcopy is None:
+ objcopy = shutil.which("objcopy")
+ if objcopy is None:
+ raise FatalError('objcopy not found')
+ shell.new_call(
+ [
+ objcopy,
+ "--wildcard",
+ "--keep-global-symbol=gst_plugin_*",
+ prelinked_obj.absolute(),
+ ],
+ cmd_dir=tmp,
+ logfile=self.logfile,
+ )
+
+ # With the stripping (really) done, all files now need to be rearchived
+ dest = Path(tmp) / source.name
+ m.log(f"Repacking library to {dest.absolute()}", self.logfile)
+
+ shell.new_call(
+ [
+ ar,
+ 'rs',
+ dest.absolute(),
+ prelinked_obj.absolute()
+ ],
+ cmd_dir=tmp,
+ logfile=self.logfile
+ )
+
# And now we paper over
os.replace(dest.absolute(), source.absolute())