@@ -294,55 +294,12 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* s
294294
295295#if defined(__linux__ ) && HAVE_PROCESS_VM_READV
296296static uintptr_t
297- find_map_start_address (proc_handle_t * handle , char * result_filename , const char * map )
297+ search_elf_file_for_section (
298+ proc_handle_t * handle ,
299+ const char * secname ,
300+ uintptr_t start_address ,
301+ const char * elf_file )
298302{
299- char maps_file_path [64 ];
300- sprintf (maps_file_path , "/proc/%d/maps" , handle -> pid );
301-
302- FILE * maps_file = fopen (maps_file_path , "r" );
303- if (maps_file == NULL ) {
304- PyErr_SetFromErrno (PyExc_OSError );
305- return 0 ;
306- }
307-
308- int match_found = 0 ;
309-
310- char line [256 ];
311- char map_filename [PATH_MAX ];
312- uintptr_t result_address = 0 ;
313- while (fgets (line , sizeof (line ), maps_file ) != NULL ) {
314- unsigned long start_address = 0 ;
315- sscanf (line , "%lx-%*x %*s %*s %*s %*s %s" , & start_address , map_filename );
316- char * filename = strrchr (map_filename , '/' );
317- if (filename != NULL ) {
318- filename ++ ; // Move past the '/'
319- } else {
320- filename = map_filename ; // No path, use the whole string
321- }
322-
323- if (!match_found && strncmp (filename , map , strlen (map )) == 0 ) {
324- match_found = 1 ;
325- result_address = start_address ;
326- strcpy (result_filename , map_filename );
327- break ;
328- }
329- }
330-
331- fclose (maps_file );
332-
333- if (!match_found ) {
334- map_filename [0 ] = '\0' ;
335- }
336-
337- return result_address ;
338- }
339-
340- static uintptr_t
341- search_map_for_section (proc_handle_t * handle , const char * secname , const char * map )
342- {
343- char elf_file [256 ];
344- uintptr_t start_address = find_map_start_address (handle , elf_file , map );
345-
346303 if (start_address == 0 ) {
347304 return 0 ;
348305 }
@@ -412,6 +369,83 @@ search_map_for_section(proc_handle_t *handle, const char* secname, const char* m
412369 return result ;
413370}
414371
372+ static uintptr_t
373+ search_linux_map_for_section (proc_handle_t * handle , const char * secname , const char * substr )
374+ {
375+ char maps_file_path [64 ];
376+ sprintf (maps_file_path , "/proc/%d/maps" , handle -> pid );
377+
378+ FILE * maps_file = fopen (maps_file_path , "r" );
379+ if (maps_file == NULL ) {
380+ PyErr_SetFromErrno (PyExc_OSError );
381+ return 0 ;
382+ }
383+
384+ size_t linelen = 0 ;
385+ size_t linesz = PATH_MAX ;
386+ char * line = PyMem_Malloc (linesz );
387+ if (!line ) {
388+ fclose (maps_file );
389+ PyErr_NoMemory ();
390+ return 0 ;
391+ }
392+
393+ uintptr_t retval = 0 ;
394+ while (fgets (line + linelen , linesz - linelen , maps_file ) != NULL ) {
395+ linelen = strlen (line );
396+ if (line [linelen - 1 ] != '\n' ) {
397+ // Read a partial line: realloc and keep reading where we left off.
398+ // Note that even the last line will be terminated by a newline.
399+ linesz *= 2 ;
400+ char * biggerline = PyMem_Realloc (line , linesz );
401+ if (!biggerline ) {
402+ PyMem_Free (line );
403+ fclose (maps_file );
404+ PyErr_NoMemory ();
405+ return 0 ;
406+ }
407+ line = biggerline ;
408+ continue ;
409+ }
410+
411+ // Read a full line: strip the newline
412+ line [linelen - 1 ] = '\0' ;
413+ // and prepare to read the next line into the start of the buffer.
414+ linelen = 0 ;
415+
416+ unsigned long start = 0 ;
417+ unsigned long path_pos = 0 ;
418+ sscanf (line , "%lx-%*x %*s %*s %*s %*s %ln" , & start , & path_pos );
419+
420+ if (!path_pos ) {
421+ // Line didn't match our format string. This shouldn't be
422+ // possible, but let's be defensive and skip the line.
423+ continue ;
424+ }
425+
426+ const char * path = line + path_pos ;
427+ const char * filename = strrchr (path , '/' );
428+ if (filename ) {
429+ filename ++ ; // Move past the '/'
430+ } else {
431+ filename = path ; // No directories, or an empty string
432+ }
433+
434+ if (strstr (filename , substr )) {
435+ retval = search_elf_file_for_section (handle , secname , start , path );
436+ if (retval ) {
437+ break ;
438+ }
439+ }
440+ }
441+
442+ PyMem_Free (line );
443+ fclose (maps_file );
444+
445+ return retval ;
446+ }
447+
448+
415449#endif // __linux__
416450
417451#ifdef MS_WINDOWS
@@ -512,21 +546,28 @@ get_py_runtime(proc_handle_t* handle)
512546{
513547 uintptr_t address = 0 ;
514548
515- #ifndef MS_WINDOWS
516- // On non-Windows platforms, try libpython first, then fall back to python
517- address = search_map_for_section (handle , "PyRuntime" , "libpython" );
518- if (address == 0 ) {
519- // TODO: Differentiate between not found and error
520- PyErr_Clear ();
521- address = search_map_for_section (handle , "PyRuntime" , "python" );
522- }
523- #else
549+ #ifdef MS_WINDOWS
524550 // On Windows, search for 'python' in executable or DLL
525551 address = search_windows_map_for_section (handle , "PyRuntime" , L"python" );
526552 if (address == 0 ) {
527553 // Error out: 'python' substring covers both executable and DLL
528554 PyErr_SetString (PyExc_RuntimeError , "Failed to find the PyRuntime section in the process." );
529555 }
556+ #elif defined(__linux__ )
557+ // On Linux, search for 'python' in executable or DLL
558+ address = search_linux_map_for_section (handle , "PyRuntime" , "python" );
559+ if (address == 0 ) {
560+ // Error out: 'python' substring covers both executable and DLL
561+ PyErr_SetString (PyExc_RuntimeError , "Failed to find the PyRuntime section in the process." );
562+ }
563+ #else
564+ // On macOS, try libpython first, then fall back to python
565+ address = search_map_for_section (handle , "PyRuntime" , "libpython" );
566+ if (address == 0 ) {
567+ // TODO: Differentiate between not found and error
568+ PyErr_Clear ();
569+ address = search_map_for_section (handle , "PyRuntime" , "python" );
570+ }
530571#endif
531572
532573 return address ;
0 commit comments