PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/pcre/pcre2lib/sljit - sljitExecAllocator.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 90 106 84.9 %
Date: 2022-01-26 Functions: 6 7 85.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  *    Stack-less Just-In-Time compiler
       3             :  *
       4             :  *    Copyright Zoltan Herczeg (hzmester@freemail.hu). All rights reserved.
       5             :  *
       6             :  * Redistribution and use in source and binary forms, with or without modification, are
       7             :  * permitted provided that the following conditions are met:
       8             :  *
       9             :  *   1. Redistributions of source code must retain the above copyright notice, this list of
      10             :  *      conditions and the following disclaimer.
      11             :  *
      12             :  *   2. Redistributions in binary form must reproduce the above copyright notice, this list
      13             :  *      of conditions and the following disclaimer in the documentation and/or other materials
      14             :  *      provided with the distribution.
      15             :  *
      16             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS ``AS IS'' AND ANY
      17             :  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      18             :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
      19             :  * SHALL THE COPYRIGHT HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
      20             :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
      21             :  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
      22             :  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
      23             :  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
      24             :  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      25             :  */
      26             : 
      27             : /*
      28             :    This file contains a simple executable memory allocator
      29             : 
      30             :    It is assumed, that executable code blocks are usually medium (or sometimes
      31             :    large) memory blocks, and the allocator is not too frequently called (less
      32             :    optimized than other allocators). Thus, using it as a generic allocator is
      33             :    not suggested.
      34             : 
      35             :    How does it work:
      36             :      Memory is allocated in continuous memory areas called chunks by alloc_chunk()
      37             :      Chunk format:
      38             :      [ block ][ block ] ... [ block ][ block terminator ]
      39             : 
      40             :    All blocks and the block terminator is started with block_header. The block
      41             :    header contains the size of the previous and the next block. These sizes
      42             :    can also contain special values.
      43             :      Block size:
      44             :        0 - The block is a free_block, with a different size member.
      45             :        1 - The block is a block terminator.
      46             :        n - The block is used at the moment, and the value contains its size.
      47             :      Previous block size:
      48             :        0 - This is the first block of the memory chunk.
      49             :        n - The size of the previous block.
      50             : 
      51             :    Using these size values we can go forward or backward on the block chain.
      52             :    The unused blocks are stored in a chain list pointed by free_blocks. This
      53             :    list is useful if we need to find a suitable memory area when the allocator
      54             :    is called.
      55             : 
      56             :    When a block is freed, the new free block is connected to its adjacent free
      57             :    blocks if possible.
      58             : 
      59             :      [ free block ][ used block ][ free block ]
      60             :    and "used block" is freed, the three blocks are connected together:
      61             :      [           one big free block           ]
      62             : */
      63             : 
      64             : /* --------------------------------------------------------------------- */
      65             : /*  System (OS) functions                                                */
      66             : /* --------------------------------------------------------------------- */
      67             : 
      68             : /* 64 KByte. */
      69             : #define CHUNK_SIZE      0x10000
      70             : 
      71             : /*
      72             :    alloc_chunk / free_chunk :
      73             :      * allocate executable system memory chunks
      74             :      * the size is always divisible by CHUNK_SIZE
      75             :    allocator_grab_lock / allocator_release_lock :
      76             :      * make the allocator thread safe
      77             :      * can be empty if the OS (or the application) does not support threading
      78             :      * only the allocator requires this lock, sljit is fully thread safe
      79             :        as it only uses local variables
      80             : */
      81             : 
      82             : #ifdef _WIN32
      83             : 
      84             : static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
      85             : {
      86             :         return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
      87             : }
      88             : 
      89             : static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
      90             : {
      91             :         SLJIT_UNUSED_ARG(size);
      92             :         VirtualFree(chunk, 0, MEM_RELEASE);
      93             : }
      94             : 
      95             : #else
      96             : 
      97             : #ifdef __APPLE__
      98             : /* Configures TARGET_OS_OSX when appropriate */
      99             : #include <TargetConditionals.h>
     100             : 
     101             : #if TARGET_OS_OSX && defined(MAP_JIT)
     102             : #include <sys/utsname.h>
     103             : #endif /* TARGET_OS_OSX && MAP_JIT */
     104             : 
     105             : #ifdef MAP_JIT
     106             : 
     107             : static SLJIT_INLINE int get_map_jit_flag()
     108             : {
     109             : #if TARGET_OS_OSX
     110             :         /* On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a version
     111             :            of macOS where it's OK to have more than one JIT block. On non-macOS systems, returns
     112             :            MAP_JIT if it is defined. */
     113             :         static int map_jit_flag = -1;
     114             : 
     115             :         /* The following code is thread safe because multiple initialization
     116             :            sets map_jit_flag to the same value and the code has no side-effects.
     117             :            Changing the kernel version witout system restart is (very) unlikely. */
     118             :         if (map_jit_flag == -1) {
     119             :                 struct utsname name;
     120             : 
     121             :                 uname(&name);
     122             : 
     123             :                 /* Kernel version for 10.14.0 (Mojave) */
     124             :                 if (atoi(name.release) >= 18) {
     125             :                         /* Only use MAP_JIT if a hardened runtime is used, because MAP_JIT is incompatible
     126             :                            with fork(). */
     127             :                         void *ptr = mmap(
     128             :                                 NULL, getpagesize(), PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
     129             :                         if (ptr == MAP_FAILED) {
     130             :                                 map_jit_flag = MAP_JIT;
     131             :                         } else {
     132             :                                 map_jit_flag = 0;
     133             :                                 munmap(ptr, getpagesize());
     134             :                         }
     135             :                 } else {
     136             :                         map_jit_flag = 0;
     137             :                 }
     138             :         }
     139             : 
     140             :         return map_jit_flag;
     141             : #else /* !TARGET_OS_OSX */
     142             :         return MAP_JIT;
     143             : #endif /* TARGET_OS_OSX */
     144             : }
     145             : 
     146             : #endif /* MAP_JIT */
     147             : 
     148             : #endif /* __APPLE__ */
     149             : 
     150        5784 : static SLJIT_INLINE void* alloc_chunk(sljit_uw size)
     151             : {
     152             :         void *retval;
     153             : 
     154             : #ifdef MAP_ANON
     155             : 
     156        5784 :         int flags = MAP_PRIVATE | MAP_ANON;
     157             : 
     158             : #ifdef MAP_JIT
     159             :         flags |= get_map_jit_flag();
     160             : #endif
     161             : 
     162        5784 :         retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, flags, -1, 0);
     163             : #else /* !MAP_ANON */
     164             :         if (dev_zero < 0) {
     165             :                 if (open_dev_zero())
     166             :                         return NULL;
     167             :         }
     168             :         retval = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, dev_zero, 0);
     169             : #endif /* MAP_ANON */
     170             : 
     171        5784 :         return (retval != MAP_FAILED) ? retval : NULL;
     172             : }
     173             : 
     174        2971 : static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size)
     175             : {
     176        2971 :         munmap(chunk, size);
     177        2971 : }
     178             : 
     179             : #endif
     180             : 
     181             : /* --------------------------------------------------------------------- */
     182             : /*  Common functions                                                     */
     183             : /* --------------------------------------------------------------------- */
     184             : 
     185             : #define CHUNK_MASK      (~(CHUNK_SIZE - 1))
     186             : 
     187             : struct block_header {
     188             :         sljit_uw size;
     189             :         sljit_uw prev_size;
     190             : };
     191             : 
     192             : struct free_block {
     193             :         struct block_header header;
     194             :         struct free_block *next;
     195             :         struct free_block *prev;
     196             :         sljit_uw size;
     197             : };
     198             : 
     199             : #define AS_BLOCK_HEADER(base, offset) \
     200             :         ((struct block_header*)(((sljit_u8*)base) + offset))
     201             : #define AS_FREE_BLOCK(base, offset) \
     202             :         ((struct free_block*)(((sljit_u8*)base) + offset))
     203             : #define MEM_START(base)         ((void*)(((sljit_u8*)base) + sizeof(struct block_header)))
     204             : #define ALIGN_SIZE(size)        (((size) + sizeof(struct block_header) + 7) & ~7)
     205             : 
     206             : static struct free_block* free_blocks;
     207             : static sljit_uw allocated_size;
     208             : static sljit_uw total_size;
     209             : 
     210       55185 : static SLJIT_INLINE void sljit_insert_free_block(struct free_block *free_block, sljit_uw size)
     211             : {
     212       55185 :         free_block->header.size = 0;
     213       55185 :         free_block->size = size;
     214             : 
     215       55185 :         free_block->next = free_blocks;
     216       55185 :         free_block->prev = NULL;
     217       55185 :         if (free_blocks)
     218       53653 :                 free_blocks->prev = free_block;
     219       55185 :         free_blocks = free_block;
     220       55185 : }
     221             : 
     222       52372 : static SLJIT_INLINE void sljit_remove_free_block(struct free_block *free_block)
     223             : {
     224       52372 :         if (free_block->next)
     225       46879 :                 free_block->next->prev = free_block->prev;
     226             : 
     227       52372 :         if (free_block->prev)
     228       48726 :                 free_block->prev->next = free_block->next;
     229             :         else {
     230             :                 SLJIT_ASSERT(free_blocks == free_block);
     231        3646 :                 free_blocks = free_block->next;
     232             :         }
     233       52372 : }
     234             : 
     235       51654 : SLJIT_API_FUNC_ATTRIBUTE void* sljit_malloc_exec(sljit_uw size)
     236             : {
     237             :         struct block_header *header;
     238             :         struct block_header *next_header;
     239             :         struct free_block *free_block;
     240             :         sljit_uw chunk_size;
     241             : 
     242       51654 :         allocator_grab_lock();
     243       51654 :         if (size < (64 - sizeof(struct block_header)))
     244           0 :                 size = (64 - sizeof(struct block_header));
     245       51654 :         size = ALIGN_SIZE(size);
     246             : 
     247       51654 :         free_block = free_blocks;
     248     6735294 :         while (free_block) {
     249     6677856 :                 if (free_block->size >= size) {
     250       45870 :                         chunk_size = free_block->size;
     251       45870 :                         if (chunk_size > size + 64) {
     252             :                                 /* We just cut a block from the end of the free block. */
     253       45324 :                                 chunk_size -= size;
     254       45324 :                                 free_block->size = chunk_size;
     255       45324 :                                 header = AS_BLOCK_HEADER(free_block, chunk_size);
     256       45324 :                                 header->prev_size = chunk_size;
     257       45324 :                                 AS_BLOCK_HEADER(header, size)->prev_size = size;
     258             :                         }
     259             :                         else {
     260         546 :                                 sljit_remove_free_block(free_block);
     261         546 :                                 header = (struct block_header*)free_block;
     262         546 :                                 size = chunk_size;
     263             :                         }
     264       45870 :                         allocated_size += size;
     265       45870 :                         header->size = size;
     266       45870 :                         allocator_release_lock();
     267       45870 :                         return MEM_START(header);
     268             :                 }
     269     6631986 :                 free_block = free_block->next;
     270             :         }
     271             : 
     272        5784 :         chunk_size = (size + sizeof(struct block_header) + CHUNK_SIZE - 1) & CHUNK_MASK;
     273        5784 :         header = (struct block_header*)alloc_chunk(chunk_size);
     274        5784 :         if (!header) {
     275           0 :                 allocator_release_lock();
     276           0 :                 return NULL;
     277             :         }
     278             : 
     279        5784 :         chunk_size -= sizeof(struct block_header);
     280        5784 :         total_size += chunk_size;
     281             : 
     282        5784 :         header->prev_size = 0;
     283        5784 :         if (chunk_size > size + 64) {
     284             :                 /* Cut the allocated space into a free and a used block. */
     285        5780 :                 allocated_size += size;
     286        5780 :                 header->size = size;
     287        5780 :                 chunk_size -= size;
     288             : 
     289        5780 :                 free_block = AS_FREE_BLOCK(header, size);
     290        5780 :                 free_block->header.prev_size = size;
     291        5780 :                 sljit_insert_free_block(free_block, chunk_size);
     292        5780 :                 next_header = AS_BLOCK_HEADER(free_block, chunk_size);
     293             :         }
     294             :         else {
     295             :                 /* All space belongs to this allocation. */
     296           4 :                 allocated_size += chunk_size;
     297           4 :                 header->size = chunk_size;
     298           4 :                 next_header = AS_BLOCK_HEADER(header, chunk_size);
     299             :         }
     300        5784 :         next_header->size = 1;
     301        5784 :         next_header->prev_size = chunk_size;
     302        5784 :         allocator_release_lock();
     303        5784 :         return MEM_START(header);
     304             : }
     305             : 
     306       51656 : SLJIT_API_FUNC_ATTRIBUTE void sljit_free_exec(void* ptr)
     307             : {
     308             :         struct block_header *header;
     309             :         struct free_block* free_block;
     310             : 
     311       51656 :         allocator_grab_lock();
     312       51656 :         header = AS_BLOCK_HEADER(ptr, -(sljit_sw)sizeof(struct block_header));
     313       51656 :         allocated_size -= header->size;
     314             : 
     315             :         /* Connecting free blocks together if possible. */
     316             : 
     317             :         /* If header->prev_size == 0, free_block will equal to header.
     318             :            In this case, free_block->header.size will be > 0. */
     319       51656 :         free_block = AS_FREE_BLOCK(header, -(sljit_sw)header->prev_size);
     320       51656 :         if (SLJIT_UNLIKELY(!free_block->header.size)) {
     321        2251 :                 free_block->size += header->size;
     322        2251 :                 header = AS_BLOCK_HEADER(free_block, free_block->size);
     323        2251 :                 header->prev_size = free_block->size;
     324             :         }
     325             :         else {
     326       49405 :                 free_block = (struct free_block*)header;
     327       49405 :                 sljit_insert_free_block(free_block, header->size);
     328             :         }
     329             : 
     330       51656 :         header = AS_BLOCK_HEADER(free_block, free_block->size);
     331       51656 :         if (SLJIT_UNLIKELY(!header->size)) {
     332       48855 :                 free_block->size += ((struct free_block*)header)->size;
     333       48855 :                 sljit_remove_free_block((struct free_block*)header);
     334       48855 :                 header = AS_BLOCK_HEADER(free_block, free_block->size);
     335       48855 :                 header->prev_size = free_block->size;
     336             :         }
     337             : 
     338             :         /* The whole chunk is free. */
     339       51656 :         if (SLJIT_UNLIKELY(!free_block->header.prev_size && header->size == 1)) {
     340             :                 /* If this block is freed, we still have (allocated_size / 2) free space. */
     341        6241 :                 if (total_size - free_block->size > (allocated_size * 3 / 2)) {
     342        2971 :                         total_size -= free_block->size;
     343        2971 :                         sljit_remove_free_block(free_block);
     344        2971 :                         free_chunk(free_block, free_block->size + sizeof(struct block_header));
     345             :                 }
     346             :         }
     347             : 
     348       51656 :         allocator_release_lock();
     349       51656 : }
     350             : 
     351           0 : SLJIT_API_FUNC_ATTRIBUTE void sljit_free_unused_memory_exec(void)
     352             : {
     353             :         struct free_block* free_block;
     354             :         struct free_block* next_free_block;
     355             : 
     356           0 :         allocator_grab_lock();
     357             : 
     358           0 :         free_block = free_blocks;
     359           0 :         while (free_block) {
     360           0 :                 next_free_block = free_block->next;
     361           0 :                 if (!free_block->header.prev_size && 
     362           0 :                                 AS_BLOCK_HEADER(free_block, free_block->size)->size == 1) {
     363           0 :                         total_size -= free_block->size;
     364           0 :                         sljit_remove_free_block(free_block);
     365           0 :                         free_chunk(free_block, free_block->size + sizeof(struct block_header));
     366             :                 }
     367           0 :                 free_block = next_free_block;
     368             :         }
     369             : 
     370             :         SLJIT_ASSERT((total_size && free_blocks) || (!total_size && !free_blocks));
     371           0 :         allocator_release_lock();
     372           0 : }

Generated by: LCOV version 1.10

Generated at Wed, 26 Jan 2022 17:13:57 +0000 (2 days ago)

Copyright © 2005-2022 The PHP Group
All rights reserved.