Last active
August 29, 2015 14:16
-
-
Save stribika/4317b6d44751dd02e494 to your computer and use it in GitHub Desktop.
Copy a function between processes using RW and RX mappings.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <cstdio> | |
#include <cstdlib> | |
#include <cstring> | |
#include <fcntl.h> | |
#include <sys/mman.h> | |
#include <sys/stat.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <unistd.h> | |
size_t const len = 65536; | |
size_t const from_child = 0; | |
size_t const to_parent = 1; | |
size_t const from_parent = 2; | |
size_t const to_child = 3; | |
int child_main(int fd, int const* chan); | |
int parent_main(int fd, int const* chan, pid_t child); | |
void error(char const* msg); | |
int foo(int a, int b); | |
int main(int argc, char* argv[]) { | |
// check arguments | |
if (argc != 2) { | |
fprintf(stderr, "argc != 2"); | |
return EXIT_FAILURE; | |
} | |
// create file for mmap | |
auto fd = open(argv[1], O_RDWR | O_CREAT, S_IRWXU); | |
if (fd < 0) { error("open"); } | |
if (unlink(argv[1]) < 0) { error("unlink"); } | |
if (ftruncate(fd, len) < 0) { error("ftruncate"); } | |
// set up communication channels | |
int chan[4]; | |
if (pipe(chan) < 0) { error("pipe"); } | |
if (pipe(chan + 2) < 0) { error("pipe"); } | |
// fork: parent - exec; child - codegen | |
auto child = fork(); | |
if (child < 0) { | |
error("fork"); | |
} else if (child == 0) { | |
return child_main(fd, chan); | |
} else { | |
return parent_main(fd, chan, child); | |
} | |
} | |
int child_main(int fd, int const* chan) { | |
// create writeable mapping | |
auto mem = mmap(nullptr, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |
if (mem == MAP_FAILED) { error("child mmap"); } | |
// write the function code into mmapped area | |
memcpy(mem, reinterpret_cast<void*>(foo), 100); | |
// tell the parent the code is ready | |
if (write(chan[to_parent], "x", 1) != 1) { error("child write"); } | |
// wait for the parent to run the code | |
char s; | |
if (read(chan[from_parent], &s, 1) != 1) { error("child read"); } | |
// clean up | |
if (munmap(mem, len) < 0) { error("child munmap"); } | |
if (close(fd) < 0) { error("child close"); } | |
return EXIT_SUCCESS; | |
} | |
int parent_main(int fd, int const* chan, pid_t child) { | |
// create executable mapping | |
auto mem = mmap(nullptr, len, PROT_READ | PROT_EXEC, MAP_SHARED, fd, 0); | |
if (mem == MAP_FAILED) { error("parent mmap"); } | |
// wait for the child to write its thing | |
char s; | |
if (read(chan[from_child], &s, 1) != 1) { error("parent read"); } | |
// execute the code from the child | |
auto f = reinterpret_cast<int (*)(int, int)>(mem); | |
printf("%d\n", f(5, 7)); | |
// tell the child we're done | |
if (write(chan[to_child], "x", 1) != 1) { error("parent write"); } | |
// clean up | |
if (munmap(mem, len) < 0) { error("parent munmap"); } | |
if (close(fd) < 0) { error("parent close"); } | |
int status; | |
if (waitpid(child, &status, 0) != child) { error("waitpid"); } | |
if (!WIFEXITED(status)) { | |
fprintf(stderr, "child crashed"); | |
return EXIT_FAILURE; | |
} | |
if (WEXITSTATUS(status) != EXIT_SUCCESS) { | |
fprintf(stderr, "child failed"); | |
return EXIT_FAILURE; | |
} | |
return EXIT_SUCCESS; | |
} | |
int foo(int a, int b) { return a + b; } | |
void error(char const* msg) { | |
perror(msg); | |
exit(EXIT_FAILURE); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment