mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-25 02:30:57 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			192 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			192 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
 | |
| │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :vi│
 | |
| ╞══════════════════════════════════════════════════════════════════════════════╡
 | |
| │ Copyright 2020 Justine Alexandra Roberts Tunney                              │
 | |
| │                                                                              │
 | |
| │ Permission to use, copy, modify, and/or distribute this software for         │
 | |
| │ any purpose with or without fee is hereby granted, provided that the         │
 | |
| │ above copyright notice and this permission notice appear in all copies.      │
 | |
| │                                                                              │
 | |
| │ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
 | |
| │ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
 | |
| │ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
 | |
| │ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
 | |
| │ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
 | |
| │ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
 | |
| │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
 | |
| │ PERFORMANCE OF THIS SOFTWARE.                                                │
 | |
| ╚─────────────────────────────────────────────────────────────────────────────*/
 | |
| #include "libc/assert.h"
 | |
| #include "libc/bits/bits.h"
 | |
| #include "libc/bits/likely.h"
 | |
| #include "libc/bits/weaken.h"
 | |
| #include "libc/calls/calls.h"
 | |
| #include "libc/calls/strace.internal.h"
 | |
| #include "libc/dce.h"
 | |
| #include "libc/errno.h"
 | |
| #include "libc/intrin/asan.internal.h"
 | |
| #include "libc/log/libfatal.internal.h"
 | |
| #include "libc/macros.internal.h"
 | |
| #include "libc/mem/mem.h"
 | |
| #include "libc/runtime/directmap.internal.h"
 | |
| #include "libc/runtime/memtrack.internal.h"
 | |
| #include "libc/runtime/runtime.h"
 | |
| #include "libc/str/str.h"
 | |
| #include "libc/sysv/consts/map.h"
 | |
| #include "libc/sysv/consts/prot.h"
 | |
| #include "libc/sysv/errfuns.h"
 | |
| 
 | |
| static void *MoveMemoryIntervals(struct MemoryInterval *d,
 | |
|                                  const struct MemoryInterval *s, int n) {
 | |
|   /* asan runtime depends on this function */
 | |
|   int i;
 | |
|   assert(n >= 0);
 | |
|   if (d > s) {
 | |
|     for (i = n; i--;) {
 | |
|       d[i] = s[i];
 | |
|     }
 | |
|   } else {
 | |
|     for (i = 0; i < n; ++i) {
 | |
|       d[i] = s[i];
 | |
|     }
 | |
|   }
 | |
|   return d;
 | |
| }
 | |
| 
 | |
| static void RemoveMemoryIntervals(struct MemoryIntervals *mm, int i, int n) {
 | |
|   /* asan runtime depends on this function */
 | |
|   assert(i >= 0);
 | |
|   assert(i + n <= mm->i);
 | |
|   MoveMemoryIntervals(mm->p + i, mm->p + i + n, mm->i - (i + n));
 | |
|   mm->i -= n;
 | |
| }
 | |
| 
 | |
| static bool ExtendMemoryIntervals(struct MemoryIntervals *mm) {
 | |
|   int prot, flags;
 | |
|   char *base, *shad;
 | |
|   size_t gran, size;
 | |
|   struct DirectMap dm;
 | |
|   gran = kMemtrackGran;
 | |
|   base = (char *)kMemtrackStart;
 | |
|   prot = PROT_READ | PROT_WRITE;
 | |
|   flags = MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED;
 | |
|   /* TODO(jart): These map handles should not leak across NT fork() */
 | |
|   if (mm->p == mm->s) {
 | |
|     if (IsAsan()) {
 | |
|       shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000);
 | |
|       dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0);
 | |
|       if (!dm.addr) return false;
 | |
|     }
 | |
|     dm = sys_mmap(base, gran, prot, flags, -1, 0);
 | |
|     if (!dm.addr) return false;
 | |
|     MoveMemoryIntervals(dm.addr, mm->p, mm->i);
 | |
|     mm->p = dm.addr;
 | |
|     mm->n = gran / sizeof(*mm->p);
 | |
|   } else {
 | |
|     size = ROUNDUP(mm->n * sizeof(*mm->p), gran);
 | |
|     base += size;
 | |
|     if (IsAsan()) {
 | |
|       shad = (char *)(((intptr_t)base >> 3) + 0x7fff8000);
 | |
|       dm = sys_mmap(shad, gran >> 3, prot, flags, -1, 0);
 | |
|       if (!dm.addr) return false;
 | |
|     }
 | |
|     dm = sys_mmap(base, gran, prot, flags, -1, 0);
 | |
|     if (!dm.addr) return false;
 | |
|     mm->n = (size + gran) / sizeof(*mm->p);
 | |
|   }
 | |
|   assert(AreMemoryIntervalsOk(mm));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| int CreateMemoryInterval(struct MemoryIntervals *mm, int i) {
 | |
|   /* asan runtime depends on this function */
 | |
|   int rc;
 | |
|   rc = 0;
 | |
|   assert(i >= 0);
 | |
|   assert(i <= mm->i);
 | |
|   assert(mm->n >= 0);
 | |
|   if (UNLIKELY(mm->i == mm->n) && !ExtendMemoryIntervals(mm)) return enomem();
 | |
|   MoveMemoryIntervals(mm->p + i + 1, mm->p + i, mm->i++ - i);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| static int PunchHole(struct MemoryIntervals *mm, int x, int y, int i) {
 | |
|   if (CreateMemoryInterval(mm, i) == -1) return -1;
 | |
|   mm->p[i].y = x - 1;
 | |
|   mm->p[i + 1].x = y + 1;
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int ReleaseMemoryIntervals(struct MemoryIntervals *mm, int x, int y,
 | |
|                            void wf(struct MemoryIntervals *, int, int)) {
 | |
|   unsigned l, r;
 | |
|   assert(y >= x);
 | |
|   assert(AreMemoryIntervalsOk(mm));
 | |
|   if (!mm->i) return 0;
 | |
|   l = FindMemoryInterval(mm, x);
 | |
|   if (l == mm->i) return 0;
 | |
|   if (!l && y < mm->p[l].x) return 0;
 | |
|   if (y < mm->p[l].x) return 0;
 | |
|   r = FindMemoryInterval(mm, y);
 | |
|   if (r == mm->i || (r > l && y < mm->p[r].x)) --r;
 | |
|   assert(r >= l);
 | |
|   assert(x <= mm->p[r].y);
 | |
|   if (l == r && x > mm->p[l].x && y < mm->p[l].y) {
 | |
|     return PunchHole(mm, x, y, l);
 | |
|   }
 | |
|   if (x > mm->p[l].x && x <= mm->p[l].y) {
 | |
|     assert(y >= mm->p[l].y);
 | |
|     if (IsWindows()) return einval();
 | |
|     mm->p[l].y = x - 1;
 | |
|     assert(mm->p[l].x <= mm->p[l].y);
 | |
|     ++l;
 | |
|   }
 | |
|   if (y >= mm->p[r].x && y < mm->p[r].y) {
 | |
|     assert(x <= mm->p[r].x);
 | |
|     if (IsWindows()) return einval();
 | |
|     mm->p[r].x = y + 1;
 | |
|     assert(mm->p[r].x <= mm->p[r].y);
 | |
|     --r;
 | |
|   }
 | |
|   if (l <= r) {
 | |
|     if (IsWindows() && wf) {
 | |
|       wf(mm, l, r);
 | |
|     }
 | |
|     RemoveMemoryIntervals(mm, l, r - l + 1);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| int TrackMemoryInterval(struct MemoryIntervals *mm, int x, int y, long h,
 | |
|                         int prot, int flags, bool readonlyfile, bool iscow,
 | |
|                         long offset, long size) {
 | |
|   /* asan runtime depends on this function */
 | |
|   unsigned i;
 | |
|   assert(y >= x);
 | |
|   assert(AreMemoryIntervalsOk(mm));
 | |
|   i = FindMemoryInterval(mm, x);
 | |
|   if (i && x == mm->p[i - 1].y + 1 && h == mm->p[i - 1].h &&
 | |
|       prot == mm->p[i - 1].prot && flags == mm->p[i - 1].flags) {
 | |
|     mm->p[i - 1].y = y;
 | |
|     if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h &&
 | |
|         prot == mm->p[i].prot && flags == mm->p[i].flags) {
 | |
|       mm->p[i - 1].y = mm->p[i].y;
 | |
|       RemoveMemoryIntervals(mm, i, 1);
 | |
|     }
 | |
|   } else if (i < mm->i && y + 1 == mm->p[i].x && h == mm->p[i].h &&
 | |
|              prot == mm->p[i].prot && flags == mm->p[i].flags) {
 | |
|     mm->p[i].x = x;
 | |
|   } else {
 | |
|     if (CreateMemoryInterval(mm, i) == -1) return -1;
 | |
|     mm->p[i].x = x;
 | |
|     mm->p[i].y = y;
 | |
|     mm->p[i].h = h;
 | |
|     mm->p[i].prot = prot;
 | |
|     mm->p[i].flags = flags;
 | |
|     mm->p[i].offset = offset;
 | |
|     mm->p[i].size = size;
 | |
|     mm->p[i].iscow = iscow;
 | |
|     mm->p[i].readonlyfile = readonlyfile;
 | |
|   }
 | |
|   return 0;
 | |
| }
 |