#define _GNU_SOURCE
#ifdef __linux__
# include <elf.h>
# include <sys/uio.h>
# if INTPTR_MAX == INT64_MAX
#define Elf_Ehdr …
#define Elf_Shdr …
#define Elf_Phdr …
# else
#define Elf_Ehdr …
#define Elf_Shdr …
#define Elf_Phdr …
# endif
# include <sys/mman.h>
#endif
#if defined(__APPLE__)
# include <TargetConditionals.h>
# if !defined(TARGET_OS_OSX)
#define TARGET_OS_OSX …
# endif
# if TARGET_OS_OSX
# include <libproc.h>
# include <mach-o/fat.h>
# include <mach-o/loader.h>
# include <mach-o/nlist.h>
# include <mach/mach.h>
# include <mach/mach_vm.h>
# include <mach/machine.h>
# include <sys/mman.h>
# include <sys/proc.h>
# include <sys/sysctl.h>
# endif
#endif
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifndef Py_BUILD_CORE_BUILTIN
#define Py_BUILD_CORE_MODULE …
#endif
#include "Python.h"
#include <internal/pycore_debug_offsets.h>
#include <internal/pycore_frame.h>
#include <internal/pycore_stackref.h>
#ifndef HAVE_PROCESS_VM_READV
#define HAVE_PROCESS_VM_READV …
#endif
#if defined(__APPLE__) && TARGET_OS_OSX
static void*
analyze_macho64(mach_port_t proc_ref, void* base, void* map)
{
struct mach_header_64* hdr = (struct mach_header_64*)map;
int ncmds = hdr->ncmds;
int cmd_cnt = 0;
struct segment_command_64* cmd = map + sizeof(struct mach_header_64);
mach_vm_size_t size = 0;
mach_msg_type_number_t count = sizeof(vm_region_basic_info_data_64_t);
mach_vm_address_t address = (mach_vm_address_t)base;
vm_region_basic_info_data_64_t region_info;
mach_port_t object_name;
for (int i = 0; cmd_cnt < 2 && i < ncmds; i++) {
if (cmd->cmd == LC_SEGMENT_64 && strcmp(cmd->segname, "__DATA") == 0) {
while (cmd->filesize != size) {
address += size;
if (mach_vm_region(
proc_ref,
&address,
&size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t)®ion_info,
&count,
&object_name)
!= KERN_SUCCESS)
{
PyErr_SetString(PyExc_RuntimeError, "Cannot get any more VM maps.\n");
return NULL;
}
}
base = (void*)address - cmd->vmaddr;
int nsects = cmd->nsects;
struct section_64* sec =
(struct section_64*)((void*)cmd + sizeof(struct segment_command_64));
for (int j = 0; j < nsects; j++) {
if (strcmp(sec[j].sectname, "PyRuntime") == 0) {
return base + sec[j].addr;
}
}
cmd_cnt++;
}
cmd = (struct segment_command_64*)((void*)cmd + cmd->cmdsize);
}
return NULL;
}
static void*
analyze_macho(char* path, void* base, mach_vm_size_t size, mach_port_t proc_ref)
{
int fd = open(path, O_RDONLY);
if (fd == -1) {
PyErr_Format(PyExc_RuntimeError, "Cannot open binary %s\n", path);
return NULL;
}
struct stat fs;
if (fstat(fd, &fs) == -1) {
PyErr_Format(PyExc_RuntimeError, "Cannot get size of binary %s\n", path);
close(fd);
return NULL;
}
void* map = mmap(0, fs.st_size, PROT_READ, MAP_SHARED, fd, 0);
if (map == MAP_FAILED) {
PyErr_Format(PyExc_RuntimeError, "Cannot map binary %s\n", path);
close(fd);
return NULL;
}
void* result = NULL;
struct mach_header_64* hdr = (struct mach_header_64*)map;
switch (hdr->magic) {
case MH_MAGIC:
case MH_CIGAM:
case FAT_MAGIC:
case FAT_CIGAM:
PyErr_SetString(PyExc_RuntimeError, "32-bit Mach-O binaries are not supported");
break;
case MH_MAGIC_64:
case MH_CIGAM_64:
result = analyze_macho64(proc_ref, base, map);
break;
default:
PyErr_SetString(PyExc_RuntimeError, "Unknown Mach-O magic");
break;
}
munmap(map, fs.st_size);
if (close(fd) != 0) {
PyErr_SetFromErrno(PyExc_OSError);
}
return result;
}
static mach_port_t
pid_to_task(pid_t pid)
{
mach_port_t task;
kern_return_t result;
result = task_for_pid(mach_task_self(), pid, &task);
if (result != KERN_SUCCESS) {
PyErr_Format(PyExc_PermissionError, "Cannot get task for PID %d", pid);
return 0;
}
return task;
}
static void*
get_py_runtime_macos(pid_t pid)
{
mach_vm_address_t address = 0;
mach_vm_size_t size = 0;
mach_msg_type_number_t count = sizeof(vm_region_basic_info_data_64_t);
vm_region_basic_info_data_64_t region_info;
mach_port_t object_name;
mach_port_t proc_ref = pid_to_task(pid);
if (proc_ref == 0) {
PyErr_SetString(PyExc_PermissionError, "Cannot get task for PID");
return NULL;
}
int match_found = 0;
char map_filename[MAXPATHLEN + 1];
void* result_address = NULL;
while (mach_vm_region(
proc_ref,
&address,
&size,
VM_REGION_BASIC_INFO_64,
(vm_region_info_t)®ion_info,
&count,
&object_name)
== KERN_SUCCESS)
{
int path_len = proc_regionfilename(pid, address, map_filename, MAXPATHLEN);
if (path_len == 0) {
address += size;
continue;
}
char* filename = strrchr(map_filename, '/');
if (filename != NULL) {
filename++;
} else {
filename = map_filename;
}
if (!match_found && strncmp(filename, "python", 6) == 0) {
match_found = 1;
result_address = analyze_macho(map_filename, (void*)address, size, proc_ref);
}
if (strncmp(filename, "libpython", 9) == 0) {
match_found = 1;
result_address = analyze_macho(map_filename, (void*)address, size, proc_ref);
break;
}
address += size;
}
return result_address;
}
#endif
#ifdef __linux__
void*
find_python_map_start_address(pid_t pid, char* result_filename)
{ … }
void*
get_py_runtime_linux(pid_t pid)
{ … }
#endif
ssize_t
read_memory(pid_t pid, void* remote_address, size_t len, void* dst)
{ … }
int
read_string(pid_t pid, _Py_DebugOffsets* debug_offsets, void* address, char* buffer, Py_ssize_t size)
{ … }
void*
get_py_runtime(pid_t pid)
{ … }
static int
parse_code_object(
int pid,
PyObject* result,
struct _Py_DebugOffsets* offsets,
void* address,
void** previous_frame)
{ … }
static int
parse_frame_object(
int pid,
PyObject* result,
struct _Py_DebugOffsets* offsets,
void* address,
void** previous_frame)
{ … }
static PyObject*
get_stack_trace(PyObject* self, PyObject* args)
{ … }
static PyMethodDef methods[] = …;
static struct PyModuleDef module = …;
PyMODINIT_FUNC
PyInit__testexternalinspection(void)
{ … }