Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 65 additions & 41 deletions shmem.c
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
#define __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ 1
#include <android/log.h>
#if __ANDROID_API__ >= 26
#include <android/sharedmem.h>
#endif
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _GNU_SOURCE
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <linux/memfd.h>
#include <sys/un.h>
#include <unistd.h>
#include <paths.h>

#if __ANDROID_API__ < 26
#ifndef __NR_memfd_create
#error "__NR_memfd_create not defined"
#endif

@twaik twaik Jun 28, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__NR_memfd_create is not restricted by ANDROID_API guard. It is not needed here. Probably

#ifndef __NR_memfd_create
#error "Unknown architecture: __NR_memfd_create not defined"
#endif

should be enough.

Which means there should not be custom __NR_memfd_create definition here and ndk builtin is fine.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MFD_* defines are not needed as well.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


#define __u32 uint32_t
#include <linux/ashmem.h>
#endif

#include "shm.h"

Expand Down Expand Up @@ -111,46 +116,65 @@ static int ancil_recv_fd(int sock)
return ((int*) CMSG_DATA(cmsg))[0];
}

static int ashmem_get_size_region(int fd)
{
#if __ANDROID_API__ >= 26
return ASharedMemory_getSize(fd);
#else
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
#endif
}

/*
* ashmem_create_region - creates a new named ashmem region and returns the file
* descriptor, or <0 on error.
*
* `name' is the label to give the region (visible in /proc/pid/maps)
* `size' is the size of the region, in page-aligned bytes
* Probe backends at runtime from newest/preferred to oldest:
* 1. memfd_create (Linux 3.17+)
* 2. ASharedMemory (Android API 26+)
* 3. /dev/ashmem (legacy)
*/
static int ashmem_create_region(char const* name, size_t size)
static int shmem_create_region(char const* name, size_t size)
{
#if __ANDROID_API__ >= 26
return ASharedMemory_create(name, size);
#else
// From https://android.googlesource.com/platform/system/core/+/master/libcutils/ashmem-dev.c
int fd = open("/dev/ashmem", O_RDWR);
if (fd < 0) return fd;
int fd = -1;

// 1) ASharedMemory — Android 8+ (API 26+), official NDK API.
// Weak symbol: resolves to NULL at runtime on older devices.
if (ASharedMemory_create) {
fd = ASharedMemory_create(name, size);
if (fd >= 0) return fd;
}

char name_buffer[ASHMEM_NAME_LEN] = {0};
strncpy(name_buffer, name, sizeof(name_buffer));
name_buffer[sizeof(name_buffer)-1] = 0;
// 2) /dev/ashmem — legacy, exists since early Android.
fd = open("/dev/ashmem", O_RDWR);
if (fd >= 0) {
char name_buffer[ASHMEM_NAME_LEN] = {0};
strncpy(name_buffer, name, sizeof(name_buffer));
name_buffer[sizeof(name_buffer)-1] = 0;

if (ioctl(fd, ASHMEM_SET_NAME, name_buffer) == 0 &&
ioctl(fd, ASHMEM_SET_SIZE, size) == 0)
return fd;
close(fd);
}

int ret = ioctl(fd, ASHMEM_SET_NAME, name_buffer);
if (ret < 0) goto error;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still no ion/dma_heap fallback here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Added dma-buf heap and ion.

// 3) memfd_create — Linux 3.17+. Last resort: seccomp on Android 8–9
// may kill the process with SIGSYS instead of returning ENOSYS.
// Only reached when neither ASharedMemory nor /dev/ashmem exist
// (e.g. termux-docker / non-Android PRoot).
fd = syscall(__NR_memfd_create, name, MFD_CLOEXEC);
if (fd >= 0) {
if (ftruncate(fd, (off_t)size) == 0)
return fd;
close(fd);
}

ret = ioctl(fd, ASHMEM_SET_SIZE, size);
if (ret < 0) goto error;
return -1;
}

return fd;
error:
close(fd);
return ret;
#endif
static int shmem_get_size_region(int fd)
{
// Try fstat first — works for memfd and regular fds.
// ashmem fds also succeed fstat but st_size is always 0
// (ashmem doesn't maintain i_size, only accessible via ioctl).
struct stat st;
if (fstat(fd, &st) == 0 && st.st_size > 0)
return (int)st.st_size;

// Fall back to backend-specific size query
if (ASharedMemory_getSize) {
int ret = ASharedMemory_getSize(fd);
if (ret > 0) return ret;
}
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}

static void ashv_check_pid()
Expand Down Expand Up @@ -275,9 +299,9 @@ static int ashv_read_remote_segment(int shmid)
}
close(recvsock);

int size = ashmem_get_size_region(descriptor);
int size = shmem_get_size_region(descriptor);
if (size == 0 || size == -1) {
DBG ("%s: ERROR: ashmem_get_size_region() returned %d on socket %s: %s", __PRETTY_FUNCTION__, size, addr.sun_path + 1, strerror(errno));
DBG ("%s: ERROR: shmem_get_size_region() returned %d on socket %s: %s", __PRETTY_FUNCTION__, size, addr.sun_path + 1, strerror(errno));
return -1;
}

Expand Down Expand Up @@ -399,14 +423,14 @@ int shmget(key_t key, size_t size, int flags)
shmem = realloc(shmem, shmem_amount * sizeof(shmem_t));
size = ROUND_UP(size, getpagesize());
shmem[idx].size = size;
shmem[idx].descriptor = ashmem_create_region(buf, size);
shmem[idx].descriptor = shmem_create_region(buf, size);
shmem[idx].addr = NULL;
shmem[idx].id = shmid;
shmem[idx].markedForDeletion = false;
shmem[idx].key = key;

if (shmem[idx].descriptor < 0) {
DBG("%s: ashmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno));
DBG("%s: shmem_create_region() failed for size %zu: %s", __PRETTY_FUNCTION__, size, strerror(errno));
shmem_amount --;
shmem = realloc(shmem, shmem_amount * sizeof(shmem_t));
pthread_mutex_unlock (&mutex);
Expand Down
2 changes: 1 addition & 1 deletion test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CFLAGS += -Wall -Wextra -Werror -std=c99
OS := $(shell uname -o 2> /dev/null)

ifeq ($(OS), Android)
CFLAGS += -I.. -llog
CFLAGS += -I.. -llog -landroid
EXTRA_CFILE = ../shmem.c
cleanup-memory: test ;
else
Expand Down