summaryrefslogtreecommitdiff
path: root/recipes
diff options
context:
space:
mode:
authorL. E. Segovia <amy@centricular.com>2023-09-01 20:29:22 -0300
committerL. E. Segovia <amy@centricular.com>2023-12-03 11:47:37 -0300
commit5a227a026b3c5660c7142108214333633119b359 (patch)
tree2ba1347af208cf97af06542a21048150036fa257 /recipes
parent2a70df82457dcd3db8381abefb055b51a8238c89 (diff)
gst-plugins-rs: Super-strip plugins on iOS
Darwin based platform are the most strange I've ever seen. Due to their mix of "flat" symbol exports (like ELF) and strong link-time symbol resolution (like Windows), ld64 does not allow stripping symbols except at link-time. Attempting to do so will either result in `strip`'s call to `ld` complaining about undefined symbols, or a completely stubbed library. The obvious approach for this would be with -exported_symbols_list, but there's a catch: Rust does not use the linker at all for this [1]. (Neither do CMake or Meson, fwiw.) This is a good approach for staticlibs, BUT Apple provides another option which allows for sliding a module definition file in: Single-Object Prelink [2] [3]. In Xcode, what this option does is: - link world + dog into a new object file (`ld -r`) with all the flags necessary, just like a dylib - then pass the file over to `libtool -static` (with extra, Xcode-specific information) This commit replicates the above by: - parsing the output of `nm` to figure out which GStreamer plugin APIs are exported, and storing these into a definition file - unpacking the archive file into a temporary directory - relinking with `-exported_symbol_lists` + definition file - recreating a newly stripped static plugin with `libtool`. This commit introduces a 10:1 saving on the biggest plugin of all, libgstrswebrtc.a: - debug: 504 MB -> 93 MB - release: 144 MB -> 18 MB Do notice that I don't strip any debugging information in the relinking. [1]: https://github.com/rust-lang/rust/blob/58b4ebcdd62d82789ac50378698280006178d5fd/compiler/rustc_codegen_ssa/src/back/link.rs#L506 [2]: https://developer.apple.com/documentation/xcode/build-settings-reference [3]: https://orangejuiceliberationfront.com/hiding-symbols-in-static-libraries-with-xcode-or-cmake/ Part-of: <https://gitlab.freedesktop.org/gstreamer/cerbero/-/merge_requests/1255>
Diffstat (limited to 'recipes')
-rw-r--r--recipes/gst-plugins-rs.recipe91
1 files changed, 90 insertions, 1 deletions
diff --git a/recipes/gst-plugins-rs.recipe b/recipes/gst-plugins-rs.recipe
index 1d463c71..4792a6c5 100644
--- a/recipes/gst-plugins-rs.recipe
+++ b/recipes/gst-plugins-rs.recipe
@@ -2,6 +2,12 @@
from custom import GStreamer
from cerbero.utils import messages as m
from cerbero.utils import shell
+from pathlib import Path
+import re
+import shutil
+import subprocess
+import tempfile
+
class Recipe(recipe.Recipe):
name = 'gst-plugins-rs'
@@ -160,5 +166,88 @@ class Recipe(recipe.Recipe):
if f.endswith('.a')]
for f in libraries:
if self.config.target_platform == Platform.ANDROID:
- shell.new_call([self.env['STRIP'], '--wildcard', '--strip-all', '--keep-symbol=gst_plugin_*', f], self.config.prefix, env=self.env)
+ 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 == Platform.IOS:
+ # Unlike ELF, for MachO Apple wants you to do
+ # Single-Object Prelink
+ source = Path(f)
+
+ # Only global symbols
+ # Only symbol names
+ # Use portable output format
+ # Skip undefined symbols
+ # Write pathname of the object file
+ manifest = subprocess.check_output([self.get_llvm_tool('nm'),
+ '-gjPUA', source.absolute()],
+ universal_newlines=True,
+ text=True)
+
+ # Now we need to match the symbols to the pattern
+
+ # Here's the catch: Apple strip is silly enough to be unable to
+ # -undefined suppress a .o because of the -two_level_namespace being
+ # the default post-10.1. So we need to determine which objects have
+ # matching symbols. The rest can be safely stripped.
+
+ # The symbol listing format is as follows:
+ #  ./libgstrswebrtc.a[gstrswebrtc-3a8116aacab254c2.2u9b7sba8k2fvc9v.rcgu.o]: _gst_plugin_rswebrtc_get_desc T 500 0
+ # Field 1 has the object name between brackets.
+ # Field 2 is the symbol name.
+ symbol_pattern = re.compile('_gst_plugin_*')
+
+ with tempfile.TemporaryDirectory(prefix='cerbero', dir=self.config.home_dir) as tmp:
+ # List those symbols that will be kept
+ symbols_to_keep = set()
+
+ for line in manifest.stdout.splitlines():
+ data = line.split(' ')
+ symbol = data[1]
+
+ if symbol_pattern.match(symbol):
+ symbols_to_keep.add(symbol)
+
+ module = (
+ Path(tmp) / source.name).with_suffix('.symbols')
+
+ with module.open('w', encoding='utf-8') as f:
+ f.write('# Stripped by Cerbero\n')
+
+ for symbol in symbols_to_keep:
+ f.write(f'{symbol}\n')
+
+ m.log(f'Symbols to preserve in {source.absolute()}:')
+ for symbol in symbols_to_keep:
+ m.log(f'\t{symbol}')
+
+ # Unpack archive
+ m.log(f"Unpacking {source.absolute()} with ar")
+ shell.new_call(
+ [shutil.which('ar'), 'xv', source.absolute()], cmd_dir=tmp, logfile=self.logfile)
+
+ # Now everything is flat in the pwd
+ print('Performing Single-Object Prelinking')
+ prelinked_obj = (
+ Path(tmp) / source.name).with_suffix('.prelinked.o')
+ cmd = ' '.join([
+ shutil.which('ld'),
+ '-r',
+ '-exported_symbols_list',
+ module,
+ '-o',
+ prelinked_obj,
+ '*.o',
+ ])
+ shell.new_call(cmd, cmd_dir=tmp,
+ shell=True, logfile=self.logfile)
+
+ # With the stripping done, all files now need to be rearchived
+ dest = Path(tmp) / source.name
+ print(f'Repacking library to {dest.absolute()}')
+ shell.new_call([shutil.which('libtool'), '-static', '-o',
+ dest.absolute(), prelinked_obj.absolute()], logfile=self.logfile)
+
+ # And now we paper over
+ os.replace(dest.absolute(), f)
+
super().post_install()