#include <dlfcn.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <setjmp.h>
#include <signal.h>
typedef void (*demo_func_t)(void);
#define TEMPLATE_PATH "/tmp/dynlink_XXXXXX"
#define MAX_LINE 1024
#define COMPILE_CMD "gcc -shared -fPIC -o %s %s"
static void
generate_source(const char *filename, const char *user_code)
{
FILE *fp;
if ((fp = fopen(filename, "w")) == NULL)
err(1, "fopen");
fprintf(fp, "#include <stdio.h>\n#include <unistd.h>\n"
"#include <fcntl.h>\n#include <stddef.h>\n"
"#include <stdint.h>\n#include <stdlib.h>\n\n"
"void demo_function(void)\n"
"{\n"
" %s\n"
"}\n", user_code);
fclose(fp);
}
static char *
create_temp_file(const char *suffix)
{
char *filename;
int fd;
/* Allocate space for template + suffix + null terminator */
if (!(filename = malloc(strlen(TEMPLATE_PATH) + strlen(suffix) + 1)))
err(1, "malloc");
strcpy(filename, TEMPLATE_PATH);
if ((fd = mkstemp(filename)) == -1)
err(1, "mkstemp");
close(fd);
/* Add suffix to the generated filename */
strcat(filename, suffix);
return filename;
}
static int
compile_shared_object(const char *src_file, const char *so_file)
{
char cmd[MAX_LINE];
snprintf(cmd, sizeof(cmd), COMPILE_CMD, so_file, src_file);
printf("[DEBUG] Compiling with command: %s\n", cmd);
return system(cmd);
}
static char *src_file, *so_file;
static void
zap_files(void)
{
if (src_file) {
unlink(src_file);
free(src_file);
src_file = NULL;
}
if (so_file) {
unlink(so_file);
free(so_file);
so_file = NULL;
}
}
/* Signal handlers for Alarm, Segmentation fault, Bus error */
jmp_buf give_up;
static void
sig_alarm(int signo)
{
fprintf(stderr, "Timeout: Execution took too long\n");
longjmp(give_up, 1);
}
static void
sig_mem(int signo)
{
fprintf(stderr, "Memory access error\n");
longjmp(give_up, 1);
}
int
main(void)
{
char *line;
void *handle;
demo_func_t func;
const char *error;
size_t linelen = 0;
/* Set up cleanup handler */
atexit(zap_files);
while (1) {
printf("Enter one line of C code (for function body):\n"
"(e.g., --> printf(\"Hello, world!\\n\"); <-- )\n"
"Type 'exit(0);' to quit\n");
if ((linelen = getline(&line, &linelen, stdin)) == -1)
err(1, "getline");
/* Create temporary source and shared object files */
src_file = create_temp_file(".c");
so_file = strdup("./temp.so");
printf("[DEBUG] Temporary files: Source = %s, "
"Shared object: %s\n", src_file, so_file);
/* Generate and compile the source */
generate_source(src_file, line);
printf("[DEBUG] Generated source file with user code\n");
if (compile_shared_object(src_file, so_file) != 0) {
fprintf(stderr, "Compilation failed\n");
goto cleanup;
}
printf("[DEBUG] Compilation successful\n");
/* Load the shared object */
handle = dlopen(so_file, RTLD_NOW);
if (handle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
goto cleanup;
}
printf("[DEBUG] Loaded shared object at %p\n", handle);
/* Get the function symbol */
func = (demo_func_t)dlsym(handle, "demo_function");
if ((error = dlerror()) != NULL) {
fprintf(stderr, "dlsym failed: %s\n", error);
dlclose(handle);
goto cleanup;
}
printf("[DEBUG] Retrieved function symbol at %p\n",
(void *)func);
/* Execute the function */
printf("\nExecuting user function:\n");
printf("--------------------\n");
/* Protect against infinite loops and some bad memory access */
if (setjmp(give_up) == 0) {
signal(SIGALRM, sig_alarm);
signal(SIGSEGV, sig_mem);
signal(SIGBUS, sig_mem);
alarm(3);
func();
alarm(0);
signal(SIGALRM, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGBUS, SIG_DFL);
}
printf("--------------------\n");
/* Cleanup */
dlclose(handle);
printf("[DEBUG] Unloaded shared object\n");
cleanup:
zap_files();
printf("[DEBUG] Removed temporary files\n");
}
return 0;
}