online compiler and debugger for c/c++

code. compile. run. debug. share.
Source Code   
Language
#include "kernel.h" /* * This program simulates user-space and kernel-space code, by running two * separate processes (and thus creating two separate address spaces). The * user process will make system calls to the kernel process, which will * perform the desired operations. * * Look at * - user.c to see the code that makes system calls to the kernel. * - kernel.c to see the code that handles the system calls. * * These are really the only two files you need to look at to understand the * system call mechanism. The rest of the files are just support code. * * Behind the scenes, the user and kernel processes communicate over a socket * pair. The user process sends a message to the kernel process, which reads * the message, performs the desired operation, and sends a response back to * the user process. There are also special requests that allow the kernel * to request reads and writes from the user process's address space. */ int main(void) { fprintf(stderr, "- kern: Kernel-space booting.\n"); kmain(); fprintf(stderr, "- kern: Kernel-space shutting down.\n"); return 0; }
#include "kernel.h" #define SIMULATED_PID 42 #define SYS_PRINT_LIMIT 4096 /* Maximum number of characters to print */ int sys_print(const_userptr_t str, int *len_printed) { char kernel_buf[SYS_PRINT_LIMIT]; int err; err = copyinstr(str, kernel_buf, sizeof(kernel_buf), NULL); if (err == 0) { fputs(kernel_buf, stdout); *len_printed = strlen(kernel_buf); return 0; } else { return err; } } int sys_get_pid(int *pid) { *pid = SIMULATED_PID; return 0; } int sys_exit(int code) { kprintf("User process exited with code %d\n", code); /* Terminate the user process */ kill(user_pid, SIGTERM); return 0; } void handle_syscall(struct syscall_args *args, struct syscall_result *result) { int err, retval; kprintf("Handling syscall %d\n", args->num); switch (args->num) { case SYS_PRINT: err = sys_print((const_userptr_t) args->args[0], (int *) &retval); break; case SYS_GET_PID: err = sys_get_pid((int *) &retval); break; case SYS_EXIT: err = sys_exit(args->args[0]); user_connected = false; return; default: kprintf("Unknown syscall number: %d\n", args->num); err = ENOSYS; retval = -1; } retval = err ? -1 : retval; result->err = err; result->ret_val = retval; kprintf("Syscall %d returning %d, err=%d\n", args->num, retval, err); }
#include "user.h" /* * Other than debugging with uprintf, the user program should never directly * make system calls on the host system. Instead, it should make system calls * to the kernel process, which will perform the desired operations. */ #define BIG_STRING_SIZE 1500 int main(void) { long ret; int exit_code = 0; char big_string[BIG_STRING_SIZE]; for (int i = 0; i < BIG_STRING_SIZE; i++) { big_string[i] = 'A' + (i % 26); } big_string[BIG_STRING_SIZE-1] = '\0'; big_string[BIG_STRING_SIZE-2] = '\n'; uprintf("User process started up.\n"); ret = system_call(SYS_PRINT, (intptr_t) "Hello, Kernel!\n", 0, 0, 0); uprintf("SYS_PRINT returned: %ld\n", ret); ret = system_call(SYS_PRINT, (intptr_t) big_string, 0, 0, 0); uprintf("SYS_PRINT returned: %ld (%s)\n", ret, strerror(errno)); ret = system_call(SYS_PRINT, 0, 0, 0, 0); uprintf("Errorneous SYS_PRINT returned: %ld (%s)\n", ret, strerror(errno)); ret = system_call(SYS_GET_PID, 0, 0, 0, 0); uprintf("SYS_GET_PID returned: %ld\n", ret); uprintf("Calling SYS_EXIT\n"); system_call(SYS_EXIT, exit_code, 0, 0, 0); uprintf("This should not be printed\n"); return 0; /* This should never be reached */ }
#ifndef COMMON_H #define COMMON_H #include <sys/types.h> #include <stdint.h> #define MAX_DATA_SIZE 1024 enum syscall_num { SYS_PRINT = 1, SYS_GET_PID, SYS_EXIT }; enum kernel_request { KREQ_READ_MEM = 1, KREQ_WRITE_MEM, KREQ_READ_STRING, KREQ_SYSCALL_RESULT }; struct syscall_args { enum syscall_num num; intptr_t args[4]; }; struct syscall_result { int err; long ret_val; }; struct memory_area { void *start; size_t size; }; struct kernel_message { enum kernel_request req; union { struct syscall_result result; struct memory_area mem_area; }; // void *addr; // size_t size; // long data; }; struct user_message { int err; char data[MAX_DATA_SIZE]; }; #endif /* COMMON_H */
#ifndef KERNEL_H #define KERNEL_H #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <sys/wait.h> #include <errno.h> #include <signal.h> #include <assert.h> #include "common.h" /* * Define userptr_t as a pointer to a one-byte struct, so it won't mix * with other pointers. [Copied from OS/161.] */ struct __userptr { char _dummy; }; typedef struct __userptr *userptr_t; typedef const struct __userptr *const_userptr_t; int copyin(const_userptr_t usersrc, void *dest, size_t len); int copyout(const void *src, userptr_t userdest, size_t len); int copyinstr(const_userptr_t usersrc, char *dest, size_t len, size_t *got); int copyoutstr(const char *src, userptr_t userdest, size_t len, size_t *got); void handle_syscall(struct syscall_args *args, struct syscall_result *result); #define kprintf(...) fprintf(stderr, "- kern: " __VA_ARGS__) #define kmalloc malloc #define kfree free void syscall_responder(void); void kmain(void); extern bool user_connected; /* Is the user process connected? */ extern int user_pid; /* Actual system PID of the user process */ #endif /* KERNEL_H */
#ifndef USER_H #define USER_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdbool.h> #include <string.h> #include <assert.h> #include <errno.h> #include <signal.h> #include <setjmp.h> #include "common.h" long system_call(enum syscall_num num, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4); #define uprintf(...) fprintf(stderr, "- user: " __VA_ARGS__) #define main user_main #endif /* USER_H */
#include "kernel.h" #ifndef KERNEL_LOG_INTERNALS /* Unless KERNEL_LOG_INTERNALS is defined, we don't want to see the * internal logging messages. */ #undef kprintf #define kprintf(...) #endif static int user_sock; /* Global socket for communication with user process */ int user_pid; /* actual PID of the user process */ bool user_connected = false; static int copyin_prim(const_userptr_t usersrc, void *dest, size_t len) { struct kernel_message kmsg; struct user_message umsg; kmsg.req = KREQ_READ_MEM; kmsg.mem_area.start = (void *) usersrc; kmsg.mem_area.size = len; if (send(user_sock, &kmsg, sizeof(kmsg), 0) == -1) { perror("send"); return ENOTCONN; } if (recv(user_sock, &umsg, sizeof(umsg), 0) == -1) { perror("recv"); return ENOTCONN; } if (umsg.err) { kprintf("Failed to read memory from user space\n"); return umsg.err; } memcpy(dest, umsg.data, len); return 0; } /* While copyin_prim can only handle data up to MAX_DATA_SIZE, copyin can handle * arbitrary data sizes by breaking it up into chunks. */ int copyin(const_userptr_t usersrc, void *dest, size_t len) { int err; size_t chunk_size; size_t remaining = len; char *dest_ptr = dest; while (remaining > 0) { chunk_size = remaining > MAX_DATA_SIZE ? MAX_DATA_SIZE : remaining; err = copyin_prim(usersrc, dest_ptr, chunk_size); if (err) { return err; } usersrc += chunk_size; dest_ptr += chunk_size; remaining -= chunk_size; } return 0; } static int copyout_prim(const void *src, userptr_t userdest, size_t len) { struct kernel_message kmsg; struct user_message umsg; kmsg.req = KREQ_WRITE_MEM; kmsg.mem_area.start = userdest; kmsg.mem_area.size = len; if (send(user_sock, &kmsg, sizeof(kmsg), 0) == -1) { perror("send"); return ENOTCONN; } if (send(user_sock, src, len, 0) == -1) { perror("send"); return ENOTCONN; } if (recv(user_sock, &umsg, sizeof(umsg), 0) == -1) { perror("recv"); return ENOTCONN; } if (umsg.err) { kprintf("Failed to write memory to user space\n"); return umsg.err; } return 0; } int copyout(const void *src, userptr_t userdest, size_t len) { int err; size_t chunk_size; size_t remaining = len; const char *src_ptr = src; while (remaining > 0) { chunk_size = remaining > MAX_DATA_SIZE ? MAX_DATA_SIZE : remaining; err = copyout_prim(src_ptr, userdest, chunk_size); if (err) { return err; } userdest += chunk_size; src_ptr += chunk_size; remaining -= chunk_size; } return 0; } static int copyinstr_prim(const_userptr_t usersrc, char *dest, size_t max_len, size_t *got) { struct kernel_message kmsg; struct user_message umsg; char *endptr; kmsg.req = KREQ_READ_STRING; kmsg.mem_area.start = (void *) usersrc; kmsg.mem_area.size = max_len; if (send(user_sock, &kmsg, sizeof(kmsg), 0) == -1) { perror("send"); return ENOTCONN; } if (recv(user_sock, &umsg, sizeof(umsg), 0) == -1) { perror("recv"); return ENOTCONN; } if (umsg.err == ENAMETOOLONG) { kprintf( "String from user space is too long, partially copied\n"); memcpy(dest, umsg.data, max_len); if (got != NULL) { *got = max_len; } return umsg.err; } else if (umsg.err) { kprintf("Failed to read string from user space\n"); return umsg.err; } endptr = stpncpy(dest, umsg.data, max_len); if (endptr - dest == max_len) { kprintf("Protocol error: string from user space not " "null-terminated\n"); return EPROTO; } if (got != NULL) { *got = endptr - dest + 1; /* includes the null terminator */ } return 0; } /* While copyinstr_prim can only handle data up to MAX_DATA_SIZE, copyinstr can * handle arbitrary data sizes by breaking it up into chunks. Here, we know if * we need to copy more if copyinstr_prim returns ENAMETOOLONG. */ int copyinstr(const_userptr_t usersrc, char *dest, size_t max_len, size_t *got) { int err; size_t chunk_size; size_t remaining = max_len; char *dest_ptr = dest; size_t total_copied = 0; while (remaining > 0) { chunk_size = remaining > MAX_DATA_SIZE ? MAX_DATA_SIZE : remaining; err = copyinstr_prim(usersrc, dest_ptr, chunk_size, &chunk_size); total_copied += chunk_size; if (err != ENAMETOOLONG) { break; } assert(chunk_size == MAX_DATA_SIZE); usersrc += chunk_size; dest_ptr += chunk_size; remaining -= chunk_size; } if (got != NULL) { *got = total_copied; } return err; } int copyoutstr(const char *src, userptr_t userdest, size_t len, size_t *got) { int err; size_t actual_len = strlen(src) + 1; if (actual_len > len) { return ENAMETOOLONG; } err = copyout(src, userdest, actual_len); if (got != NULL && err == 0) { *got = actual_len; } return err; } void syscall_responder(void) { struct syscall_args args; struct syscall_result result; struct kernel_message kmsg; ssize_t n; while (user_connected) { n = recv(user_sock, &args, sizeof(args), 0); if (n <= 0) { if (n == 0) { printf("User process has terminated.\n"); } else { perror("recv"); } break; } kprintf("Received syscall request: %d\n", args.num); handle_syscall(&args, &result); if (result.err == ENOTCONN) { user_connected = false; } if (!user_connected) { break; } kmsg.req = KREQ_SYSCALL_RESULT; kmsg.result = result; kprintf("Sending syscall result\n"); if (send(user_sock, &kmsg, sizeof(kmsg), 0) == -1) { perror("send"); break; } } } void user_main(void); void kmain(void) { int socks[2]; pid_t pid; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1) { perror("socketpair"); exit(1); } pid = fork(); if (pid == -1) { perror("fork"); exit(1); } if (pid == 0) { /* Child process */ close(socks[0]); dup2(socks[1], STDIN_FILENO); dup2(socks[1], STDOUT_FILENO); close(socks[1]); // execl("./user", "user", NULL); // perror("execl"); user_main(); exit(1); } /* Parent process */ close(socks[1]); user_sock = socks[0]; user_pid = pid; user_connected = true; syscall_responder(); close(user_sock); wait(NULL); }
#include "user.h" #ifndef USER_LOG_INTERNALS /* Unless USER_LOG_INTERNALS is defined, we don't want to see the * internal logging messages. */ #undef uprintf #define uprintf(...) #endif /* Fault handling, if memory access fails, we need to return EFAULT to the * kernel process */ jmp_buf bail_out; static void sigsegv_handler(int signo) { longjmp(bail_out, 1); } const char *request_names[] = {[KREQ_READ_MEM] = "KREQ_READ_MEM", [KREQ_WRITE_MEM] = "KREQ_WRITE_MEM", [KREQ_READ_STRING] = "KREQ_READ_STRING", [KREQ_SYSCALL_RESULT] = "KREQ_SYSCALL_RESULT"}; const char * request_name(enum kernel_request req) { if (req < KREQ_READ_MEM || req > KREQ_SYSCALL_RESULT) { return "Unknown request"; } return request_names[req]; } long system_call(enum syscall_num num, intptr_t arg1, intptr_t arg2, intptr_t arg3, intptr_t arg4) { struct syscall_args args; struct kernel_message kmsg; struct user_message umsg; ssize_t n; char *endptr; bool done = false; args.num = num; args.args[0] = arg1; args.args[1] = arg2; args.args[2] = arg3; args.args[3] = arg4; uprintf("Sending syscall %d\n", num); write(STDOUT_FILENO, &args, sizeof(args)); /* Install signal handler for SIGSEGV and SIGBUS */ signal(SIGSEGV, sigsegv_handler); signal(SIGBUS, sigsegv_handler); while (!done) { if (setjmp(bail_out) != 0) { uprintf("Memory access failed!\n"); umsg.err = EFAULT; write(STDOUT_FILENO, &umsg, sizeof(umsg)); continue; } uprintf("Waiting for kernel message...\n"); n = read(STDIN_FILENO, &kmsg, sizeof(kmsg)); if (n <= 0) { perror("read"); exit(1); } uprintf("Got kernel message, size: %zd, %s\n", n, request_name(kmsg.req)); assert(kmsg.req == KREQ_SYSCALL_RESULT || kmsg.mem_area.size <= MAX_DATA_SIZE); switch (kmsg.req) { case KREQ_READ_MEM: uprintf("Copying memory to kernel space\n"); memcpy(umsg.data, kmsg.mem_area.start, kmsg.mem_area.size); umsg.err = 0; write(STDOUT_FILENO, &umsg, sizeof(umsg)); break; case KREQ_WRITE_MEM: uprintf("Copying memory from kernel space\n"); // Receive another message with the data payload n = read(STDIN_FILENO, kmsg.mem_area.start, kmsg.mem_area.size); if (n <= 0) { perror("read"); exit(1); } umsg.err = 0; write(STDOUT_FILENO, &umsg, sizeof(umsg)); break; case KREQ_READ_STRING: uprintf("Copying string to kernel space\n"); // strncpy(umsg.data, kmsg.addr, kmsg.size); endptr = stpncpy(umsg.data, kmsg.mem_area.start, kmsg.mem_area.size); umsg.err = (endptr - umsg.data < kmsg.mem_area.size) ? 0 : ENAMETOOLONG; write(STDOUT_FILENO, &umsg, sizeof(umsg)); break; case KREQ_SYSCALL_RESULT: uprintf("Got syscall result\n"); done = true; break; default: uprintf("Unknown kernel request: %d\n", kmsg.req); umsg.err = 0; write(STDOUT_FILENO, &umsg, sizeof(umsg)); } } /* Restore default signal handler */ signal(SIGSEGV, SIG_DFL); signal(SIGBUS, SIG_DFL); if (kmsg.result.err == 0) { errno = 0; return kmsg.result.ret_val; } else { errno = kmsg.result.err; return -1; } }

Compiling Program...

Command line arguments:
Standard Input: Interactive Console Text
×

                

                

Program is not being debugged. Click "Debug" button to start program in debug mode.

#FunctionFile:Line
VariableValue
RegisterValue
ExpressionValue