attach.cpp
1 // This is much simpler than the windows version because we're using gdb and 2 // we assume that gdb will call things in the correct thread already. 3 4 //compile with: g++ -shared -o attach_linux.so -fPIC -nostartfiles attach_linux.c 5 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <dlfcn.h> 10 #include <stdbool.h> 11 12 #include "../common/python.h" 13 #include "../common/ref_utils.hpp" 14 #include "../common/py_utils.hpp" 15 #include "../common/py_settrace.hpp" 16 //#include <unistd.h> used for usleep 17 18 // Exported function: hello(): Just to print something and check that we've been 19 // able to connect. 20 extern "C" int hello(void); 21 22 int hello() 23 { 24 printf("Hello world!\n"); 25 26 void *module = dlopen(nullptr, 0x2); 27 28 void *hndl = dlsym (module, "PyGILState_Ensure"); 29 if(hndl == nullptr){ 30 printf("nullptr\n"); 31 32 }else{ 33 printf("Worked (found PyGILState_Ensure)!\n"); 34 } 35 36 printf("%d", GetPythonVersion(module)); 37 38 39 return 2; 40 } 41 42 43 // Internal function to keep on the tracing 44 int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug); 45 46 // Implementation details below 47 typedef PyObject* (PyImport_ImportModuleNoBlock) (const char *name); 48 typedef int (*PyEval_ThreadsInitialized)(); 49 typedef unsigned long (*_PyEval_GetSwitchInterval)(void); 50 typedef void (*_PyEval_SetSwitchInterval)(unsigned long microseconds); 51 52 // isDebug is pretty important! Must be true on python debug builds (python_d) 53 // If this value is passed wrongly the program will crash. 54 extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo); 55 56 int DoAttach(bool isDebug, const char *command, bool showDebugInfo) 57 { 58 void *module = dlopen(nullptr, 0x2); 59 DEFINE_PROC(isInitFunc, Py_IsInitialized*, "Py_IsInitialized", 1); 60 DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", 51); 61 DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", 51); 62 63 64 if(!isInitFunc()){ 65 if(showDebugInfo){ 66 printf("Py_IsInitialized returned false.\n"); 67 } 68 return 2; 69 } 70 71 PythonVersion version = GetPythonVersion(module); 72 73 DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", 51); 74 75 auto head = interpHead(); 76 if (head == nullptr) { 77 // this interpreter is loaded but not initialized. 78 if(showDebugInfo){ 79 printf("Interpreter not initialized!\n"); 80 } 81 return 54; 82 } 83 84 // Note: unlike windows where we have to do many things to enable threading 85 // to work to get the gil, here we'll be executing in an existing thread, 86 // so, it's mostly a matter of getting the GIL and running it and we shouldn't 87 // have any more problems. 88 89 DEFINE_PROC(pyRun_SimpleString, PyRun_SimpleString*, "PyRun_SimpleString", 51); 90 91 GilHolder gilLock(gilEnsure, gilRelease); // acquire and hold the GIL until done... 92 93 pyRun_SimpleString(command); 94 return 0; 95 } 96 97 98 // This is the function which enables us to set the sys.settrace for all the threads 99 // which are already running. 100 extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone); 101 102 int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone) 103 { 104 void *module = dlopen(nullptr, 0x2); 105 bool isDebug = false; 106 PyObjectHolder traceFunc(isDebug, (PyObject*) pTraceFunc, true); 107 PyObjectHolder setTraceFunc(isDebug, (PyObject*) pSetTraceFunc, true); 108 PyObjectHolder pyNone(isDebug, reinterpret_cast<PyObject*>(pPyNone), true); 109 return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone); 110 } 111