mirror of
https://github.com/frida/frida-core.git
synced 2026-11-10 10:01:35 -10:00
Compare commits
No files matched your search
@@ -1400,12 +1400,12 @@ namespace Frida.Agent {
|
||||
if (nb_api.unload_library == null)
|
||||
parameters.append ("|eternal|sticky");
|
||||
/*
|
||||
* Disable ExitMonitor to work around a bug in Android's libndk_translation.so on Android 11.
|
||||
* We need to avoid modifying libc.so ranges that the translator potentially depends on, to
|
||||
* avoid blowing up when Interceptor's CPU cache flush results in the translated code being
|
||||
* discarded, which seems like an edge-case the translator doesn't handle.
|
||||
* Disable Exceptor and ExitMonitor to work around a bug in Android's libndk_translation.so
|
||||
* on Android 11. We need to avoid modifying libc.so ranges that the translator potentially
|
||||
* depends on, to avoid blowing up when Interceptor's CPU cache flush results in the translated
|
||||
* code being discarded, which seems like an edge-case the translator doesn't handle.
|
||||
*/
|
||||
parameters.append ("|exit-monitor:off");
|
||||
parameters.append ("|exceptor:off|exit-monitor:off");
|
||||
|
||||
emulated_bridge_state = new BridgeState (parameters.str);
|
||||
|
||||
|
||||
@@ -446,7 +446,7 @@ namespace Frida {
|
||||
|
||||
public string read_fixed_string (size_t offset, size_t size) throws Error {
|
||||
string * start = (string *) get_pointer (offset, size);
|
||||
size_t max_length = size - offset;
|
||||
size_t max_length = size_t.min (size, this.size - offset);
|
||||
string * end = memchr (start, 0, max_length);
|
||||
size_t n;
|
||||
if (end != null)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef __FRIDA_SYSCALL_H__
|
||||
#define __FRIDA_SYSCALL_H__
|
||||
#ifndef __FRIDA_LINUX_SYSCALL_H__
|
||||
#define __FRIDA_LINUX_SYSCALL_H__
|
||||
|
||||
#include <linux/unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
@@ -14,7 +14,7 @@ namespace Frida {
|
||||
[CCode (cheader_filename = "sys/mman.h", cname = "MAP_ANONYMOUS")]
|
||||
public const int MAP_ANONYMOUS;
|
||||
|
||||
[CCode (cheader_filename = "linux/frida-syscall.h", cprefix = "SYS_", has_type_id = false)]
|
||||
[CCode (cheader_filename = "linux-syscall.h", cprefix = "SYS_", has_type_id = false)]
|
||||
public enum SysCall {
|
||||
memfd_create,
|
||||
pidfd_getfd,
|
||||
@@ -0,0 +1,107 @@
|
||||
namespace Frida {
|
||||
private uint linux_major = 0;
|
||||
private uint linux_minor = 0;
|
||||
|
||||
public bool check_kernel_version (uint major, uint minor) {
|
||||
if (linux_major == 0) {
|
||||
var name = Posix.utsname ();
|
||||
name.release.scanf ("%u.%u", out linux_major, out linux_minor);
|
||||
}
|
||||
|
||||
return (linux_major == major && linux_minor >= minor) || linux_major > major;
|
||||
}
|
||||
|
||||
public class FileDescriptor : Object, FileDescriptorBased {
|
||||
public int handle;
|
||||
|
||||
public FileDescriptor (int handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
~FileDescriptor () {
|
||||
if (handle != -1)
|
||||
Posix.close (handle);
|
||||
}
|
||||
|
||||
public int steal () {
|
||||
int result = handle;
|
||||
handle = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int get_fd () {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PidFileDescriptor : FileDescriptor {
|
||||
private uint pid;
|
||||
|
||||
private PidFileDescriptor (int fd, uint pid) {
|
||||
base (fd);
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public static bool is_supported () {
|
||||
return check_kernel_version (5, 3);
|
||||
}
|
||||
|
||||
public static bool getfd_is_supported () {
|
||||
return check_kernel_version (5, 6);
|
||||
}
|
||||
|
||||
public static PidFileDescriptor from_pid (uint pid) throws Error {
|
||||
int fd = pidfd_open (pid, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new PidFileDescriptor (fd, pid);
|
||||
}
|
||||
|
||||
public FileDescriptor getfd (int targetfd) throws Error {
|
||||
int fd = pidfd_getfd (handle, targetfd, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new FileDescriptor (fd);
|
||||
}
|
||||
|
||||
private static int pidfd_open (uint pid, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
private static int pidfd_getfd (int pidfd, int targetfd, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_getfd, pidfd, targetfd, flags);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_pidfd_error (uint pid, int err) throws Error {
|
||||
switch (err) {
|
||||
case Posix.ESRCH:
|
||||
throw new Error.PROCESS_NOT_FOUND ("Process not found");
|
||||
case Posix.EPERM:
|
||||
throw new Error.PERMISSION_DENIED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MemoryFileDescriptor {
|
||||
public bool is_supported () {
|
||||
return check_kernel_version (3, 17);
|
||||
}
|
||||
|
||||
public static FileDescriptor from_bytes (string name, Bytes bytes) {
|
||||
assert (is_supported ());
|
||||
|
||||
var fd = new FileDescriptor (memfd_create (name, 0));
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
ssize_t n = Posix.write (fd.handle, data, data.length);
|
||||
assert (n == data.length);
|
||||
return fd;
|
||||
}
|
||||
|
||||
private int memfd_create (string name, uint flags) {
|
||||
return Linux.syscall (SysCall.memfd_create, name, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
-1
@@ -29,6 +29,15 @@ if host_os_family == 'darwin'
|
||||
base_sources += 'corefoundation.vala'
|
||||
endif
|
||||
|
||||
if host_os_family == 'linux'
|
||||
base_sources += 'linux.vala'
|
||||
extra_vala_args += [
|
||||
'--pkg=linux',
|
||||
'--pkg=linux-system',
|
||||
'--pkg=posix',
|
||||
]
|
||||
endif
|
||||
|
||||
extra_deps = []
|
||||
if host_os_family != 'windows'
|
||||
extra_vala_args += '--pkg=gio-unix-2.0'
|
||||
@@ -46,6 +55,12 @@ if host_os_family == 'darwin'
|
||||
'--pkg=darwin-xpc',
|
||||
]
|
||||
endif
|
||||
if host_os_family == 'linux'
|
||||
base_vala_args += [
|
||||
'--vapidir=' + meson.current_source_dir(),
|
||||
'--pkg=gio-unix-2.0',
|
||||
]
|
||||
endif
|
||||
|
||||
base_gir_name = f'FridaBase-@[email protected]'
|
||||
|
||||
@@ -63,7 +78,7 @@ base = static_library('frida-base-' + api_version, base_sources,
|
||||
base_dep = declare_dependency(
|
||||
include_directories: include_directories('.'),
|
||||
link_with: base,
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep],
|
||||
dependencies: [nice_dep, json_glib_dep, gum_dep] + extra_deps,
|
||||
)
|
||||
|
||||
base_build_dir = meson.current_build_dir()
|
||||
|
||||
+52
-12
@@ -46,7 +46,8 @@ namespace Frida {
|
||||
}
|
||||
private GLib.Error? _error;
|
||||
|
||||
private Gee.ArrayQueue<CompletionFuncEntry> on_complete;
|
||||
private Gee.ArrayQueue<CompletionFuncEntry>? on_complete;
|
||||
private Gee.ArrayQueue<ThenHandlerEntry<T>>? on_then;
|
||||
|
||||
public async T wait_async (Cancellable? cancellable) throws Frida.Error, IOError {
|
||||
if (_ready)
|
||||
@@ -74,7 +75,21 @@ namespace Frida {
|
||||
return get_result ();
|
||||
}
|
||||
|
||||
private T get_result () throws Frida.Error, IOError {
|
||||
public void then (owned FutureCompletionHandler<T> handler) {
|
||||
if (_ready) {
|
||||
schedule (() => {
|
||||
handler (this);
|
||||
return Source.REMOVE;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (on_then == null)
|
||||
on_then = new Gee.ArrayQueue<ThenHandlerEntry<T>> ();
|
||||
on_then.offer (new ThenHandlerEntry<T> ((owned) handler));
|
||||
}
|
||||
|
||||
public T get_result () throws Frida.Error, IOError {
|
||||
if (error != null) {
|
||||
if (error is Frida.Error)
|
||||
throw (Frida.Error) error;
|
||||
@@ -111,19 +126,32 @@ namespace Frida {
|
||||
internal void transition_to_ready () {
|
||||
_ready = true;
|
||||
|
||||
if (on_complete != null && !on_complete.is_empty) {
|
||||
var source = new IdleSource ();
|
||||
source.set_priority (Priority.HIGH);
|
||||
source.set_callback (() => {
|
||||
CompletionFuncEntry? entry;
|
||||
while ((entry = on_complete.poll ()) != null)
|
||||
entry.func ();
|
||||
on_complete = null;
|
||||
return false;
|
||||
if (on_complete != null || on_then != null) {
|
||||
schedule (() => {
|
||||
if (on_complete != null) {
|
||||
CompletionFuncEntry? entry;
|
||||
while ((entry = on_complete.poll ()) != null)
|
||||
entry.func ();
|
||||
on_complete = null;
|
||||
}
|
||||
|
||||
if (on_then != null) {
|
||||
ThenHandlerEntry? entry;
|
||||
while ((entry = on_then.poll ()) != null)
|
||||
entry.handler (this);
|
||||
on_then = null;
|
||||
}
|
||||
|
||||
return Source.REMOVE;
|
||||
});
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
}
|
||||
|
||||
private static void schedule (owned SourceFunc function) {
|
||||
var source = new IdleSource ();
|
||||
source.set_callback ((owned) function);
|
||||
source.attach (MainContext.get_thread_default ());
|
||||
}
|
||||
}
|
||||
|
||||
private class CompletionFuncEntry {
|
||||
@@ -133,6 +161,14 @@ namespace Frida {
|
||||
this.func = (owned) func;
|
||||
}
|
||||
}
|
||||
|
||||
private class ThenHandlerEntry<T> {
|
||||
public FutureCompletionHandler<T> handler;
|
||||
|
||||
public ThenHandlerEntry (owned FutureCompletionHandler<T> handler) {
|
||||
this.handler = (owned) handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface Future<T> : Object {
|
||||
@@ -140,5 +176,9 @@ namespace Frida {
|
||||
public abstract T? value { get; }
|
||||
public abstract GLib.Error? error { get; }
|
||||
public abstract async T wait_async (Cancellable? cancellable) throws Frida.Error, IOError;
|
||||
public abstract void then (owned FutureCompletionHandler<T> handler);
|
||||
public abstract T get_result () throws Frida.Error, IOError;
|
||||
}
|
||||
|
||||
public delegate void FutureCompletionHandler<T> (Future<T> future);
|
||||
}
|
||||
+8
-1
@@ -423,6 +423,7 @@ if compiler_backend_opt.allowed()
|
||||
go,
|
||||
host_os,
|
||||
host_abi,
|
||||
shlib_suffix,
|
||||
cc.get_id(),
|
||||
'>>>', cc.cmd_array(), '<<<',
|
||||
'>>>', ar, '<<<',
|
||||
@@ -436,7 +437,13 @@ if compiler_backend_opt.allowed()
|
||||
status = lines[0]
|
||||
if status == 'ok'
|
||||
have_compiler_backend = true
|
||||
go_config = lines[1]
|
||||
tokens = lines[1].split(' ')
|
||||
go_mode = tokens[0]
|
||||
go_config = tokens[1]
|
||||
compiler_backend_static_compilation = go_mode == 'c-archive'
|
||||
if compiler_backend_static_compilation
|
||||
vala_flags += '--define=COMPILER_BACKEND_STATIC_COMPILATION'
|
||||
endif
|
||||
else
|
||||
error_message = lines[1]
|
||||
if compiler_backend_opt.enabled()
|
||||
|
||||
+2
-1
@@ -510,7 +510,8 @@ def function_is_public(name):
|
||||
"parse_control_address",
|
||||
"parse_cluster_address",
|
||||
"parse_socket_address",
|
||||
"negotiate_connection"
|
||||
"negotiate_connection",
|
||||
"check_kernel_version",
|
||||
]
|
||||
|
||||
def parse_vala_object_types(source) -> List[ApiObjectType]:
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
extern void FRIDA_CGO_INIT_FUNC ();
|
||||
|
||||
void
|
||||
frida_compiler_backend_init (void)
|
||||
_frida_compiler_backend_init_go_runtime (void)
|
||||
{
|
||||
FRIDA_CGO_INIT_FUNC ();
|
||||
}
|
||||
@@ -21,7 +21,7 @@ frida_compiler_backend_init (void)
|
||||
#else
|
||||
|
||||
void
|
||||
frida_compiler_backend_init (void)
|
||||
_frida_compiler_backend_init_go_runtime (void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -144,8 +144,8 @@ type BuildEndCallback func()
|
||||
type BuildOutputCallback func(bundle string)
|
||||
type BuildDiagnosticCallback func(d Diagnostic)
|
||||
|
||||
//export frida_compiler_backend_build
|
||||
func frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
//export _frida_compiler_backend_build
|
||||
func _frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
disableTypeCheck, sourceMap, compress uintptr,
|
||||
onCompleteFn C.FridaBuildCompleteFunc, onCompleteData unsafe.Pointer, onCompleteDataDestroy C.FridaDestroyFunc,
|
||||
onDiagnosticFn C.FridaDiagnosticFunc, onDiagnosticData unsafe.Pointer) {
|
||||
@@ -179,8 +179,8 @@ func frida_compiler_backend_build(cProjectRoot, cEntrypoint *C.char, outputForma
|
||||
}()
|
||||
}
|
||||
|
||||
//export frida_compiler_backend_watch
|
||||
func frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
//export _frida_compiler_backend_watch
|
||||
func _frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputFormat C.FridaOutputFormat, bundleFormat C.FridaBundleFormat,
|
||||
disableTypeCheck, sourceMap, compress uintptr,
|
||||
onReadyFn C.FridaWatchReadyFunc, onReadyData unsafe.Pointer, onReadyDataDestroy C.FridaDestroyFunc,
|
||||
onStartingFn C.FridaStartingFunc, onStartingData unsafe.Pointer,
|
||||
@@ -238,8 +238,8 @@ func frida_compiler_backend_watch(cProjectRoot, cEntrypoint *C.char, outputForma
|
||||
}()
|
||||
}
|
||||
|
||||
//export frida_compiler_backend_watch_session_dispose
|
||||
func frida_compiler_backend_watch_session_dispose(h uintptr) {
|
||||
//export _frida_compiler_backend_watch_session_dispose
|
||||
func _frida_compiler_backend_watch_session_dispose(h uintptr) {
|
||||
handle := cgo.Handle(h)
|
||||
session := handle.Value().(*WatchSession)
|
||||
session.Dispose()
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
global:
|
||||
_frida_compiler_backend_*;
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
||||
@@ -57,62 +57,79 @@ def build_backend(
|
||||
run(npm, "install")
|
||||
# run(npm, "link", "/home/oleavr/src/frida-fs")
|
||||
|
||||
backend_a = priv_dir / "frida-compiler-backend.a"
|
||||
backend_h = priv_dir / "frida-compiler-backend.h"
|
||||
mode = config["mode"]
|
||||
|
||||
backend_stem = "frida-compiler-backend"
|
||||
if mode == "c-shared":
|
||||
backend_stem += "-raw"
|
||||
|
||||
backend_a = priv_dir / f"{backend_stem}.a"
|
||||
backend_shlib = priv_dir / (f"{backend_stem}" + config["shlib_suffix"])
|
||||
backend_h = priv_dir / f"{backend_stem}.h"
|
||||
|
||||
extra_go_args = config["extra_go_args"].copy()
|
||||
|
||||
if mode == "c-shared":
|
||||
version_script = priv_dir / "backend.version"
|
||||
extra_go_args.append(f"-ldflags=-linkmode=external -extldflags=-Wl,--version-script={version_script}")
|
||||
|
||||
run(
|
||||
go,
|
||||
"build",
|
||||
"-buildmode=c-archive",
|
||||
f"-buildmode={mode}",
|
||||
"-o",
|
||||
backend_a.name,
|
||||
backend_a.name if mode == "c-archive" else backend_shlib.name,
|
||||
"-buildvcs=false",
|
||||
*config["extra_go_args"],
|
||||
*extra_go_args,
|
||||
*go_sources,
|
||||
)
|
||||
|
||||
symbols_to_scramble = detect_conflictful_symbols_in_archive(backend_a, config["nm"])
|
||||
scramble_symbols_in_archive(backend_a, symbols_to_scramble, config["ranlib"])
|
||||
if mode == "c-archive":
|
||||
symbols_to_scramble = detect_conflictful_symbols_in_archive(backend_a, config["nm"])
|
||||
scramble_symbols_in_archive(backend_a, symbols_to_scramble, config["ranlib"])
|
||||
|
||||
if (mingw := config.get("mingw")) is not None and (abi := config["abi"]) in {
|
||||
"x86",
|
||||
"x86_64",
|
||||
}:
|
||||
prefix = Path(mingw["prefix"])
|
||||
ar = config["ar"]
|
||||
if (mingw := config.get("mingw")) is not None and (abi := config["abi"]) in {
|
||||
"x86",
|
||||
"x86_64",
|
||||
}:
|
||||
prefix = Path(mingw["prefix"])
|
||||
ar = config["ar"]
|
||||
|
||||
libmingwex_a = prefix / "lib" / "libmingwex.a"
|
||||
if not libmingwex_a.exists():
|
||||
libmingwex_a = prefix / mingw["triplet"] / "lib" / "libmingwex.a"
|
||||
libmingwex_a = prefix / "lib" / "libmingwex.a"
|
||||
if not libmingwex_a.exists():
|
||||
libmingwex_a = prefix / mingw["triplet"] / "lib" / "libmingwex.a"
|
||||
|
||||
libgcc_objects = [
|
||||
"_chkstk_ms.o",
|
||||
]
|
||||
run(*ar, "x", mingw["libcc"], *libgcc_objects)
|
||||
|
||||
gwex_objflavor = "64" if abi == "x86_64" else "32"
|
||||
gwex_objects = [
|
||||
f"lib{gwex_objflavor}_libmingwex_a-{name}.o"
|
||||
for name in [
|
||||
"dmisc",
|
||||
"gdtoa",
|
||||
"gmisc",
|
||||
"mingw_fprintf",
|
||||
"mingw_pformat",
|
||||
"misc",
|
||||
libgcc_objects = [
|
||||
"_chkstk_ms.o",
|
||||
]
|
||||
]
|
||||
run(*ar, "x", libmingwex_a, *gwex_objects)
|
||||
run(*ar, "x", mingw["libcc"], *libgcc_objects)
|
||||
|
||||
extra_objects = []
|
||||
if abi == "x86_64":
|
||||
run(*ar, "x", backend_a.name, "go.o")
|
||||
sort_pdata_in_object(Path(priv_dir) / "go.o")
|
||||
extra_objects.append("go.o")
|
||||
gwex_objflavor = "64" if abi == "x86_64" else "32"
|
||||
gwex_objects = [
|
||||
f"lib{gwex_objflavor}_libmingwex_a-{name}.o"
|
||||
for name in [
|
||||
"dmisc",
|
||||
"gdtoa",
|
||||
"gmisc",
|
||||
"mingw_fprintf",
|
||||
"mingw_pformat",
|
||||
"misc",
|
||||
]
|
||||
]
|
||||
run(*ar, "x", libmingwex_a, *gwex_objects)
|
||||
|
||||
run(*ar, "rs", backend_a.name, *libgcc_objects, *gwex_objects, *extra_objects)
|
||||
extra_objects = []
|
||||
if abi == "x86_64":
|
||||
run(*ar, "x", backend_a.name, "go.o")
|
||||
sort_pdata_in_object(Path(priv_dir) / "go.o")
|
||||
extra_objects.append("go.o")
|
||||
|
||||
run(*ar, "rs", backend_a.name, *libgcc_objects, *gwex_objects, *extra_objects)
|
||||
|
||||
shutil.copy(priv_dir / backend_a.name, output_dir / backend_a.name)
|
||||
else:
|
||||
shutil.copy(priv_dir / backend_shlib.name, output_dir / backend_shlib.name)
|
||||
|
||||
shutil.copy(priv_dir / backend_a.name, output_dir / backend_a.name)
|
||||
shutil.copy(priv_dir / backend_h.name, output_dir / backend_h.name)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[resource-compiler]
|
||||
namespace = Frida.Data.Compiler
|
||||
+81
-26
@@ -29,6 +29,8 @@ namespace Frida {
|
||||
|
||||
public async string build (string entrypoint, BuildOptions? options = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
CompilerBackend.check_available ();
|
||||
|
||||
BuildOptions opts = (options != null) ? options : new BuildOptions ();
|
||||
string project_root = compute_project_root (entrypoint, opts);
|
||||
|
||||
@@ -77,6 +79,8 @@ namespace Frida {
|
||||
|
||||
public async void watch (string entrypoint, WatchOptions? options = null, Cancellable? cancellable = null)
|
||||
throws Error, IOError {
|
||||
CompilerBackend.check_available ();
|
||||
|
||||
WatchOptions opts = (options != null) ? options : new WatchOptions ();
|
||||
string project_root = compute_project_root (entrypoint, opts);
|
||||
|
||||
@@ -227,40 +231,82 @@ namespace Frida {
|
||||
}
|
||||
|
||||
namespace CompilerBackend {
|
||||
private extern void init ();
|
||||
|
||||
private void init () {
|
||||
#if HAVE_COMPILER_BACKEND
|
||||
private extern void build (string project_root, string entrypoint, OutputFormat output_format, BundleFormat bundle_format,
|
||||
size_t disable_type_check, size_t source_map, size_t compress, owned BuildCompleteFunc on_complete,
|
||||
DiagnosticFunc on_diagnostic);
|
||||
private extern void watch (string project_root, string entrypoint, OutputFormat output_format, BundleFormat bundle_format,
|
||||
size_t disable_type_check, size_t source_map, size_t compress, owned WatchReadyFunc on_ready,
|
||||
StartingFunc on_starting, FinishedFunc on_finished, OutputFunc on_output, DiagnosticFunc on_diagnostic);
|
||||
|
||||
namespace WatchSession {
|
||||
private extern void dispose (size_t handle);
|
||||
}
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
_init_go_runtime ();
|
||||
build = (BuildFunc) _build;
|
||||
watch = (WatchFunc) _watch;
|
||||
WatchSession.dispose = (WatchSession.DisposeFunc) WatchSession._dispose;
|
||||
#else
|
||||
private const string DISABLED_MESSAGE = "Compiler backend disabled at build-time";
|
||||
unowned uint8[] backend_so = Frida.Data.Compiler.get_frida_compiler_backend_so_blob ().data;
|
||||
|
||||
private void build (string project_root, string entrypoint, OutputFormat output_format, BundleFormat bundle_format,
|
||||
size_t disable_type_check, size_t source_map, size_t compress, owned BuildCompleteFunc on_complete,
|
||||
DiagnosticFunc on_diagnostic) {
|
||||
on_complete (null, DISABLED_MESSAGE);
|
||||
}
|
||||
FileDescriptor fd;
|
||||
if (MemoryFileDescriptor.is_supported ()) {
|
||||
fd = MemoryFileDescriptor.from_bytes ("frida-compiler-backend.so", new Bytes.static (backend_so));
|
||||
} else {
|
||||
string name_used;
|
||||
try {
|
||||
fd = new FileDescriptor (FileUtils.open_tmp ("frida-compiler-backend-XXXXXX.so", out name_used));
|
||||
FileUtils.unlink (name_used);
|
||||
|
||||
private void watch (string project_root, string entrypoint, OutputFormat output_format, BundleFormat bundle_format,
|
||||
size_t disable_type_check, size_t source_map, size_t compress, owned WatchReadyFunc on_ready,
|
||||
StartingFunc on_starting, FinishedFunc on_finished, OutputFunc on_output, DiagnosticFunc on_diagnostic) {
|
||||
on_ready (0, DISABLED_MESSAGE);
|
||||
}
|
||||
var output = new UnixOutputStream (fd.handle, false);
|
||||
output.write_all (backend_so, null);
|
||||
|
||||
namespace WatchSession {
|
||||
private void dispose (size_t handle) {
|
||||
Posix.lseek (fd.handle, 0, Posix.SEEK_SET);
|
||||
} catch (GLib.Error e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
Module backend;
|
||||
try {
|
||||
backend = new Module ("/proc/self/fd/%d".printf (fd.handle), LOCAL);
|
||||
} catch (ModuleError e) {
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
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");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
private void check_available () throws Error {
|
||||
if (build == null)
|
||||
throw new Error.NOT_SUPPORTED ("Compiler backend disabled at build-time");
|
||||
}
|
||||
|
||||
private BuildFunc? build;
|
||||
private WatchFunc? watch;
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void BuildFunc (string project_root, string entrypoint, OutputFormat output_format,
|
||||
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
|
||||
owned BuildCompleteFunc on_complete, DiagnosticFunc on_diagnostic);
|
||||
|
||||
[CCode (has_target = false)]
|
||||
private delegate void WatchFunc (string project_root, string entrypoint, OutputFormat output_format,
|
||||
BundleFormat bundle_format, size_t disable_type_check, size_t source_map, size_t compress,
|
||||
owned WatchReadyFunc on_ready, StartingFunc on_starting, FinishedFunc on_finished, OutputFunc on_output,
|
||||
DiagnosticFunc on_diagnostic);
|
||||
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
private extern void _init_go_runtime ();
|
||||
private extern void _build ();
|
||||
private extern void _watch ();
|
||||
#endif
|
||||
|
||||
namespace WatchSession {
|
||||
[CCode (has_target = false)]
|
||||
private delegate void DisposeFunc (size_t handle);
|
||||
|
||||
private DisposeFunc? dispose;
|
||||
|
||||
#if COMPILER_BACKEND_STATIC_COMPILATION
|
||||
private extern void _dispose ();
|
||||
#endif
|
||||
}
|
||||
|
||||
private delegate void BuildCompleteFunc (owned string? bundle, owned string? error_message);
|
||||
private delegate void WatchReadyFunc (size_t session_handle, owned string? error_message);
|
||||
@@ -269,6 +315,15 @@ namespace Frida {
|
||||
private delegate void OutputFunc (owned string bundle);
|
||||
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_STATIC_COMPILATION
|
||||
private static T resolve_symbol<T> (Module m, string name) {
|
||||
void * address;
|
||||
if (!m.symbol (name, out address))
|
||||
assert_not_reached ();
|
||||
return (T) address;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private string compute_project_root (string entrypoint, CompilerOptions options) {
|
||||
|
||||
@@ -9,6 +9,7 @@ if have_compiler_backend
|
||||
'go.mod',
|
||||
'go.sum',
|
||||
'backend.go',
|
||||
'backend.version',
|
||||
'tscompiler.go',
|
||||
'tsconfig.go',
|
||||
'shims.go',
|
||||
@@ -16,7 +17,7 @@ if have_compiler_backend
|
||||
'package-lock.json',
|
||||
],
|
||||
output: [
|
||||
'frida-compiler-backend.a',
|
||||
'frida-compiler-backend' + (compiler_backend_static_compilation ? '.a' : ('-raw' + shlib_suffix)),
|
||||
'frida-compiler-backend.h',
|
||||
],
|
||||
command: [
|
||||
@@ -30,5 +31,35 @@ if have_compiler_backend
|
||||
'@INPUT@',
|
||||
],
|
||||
)
|
||||
backend_libs += compiler_backend[0]
|
||||
if compiler_backend_static_compilation
|
||||
backend_libs += compiler_backend[0]
|
||||
else
|
||||
filename = 'frida-compiler-backend' + shlib_suffix
|
||||
identity = (host_os_family == 'darwin') ? 'FridaCompilerBackend' : filename
|
||||
compiler_backend_shlib = custom_target('frida-compiler-backend-shlib',
|
||||
input: compiler_backend[0],
|
||||
output: filename,
|
||||
command: post_process + ['shared-library', identity, 'elf-cleaner:off'],
|
||||
)
|
||||
|
||||
compiler_data = custom_target('frida-data-compiler',
|
||||
input: [
|
||||
'compiler.resources',
|
||||
compiler_backend_shlib,
|
||||
],
|
||||
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
|
||||
@@ -3167,114 +3167,9 @@ namespace Frida {
|
||||
return Linux.syscall (SysCall.tgkill, tgid, tid, sig);
|
||||
}
|
||||
|
||||
private uint linux_major = 0;
|
||||
private uint linux_minor = 0;
|
||||
|
||||
public bool check_kernel_version (uint major, uint minor) {
|
||||
if (linux_major == 0) {
|
||||
var name = Posix.utsname ();
|
||||
name.release.scanf ("%u.%u", out linux_major, out linux_minor);
|
||||
}
|
||||
|
||||
return (linux_major == major && linux_minor >= minor) || linux_major > major;
|
||||
}
|
||||
|
||||
public extern bool _syscall_satisfies (int syscall_id, LinuxSyscall mask);
|
||||
|
||||
public class FileDescriptor : Object, FileDescriptorBased {
|
||||
public int handle;
|
||||
|
||||
public FileDescriptor (int handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
~FileDescriptor () {
|
||||
if (handle != -1)
|
||||
Posix.close (handle);
|
||||
}
|
||||
|
||||
public int steal () {
|
||||
int result = handle;
|
||||
handle = -1;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int get_fd () {
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PidFileDescriptor : FileDescriptor {
|
||||
private uint pid;
|
||||
|
||||
private PidFileDescriptor (int fd, uint pid) {
|
||||
base (fd);
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public static bool is_supported () {
|
||||
return check_kernel_version (5, 3);
|
||||
}
|
||||
|
||||
public static bool getfd_is_supported () {
|
||||
return check_kernel_version (5, 6);
|
||||
}
|
||||
|
||||
public static PidFileDescriptor from_pid (uint pid) throws Error {
|
||||
int fd = pidfd_open (pid, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new PidFileDescriptor (fd, pid);
|
||||
}
|
||||
|
||||
public FileDescriptor getfd (int targetfd) throws Error {
|
||||
int fd = pidfd_getfd (handle, targetfd, 0);
|
||||
if (fd == -1)
|
||||
throw_pidfd_error (pid, errno);
|
||||
return new FileDescriptor (fd);
|
||||
}
|
||||
|
||||
private static int pidfd_open (uint pid, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
private static int pidfd_getfd (int pidfd, int targetfd, uint flags) {
|
||||
return Linux.syscall (SysCall.pidfd_getfd, pidfd, targetfd, flags);
|
||||
}
|
||||
|
||||
[NoReturn]
|
||||
private static void throw_pidfd_error (uint pid, int err) throws Error {
|
||||
switch (err) {
|
||||
case Posix.ESRCH:
|
||||
throw new Error.PROCESS_NOT_FOUND ("Process not found");
|
||||
case Posix.EPERM:
|
||||
throw new Error.PERMISSION_DENIED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
default:
|
||||
throw new Error.NOT_SUPPORTED ("Unable to use pidfd for pid %u: %s", pid, strerror (err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace MemoryFileDescriptor {
|
||||
public bool is_supported () {
|
||||
return check_kernel_version (3, 17);
|
||||
}
|
||||
|
||||
public static FileDescriptor from_bytes (string name, Bytes bytes) {
|
||||
assert (is_supported ());
|
||||
|
||||
var fd = new FileDescriptor (memfd_create (name, 0));
|
||||
unowned uint8[] data = bytes.get_data ();
|
||||
ssize_t n = Posix.write (fd.handle, data, data.length);
|
||||
assert (n == data.length);
|
||||
return fd;
|
||||
}
|
||||
|
||||
private int memfd_create (string name, uint flags) {
|
||||
return Linux.syscall (SysCall.memfd_create, name, flags);
|
||||
}
|
||||
}
|
||||
|
||||
private void make_pty (out FileDescriptor read, out FileDescriptor write) throws Error {
|
||||
#if HAVE_OPENPTY
|
||||
int rfd = -1, wfd = -1;
|
||||
|
||||
+2
-3
@@ -365,16 +365,14 @@ if have_local_backend
|
||||
helper_backend_vala_args = [
|
||||
base_vala_args,
|
||||
gum_vala_args,
|
||||
'--pkg=gio-unix-2.0',
|
||||
'--pkg=posix',
|
||||
]
|
||||
helper_backend = static_library('frida-helper-backend', helper_backend_sources,
|
||||
c_args: frida_component_cflags,
|
||||
vala_args: [
|
||||
helper_backend_vala_args,
|
||||
'--vapidir=' + meson.current_source_dir() / 'linux',
|
||||
'--pkg=frida-sysapi',
|
||||
'--pkg=linux',
|
||||
'--pkg=linux-system',
|
||||
'--pkg=frida-gum-linux-1.0',
|
||||
],
|
||||
dependencies: [gio_dep, gio_unix_dep, gee_dep, gum_dep, base_dep] + helper_backend_extra_deps,
|
||||
@@ -635,6 +633,7 @@ core = library('frida-core', core_sources,
|
||||
base_vala_args,
|
||||
gumjs_inspector_vala_args,
|
||||
'--pkg=gmodule-2.0',
|
||||
'--pkg=libsoup-3.0',
|
||||
],
|
||||
vala_gir: core_gir_name,
|
||||
link_args: backend_libs_private,
|
||||
|
||||
+424
-253
@@ -108,9 +108,9 @@ namespace Frida {
|
||||
foreach (string spec_str in opts.specs) {
|
||||
string name, version_spec_val;
|
||||
int at = spec_str.last_index_of ("@");
|
||||
if (at != -1) {
|
||||
name = spec_str.substring (0, at);
|
||||
version_spec_val = spec_str.substring (at + 1);
|
||||
if (at > 0) {
|
||||
name = spec_str[:at];
|
||||
version_spec_val = spec_str[at + 1:];
|
||||
} else {
|
||||
name = spec_str;
|
||||
version_spec_val = "latest";
|
||||
@@ -120,9 +120,8 @@ namespace Frida {
|
||||
|
||||
install_progress (PREPARING_DEPENDENCIES, 0.05);
|
||||
var wanted_deps_list = new Gee.ArrayList<PackageDependency> ();
|
||||
foreach (var dep_entry in manifest.dependencies.all.values) {
|
||||
foreach (var dep_entry in manifest.dependencies.all.values)
|
||||
wanted_deps_list.add (dep_entry);
|
||||
}
|
||||
foreach (var e in specs.entries) {
|
||||
string name = e.key;
|
||||
string version_spec_val = e.value;
|
||||
@@ -144,6 +143,7 @@ namespace Frida {
|
||||
});
|
||||
}
|
||||
|
||||
var tracker = new InstallProgressTracker ();
|
||||
var all_physical_installs = new Gee.HashMap<string, Promise<PackageLockEntry>> ();
|
||||
var resolution_cache = new Gee.HashMap<string, Promise<ResolvedPackageData>> ();
|
||||
var packument_cache = new Gee.HashMap<string, Promise<Json.Node>> ();
|
||||
@@ -165,95 +165,59 @@ namespace Frida {
|
||||
project_root,
|
||||
manifest,
|
||||
prefer_lockfile_for_this_dep,
|
||||
cancellable,
|
||||
dep_link_promise,
|
||||
tracker,
|
||||
all_physical_installs,
|
||||
resolution_cache,
|
||||
packument_cache,
|
||||
top_level_placements
|
||||
top_level_placements,
|
||||
dep_link_promise,
|
||||
cancellable
|
||||
);
|
||||
}
|
||||
|
||||
double dep_processing_start_fraction = 0.1;
|
||||
double dep_processing_span = 0.7;
|
||||
int completed_top_level_deps = 0;
|
||||
int num_wanted_deps = wanted_deps_list.size;
|
||||
if (num_wanted_deps == 0) {
|
||||
install_progress (RESOLVING_AND_INSTALLING_ALL, dep_processing_start_fraction + dep_processing_span, null);
|
||||
} else {
|
||||
foreach (var f in initial_dep_link_futures) {
|
||||
PackageLockEntry ple = yield f.wait_async (cancellable);
|
||||
completed_top_level_deps++;
|
||||
double current_fraction = dep_processing_start_fraction +
|
||||
((double) completed_top_level_deps / num_wanted_deps) * dep_processing_span;
|
||||
install_progress (RESOLVING_AND_INSTALLING_ALL, current_fraction, ple.toplevel_dep.name);
|
||||
}
|
||||
}
|
||||
var initial_dep_results = new Gee.ArrayList<PackageLockEntry> ();
|
||||
foreach (var f in initial_dep_link_futures)
|
||||
initial_dep_results.add (yield f.wait_async (cancellable));
|
||||
|
||||
double physical_completion_start_fraction = dep_processing_start_fraction + dep_processing_span;
|
||||
double physical_completion_span = 0.05;
|
||||
install_progress (AWAITING_COMPLETION, physical_completion_start_fraction);
|
||||
foreach (var promise in all_physical_installs.values)
|
||||
yield promise.future.wait_async (cancellable);
|
||||
install_progress (DEPENDENCIES_PROCESSED, physical_completion_start_fraction + physical_completion_span);
|
||||
install_progress (DEPENDENCIES_PROCESSED, 0.98);
|
||||
|
||||
var finished_installs = new Gee.HashMap<string, PackageLockEntry> ();
|
||||
var finished_installs = new Gee.TreeMap<string, PackageLockEntry> ();
|
||||
foreach (var entry in all_physical_installs.entries)
|
||||
finished_installs[entry.key] = yield entry.value.future.wait_async (cancellable);
|
||||
|
||||
var pkgs = new Gee.ArrayList<Package> ();
|
||||
var new_manifest_dependencies = new PackageDependencies ();
|
||||
foreach (var future_ple in initial_dep_link_futures) {
|
||||
PackageLockEntry ple = yield future_ple.wait_async (cancellable);
|
||||
PackageDependency? original_wanted_dep = null;
|
||||
foreach (var wd in wanted_deps_list) {
|
||||
if (wd == ple.toplevel_dep) {
|
||||
original_wanted_dep = wd;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (original_wanted_dep != null) {
|
||||
new_manifest_dependencies.add (new PackageDependency () {
|
||||
name = ple.name,
|
||||
version = original_wanted_dep.derive_version (ple.version),
|
||||
role = original_wanted_dep.role,
|
||||
});
|
||||
}
|
||||
foreach (var ple in initial_dep_results) {
|
||||
if (ple.newly_installed)
|
||||
pkgs.add (new Package (ple.name, ple.version, ple.description));
|
||||
|
||||
var original_dep = ple.toplevel_dep;
|
||||
var version = specs.has_key (ple.name)
|
||||
? original_dep.derive_version (ple.version)
|
||||
: original_dep.version;
|
||||
new_manifest_dependencies.add (new PackageDependency () {
|
||||
name = ple.name,
|
||||
version = version,
|
||||
role = original_dep.role,
|
||||
});
|
||||
}
|
||||
manifest.dependencies = new_manifest_dependencies;
|
||||
|
||||
double finalizing_manifests_start_fraction = physical_completion_start_fraction + physical_completion_span;
|
||||
install_progress (FINALIZING_MANIFESTS, finalizing_manifests_start_fraction);
|
||||
yield write_back_manifests (manifest, finished_installs, pkg_json_file, lock_file, cancellable);
|
||||
install_progress (FINALIZING_MANIFESTS, 0.95);
|
||||
|
||||
var pkgs = new Gee.ArrayList<Package> ();
|
||||
foreach (var future_ple in initial_dep_link_futures) {
|
||||
PackageLockEntry e = yield future_ple.wait_async (cancellable);
|
||||
bool already_added = false;
|
||||
foreach (var p in pkgs) {
|
||||
if (p.name == e.name && p.version == e.version) {
|
||||
already_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!already_added)
|
||||
pkgs.add (new Package (e.name, e.version, e.description));
|
||||
}
|
||||
install_progress (FINALIZING_MANIFESTS, 0.99);
|
||||
yield write_back_manifests (manifest, initial_dep_results, finished_installs, pkg_json_file, lock_file,
|
||||
cancellable);
|
||||
|
||||
install_progress (COMPLETE, 1.0);
|
||||
return new PackageInstallResult (new PackageList (pkgs));
|
||||
}
|
||||
|
||||
private static File install_dir_for_dependency (string name, File node_modules_root) {
|
||||
File location = node_modules_root;
|
||||
foreach (unowned string part in name.split ("/"))
|
||||
location = location.get_child (part);
|
||||
return location;
|
||||
return new PackageInstallResult (new PackageList (pkgs));
|
||||
}
|
||||
|
||||
private class Manifest {
|
||||
public string? name;
|
||||
public string? version;
|
||||
public string? license;
|
||||
public Gee.List<FundingSource>? funding;
|
||||
public Gee.Map<string, string>? engines;
|
||||
public PackageDependencies dependencies = new PackageDependencies ();
|
||||
public Gee.Map<string, PackageLockPackageInfo> locked_packages = new Gee.HashMap<string, PackageLockPackageInfo> ();
|
||||
}
|
||||
@@ -275,11 +239,12 @@ namespace Frida {
|
||||
private enum PackageRole {
|
||||
RUNTIME,
|
||||
DEVELOPMENT,
|
||||
OPTIONAL
|
||||
OPTIONAL,
|
||||
PEER
|
||||
}
|
||||
|
||||
private class PackageDependencies {
|
||||
public Gee.Map<string, PackageDependency> all = new Gee.HashMap<string, PackageDependency> ();
|
||||
public Gee.Map<string, PackageDependency> all = new Gee.TreeMap<string, PackageDependency> ();
|
||||
|
||||
public Gee.Map<string, PackageDependency> runtime {
|
||||
get {
|
||||
@@ -297,15 +262,33 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
public Gee.Map<string, PackageDependency> optional {
|
||||
get {
|
||||
if (_optional == null)
|
||||
_optional = compute_subset_with_role (OPTIONAL);
|
||||
return _optional;
|
||||
}
|
||||
}
|
||||
|
||||
public Gee.Map<string, PackageDependency> peer {
|
||||
get {
|
||||
if (_peer == null)
|
||||
_peer = compute_subset_with_role (PEER);
|
||||
return _peer;
|
||||
}
|
||||
}
|
||||
|
||||
private Gee.Map<string, PackageDependency> _runtime;
|
||||
private Gee.Map<string, PackageDependency> _development;
|
||||
private Gee.Map<string, PackageDependency> _optional;
|
||||
private Gee.Map<string, PackageDependency> _peer;
|
||||
|
||||
public void add (PackageDependency d) {
|
||||
all[d.name] = d;
|
||||
}
|
||||
|
||||
private Gee.Map<string, PackageDependency> compute_subset_with_role (PackageRole role) {
|
||||
var result = new Gee.HashMap<string, PackageDependency> ();
|
||||
var result = new Gee.TreeMap<string, PackageDependency> ();
|
||||
foreach (PackageDependency d in all.values) {
|
||||
if (d.role == role)
|
||||
result[d.name] = d;
|
||||
@@ -326,26 +309,40 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class FundingSource {
|
||||
public string? as_string;
|
||||
public string? type;
|
||||
public string? url;
|
||||
}
|
||||
|
||||
private class PackageLockPackageInfo {
|
||||
public string path_key;
|
||||
public string? name;
|
||||
public string? version;
|
||||
public string? resolved;
|
||||
public string? integrity;
|
||||
public string? license;
|
||||
public Gee.List<FundingSource>? funding;
|
||||
public Gee.Map<string, string>? engines;
|
||||
public PackageDependencies dependencies = new PackageDependencies ();
|
||||
public bool is_dev = false;
|
||||
public bool is_optional = false;
|
||||
}
|
||||
|
||||
private class PackageLockEntry {
|
||||
public string id;
|
||||
public string name;
|
||||
public string version;
|
||||
public string resolved;
|
||||
public string integrity;
|
||||
public string? description;
|
||||
public string? license;
|
||||
public Gee.List<FundingSource>? funding;
|
||||
public Gee.Map<string, string>? engines;
|
||||
public PackageDependencies dependencies;
|
||||
public PackageDependency toplevel_dep;
|
||||
public bool newly_installed;
|
||||
public Gee.List<PackageLockEntry> children = new Gee.ArrayList<PackageLockEntry> ();
|
||||
}
|
||||
|
||||
private async Manifest load_manifest (File pkg_json_file, File lock_file, Cancellable? cancellable) throws Error, IOError {
|
||||
@@ -362,6 +359,9 @@ namespace Frida {
|
||||
m.version = r.get_string_value ();
|
||||
r.end_member ();
|
||||
|
||||
m.license = read_license (r);
|
||||
m.funding = read_funding (r);
|
||||
m.engines = read_engines (r);
|
||||
m.dependencies = read_dependencies (r);
|
||||
}
|
||||
|
||||
@@ -390,6 +390,11 @@ namespace Frida {
|
||||
lock_r.read_member ("name");
|
||||
pli.name = lock_r.get_string_value ();
|
||||
lock_r.end_member ();
|
||||
} else {
|
||||
int last_start = path_key.last_index_of ("/node_modules/");
|
||||
pli.name = (last_start != -1)
|
||||
? path_key[last_start + 14:]
|
||||
: path_key[13:];
|
||||
}
|
||||
|
||||
lock_r.read_member ("version");
|
||||
@@ -416,6 +421,9 @@ namespace Frida {
|
||||
lock_r.end_member ();
|
||||
}
|
||||
|
||||
pli.license = read_license (lock_r);
|
||||
pli.funding = read_funding (lock_r);
|
||||
pli.engines = read_engines (lock_r);
|
||||
pli.dependencies = read_dependencies_from_lock_entry (lock_r, path_key);
|
||||
|
||||
lock_r.read_member ("dev");
|
||||
@@ -437,8 +445,9 @@ namespace Frida {
|
||||
return m;
|
||||
}
|
||||
|
||||
private async void write_back_manifests (Manifest manifest, Gee.Map<string, PackageLockEntry> installed, File pkg_json_file,
|
||||
File lock_file, Cancellable? cancellable) throws Error, IOError {
|
||||
private async void write_back_manifests (Manifest manifest, Gee.List<PackageLockEntry> toplevel_installed,
|
||||
Gee.Map<string, PackageLockEntry> all_installed, File pkg_json_file, File lock_file,
|
||||
Cancellable? cancellable) throws Error, IOError {
|
||||
string? name = null;
|
||||
Json.Node? root = null;
|
||||
string? old_pkg_json = null;
|
||||
@@ -474,13 +483,16 @@ namespace Frida {
|
||||
}
|
||||
|
||||
Json.Object obj = root.get_object ();
|
||||
|
||||
var deps_obj = new Json.Object ();
|
||||
obj.set_object_member ("dependencies", deps_obj);
|
||||
foreach (PackageLockEntry e in installed.values) {
|
||||
PackageDependency toplevel_dep = e.toplevel_dep;
|
||||
if (e.name == toplevel_dep.name)
|
||||
deps_obj.set_string_member (e.name, toplevel_dep.derive_version (e.version).spec);
|
||||
}
|
||||
foreach (PackageDependency dep in manifest.dependencies.runtime.values)
|
||||
deps_obj.set_string_member (dep.name, dep.version.spec);
|
||||
|
||||
var dev_deps_obj = new Json.Object ();
|
||||
obj.set_object_member ("devDependencies", dev_deps_obj);
|
||||
foreach (PackageDependency dep in manifest.dependencies.development.values)
|
||||
dev_deps_obj.set_string_member (dep.name, dep.version.spec);
|
||||
|
||||
string new_pkg_json = generate_npm_style_json (root, indent_level, indent_char);
|
||||
bool pkg_json_changed = old_pkg_json == null || new_pkg_json != old_pkg_json;
|
||||
@@ -488,9 +500,10 @@ namespace Frida {
|
||||
yield FS.write_all_text (pkg_json_file, new_pkg_json, cancellable);
|
||||
|
||||
var b = new Json.Builder ();
|
||||
b.begin_object ()
|
||||
.set_member_name ("name")
|
||||
.add_string_value (name)
|
||||
b.begin_object ();
|
||||
write_name (name, b);
|
||||
write_version (manifest.version, b);
|
||||
b
|
||||
.set_member_name ("lockfileVersion")
|
||||
.add_int_value (3)
|
||||
.set_member_name ("requires")
|
||||
@@ -498,26 +511,19 @@ namespace Frida {
|
||||
.set_member_name ("packages")
|
||||
.begin_object ();
|
||||
|
||||
b.set_member_name ("")
|
||||
.begin_object ();
|
||||
if (manifest.name != null)
|
||||
b.set_member_name ("name").add_string_value (manifest.name);
|
||||
if (manifest.version != null)
|
||||
b.set_member_name ("version").add_string_value (manifest.version);
|
||||
b.set_member_name ("dependencies").begin_object ();
|
||||
foreach (PackageLockEntry e in installed.values) {
|
||||
PackageDependency toplevel_dep = e.toplevel_dep;
|
||||
if (e.name == toplevel_dep.name) {
|
||||
b
|
||||
.set_member_name (e.name)
|
||||
.add_string_value (toplevel_dep.derive_version (e.version).spec);
|
||||
}
|
||||
}
|
||||
b
|
||||
.end_object ()
|
||||
.end_object ();
|
||||
b.set_member_name ("").begin_object ();
|
||||
write_name (manifest.name, b);
|
||||
write_version (manifest.version, b);
|
||||
write_license (manifest.license, b);
|
||||
write_funding (manifest.funding, b);
|
||||
write_dependencies_section ("dependencies", manifest.dependencies.runtime, b);
|
||||
write_dependencies_section ("devDependencies", manifest.dependencies.development, b);
|
||||
write_engines (manifest.engines, b);
|
||||
b.end_object ();
|
||||
|
||||
foreach (var entry in installed.entries) {
|
||||
var runtime_reach = compute_runtime_reach (toplevel_installed, all_installed);
|
||||
|
||||
foreach (var entry in all_installed.entries) {
|
||||
string lockfile_key = entry.key;
|
||||
PackageLockEntry e = entry.value;
|
||||
bool is_root_package = lockfile_key == "";
|
||||
@@ -526,59 +532,26 @@ namespace Frida {
|
||||
.set_member_name (lockfile_key)
|
||||
.begin_object ();
|
||||
|
||||
if (is_root_package) {
|
||||
b
|
||||
.set_member_name ("name")
|
||||
.add_string_value (e.name);
|
||||
}
|
||||
if (is_root_package)
|
||||
write_name (e.name, b);
|
||||
|
||||
write_version (e.version, b);
|
||||
|
||||
b
|
||||
.set_member_name ("version")
|
||||
.add_string_value (e.version)
|
||||
.set_member_name ("resolved")
|
||||
.add_string_value (e.resolved)
|
||||
.set_member_name ("integrity")
|
||||
.add_string_value (e.integrity);
|
||||
|
||||
if (e.license != null)
|
||||
b.set_member_name ("license").add_string_value (e.license);
|
||||
if (!runtime_reach.contains (lockfile_key))
|
||||
b.set_member_name ("dev").add_boolean_value (true);
|
||||
|
||||
if (e.toplevel_dep.role == DEVELOPMENT && e.name == e.toplevel_dep.name) {
|
||||
b
|
||||
.set_member_name ("dev")
|
||||
.add_boolean_value (true);
|
||||
}
|
||||
|
||||
if (!e.dependencies.runtime.is_empty) {
|
||||
b.set_member_name ("dependencies").begin_object ();
|
||||
foreach (PackageDependency dep_info in e.dependencies.runtime.values) {
|
||||
b
|
||||
.set_member_name (dep_info.name)
|
||||
.add_string_value (dep_info.version.spec);
|
||||
}
|
||||
b.end_object ();
|
||||
}
|
||||
|
||||
if (!e.dependencies.all.is_empty) {
|
||||
bool has_optional = false;
|
||||
foreach (PackageDependency dep_info in e.dependencies.all.values) {
|
||||
if (dep_info.role == OPTIONAL) {
|
||||
has_optional = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (has_optional) {
|
||||
b.set_member_name ("optionalDependencies").begin_object ();
|
||||
foreach (PackageDependency dep_info in e.dependencies.all.values) {
|
||||
if (dep_info.role == OPTIONAL) {
|
||||
b
|
||||
.set_member_name (dep_info.name)
|
||||
.add_string_value (dep_info.version.spec);
|
||||
}
|
||||
}
|
||||
b.end_object ();
|
||||
}
|
||||
}
|
||||
write_license (e.license, b);
|
||||
write_dependencies_section ("dependencies", e.dependencies.runtime, b);
|
||||
write_engines (e.engines, b);
|
||||
write_dependencies_section ("optionalDependencies", e.dependencies.optional, b);
|
||||
write_funding (e.funding, b);
|
||||
write_dependencies_section ("peerDependencies", e.dependencies.peer, b);
|
||||
|
||||
b.end_object ();
|
||||
}
|
||||
@@ -591,6 +564,28 @@ namespace Frida {
|
||||
yield FS.write_all_text (lock_file, lock_json, cancellable);
|
||||
}
|
||||
|
||||
private static Gee.Set<string> compute_runtime_reach (Gee.List<PackageLockEntry> toplevel_installed,
|
||||
Gee.Map<string, PackageLockEntry> all_installed) {
|
||||
Gee.Set<string> runtime_reach = new Gee.HashSet<string> ();
|
||||
|
||||
Gee.Queue<string> q = new Gee.LinkedList<string> ();
|
||||
foreach (var ple in toplevel_installed)
|
||||
if (ple.toplevel_dep.role != DEVELOPMENT)
|
||||
q.offer (ple.id);
|
||||
|
||||
while (!q.is_empty) {
|
||||
string k = q.poll ();
|
||||
if (!runtime_reach.add (k))
|
||||
continue;
|
||||
|
||||
PackageLockEntry ple = all_installed[k];
|
||||
foreach (var sub in ple.children)
|
||||
q.offer (sub.id);
|
||||
}
|
||||
|
||||
return runtime_reach;
|
||||
}
|
||||
|
||||
private async void perform_install (
|
||||
string name,
|
||||
PackageVersion version_spec,
|
||||
@@ -599,12 +594,13 @@ namespace Frida {
|
||||
File project_root,
|
||||
Manifest manifest,
|
||||
bool prefer_lockfile_for_this_dep,
|
||||
Cancellable? cancellable,
|
||||
Promise<PackageLockEntry> dep_link_promise,
|
||||
InstallProgressTracker tracker,
|
||||
Gee.Map<string, Promise<PackageLockEntry>> all_physical_installs,
|
||||
Gee.Map<string, Promise<ResolvedPackageData>> resolution_cache,
|
||||
Gee.Map<string, Promise<Json.Node>> packument_cache,
|
||||
Gee.Map<string, string> top_level_placements) {
|
||||
Gee.Map<string, string> top_level_placements,
|
||||
Promise<PackageLockEntry> dep_link_promise,
|
||||
Cancellable? cancellable) {
|
||||
try {
|
||||
install_progress (RESOLVING_PACKAGE, -1.0, "%s@%s".printf (name, version_spec.spec));
|
||||
string resolution_id = name + "@" + version_spec.spec;
|
||||
@@ -629,7 +625,9 @@ namespace Frida {
|
||||
integrity = li.integrity,
|
||||
shasum = null,
|
||||
description = null,
|
||||
license = null,
|
||||
license = li.license,
|
||||
funding = li.funding,
|
||||
engines = li.engines,
|
||||
dependencies = li.dependencies
|
||||
};
|
||||
rpd_came_from_lockfile = true;
|
||||
@@ -645,7 +643,8 @@ namespace Frida {
|
||||
rpd_promise.resolve (rpd);
|
||||
break;
|
||||
}
|
||||
} catch (Error e) { }
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -685,111 +684,146 @@ namespace Frida {
|
||||
Promise<PackageLockEntry>? physical_install_promise = all_physical_installs[actual_install_lockfile_key];
|
||||
if (physical_install_promise != null) {
|
||||
PackageLockEntry existing_ple = yield physical_install_promise.future.wait_async (cancellable);
|
||||
|
||||
dep_link_promise.resolve (new PackageLockEntry () {
|
||||
id = actual_install_lockfile_key,
|
||||
name = existing_ple.name,
|
||||
version = existing_ple.version,
|
||||
resolved = existing_ple.resolved,
|
||||
integrity = existing_ple.integrity,
|
||||
description = existing_ple.description,
|
||||
license = existing_ple.license,
|
||||
funding = existing_ple.funding,
|
||||
engines = existing_ple.engines,
|
||||
dependencies = existing_ple.dependencies,
|
||||
toplevel_dep = toplevel_dep
|
||||
toplevel_dep = toplevel_dep,
|
||||
newly_installed = existing_ple.newly_installed,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
all_physical_installs[actual_install_lockfile_key] = dep_link_promise;
|
||||
var physical_install_completion = new Promise<PackageLockEntry> ();
|
||||
all_physical_installs[actual_install_lockfile_key] = physical_install_completion;
|
||||
|
||||
bool already_correctly_installed = false;
|
||||
if (target_dir.query_exists (cancellable)) {
|
||||
File installed_pkg_json_file = target_dir.get_child ("package.json");
|
||||
if (installed_pkg_json_file.query_exists (cancellable)) {
|
||||
try {
|
||||
Json.Reader installed_pkg_reader =
|
||||
yield load_json (installed_pkg_json_file, cancellable);
|
||||
if (installed_pkg_reader.read_member ("version")) {
|
||||
string? installed_version = installed_pkg_reader.get_string_value ();
|
||||
installed_pkg_reader.end_member ();
|
||||
if (installed_version == rpd.effective_version) {
|
||||
already_correctly_installed = true;
|
||||
install_progress (PACKAGE_ALREADY_INSTALLED, -1.0,
|
||||
"%s@%s".printf (rpd.name, rpd.effective_version));
|
||||
}
|
||||
}
|
||||
} catch (Error e) { }
|
||||
}
|
||||
}
|
||||
tracker.total_physical++;
|
||||
physical_install_completion.future.then (future => {
|
||||
tracker.completed_physical++;
|
||||
|
||||
var sub_dep_futures = new Gee.ArrayList<Future<PackageLockEntry>> ();
|
||||
File sub_deps_parent_node_modules_dir = target_dir.get_child ("node_modules");
|
||||
PackageDependencies dependencies_to_recurse = rpd.dependencies;
|
||||
double dep_processing_start_fraction = 0.1;
|
||||
double dep_processing_span = 0.88;
|
||||
double current_fraction = dep_processing_start_fraction
|
||||
+ ((double) tracker.completed_physical / tracker.total_physical) * dep_processing_span;
|
||||
|
||||
if (!dependencies_to_recurse.all.is_empty) {
|
||||
FS.mkdirp (sub_deps_parent_node_modules_dir, cancellable);
|
||||
}
|
||||
|
||||
foreach (PackageDependency d in dependencies_to_recurse.all.values) {
|
||||
bool install_this_sub_dep = d.role == RUNTIME || d.role == OPTIONAL;
|
||||
if (!install_this_sub_dep)
|
||||
continue;
|
||||
|
||||
var sub_dep_link_promise = new Promise<PackageLockEntry> ();
|
||||
sub_dep_futures.add (sub_dep_link_promise.future);
|
||||
perform_install.begin (
|
||||
d.name,
|
||||
d.version,
|
||||
toplevel_dep,
|
||||
sub_deps_parent_node_modules_dir,
|
||||
project_root,
|
||||
manifest,
|
||||
true,
|
||||
cancellable,
|
||||
sub_dep_link_promise,
|
||||
all_physical_installs,
|
||||
resolution_cache,
|
||||
packument_cache,
|
||||
top_level_placements
|
||||
);
|
||||
}
|
||||
foreach (var f in sub_dep_futures)
|
||||
yield f.wait_async (cancellable);
|
||||
|
||||
if (!already_correctly_installed) {
|
||||
yield download_and_unpack (rpd.name, rpd.effective_version, rpd.resolved_url, target_dir,
|
||||
rpd.integrity, rpd.shasum, cancellable);
|
||||
install_progress (PACKAGE_INSTALLED, -1.0, "%s@%s".printf (rpd.name, rpd.effective_version));
|
||||
}
|
||||
|
||||
if (rpd.description == null || rpd.license == null) {
|
||||
Json.Reader reader = yield load_json (target_dir.get_child ("package.json"), cancellable);
|
||||
update_rpd_description_license_from_reader (reader, rpd);
|
||||
}
|
||||
|
||||
dep_link_promise.resolve (new PackageLockEntry () {
|
||||
name = rpd.name,
|
||||
version = rpd.effective_version,
|
||||
resolved = rpd.resolved_url,
|
||||
integrity = rpd.integrity,
|
||||
description = rpd.description,
|
||||
license = rpd.license,
|
||||
dependencies = rpd.dependencies,
|
||||
toplevel_dep = toplevel_dep
|
||||
install_progress (RESOLVING_AND_INSTALLING_ALL, current_fraction);
|
||||
});
|
||||
|
||||
try {
|
||||
bool already_correctly_installed = false;
|
||||
if (target_dir.query_exists (cancellable)) {
|
||||
File installed_pkg_json_file = target_dir.get_child ("package.json");
|
||||
if (installed_pkg_json_file.query_exists (cancellable)) {
|
||||
try {
|
||||
Json.Reader installed_pkg_reader =
|
||||
yield load_json (installed_pkg_json_file, cancellable);
|
||||
if (installed_pkg_reader.read_member ("version")) {
|
||||
string? installed_version =
|
||||
installed_pkg_reader.get_string_value ();
|
||||
installed_pkg_reader.end_member ();
|
||||
if (installed_version == rpd.effective_version) {
|
||||
already_correctly_installed = true;
|
||||
install_progress (PACKAGE_ALREADY_INSTALLED, -1.0,
|
||||
"%s@%s".printf (rpd.name, rpd.effective_version));
|
||||
}
|
||||
}
|
||||
} catch (Error e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!already_correctly_installed) {
|
||||
yield download_and_unpack (rpd.name, rpd.effective_version, rpd.resolved_url, target_dir,
|
||||
rpd.integrity, rpd.shasum, cancellable);
|
||||
install_progress (PACKAGE_INSTALLED, -1.0,
|
||||
"%s@%s".printf (rpd.name, rpd.effective_version));
|
||||
}
|
||||
|
||||
if (rpd.description == null) {
|
||||
Json.Reader reader = yield load_json (target_dir.get_child ("package.json"), cancellable);
|
||||
rpd.description = read_description (reader);
|
||||
}
|
||||
|
||||
var ple = new PackageLockEntry () {
|
||||
id = actual_install_lockfile_key,
|
||||
name = rpd.name,
|
||||
version = rpd.effective_version,
|
||||
resolved = rpd.resolved_url,
|
||||
integrity = rpd.integrity,
|
||||
description = rpd.description,
|
||||
license = rpd.license,
|
||||
funding = rpd.funding,
|
||||
engines = rpd.engines,
|
||||
dependencies = rpd.dependencies,
|
||||
toplevel_dep = toplevel_dep,
|
||||
newly_installed = !already_correctly_installed,
|
||||
};
|
||||
|
||||
physical_install_completion.resolve (ple);
|
||||
|
||||
var sub_dep_futures = new Gee.ArrayList<Future<PackageLockEntry>> ();
|
||||
File sub_deps_parent_node_modules_dir = target_dir.get_child ("node_modules");
|
||||
PackageDependencies dependencies_to_recurse = rpd.dependencies;
|
||||
|
||||
if (!dependencies_to_recurse.all.is_empty)
|
||||
FS.mkdirp (sub_deps_parent_node_modules_dir, cancellable);
|
||||
|
||||
foreach (PackageDependency d in dependencies_to_recurse.all.values) {
|
||||
bool install_this_sub_dep = d.role == RUNTIME;
|
||||
if (!install_this_sub_dep)
|
||||
continue;
|
||||
|
||||
var sub_dep_link_promise = new Promise<PackageLockEntry> ();
|
||||
sub_dep_futures.add (sub_dep_link_promise.future);
|
||||
|
||||
perform_install.begin (
|
||||
d.name,
|
||||
d.version,
|
||||
toplevel_dep,
|
||||
sub_deps_parent_node_modules_dir,
|
||||
project_root,
|
||||
manifest,
|
||||
true,
|
||||
tracker,
|
||||
all_physical_installs,
|
||||
resolution_cache,
|
||||
packument_cache,
|
||||
top_level_placements,
|
||||
sub_dep_link_promise,
|
||||
cancellable
|
||||
);
|
||||
}
|
||||
foreach (var f in sub_dep_futures)
|
||||
ple.children.add (yield f.wait_async (cancellable));
|
||||
|
||||
dep_link_promise.resolve (ple);
|
||||
} catch (GLib.Error e) {
|
||||
physical_install_completion.reject (e);
|
||||
throw e;
|
||||
}
|
||||
} catch (GLib.Error e) {
|
||||
dep_link_promise.reject (e);
|
||||
}
|
||||
}
|
||||
|
||||
private class ResolvedPackageData {
|
||||
public string name;
|
||||
public string effective_version;
|
||||
public string resolved_url;
|
||||
public string? integrity;
|
||||
public string? shasum;
|
||||
public string? description;
|
||||
public string? license;
|
||||
public PackageDependencies dependencies;
|
||||
private class InstallProgressTracker {
|
||||
public int total_physical = 0;
|
||||
public int completed_physical = 0;
|
||||
}
|
||||
|
||||
private static File install_dir_for_dependency (string name, File node_modules_root) {
|
||||
File location = node_modules_root;
|
||||
foreach (unowned string part in name.split ("/"))
|
||||
location = location.get_child (part);
|
||||
return location;
|
||||
}
|
||||
|
||||
private async void fetch_and_resolve_package_data (string name, PackageVersion version_spec,
|
||||
@@ -879,6 +913,19 @@ namespace Frida {
|
||||
}
|
||||
}
|
||||
|
||||
private class ResolvedPackageData {
|
||||
public string name;
|
||||
public string effective_version;
|
||||
public string resolved_url;
|
||||
public string? integrity;
|
||||
public string? shasum;
|
||||
public string? description;
|
||||
public string? license;
|
||||
public Gee.List<FundingSource>? funding;
|
||||
public Gee.Map<string, string>? engines;
|
||||
public PackageDependencies dependencies;
|
||||
}
|
||||
|
||||
private async void fetch_packument_for_cache (string name, Promise<Json.Node> request, Cancellable? cancellable)
|
||||
throws Error, IOError {
|
||||
try {
|
||||
@@ -892,6 +939,8 @@ namespace Frida {
|
||||
private static void read_package_version_metadata (Json.Reader reader, ResolvedPackageData rpd) throws Error {
|
||||
rpd.description = read_description (reader);
|
||||
rpd.license = read_license (reader);
|
||||
rpd.funding = read_funding (reader);
|
||||
rpd.engines = read_engines (reader);
|
||||
|
||||
reader.read_member ("dist");
|
||||
|
||||
@@ -913,15 +962,20 @@ namespace Frida {
|
||||
|
||||
reader.end_member ();
|
||||
|
||||
rpd.license = read_license (reader);
|
||||
rpd.funding = read_funding (reader);
|
||||
rpd.engines = read_engines (reader);
|
||||
rpd.dependencies = read_dependencies (reader);
|
||||
}
|
||||
|
||||
private static void update_rpd_description_license_from_reader (Json.Reader reader, ResolvedPackageData rpd) {
|
||||
if (rpd.description == null)
|
||||
rpd.description = read_description (reader);
|
||||
private static void write_name (string? name, Json.Builder b) {
|
||||
if (name != null)
|
||||
b.set_member_name ("name").add_string_value (name);
|
||||
}
|
||||
|
||||
if (rpd.license == null)
|
||||
rpd.license = read_license (reader);
|
||||
private static void write_version (string? version, Json.Builder b) {
|
||||
if (version != null)
|
||||
b.set_member_name ("version").add_string_value (version);
|
||||
}
|
||||
|
||||
private static string? read_description (Json.Reader reader) {
|
||||
@@ -947,10 +1001,118 @@ namespace Frida {
|
||||
return license;
|
||||
}
|
||||
|
||||
private static void write_license (string? license, Json.Builder b) {
|
||||
if (license != null)
|
||||
b.set_member_name ("license").add_string_value (license);
|
||||
}
|
||||
|
||||
private static Gee.List<FundingSource>? read_funding (Json.Reader reader) throws Error {
|
||||
if (!reader.read_member ("funding")) {
|
||||
reader.end_member ();
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new Gee.ArrayList<FundingSource> ();
|
||||
|
||||
if (reader.is_array ()) {
|
||||
int n = reader.count_elements ();
|
||||
for (int i = 0; i < n; i++) {
|
||||
reader.read_element (i);
|
||||
read_funding_source (reader, result);
|
||||
reader.end_element ();
|
||||
}
|
||||
} else {
|
||||
read_funding_source (reader, result);
|
||||
}
|
||||
|
||||
reader.end_member ();
|
||||
|
||||
return result.is_empty ? null : result;
|
||||
}
|
||||
|
||||
private static void write_funding (Gee.List<FundingSource>? funding, Json.Builder b) {
|
||||
if (funding == null)
|
||||
return;
|
||||
b.set_member_name ("funding");
|
||||
if (funding.size == 1) {
|
||||
write_funding_source (funding.get (0), b);
|
||||
} else {
|
||||
b.begin_array ();
|
||||
foreach (var s in funding)
|
||||
write_funding_source (s, b);
|
||||
b.end_array ();
|
||||
}
|
||||
}
|
||||
|
||||
private static void read_funding_source (Json.Reader r, Gee.List<FundingSource> list) throws Error {
|
||||
if (r.is_object ()) {
|
||||
var source = new FundingSource ();
|
||||
r.read_member ("type");
|
||||
source.type = r.get_string_value ();
|
||||
r.end_member ();
|
||||
r.read_member ("url");
|
||||
source.url = r.get_string_value ();
|
||||
r.end_member ();
|
||||
list.add (source);
|
||||
} else {
|
||||
var val = r.get_string_value ();
|
||||
if (val == null)
|
||||
throw new Error.PROTOCOL ("Invalid 'funding' field");
|
||||
list.add (new FundingSource () { as_string = val });
|
||||
}
|
||||
}
|
||||
|
||||
private static void write_funding_source (FundingSource s, Json.Builder b) {
|
||||
if (s.as_string != null) {
|
||||
b.add_string_value (s.as_string);
|
||||
} else {
|
||||
b.begin_object ();
|
||||
if (s.type != null)
|
||||
b.set_member_name ("type").add_string_value (s.type);
|
||||
if (s.url != null)
|
||||
b.set_member_name ("url").add_string_value (s.url);
|
||||
b.end_object ();
|
||||
}
|
||||
}
|
||||
|
||||
private static Gee.Map<string, string>? read_engines (Json.Reader reader) throws Error {
|
||||
if (!reader.read_member ("engines")) {
|
||||
reader.end_member ();
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new Gee.TreeMap<string, string> ();
|
||||
string[]? members = reader.list_members ();
|
||||
if (members == null)
|
||||
throw new Error.PROTOCOL ("Invalid 'engines' field");
|
||||
foreach (unowned string name in members) {
|
||||
reader.read_member (name);
|
||||
string? val = reader.get_string_value ();
|
||||
if (val == null)
|
||||
throw new Error.PROTOCOL ("Invalid 'engines' value");
|
||||
result[name] = val;
|
||||
reader.end_member ();
|
||||
}
|
||||
|
||||
reader.end_member ();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void write_engines (Gee.Map<string, string>? engines, Json.Builder b) {
|
||||
if (engines == null)
|
||||
return;
|
||||
b.set_member_name ("engines").begin_object ();
|
||||
foreach (var e in engines.entries)
|
||||
b.set_member_name (e.key).add_string_value (e.value);
|
||||
b.end_object ();
|
||||
}
|
||||
|
||||
private static PackageDependencies read_dependencies (Json.Reader r) throws Error {
|
||||
var deps = new PackageDependencies ();
|
||||
|
||||
string[] section_keys = {"dependencies", "devDependencies"};
|
||||
string[] section_keys = { "dependencies", "devDependencies", "optionalDependencies", "peerDependencies" };
|
||||
PackageRole[] section_roles = { RUNTIME, DEVELOPMENT, OPTIONAL, PEER };
|
||||
for (uint i = 0; i != section_keys.length; i++) {
|
||||
unowned string section_key = section_keys[i];
|
||||
|
||||
@@ -963,7 +1125,7 @@ namespace Frida {
|
||||
if (names == null)
|
||||
throw new Error.PROTOCOL ("Invalid package.json section for '%s'", section_key);
|
||||
|
||||
PackageRole role = (i == 0) ? PackageRole.RUNTIME : PackageRole.DEVELOPMENT;
|
||||
PackageRole role = section_roles[i];
|
||||
foreach (unowned string name in names) {
|
||||
r.read_member (name);
|
||||
|
||||
@@ -991,7 +1153,8 @@ namespace Frida {
|
||||
private static PackageDependencies read_dependencies_from_lock_entry (Json.Reader r, string parent_path_key) throws Error {
|
||||
var deps = new PackageDependencies ();
|
||||
|
||||
string[] section_keys = { "dependencies", "optionalDependencies" };
|
||||
string[] section_keys = { "dependencies", "optionalDependencies", "peerDependencies" };
|
||||
PackageRole[] section_roles = { RUNTIME, OPTIONAL, PEER };
|
||||
for (uint i = 0; i != section_keys.length; i++) {
|
||||
unowned string section_key = section_keys[i];
|
||||
|
||||
@@ -1006,7 +1169,7 @@ namespace Frida {
|
||||
section_key, parent_path_key);
|
||||
}
|
||||
|
||||
PackageRole role = (i == 1) ? PackageRole.OPTIONAL : PackageRole.RUNTIME;
|
||||
PackageRole role = section_roles[i];
|
||||
|
||||
foreach (unowned string dep_name in dep_names) {
|
||||
r.read_member (dep_name);
|
||||
@@ -1032,6 +1195,15 @@ namespace Frida {
|
||||
return deps;
|
||||
}
|
||||
|
||||
private static void write_dependencies_section (string section, Gee.Map<string, PackageDependency> deps, Json.Builder b) {
|
||||
if (deps.is_empty)
|
||||
return;
|
||||
b.set_member_name (section).begin_object ();
|
||||
foreach (PackageDependency dep in deps.values)
|
||||
b.set_member_name (dep.name).add_string_value (dep.version.spec);
|
||||
b.end_object ();
|
||||
}
|
||||
|
||||
private async void download_and_unpack (string name, string version, string tarball_url, File dest_root, string? integrity,
|
||||
string? shasum, Cancellable? cancellable) throws Error, IOError {
|
||||
int io_priority = Priority.DEFAULT;
|
||||
@@ -1207,7 +1379,6 @@ namespace Frida {
|
||||
DOWNLOADING_PACKAGE,
|
||||
PACKAGE_INSTALLED,
|
||||
RESOLVING_AND_INSTALLING_ALL,
|
||||
AWAITING_COMPLETION,
|
||||
DEPENDENCIES_PROCESSED,
|
||||
FINALIZING_MANIFESTS,
|
||||
COMPLETE,
|
||||
@@ -1628,10 +1799,10 @@ namespace Frida {
|
||||
string core_part_of_spec = stripped_spec;
|
||||
int plus_idx = core_part_of_spec.index_of_char ('+');
|
||||
if (plus_idx != -1)
|
||||
core_part_of_spec = core_part_of_spec.substring (0, plus_idx);
|
||||
core_part_of_spec = core_part_of_spec[:plus_idx];
|
||||
int dash_idx = core_part_of_spec.index_of_char ('-');
|
||||
if (dash_idx != -1)
|
||||
core_part_of_spec = core_part_of_spec.substring (0, dash_idx);
|
||||
core_part_of_spec = core_part_of_spec[:dash_idx];
|
||||
|
||||
return count_char (core_part_of_spec, '.') == 2;
|
||||
} catch (Error e) {
|
||||
@@ -1953,11 +2124,11 @@ namespace Frida {
|
||||
string size_field = header.read_fixed_string (124, 12).split (" ")[0];
|
||||
uint64 file_size = 0;
|
||||
if (!uint64.try_parse (size_field, out file_size, null, 8))
|
||||
throw new Error.PROTOCOL ("Invalid tarball size (file '%s' corrupt)", safe_entry);
|
||||
throw new Error.PROTOCOL ("Invalid tarball size (file '%s' corrupt)", name);
|
||||
|
||||
var typeflag = (char) header.read_uint8 (156);
|
||||
|
||||
if (typeflag == '0' || typeflag == '\0') {
|
||||
if ((typeflag == '0' || typeflag == '\0') && safe_entry != "") {
|
||||
current_file = root.get_child (safe_entry);
|
||||
FS.mkdirp (current_file.get_parent (), cancellable);
|
||||
try {
|
||||
@@ -1983,9 +2154,9 @@ namespace Frida {
|
||||
|
||||
private static string sanitize_entry (string name, File root) throws Error {
|
||||
int slash = name.index_of ("/");
|
||||
string entry = (slash == -1) ? name : name[slash + 1:];
|
||||
if (entry.length == 0)
|
||||
throw new Error.PROTOCOL ("Empty tar entry name: %s", name);
|
||||
string entry = (slash == -1) ? "" : name[slash + 1:];
|
||||
if (entry == "")
|
||||
return entry;
|
||||
|
||||
entry = entry.replace ("/", Path.DIR_SEPARATOR_S);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/frida/frida-gum.git
|
||||
revision = 17.2.0
|
||||
revision = 17.2.6
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
|
||||
+9
-7
@@ -18,7 +18,6 @@ if platform.system() == "Windows":
|
||||
|
||||
|
||||
FRIDA_OS_TO_GO_OS = {
|
||||
"android": "linux", # XXX: Work around lack of support for -buildmode=c-archive
|
||||
"macos": "darwin",
|
||||
}
|
||||
|
||||
@@ -53,6 +52,7 @@ def main(argv: List[str]):
|
||||
go = Path(args.pop(0))
|
||||
host_os = args.pop(0)
|
||||
host_abi = args.pop(0)
|
||||
shlib_suffix = args.pop(0)
|
||||
cc_id = args.pop(0)
|
||||
cc_cmd_array = pop_cmd_array_arg(args)
|
||||
ar_cmd_array = pop_cmd_array_arg(args)
|
||||
@@ -64,6 +64,7 @@ def main(argv: List[str]):
|
||||
go,
|
||||
host_os,
|
||||
host_abi,
|
||||
shlib_suffix,
|
||||
cc_id,
|
||||
cc_cmd_array,
|
||||
ar_cmd_array,
|
||||
@@ -71,7 +72,7 @@ def main(argv: List[str]):
|
||||
ranlib_cmd_array,
|
||||
)
|
||||
print("ok")
|
||||
print(base64.b64encode(json.dumps(config).encode("utf-8")).decode("ascii"))
|
||||
print(config["mode"], base64.b64encode(json.dumps(config).encode("utf-8")).decode("ascii"))
|
||||
except Exception as e:
|
||||
print("error")
|
||||
print(str(e).replace("\n", "\\n"))
|
||||
@@ -95,6 +96,7 @@ def detect_config(
|
||||
go: Path,
|
||||
host_os: str,
|
||||
host_abi: str,
|
||||
shlib_suffix: str,
|
||||
cc_id: str,
|
||||
cc_cmd_array: List[str],
|
||||
ar_cmd_array: Optional[List[str]],
|
||||
@@ -141,10 +143,6 @@ def detect_config(
|
||||
extra_go_args.append("-ldflags=-extldflags=-mmacosx-version-min=11.0")
|
||||
env["MACOSX_DEPLOYMENT_TARGET"] = "11.0"
|
||||
|
||||
if host_os == "android":
|
||||
# XXX: Avoid socklen_t compatibility issue caused by us using GOOS=linux for Android
|
||||
extra_go_args += ["-tags", "netgo"]
|
||||
|
||||
def run(*args):
|
||||
return subprocess.run(
|
||||
args,
|
||||
@@ -165,14 +163,18 @@ def detect_config(
|
||||
env["GOOS"] = goos
|
||||
env["GOARCH"] = goarch
|
||||
|
||||
mode = "c-shared" if host_os == "android" else "c-archive"
|
||||
|
||||
try:
|
||||
run(go, "build", "-buildmode=c-archive", "-o", "cgotest.a", "-buildvcs=false")
|
||||
run(go, "build", f"-buildmode={mode}", "-o", "cgotest.a", "-buildvcs=false")
|
||||
except subprocess.CalledProcessError as e:
|
||||
raise BuildError(e.output.strip())
|
||||
|
||||
config = {
|
||||
"mode": mode,
|
||||
"os": host_os,
|
||||
"abi": host_abi,
|
||||
"shlib_suffix": shlib_suffix,
|
||||
"extra_go_args": extra_go_args,
|
||||
"env": env,
|
||||
"ar": ar_cmd_array,
|
||||
|
||||
@@ -23,6 +23,7 @@ def main(argv):
|
||||
input_entitlements_path = args.pop(0) if args else None
|
||||
else:
|
||||
input_entitlements_path = None
|
||||
flags = set(args.pop(0).split("|") if args else [])
|
||||
|
||||
is_apple_os = host_os in {"macos", "ios", "watchos", "tvos"}
|
||||
|
||||
@@ -62,7 +63,7 @@ def main(argv):
|
||||
codesign_args += ["--entitlements", input_entitlements_path]
|
||||
subprocess.run(codesign + codesign_args + [intermediate_path], **run_kwargs)
|
||||
|
||||
if host_os == "android":
|
||||
if host_os == "android" and "elf-cleaner:off" not in flags:
|
||||
api_level = 19 if host_abi in {"x86", "arm"} else 21
|
||||
subprocess.run(termux_elf_cleaner + ["--api-level", str(api_level), "--quiet", intermediate_path],
|
||||
**run_kwargs)
|
||||
|
||||
Reference in new issue
Block a user