@@ -116,6 +116,8 @@ typedef struct {
116116 mach_port_t task ;
117117#elif defined(MS_WINDOWS )
118118 HANDLE hProcess ;
119+ #elif defined(__linux__ )
120+ int memfd ;
119121#endif
120122 page_cache_entry_t pages [MAX_PAGES ];
121123 Py_ssize_t page_size ;
@@ -162,6 +164,8 @@ _Py_RemoteDebug_InitProcHandle(proc_handle_t *handle, pid_t pid) {
162164 _set_debug_exception_cause (PyExc_RuntimeError , "Failed to initialize Windows process handle" );
163165 return -1 ;
164166 }
167+ #elif defined(__linux__ )
168+ handle -> memfd = -1 ;
165169#endif
166170 handle -> page_size = get_page_size ();
167171 for (int i = 0 ; i < MAX_PAGES ; i ++ ) {
@@ -179,6 +183,11 @@ _Py_RemoteDebug_CleanupProcHandle(proc_handle_t *handle) {
179183 CloseHandle (handle -> hProcess );
180184 handle -> hProcess = NULL ;
181185 }
186+ #elif defined(__linux__ )
187+ if (handle -> memfd != -1 ) {
188+ close (handle -> memfd );
189+ handle -> memfd = -1 ;
190+ }
182191#endif
183192 handle -> pid = 0 ;
184193 _Py_RemoteDebug_FreePageCache (handle );
@@ -907,6 +916,62 @@ _Py_RemoteDebug_GetPyRuntimeAddress(proc_handle_t* handle)
907916 return address ;
908917}
909918
919+ #if defined(__linux__ ) && (HAVE_PREADV || HAVE_PWRITEV )
920+ static int
921+ open_proc_mem_fd (proc_handle_t * handle )
922+ {
923+ char mem_file_path [64 ];
924+ sprintf (mem_file_path , "/proc/%d/mem" , handle -> pid );
925+
926+ handle -> memfd = open (mem_file_path , O_RDWR );
927+ if (handle -> memfd == -1 ) {
928+ PyErr_SetFromErrno (PyExc_OSError );
929+ _set_debug_exception_cause (PyExc_OSError ,
930+ "failed to open file %s: %s" , mem_file_path , strerror (errno ));
931+ return -1 ;
932+ }
933+ return 0 ;
934+ }
935+ #endif // __linux__
936+
937+ static int
938+ read_remote_memory_fallback (proc_handle_t * handle , uintptr_t remote_address , size_t len , void * dst )
939+ {
940+ #if defined(__linux__ ) && HAVE_PREADV
941+ if (handle -> memfd == -1 ) {
942+ if (open_proc_mem_fd (handle ) < 0 ) {
943+ return -1 ;
944+ }
945+ }
946+
947+ struct iovec local [1 ];
948+ Py_ssize_t result = 0 ;
949+ Py_ssize_t read_bytes = 0 ;
950+
951+ do {
952+ local [0 ].iov_base = (char * )dst + result ;
953+ local [0 ].iov_len = len - result ;
954+ off_t offset = remote_address + result ;
955+
956+ read_bytes = preadv (handle -> memfd , local , 1 , offset );
957+ if (read_bytes < 0 ) {
958+ PyErr_SetFromErrno (PyExc_OSError );
959+ _set_debug_exception_cause (PyExc_OSError ,
960+ "pread failed for PID %d at address 0x%lx "
961+ "(size %zu, partial read %zd bytes): %s" ,
962+ handle -> pid , remote_address + result , len - result , result , strerror (errno ));
963+ return -1 ;
964+ }
965+
966+ result += read_bytes ;
967+ } while ((size_t )read_bytes != local [0 ].iov_len );
968+ return 0 ;
969+ #else
970+ PyErr_SetFromErrno (PyExc_OSError );
971+ return -1 ;
972+ #endif
973+ }
974+
910975// Platform-independent memory read function
911976static int
912977_Py_RemoteDebug_ReadRemoteMemory (proc_handle_t * handle , uintptr_t remote_address , size_t len , void * dst )
@@ -928,6 +993,9 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
928993 } while (result < len );
929994 return 0 ;
930995#elif defined(__linux__ ) && HAVE_PROCESS_VM_READV
996+ if (handle -> memfd != -1 ) {
997+ return read_remote_memory_fallback (handle , remote_address , len , dst );
998+ }
931999 struct iovec local [1 ];
9321000 struct iovec remote [1 ];
9331001 Py_ssize_t result = 0 ;
@@ -941,6 +1009,9 @@ _Py_RemoteDebug_ReadRemoteMemory(proc_handle_t *handle, uintptr_t remote_address
9411009
9421010 read_bytes = process_vm_readv (handle -> pid , local , 1 , remote , 1 , 0 );
9431011 if (read_bytes < 0 ) {
1012+ if (errno == ENOSYS ) {
1013+ return read_remote_memory_fallback (handle , remote_address , len , dst );
1014+ }
9441015 PyErr_SetFromErrno (PyExc_OSError );
9451016 _set_debug_exception_cause (PyExc_OSError ,
9461017 "process_vm_readv failed for PID %d at address 0x%lx "
0 commit comments