build: refactor Linux binary stripping to align with upstream (#48197)

build: refactor Linux binary stripping to align with upstream (#47932)

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: David Sanders <dsanders11@ucsbalum.com>
This commit is contained in:
trop[bot] 2025-08-28 10:51:07 -07:00 committed by GitHub
commit 9eede35fc1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 245 additions and 263 deletions

View file

@ -17,9 +17,6 @@ inputs:
is-release: is-release:
description: 'Is release build' description: 'Is release build'
required: true required: true
strip-binaries:
description: 'Strip binaries (Linux only)'
required: false
generate-symbols: generate-symbols:
description: 'Generate symbols' description: 'Generate symbols'
required: true required: true
@ -66,14 +63,6 @@ runs:
NINJA_SUMMARIZE_BUILD=1 e build NINJA_SUMMARIZE_BUILD=1 e build
cp out/Default/.ninja_log out/electron_ninja_log cp out/Default/.ninja_log out/electron_ninja_log
node electron/script/check-symlinks.js node electron/script/check-symlinks.js
- name: Strip Electron Binaries ${{ inputs.step-suffix }}
shell: bash
if: ${{ inputs.strip-binaries == 'true' }}
run: |
cd src
electron/script/copy-debug-symbols.py --target-cpu="${{ inputs.target-arch }}" --out-dir=out/Default/debug --compress
electron/script/strip-binaries.py --target-cpu="${{ inputs.target-arch }}" --verbose
electron/script/add-debug-link.py --target-cpu="${{ inputs.target-arch }}" --debug-dir=out/Default/debug
- name: Build Electron dist.zip ${{ inputs.step-suffix }} - name: Build Electron dist.zip ${{ inputs.step-suffix }}
shell: bash shell: bash
run: | run: |
@ -100,19 +89,6 @@ runs:
sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args sed $SEDOPTION '/.*builtins-pgo/d' out/Default/mksnapshot_args
sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args sed $SEDOPTION '/--turbo-profiling-input/d' out/Default/mksnapshot_args
if [ "${{ inputs.target-platform }}" = "linux" ]; then
if [ "${{ inputs.target-arch }}" = "arm" ]; then
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/mksnapshot
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x86_v8_arm/v8_context_snapshot_generator
elif [ "${{ inputs.target-arch }}" = "arm64" ]; then
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/mksnapshot
electron/script/strip-binaries.py --file $PWD/out/Default/clang_x64_v8_arm64/v8_context_snapshot_generator
else
electron/script/strip-binaries.py --file $PWD/out/Default/mksnapshot
electron/script/strip-binaries.py --file $PWD/out/Default/v8_context_snapshot_generator
fi
fi
e build --target electron:electron_mksnapshot_zip e build --target electron:electron_mksnapshot_zip
if [ "${{ inputs.target-platform }}" = "win" ]; then if [ "${{ inputs.target-platform }}" = "win" ]; then
cd out/Default cd out/Default

View file

@ -50,7 +50,6 @@ jobs:
is-release: true is-release: true
gn-build-type: release gn-build-type: release
generate-symbols: true generate-symbols: true
strip-binaries: true
upload-to-storage: ${{ inputs.upload-to-storage }} upload-to-storage: ${{ inputs.upload-to-storage }}
secrets: inherit secrets: inherit
@ -66,7 +65,6 @@ jobs:
is-release: true is-release: true
gn-build-type: release gn-build-type: release
generate-symbols: true generate-symbols: true
strip-binaries: true
upload-to-storage: ${{ inputs.upload-to-storage }} upload-to-storage: ${{ inputs.upload-to-storage }}
secrets: inherit secrets: inherit
@ -82,6 +80,5 @@ jobs:
is-release: true is-release: true
gn-build-type: release gn-build-type: release
generate-symbols: true generate-symbols: true
strip-binaries: true
upload-to-storage: ${{ inputs.upload-to-storage }} upload-to-storage: ${{ inputs.upload-to-storage }}
secrets: inherit secrets: inherit

View file

@ -48,11 +48,6 @@ on:
required: true required: true
type: string type: string
default: '0' default: '0'
strip-binaries:
description: 'Strip the binaries before release (Linux only)'
required: false
type: boolean
default: false
is-asan: is-asan:
description: 'Building the Address Sanitizer (ASan) Linux build' description: 'Building the Address Sanitizer (ASan) Linux build'
required: false required: false
@ -198,7 +193,6 @@ jobs:
artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }} artifact-platform: ${{ inputs.target-platform == 'macos' && 'darwin' || inputs.target-platform }}
is-release: '${{ inputs.is-release }}' is-release: '${{ inputs.is-release }}'
generate-symbols: '${{ inputs.generate-symbols }}' generate-symbols: '${{ inputs.generate-symbols }}'
strip-binaries: '${{ inputs.strip-binaries }}'
upload-to-storage: '${{ inputs.upload-to-storage }}' upload-to-storage: '${{ inputs.upload-to-storage }}'
is-asan: '${{ inputs.is-asan }}' is-asan: '${{ inputs.is-asan }}'
- name: Set GN_EXTRA_ARGS for MAS Build - name: Set GN_EXTRA_ARGS for MAS Build

View file

@ -44,6 +44,7 @@ if (is_mac) {
if (is_linux) { if (is_linux) {
import("//build/config/linux/pkg_config.gni") import("//build/config/linux/pkg_config.gni")
import("//electron/build/linux/strip_binary.gni")
import("//tools/generate_stubs/rules.gni") import("//tools/generate_stubs/rules.gni")
pkg_config("gio_unix") { pkg_config("gio_unix") {
@ -1422,6 +1423,18 @@ dist_zip("electron_dist_zip") {
":licenses", ":licenses",
] ]
if (is_linux) { if (is_linux) {
if (is_official_build) {
data_deps += [
":strip_chrome_crashpad_handler",
":strip_chrome_sandbox",
":strip_electron_binary",
":strip_libEGL_shlib",
":strip_libGLESv2_shlib",
":strip_libffmpeg_shlib",
":strip_libvk_swiftshader_shlib",
]
}
data_deps += [ "//sandbox/linux:chrome_sandbox" ] data_deps += [ "//sandbox/linux:chrome_sandbox" ]
} }
deps = data_deps deps = data_deps
@ -1467,6 +1480,16 @@ group("electron_mksnapshot") {
dist_zip("electron_mksnapshot_zip") { dist_zip("electron_mksnapshot_zip") {
data_deps = mksnapshot_deps data_deps = mksnapshot_deps
if (is_linux && is_official_build) {
data_deps += [
":strip_libEGL_shlib",
":strip_libGLESv2_shlib",
":strip_libffmpeg_shlib",
":strip_libvk_swiftshader_shlib",
":strip_mksnapshot_binary",
":strip_v8_context_snapshot_generator_binary",
]
}
deps = data_deps deps = data_deps
outputs = [ "$root_build_dir/mksnapshot.zip" ] outputs = [ "$root_build_dir/mksnapshot.zip" ]
} }
@ -1591,3 +1614,78 @@ group("copy_node_headers") {
group("node_headers") { group("node_headers") {
public_deps = [ ":tar_node_headers" ] public_deps = [ ":tar_node_headers" ]
} }
if (is_linux && is_official_build) {
strip_binary("strip_electron_binary") {
binary_input = "$root_out_dir/$electron_project_name"
symbol_output = "$root_out_dir/debug/$electron_project_name.debug"
compress_debug_sections = true
deps = [ ":electron_app" ]
}
strip_binary("strip_chrome_crashpad_handler") {
binary_input = "$root_out_dir/chrome_crashpad_handler"
symbol_output = "$root_out_dir/debug/chrome_crashpad_handler.debug"
compress_debug_sections = true
deps = [ "//components/crash/core/app:chrome_crashpad_handler" ]
}
strip_binary("strip_chrome_sandbox") {
binary_input = "$root_out_dir/chrome_sandbox"
symbol_output = "$root_out_dir/debug/chrome-sandbox.debug"
compress_debug_sections = true
deps = [ "//sandbox/linux:chrome_sandbox" ]
}
strip_binary("strip_libEGL_shlib") {
binary_input = "$root_out_dir/libEGL.so"
symbol_output = "$root_out_dir/debug/libEGL.so.debug"
compress_debug_sections = true
deps = [ "//third_party/angle:libEGL" ]
}
strip_binary("strip_libGLESv2_shlib") {
binary_input = "$root_out_dir/libGLESv2.so"
symbol_output = "$root_out_dir/debug/libGLESv2.so.debug"
compress_debug_sections = true
deps = [ "//third_party/angle:libGLESv2" ]
}
strip_binary("strip_libffmpeg_shlib") {
binary_input = "$root_out_dir/libffmpeg.so"
symbol_output = "$root_out_dir/debug/libffmpeg.so.debug"
compress_debug_sections = true
deps = [ "//third_party/ffmpeg" ]
}
strip_binary("strip_libvk_swiftshader_shlib") {
binary_input = "$root_out_dir/libvk_swiftshader.so"
symbol_output = "$root_out_dir/debug/libvk_swiftshader.so.debug"
compress_debug_sections = true
deps = [ "//third_party/swiftshader/src/Vulkan:swiftshader_libvulkan" ]
}
strip_binary("strip_mksnapshot_binary") {
_binary_path = rebase_path(
get_label_info(
":v8_context_snapshot_generator($v8_snapshot_toolchain)",
"root_out_dir") + "/mksnapshot",
root_build_dir)
binary_input = "$root_out_dir/$_binary_path"
symbol_output = "$root_out_dir/debug/${_binary_path}.debug"
compress_debug_sections = true
deps = mksnapshot_deps
}
strip_binary("strip_v8_context_snapshot_generator_binary") {
_binary_path = rebase_path(
get_label_info(
":v8_context_snapshot_generator($v8_snapshot_toolchain)",
"root_out_dir") + "/v8_context_snapshot_generator",
root_build_dir)
binary_input = "$root_out_dir/$_binary_path"
symbol_output = "$root_out_dir/debug/${_binary_path}.debug"
compress_debug_sections = true
deps = mksnapshot_deps
}
}

View file

@ -69,3 +69,6 @@ v8_expose_public_symbols = true
# Disable snapshotting a page when printing for its content to be analyzed for # Disable snapshotting a page when printing for its content to be analyzed for
# sensitive content by enterprise users. # sensitive content by enterprise users.
enterprise_cloud_content_analysis = false enterprise_cloud_content_analysis = false
# We don't use anything from here, and it causes target collisions
enable_linux_installer = false

View file

@ -0,0 +1,70 @@
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This has been adapted from https://source.chromium.org/chromium/chromium/src/+/main:build/linux/strip_binary.gni;drc=c220a41e0422d45f1657c28146d32e99cc53640b
# The notable difference is it has an option to compress the debug sections
import("//build/config/clang/clang.gni")
import("//build/toolchain/toolchain.gni")
# Extracts symbols from a binary into a symbol file.
#
# Args:
# binary_input: Path to the binary containing symbols to extract, e.g.:
# "$root_out_dir/chrome"
# symbol_output: Desired output file for symbols, e.g.:
# "$root_out_dir/chrome.debug"
# stripped_binary_output: Desired output file for stripped file, e.g.:
# "$root_out_dir/chrome.stripped"
# compress_debug_sections: If true, compress the extracted debug sections
template("strip_binary") {
forward_variables_from(invoker,
[
"deps",
"testonly",
])
action("${target_name}") {
llvm_strip_binary = "${clang_base_path}/bin/llvm-strip"
llvm_objcopy_binary = "${clang_base_path}/bin/llvm-objcopy"
script = "//electron/build/linux/strip_binary.py"
if (defined(invoker.stripped_binary_output)) {
stripped_binary_output = invoker.stripped_binary_output
} else {
stripped_binary_output = invoker.binary_input + ".stripped"
}
if (defined(invoker.symbol_output)) {
symbol_output = invoker.symbol_output
} else {
symbol_output = invoker.binary_input + ".debug"
}
inputs = [
invoker.binary_input,
llvm_strip_binary,
llvm_objcopy_binary,
]
outputs = [
symbol_output,
stripped_binary_output,
]
args = [
"--llvm-strip-binary-path",
rebase_path(llvm_strip_binary, root_build_dir),
"--llvm-objcopy-binary-path",
rebase_path(llvm_objcopy_binary, root_build_dir),
"--symbol-output",
rebase_path(symbol_output, root_build_dir),
"--stripped-binary-output",
rebase_path(stripped_binary_output, root_build_dir),
"--binary-input",
rebase_path(invoker.binary_input, root_build_dir),
]
if (defined(invoker.compress_debug_sections) &&
invoker.compress_debug_sections) {
args += [ "--compress-debug-sections" ]
}
}
}

View file

@ -0,0 +1,63 @@
#!/usr/bin/env python3
#
# Copyright 2021 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This has been adapted from https://source.chromium.org/chromium/chromium/src/+/main:build/linux/strip_binary.py;drc=c220a41e0422d45f1657c28146d32e99cc53640b
# The notable difference is it has an option to compress the debug sections
import argparse
import subprocess
import sys
def main() -> int:
parser = argparse.ArgumentParser(description="Strip binary using LLVM tools.")
parser.add_argument("--llvm-strip-binary-path",
help="Path to llvm-strip executable.")
parser.add_argument("--llvm-objcopy-binary-path",
required=True,
help="Path to llvm-objcopy executable.")
parser.add_argument("--binary-input", help="Input ELF binary.")
parser.add_argument("--symbol-output",
help="File to write extracted debug info (.debug).")
parser.add_argument("--compress-debug-sections",
action="store_true",
help="Compress extracted debug info.")
parser.add_argument("--stripped-binary-output",
help="File to write stripped binary.")
args = parser.parse_args()
# Replicate the behavior of:
# eu-strip <binary_input> -o <stripped_binary_output> -f <symbol_output>
objcopy_args = [
"--only-keep-debug",
args.binary_input,
args.symbol_output,
]
if args.compress_debug_sections:
objcopy_args.insert(0, "--compress-debug-sections")
subprocess.check_output([args.llvm_objcopy_binary_path] + objcopy_args)
subprocess.check_output([
args.llvm_strip_binary_path,
"--strip-debug",
"--strip-unneeded",
"-o",
args.stripped_binary_output,
args.binary_input,
])
subprocess.check_output([
args.llvm_objcopy_binary_path,
f"--add-gnu-debuglink={args.symbol_output}",
args.stripped_binary_output,
])
return 0
if __name__ == "__main__":
sys.exit(main())

View file

@ -41,6 +41,8 @@ PATHS_TO_SKIP = [
'resources/inspector', 'resources/inspector',
'gen/third_party/devtools-frontend/src', 'gen/third_party/devtools-frontend/src',
'gen/ui/webui', 'gen/ui/webui',
# Skip because these get zipped separately in script/zip-symbols.py
'debug',
] ]
def skip_path(dep, dist_zip, target_cpu): def skip_path(dep, dist_zip, target_cpu):
@ -80,6 +82,11 @@ def main(argv):
dep = dep.strip() dep = dep.strip()
if not skip_path(dep, dist_zip, target_cpu): if not skip_path(dep, dist_zip, target_cpu):
dist_files.add(dep) dist_files.add(dep)
# On Linux, filter out any files which have a .stripped companion
if sys.platform == 'linux':
dist_files = {
dep for dep in dist_files if f"{dep.removeprefix('./')}.stripped" not in dist_files
}
if sys.platform == 'darwin' and not should_flatten: if sys.platform == 'darwin' and not should_flatten:
execute(['zip', '-r', '-y', dist_zip] + list(dist_files)) execute(['zip', '-r', '-y', dist_zip] + list(dist_files))
else: else:
@ -96,10 +103,13 @@ def main(argv):
dirname = os.path.dirname(dep) dirname = os.path.dirname(dep)
arcname = ( arcname = (
os.path.join(dirname, 'chrome-sandbox') os.path.join(dirname, 'chrome-sandbox')
if basename == 'chrome_sandbox' if basename.removesuffix('.stripped') == 'chrome_sandbox'
else dep else dep
) )
name_to_write = arcname name_to_write = arcname
# On Linux, strip the .stripped suffix from the name before zipping
if sys.platform == 'linux':
name_to_write = name_to_write.removesuffix('.stripped')
if should_flatten: if should_flatten:
if flatten_relative_to: if flatten_relative_to:
if name_to_write.startswith(flatten_relative_to): if name_to_write.startswith(flatten_relative_to):

View file

@ -191,12 +191,6 @@ $ ./out/Testing/electron
### Packaging ### Packaging
On linux, first strip the debugging and symbol information:
```sh
$ electron/script/strip-binaries.py -d out/Release
```
To package the electron build as a distributable zip file: To package the electron build as a distributable zip file:
```sh ```sh

View file

@ -1,62 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import sys
from lib.config import PLATFORM
from lib.util import execute, get_linux_binaries, get_out_dir
def add_debug_link_into_binaries(directory, target_cpu, debug_dir):
for binary in get_linux_binaries():
binary_path = os.path.join(directory, binary)
if os.path.isfile(binary_path):
add_debug_link_into_binary(binary_path, target_cpu, debug_dir)
def add_debug_link_into_binary(binary_path, target_cpu, debug_dir):
if PLATFORM == 'linux' and target_cpu in ('x86', 'arm', 'arm64'):
# Skip because no objcopy binary on the given target.
return
debug_name = get_debug_name(binary_path)
# Make sure the path to the binary is not relative because of cwd param.
real_binary_path = os.path.realpath(binary_path)
cmd = ['objcopy', '--add-gnu-debuglink=' + debug_name, real_binary_path]
execute(cmd, cwd=debug_dir)
def get_debug_name(binary_path):
return os.path.basename(binary_path) + '.debug'
def main():
args = parse_args()
if args.file:
add_debug_link_into_binary(args.file, args.target_cpu, args.debug_dir)
else:
add_debug_link_into_binaries(args.directory, args.target_cpu,
args.debug_dir)
def parse_args():
parser = argparse.ArgumentParser(description='Add debug link to binaries')
parser.add_argument('-d', '--directory',
help='Path to the dir that contains files to add links',
default=get_out_dir(),
required=False)
parser.add_argument('-f', '--file',
help='Path to a specific file to add debug link',
required=False)
parser.add_argument('-s', '--debug-dir',
help='Path to the dir that contain the debugs',
default=None,
required=True)
parser.add_argument('-v', '--verbose',
action='store_true',
help='Prints the output of the subprocesses')
parser.add_argument('--target-cpu',
default='',
required=False,
help='Target cpu of binaries to add debug link')
return parser.parse_args()
if __name__ == '__main__':
sys.exit(main())

View file

@ -1,69 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import sys
from lib.config import PLATFORM
from lib.util import execute, get_linux_binaries, get_out_dir, safe_mkdir
# It has to be done before stripping the binaries.
def copy_debug_from_binaries(directory, out_dir, target_cpu, compress):
for binary in get_linux_binaries():
binary_path = os.path.join(directory, binary)
if os.path.isfile(binary_path):
copy_debug_from_binary(binary_path, out_dir, target_cpu, compress)
def copy_debug_from_binary(binary_path, out_dir, target_cpu, compress):
if PLATFORM == 'linux' and target_cpu in ('x86', 'arm', 'arm64'):
# Skip because no objcopy binary on the given target.
return
debug_name = get_debug_name(binary_path)
cmd = ['objcopy', '--only-keep-debug']
if compress:
cmd.extend(['--compress-debug-sections'])
cmd.extend([binary_path, os.path.join(out_dir, debug_name)])
execute(cmd)
def get_debug_name(binary_path):
return os.path.basename(binary_path) + '.debug'
def main():
args = parse_args()
safe_mkdir(args.out_dir)
if args.file:
copy_debug_from_binary(args.file, args.out_dir, args.target_cpu,
args.compress)
else:
copy_debug_from_binaries(args.directory, args.out_dir, args.target_cpu,
args.compress)
def parse_args():
parser = argparse.ArgumentParser(description='Copy debug from binaries')
parser.add_argument('-d', '--directory',
help='Path to the dir that contains files to copy',
default=get_out_dir(),
required=False)
parser.add_argument('-f', '--file',
help='Path to a specific file to copy debug symbols',
required=False)
parser.add_argument('-o', '--out-dir',
help='Path to the dir that will contain the debugs',
default=None,
required=True)
parser.add_argument('-v', '--verbose',
action='store_true',
help='Prints the output of the subprocesses')
parser.add_argument('--target-cpu',
default='',
required=False,
help='Target cpu of binaries to copy debug symbols')
parser.add_argument('--compress',
action='store_true',
required=False,
help='Compress the debug symbols')
return parser.parse_args()
if __name__ == '__main__':
sys.exit(main())

View file

@ -207,14 +207,3 @@ def get_depot_tools_executable(name):
if sys.platform == 'win32': if sys.platform == 'win32':
path += '.bat' path += '.bat'
return path return path
def get_linux_binaries():
return [
'chrome-sandbox',
'chrome_crashpad_handler',
get_electron_branding()['project_name'],
'libEGL.so',
'libGLESv2.so',
'libffmpeg.so',
'libvk_swiftshader.so',
]

View file

@ -1,81 +0,0 @@
#!/usr/bin/env python3
import argparse
import os
import sys
from lib.config import set_verbose_mode, is_verbose_mode, verbose_mode_print
from lib.util import execute, get_linux_binaries, get_out_dir
def get_size(path):
size = os.path.getsize(path)
units = ["bytes", "KB", "MB", "GB"]
for unit in units:
if size < 1024:
return f"{size:.2f} {unit}"
size /= 1024
raise ValueError("File size is too large to be processed")
def strip_binaries(directory, target_cpu):
if not os.path.isdir(directory):
verbose_mode_print('Directory ' + directory + ' does not exist.')
return
verbose_mode_print('Stripping binaries in ' + directory)
for binary in get_linux_binaries():
verbose_mode_print('\nStripping ' + binary)
binary_path = os.path.join(directory, binary)
if os.path.isfile(binary_path):
strip_binary(binary_path, target_cpu)
def strip_binary(binary_path, target_cpu):
if target_cpu == 'arm':
strip = 'arm-linux-gnueabihf-strip'
elif target_cpu == 'arm64':
strip = 'aarch64-linux-gnu-strip'
else:
strip = 'strip'
strip_args = [strip,
'--discard-all',
'--strip-debug',
'--preserve-dates',
binary_path]
if (is_verbose_mode()):
strip_args.insert(1, '--verbose')
verbose_mode_print('Binary size before stripping: ' +
str(get_size(binary_path)))
execute(strip_args)
verbose_mode_print('Binary size after stripping: ' +
str(get_size(binary_path)))
def main():
args = parse_args()
set_verbose_mode(args.verbose)
if args.file:
strip_binary(args.file, args.target_cpu)
else:
strip_binaries(args.directory, args.target_cpu)
def parse_args():
parser = argparse.ArgumentParser(description='Strip linux binaries')
parser.add_argument('-d', '--directory',
help='Path to the dir that contains files to strip.',
default=get_out_dir(),
required=False)
parser.add_argument('-f', '--file',
help='Path to a specific file to strip.',
required=False)
parser.add_argument('-v', '--verbose',
default=False,
action='store_true',
help='Prints the output of the subprocesses')
parser.add_argument('--target-cpu',
default='',
required=False,
help='Target cpu of binaries to strip')
return parser.parse_args()
if __name__ == '__main__':
sys.exit(main())