2
2
3
3
#ifdef __linux__
4
4
# include <elf.h>
5
- # include <sys/mman.h>
6
5
# include <sys/uio.h>
7
6
#if INTPTR_MAX == INT64_MAX
8
7
# define Elf_Ehdr Elf64_Ehdr
13
12
#endif
14
13
#endif
15
14
15
+ #ifdef __APPLE__
16
+ #include <mach-o/fat.h>
17
+ #include <mach-o/loader.h>
18
+ #include <mach-o/nlist.h>
19
+ #include <mach/mach.h>
20
+ #include <mach/mach_vm.h>
21
+ #include <mach/machine.h>
22
+ #include <libproc.h>
23
+ #endif
24
+
16
25
#include <errno.h>
17
26
#include <fcntl.h>
27
+ #include <stddef.h>
28
+ #include <stdint.h>
18
29
#include <stdio.h>
19
30
#include <stdlib.h>
20
31
#include <string.h>
32
+ #include <sys/mman.h>
33
+ #include <sys/param.h>
34
+ #include <sys/proc.h>
21
35
#include <sys/stat.h>
36
+ #include <sys/sysctl.h>
22
37
#include <sys/types.h>
23
38
#include <unistd.h>
24
- #include <stdint.h>
25
39
26
40
#ifndef Py_BUILD_CORE_BUILTIN
27
41
# define Py_BUILD_CORE_MODULE 1
28
42
#endif
29
43
#include "Python.h"
30
44
#include <internal/pycore_runtime.h>
31
45
46
+ #ifndef HAVE_PROCESS_VM_READV
47
+ #define HAVE_PROCESS_VM_READV 0
48
+ #endif
49
+
50
+ #ifdef __APPLE__
51
+
52
+ static void * analyze_macho64 (mach_port_t proc_ref , void * base , void * map ) {
53
+ struct mach_header_64 * hdr = (struct mach_header_64 * )map ;
54
+ int ncmds = hdr -> ncmds ;
55
+
56
+ int cmd_cnt = 0 ;
57
+ struct segment_command_64 * cmd = map + sizeof (struct mach_header_64 );
58
+
59
+ mach_vm_size_t size = 0 ;
60
+ mach_msg_type_number_t count = sizeof (vm_region_basic_info_data_64_t );
61
+ mach_vm_address_t address = (mach_vm_address_t )base ;
62
+ vm_region_basic_info_data_64_t region_info ;
63
+ mach_port_t object_name ;
64
+
65
+ for (register int i = 0 ; cmd_cnt < 2 && i < ncmds ; i ++ ) {
66
+ if (cmd -> cmd == LC_SEGMENT_64 && strcmp (cmd -> segname , "__DATA" ) == 0 ) {
67
+ while (cmd -> filesize != size ) {
68
+ address += size ;
69
+ if (mach_vm_region (
70
+ proc_ref , & address , & size , VM_REGION_BASIC_INFO_64 ,
71
+ (vm_region_info_t )& region_info , // cppcheck-suppress [uninitvar]
72
+ & count , & object_name ) != KERN_SUCCESS ) {
73
+ printf ("Cannot get any more VM maps.\n" );
74
+ return 0 ;
75
+ }
76
+ }
77
+ base = (void * )address - cmd -> vmaddr ;
78
+
79
+ int nsects = cmd -> nsects ;
80
+ struct section_64 * sec =
81
+ (struct section_64 * )((void * )cmd +
82
+ sizeof (struct segment_command_64 ));
83
+ for (register int j = 0 ; j < nsects ; j ++ ) {
84
+ if (strcmp (sec [j ].sectname , "PyRuntime" ) == 0 ) {
85
+ return base + sec [j ].addr ;
86
+ }
87
+ }
88
+ cmd_cnt ++ ;
89
+ }
90
+
91
+ cmd = (struct segment_command_64 * )((void * )cmd + cmd -> cmdsize );
92
+ }
93
+ return 0 ;
94
+ }
95
+
96
+ typedef struct {
97
+ void * addr ;
98
+ size_t size ;
99
+ } map_t ;
100
+
101
+ static inline map_t * map_new (int fd , size_t size , int flags ) {
102
+ void * addr = mmap (0 , size , PROT_READ , flags , fd , 0 );
103
+ if (!(addr ))
104
+ return NULL ;
105
+
106
+ map_t * map = malloc (sizeof (map_t ));
107
+ if (map == MAP_FAILED ) {
108
+ munmap (map , size );
109
+ return NULL ;
110
+ }
111
+
112
+ map -> size = size ;
113
+ map -> addr = addr ;
114
+
115
+ return map ;
116
+ }
117
+
118
+ static void * analyze_macho (char * path , void * base , mach_vm_size_t size ,
119
+ mach_port_t proc_ref ) {
120
+ int fd = open (path , O_RDONLY );
121
+ if (fd == -1 ) {
122
+ printf ("Cannot open binary %s\n" , path );
123
+ return 0 ;
124
+ }
125
+
126
+ // This would cause problem if allocated in the stack frame
127
+ void * fs_buffer = malloc (sizeof (struct stat ));
128
+ struct stat * fs = (struct stat * )fs_buffer ;
129
+ map_t * map = NULL ;
130
+ if (fstat (fd , fs ) == -1 ) {
131
+ printf ("Cannot get size of binary %s\n" , path );
132
+ return 0 ;
133
+ }
134
+
135
+ map = map_new (fd , fs -> st_size , MAP_SHARED );
136
+ if (!map ) {
137
+ printf ("Cannot map binary %s\n" , path );
138
+ return 0 ;
139
+ }
140
+
141
+ void * map_addr = map -> addr ;
142
+
143
+ struct mach_header_64 * hdr = (struct mach_header_64 * )map_addr ;
144
+ switch (hdr -> magic ) {
145
+ case MH_MAGIC :
146
+ case MH_CIGAM :
147
+ case FAT_MAGIC :
148
+ case FAT_CIGAM :
149
+ printf ("Mach-O 32 or FAT binaries are not supported\n" );
150
+ return 0 ;
151
+ case MH_MAGIC_64 :
152
+ case MH_CIGAM_64 :
153
+ return analyze_macho64 (proc_ref , base , map_addr );
154
+ default :
155
+ printf ("Unknown Mach-O magic\n" );
156
+ return 0 ;
157
+ }
158
+
159
+ return 0 ;
160
+ }
161
+
162
+ static mach_port_t pid_to_task (pid_t pid ) {
163
+ mach_port_t task ;
164
+ kern_return_t result ;
165
+
166
+ result = task_for_pid (mach_task_self (), pid , & task );
167
+ if (result != KERN_SUCCESS ) {
168
+ printf ("Call to task_for_pid failed on PID %d: %s" , pid , mach_error_string (result ));
169
+ return 0 ;
170
+ }
171
+ return task ;
172
+ }
173
+
174
+ static void * get_py_runtime (pid_t pid ) {
175
+ mach_vm_address_t address = 0 ;
176
+ mach_vm_size_t size = 0 ;
177
+ mach_msg_type_number_t count = sizeof (vm_region_basic_info_data_64_t );
178
+ vm_region_basic_info_data_64_t region_info ;
179
+ mach_port_t object_name ;
180
+
181
+ mach_port_t proc_ref = pid_to_task (pid );
182
+ if (proc_ref == 0 ) {
183
+ printf ("Cannot get task for PID\n" );
184
+ return NULL ;
185
+ }
186
+
187
+ int match_found = 0 ;
188
+ char map_filename [MAXPATHLEN + 1 ];
189
+ void * result_address = NULL ;
190
+ while (mach_vm_region (
191
+ proc_ref , & address , & size , VM_REGION_BASIC_INFO_64 ,
192
+ (vm_region_info_t )& region_info , // cppcheck-suppress [uninitvar]
193
+ & count , & object_name ) == KERN_SUCCESS ) {
194
+
195
+ int path_len = proc_regionfilename (pid , address , map_filename , MAXPATHLEN );
196
+ if (path_len == 0 ) {
197
+ address += size ;
198
+ continue ;
199
+ }
200
+
201
+ char * filename = strrchr (map_filename , '/' );
202
+ if (filename != NULL ) {
203
+ filename ++ ; // Move past the '/'
204
+ } else {
205
+ filename = map_filename ; // No path, use the whole string
206
+ }
207
+
208
+ // Check if the filename starts with "python" or "libpython"
209
+ if (!match_found && strncmp (filename , "python" , 6 ) == 0 ) {
210
+ match_found = 1 ;
211
+ result_address = analyze_macho (map_filename , (void * )address , size , proc_ref );
212
+ }
213
+ if (strncmp (filename , "libpython" , 9 ) == 0 ) {
214
+ match_found = 1 ;
215
+ result_address = analyze_macho (map_filename , (void * )address , size , proc_ref );
216
+ break ;
217
+ }
218
+
219
+ address += size ;
220
+ }
221
+ return result_address ;
222
+ }
223
+ #endif
32
224
33
225
34
- unsigned long
35
- get_py_runtime (char * elf_file ) {
36
226
#ifdef __linux__
227
+ void *
228
+ get_py_runtime (pid_t pid ) {
229
+
230
+ char elf_file [256 ];
231
+ unsigned long start_address = find_python_map_start_address (pid , elf_file );
232
+
233
+ if (start_address == 0 ) {
234
+ PyErr_SetString (PyExc_RuntimeError , "No memory map associated with python or libpython found" );
235
+ return NULL ;
236
+ }
237
+
37
238
unsigned long result = 0 ;
38
239
39
240
int fd = open (elf_file , O_RDONLY );
@@ -70,15 +271,12 @@ get_py_runtime(char* elf_file) {
70
271
}
71
272
72
273
if (py_runtime_section != NULL ) {
73
- result = (unsigned long )py_runtime_section -> sh_addr ;
274
+ result = start_address + (unsigned long )py_runtime_section -> sh_addr ;
74
275
}
75
276
76
277
close (fd );
77
278
munmap (file_memory , file_stats .st_size );
78
279
return result ;
79
- #else
80
- return 0 ;
81
- #endif
82
280
}
83
281
84
282
unsigned long
@@ -129,15 +327,13 @@ find_python_map_start_address(pid_t pid, char* result_filename) {
129
327
130
328
return result_address ;
131
329
}
330
+ #endif
132
331
133
332
ssize_t
134
333
read_memory (pid_t pid , void * remote_address , ssize_t size , void * local_address ) {
135
- #ifndef HAVE_PROCESS_VM_READV
136
- return -1
137
- #else
138
334
ssize_t total_bytes_read = 0 ;
335
+ #ifdef __linux__
139
336
ssize_t bytes_read ;
140
-
141
337
while (total_bytes_read < size ) {
142
338
struct iovec local_iov = {(char * )local_address + total_bytes_read , size - total_bytes_read };
143
339
struct iovec remote_iov = {(char * )remote_address + total_bytes_read , size - total_bytes_read };
@@ -154,9 +350,23 @@ read_memory(pid_t pid, void* remote_address, ssize_t size, void* local_address)
154
350
break ;
155
351
}
156
352
}
157
-
158
- return total_bytes_read ;
353
+ #elif defined(__APPLE__ )
354
+ ssize_t result = -1 ;
355
+ kern_return_t kr = mach_vm_read_overwrite (
356
+ pid_to_task (pid ),
357
+ (mach_vm_address_t )remote_address ,
358
+ size ,
359
+ (mach_vm_address_t )local_address ,
360
+ (mach_vm_size_t * )& result );
361
+
362
+ if (kr != KERN_SUCCESS ) {
363
+ return -1 ;
364
+ }
365
+ total_bytes_read = size ;
366
+ #else
367
+ return -1 ;
159
368
#endif
369
+ return total_bytes_read ;
160
370
}
161
371
162
372
int
@@ -191,26 +401,16 @@ get_stack_trace(PyObject* self, PyObject* args) {
191
401
return NULL ;
192
402
}
193
403
194
- char map_filename [256 ];
195
- unsigned long start_address = find_python_map_start_address (pid , map_filename );
196
-
197
- if (start_address == 0 ) {
198
- PyErr_SetString (PyExc_RuntimeError , "No memory map associated with python or libpython found" );
199
- return NULL ;
200
- }
201
-
202
- unsigned long py_runtime_address = get_py_runtime (map_filename );
203
- if (py_runtime_address == 0 ) {
404
+ void * runtime_start_address = get_py_runtime (pid );
405
+ if (runtime_start_address == NULL ) {
204
406
PyErr_SetString (PyExc_RuntimeError , "Failed to get .PyRuntime address" );
205
407
return NULL ;
206
408
}
207
409
208
- void * runtime_state_address = (void * )start_address + py_runtime_address ;
209
-
210
410
size_t size = sizeof (struct _Py_DebugOffsets );
211
411
struct _Py_DebugOffsets local_debug_offsets ;
212
412
213
- ssize_t bytes_read = read_memory (pid , runtime_state_address , size , & local_debug_offsets );
413
+ ssize_t bytes_read = read_memory (pid , runtime_start_address , size , & local_debug_offsets );
214
414
if (bytes_read == -1 ) {
215
415
return NULL ;
216
416
}
@@ -220,7 +420,7 @@ get_stack_trace(PyObject* self, PyObject* args) {
220
420
void * address_of_interpreter_state ;
221
421
bytes_read = read_memory (
222
422
pid ,
223
- (void * )(runtime_state_address + thread_state_list_head ),
423
+ (void * )(runtime_start_address + thread_state_list_head ),
224
424
sizeof (void * ),
225
425
& address_of_interpreter_state );
226
426
if (bytes_read == -1 ) {
0 commit comments