Общий ответ заключается в том, что вы должны реализовать это самостоятельно, используя сборку. После соединения с libc у вас есть адрес функции, которую вы хотите вызвать, и вам нужно передать параметры в функцию вручную (используя соглашение о вызовах для любой платформы, на которой работает ваша виртуальная машина).
К счастью, есть библиотека, libffi , которая делает именно то, что вы хотите. Он также довольно прост в использовании, его источник включает некоторую документацию и примеры. Если вам интересно посмотреть, как это работает, вы можете взглянуть на его код (например, , вызывающий функцию с использованием соглашения о вызовах Unix ).
Что касается типов параметров, вы обычно должны позволить пользователю описать их для вас, принять их вслепую и передать их дальше libffi (или аппаратному обеспечению, если вы делаете это без libffi). Другим способом было бы проанализировать заголовочный файл C для вызываемой функции, который менее подвержен ошибкам - но в любом случае, действительно нет безопасного пути, поскольку двоичный код функции не описывает ее интерфейс (с printf и его список переменных параметров тем более).