dlmalloc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. /*
  2. * Copyright t lefering
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. * These are the four essential freedoms with GNU GPL software:
  18. * 1: freedom to run the program, for any purpose
  19. * 2: freedom to study how the program works, and change it to make it do what you wish
  20. * 3: freedom to redistribute copies to help your Free Software friends
  21. * 4: freedom to distribute copies of your modified versions to your Free Software friends
  22. * , ,
  23. * / \
  24. * ((__-^^-,-^^-__))
  25. * `-_---' `---_-'
  26. * `--|o` 'o|--'
  27. * \ ` /
  28. * ): :(
  29. * :o_o:
  30. * "-"
  31. */
  32. // This file is a wrapper around malloc.c, which is the upstream source file.
  33. // It sets configuration flags and controls which symbols are exported.
  34. #include <stdio.h>
  35. #include <stddef.h>
  36. #include <malloc.h>
  37. #include <errno.h>
  38. #include <string.h>
  39. // Define configuration macros for dlmalloc.
  40. #define LACKS_SYS_TYPES_H 1
  41. // WebAssembly doesn't have mmap-style memory allocation.
  42. #define HAVE_MMAP 0
  43. // WebAssembly doesn't support shrinking linear memory.
  44. #define MORECORE_CANNOT_TRIM 1
  45. // Disable sanity checks to reduce code size.
  46. #define ABORT __builtin_unreachable()
  47. // If threads are enabled, enable support for threads.
  48. #ifdef _REENTRANT
  49. #define USE_LOCKS 1
  50. #endif
  51. // Make malloc deterministic.
  52. #define LACKS_TIME_H 1
  53. // Disable malloc statistics generation to reduce code size.
  54. #define NO_MALLINFO 1
  55. #define NO_MALLOC_STATS 1
  56. // Align malloc regions to 16, to avoid unaligned SIMD accesses.
  57. #define MALLOC_ALIGNMENT 16
  58. // Declare errno values used by dlmalloc. We define them like this to avoid
  59. // putting specific errno values in the ABI.
  60. //extern const int __ENOMEM;
  61. //#define ENOMEM __ENOMEM
  62. //extern const int __EINVAL;
  63. //#define EINVAL __EINVAL
  64. // Define USE_DL_PREFIX so that we leave dlmalloc's names prefixed with 'dl'.
  65. // We define them as "static", and we wrap them with public names below. This
  66. // serves two purposes:
  67. //
  68. // One is to make it easy to control which symbols are exported; dlmalloc
  69. // defines several non-standard functions and we wish to explicitly control
  70. // which functions are part of our public-facing interface.
  71. //
  72. // The other is to protect against compilers optimizing based on the assumption
  73. // that they know what functions with names like "malloc" do. Code in the
  74. // implementation will call functions like "dlmalloc" and assume it can use
  75. // the resulting pointers to access the metadata outside of the nominally
  76. // allocated objects. However, if the function were named "malloc", compilers
  77. // might see code like that and assume it has undefined behavior and can be
  78. // optimized away. By using "dlmalloc" in the implementation, we don't need
  79. // -fno-builtin to avoid this problem.
  80. #define USE_DL_PREFIX 1
  81. #define DLMALLOC_EXPORT static inline
  82. // This isn't declared with DLMALLOC_EXPORT so make it static explicitly.
  83. static size_t dlmalloc_usable_size(void *);
  84. void abort(void)
  85. {
  86. // wasm doesn't support signals, so just trap to halt the program.
  87. __builtin_trap();
  88. }
  89. int errno = 0;
  90. /*
  91. * The page size in WebAssembly is fixed at 64 KiB. If this ever changes,
  92. * it's expected that applications will need to opt in, so we can change
  93. * this.
  94. */
  95. #define PAGESIZE (0x10000)
  96. #define sysconf(name) PAGESIZE
  97. #define _SC_PAGESIZE
  98. #define BLKSIZ PAGESIZE
  99. static void *allocateMemory(unsigned bytes_required);
  100. static void freeMemory(void *startOfMemoryToFree);
  101. typedef long align;
  102. // header of an allocation block
  103. typedef union header {
  104. struct {
  105. union header *next; // Pointer to circular successor
  106. unsigned int size; // Size of the block
  107. } value;
  108. long align; // Forces block alignment
  109. } header_t;
  110. static header_t *free_list = (header_t *) NULL;
  111. static unsigned int initial_offset = 8; // preserve address 0 for null and pad by 4 bytes.
  112. static int is_initialised = 0;
  113. static header_t *getMoreMemory(unsigned bytes_required)
  114. {
  115. // We need to add the header to the bytes required.
  116. bytes_required += sizeof(header_t);
  117. // The memory gets delivered in blocks. Ensure we get enough.
  118. unsigned int blocks = bytes_required / BLKSIZ;
  119. if (blocks * BLKSIZ < bytes_required)
  120. blocks += 1;
  121. unsigned int start_of_new_memory = __builtin_wasm_memory_size(0) * BLKSIZ;
  122. if (__builtin_wasm_memory_grow(0, blocks) == -1)
  123. return NULL;
  124. long end_of_new_memory = __builtin_wasm_memory_size(0) * BLKSIZ;
  125. // Create the block to insert.
  126. header_t *block_to_insert = (header_t *) start_of_new_memory;
  127. block_to_insert->value.size = end_of_new_memory - start_of_new_memory - sizeof(header_t);
  128. block_to_insert->value.next = NULL;
  129. // add to the free list
  130. freeMemory((void *)(block_to_insert + 1));
  131. return free_list;
  132. }
  133. static void ensureInitialised()
  134. {
  135. if (is_initialised == 0) {
  136. is_initialised = 1;
  137. // initialise the memory allocator.
  138. unsigned int bytes_length = __builtin_wasm_memory_size(0) * BLKSIZ;
  139. // Start at 1 to save 0 for NULL.
  140. header_t *unallocated = (header_t *) initial_offset;
  141. unallocated->value.size = bytes_length - (sizeof(header_t) + initial_offset);
  142. unallocated->value.next = NULL;
  143. free_list = unallocated;
  144. }
  145. }
  146. static __attribute__((used))
  147. void *allocateMemory(unsigned bytes_required)
  148. {
  149. ensureInitialised();
  150. // Pad to 8 bytes until I find a better solution.
  151. bytes_required += (8 - bytes_required % 8) % 8;
  152. unsigned int bytes_required_plus_header = bytes_required + sizeof(header_t);
  153. if (free_list == NULL) {
  154. free_list = getMoreMemory(bytes_required_plus_header);
  155. if (free_list == NULL)
  156. return NULL;
  157. }
  158. header_t *current = free_list;
  159. header_t *previous = current;
  160. while (current != NULL) {
  161. if (current->value.size == bytes_required) {
  162. // exact match
  163. if (current == free_list) {
  164. // allocate all of the free list
  165. free_list = NULL;
  166. current->value.next = NULL;
  167. return current + 1;
  168. } else {
  169. // remove the block
  170. previous->value.next = current->value.next;
  171. current->value.next = NULL;
  172. return current + 1;
  173. }
  174. } else if (current->value.size > bytes_required) {
  175. // split the bigger block
  176. // create the unallocated portion
  177. header_t *unallocated = (header_t *) ((char *)current + bytes_required_plus_header);
  178. unallocated->value.size = current->value.size - bytes_required_plus_header;
  179. unallocated->value.next = current->value.next;
  180. if (current == free_list) {
  181. // We are at the start of the list so make the free listthe unallocated block.
  182. free_list = unallocated;
  183. } else {
  184. // We past the start of the list so remove the current block.
  185. previous->value.next = unallocated;
  186. }
  187. // prepare the allocated portion.
  188. current->value.size = bytes_required;
  189. current->value.next = NULL;
  190. return current + 1;
  191. }
  192. previous = current;
  193. current = current->value.next;
  194. }
  195. // No block was big enough. Grow the memory and try again
  196. if (getMoreMemory(bytes_required) == NULL)
  197. return NULL;
  198. return allocateMemory(bytes_required_plus_header);
  199. }
  200. static __attribute__((used))
  201. void freeMemory(void *ptr)
  202. {
  203. ensureInitialised();
  204. if (ptr == NULL)
  205. return;
  206. header_t *unallocated = ((header_t *) ptr) - 1;
  207. if (free_list == NULL) {
  208. // If the free list is null the unallocated block becomes the free list.
  209. free_list = unallocated;
  210. return;
  211. }
  212. // Find the place in the free list where the unallocated block should be inserted.
  213. header_t *current = free_list;
  214. header_t *previous = current;
  215. while (current != NULL) {
  216. if (unallocated > previous && unallocated < current)
  217. break; // The unallocated block is between the previous and current.
  218. else if (current == previous && unallocated < current) {
  219. // There is only one block in the list and it is after the unallocated.
  220. previous = NULL;
  221. break;
  222. }
  223. // Step forward.
  224. previous = current;
  225. current = current->value.next;
  226. }
  227. // Attach the unallocated block to the current block.
  228. if (current != NULL) {
  229. // Are the blocks adjacent?
  230. if (current == (header_t *) ((char *)(unallocated + 1) + unallocated->value.size)) {
  231. // Merge the unallocated with the current block.
  232. unallocated->value.size += current->value.size + sizeof(header_t);
  233. unallocated->value.next = current->value.next;
  234. } else {
  235. // Chain the unallocated block to the current.
  236. unallocated->value.next = current;
  237. }
  238. }
  239. if (previous == NULL) {
  240. // The unallocated block now starts the free list.
  241. free_list = unallocated;
  242. } else {
  243. // Are the blocks adjacent?
  244. if (unallocated == (header_t *) ((char *)(previous + 1) + previous->value.size)) {
  245. // Merge the previous block with the unallocated.
  246. previous->value.size += unallocated->value.size + sizeof(header_t);
  247. previous->value.next = unallocated->value.next;
  248. } else {
  249. // Chain the previous block to the unallocated.
  250. previous->value.next = unallocated;
  251. }
  252. }
  253. }
  254. static __attribute__((used))
  255. double reportFreeMemory()
  256. {
  257. ensureInitialised();
  258. if (free_list == NULL)
  259. return 0;
  260. header_t *current = free_list;
  261. unsigned int total = 0;
  262. while (current != NULL) {
  263. total += current->value.size;
  264. current = current->value.next;
  265. }
  266. return (double)total;
  267. }
  268. // Bare-bones implementation of sbrk.
  269. void *sbrk(size_t increment)
  270. {
  271. // sbrk(0) returns the current memory size.
  272. if (increment == 0) {
  273. // The wasm spec doesn't guarantee that memory.grow of 0 always succeeds.
  274. return (void *)(__builtin_wasm_memory_size(0) * PAGESIZE);
  275. }
  276. return (allocateMemory(increment));
  277. }
  278. // Include the upstream dlmalloc's malloc.c.
  279. #define LACKS_SYS_TYPES_H 1
  280. #include "malloc.c"
  281. // Export the public names.
  282. void *malloc(size_t size)
  283. {
  284. return dlmalloc(size);
  285. }
  286. void free(void *ptr)
  287. {
  288. dlfree(ptr);
  289. }
  290. void *calloc(size_t nmemb, size_t size)
  291. {
  292. return dlcalloc(nmemb, size);
  293. }
  294. void *realloc(void *ptr, size_t size)
  295. {
  296. return dlrealloc(ptr, size);
  297. }
  298. #if 0
  299. int posix_memalign(void **memptr, size_t alignment, size_t size)
  300. {
  301. return dlposix_memalign(memptr, alignment, size);
  302. }
  303. void *aligned_alloc(size_t alignment, size_t bytes)
  304. {
  305. return dlmemalign(alignment, bytes);
  306. }
  307. size_t malloc_usable_size(void *ptr)
  308. {
  309. return dlmalloc_usable_size(ptr);
  310. }
  311. // Define these to satisfy musl references.
  312. void *__libc_malloc(size_t) __attribute__((alias("malloc")));
  313. void __libc_free(void *) __attribute__((alias("free")));
  314. void *__libc_calloc(size_t nmemb, size_t size) __attribute__((alias("calloc")));
  315. #endif