I'm surprised at how little information there is out there for this. Many of the solutions out there were not available on macOS / iOS. Others still, were only about getting the path of the current executable, and had nothing to do with the handle.
After a TON of searching, I finally came across some resources saying to iterate through all the loaded images using _dyld_image_count() and _dyld_get_image_name(). I initially decided against this, as it just didn't seem like an unreasonably slow way of doing things.
Eventually, I decided to go with iterating over all the loaded images, as it was the only actual solution I had come across. I googled for examples, and couldn't find any tutorials on the topic. However, I did come across an open source C++ library that implemented the functionality (found here).
I translated it to normal C, and got rid of some excess things (such as stripping the handle). During the testing process, I noticed that the library I wanted was always last in the list (my best guess is that it stores them in the order they were loaded, and since mine isn't a system library, it will be one of the last ones loaded). This guaranteed a slow performance (in reference to the computer - to a human it'd still be nearly instantaneous). So, I did a clever optimization that started the search at the end of the list, rather than the beginning.
This is the final code for my solution:
// NOT a thread safe way of doing things
NSString *pathFromHandle(void* handle)
{
// Since we know the image we want will always be near the end of the list, start there and go backwards
for (uint32_t i = (_dyld_image_count() - 1); i >= 0; i--)
{
const char* image_name = _dyld_get_image_name(i);
// Why dlopen doesn't effect _dyld stuff: if an image is already loaded, it returns the existing handle.
void* probe_handle = dlopen(image_name, RTLD_LAZY);
dlclose(probe_handle);
if (handle == probe_handle)
{
return [NSString stringWithUTF8String:image_name];
}
}
return NULL;
}
It's important to note that this solution is not thread safe, as _dyld_image_count() and _dyld_get_image_name() are inherently not thread safe. This means that any other thread could load / unload an image and have a negative impact on our search.
Additionally, the resource I used questioned why dlopen didn't have an effect on _dyld_image_count(). This is because if an image is already loaded, dlopen does not load a new instance of the image, but rather returns the existing handle for it.