diff --git a/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.c b/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.c new file mode 100644 index 0000000..7508ac3 --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.c @@ -0,0 +1,127 @@ +#include "android_sysvshm.h" + +#define REQUEST_CODE_SHMGET 0 +#define REQUEST_CODE_GET_FD 1 +#define REQUEST_CODE_DELETE 2 + +/* based on https://github.com/pelya/android-shmem */ + +#define MIN_REQUEST_LENGTH 5 + +shmemory_t* shmemories = NULL; +int shmemory_count = 0; +int sysvshm_server_fd = -1; +pthread_mutex_t sysvshm_mutex = PTHREAD_MUTEX_INITIALIZER; + +int find_shmemory_index(int shmid) { + for (int i = 0; i < shmemory_count; i++) if (shmemories[i].id == shmid) return i; + return -1; +} + +void sysvshm_connect(void) { + if (sysvshm_server_fd >= 0) return; + char* path = getenv("ANDROID_SYSVSHM_SERVER"); + + int fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) return; + + struct sockaddr_un server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sun_family = AF_LOCAL; + + strncpy(server_addr.sun_path, path, sizeof(server_addr.sun_path) - 1); + + int res; + do { + res = 0; + if (connect(fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_un)) < 0) res = -errno; + } + while (res == -EINTR); + + if (res < 0) { + close(fd); + return; + } + + sysvshm_server_fd = fd; +} + +void sysvshm_close(void) { + if (sysvshm_server_fd >= 0) { + close(sysvshm_server_fd); + sysvshm_server_fd = -1; + } +} + +int sysvshm_shmget_request(size_t size) { + if (sysvshm_server_fd < 0) return 0; + + char request_data[MIN_REQUEST_LENGTH]; + request_data[0] = REQUEST_CODE_SHMGET; + *(int*)(request_data + 1) = size; + + int res = write(sysvshm_server_fd, request_data, sizeof(request_data)); + if (res < 0) return 0; + + int shmid; + res = read(sysvshm_server_fd, &shmid, 4); + return res == 4 ? shmid : 0; +} + +int sysvshm_get_fd_request(int shmid) { + if (sysvshm_server_fd < 0) return 0; + + char request_data[MIN_REQUEST_LENGTH]; + request_data[0] = REQUEST_CODE_GET_FD; + *(int*)(request_data + 1) = shmid; + + int res = write(sysvshm_server_fd, request_data, sizeof(request_data)); + if (res < 0) return -1; + + char zero = 0; + struct iovec iovmsg = {.iov_base = &zero, .iov_len = 1}; + struct { + struct cmsghdr align; + int fds[1]; + } ctrlmsg; + + struct msghdr msg = { + .msg_name = NULL, + .msg_namelen = 0, + .msg_iov = &iovmsg, + .msg_iovlen = 1, + .msg_flags = 0, + .msg_control = &ctrlmsg, + .msg_controllen = sizeof(struct cmsghdr) + sizeof(int) + }; + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = msg.msg_controllen; + ((int*)CMSG_DATA(cmsg))[0] = -1; + + recvmsg(sysvshm_server_fd, &msg, 0); + return ((int*)CMSG_DATA(cmsg))[0]; +} + +bool sysvshm_delete_request(int shmid) { + if (sysvshm_server_fd < 0) return false; + + char request_data[MIN_REQUEST_LENGTH]; + request_data[0] = REQUEST_CODE_DELETE; + *(int*)(request_data + 1) = shmid; + + int res = write(sysvshm_server_fd, request_data, sizeof(request_data)); + return res > 0 ? true : false; +} + +void sysvshm_delete(int index) { + sysvshm_connect(); + sysvshm_delete_request(shmemories[index].id); + sysvshm_close(); + + if (shmemories[index].fd >= 0) close(shmemories[index].fd); + shmemory_count--; + memmove(&shmemories[index], &shmemories[index+1], (shmemory_count - index) * sizeof(shmemory_t)); +} \ No newline at end of file diff --git a/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.h b/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.h new file mode 100644 index 0000000..f8da9c1 --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/android_sysvshm.h @@ -0,0 +1,38 @@ +#ifndef __ANDROID_SYSVSHM +#define __ANDROID_SYSVSHM + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* based on https://github.com/pelya/android-shmem */ + +typedef struct { + int id; + void* addr; + int fd; + size_t size; + char marked_for_delete; +} shmemory_t; + +extern shmemory_t* shmemories; +extern int shmemory_count; +extern int sysvshm_server_fd; +extern pthread_mutex_t sysvshm_mutex; + +extern int find_shmemory_index(int shmid); +extern void sysvshm_connect(void); +extern void sysvshm_close(void); +extern int sysvshm_shmget_request(size_t size); +extern int sysvshm_get_fd_request(int shmid); +extern bool sysvshm_delete_request(int shmid); +extern void sysvshm_delete(int index); + +#endif \ No newline at end of file diff --git a/glibc_patches/sysdeps/unix/sysv/linux/shmat.c b/glibc_patches/sysdeps/unix/sysv/linux/shmat.c new file mode 100644 index 0000000..b651ec3 --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/shmat.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +/* Attach the shared memory segment associated with SHMID to the data + segment of the calling process. SHMADDR and SHMFLG determine how + and where the segment is attached. */ + +void* shmat(int shmid, void const* shmaddr, int shmflg) +{ + pthread_mutex_lock(&sysvshm_mutex); + + void* addr = NULL; + int index = find_shmemory_index(shmid); + if (index != -1) { + if (shmemories[index].addr == NULL) { + shmemories[index].addr = mmap(NULL, shmemories[index].size, PROT_READ | (shmflg == 0 ? PROT_WRITE : 0), MAP_SHARED, shmemories[index].fd, 0); + if (shmemories[index].addr == MAP_FAILED) shmemories[index].addr = NULL; + } + addr = shmemories[index].addr; + } + + pthread_mutex_unlock(&sysvshm_mutex); + return addr ? addr : (void *)-1; +} diff --git a/glibc_patches/sysdeps/unix/sysv/linux/shmctl.c b/glibc_patches/sysdeps/unix/sysv/linux/shmctl.c new file mode 100644 index 0000000..920c604 --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/shmctl.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef shmid_ds +# define shmid_ds shmid64_ds +#endif + +int shmctl(int shmid, int cmd, struct shmid_ds *buf) { + if (cmd == IPC_RMID) { + pthread_mutex_lock(&sysvshm_mutex); + + int index = find_shmemory_index(shmid); + if (index != -1) { + if (shmemories[index].addr) { + shmemories[index].marked_for_delete = 1; + } + else sysvshm_delete(index); + } + + pthread_mutex_unlock(&sysvshm_mutex); + return 0; + } + else if (cmd == IPC_STAT) { + pthread_mutex_lock(&sysvshm_mutex); + + int index = find_shmemory_index(shmid); + if (!buf || index == -1) { + pthread_mutex_unlock(&sysvshm_mutex); + return -1; + } + + memset(buf, 0, sizeof(struct shmid_ds)); + buf->shm_segsz = shmemories[index].size; + buf->shm_nattch = 1; + buf->shm_perm.key = IPC_PRIVATE; + buf->shm_perm.uid = geteuid(); + buf->shm_perm.gid = getegid(); + buf->shm_perm.cuid = geteuid(); + buf->shm_perm.cgid = getegid(); + buf->shm_perm.mode = 0666; + buf->shm_perm.seq = 1; + + pthread_mutex_unlock(&sysvshm_mutex); + return 0; + } + return -1; +} + +weak_alias (shmctl, __shmctl64) diff --git a/glibc_patches/sysdeps/unix/sysv/linux/shmdt.c b/glibc_patches/sysdeps/unix/sysv/linux/shmdt.c new file mode 100644 index 0000000..f012fbe --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/shmdt.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +/* Detach shared memory segment starting at address specified by SHMADDR + from the caller's data segment. */ + +int shmdt(void const* shmaddr) { + pthread_mutex_lock(&sysvshm_mutex); + + for (int i = 0; i < shmemory_count; i++) { + if (shmemories[i].addr == shmaddr) { + munmap(shmemories[i].addr, shmemories[i].size); + shmemories[i].addr = NULL; + if (shmemories[i].marked_for_delete) sysvshm_delete(i); + break; + } + } + + pthread_mutex_unlock(&sysvshm_mutex); + return 0; +} diff --git a/glibc_patches/sysdeps/unix/sysv/linux/shmget.c b/glibc_patches/sysdeps/unix/sysv/linux/shmget.c new file mode 100644 index 0000000..e719b4f --- /dev/null +++ b/glibc_patches/sysdeps/unix/sysv/linux/shmget.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) + +/* Return an identifier for an shared memory segment of at least size SIZE + which is associated with KEY. */ + +int shmget(key_t key, size_t size, int flags) { + if (key != IPC_PRIVATE) return -1; + + pthread_mutex_lock(&sysvshm_mutex); + + sysvshm_connect(); + int shmid = sysvshm_shmget_request(size); + if (shmid == 0) { + sysvshm_close(); + pthread_mutex_unlock(&sysvshm_mutex); + return -1; + } + + size = ROUND_UP(size, getpagesize()); + int index = shmemory_count; + shmemory_count++; + shmemories = realloc(shmemories, shmemory_count * sizeof(shmemory_t)); + shmemories[index].size = size; + shmemories[index].fd = sysvshm_get_fd_request(shmid); + shmemories[index].addr = NULL; + shmemories[index].id = shmid; + shmemories[index].marked_for_delete = 0; + + sysvshm_close(); + + if (shmemories[index].fd < 0) { + shmemory_count--; + shmemories = realloc(shmemories, shmemory_count * sizeof(shmemory_t)); + pthread_mutex_unlock(&sysvshm_mutex); + return -1; + } + + pthread_mutex_unlock(&sysvshm_mutex); + return shmid; +}