Skip to content

Instantly share code, notes, and snippets.

@stribika
Last active August 29, 2015 14:16
Show Gist options
  • Save stribika/4317b6d44751dd02e494 to your computer and use it in GitHub Desktop.
Save stribika/4317b6d44751dd02e494 to your computer and use it in GitHub Desktop.
Copy a function between processes using RW and RX mappings.
#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