Open ajwerner opened 2 months ago
I took a look and you are right, I can't see why these types have explicit lifetimes. Perhaps @fabianfreyer can comment?
I guess the idea in some cases may have been to tie the lifetime to a prerequisite object. Like if you look in frida-gum, the Iterceptor::obtain
indeed ensures that the returned interceptor holds a lifetime that is shorter than that of the reference to the Gum. That seems legit enough. I suppose a similar thing could be done for the things that require a Frida
.
I think that that's the right way to go. We should replace *::new()
with *::obtain(&'b Frida)
(or whatever), to tie them to the lifetime of the Frida object.
Another approach here, that I think I prefer because it'll simplify the programming model would be to wrap the Frida object as a process-global singleton and track its references with refcounts. Then any derived object that assumes Frida is live should hold onto one of these owned references. That way it'll become impossible to mess up, and easier to program with because the objects will be static.
I can live with that. But it would have to be done for both Frida
and Gum
.
What do other Frida bindings do with regards to getting a handle to Frida or gum?
Looking at this more, gobject is already doing atomic refcounting for us. I think rather than rust having its own refcounts, we should use g_object_ref
and g_object_unref
and have a clone impl to increment the ref counts. Then for objects in rust which depend on a reference existing, we can have them hold refs.
One thing I can't easily figure out is which APIs are thread safe and which aren't. It looks to me like the gum APIs are all thread safe whereas the Frida-core APIs seem to not be thread-safe, does that seem right?
I'm realizing I don't know enough to know what makes sense. I see now that g_object_unref
is only exposed on when #[cfg(not(any(target_os = "windows", target_os = "android", target_vendor = "apple",)))]
. What's that about?
I'm realizing I don't know enough to know what makes sense. I see now that
g_object_unref
is only exposed on when#[cfg(not(any(target_os = "windows", target_os = "android", target_vendor = "apple",)))]
. What's that about?
Our releng repo's devkit.py, which generates the devkits, renames all symbols not prefixed with frida_
so they're prefixed with _frida
. The motivation is to avoid conflicts in situations where the user builds a shared library with all symbols visible and this gets loaded into a process that's already got e.g. a GLib of its own loaded. However, we haven't yet implemented this renaming logic for Microsoft and Apple toolchains, so that's why we have some weird-looking code like the above to work around this (hopefully temporary) misery 😅
Thanks for the hint! Given that, it seems like we should use the gobject refcounts data types an instance of a gobject, and cloning in rust should increment the refcount of the underlying gobject.
The two special cases are the "namespaces" Gum and Frida which aren't objects, but rather are magical handles to ensure the relevant runtime has been initialized. For those, we should have rust-level reference counting.
Any rust handle to a gobject pointer then should also hold onto the namespace handle to make sure we don't accidentally deinit too early.
How does that sound to folks?
Thanks for the hint! Given that, it seems like we should use the gobject refcounts data types an instance of a gobject, and cloning in rust should increment the refcount of the underlying gobject.
The two special cases are the "namespaces" Gum and Frida which aren't objects, but rather are magical handles to ensure the relevant runtime has been initialized. For those, we should have rust-level reference counting.
Any rust handle to a gobject pointer then should also hold onto the namespace handle to make sure we don't accidentally deinit too early.
How does that sound to folks?
Sounds great to me!
Yup sounds good to me too.
I don't understand the lifetimes on things like
Device
,Injector
etc. They aren't borrowing from a stack, they own the relevant heap allocations make to construct them. The lifetimes can be literally whatever. For example:These lifetimes makes the APIs harder to both use and understand. Is there some purpose they serve, or were they just cargo-culted from the start?