diff --git a/graalpython/com.oracle.graal.python.c_embed/.gitignore b/graalpython/com.oracle.graal.python.c_embed/.gitignore
new file mode 100644
index 0000000000..07ed7069a2
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.c_embed/.gitignore
@@ -0,0 +1 @@
+build/*
\ No newline at end of file
diff --git a/graalpython/com.oracle.graal.python.c_embed/CMakeLists.txt b/graalpython/com.oracle.graal.python.c_embed/CMakeLists.txt
new file mode 100644
index 0000000000..375fbc6eef
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.c_embed/CMakeLists.txt
@@ -0,0 +1,55 @@
+cmake_minimum_required(VERSION 3.22)
+project(com.oracle.graal.python.c_embed LANGUAGES C DESCRIPTION "GraalPython C Embedding API")
+
+set(CMAKE_C_STANDARD 11)
+set(LIB_NAME "graalpython-embed")
+
+# don't install into the system but into the MX project's output dir
+set(CMAKE_INSTALL_PREFIX ${CMAKE_BINARY_DIR})
+
+function(require_var var)
+    if (NOT DEFINED ${var})
+        message(FATAL_ERROR "${var} needs to be set")
+    endif()
+endfunction()
+
+require_var(CEXT_H_INC)
+#
+# The statically linked graal native library for graalpy
+# e.g. libpythonvm.dylib
+# and the header files for graal_isolate etc.
+require_var(RUNTIME_LIB_DIR)
+
+#
+# e.g. libpython-native.dylib
+# should be statically linked too to expose static symbols
+require_var(PYTHON_NATIVE_LIB)
+
+message("Using graalpython runtime lib dir: ${RUNTIME_LIB_DIR}")
+message("Using graalpython native lib: ${PYTHON_NATIVE_LIB}")
+
+add_library(pythonvm STATIC IMPORTED)
+set_target_properties(pythonvm PROPERTIES IMPORTED_LOCATION "${RUNTIME_LIB_DIR}/libpythonvm.dylib")
+
+#
+# depend on libpython-native
+add_library(python-native STATIC IMPORTED)
+set_target_properties(python-native PROPERTIES IMPORTED_LOCATION "${PYTHON_NATIVE_LIB}")
+
+set(SRC_FILES src/graalpython-embed.c)
+
+add_library(${LIB_NAME} SHARED ${SRC_FILES})
+
+target_include_directories(${LIB_NAME} PUBLIC include)
+target_include_directories(${LIB_NAME} PRIVATE "${RUNTIME_LIB_DIR}")
+
+target_link_libraries(${LIB_NAME} pythonvm)
+target_link_libraries(${LIB_NAME} python-native)
+
+#
+# for pyport.h and exports.h
+target_include_directories(${LIB_NAME} PRIVATE "${CEXT_H_INC}")
+
+install(TARGETS ${LIB_NAME} DESTINATION bin)
+
+message("Created lib in: ${CMAKE_INSTALL_PREFIX}")
\ No newline at end of file
diff --git a/graalpython/com.oracle.graal.python.c_embed/README.md b/graalpython/com.oracle.graal.python.c_embed/README.md
new file mode 100644
index 0000000000..f1f84ecf7a
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.c_embed/README.md
@@ -0,0 +1,19 @@
+C-Python compatible bindings for graalpython
+
+
+
+## Manual building
+
+1. build graalpy stuff: `mx python-jvm`
+2. regen code: `mx python-capi-forwards`
+3. (if changed) rebuild graalpy native lib: `mx python-svm`
+4. build c lib: `mx build --dependencies com.oracle.graal.python.c_embed`
+
+## Test exported symbols (from graalpython dir)
+`nm -gU mxbuild/darwin-aarch64/com.oracle.graal.python.c_embed/aarch64/libgraalpython-embed.dylib | grep -i "T _Py"`
+
+
+# TODOs
+
+- check if static linking is working
+- make RUNTIME_LIB_DIR dynamic in mx suite
\ No newline at end of file
diff --git a/graalpython/com.oracle.graal.python.c_embed/include/graalpython-embed.h b/graalpython/com.oracle.graal.python.c_embed/include/graalpython-embed.h
new file mode 100644
index 0000000000..99407ae9c7
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.c_embed/include/graalpython-embed.h
@@ -0,0 +1,474 @@
+#include <stdio.h>
+#include "Python.h"
+// fix missing symbol PyExc_BaseException
+#include "pyerrors.h"
+#ifndef GRAALPYTHON_EMBED_H
+#define GRAALPYTHON_EMBED_H
+
+
+
+
+
+// {{start CAPI_BUILTINS}}
+// GENERATED CODE - see CApiCodeGen
+// This can be re-generated using the 'mx python-capi-forwards' command or
+// by executing the main class CApiCodeGen
+
+#define CAPI_BUILTINS \
+    BUILTIN(PyByteArray_Resize, int, PyObject*, Py_ssize_t) \
+    BUILTIN(PyBytes_AsString, char*, PyObject*) \
+    BUILTIN(PyBytes_FromObject, PyObject*, PyObject*) \
+    BUILTIN(PyBytes_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyCallIter_New, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyCallable_Check, int, PyObject*) \
+    BUILTIN(PyCapsule_GetContext, void*, PyObject*) \
+    BUILTIN(PyCapsule_GetDestructor, PyCapsule_Destructor, PyObject*) \
+    BUILTIN(PyCapsule_GetName, const char*, PyObject*) \
+    BUILTIN(PyCapsule_GetPointer, void*, PyObject*, const char*) \
+    BUILTIN(PyCapsule_Import, void*, const char*, int) \
+    BUILTIN(PyCapsule_IsValid, int, PyObject*, const char*) \
+    BUILTIN(PyCapsule_New, PyObject*, void*, const char*, PyCapsule_Destructor) \
+    BUILTIN(PyCapsule_SetContext, int, PyObject*, void*) \
+    BUILTIN(PyCapsule_SetDestructor, int, PyObject*, PyCapsule_Destructor) \
+    BUILTIN(PyCapsule_SetName, int, PyObject*, const char*) \
+    BUILTIN(PyCapsule_SetPointer, int, PyObject*, void*) \
+    BUILTIN(PyClassMethod_New, PyObject*, PyObject*) \
+    BUILTIN(PyCode_Addr2Line, int, PyCodeObject*, int) \
+    BUILTIN(PyCode_GetFileName, PyObject*, PyCodeObject*) \
+    BUILTIN(PyCode_GetName, PyObject*, PyCodeObject*) \
+    BUILTIN(PyCode_New, PyCodeObject*, int, int, int, int, int, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, int, PyObject*) \
+    BUILTIN(PyCode_NewEmpty, PyCodeObject*, const char*, const char*, int) \
+    BUILTIN(PyCode_NewWithPosOnlyArgs, PyCodeObject*, int, int, int, int, int, int, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, int, PyObject*) \
+    BUILTIN(PyCodec_Decoder, PyObject*, const char*) \
+    BUILTIN(PyCodec_Encoder, PyObject*, const char*) \
+    BUILTIN(PyComplex_FromDoubles, PyObject*, double, double) \
+    BUILTIN(PyComplex_ImagAsDouble, double, PyObject*) \
+    BUILTIN(PyComplex_RealAsDouble, double, PyObject*) \
+    BUILTIN(PyContextVar_New, PyObject*, const char*, PyObject*) \
+    BUILTIN(PyContextVar_Set, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyDictProxy_New, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Clear, void, PyObject*) \
+    BUILTIN(PyDict_Contains, int, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Copy, PyObject*, PyObject*) \
+    BUILTIN(PyDict_DelItem, int, PyObject*, PyObject*) \
+    BUILTIN(PyDict_GetItem, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyDict_GetItemWithError, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Items, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Keys, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Merge, int, PyObject*, PyObject*, int) \
+    BUILTIN(PyDict_New, PyObject*) \
+    BUILTIN(PyDict_SetDefault, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyDict_SetItem, int, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyDict_Update, int, PyObject*, PyObject*) \
+    BUILTIN(PyDict_Values, PyObject*, PyObject*) \
+    BUILTIN(PyErr_Display, void, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyErr_GivenExceptionMatches, int, PyObject*, PyObject*) \
+    BUILTIN(PyErr_NewException, PyObject*, const char*, PyObject*, PyObject*) \
+    BUILTIN(PyErr_NewExceptionWithDoc, PyObject*, const char*, const char*, PyObject*, PyObject*) \
+    BUILTIN(PyErr_Occurred, PyObject*) \
+    BUILTIN(PyErr_PrintEx, void, int) \
+    BUILTIN(PyErr_Restore, void, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyErr_SetExcInfo, void, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyEval_GetBuiltins, PyObject*) \
+    BUILTIN(PyEval_GetFrame, PyFrameObject*) \
+    BUILTIN(PyEval_RestoreThread, void, PyThreadState*) \
+    BUILTIN(PyEval_SaveThread, PyThreadState*) \
+    BUILTIN(PyException_GetCause, PyObject*, PyObject*) \
+    BUILTIN(PyException_GetContext, PyObject*, PyObject*) \
+    BUILTIN(PyException_GetTraceback, PyObject*, PyObject*) \
+    BUILTIN(PyException_SetCause, void, PyObject*, PyObject*) \
+    BUILTIN(PyException_SetContext, void, PyObject*, PyObject*) \
+    BUILTIN(PyException_SetTraceback, int, PyObject*, PyObject*) \
+    BUILTIN(PyFile_WriteObject, int, PyObject*, PyObject*, int) \
+    BUILTIN(PyFloat_FromDouble, PyObject*, double) \
+    BUILTIN(PyFloat_FromString, PyObject*, PyObject*) \
+    BUILTIN(PyFrame_GetBack, PyFrameObject*, PyFrameObject*) \
+    BUILTIN(PyFrame_GetBuiltins, PyObject*, PyFrameObject*) \
+    BUILTIN(PyFrame_GetCode, PyCodeObject*, PyFrameObject*) \
+    BUILTIN(PyFrame_GetGlobals, PyObject*, PyFrameObject*) \
+    BUILTIN(PyFrame_GetLasti, int, PyFrameObject*) \
+    BUILTIN(PyFrame_GetLineNumber, int, PyFrameObject*) \
+    BUILTIN(PyFrame_GetLocals, PyObject*, PyFrameObject*) \
+    BUILTIN(PyFrame_New, PyFrameObject*, PyThreadState*, PyCodeObject*, PyObject*, PyObject*) \
+    BUILTIN(PyFrozenSet_New, PyObject*, PyObject*) \
+    BUILTIN(PyGILState_Check, int) \
+    BUILTIN(PyImport_GetModuleDict, PyObject*) \
+    BUILTIN(PyImport_Import, PyObject*, PyObject*) \
+    BUILTIN(PyImport_ImportModule, PyObject*, const char*) \
+    BUILTIN(PyImport_ImportModuleLevelObject, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*, int) \
+    BUILTIN(PyImport_ImportModuleNoBlock, PyObject*, const char*) \
+    BUILTIN(PyIndex_Check, int, PyObject*) \
+    BUILTIN(PyInstanceMethod_New, PyObject*, PyObject*) \
+    BUILTIN(PyIter_Check, int, PyObject*) \
+    BUILTIN(PyIter_Next, PyObject*, PyObject*) \
+    BUILTIN(PyList_Append, int, PyObject*, PyObject*) \
+    BUILTIN(PyList_AsTuple, PyObject*, PyObject*) \
+    BUILTIN(PyList_GetItem, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PyList_GetSlice, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PyList_Insert, int, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(PyList_New, PyObject*, Py_ssize_t) \
+    BUILTIN(PyList_Reverse, int, PyObject*) \
+    BUILTIN(PyList_SetItem, int, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(PyList_SetSlice, int, PyObject*, Py_ssize_t, Py_ssize_t, PyObject*) \
+    BUILTIN(PyList_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyList_Sort, int, PyObject*) \
+    BUILTIN(PyLong_AsVoidPtr, void*, PyObject*) \
+    BUILTIN(PyLong_FromDouble, PyObject*, double) \
+    BUILTIN(PyLong_FromLong, PyObject*, long) \
+    BUILTIN(PyLong_FromLongLong, PyObject*, long long) \
+    BUILTIN(PyLong_FromSize_t, PyObject*, size_t) \
+    BUILTIN(PyLong_FromSsize_t, PyObject*, Py_ssize_t) \
+    BUILTIN(PyLong_FromUnicodeObject, PyObject*, PyObject*, int) \
+    BUILTIN(PyLong_FromUnsignedLong, PyObject*, unsigned long) \
+    BUILTIN(PyLong_FromUnsignedLongLong, PyObject*, unsigned long long) \
+    BUILTIN(PyMapping_Check, int, PyObject*) \
+    BUILTIN(PyMapping_Items, PyObject*, PyObject*) \
+    BUILTIN(PyMapping_Keys, PyObject*, PyObject*) \
+    BUILTIN(PyMapping_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyMapping_Values, PyObject*, PyObject*) \
+    BUILTIN(PyMemoryView_FromObject, PyObject*, PyObject*) \
+    BUILTIN(PyMemoryView_GetContiguous, PyObject*, PyObject*, int, char) \
+    BUILTIN(PyMethod_New, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyModule_AddIntConstant, int, PyObject*, const char*, long) \
+    BUILTIN(PyModule_AddObjectRef, int, PyObject*, const char*, PyObject*) \
+    BUILTIN(PyModule_GetNameObject, PyObject*, PyObject*) \
+    BUILTIN(PyModule_New, PyObject*, const char*) \
+    BUILTIN(PyModule_NewObject, PyObject*, PyObject*) \
+    BUILTIN(PyModule_SetDocString, int, PyObject*, const char*) \
+    BUILTIN(PyNumber_Absolute, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_Check, int, PyObject*) \
+    BUILTIN(PyNumber_Divmod, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_Float, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_InPlacePower, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_Index, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_Long, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_Power, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyNumber_ToBase, PyObject*, PyObject*, int) \
+    BUILTIN(PyOS_FSPath, PyObject*, PyObject*) \
+    BUILTIN(PyObject_ASCII, PyObject*, PyObject*) \
+    BUILTIN(PyObject_AsFileDescriptor, int, PyObject*) \
+    BUILTIN(PyObject_Bytes, PyObject*, PyObject*) \
+    BUILTIN(PyObject_ClearWeakRefs, void, PyObject*) \
+    BUILTIN(PyObject_DelItem, int, PyObject*, PyObject*) \
+    BUILTIN(PyObject_Dir, PyObject*, PyObject*) \
+    BUILTIN(PyObject_Format, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyObject_GetDoc, const char*, PyObject*) \
+    BUILTIN(PyObject_GetItem, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyObject_GetIter, PyObject*, PyObject*) \
+    BUILTIN(PyObject_HasAttr, int, PyObject*, PyObject*) \
+    BUILTIN(PyObject_HasAttrString, int, PyObject*, const char*) \
+    BUILTIN(PyObject_Hash, Py_hash_t, PyObject*) \
+    BUILTIN(PyObject_HashNotImplemented, Py_hash_t, PyObject*) \
+    BUILTIN(PyObject_IsInstance, int, PyObject*, PyObject*) \
+    BUILTIN(PyObject_IsSubclass, int, PyObject*, PyObject*) \
+    BUILTIN(PyObject_IsTrue, int, PyObject*) \
+    BUILTIN(PyObject_LengthHint, Py_ssize_t, PyObject*, Py_ssize_t) \
+    BUILTIN(PyObject_Repr, PyObject*, PyObject*) \
+    BUILTIN(PyObject_RichCompare, PyObject*, PyObject*, PyObject*, int) \
+    BUILTIN(PyObject_SetDoc, int, PyObject*, const char*) \
+    BUILTIN(PyObject_SetItem, int, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyObject_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyObject_Str, PyObject*, PyObject*) \
+    BUILTIN(PyObject_Type, PyObject*, PyObject*) \
+    BUILTIN(PyRun_StringFlags, PyObject*, const char*, int, PyObject*, PyObject*, PyCompilerFlags*) \
+    BUILTIN(PySeqIter_New, PyObject*, PyObject*) \
+    BUILTIN(PySequence_Check, int, PyObject*) \
+    BUILTIN(PySequence_Concat, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PySequence_Contains, int, PyObject*, PyObject*) \
+    BUILTIN(PySequence_Count, Py_ssize_t, PyObject*, PyObject*) \
+    BUILTIN(PySequence_DelItem, int, PyObject*, Py_ssize_t) \
+    BUILTIN(PySequence_DelSlice, int, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PySequence_GetItem, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PySequence_GetSlice, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PySequence_InPlaceConcat, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PySequence_InPlaceRepeat, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PySequence_Index, Py_ssize_t, PyObject*, PyObject*) \
+    BUILTIN(PySequence_Length, Py_ssize_t, PyObject*) \
+    BUILTIN(PySequence_List, PyObject*, PyObject*) \
+    BUILTIN(PySequence_Repeat, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PySequence_SetItem, int, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(PySequence_SetSlice, int, PyObject*, Py_ssize_t, Py_ssize_t, PyObject*) \
+    BUILTIN(PySequence_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PySequence_Tuple, PyObject*, PyObject*) \
+    BUILTIN(PySet_Add, int, PyObject*, PyObject*) \
+    BUILTIN(PySet_Clear, int, PyObject*) \
+    BUILTIN(PySet_Contains, int, PyObject*, PyObject*) \
+    BUILTIN(PySet_Discard, int, PyObject*, PyObject*) \
+    BUILTIN(PySet_New, PyObject*, PyObject*) \
+    BUILTIN(PySet_Pop, PyObject*, PyObject*) \
+    BUILTIN(PySet_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PySlice_New, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyStaticMethod_New, PyObject*, PyObject*) \
+    BUILTIN(PyStructSequence_New, PyObject*, PyTypeObject*) \
+    BUILTIN(PySys_GetObject, PyObject*, const char*) \
+    BUILTIN(PyThreadState_Get, PyThreadState*) \
+    BUILTIN(PyThreadState_GetDict, PyObject*) \
+    BUILTIN(PyThread_acquire_lock, int, PyThread_type_lock, int) \
+    BUILTIN(PyThread_allocate_lock, PyThread_type_lock) \
+    BUILTIN(PyThread_get_thread_ident, unsigned long) \
+    BUILTIN(PyThread_release_lock, void, PyThread_type_lock) \
+    BUILTIN(PyTraceBack_Here, int, PyFrameObject*) \
+    BUILTIN(PyTraceMalloc_Track, int, unsigned int, uintptr_t, size_t) \
+    BUILTIN(PyTraceMalloc_Untrack, int, unsigned int, uintptr_t) \
+    BUILTIN(PyTruffleByteArray_FromStringAndSize, PyObject*, const char*, Py_ssize_t) \
+    BUILTIN(PyTruffleBytes_Concat, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleBytes_FromFormat, PyObject*, const char*, PyObject*) \
+    BUILTIN(PyTruffleBytes_FromStringAndSize, PyObject*, const char*, Py_ssize_t) \
+    BUILTIN(PyTruffleCMethod_NewEx, PyObject*, PyMethodDef*, const char*, void*, int, int, PyObject*, PyObject*, PyTypeObject*, const char*) \
+    BUILTIN(PyTruffleComplex_AsCComplex, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleContextVar_Get, PyObject*, PyObject*, PyObject*, void*) \
+    BUILTIN(PyTruffleDateTimeCAPI_DateTime_FromDateAndTime, PyObject*, int, int, int, int, int, int, int, PyObject*, PyTypeObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_DateTime_FromDateAndTimeAndFold, PyObject*, int, int, int, int, int, int, int, PyObject*, int, PyTypeObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_DateTime_FromTimestamp, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_Date_FromDate, PyObject*, int, int, int, PyTypeObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_Date_FromTimestamp, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_Delta_FromDelta, PyObject*, int, int, int, int, PyTypeObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_TimeZone_FromTimeZone, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_Time_FromTime, PyObject*, int, int, int, int, PyObject*, PyTypeObject*) \
+    BUILTIN(PyTruffleDateTimeCAPI_Time_FromTimeAndFold, PyObject*, int, int, int, int, PyObject*, int, PyTypeObject*) \
+    BUILTIN(PyTruffleDescr_NewClassMethod, PyObject*, void*, const char*, const char*, int, int, void*, PyTypeObject*) \
+    BUILTIN(PyTruffleDescr_NewGetSet, PyObject*, const char*, PyTypeObject*, void*, void*, const char*, void*) \
+    BUILTIN(PyTruffleDict_Next, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTruffleErr_Fetch, PyObject*) \
+    BUILTIN(PyTruffleErr_GetExcInfo, PyObject*) \
+    BUILTIN(PyTruffleErr_WarnExplicit, PyObject*, PyObject*, PyObject*, PyObject*, int, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleFloat_AsDouble, double, PyObject*) \
+    BUILTIN(PyTruffleGILState_Ensure, int) \
+    BUILTIN(PyTruffleGILState_Release, void) \
+    BUILTIN(PyTruffleHash_InitSecret, void, int8_t*) \
+    BUILTIN(PyTruffleLong_AsPrimitive, long, PyObject*, int, long) \
+    BUILTIN(PyTruffleLong_FromString, PyObject*, PyObject*, int, int) \
+    BUILTIN(PyTruffleLong_One, PyObject*) \
+    BUILTIN(PyTruffleLong_Zero, PyObject*) \
+    BUILTIN(PyTruffleModule_AddFunctionToModule, int, void*, PyObject*, const char*, void*, int, int, const char*) \
+    BUILTIN(PyTruffleNumber_BinOp, PyObject*, PyObject*, PyObject*, int) \
+    BUILTIN(PyTruffleNumber_InPlaceBinOp, PyObject*, PyObject*, PyObject*, int) \
+    BUILTIN(PyTruffleNumber_UnaryOp, PyObject*, PyObject*, int) \
+    BUILTIN(PyTruffleObject_CallFunctionObjArgs, PyObject*, PyObject*, va_list*) \
+    BUILTIN(PyTruffleObject_CallMethodObjArgs, PyObject*, PyObject*, PyObject*, va_list*) \
+    BUILTIN(PyTruffleObject_GC_Track, void, void*) \
+    BUILTIN(PyTruffleObject_GC_UnTrack, void, void*) \
+    BUILTIN(PyTruffleObject_GenericGetAttr, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleObject_GenericSetAttr, int, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleObject_GetItemString, PyObject*, PyObject*, const char*) \
+    BUILTIN(PyTruffleState_FindModule, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTruffleStructSequence_InitType2, int, PyTypeObject*, void*, int) \
+    BUILTIN(PyTruffleStructSequence_NewType, PyTypeObject*, const char*, const char*, void*, int) \
+    BUILTIN(PyTruffleType_AddFunctionToType, int, void*, PyTypeObject*, PyObject*, const char*, void*, int, int, const char*) \
+    BUILTIN(PyTruffleType_AddGetSet, int, PyTypeObject*, PyObject*, const char*, void*, void*, const char*, void*) \
+    BUILTIN(PyTruffleType_AddMember, int, PyTypeObject*, PyObject*, const char*, int, Py_ssize_t, int, const char*) \
+    BUILTIN(PyTruffleType_AddSlot, int, PyTypeObject*, PyObject*, const char*, void*, int, int, const char*) \
+    BUILTIN(PyTruffleUnicode_Decode, PyObject*, PyObject*, const char*, const char*) \
+    BUILTIN(PyTruffleUnicode_DecodeUTF16Stateful, PyObject*, void*, Py_ssize_t, const char*, int, int) \
+    BUILTIN(PyTruffleUnicode_DecodeUTF32Stateful, PyObject*, void*, Py_ssize_t, const char*, int, int) \
+    BUILTIN(PyTruffleUnicode_DecodeUTF8Stateful, PyObject*, void*, Py_ssize_t, const char*, int) \
+    BUILTIN(PyTruffleUnicode_FromUCS, PyObject*, void*, Py_ssize_t, int) \
+    BUILTIN(PyTruffleUnicode_FromUTF, PyObject*, void*, Py_ssize_t, int) \
+    BUILTIN(PyTruffleUnicode_LookupAndIntern, PyObject*, PyObject*) \
+    BUILTIN(PyTruffleUnicode_New, PyObject*, void*, Py_ssize_t, Py_ssize_t, Py_UCS4) \
+    BUILTIN(PyTruffle_AddInheritedSlots, void, PyTypeObject*) \
+    BUILTIN(PyTruffle_Arg_ParseArrayAndKeywords, int, void*, Py_ssize_t, PyObject*, const char*, void*, void*) \
+    BUILTIN(PyTruffle_Arg_ParseTupleAndKeywords, int, PyObject*, PyObject*, const char*, void*, void*) \
+    BUILTIN(PyTruffle_Array_getbuffer, int, PyObject*, Py_buffer*, int) \
+    BUILTIN(PyTruffle_Array_releasebuffer, void, PyObject*, Py_buffer*) \
+    BUILTIN(PyTruffle_BulkNotifyRefCount, void, void*, int) \
+    BUILTIN(PyTruffle_ByteArray_EmptyWithCapacity, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTruffle_Bytes_CheckEmbeddedNull, int, PyObject*) \
+    BUILTIN(PyTruffle_Bytes_EmptyWithCapacity, PyObject*, long) \
+    BUILTIN(PyTruffle_Compute_Mro, PyObject*, PyTypeObject*, const char*) \
+    BUILTIN(PyTruffle_Debug, int, void*) \
+    BUILTIN(PyTruffle_DebugTrace, void) \
+    BUILTIN(PyTruffle_Ellipsis, PyObject*) \
+    BUILTIN(PyTruffle_False, PyObject*) \
+    BUILTIN(PyTruffle_FatalErrorFunc, void, const char*, const char*, int) \
+    BUILTIN(PyTruffle_FileSystemDefaultEncoding, PyObject*) \
+    BUILTIN(PyTruffle_GetInitialNativeMemory, size_t) \
+    BUILTIN(PyTruffle_GetMMapData, char*, PyObject*) \
+    BUILTIN(PyTruffle_GetMaxNativeMemory, size_t) \
+    BUILTIN(PyTruffle_HashConstant, long, int) \
+    BUILTIN(PyTruffle_InitBuiltinTypesAndStructs, void, void*) \
+    BUILTIN(PyTruffle_LogString, void, int, const char*) \
+    BUILTIN(PyTruffle_MemoryViewFromBuffer, PyObject*, void*, PyObject*, Py_ssize_t, int, Py_ssize_t, const char*, int, void*, void*, void*, void*) \
+    BUILTIN(PyTruffle_Native_Options, int) \
+    BUILTIN(PyTruffle_NewTypeDict, PyObject*, PyTypeObject*) \
+    BUILTIN(PyTruffle_NoValue, PyObject*) \
+    BUILTIN(PyTruffle_None, PyObject*) \
+    BUILTIN(PyTruffle_NotImplemented, PyObject*) \
+    BUILTIN(PyTruffle_NotifyRefCount, void, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTruffle_Object_Free, void, void*) \
+    BUILTIN(PyTruffle_PyDateTime_GET_TZINFO, PyObject*, PyObject*) \
+    BUILTIN(PyTruffle_PyUnicode_Find, Py_ssize_t, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t, int) \
+    BUILTIN(PyTruffle_Register_NULL, void, void*) \
+    BUILTIN(PyTruffle_Set_Native_Slots, int, PyTypeObject*, void*, void*) \
+    BUILTIN(PyTruffle_ToNative, int, void*) \
+    BUILTIN(PyTruffle_Trace_Type, int, void*, void*) \
+    BUILTIN(PyTruffle_TriggerGC, void, size_t) \
+    BUILTIN(PyTruffle_True, PyObject*) \
+    BUILTIN(PyTruffle_Type, PyTypeObject*, const char*) \
+    BUILTIN(PyTruffle_Type_Modified, int, PyTypeObject*, const char*, PyObject*) \
+    BUILTIN(PyTruffle_Unicode_AsUTF8AndSize_CharPtr, const char*, PyObject*) \
+    BUILTIN(PyTruffle_Unicode_AsUTF8AndSize_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyTruffle_Unicode_AsUnicodeAndSize_CharPtr, Py_UNICODE*, PyObject*) \
+    BUILTIN(PyTruffle_Unicode_AsUnicodeAndSize_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyTruffle_Unicode_AsWideChar, PyObject*, PyObject*, int) \
+    BUILTIN(PyTruffle_Unicode_FromFormat, PyObject*, const char*, va_list*) \
+    BUILTIN(PyTruffle_tss_create, long) \
+    BUILTIN(PyTruffle_tss_delete, void, long) \
+    BUILTIN(PyTruffle_tss_get, void*, long) \
+    BUILTIN(PyTruffle_tss_set, int, long, void*) \
+    BUILTIN(PyTuple_GetItem, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTuple_GetSlice, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PyTuple_New, PyObject*, Py_ssize_t) \
+    BUILTIN(PyTuple_SetItem, int, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(PyTuple_Size, Py_ssize_t, PyObject*) \
+    BUILTIN(PyType_IsSubtype, int, PyTypeObject*, PyTypeObject*) \
+    BUILTIN(PyUnicodeDecodeError_Create, PyObject*, const char*, const char*, Py_ssize_t, Py_ssize_t, Py_ssize_t, const char*) \
+    BUILTIN(PyUnicode_AsEncodedString, PyObject*, PyObject*, const char*, const char*) \
+    BUILTIN(PyUnicode_AsUnicodeEscapeString, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_Compare, int, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_Concat, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_Contains, int, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_Count, Py_ssize_t, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PyUnicode_DecodeFSDefault, PyObject*, const char*) \
+    BUILTIN(PyUnicode_EncodeFSDefault, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_FindChar, Py_ssize_t, PyObject*, Py_UCS4, Py_ssize_t, Py_ssize_t, int) \
+    BUILTIN(PyUnicode_Format, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_FromEncodedObject, PyObject*, PyObject*, const char*, const char*) \
+    BUILTIN(PyUnicode_FromObject, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_FromOrdinal, PyObject*, int) \
+    BUILTIN(PyUnicode_FromString, PyObject*, const char*) \
+    BUILTIN(PyUnicode_FromWideChar, PyObject*, const wchar_t*, Py_ssize_t) \
+    BUILTIN(PyUnicode_Join, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(PyUnicode_ReadChar, Py_UCS4, PyObject*, Py_ssize_t) \
+    BUILTIN(PyUnicode_Replace, PyObject*, PyObject*, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PyUnicode_Split, PyObject*, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(PyUnicode_Substring, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t) \
+    BUILTIN(PyUnicode_Tailmatch, Py_ssize_t, PyObject*, PyObject*, Py_ssize_t, Py_ssize_t, int) \
+    BUILTIN(PyWeakref_GetObject, PyObject*, PyObject*) \
+    BUILTIN(PyWeakref_NewRef, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(Py_AtExit, int, void (*)(void)) \
+    BUILTIN(Py_CompileString, PyObject*, const char*, const char*, int) \
+    BUILTIN(Py_CompileStringExFlags, PyObject*, const char*, const char*, int, PyCompilerFlags*, int) \
+    BUILTIN(Py_CompileStringObject, PyObject*, const char*, PyObject*, int, PyCompilerFlags*, int) \
+    BUILTIN(Py_EnterRecursiveCall, int, const char*) \
+    BUILTIN(Py_GenericAlias, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(Py_LeaveRecursiveCall, void) \
+    BUILTIN(Py_get_PyASCIIObject_length, Py_ssize_t, PyASCIIObject*) \
+    BUILTIN(Py_get_PyASCIIObject_state_ascii, unsigned int, PyASCIIObject*) \
+    BUILTIN(Py_get_PyASCIIObject_state_compact, unsigned int, PyASCIIObject*) \
+    BUILTIN(Py_get_PyASCIIObject_state_kind, unsigned int, PyASCIIObject*) \
+    BUILTIN(Py_get_PyASCIIObject_state_ready, unsigned int, PyASCIIObject*) \
+    BUILTIN(Py_get_PyASCIIObject_wstr, wchar_t*, PyASCIIObject*) \
+    BUILTIN(Py_get_PyByteArrayObject_ob_exports, Py_ssize_t, PyByteArrayObject*) \
+    BUILTIN(Py_get_PyByteArrayObject_ob_start, void*, PyByteArrayObject*) \
+    BUILTIN(Py_get_PyCFunctionObject_m_ml, PyMethodDef*, PyCFunctionObject*) \
+    BUILTIN(Py_get_PyCFunctionObject_m_module, PyObject*, PyCFunctionObject*) \
+    BUILTIN(Py_get_PyCFunctionObject_m_self, PyObject*, PyCFunctionObject*) \
+    BUILTIN(Py_get_PyCFunctionObject_m_weakreflist, PyObject*, PyCFunctionObject*) \
+    BUILTIN(Py_get_PyCFunctionObject_vectorcall, vectorcallfunc, PyCFunctionObject*) \
+    BUILTIN(Py_get_PyCMethodObject_mm_class, PyTypeObject*, PyCMethodObject*) \
+    BUILTIN(Py_get_PyCompactUnicodeObject_wstr_length, Py_ssize_t, PyCompactUnicodeObject*) \
+    BUILTIN(Py_get_PyDescrObject_d_name, PyObject*, PyDescrObject*) \
+    BUILTIN(Py_get_PyDescrObject_d_type, PyTypeObject*, PyDescrObject*) \
+    BUILTIN(Py_get_PyFrameObject_f_lineno, int, PyFrameObject*) \
+    BUILTIN(Py_get_PyGetSetDef_closure, void*, PyGetSetDef*) \
+    BUILTIN(Py_get_PyGetSetDef_doc, const char*, PyGetSetDef*) \
+    BUILTIN(Py_get_PyGetSetDef_get, getter, PyGetSetDef*) \
+    BUILTIN(Py_get_PyGetSetDef_name, const char*, PyGetSetDef*) \
+    BUILTIN(Py_get_PyGetSetDef_set, setter, PyGetSetDef*) \
+    BUILTIN(Py_get_PyInstanceMethodObject_func, PyObject*, PyInstanceMethodObject*) \
+    BUILTIN(Py_get_PyListObject_ob_item, PyObject**, PyListObject*) \
+    BUILTIN(Py_get_PyMethodDef_ml_doc, void*, PyMethodDef*) \
+    BUILTIN(Py_get_PyMethodDef_ml_flags, int, PyMethodDef*) \
+    BUILTIN(Py_get_PyMethodDef_ml_meth, PyCFunction, PyMethodDef*) \
+    BUILTIN(Py_get_PyMethodDef_ml_name, void*, PyMethodDef*) \
+    BUILTIN(Py_get_PyMethodDescrObject_d_method, PyMethodDef*, PyMethodDescrObject*) \
+    BUILTIN(Py_get_PyMethodObject_im_func, PyObject*, PyMethodObject*) \
+    BUILTIN(Py_get_PyMethodObject_im_self, PyObject*, PyMethodObject*) \
+    BUILTIN(Py_get_PyModuleDef_m_doc, const char*, PyModuleDef*) \
+    BUILTIN(Py_get_PyModuleDef_m_methods, PyMethodDef*, PyModuleDef*) \
+    BUILTIN(Py_get_PyModuleDef_m_name, const char*, PyModuleDef*) \
+    BUILTIN(Py_get_PyModuleDef_m_size, Py_ssize_t, PyModuleDef*) \
+    BUILTIN(Py_get_PyModuleObject_md_def, PyModuleDef*, PyModuleObject*) \
+    BUILTIN(Py_get_PyModuleObject_md_dict, PyObject*, PyModuleObject*) \
+    BUILTIN(Py_get_PyModuleObject_md_state, void*, PyModuleObject*) \
+    BUILTIN(Py_get_PyObject_ob_refcnt, Py_ssize_t, PyObject*) \
+    BUILTIN(Py_get_PyObject_ob_type, PyTypeObject*, PyObject*) \
+    BUILTIN(Py_get_PySetObject_used, Py_ssize_t, PySetObject*) \
+    BUILTIN(Py_get_PySliceObject_start, PyObject*, PySliceObject*) \
+    BUILTIN(Py_get_PySliceObject_step, PyObject*, PySliceObject*) \
+    BUILTIN(Py_get_PySliceObject_stop, PyObject*, PySliceObject*) \
+    BUILTIN(Py_get_PyTupleObject_ob_item, PyObject**, PyTupleObject*) \
+    BUILTIN(Py_get_PyUnicodeObject_data, void*, PyUnicodeObject*) \
+    BUILTIN(Py_get_PyVarObject_ob_size, Py_ssize_t, PyVarObject*) \
+    BUILTIN(Py_get_dummy, void*, void*) \
+    BUILTIN(Py_set_PyByteArrayObject_ob_exports, void, PyByteArrayObject*, int) \
+    BUILTIN(Py_set_PyFrameObject_f_lineno, void, PyFrameObject*, int) \
+    BUILTIN(Py_set_PyModuleObject_md_def, void, PyModuleObject*, PyModuleDef*) \
+    BUILTIN(Py_set_PyModuleObject_md_state, void, PyModuleObject*, void*) \
+    BUILTIN(Py_set_PyObject_ob_refcnt, void, PyObject*, Py_ssize_t) \
+    BUILTIN(Py_set_PyTypeObject_tp_as_buffer, void, PyTypeObject*, PyBufferProcs*) \
+    BUILTIN(Py_set_PyTypeObject_tp_base, void, PyTypeObject*, PyTypeObject*) \
+    BUILTIN(Py_set_PyTypeObject_tp_bases, void, PyTypeObject*, PyObject*) \
+    BUILTIN(Py_set_PyTypeObject_tp_clear, void, PyTypeObject*, inquiry) \
+    BUILTIN(Py_set_PyTypeObject_tp_dict, void, PyTypeObject*, PyObject*) \
+    BUILTIN(Py_set_PyTypeObject_tp_dictoffset, void, PyTypeObject*, Py_ssize_t) \
+    BUILTIN(Py_set_PyTypeObject_tp_finalize, void, PyTypeObject*, destructor) \
+    BUILTIN(Py_set_PyTypeObject_tp_getattr, void, PyTypeObject*, getattrfunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_getattro, void, PyTypeObject*, getattrofunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_iter, void, PyTypeObject*, getiterfunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_iternext, void, PyTypeObject*, iternextfunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_mro, void, PyTypeObject*, PyObject*) \
+    BUILTIN(Py_set_PyTypeObject_tp_new, void, PyTypeObject*, newfunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_setattr, void, PyTypeObject*, setattrfunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_setattro, void, PyTypeObject*, setattrofunc) \
+    BUILTIN(Py_set_PyTypeObject_tp_subclasses, void, PyTypeObject*, PyObject*) \
+    BUILTIN(Py_set_PyTypeObject_tp_traverse, void, PyTypeObject*, traverseproc) \
+    BUILTIN(Py_set_PyTypeObject_tp_weaklistoffset, void, PyTypeObject*, Py_ssize_t) \
+    BUILTIN(Py_set_PyVarObject_ob_size, void, PyVarObject*, Py_ssize_t) \
+    BUILTIN(_PyArray_Data, char*, PyObject*) \
+    BUILTIN(_PyArray_Resize, int, PyObject*, Py_ssize_t) \
+    BUILTIN(_PyBytes_Join, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(_PyDict_Pop, PyObject*, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(_PyDict_SetItem_KnownHash, int, PyObject*, PyObject*, PyObject*, Py_hash_t) \
+    BUILTIN(_PyErr_BadInternalCall, void, const char*, int) \
+    BUILTIN(_PyErr_ChainExceptions, void, PyObject*, PyObject*, PyObject*) \
+    BUILTIN(_PyErr_Occurred, PyObject*, PyThreadState*) \
+    BUILTIN(_PyErr_WriteUnraisableMsg, void, const char*, PyObject*) \
+    BUILTIN(_PyList_Extend, PyObject*, PyListObject*, PyObject*) \
+    BUILTIN(_PyList_SET_ITEM, void, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(_PyLong_AsByteArray, int, PyLongObject*, unsigned char*, size_t, int, int) \
+    BUILTIN(_PyLong_Sign, int, PyObject*) \
+    BUILTIN(_PyNamespace_New, PyObject*, PyObject*) \
+    BUILTIN(_PyNumber_Index, PyObject*, PyObject*) \
+    BUILTIN(_PyObject_Dump, void, PyObject*) \
+    BUILTIN(_PyObject_MakeTpCall, PyObject*, PyThreadState*, PyObject*, PyObject*const*, Py_ssize_t, PyObject*) \
+    BUILTIN(_PyTraceMalloc_NewReference, int, PyObject*) \
+    BUILTIN(_PyTraceback_Add, void, const char*, const char*, int) \
+    BUILTIN(_PyTruffleBytes_Resize, int, PyObject*, Py_ssize_t) \
+    BUILTIN(_PyTruffleErr_CreateAndSetException, void, PyObject*, PyObject*) \
+    BUILTIN(_PyTruffleErr_Warn, PyObject*, PyObject*, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(_PyTruffleEval_EvalCodeEx, PyObject*, PyObject*, PyObject*, PyObject*, PyObject*const*, int, PyObject*const*, int, PyObject*const*, int, PyObject*, PyObject*) \
+    BUILTIN(_PyTruffleModule_CreateInitialized_PyModule_New, PyModuleObject*, const char*) \
+    BUILTIN(_PyTruffleModule_GetAndIncMaxModuleNumber, Py_ssize_t) \
+    BUILTIN(_PyTruffleObject_Call1, PyObject*, PyObject*, PyObject*, PyObject*, int) \
+    BUILTIN(_PyTruffleObject_CallMethod1, PyObject*, PyObject*, const char*, PyObject*, int) \
+    BUILTIN(_PyTruffleSet_NextEntry, PyObject*, PyObject*, Py_ssize_t) \
+    BUILTIN(_PyTuple_SET_ITEM, int, PyObject*, Py_ssize_t, PyObject*) \
+    BUILTIN(_PyType_Lookup, PyObject*, PyTypeObject*, PyObject*) \
+    BUILTIN(_PyUnicode_AsASCIIString, PyObject*, PyObject*, const char*) \
+    BUILTIN(_PyUnicode_AsLatin1String, PyObject*, PyObject*, const char*) \
+    BUILTIN(_PyUnicode_AsUTF8String, PyObject*, PyObject*, const char*) \
+    BUILTIN(_PyUnicode_EqualToASCIIString, int, PyObject*, const char*) \
+    BUILTIN(_Py_GetErrorHandler, _Py_error_handler, const char*) \
+    BUILTIN(_Py_HashBytes, Py_hash_t, const void*, Py_ssize_t) \
+    BUILTIN(_Py_HashDouble, Py_hash_t, PyObject*, double) \
+
+// {{end CAPI_BUILTINS}}
+
+#define STUB(NAME, RET, ...) extern RET NAME(__VA_ARGS__);
+STUB_DEFS
+#undef STUB
+
+#endif //GRAALPYTHON_EMBED_H
diff --git a/graalpython/com.oracle.graal.python.c_embed/src/graalpython-embed.c b/graalpython/com.oracle.graal.python.c_embed/src/graalpython-embed.c
new file mode 100644
index 0000000000..72607874b2
--- /dev/null
+++ b/graalpython/com.oracle.graal.python.c_embed/src/graalpython-embed.c
@@ -0,0 +1,294 @@
+#include <stdio.h>
+#include <assert.h>
+#include <stddef.h> /* For offsetof */
+#include "Python.h"
+#include "exports.h"
+#include "pyport.h"
+#include "object.h"
+#include "datetime.h"
+#include "structmember.h"
+#include <graal_isolate.h>
+#include <libpythonvm.h>
+
+
+static graal_isolatethread_t* graal_thread = NULL;
+
+PyAPI_FUNC(int) Py_IsInitialized() {
+    return graal_thread != NULL;
+}
+
+PyAPI_FUNC(void) Py_InitializeEx(int initsigs) {
+    assert(graal_thread == NULL);
+    if(graal_create_isolate(NULL, NULL, &graal_thread) != 0) {
+        fprintf(stderr, "graal_create_isolate failed\n");
+        return;
+    }
+    fprintf(stderr, "graal_create_isolate succeeded\n");
+
+    graalpy_init_embed(graal_thread);
+
+    fprintf(stderr, "graalpy_init_embed succeeded, acquire GIL\n");
+    // accuire GIL
+    PyGILState_STATE gstate;
+    gstate = PyGILState_Ensure();
+
+    fprintf(stderr, "got GIL\n");
+}
+
+static void unimplemented(const char* name) {
+	printf("Function not implemented in graalpython-embed: %s\n", name);
+}
+
+#define FUNC_NOT_IMPLEMENTED unimplemented(__func__); exit(-1);
+
+
+// {{start CAPI_BUILTINS}}
+// GENERATED CODE - see CApiCodeGen
+// This can be re-generated using the 'mx python-capi-forwards' command or
+// by executing the main class CApiCodeGen
+
+PyAPI_FUNC(int64_t*) PyTruffle_constants() {
+    static int64_t constants[] = {
+        (int64_t) PYLONG_BITS_IN_DIGIT,
+        (int64_t) READONLY,
+        (int64_t) CHAR_MIN,
+        0xdead1111 // marker value
+    };
+    return constants;
+}
+PyAPI_FUNC(Py_ssize_t*) PyTruffle_struct_offsets() {
+    static Py_ssize_t offsets[] = {
+        offsetof(PyObject, ob_refcnt),
+        offsetof(PyObject, ob_type),
+        offsetof(PyVarObject, ob_size),
+        offsetof(PyModuleDef, m_name),
+        offsetof(PyModuleDef, m_doc),
+        offsetof(PyModuleDef, m_size),
+        offsetof(PyModuleDef, m_methods),
+        offsetof(PyModuleDef, m_slots),
+        offsetof(PyModuleDef, m_traverse),
+        offsetof(PyModuleDef, m_clear),
+        offsetof(PyModuleDef, m_free),
+        offsetof(PyModuleDef_Slot, slot),
+        offsetof(PyModuleDef_Slot, value),
+        offsetof(PyMethodDef, ml_name),
+        offsetof(PyMethodDef, ml_meth),
+        offsetof(PyMethodDef, ml_flags),
+        offsetof(PyMethodDef, ml_doc),
+        offsetof(PyMemoryViewObject, hash),
+        offsetof(PyMemoryViewObject, flags),
+        offsetof(PyMemoryViewObject, exports),
+        offsetof(PyMemoryViewObject, view),
+        offsetof(PyMemoryViewObject, ob_array),
+        offsetof(Py_buffer, buf),
+        offsetof(Py_buffer, obj),
+        offsetof(Py_buffer, len),
+        offsetof(Py_buffer, itemsize),
+        offsetof(Py_buffer, readonly),
+        offsetof(Py_buffer, ndim),
+        offsetof(Py_buffer, format),
+        offsetof(Py_buffer, shape),
+        offsetof(Py_buffer, strides),
+        offsetof(Py_buffer, suboffsets),
+        offsetof(Py_buffer, internal),
+        offsetof(PyDateTime_CAPI, DateType),
+        offsetof(PyDateTime_CAPI, DateTimeType),
+        offsetof(PyDateTime_CAPI, TimeType),
+        offsetof(PyDateTime_CAPI, DeltaType),
+        offsetof(PyDateTime_CAPI, TZInfoType),
+        offsetof(PyDateTime_CAPI, TimeZone_UTC),
+        offsetof(PyDateTime_CAPI, Date_FromDate),
+        offsetof(PyDateTime_CAPI, DateTime_FromDateAndTime),
+        offsetof(PyDateTime_CAPI, Time_FromTime),
+        offsetof(PyDateTime_CAPI, Delta_FromDelta),
+        offsetof(PyDateTime_CAPI, TimeZone_FromTimeZone),
+        offsetof(PyDateTime_CAPI, DateTime_FromTimestamp),
+        offsetof(PyDateTime_CAPI, Date_FromTimestamp),
+        offsetof(PyDateTime_CAPI, DateTime_FromDateAndTimeAndFold),
+        offsetof(PyDateTime_CAPI, Time_FromTimeAndFold),
+        offsetof(PyNumberMethods, nb_add),
+        offsetof(PyNumberMethods, nb_subtract),
+        offsetof(PyNumberMethods, nb_multiply),
+        offsetof(PyNumberMethods, nb_remainder),
+        offsetof(PyNumberMethods, nb_divmod),
+        offsetof(PyNumberMethods, nb_power),
+        offsetof(PyNumberMethods, nb_negative),
+        offsetof(PyNumberMethods, nb_positive),
+        offsetof(PyNumberMethods, nb_absolute),
+        offsetof(PyNumberMethods, nb_bool),
+        offsetof(PyNumberMethods, nb_invert),
+        offsetof(PyNumberMethods, nb_lshift),
+        offsetof(PyNumberMethods, nb_rshift),
+        offsetof(PyNumberMethods, nb_and),
+        offsetof(PyNumberMethods, nb_xor),
+        offsetof(PyNumberMethods, nb_or),
+        offsetof(PyNumberMethods, nb_int),
+        offsetof(PyNumberMethods, nb_reserved),
+        offsetof(PyNumberMethods, nb_float),
+        offsetof(PyNumberMethods, nb_inplace_add),
+        offsetof(PyNumberMethods, nb_inplace_subtract),
+        offsetof(PyNumberMethods, nb_inplace_multiply),
+        offsetof(PyNumberMethods, nb_inplace_remainder),
+        offsetof(PyNumberMethods, nb_inplace_power),
+        offsetof(PyNumberMethods, nb_inplace_lshift),
+        offsetof(PyNumberMethods, nb_inplace_rshift),
+        offsetof(PyNumberMethods, nb_inplace_and),
+        offsetof(PyNumberMethods, nb_inplace_xor),
+        offsetof(PyNumberMethods, nb_inplace_or),
+        offsetof(PyNumberMethods, nb_floor_divide),
+        offsetof(PyNumberMethods, nb_true_divide),
+        offsetof(PyNumberMethods, nb_inplace_floor_divide),
+        offsetof(PyNumberMethods, nb_inplace_true_divide),
+        offsetof(PyNumberMethods, nb_index),
+        offsetof(PyNumberMethods, nb_matrix_multiply),
+        offsetof(PyNumberMethods, nb_inplace_matrix_multiply),
+        offsetof(PySequenceMethods, sq_length),
+        offsetof(PySequenceMethods, sq_concat),
+        offsetof(PySequenceMethods, sq_repeat),
+        offsetof(PySequenceMethods, sq_item),
+        offsetof(PySequenceMethods, was_sq_slice),
+        offsetof(PySequenceMethods, sq_ass_item),
+        offsetof(PySequenceMethods, was_sq_ass_slice),
+        offsetof(PySequenceMethods, sq_contains),
+        offsetof(PySequenceMethods, sq_inplace_concat),
+        offsetof(PySequenceMethods, sq_inplace_repeat),
+        offsetof(PyMappingMethods, mp_length),
+        offsetof(PyMappingMethods, mp_subscript),
+        offsetof(PyMappingMethods, mp_ass_subscript),
+        offsetof(PyAsyncMethods, am_await),
+        offsetof(PyAsyncMethods, am_aiter),
+        offsetof(PyAsyncMethods, am_anext),
+        offsetof(PyAsyncMethods, am_send),
+        offsetof(PyBufferProcs, bf_getbuffer),
+        offsetof(PyBufferProcs, bf_releasebuffer),
+        offsetof(PyTypeObject, tp_name),
+        offsetof(PyTypeObject, tp_basicsize),
+        offsetof(PyTypeObject, tp_itemsize),
+        offsetof(PyTypeObject, tp_dealloc),
+        offsetof(PyTypeObject, tp_vectorcall_offset),
+        offsetof(PyTypeObject, tp_getattr),
+        offsetof(PyTypeObject, tp_setattr),
+        offsetof(PyTypeObject, tp_as_async),
+        offsetof(PyTypeObject, tp_repr),
+        offsetof(PyTypeObject, tp_as_number),
+        offsetof(PyTypeObject, tp_as_sequence),
+        offsetof(PyTypeObject, tp_as_mapping),
+        offsetof(PyTypeObject, tp_hash),
+        offsetof(PyTypeObject, tp_call),
+        offsetof(PyTypeObject, tp_str),
+        offsetof(PyTypeObject, tp_getattro),
+        offsetof(PyTypeObject, tp_setattro),
+        offsetof(PyTypeObject, tp_as_buffer),
+        offsetof(PyTypeObject, tp_flags),
+        offsetof(PyTypeObject, tp_doc),
+        offsetof(PyTypeObject, tp_traverse),
+        offsetof(PyTypeObject, tp_clear),
+        offsetof(PyTypeObject, tp_richcompare),
+        offsetof(PyTypeObject, tp_weaklistoffset),
+        offsetof(PyTypeObject, tp_iter),
+        offsetof(PyTypeObject, tp_iternext),
+        offsetof(PyTypeObject, tp_methods),
+        offsetof(PyTypeObject, tp_members),
+        offsetof(PyTypeObject, tp_getset),
+        offsetof(PyTypeObject, tp_base),
+        offsetof(PyTypeObject, tp_dict),
+        offsetof(PyTypeObject, tp_descr_get),
+        offsetof(PyTypeObject, tp_descr_set),
+        offsetof(PyTypeObject, tp_dictoffset),
+        offsetof(PyTypeObject, tp_init),
+        offsetof(PyTypeObject, tp_alloc),
+        offsetof(PyTypeObject, tp_new),
+        offsetof(PyTypeObject, tp_free),
+        offsetof(PyTypeObject, tp_is_gc),
+        offsetof(PyTypeObject, tp_bases),
+        offsetof(PyTypeObject, tp_mro),
+        offsetof(PyTypeObject, tp_cache),
+        offsetof(PyTypeObject, tp_subclasses),
+        offsetof(PyTypeObject, tp_weaklist),
+        offsetof(PyTypeObject, tp_del),
+        offsetof(PyTypeObject, tp_version_tag),
+        offsetof(PyTypeObject, tp_finalize),
+        offsetof(PyTypeObject, tp_vectorcall),
+        offsetof(PyBytesObject, ob_shash),
+        offsetof(PyBytesObject, ob_sval),
+        offsetof(PyListObject, ob_item),
+        offsetof(PyListObject, allocated),
+        offsetof(PyTupleObject, ob_item),
+        offsetof(PyFloatObject, ob_fval),
+        offsetof(PyModuleDef_Base, m_index),
+        offsetof(PyComplexObject, cval.real),
+        offsetof(PyComplexObject, cval.imag),
+        offsetof(PyASCIIObject, length),
+        offsetof(PyASCIIObject, hash),
+        offsetof(PyASCIIObject, state),
+        offsetof(PyASCIIObject, wstr),
+        offsetof(PyCompactUnicodeObject, utf8_length),
+        offsetof(PyCompactUnicodeObject, utf8),
+        offsetof(PyCompactUnicodeObject, wstr_length),
+        offsetof(PyUnicodeObject, data),
+        offsetof(PyGetSetDef, name),
+        offsetof(PyGetSetDef, get),
+        offsetof(PyGetSetDef, set),
+        offsetof(PyGetSetDef, doc),
+        offsetof(PyGetSetDef, closure),
+        offsetof(PyMemberDef, name),
+        offsetof(PyMemberDef, type),
+        offsetof(PyMemberDef, offset),
+        offsetof(PyMemberDef, flags),
+        offsetof(PyMemberDef, doc),
+        offsetof(PyThreadState, interp),
+        offsetof(PyThreadState, dict),
+        offsetof(PyBaseExceptionObject, dict),
+        offsetof(PyBaseExceptionObject, args),
+        offsetof(PyBaseExceptionObject, traceback),
+        offsetof(PyBaseExceptionObject, context),
+        offsetof(PyBaseExceptionObject, cause),
+        offsetof(PyBaseExceptionObject, suppress_context),
+        0xdead2222 // marker value
+    };
+    return offsets;
+}
+PyAPI_FUNC(Py_ssize_t*) PyTruffle_struct_sizes() {
+    static Py_ssize_t sizes[] = {
+        sizeof(PyModuleDef),
+        sizeof(PyModuleDef_Slot),
+        sizeof(PyMethodDef),
+        sizeof(PyObject),
+        sizeof(PyBytesObject),
+        sizeof(PyListObject),
+        sizeof(PyVarObject),
+        sizeof(PyMemoryViewObject),
+        sizeof(Py_buffer),
+        sizeof(PyDateTime_CAPI),
+        sizeof(PyNumberMethods),
+        sizeof(PySequenceMethods),
+        sizeof(PyMappingMethods),
+        sizeof(PyAsyncMethods),
+        sizeof(PyBufferProcs),
+        sizeof(PyTypeObject),
+        sizeof(PyTupleObject),
+        sizeof(PyFloatObject),
+        sizeof(PyLongObject),
+        sizeof(PyModuleDef_Base),
+        sizeof(PyComplexObject),
+        sizeof(PyDateTime_Date),
+        sizeof(PyDateTime_Time),
+        sizeof(PyDateTime_DateTime),
+        sizeof(PyDateTime_Delta),
+        sizeof(PyASCIIObject),
+        sizeof(PyCompactUnicodeObject),
+        sizeof(PyBaseExceptionObject),
+        sizeof(PyUnicodeObject),
+        sizeof(Py_UNICODE),
+        sizeof(PyGetSetDef),
+        sizeof(PyMemberDef),
+        sizeof(PyThreadState),
+        sizeof(wchar_t),
+        sizeof(long long),
+        sizeof(Py_ssize_t),
+        0xdead3333 // marker value
+    };
+    return sizes;
+}
+// {{end CAPI_BUILTINS}}
+
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java
index 8da1528028..d504c8f37f 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java
@@ -940,6 +940,7 @@ protected void initializeMultiThreading(PythonContext context) {
 
     @Override
     protected void initializeThread(PythonContext context, Thread thread) {
+        LOGGER.info("PythonLanguage.initializeThread");
         context.attachThread(thread, threadState);
     }
 
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
index d834c1eef1..4e21c62719 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextCEvalBuiltins.java
@@ -103,7 +103,9 @@ abstract static class PyEval_SaveThread extends CApiNullaryBuiltinNode {
 
         @Specialization
         static Object save(@Cached GilNode gil) {
+            // LOGGER.info("PyEval_SaveThread gil defined: " + (gil != null));
             PythonContext context = PythonContext.get(gil);
+            LOGGER.info(() -> "PyEval_SaveThread context defined: " + (context != null));
             Object threadState = PThreadState.getThreadState(PythonLanguage.get(gil), context);
             LOGGER.fine("C extension releases GIL");
             gil.release(context, true);
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiCodeGen.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiCodeGen.java
index 86bbf009e3..baeb5b0cc8 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiCodeGen.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiCodeGen.java
@@ -142,7 +142,7 @@ private static Path resolvePath(Path path) {
      *
      * @return true if the file was modified, false if there were no changes
      */
-    private static boolean writeGenerated(Path path, List<String> contents) throws IOException {
+    static boolean writeGenerated(Path path, List<String> contents) throws IOException {
         Path capi = CApiCodeGen.resolvePath(path);
         List<String> lines = Files.readAllLines(capi);
         int start = -1;
@@ -440,6 +440,8 @@ public static void main(String[] args) throws IOException {
         changed |= generateCApiHeader(javaBuiltins);
         changed |= generateBuiltinRegistry(javaBuiltins);
         changed |= checkImports(allBuiltins);
+        changed |= CEmbedCodeGen.generateCPyEmbedHeader(javaBuiltins);
+        changed |= CEmbedCodeGen.generateCPyEmbedSource(javaBuiltins);
         if (changed) {
             System.exit(-1);
         }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
index 95d83ecdc2..12e211637a 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiContext.java
@@ -530,21 +530,26 @@ public int getId() {
     @TruffleBoundary
     public static CApiContext ensureCapiWasLoaded() {
         try {
+            System.out.println("ensureCapiWasLoaded called  python context: " + PythonContext.get(null));
             return CApiContext.ensureCapiWasLoaded(null, PythonContext.get(null), T_EMPTY_STRING, T_EMPTY_STRING);
         } catch (Exception e) {
+            System.err.println("Error loading CAPI: " + e.getMessage());
             throw CompilerDirectives.shouldNotReachHere(e);
         }
     }
 
     @TruffleBoundary
     public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context, TruffleString name, TruffleString path) throws IOException, ImportException, ApiInitException {
+        System.err.println("ensureCapiWasLoaded called with context " + context + " and name " + name + " and path " + path + " and node " + node);
         if (!context.hasCApiContext()) {
+            System.err.println("Loading CAPI");
             Env env = context.getEnv();
             InteropLibrary U = InteropLibrary.getUncached();
 
             TruffleFile homePath = env.getInternalTruffleFile(context.getCAPIHome().toJavaStringUncached());
             // e.g. "libpython-native.so"
             String libName = context.getLLVMSupportExt("python");
+            System.err.println("Loading CAPI from " + libName);
             TruffleFile capiFile = homePath.resolve(libName).getCanonicalFile(LinkOption.NOFOLLOW_LINKS);
             try {
                 SourceBuilder capiSrcBuilder;
@@ -555,11 +560,12 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
                         LOGGER.warning("GraalPy option 'NativeModules' is set to true, " +
                                         "but only one context in the process can use native modules, " +
                                         "second and other contexts fallback to NativeModules=false and " +
-                                        "will use LLVM bitcode execution via GraalVM LLVM.");
+                                        "will use LLVM bitcode executxion via GraalVM LLVM.");
                     }
                 } else {
                     useNative = false;
                 }
+                LOGGER.info(() -> "Loading CAPI from " + capiFile + " as " + (useNative ? "native" : "bitcode"));
                 if (useNative) {
                     context.ensureNFILanguage(node, "NativeModules", "true");
                     capiSrcBuilder = Source.newBuilder(J_NFI_LANGUAGE, "load(RTLD_GLOBAL) \"" + capiFile.getPath() + "\"", "<libpython>");
@@ -570,26 +576,32 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
                 if (!context.getLanguage().getEngineOption(PythonOptions.ExposeInternalSources)) {
                     capiSrcBuilder.internal(true);
                 }
-                LOGGER.config(() -> "loading CAPI from " + capiFile + " as " + (useNative ? "native" : "bitcode"));
+                LOGGER.info(() -> "loading CAPI from " + capiFile + " as " + (useNative ? "native" : "bitcode"));
                 CallTarget capiLibraryCallTarget = context.getEnv().parseInternal(capiSrcBuilder.build());
 
                 Object capiLibrary = capiLibraryCallTarget.call();
+                LOGGER.info(() -> "Checking init function");
                 Object initFunction = U.readMember(capiLibrary, "initialize_graal_capi");
                 CApiContext cApiContext = new CApiContext(context, capiLibrary, useNative);
                 context.setCApiContext(cApiContext);
                 if (!U.isExecutable(initFunction)) {
                     Object signature = env.parseInternal(Source.newBuilder(J_NFI_LANGUAGE, "(ENV,(SINT32):POINTER):VOID", "exec").build()).call();
                     initFunction = SignatureLibrary.getUncached().bind(signature, initFunction);
+                    LOGGER.info(() -> "Bound & calling init function");
                     U.execute(initFunction, new GetBuiltin());
                 } else {
+                    LOGGER.info(() -> "Executing init function");
                     U.execute(initFunction, NativePointer.createNull(), new GetBuiltin());
                 }
-
+                LOGGER.info(() -> "Checking builtins");
                 assert CApiCodeGen.assertBuiltins(capiLibrary);
+                LOGGER.info(() -> "initWrapper");
                 cApiContext.pyDateTimeCAPICapsule = PyDateTimeCAPIWrapper.initWrapper(context, cApiContext);
+                LOGGER.info(() -> "runCApiHooks");
                 context.runCApiHooks();
 
                 if (useNative) {
+                    LOGGER.info(() -> "registering native finalizer");
                     /*
                      * C++ libraries sometimes declare global objects that have destructors that
                      * call Py_DECREF. Those destructors are then called during native shutdown,
@@ -609,7 +621,7 @@ public static CApiContext ensureCapiWasLoaded(Node node, PythonContext context,
                         LOGGER.warning(() -> "didn't register a native finalizer due to: " + e.getMessage());
                     }
                 }
-
+                LOGGER.info(() -> "loaded CAPI from " + capiFile + " as " + (useNative ? "native" : "bitcode") + " successfully");
                 return cApiContext;
             } catch (PException e) {
                 /*
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedBuiltins.java
new file mode 100644
index 0000000000..df39e281da
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedBuiltins.java
@@ -0,0 +1,28 @@
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import com.oracle.graal.python.runtime.GilNode;
+import com.oracle.graal.python.runtime.PythonContext;
+import org.graalvm.nativeimage.IsolateThread;
+import org.graalvm.nativeimage.c.function.CEntryPoint;
+import org.graalvm.polyglot.Context;
+
+public class CEmbedBuiltins {
+
+    static Context pyContext;
+
+    @CEntryPoint(name = "graalpy_init_embed")
+    public static void GraalPyInitEmbed(IsolateThread thread) {
+        System.out.println("GraalPyInitEmbed from thread " + thread.rawValue());
+        pyContext = Context.newBuilder("python")
+                .allowAllAccess(true)
+                .option("python.PythonHome", "/Users/mkind/Dev/Uni/RE23/graal/sdk/mxbuild/darwin-aarch64/PYTHON_NATIVE_STANDALONE_SVM_JAVA21/graalpy-community-24.0.0-dev-macos-aarch64")
+                .build();
+        pyContext.initialize("python");
+        pyContext.enter();
+        GilNode.getUncached().acquire();
+        CApiContext ctx = CApiContext.ensureCapiWasLoaded();
+        System.out.println("GraalPyInitEmbed from thread " + thread.rawValue() + " done, got ctx " + ctx.toString());
+        // get GIL
+
+    }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedCodeGen.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedCodeGen.java
new file mode 100644
index 0000000000..68f4361e34
--- /dev/null
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CEmbedCodeGen.java
@@ -0,0 +1,129 @@
+package com.oracle.graal.python.builtins.objects.cext.capi;
+
+import com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor;
+import com.oracle.graal.python.builtins.objects.cext.structs.CConstants;
+import com.oracle.graal.python.builtins.objects.cext.structs.CFields;
+import com.oracle.graal.python.builtins.objects.cext.structs.CStructs;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.Direct;
+import static com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiCallPath.NotImplemented;
+
+public final class CEmbedCodeGen {
+
+    /**
+     * Generates the c-python compatible header file cpy_embed.h
+     * to allow embedding GraalPython in other projects that assume
+     * the CPython C API.
+     */
+     static boolean generateCPyEmbedHeader(List<CApiCodeGen.CApiBuiltinDesc> javaBuiltins) throws IOException {
+        System.out.println("Generating cpy_embed.h");
+        List<String> lines = new ArrayList<>();
+         lines.add("#define CAPI_BUILTINS \\");
+         int id = 0;
+         for (var entry : javaBuiltins) {
+             assert (id++) == entry.id;
+             String line = "    BUILTIN(" + entry.name + ", " + entry.returnType.cSignature;
+             for (var arg : entry.arguments) {
+                 line += ", " + arg.cSignature;
+             }
+             line += ") \\";
+             lines.add(line);
+         }
+         lines.add("");
+
+        return CApiCodeGen.writeGenerated(Path.of("com.oracle.graal.python.c_embed", "include", "graalpython-embed.h"), lines);
+    }
+
+    /**
+     * Generates the c-python compatible source file cpy_embed.c
+     * to allow embedding GraalPython in other projects that assume
+     * the CPython C API.
+     */
+    static boolean generateCPyEmbedSource(List<CApiCodeGen.CApiBuiltinDesc> javaBuiltins) throws IOException {
+        System.out.println("Generating graalpython-embed.c");
+        List<String> lines = new ArrayList<>();
+        for (var entry : javaBuiltins) {
+            String name = entry.name;
+            CApiCodeGen.CApiBuiltinDesc value = entry;
+            if (value.call == NotImplemented) {
+                lines.add("#undef " + name);
+                String line = "PyAPI_FUNC(" + value.returnType.cSignature + ") " + name + "(";
+                for (int i = 0; i < value.arguments.length; i++) {
+                    line += (i == 0 ? "" : ", ") + CApiCodeGen.CApiBuiltinDesc.getArgSignatureWithName(value.arguments[i], i);
+                }
+                line += ") {";
+                lines.add(line);
+                System.out.println("Generating " + name + " with call type " + value.call);
+                if (value.call == Direct) { // DISABLED
+                    line = "    " + (value.returnType == ArgDescriptor.Void ? "" : "return ") + "Graal" + name + "(";
+                    for (int i = 0; i < value.arguments.length; i++) {
+                        line += (i == 0 ? "" : ", ");
+                        line += argName(i);
+                    }
+                    line += ");";
+                } else {
+                    line = "    FUNC_NOT_IMPLEMENTED";
+                }
+                //line = "    FUNC_NOT_IMPLEMENTED";
+                lines.add(line);
+                lines.add("}");
+            }
+        }
+
+        lines.add("PyAPI_FUNC(int64_t*) PyTruffle_constants() {");
+        lines.add("    static int64_t constants[] = {");
+        for (CConstants constant : CConstants.VALUES) {
+            lines.add("        (int64_t) " + constant.name() + ",");
+        }
+        lines.add("        0xdead1111 // marker value");
+        lines.add("    };");
+        lines.add("    return constants;");
+        lines.add("}");
+        lines.add("PyAPI_FUNC(Py_ssize_t*) PyTruffle_struct_offsets() {");
+        lines.add("    static Py_ssize_t offsets[] = {");
+        for (CFields field : CFields.VALUES) {
+            int delim = field.name().indexOf("__");
+            assert delim != -1;
+            String struct = field.name().substring(0, delim);
+            String name = field.name().substring(delim + 2);
+            name = name.replace("__", "."); // to allow inlined structs
+            lines.add("        offsetof(" + struct + ", " + name + "),");
+        }
+        lines.add("        0xdead2222 // marker value");
+        lines.add("    };");
+        lines.add("    return offsets;");
+        lines.add("}");
+        lines.add("PyAPI_FUNC(Py_ssize_t*) PyTruffle_struct_sizes() {");
+        lines.add("    static Py_ssize_t sizes[] = {");
+        for (CStructs struct : CStructs.VALUES) {
+            lines.add("        sizeof(" + struct.name().replace("__", " ") + "),");
+        }
+        lines.add("        0xdead3333 // marker value");
+        lines.add("    };");
+        lines.add("    return sizes;");
+        lines.add("}");
+
+        return CApiCodeGen.writeGenerated(Path.of("com.oracle.graal.python.c_embed", "src", "graalpython-embed.c"), lines);
+    }
+
+    private static String argName(int i) {
+        return "" + (char) ('a' + i);
+    }
+
+    public static void main(String[] args) throws IOException {
+        List<CApiCodeGen.CApiBuiltinDesc> builtins =  CApiFunction.getJavaBuiltinDefinitions();
+        List<CApiCodeGen.CApiBuiltinDesc> javaBuiltins = new ArrayList<>();
+        for (CApiCodeGen.CApiBuiltinDesc builtin : builtins) {
+            if (builtin.id != -1) {
+                javaBuiltins.add(builtin);
+            }
+        }
+        generateCPyEmbedHeader(javaBuiltins);
+        generateCPyEmbedSource(javaBuiltins);
+    }
+}
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
index dbda35cc2f..b3bb1f2983 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java
@@ -1150,6 +1150,7 @@ public PythonContext(PythonLanguage language, TruffleLanguage.Env env) {
     private static final ContextReference<PythonContext> REFERENCE = ContextReference.create(PythonLanguage.class);
 
     public static PythonContext get(Node node) {
+        // System.out.println("PythonContext.get " + REFERENCE.get(node));
         return REFERENCE.get(node);
     }
 
diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py
index 9dd45d043c..832657ad68 100644
--- a/mx.graalpython/mx_graalpython.py
+++ b/mx.graalpython/mx_graalpython.py
@@ -2274,7 +2274,7 @@ def as_license_regex(name):
             jar_distributions=['graalpython:GRAALPYTHON-LAUNCHER', 'sdk:MAVEN_DOWNLOADER'],
             main_class=GRAALPYTHON_MAIN_CLASS,
             build_args=[
-                '-H:+DetectUserDirectoriesInImageHeap',
+                '-H:-DetectUserDirectoriesInImageHeap',
                 '-H:-CopyLanguageResources',
                 '-Dpolyglot.python.PosixModuleBackend=native',
                 '-Dpolyglot.python.Sha3ModuleBackend=native',
diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py
index 73a3ba4e23..194eb5abc5 100644
--- a/mx.graalpython/suite.py
+++ b/mx.graalpython/suite.py
@@ -651,6 +651,31 @@
             ],
         },
 
+        "com.oracle.graal.python.c_embed": {
+            "subDir": "graalpython",
+            "class": "CMakeNinjaProject",
+            "toolchain": "sulong:SULONG_BOOTSTRAP_TOOLCHAIN",
+            "max_jobs": "8",
+            "vpath": True,
+            "ninja_targets": ["all"],
+            "ninja_install_targets": ["install"],
+            "os_arch": {
+                "<others>": {
+                    "<others>": {
+                        "cmakeConfig": {
+                            "CEXT_H_INC": "<path:com.oracle.graal.python.cext>/include",
+                            # TODO make dynamic
+                            "RUNTIME_LIB_DIR": "/Users/mkind/Dev/Uni/RE23/graal/sdk/mxbuild/darwin-aarch64/libpythonvm.dylib.image",
+                            "PYTHON_NATIVE_LIB": "/Users/mkind/Dev/Uni/RE23/graalpython/mxbuild/darwin-aarch64/com.oracle.graal.python.cext/aarch64/bin/libpython-native.dylib"
+                        },
+                        "results": [
+                            # "bin/<lib:python-native>"
+                            # TODO
+                        ],
+                    },
+                },
+            }
+        },
         "com.oracle.graal.python.cext": {
             "subDir": "graalpython",
             "class": "CMakeNinjaProject",