ashmem-dev.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. /*
  2. * Copyright (C) 2008 The Android Open Source Project
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "ashmem.h"
  17. #include <dlfcn.h>
  18. #include <errno.h>
  19. #include <pthread.h>
  20. #include <unistd.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <sys/mman.h>
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include <sys/ioctl.h>
  27. #include <sys/stat.h> /* for fdstat() */
  28. #include <fcntl.h>
  29. #include <linux/ashmem.h>
  30. #include <sys/system_properties.h>
  31. #define ASHMEM_DEVICE "/dev/ashmem"
  32. /* Technical note regarding reading system properties.
  33. *
  34. * Try to use the new __system_property_read_callback API that appeared in
  35. * Android O / API level 26 when available. Otherwise use the deprecated
  36. * __system_property_get function.
  37. *
  38. * For more technical details from an NDK maintainer, see:
  39. * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
  40. */
  41. /* Weak symbol import */
  42. void __system_property_read_callback(
  43. const prop_info* info,
  44. void (*callback)(
  45. void* cookie, const char* name, const char* value, uint32_t serial),
  46. void* cookie) __attribute__((weak));
  47. /* Callback used with __system_property_read_callback. */
  48. static void prop_read_int(void* cookie,
  49. const char* name,
  50. const char* value,
  51. uint32_t serial) {
  52. *(int *)cookie = atoi(value);
  53. (void)name;
  54. (void)serial;
  55. }
  56. static int system_property_get_int(const char* name) {
  57. int result = 0;
  58. if (__system_property_read_callback) {
  59. const prop_info* info = __system_property_find(name);
  60. if (info)
  61. __system_property_read_callback(info, &prop_read_int, &result);
  62. } else {
  63. char value[PROP_VALUE_MAX] = {};
  64. if (__system_property_get(name, value) >= 1)
  65. result = atoi(value);
  66. }
  67. return result;
  68. }
  69. static int device_api_level() {
  70. static int s_api_level = -1;
  71. if (s_api_level < 0)
  72. s_api_level = system_property_get_int("ro.build.version.sdk");
  73. return s_api_level;
  74. }
  75. typedef enum {
  76. ASHMEM_STATUS_INIT,
  77. ASHMEM_STATUS_NOT_SUPPORTED,
  78. ASHMEM_STATUS_SUPPORTED,
  79. } AshmemStatus;
  80. static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT;
  81. static dev_t s_ashmem_dev;
  82. /* Return the dev_t of a given file path, or 0 if not available, */
  83. static dev_t ashmem_find_dev(const char* path) {
  84. struct stat st;
  85. dev_t result = 0;
  86. if (stat(path, &st) == 0 && S_ISCHR(st.st_mode))
  87. result = st.st_dev;
  88. return result;
  89. }
  90. static AshmemStatus ashmem_get_status(void) {
  91. /* NOTE: No need to make this thread-safe, assuming that
  92. * all threads will find the same value. */
  93. if (s_ashmem_status != ASHMEM_STATUS_INIT)
  94. return s_ashmem_status;
  95. s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE);
  96. s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED
  97. : ASHMEM_STATUS_SUPPORTED;
  98. return s_ashmem_status;
  99. }
  100. /* Returns true iff the ashmem device ioctl should be used for a given fd.
  101. * NOTE: Try not to use fstat() when possible to avoid performance issues. */
  102. static int ashmem_dev_fd_check(int fd) {
  103. if (device_api_level() <= __ANDROID_API_O_MR1__)
  104. return 1;
  105. if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) {
  106. struct stat st;
  107. return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
  108. st.st_dev != 0 && st.st_dev == s_ashmem_dev);
  109. }
  110. return 0;
  111. }
  112. /*
  113. * ashmem_create_region - creates a new ashmem region and returns the file
  114. * descriptor, or <0 on error
  115. *
  116. * `name' is an optional label to give the region (visible in /proc/pid/maps)
  117. * `size' is the size of the region, in page-aligned bytes
  118. */
  119. static int ashmem_dev_create_region(const char *name, size_t size) {
  120. int fd = open(ASHMEM_DEVICE, O_RDWR);
  121. if (fd < 0)
  122. return fd;
  123. int ret;
  124. if (name) {
  125. char buf[ASHMEM_NAME_LEN];
  126. strlcpy(buf, name, sizeof(buf));
  127. ret = ioctl(fd, ASHMEM_SET_NAME, buf);
  128. if (ret < 0)
  129. goto error;
  130. }
  131. ret = ioctl(fd, ASHMEM_SET_SIZE, size);
  132. if (ret < 0)
  133. goto error;
  134. return fd;
  135. error:
  136. close(fd);
  137. return ret;
  138. }
  139. static int ashmem_dev_set_prot_region(int fd, int prot) {
  140. return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
  141. }
  142. static int ashmem_dev_get_prot_region(int fd) {
  143. return ioctl(fd, ASHMEM_GET_PROT_MASK);
  144. }
  145. static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) {
  146. struct ashmem_pin pin = { offset, len };
  147. return ioctl(fd, ASHMEM_PIN, &pin);
  148. }
  149. static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) {
  150. struct ashmem_pin pin = { offset, len };
  151. return ioctl(fd, ASHMEM_UNPIN, &pin);
  152. }
  153. static size_t ashmem_dev_get_size_region(int fd) {
  154. return ioctl(fd, ASHMEM_GET_SIZE, NULL);
  155. }
  156. // Starting with API level 26, the following functions from
  157. // libandroid.so should be used to create shared memory regions.
  158. typedef int(*ASharedMemory_createFunc)(const char*, size_t);
  159. typedef size_t(*ASharedMemory_getSizeFunc)(int fd);
  160. typedef int(*ASharedMemory_setProtFunc)(int fd, int prot);
  161. // Function pointers to shared memory functions.
  162. typedef struct {
  163. ASharedMemory_createFunc create;
  164. ASharedMemory_getSizeFunc getSize;
  165. ASharedMemory_setProtFunc setProt;
  166. } ASharedMemoryFuncs;
  167. static ASharedMemoryFuncs s_ashmem_funcs = {};
  168. static pthread_once_t s_ashmem_funcs_once = PTHREAD_ONCE_INIT;
  169. static void ashmem_init_funcs() {
  170. ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
  171. if (device_api_level() >= __ANDROID_API_O__) {
  172. /* Leaked intentionally! */
  173. void* lib = dlopen("libandroid.so", RTLD_NOW);
  174. funcs->create =
  175. (ASharedMemory_createFunc)dlsym(lib, "ASharedMemory_create");
  176. funcs->getSize =
  177. (ASharedMemory_getSizeFunc)dlsym(lib, "ASharedMemory_getSize");
  178. funcs->setProt =
  179. (ASharedMemory_setProtFunc)dlsym(lib, "ASharedMemory_setProt");
  180. } else {
  181. funcs->create = &ashmem_dev_create_region;
  182. funcs->getSize = &ashmem_dev_get_size_region;
  183. funcs->setProt = &ashmem_dev_set_prot_region;
  184. }
  185. }
  186. static const ASharedMemoryFuncs* ashmem_get_funcs() {
  187. pthread_once(&s_ashmem_funcs_once, ashmem_init_funcs);
  188. return &s_ashmem_funcs;
  189. }
  190. int ashmem_create_region(const char* name, size_t size) {
  191. return ashmem_get_funcs()->create(name, size);
  192. }
  193. int ashmem_set_prot_region(int fd, int prot) {
  194. return ashmem_get_funcs()->setProt(fd, prot);
  195. }
  196. int ashmem_get_prot_region(int fd) {
  197. if (ashmem_dev_fd_check(fd))
  198. return ashmem_dev_get_prot_region(fd);
  199. /* There are only two practical values to return here: either
  200. * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine
  201. * the flags by trying to mmap() the region read-write first.
  202. */
  203. int result = PROT_READ;
  204. const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
  205. void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
  206. MAP_SHARED, fd, 0);
  207. if (m != MAP_FAILED) {
  208. munmap(m, page_size);
  209. result = PROT_READ|PROT_WRITE;
  210. }
  211. return result;
  212. }
  213. int ashmem_pin_region(int fd, size_t offset, size_t len) {
  214. if (ashmem_dev_fd_check(fd))
  215. return ashmem_dev_pin_region(fd, offset, len);
  216. return ASHMEM_NOT_PURGED;
  217. }
  218. int ashmem_unpin_region(int fd, size_t offset, size_t len) {
  219. if (ashmem_dev_fd_check(fd))
  220. return ashmem_dev_unpin_region(fd, offset, len);
  221. /* NOTE: It is not possible to use madvise() here because it requires a
  222. * memory address. This could be done in the caller though, instead of
  223. * this function. */
  224. return 0;
  225. }
  226. int ashmem_get_size_region(int fd) {
  227. /* NOTE: Original API returns an int. Avoid breaking it. */
  228. return (int)ashmem_get_funcs()->getSize(fd);
  229. }
  230. int ashmem_device_is_supported(void) {
  231. return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED;
  232. }