vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | Refs

chunk.c (6053B)


      1 #define _XOPEN_SOURCE 500
      2 #define _GNU_SOURCE // for MAP_32BIT
      3 
      4 #include <stdio.h>
      5 #include <unistd.h>
      6 #include <fcntl.h>
      7 #include <stdlib.h>
      8 #include <string.h>
      9 #include <sys/mman.h>
     10 #include <errno.h>
     11 #include <assert.h>
     12 
     13 #include "vx32.h"
     14 #include "vx32impl.h"
     15 
     16 extern int vx_elfbigmem;
     17 
     18 typedef struct vxmem_chunk vxmem_chunk;
     19 
     20 struct vxmem_chunk {
     21 	vxmem mem;
     22 	int fd;
     23 	off_t size;
     24 	uint8_t *perm;
     25 };
     26 
     27 static void chunk_free(vxmem *mem)
     28 {
     29 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
     30 
     31 	if(mem->mapped){
     32 		vxmem_unmap(mem, mem->mapped);
     33 		mem->mapped = NULL;
     34 	}
     35 
     36 	free(chunk->perm);
     37 	close(chunk->fd);
     38 	free(chunk);
     39 }
     40 
     41 static int chunk_resize(vxmem *mem, size_t size)
     42 {
     43 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
     44 	uint8_t *perm;
     45 	uint32_t onpage, npage;
     46 
     47 	if(size == chunk->size)
     48 		return 0;
     49 
     50 	if(mem->mapped){
     51 		assert(!vx_elfbigmem);
     52 		vxmem_unmap(mem, mem->mapped);
     53 		mem->mapped = NULL;
     54 	}
     55 
     56 	if(ftruncate(chunk->fd, size) < 0)
     57 		return -1;
     58 	onpage = VXPAGEROUND(chunk->size) / VXPAGESIZE;
     59 	npage = VXPAGEROUND(size) / VXPAGESIZE;
     60 	perm = realloc(chunk->perm, npage);
     61 	if (perm == NULL)
     62 		return -1;
     63 	if(npage > onpage)
     64 		memset(perm + onpage, 0, npage-onpage);
     65 	chunk->perm = perm;
     66 	chunk->size = size;
     67 	return 0;
     68 }	
     69 
     70 vxmem *vxmem_chunk_copy(vxmem *mem)
     71 {
     72 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
     73 	uint8_t *perm;
     74 	uint32_t i, npage;
     75 
     76 	assert(mem->free == chunk_free);
     77 	vxmem *nmem = vxmem_chunk_new(chunk->size);
     78 	if (nmem == NULL)
     79 		return NULL;
     80 	npage = VXPAGEROUND(chunk->size) / VXPAGESIZE;
     81 	int n = 0;
     82 	vxmem_chunk *nchunk = (vxmem_chunk*)nmem;
     83 	char *buf = malloc(4096);
     84 	memmove(nchunk->perm, chunk->perm, npage);
     85 	for (i=0; i<npage; i++) {
     86 		if (nchunk->perm[i]) {
     87 			vxmem_read(mem, buf, i*VXPAGESIZE, VXPAGESIZE);
     88 			vxmem_write(nmem, buf, i*VXPAGESIZE, VXPAGESIZE);
     89 			n++;
     90 		}
     91 	}
     92 	free(buf);
     93 	return nmem;
     94 }
     95 
     96 static ssize_t chunk_read(vxmem *mem, void *data, uint32_t addr, uint32_t len)
     97 {
     98 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
     99 	
    100 	if (addr >= chunk->size)
    101 		return -1;
    102 	if (len > chunk->size - addr)
    103 		len = chunk->size - addr;
    104 	return pread(chunk->fd, data, len, addr);
    105 }
    106 
    107 static ssize_t chunk_write(vxmem *mem, const void *data, uint32_t addr, uint32_t len)
    108 {
    109 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
    110 	
    111 	if (addr >= chunk->size)
    112 		return -1;
    113 	if (len > chunk->size - addr)
    114 		len = chunk->size - addr;
    115 	return pwrite(chunk->fd, data, len, addr);
    116 }
    117 
    118 static int chunk_checkperm(vxmem *mem, uint32_t addr, uint32_t len, uint32_t perm, uint32_t *out_faultva)
    119 {
    120 	uint32_t pn, pe, cpe, va;
    121 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
    122 	
    123 	if (addr + len < addr)
    124 		return 0;
    125 	if (len == 0)
    126 		return 1;
    127 	pn = addr / VXPAGESIZE;
    128 	pe = VXPAGEROUND(addr + len-1) / VXPAGESIZE;
    129 	cpe = VXPAGEROUND(chunk->size) / VXPAGESIZE;
    130 	for (; pn < pe; pn++) {
    131 		if (pn >= cpe) {
    132 			if (out_faultva)
    133 				*out_faultva = chunk->size;
    134 			return 0;
    135 		}
    136 		if ((chunk->perm[pn] & perm) != perm) {
    137 			if (out_faultva) {
    138 				va = pn * VXPAGESIZE;
    139 				if (va < addr)
    140 					va = addr;
    141 				*out_faultva = va;
    142 			}
    143 			return 0;
    144 		}
    145 	}
    146 	return 1;
    147 }
    148 
    149 static int mmperm(int perm)
    150 {
    151 	int m;
    152 	
    153 	if (perm == 0)
    154 		m = PROT_NONE;
    155 	else {
    156 		m = 0;
    157 		if (perm & VXPERM_READ)
    158 			m |= PROT_READ;
    159 		if (perm & VXPERM_WRITE)
    160 			m |= PROT_WRITE;
    161 		if (perm & VXPERM_EXEC)
    162 			m |= PROT_EXEC;
    163 	}
    164 	return m;
    165 }
    166 
    167 static int chunk_setperm(vxmem *mem, uint32_t addr, uint32_t len, uint32_t perm)
    168 {
    169 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
    170 	uint32_t a;
    171 	
    172 	if (addr + len < addr || addr + len > chunk->size) {
    173 		errno = EINVAL;
    174 		return -1;
    175 	}
    176 
    177 	if (mem->mapped) {
    178 		if (mprotect(mem->mapped->base + addr, len, mmperm(perm)) < 0)
    179 			return -1;
    180 	}
    181 
    182 	for (a = addr; a < addr + len; a += VXPAGESIZE)
    183 		chunk->perm[a/VXPAGESIZE] = perm;
    184 	return 0;
    185 }
    186 
    187 static vxmmap *chunk_map(vxmem *mem, uint32_t flags)
    188 {
    189 	vxmmap *mm;
    190 	void *v;
    191 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
    192 	
    193 	if (mem->mapped) {
    194 		mem->mapped->ref++;	// XXX get rid of ref?
    195 		return mem->mapped;
    196 	}
    197 
    198 	mm = malloc(sizeof *mm);
    199 	if (mm == NULL)
    200 		return NULL;
    201 	if ((v = mmap(0, chunk->size, PROT_NONE, MAP_32BIT | (vx_elfbigmem ? MAP_PRIVATE : MAP_SHARED), chunk->fd, 0)) == (void*)-1) {
    202 		free(mm);
    203 		return NULL;
    204 	}
    205 //	vxprint("chunk_map %p size %08x\n", v, chunk->size);
    206 
    207 	// Now set the permissions on all pages that should be accessible.
    208 	unsigned npages = chunk->size / VXPAGESIZE;
    209 	for (unsigned i = 0; i < npages; ) {
    210 		uint8_t pageperm = chunk->perm[i];
    211 
    212 		// For efficiency, merge mprotect calls on page ranges.
    213 		unsigned j;
    214 		for (j = i+1; j < npages; j++)
    215 			if (chunk->perm[j] != pageperm)
    216 				break;
    217 
    218 		if (pageperm == 0) {
    219 			i = j;		// nothing to do for this range
    220 			continue;
    221 		}
    222 
    223 		// Calculate the effective permission to map with in the host.
    224 		int prot = mmperm(pageperm);
    225 		assert(prot >= 0);
    226 
    227 		// Set the permissions on this range.
    228 		if (mprotect((char*)v + i*VXPAGESIZE, (j-i)*VXPAGESIZE, prot) < 0) {
    229 			munmap(v, chunk->size);
    230 			free(mm);
    231 			return NULL;
    232 		}
    233 
    234 		i = j;
    235 	}
    236 
    237 	mm->base = v;
    238 	mm->size = chunk->size;
    239 	mm->ref = 2;	// XXX get rid of ref?
    240 	mem->mapped = mm;
    241 	return mm;
    242 }
    243 
    244 static void chunk_unmap(vxmem *mem, vxmmap *mm)
    245 {
    246 	vxmem_chunk *chunk = (vxmem_chunk*)mem;
    247 	
    248 	if(mm == NULL)
    249 		return;
    250 //	if(--mm->ref > 0)	// XXX get rid of ref?
    251 //		return;
    252 	if(mm == mem->mapped)
    253 		mem->mapped = NULL;
    254 //	vxprint("chunk_unmap %p size %08x\n", mm->base, mm->size);
    255 	munmap(mm->base, mm->size);
    256 	free(mm);
    257 }
    258 
    259 static vxmem chunk_proto = 
    260 {
    261 	chunk_read,
    262 	chunk_write,
    263 	chunk_map,
    264 	chunk_unmap,
    265 	chunk_checkperm,
    266 	chunk_setperm,
    267 	chunk_resize,
    268 	chunk_free,
    269 };
    270 
    271 static vxmem *vxmem_chunk_fromfd(int fd, off_t size)
    272 {
    273 	vxmem_chunk *chunk;
    274 	
    275 	chunk = calloc(sizeof *chunk, 1);
    276 	if (chunk == NULL)
    277 		return NULL;
    278 	chunk->mem = chunk_proto;
    279 	chunk->fd = fd;
    280 	if (chunk_resize((vxmem*)chunk, size) < 0) {
    281 		free(chunk);
    282 		return NULL;
    283 	}
    284 	return (vxmem*)chunk;
    285 }
    286 
    287 vxmem *vxmem_chunk_new(int size)
    288 {
    289 	int fd;
    290 	char tmpfn[] = "/var/tmp/vxXXXXXX";
    291 	vxmem *mem;
    292 	
    293 	if ((fd = mkstemp(tmpfn)) < 0)
    294 		return NULL;
    295 	unlink(tmpfn);
    296 	if ((mem = vxmem_chunk_fromfd(fd, size)) == NULL) {
    297 		close(fd);
    298 		return NULL;
    299 	}
    300 	return mem;
    301 }
    302