+#include "Tracing.h"
+
+// this was in dyld_priv.h but it is no longer exported
+extern "C" {
+ const struct dyld_all_image_infos* _dyld_get_all_image_infos();
+}
+
+RemoteBuffer::RemoteBuffer() : _localAddress(0), _size(0) {}
+bool RemoteBuffer::map(task_t task, mach_vm_address_t remote_address, bool shared) {
+ vm_prot_t cur_protection = VM_PROT_NONE;
+ vm_prot_t max_protection = VM_PROT_NONE;
+ if (_size == 0) {
+ _kr = KERN_NO_SPACE;
+ return false;
+ }
+ _localAddress = 0;
+ _kr = mach_vm_remap(mach_task_self(),
+ &_localAddress,
+ _size,
+ 0, // mask
+ VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR | (shared ? 0 : VM_FLAGS_RESILIENT_CODESIGN),
+ task,
+ remote_address,
+ !shared,
+ &cur_protection,
+ &max_protection,
+ VM_INHERIT_NONE);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_REMAP, _localAddress, (uint64_t)_size, _kr, remote_address);
+ if (shared && (cur_protection != (VM_PROT_READ|VM_PROT_WRITE))) {
+ if (_kr == KERN_SUCCESS && _localAddress != 0) {
+ _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
+ }
+ _localAddress = 0;
+ _kr = KERN_PROTECTION_FAILURE;
+ }
+ return (_kr == KERN_SUCCESS);
+}
+
+RemoteBuffer::RemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation)
+ : _localAddress(0), _size(remote_size), _kr(KERN_SUCCESS) {
+ // Try the initial map
+ if (map(task, remote_address, shared)) return;
+ // It failed, try to calculate the largest size that can fit in the same page as the remote_address
+ uint64_t newSize = PAGE_SIZE - remote_address%PAGE_SIZE;;
+ // If truncation is allowed and the newSize is different than the original size try that
+ if (!allow_truncation && newSize != _size) return;
+ _size = newSize;
+ if (map(task, remote_address, shared)) return;
+ // That did not work, null out the buffer
+ _size = 0;
+ _localAddress = 0;
+}
+RemoteBuffer::~RemoteBuffer() {
+ if (_localAddress) {
+ _kr = vm_deallocate(mach_task_self(), _localAddress, _size);
+ dyld3::kdebug_trace_dyld_marker(DBG_DYLD_DEBUGGING_VM_UNMAP, _localAddress, (uint64_t)_size, _kr, 0);
+ }
+}
+void *RemoteBuffer::getLocalAddress() { return (void *)_localAddress; }
+size_t RemoteBuffer::getSize() { return _size; }
+kern_return_t RemoteBuffer::getKernelReturn() { return _kr; }
+
+void withRemoteBuffer(task_t task, mach_vm_address_t remote_address, size_t remote_size, bool shared, bool allow_truncation, kern_return_t *kr, void (^block)(void *buffer, size_t size)) {
+ kern_return_t krSink = KERN_SUCCESS;
+ if (kr == nullptr) {
+ kr = &krSink;
+ }
+ RemoteBuffer buffer(task, remote_address, remote_size, shared, allow_truncation);
+ *kr = buffer.getKernelReturn();
+ if (*kr == KERN_SUCCESS) {
+ block(buffer.getLocalAddress(), buffer.getSize());
+ }
+}
+