build: Polish shared library output

A grab-bag of related improvements aimed at producing a clean shared
libfrida-core-1.0.so suitable for distro packaging:

- Use frida-1.0/ for the per-API-version asset directory (matching
  libfrida-core-1.0.so, frida-core-1.0.pc, etc.) so future API
  versions can install in parallel.
- Default the assets option to installed for shared builds, embedded
  for static.
- Install the typelib via gobject-introspection's typelibdir variable,
  so it lands next to the system's other typelibs.
- Force frida-gum, quickjs, and libtcc to static linking, and skip
  in-tree agent/gadget/payload + their pkg-config files when building
  shared (the agent and gadget come from compat in that case).
- Disable GumJS in shared builds and rely on the new inspector-only
  option in frida-gum to keep Script.enable_debugger() working.
- Build the compiler backend as an installable plugin under
  frida-1.0/plugins/ when assets=installed, with new
  COMPILER_BACKEND_INSTALLED_LIBRARY and
  COMPILER_BACKEND_INSTALLED_EXECUTABLE modes. Rename the existing
  modes to LINKED, EMBEDDED_LIBRARY, and EMBEDDED_EXECUTABLE for
  symmetry. The plugin is loaded by path at runtime, so distros can
  ship it as an optional separate package and frida-core gracefully
  reports a context-rich error when it's missing.
This commit is contained in:
Ole André Vadla Ravnås committed 2026-04-08 16:43:57 +02:00
1 parent 751f43145b
commit cd3b35bdde
13 files changed
+148 -72

No files matched your search

+2 -2
View File
@@ -67,7 +67,7 @@ modulated_agent = custom_target('frida-agent-modulated',
)
need_universal_agent = host_os_family == 'darwin' \
and get_option('assets') == 'installed' \
and assets_mode == 'installed' \
and agent_compat != ''
if host_os_family == 'darwin'
@@ -80,7 +80,7 @@ agent = custom_target('frida-agent',
output: need_universal_agent ? f'@agent_name@-@host_abi@' : agent_name,
command: post_process + ['shared-library', identity],
build_by_default: true,
install: get_option('assets') == 'installed' and not need_universal_agent,
install: assets_mode == 'installed' and not need_universal_agent,
install_dir: asset_dir,
)
+10 -8
View File
@@ -112,7 +112,7 @@ base = static_library('frida-base-' + api_version, base_sources,
vala_gir: base_gir_name,
vala_vapi: f'frida-base-@[email protected]',
dependencies: [glib_dep, gobject_dep, gio_dep, libsoup_dep, nice_dep, usrsctp_dep, gee_dep, json_glib_dep, gum_dep, openssl_dep] + extra_deps,
install: true,
install: have_in_tree_payloads,
install_dir: [true, header_install_dir, true, false],
)
@@ -124,10 +124,12 @@ base_dep = declare_dependency(
base_build_dir = meson.current_build_dir()
pkg = import('pkgconfig')
pkg.generate(base,
filebase: f'frida-base-@api_version@',
name: 'Frida Base Library',
version: frida_version,
description: 'Basic building blocks',
)
if have_in_tree_payloads
pkg = import('pkgconfig')
pkg.generate(base,
filebase: f'frida-base-@api_version@',
name: 'Frida Base Library',
version: frida_version,
description: 'Basic building blocks',
)
endif
+8 -6
View File
@@ -4,10 +4,12 @@ endif
subdir('base')
subdir('netif')
subdir('pipe')
subdir('payload')
if have_local_backend and not have_shared_glib
subdir('agent')
endif
if build_gadget and not have_shared_glib
subdir('gadget')
if have_in_tree_payloads
subdir('payload')
if have_local_backend
subdir('agent')
endif
if build_gadget
subdir('gadget')
endif
endif
+44 -14
View File
@@ -224,15 +224,16 @@ helper_name = 'frida-helper' + exe_suffix
agent_name = 'frida-agent' + shlib_suffix
gadget_name = 'frida-gadget' + shlib_suffix
root_asset_dir = get_option('libdir') / 'frida'
frida_libdir_name = f'frida-@api_version@'
root_asset_dir = get_option('libdir') / frida_libdir_name
if host_os_family == 'darwin'
asset_dir = root_asset_dir
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida'
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name
else
asset_dir = root_asset_dir / host_cpu_mode
asset_dir_modern = root_asset_dir / '64'
asset_dir_legacy = root_asset_dir / '32'
default_asset_path_template = get_option('prefix') / get_option('libdir') / 'frida' / '<arch>'
default_asset_path_template = get_option('prefix') / get_option('libdir') / frida_libdir_name / '<arch>'
endif
asset_path_template = get_option('asset_path_template')
@@ -240,7 +241,12 @@ if asset_path_template == ''
asset_path_template = default_asset_path_template
endif
if get_option('assets') == 'embedded'
assets_mode = get_option('assets')
if assets_mode == 'auto'
assets_mode = (get_option('default_library') == 'shared') ? 'installed' : 'embedded'
endif
if assets_mode == 'embedded'
vala_flags += '--define=HAVE_EMBEDDED_ASSETS'
else
cdata.set_quoted('FRIDA_HELPER_PATH', asset_path_template / helper_name)
@@ -312,11 +318,16 @@ if get_option('b_sanitize') == 'address'
cdata.set('HAVE_ASAN', 1)
endif
have_in_tree_payloads = get_option('default_library') != 'shared'
gum_dep = dependency('frida-gum-1.0', default_options: [
'default_library=static',
f'frida_version=@frida_version@',
'gumjs=enabled',
'gumjs=' + (have_in_tree_payloads ? 'enabled' : 'disabled'),
'inspector=enabled',
])
gumjs_dep = dependency('frida-gumjs-1.0')
if have_in_tree_payloads
gumjs_dep = dependency('frida-gumjs-1.0')
endif
gumjs_inspector_dep = dependency('frida-gumjs-inspector-1.0')
glib_version_req = '>=2.74'
@@ -387,6 +398,7 @@ if cc.has_function('soup_websocket_connection_pause_input', dependencies: [libso
endif
brotlidec_dep = dependency('libbrotlidec')
quickjs_dep = dependency('quickjs', required: get_option('barebone_backend'), default_options: [
'default_library=static',
'libc=false',
'bignum=true',
'atomics=disabled',
@@ -494,6 +506,7 @@ if compiler_backend_opt.allowed()
exe_suffix,
shlib_suffix,
cc.get_id(),
assets_mode,
'>>>', cc.cmd_array(), '<<<',
'>>>', ar, '<<<',
'>>>', nm, '<<<',
@@ -511,11 +524,23 @@ if compiler_backend_opt.allowed()
go_config = tokens[1]
if compiler_backend_mode == 'c-archive'
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED'
vala_flags += '--define=COMPILER_BACKEND_LINKED'
elif compiler_backend_mode == 'c-shared'
vala_flags += '--define=COMPILER_BACKEND_SHARED'
if assets_mode == 'installed'
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_LIBRARY'
cdata.set_quoted('FRIDA_COMPILER_BACKEND_PATH',
get_option('prefix') / get_option('libdir') / frida_libdir_name / 'plugins' / ('frida-compiler-backend' + shlib_suffix))
else
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_LIBRARY'
endif
elif compiler_backend_mode == 'pie'
vala_flags += '--define=COMPILER_BACKEND_EXECUTABLE'
if assets_mode == 'installed'
vala_flags += '--define=COMPILER_BACKEND_INSTALLED_EXECUTABLE'
cdata.set_quoted('FRIDA_COMPILER_BACKEND_PATH',
get_option('prefix') / get_option('libdir') / frida_libdir_name / 'plugins' / ('frida-compiler-backend' + exe_suffix))
else
vala_flags += '--define=COMPILER_BACKEND_EMBEDDED_EXECUTABLE'
endif
else
error('Unsupported compiler backend mode: ' + compiler_backend_mode)
endif
@@ -586,8 +611,10 @@ endif
gi_dep = dependency('gobject-introspection-1.0', required: false)
if gi_dep.found()
girdir = gi_dep.get_variable('girdir')
typelibdir = gi_dep.get_variable('typelibdir')
else
girdir = get_option('prefix') / get_option('datadir') / 'gir-1.0'
typelibdir = get_option('prefix') / get_option('libdir') / 'girepository-1.0'
endif
vapidir = get_option('prefix') / get_option('datadir') / 'vala' / 'vapi'
@@ -596,12 +623,15 @@ gum_vala_args = [
'--pkg=frida-gum-1.0',
'--vapidir=' + gum_dep.get_variable('frida_vapidir'),
]
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
gumjs_inspector_vala_args = ['--pkg=frida-gumjs-inspector-1.0', gum_vala_args]
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
if have_v8
vala_flags += ['--define=HAVE_V8']
if have_in_tree_payloads
gumjs_vala_args = ['--pkg=frida-gumjs-1.0', '--pkg=gio-2.0', gum_vala_args]
have_v8 = gumjs_dep.get_variable('gumjs_v8') == 'enabled'
if have_v8
vala_flags += ['--define=HAVE_V8']
endif
endif
backend_deps_private = []
@@ -916,7 +946,7 @@ summary(
summary(
{
'compat': compat_summary,
'assets': get_option('assets'),
'assets': assets_mode,
'mapper': have_mapper,
'connectivity': tls_provider_dep.found(),
'gadget': build_gadget,
+2 -2
View File
@@ -48,8 +48,8 @@ option('compiler_backend',
option('assets',
type: 'combo',
choices: ['embedded', 'installed'],
value: 'embedded',
choices: ['auto', 'embedded', 'installed'],
value: 'auto',
description: 'Whether assets such as frida-agent should be embedded vs installed and loaded at runtime'
)
+1 -1
View File
@@ -37,7 +37,7 @@ raw_server = executable('frida-server-raw', server_sources,
)
server_name = 'frida-server' + exe_suffix
need_universal_server = get_option('assets') == 'installed' and server_compat != ''
need_universal_server = assets_mode == 'installed' and server_compat != ''
server = custom_target('frida-server',
input: [raw_server, 'frida-server.xcent'],
+1 -1
View File
@@ -52,7 +52,7 @@ if g_ir_compiler.found()
output: f'Frida-@[email protected]',
depends: core,
install: true,
install_dir: get_option('libdir') / 'girepository-1.0',
install_dir: typelibdir,
)
endif
+36 -9
View File
@@ -235,12 +235,23 @@ namespace Frida {
namespace CompilerBackend {
private void init () {
#if HAVE_COMPILER_BACKEND
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
_init_go_runtime ();
build = (BuildFunc) _build;
watch = (WatchFunc) _watch;
WatchSession.dispose = (WatchSession.DisposeFunc) WatchSession._dispose;
#elif COMPILER_BACKEND_SHARED
#elif COMPILER_BACKEND_INSTALLED_LIBRARY
Module? backend = null;
try {
backend = new Module (Config.FRIDA_COMPILER_BACKEND_PATH, LOCAL);
} catch (ModuleError e) {
return;
}
build = resolve_symbol (backend, "_frida_compiler_backend_build");
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
#elif COMPILER_BACKEND_EMBEDDED_LIBRARY
unowned uint8[] backend_so = Frida.Data.Compiler.get_frida_compiler_backend_so_blob ().data;
Module? backend = null;
@@ -276,7 +287,7 @@ namespace Frida {
build = resolve_symbol (backend, "_frida_compiler_backend_build");
watch = resolve_symbol (backend, "_frida_compiler_backend_watch");
WatchSession.dispose = resolve_symbol (backend, "_frida_compiler_backend_watch_session_dispose");
#elif COMPILER_BACKEND_EXECUTABLE
#elif COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
backend_process = new BackendProcess ();
build = executable_build;
@@ -287,8 +298,15 @@ namespace Frida {
}
private void check_available () throws Error {
if (build == null)
if (build == null) {
#if COMPILER_BACKEND_INSTALLED_LIBRARY || COMPILER_BACKEND_INSTALLED_EXECUTABLE
throw new Error.NOT_SUPPORTED (
"Compiler backend plugin not installed; expected at: %s",
Config.FRIDA_COMPILER_BACKEND_PATH);
#else
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
#endif
}
}
private BuildFunc? build;
@@ -305,7 +323,7 @@ namespace Frida {
string platform, string[] externals, StartingFunc on_starting, FinishedFunc on_finished,
OutputFunc on_output, DiagnosticFunc on_diagnostic, owned WatchReadyFunc on_ready);
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
private extern void _init_go_runtime ();
private extern void _build ();
private extern void _watch ();
@@ -317,7 +335,7 @@ namespace Frida {
private DisposeFunc? dispose;
#if COMPILER_BACKEND_EMBEDDED
#if COMPILER_BACKEND_LINKED
private extern void _dispose ();
#endif
}
@@ -330,7 +348,7 @@ namespace Frida {
private delegate void DiagnosticFunc (owned string category, int code, owned string? path, int line, int character,
owned string text);
#if HAVE_COMPILER_BACKEND && COMPILER_BACKEND_SHARED
#if HAVE_COMPILER_BACKEND && (COMPILER_BACKEND_EMBEDDED_LIBRARY || COMPILER_BACKEND_INSTALLED_LIBRARY)
private T resolve_symbol<T> (Module m, string name) {
void * address;
if (!m.symbol (name, out address))
@@ -339,7 +357,7 @@ namespace Frida {
}
#endif
#if COMPILER_BACKEND_EXECUTABLE
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE || COMPILER_BACKEND_INSTALLED_EXECUTABLE
private BackendProcess? backend_process;
private static void executable_build (string project_root, string entrypoint, OutputFormat output_format,
@@ -393,7 +411,13 @@ namespace Frida {
if (process != null)
return;
#if COMPILER_BACKEND_INSTALLED_EXECUTABLE
string path = Config.FRIDA_COMPILER_BACKEND_PATH;
bool unlink_after = false;
#else
string path = extract_backend_executable ();
bool unlink_after = true;
#endif
try {
var p = new Subprocess (STDIN_PIPE | STDOUT_PIPE | STDERR_PIPE, path);
process = p;
@@ -408,10 +432,12 @@ namespace Frida {
process_incoming_messages.begin ();
process_stderr_stream.begin (errput);
} finally {
FileUtils.unlink (path);
if (unlink_after)
FileUtils.unlink (path);
}
}
#if COMPILER_BACKEND_EMBEDDED_EXECUTABLE
private static string extract_backend_executable () throws GLib.Error {
unowned uint8[] blob = Frida.Data.Compiler.get_frida_compiler_backend_blob ().data;
@@ -425,6 +451,7 @@ namespace Frida {
return path;
}
#endif
private void handle_process_failure (string message) {
io_cancellable.cancel ();
+27 -19
View File
@@ -58,6 +58,8 @@ if have_compiler_backend
input: compiler_backend[0],
output: processed_filename,
command: post_process + ['shared-library', identity, 'elf-cleaner:off'],
install: assets_mode == 'installed',
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
)
elif compiler_backend_mode == 'pie'
processed_filename = 'frida-compiler-backend' + exe_suffix
@@ -65,27 +67,33 @@ if have_compiler_backend
input: compiler_backend[0],
output: processed_filename,
command: post_process + ['executable', processed_filename],
install: assets_mode == 'installed',
install_dir: get_option('libdir') / frida_libdir_name / 'plugins',
)
endif
compiler_data = custom_target('frida-data-compiler',
input: [
'compiler.resources',
compiler_backend_binary,
],
output: [
'frida-data-compiler.vapi',
'frida-data-compiler.h',
'frida-data-compiler.c',
'frida-data-compiler-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-compiler',
'@INPUT1@',
],
)
backend_sources += compiler_data
if assets_mode == 'installed'
core_extra_link_depends += compiler_backend_binary
else
compiler_data = custom_target('frida-data-compiler',
input: [
'compiler.resources',
compiler_backend_binary,
],
output: [
'frida-data-compiler.vapi',
'frida-data-compiler.h',
'frida-data-compiler.c',
'frida-data-compiler-blob' + resource_blob_extension,
],
command: [
resource_compiler_cmd_array,
'-c', '@INPUT0@',
'-o', meson.current_build_dir() / 'frida-data-compiler',
'@INPUT1@',
],
)
backend_sources += compiler_data
endif
endif
endif
+1 -1
View File
@@ -23,5 +23,5 @@ custom_target('core-devkit',
meson.current_build_dir(),
] + devkit_options,
install: true,
install_dir: get_option('libdir') / 'frida' / 'devkits' / 'core'
install_dir: get_option('libdir') / frida_libdir_name / 'devkits' / 'core'
)
+11 -8
View File
@@ -131,11 +131,11 @@ if have_local_backend
output: helper_name,
command: post_process_helper,
build_by_default: true,
install: get_option('assets') == 'installed',
install: assets_mode == 'installed',
install_dir: asset_dir,
)
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -256,7 +256,7 @@ if have_local_backend
override_options: ['b_asneeded=false'],
)
need_universal_helper = get_option('assets') == 'installed' and helper_compat != ''
need_universal_helper = assets_mode == 'installed' and helper_compat != ''
helper = custom_target('frida-helper',
input: [
@@ -266,7 +266,7 @@ if have_local_backend
output: need_universal_helper ? f'@helper_name@-@host_abi@' : helper_name,
command: post_process_helper + '@INPUT1@',
build_by_default: true,
install: get_option('assets') == 'installed' and not need_universal_helper,
install: assets_mode == 'installed' and not need_universal_helper,
install_dir: asset_dir,
)
@@ -287,7 +287,7 @@ if have_local_backend
)
endif
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -431,11 +431,11 @@ if have_local_backend
output: helper_name,
command: post_process_helper,
build_by_default: true,
install: get_option('assets') == 'installed',
install: assets_mode == 'installed',
install_dir: asset_dir,
)
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if helper_modern == 'auto'
helper_modern = host_is_modern ? helper : ''
endif
@@ -484,7 +484,7 @@ if have_local_backend
]
endif
if get_option('assets') == 'embedded'
if assets_mode == 'embedded'
if agent_modern == 'auto'
agent_modern = host_is_modern ? agent : ''
endif
@@ -651,6 +651,8 @@ if host_os_family != 'windows'
backend_vala_args_private += '--pkg=posix'
endif
core_extra_link_depends = []
subdir('compiler')
core_sources = base_sources + backend_sources + compiler_sources
@@ -729,6 +731,7 @@ core = library(core_lib_name, core_sources,
],
vala_gir: core_gir_name,
link_args: core_extra_link_args,
link_depends: core_extra_link_depends,
link_with: backend_libs,
dependencies: core_deps + internal_deps,
soversion: frida_major_version,
+4 -1
View File
@@ -56,6 +56,7 @@ def main(argv: List[str]):
exe_suffix = args.pop(0)
shlib_suffix = args.pop(0)
cc_id = args.pop(0)
assets_mode = args.pop(0)
cc_cmd_array = pop_cmd_array_arg(args)
ar_cmd_array = pop_cmd_array_arg(args)
nm_cmd_array = pop_cmd_array_arg(args)
@@ -70,6 +71,7 @@ def main(argv: List[str]):
exe_suffix,
shlib_suffix,
cc_id,
assets_mode,
cc_cmd_array,
ar_cmd_array,
nm_cmd_array,
@@ -104,6 +106,7 @@ def detect_config(
exe_suffix: str,
shlib_suffix: str,
cc_id: str,
assets_mode: str,
cc_cmd_array: List[str],
ar_cmd_array: Optional[List[str]],
nm_cmd_array: Optional[List[str]],
@@ -171,7 +174,7 @@ def detect_config(
if libc == "musl":
mode = "pie"
elif host_os == "android":
elif host_os == "android" or assets_mode == "installed":
mode = "c-shared"
else:
mode = "c-archive"
+1
View File
@@ -3,4 +3,5 @@ namespace Config {
public const string FRIDA_PREFIX;
public const string FRIDA_HELPER_PATH;
public const string FRIDA_AGENT_PATH;
public const string FRIDA_COMPILER_BACKEND_PATH;
}