From 421f5ba652c0e10921ffb47932bfec7fc13c46ae Mon Sep 17 00:00:00 2001 From: brunosousa85 Date: Tue, 10 Oct 2023 10:36:21 -0300 Subject: [PATCH] Add android_sysvshm --- android_sysvshm/CMakeLists.txt | 11 ++ android_sysvshm/README.md | 6 + android_sysvshm/android_sysvshm.c | 255 ++++++++++++++++++++++++++++++ android_sysvshm/build.sh | 15 ++ android_sysvshm/cross-arm64.cmake | 3 + android_sysvshm/cross-armhf.cmake | 3 + android_sysvshm/sys/shm.h | 102 ++++++++++++ 7 files changed, 395 insertions(+) create mode 100644 android_sysvshm/CMakeLists.txt create mode 100644 android_sysvshm/README.md create mode 100644 android_sysvshm/android_sysvshm.c create mode 100644 android_sysvshm/build.sh create mode 100644 android_sysvshm/cross-arm64.cmake create mode 100644 android_sysvshm/cross-armhf.cmake create mode 100644 android_sysvshm/sys/shm.h diff --git a/android_sysvshm/CMakeLists.txt b/android_sysvshm/CMakeLists.txt new file mode 100644 index 0000000..c668711 --- /dev/null +++ b/android_sysvshm/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) + +project(AndroidSysVSHM C) +message("Building ${PROJECT_NAME}") + +set(CMAKE_VERBOSE_MAKEFILE on) +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=gnu99 -shared -fPIC") + +MESSAGE(STATUS "Compiler options: ${CMAKE_C_FLAGS}") + +add_library(android-sysvshm SHARED android_sysvshm.c) \ No newline at end of file diff --git a/android_sysvshm/README.md b/android_sysvshm/README.md new file mode 100644 index 0000000..7c7fe47 --- /dev/null +++ b/android_sysvshm/README.md @@ -0,0 +1,6 @@ +This is the Android SysVSHM used in Winlator based on https://github.com/pelya/android-shmem + +## Build dependencies + + $ dpkg --add-architecture armhf + $ apt install build-essential make cmake g++-arm-linux-gnueabihf \ No newline at end of file diff --git a/android_sysvshm/android_sysvshm.c b/android_sysvshm/android_sysvshm.c new file mode 100644 index 0000000..92cacd8 --- /dev/null +++ b/android_sysvshm/android_sysvshm.c @@ -0,0 +1,255 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys/shm.h" + +#define REQUEST_CODE_SHMGET 0 +#define REQUEST_CODE_GET_FD 1 +#define REQUEST_CODE_DELETE 2 + +#define MIN_REQUEST_LENGTH 5 +#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) + +/* 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; + +static shmemory_t* shmemories = NULL; +static int shmemory_count = 0; +static int sysvshm_server_fd = -1; +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + +static int find_shmemory_index(int shmid) { + for (int i = 0; i < shmemory_count; i++) if (shmemories[i].id == shmid) return i; + return -1; +} + +static void sysvshm_connect() { + 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; +} + +static void sysvshm_close() { + if (sysvshm_server_fd >= 0) { + close(sysvshm_server_fd); + sysvshm_server_fd = -1; + } +} + +static 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; + memcpy(request_data + 1, &size, 4); + + 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; +} + +static 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; + memcpy(request_data + 1, &shmid, 4); + + 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]; +} + +static void sysvshm_delete_request(int shmid) { + if (sysvshm_server_fd < 0) return; + + char request_data[MIN_REQUEST_LENGTH]; + request_data[0] = REQUEST_CODE_DELETE; + memcpy(request_data + 1, &shmid, 4); + + write(sysvshm_server_fd, request_data, sizeof(request_data)); +} + +static 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)); +} + +int shmget(key_t key, size_t size, int flags) { + if (key != IPC_PRIVATE) return -1; + + pthread_mutex_lock(&mutex); + + sysvshm_connect(); + int shmid = sysvshm_shmget_request(size); + if (shmid == 0) { + sysvshm_close(); + pthread_mutex_unlock(&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(&mutex); + return -1; + } + + pthread_mutex_unlock(&mutex); + return shmid; +} + +void* shmat(int shmid, const void* shmaddr, int shmflg) { + pthread_mutex_lock(&mutex); + + void* addr; + 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(&mutex); + return addr ? addr : (void *)-1; +} + +int shmdt(const void* shmaddr) { + pthread_mutex_lock(&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(&mutex); + return 0; +} + +int shmctl(int shmid, int cmd, struct shmid_ds* buf) { + if (cmd == IPC_RMID) { + pthread_mutex_lock(&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(&mutex); + return 0; + } + else if (cmd == IPC_STAT) { + pthread_mutex_lock(&mutex); + + int index = find_shmemory_index(shmid); + if (!buf || index == -1) { + pthread_mutex_unlock(&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(&mutex); + return 0; + } + return -1; +} \ No newline at end of file diff --git a/android_sysvshm/build.sh b/android_sysvshm/build.sh new file mode 100644 index 0000000..5ea093f --- /dev/null +++ b/android_sysvshm/build.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +rm -r build64 +mkdir build64 +cd build64 +cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-arm64.cmake +make -j8 + +cd .. + +rm -r build +mkdir build +cd build +cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-armhf.cmake +make -j8 \ No newline at end of file diff --git a/android_sysvshm/cross-arm64.cmake b/android_sysvshm/cross-arm64.cmake new file mode 100644 index 0000000..1cc4db2 --- /dev/null +++ b/android_sysvshm/cross-arm64.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CROSS_PATH "/usr/lib/aarch64-linux-gnu") +set(CMAKE_C_COMPILER "aarch64-linux-gnu-gcc") \ No newline at end of file diff --git a/android_sysvshm/cross-armhf.cmake b/android_sysvshm/cross-armhf.cmake new file mode 100644 index 0000000..201d43b --- /dev/null +++ b/android_sysvshm/cross-armhf.cmake @@ -0,0 +1,3 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CROSS_PATH "/usr/lib/arm-linux-gnueabihf") +set(CMAKE_C_COMPILER "arm-linux-gnueabihf-gcc") \ No newline at end of file diff --git a/android_sysvshm/sys/shm.h b/android_sysvshm/sys/shm.h new file mode 100644 index 0000000..da36fe3 --- /dev/null +++ b/android_sysvshm/sys/shm.h @@ -0,0 +1,102 @@ +#ifndef _SYS_SHM_H +#define _SYS_SHM_H 1 + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following System V style IPC functions implement a shared memory + facility. The definition is found in XPG4.2. */ + +/* Mode bits for `msgget', `semget', and `shmget'. */ +#define IPC_CREAT 01000 /* Create key if key does not exist. */ +#define IPC_EXCL 02000 /* Fail if key exists. */ +#define IPC_NOWAIT 04000 /* Return error on wait. */ + +/* Control commands for `msgctl', `semctl', and `shmctl'. */ +#define IPC_RMID 0 /* Remove identifier. */ +#define IPC_SET 1 /* Set `ipc_perm' options. */ +#define IPC_STAT 2 /* Get `ipc_perm' options. */ +#define IPC_INFO 3 /* See ipcs. */ + +/* Special key values. */ +#define IPC_PRIVATE ((key_t) 0) /* Private key. */ + +/* Permission flag for shmget. */ +#define SHM_R 0400 /* or S_IRUGO from */ +#define SHM_W 0200 /* or S_IWUGO from */ + +/* Flags for `shmat'. */ +#define SHM_RDONLY 010000 /* attach read-only else read-write */ +#define SHM_RND 020000 /* round attach address to SHMLBA */ +#define SHM_REMAP 040000 /* take-over region on attach */ +#define SHM_EXEC 0100000 /* execution access */ + +/* Commands for `shmctl'. */ +#define SHM_LOCK 11 /* lock segment (root only) */ +#define SHM_UNLOCK 12 /* unlock segment (root only) */ + +/* Type to count number of attaches. */ +typedef unsigned long int shmatt_t; +typedef long int __long_time_t; /* to keep compatibility to Debian Wheezy armhf */ +/* +typedef int32_t __key_t; +typedef __key_t key_t; +typedef uint32_t __uid_t; +typedef uint32_t __gid_t; +*/ + +/* Data structure used to pass permission information to IPC operations. */ +struct debian_ipc_perm /* We cannot use Android version, because there are no padding fields */ + { + key_t __key; /* Key. */ + __uid_t uid; /* Owner's user ID. */ + __gid_t gid; /* Owner's group ID. */ + __uid_t cuid; /* Creator's user ID. */ + __gid_t cgid; /* Creator's group ID. */ + unsigned short int mode; /* Read/write permission. */ + unsigned short int __pad1; + unsigned short int __seq; /* Sequence number. */ + unsigned short int __pad2; + unsigned long int __unused1; + unsigned long int __unused2; + }; + +/* Data structure describing a shared memory segment. */ +struct shmid_ds + { + struct debian_ipc_perm shm_perm; /* operation permission struct */ + size_t shm_segsz; /* size of segment in bytes */ + __long_time_t shm_atime; /* time of last shmat() */ + unsigned long int __unused1; + __long_time_t shm_dtime; /* time of last shmdt() */ + unsigned long int __unused2; + __long_time_t shm_ctime; /* time of last change by shmctl() */ + unsigned long int __unused3; + __pid_t shm_cpid; /* pid of creator */ + __pid_t shm_lpid; /* pid of last shmop */ + shmatt_t shm_nattch; /* number of current attaches */ + unsigned long int __unused4; + unsigned long int __unused5; + }; + +/* Shared memory control operation. */ +extern int shmctl (int __shmid, int __cmd, struct shmid_ds *__buf); + +/* Get shared memory segment. */ +extern int shmget (key_t __key, size_t __size, int __shmflg); + +/* Attach shared memory segment. */ +extern void *shmat (int __shmid, const void *__shmaddr, int __shmflg); + +/* Detach shared memory segment. */ +extern int shmdt (const void *__shmaddr); + +#ifdef __cplusplus +} +#endif + +#endif /* sys/shm.h */ \ No newline at end of file