1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                 Eclipse Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*          http://www.eclipse.org/org/documents/epl-v10.html           *
11*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#if defined(_UWIN) && defined(_BLD_ast)
23
24void _STUB_malloc(){}
25
26#else
27
28#if _UWIN
29
30#define calloc		______calloc
31#define _ast_free	______free
32#define malloc		______malloc
33#define mallinfo	______mallinfo
34#define mallopt		______mallopt
35#define mstats		______mstats
36#define realloc		______realloc
37
38#define _STDLIB_H_	1
39
40extern int		atexit(void(*)(void));
41extern char*		getenv(const char*);
42
43#endif
44
45#include	"vmhdr.h"
46#include	<errno.h>
47
48#if _UWIN
49
50#include	<malloc.h>
51
52#define _map_malloc	1
53#define _mal_alloca	1
54
55#undef	calloc
56#define calloc		_ast_calloc
57#undef	_ast_free
58#define free		_ast_free
59#undef	malloc
60#define malloc		_ast_malloc
61#undef	mallinfo
62typedef struct ______mallinfo Mallinfo_t;
63#undef	mallopt
64#undef	mstats
65typedef struct ______mstats Mstats_t;
66#undef	realloc
67#define realloc		_ast_realloc
68
69#endif
70
71#if __STD_C
72#define F0(f,t0)		f(t0)
73#define F1(f,t1,a1)		f(t1 a1)
74#define F2(f,t1,a1,t2,a2)	f(t1 a1, t2 a2)
75#else
76#define F0(f,t0)		f()
77#define F1(f,t1,a1)		f(a1) t1 a1;
78#define F2(f,t1,a1,t2,a2)	f(a1, a2) t1 a1; t2 a2;
79#endif
80
81/*
82 * define _AST_std_malloc=1 to force the standard malloc
83 * if _map_malloc is also defined then _ast_malloc etc.
84 * will simply call malloc etc.
85 */
86
87#if !defined(_AST_std_malloc) && __CYGWIN__
88#define _AST_std_malloc	1
89#endif
90
91/*	malloc compatibility functions
92**
93**	These are aware of debugging/profiling and are driven by the
94**	VMALLOC_OPTIONS environment variable which is a comma or space
95**	separated list of [no]name[=value] options:
96**
97**	    abort	if Vmregion==Vmdebug then VM_DBABORT is set,
98**			otherwise _BLD_DEBUG enabled assertions abort()
99**			on failure
100**	    break	try sbrk() block allocator first
101**	    check	if Vmregion==Vmbest then the region is checked every op
102**	    free	disable addfreelist()
103**	    keep	disable free -- if code works with this enabled then it
104**	    		probably accesses free'd data
105**	    method=m	sets Vmregion=m if not defined, m (Vm prefix optional)
106**			may be one of { best debug last profile }
107**	    mmap	try mmap() block allocator first
108**	    period=n	sets Vmregion=Vmdebug if not defined, if
109**			Vmregion==Vmdebug the region is checked every n ops
110**	    profile=f	sets Vmregion=Vmprofile if not set, if
111**			Vmregion==Vmprofile then profile info printed to file f
112**	    start=n	sets Vmregion=Vmdebug if not defined, if
113**			Vmregion==Vmdebug region checking starts after n ops
114**	    trace=f	enables tracing to file f
115**	    warn=f	sets Vmregion=Vmdebug if not defined, if
116**			Vmregion==Vmdebug then warnings printed to file f
117**	    watch=a	sets Vmregion=Vmdebug if not defined, if
118**			Vmregion==Vmdebug then address a is watched
119**
120**	Output files are created if they don't exist. &n and /dev/fd/n name
121**	the file descriptor n which must be open for writing. The pattern %p
122**	in a file name is replaced by the process ID.
123**
124**	VMALLOC_OPTIONS combines the features of these previously used env vars:
125**	    { VMCHECK VMDEBUG VMETHOD VMPROFILE VMTRACE }
126**
127**	Written by Kiem-Phong Vo, kpv@research.att.com, 01/16/94.
128*/
129
130#if _sys_stat
131#include	<sys/stat.h>
132#endif
133#include	<fcntl.h>
134
135#ifdef S_IRUSR
136#define CREAT_MODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
137#else
138#define CREAT_MODE	0644
139#endif
140
141static Vmulong_t	_Vmdbstart = 0;
142static Vmulong_t	_Vmdbcheck = 0;
143static Vmulong_t	_Vmdbtime = 0;
144static int		_Vmpffd = -1;
145
146#if ( !_std_malloc || !_BLD_ast ) && !_AST_std_malloc
147
148#if !_map_malloc
149#undef calloc
150#undef cfree
151#undef free
152#undef mallinfo
153#undef malloc
154#undef mallopt
155#undef memalign
156#undef mstats
157#undef realloc
158#undef valloc
159
160#if _malloc_hook
161
162#include <malloc.h>
163
164#undef	calloc
165#undef	cfree
166#undef	free
167#undef	malloc
168#undef	memalign
169#undef	realloc
170
171#define calloc		_ast_calloc
172#define cfree		_ast_cfree
173#define free		_ast_free
174#define malloc		_ast_malloc
175#define memalign	_ast_memalign
176#define realloc		_ast_realloc
177
178#endif
179
180#endif
181
182#if _WINIX
183
184#include <ast_windows.h>
185
186#if _UWIN
187
188#define VMRECORD(p)	_vmrecord(p)
189#define VMBLOCK		{ int _vmblock = _sigblock();
190#define VMUNBLOCK	_sigunblock(_vmblock); }
191
192extern int		_sigblock(void);
193extern void		_sigunblock(int);
194extern unsigned long	_record[2048];
195
196__inline Void_t* _vmrecord(Void_t* p)
197{
198	register unsigned long	v = ((unsigned long)p)>>16;
199
200	_record[v>>5] |= 1<<((v&0x1f));
201	return p;
202}
203
204#else
205
206#define getenv(s)	lcl_getenv(s)
207
208static char*
209lcl_getenv(const char* s)
210{
211	int		n;
212	static char	buf[512];
213
214	if (!(n = GetEnvironmentVariable(s, buf, sizeof(buf))) || n > sizeof(buf))
215		return 0;
216	return buf;
217}
218
219#endif /* _UWIN */
220
221#endif /* _WINIX */
222
223#ifndef VMRECORD
224#define VMRECORD(p)	(p)
225#define VMBLOCK
226#define VMUNBLOCK
227#endif
228
229#if defined(__EXPORT__)
230#define extern		extern __EXPORT__
231#endif
232
233static int		_Vmflinit = 0;
234#define VMFLINIT() \
235	{ if(!_Vmflinit)	vmflinit(); \
236	  if(_Vmdbcheck) \
237	  { if(_Vmdbtime < _Vmdbstart) _Vmdbtime += 1; \
238	    else if((_Vmdbtime += 1) < _Vmdbstart) _Vmdbtime = _Vmdbstart; \
239	    if(_Vmdbtime >= _Vmdbstart && (_Vmdbtime % _Vmdbcheck) == 0 && \
240	       Vmregion->meth.meth == VM_MTDEBUG) \
241		vmdbcheck(Vmregion); \
242	  } \
243	}
244
245#if __STD_C
246static int vmflinit(void)
247#else
248static int vmflinit()
249#endif
250{
251	char*		file;
252	int		line;
253	Void_t*		func;
254
255	/* this must be done now to avoid any inadvertent recursion (more below) */
256	_Vmflinit = 1;
257	VMFLF(Vmregion,file,line,func);
258
259	/* if getenv() calls malloc(), the options may not affect the eventual region */
260	VMOPTIONS();
261
262	/* reset file and line number to correct values for the call */
263	Vmregion->file = file;
264	Vmregion->line = line;
265	Vmregion->func = func;
266
267	return 0;
268}
269
270/* use multiple regions to reduce blocking by concurrent threads  */
271#if _mem_mmap_anon || _mem_mmap_zero
272static Vmalloc_t	*Region[64];	/* list of concurrent regions	*/
273static unsigned int	Regmax = 64;	/* max number of regions	*/
274#else
275static Vmalloc_t*	Region[1];	/* list of concurrent regions	*/
276static unsigned int	Regmax = 0;
277#endif
278static unsigned int	Regnum = 0; 	/* current #concurrent regions	*/
279
280/* statistics */
281static unsigned int	Regopen = 0; 	/* #allocation calls opened	*/
282static unsigned int	Reglock = 0; 	/* #allocation calls locked	*/
283static unsigned int	Regprobe = 0; 	/* #probes to find a region	*/
284
285int setregmax(int regmax)
286{
287	int	oldmax = Regmax;
288
289	if(regmax >= Regnum && regmax <= sizeof(Region)/sizeof(Region[0]))
290		Regmax = regmax;
291
292	return oldmax;
293}
294
295/* return statistics */
296int _mallocstat(Vmstat_t* st)
297{
298	Vmstat_t	vmst;
299	int		k;
300
301	if(vmstat(Vmregion, st) < 0) /* add up all stats */
302		return -1;
303	for(k = 0; k < Regnum; ++k)
304	{	if(!Region[k])
305			continue;
306		if(vmstat(Region[k], &vmst) < 0 )
307			return -1;
308		st->n_busy += vmst.n_busy;
309		st->n_free += vmst.n_free;
310		st->s_busy += vmst.s_busy;
311		st->s_free += vmst.s_free;
312		st->m_busy += vmst.m_busy;
313		st->m_free += vmst.m_free;
314		st->n_seg  += vmst.n_seg;
315		st->extent += vmst.extent;
316	}
317
318	st->n_region = Regnum+1;
319	st->n_open = Regopen;
320	st->n_lock = Reglock;
321	st->n_probe = Regprobe;
322
323	return 0;
324}
325
326/* find the region that a block was allocated from */
327static Vmalloc_t* regionof(Void_t* addr)
328{
329	int	k;
330
331#if USE_NATIVE
332#define CAUTIOUS	1
333#else
334#define CAUTIOUS	0
335#endif
336	if(CAUTIOUS || Vmregion->meth.meth != VM_MTBEST )
337	{	/* addr will not be dereferenced here */
338		if(vmaddr(Vmregion,addr) == 0 )
339			return Vmregion;
340		for(k = 0; k < Regnum; ++k)
341			if(Region[k] && vmaddr(Region[k], addr) == 0 )
342				return Region[k];
343		return NIL(Vmalloc_t*);
344	}
345	else
346	{	/* fast, but susceptible to bad data */
347		Vmdata_t *vd = SEG(BLOCK(addr))->vmdt;
348		if(Vmregion->data == vd )
349			return Vmregion;
350		for(k = 0; k < Regnum; ++k)
351			if(Region[k] && Region[k]->data == vd)
352				return Region[k];
353		return NIL(Vmalloc_t*);
354	}
355}
356
357/* manage a cache of free objects */
358typedef struct _regfree_s
359{	struct _regfree_s*	next;
360} Regfree_t;
361static Regfree_t	*Regfree;
362
363static void addfreelist(Regfree_t* data)
364{
365	unsigned int	k;
366	Regfree_t	*head;
367
368	for(k = 0;; ASOLOOP(k) )
369	{	data->next = head = Regfree;
370		if(asocasptr(&Regfree, head, data) == (Void_t*)head )
371			return;
372	}
373}
374
375static void clrfreelist()
376{
377	Regfree_t	*list, *next;
378	Vmalloc_t	*vm;
379
380	if(!(list = Regfree) )
381		return; /* nothing to do */
382
383	if(asocasptr(&Regfree, list, NIL(Regfree_t*)) != list )
384		return; /* somebody else is doing it */
385
386	for(; list; list = next)
387	{	next = list->next;
388		if(vm = regionof((Void_t*)list))
389		{	if(asocasint(&vm->data->lock, 0, 1) == 0) /* can free this now */
390			{	(void)(*vm->meth.freef)(vm, (Void_t*)list, 1);
391				vm->data->lock = 0;
392			}
393			else	addfreelist(list); /* ah well, back in the queue */
394		}
395	}
396}
397
398/* get a suitable region to allocate from */
399typedef struct _regdisc_s
400{	Vmdisc_t	disc;
401	char		slop[64]; /* to absorb any extra data in Vmdcsystem */
402} Regdisc_t;
403
404static int regexcept(Vmalloc_t* vm, int type, Void_t* data, Vmdisc_t* disc)
405{
406	if(type == VM_OPEN)
407	{	if(data) /* make vmopen allocate all memory using discipline */
408			*(Void_t**)data = data; /* just make it non-NULL */
409		return 0;
410	}
411	return 0;
412}
413
414static Vmalloc_t* getregion(int* local)
415{
416	Vmalloc_t		*vm;
417	int			p, pos;
418
419	static unsigned int	Rand = 0xdeadbeef; /* a cheap prng */
420#define RAND()			(Rand = Rand*16777617 + 3)
421
422	clrfreelist();
423
424	if(Regmax <= 0 )
425	{	/* uni-process/thread */
426		*local = 1;
427		Vmregion->data->lock = 1;
428		return Vmregion;
429	}
430	else if(asocasint(&Vmregion->data->lock, 0, 1) == 0 )
431	{	/* Vmregion is open, so use it */
432		*local = 1;
433		asoincint(&Regopen);
434		return Vmregion;
435	}
436
437	asoincint(&Regprobe); /* probe Region[] to find an open region */
438	if(Regnum == 0)
439		pos = 0;
440	else for(pos = p = RAND()%Regnum;; )
441	{	if(Region[p] && asocasint(&Region[p]->data->lock, 0, 1) == 0 )
442		{	*local = 1;
443			asoincint(&Regopen);
444			return Region[p];
445		}
446		if((p = (p+1)%Regnum) == pos )
447			break;
448	}
449
450	/* grab the next open slot for a new region */
451	while((p = Regnum) < Regmax)
452		if(asocasint(&Regnum, p, p+1) == p )
453			break;
454	if(p < Regmax) /* this slot is now ours */
455	{	static Regdisc_t	Regdisc;
456		if(!Regdisc.disc.exceptf) /* one time initialization */
457		{	GETPAGESIZE(_Vmpagesize);
458			memcpy(&Regdisc, Vmdcsystem, Vmdcsystem->size);
459			Regdisc.disc.round = ROUND(_Vmpagesize, 64*1024);
460			Regdisc.disc.exceptf = regexcept;
461		}
462
463		/**/ASSERT(Region[p] == NIL(Vmalloc_t*));
464		if((vm = vmopen(&Regdisc.disc, Vmbest, VM_SHARE)) != NIL(Vmalloc_t*) )
465		{	vm->data->lock = 1; /* lock new region now */
466			*local = 1;
467			asoincint(&Regopen);
468			return (Region[p] = vm);
469		}
470		else	Region[p] = Vmregion; /* better than nothing */
471	}
472
473	/* must return something */
474	vm = Region[pos] ? Region[pos] : Vmregion;
475	if(asocasint(&vm->data->lock, 0, 1) == 0)
476	{	*local = 1;
477		asoincint(&Regopen);
478	}
479	else
480	{	*local = 0;
481		asoincint(&Reglock);
482	}
483	return vm;
484}
485
486#if __STD_C
487extern Void_t* calloc(reg size_t n_obj, reg size_t s_obj)
488#else
489extern Void_t* calloc(n_obj, s_obj)
490reg size_t	n_obj;
491reg size_t	s_obj;
492#endif
493{
494	Void_t		*addr;
495	Vmalloc_t	*vm;
496	int		local = 0;
497	VMFLINIT();
498
499	vm = getregion(&local);
500	addr = (*vm->meth.resizef)(vm, NIL(Void_t*), n_obj*s_obj, VM_RSZERO, local);
501	if(local)
502	{	/**/ASSERT(vm->data->lock == 1);
503		vm->data->lock = 0;
504	}
505	return VMRECORD(addr);
506}
507
508#if __STD_C
509extern Void_t* malloc(reg size_t size)
510#else
511extern Void_t* malloc(size)
512reg size_t	size;
513#endif
514{
515	Void_t		*addr;
516	Vmalloc_t	*vm;
517	int		local = 0;
518	VMFLINIT();
519
520	vm = getregion(&local);
521	addr = (*vm->meth.allocf)(vm, size, local);
522	if(local)
523	{	/**/ASSERT(vm->data->lock == 1);
524		vm->data->lock = 0;
525	}
526	return VMRECORD(addr);
527}
528
529#if __STD_C
530extern Void_t* realloc(reg Void_t* data, reg size_t size)
531#else
532extern Void_t* realloc(data,size)
533reg Void_t*	data;	/* block to be reallocated	*/
534reg size_t	size;	/* new size			*/
535#endif
536{
537	ssize_t		copy;
538	Void_t		*addr;
539	Vmalloc_t	*vm;
540	VMFLINIT();
541
542	if(!data)
543		return malloc(size);
544	else if((vm = regionof(data)) )
545	{	if(vm == Vmregion && vm != Vmheap) /* no multiple region usage here */
546		{	addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 0);
547			return VMRECORD(addr);
548		}
549		if(asocasint(&vm->data->lock, 0, 1) == 0 ) /* region is open */
550		{	addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 1);
551			vm->data->lock = 0;
552			return VMRECORD(addr);
553		}
554		else if(Regmax > 0 && Vmregion == Vmheap && (addr = malloc(size)) )
555		{	if((copy = SIZE(BLOCK(data))&~BITS) > size )
556				copy = size;
557			memcpy(addr, data, copy);
558			addfreelist((Regfree_t*)data);
559			return VMRECORD(addr);
560		}
561		else /* this may block but it is the best that we can do now */
562		{	addr = (*vm->meth.resizef)(vm, data, size, VM_RSCOPY|VM_RSMOVE, 0);
563			return VMRECORD(addr);
564		}
565	}
566	else /* not our data */
567	{
568#if USE_NATIVE
569#undef	realloc /* let the native realloc() take care of it */
570#if __STD_C
571		extern Void_t*	realloc(Void_t*, size_t);
572#else
573		extern Void_t*	realloc();
574#endif
575		return realloc(data, size);
576#else
577		return NIL(Void_t*);
578#endif
579	}
580}
581
582#if __STD_C
583extern void free(reg Void_t* data)
584#else
585extern void free(data)
586reg Void_t*	data;
587#endif
588{
589	Vmalloc_t	*vm;
590	VMFLINIT();
591
592	if(!data || (_Vmassert & VM_keep))
593		return;
594	else if((vm = regionof(data)) )
595	{
596		if(vm == Vmregion && Vmregion != Vmheap || (_Vmassert & VM_free))
597			(void)(*vm->meth.freef)(vm, data, 0);
598		else	addfreelist((Regfree_t*)data);
599		return;
600	}
601	else /* not our data */
602	{
603#if USE_NATIVE
604#undef	free /* let the native free() take care of it */
605#if __STD_C
606		extern void	free(Void_t*);
607#else
608		extern void	free();
609#endif
610		free(data);
611#endif
612		return;
613	}
614}
615
616#if __STD_C
617extern void cfree(reg Void_t* data)
618#else
619extern void cfree(data)
620reg Void_t*	data;
621#endif
622{
623	free(data);
624}
625
626#if __STD_C
627extern Void_t* memalign(reg size_t align, reg size_t size)
628#else
629extern Void_t* memalign(align, size)
630reg size_t	align;
631reg size_t	size;
632#endif
633{
634	Void_t		*addr;
635	Vmalloc_t	*vm;
636	int		local = 0;
637	VMFLINIT();
638
639	vm = getregion(&local);
640	VMBLOCK
641	addr = (*vm->meth.alignf)(vm, size, align, local);
642	if(local)
643	{	/**/ASSERT(vm->data->lock == 1);
644		vm->data->lock = 0;
645	}
646	VMUNBLOCK
647	return VMRECORD(addr);
648}
649
650#if __STD_C
651extern int posix_memalign(reg Void_t **memptr, reg size_t align, reg size_t size)
652#else
653extern int posix_memalign(memptr, align, size)
654reg Void_t**	memptr;
655reg size_t	align;
656reg size_t	size;
657#endif
658{
659	Void_t	*mem;
660
661	if(align == 0 || (align%sizeof(Void_t*)) != 0 || ((align-1)&align) != 0 )
662		return EINVAL;
663
664	if(!(mem = memalign(align, size)) )
665		return ENOMEM;
666
667	*memptr = mem;
668	return 0;
669}
670
671#if __STD_C
672extern Void_t* valloc(reg size_t size)
673#else
674extern Void_t* valloc(size)
675reg size_t	size;
676#endif
677{
678	VMFLINIT();
679
680	GETPAGESIZE(_Vmpagesize);
681	return VMRECORD(memalign(_Vmpagesize, size));
682}
683
684#if __STD_C
685extern Void_t* pvalloc(reg size_t size)
686#else
687extern Void_t* pvalloc(size)
688reg size_t	size;
689#endif
690{
691	VMFLINIT();
692
693	GETPAGESIZE(_Vmpagesize);
694	return VMRECORD(memalign(_Vmpagesize, ROUND(size,_Vmpagesize)) );
695}
696
697#if !_PACKAGE_ast
698#if __STD_C
699char* strdup(const char* s)
700#else
701char* strdup(s)
702char*	s;
703#endif
704{
705	char	*ns;
706	size_t	n;
707
708	if(!s)
709		return NIL(char*);
710	else
711	{	n = strlen(s);
712		if((ns = malloc(n+1)) )
713			memcpy(ns,s,n+1);
714		return ns;
715	}
716}
717#endif /* _PACKAGE_ast */
718
719#if !_lib_alloca || _mal_alloca
720#ifndef _stk_down
721#define _stk_down	0
722#endif
723typedef struct _alloca_s	Alloca_t;
724union _alloca_u
725{	struct
726	{	char*		addr;
727		Alloca_t*	next;
728	} head;
729	char	array[ALIGN];
730};
731struct _alloca_s
732{	union _alloca_u	head;
733	Vmuchar_t	data[1];
734};
735
736#if __STD_C
737extern Void_t* alloca(size_t size)
738#else
739extern Void_t* alloca(size)
740size_t	size;
741#endif
742{	char		array[ALIGN];
743	char*		file;
744	int		line;
745	Void_t*		func;
746	Alloca_t*	f;
747	Vmalloc_t	*vm;
748	static Alloca_t* Frame;
749
750	VMFLINIT();
751
752	VMFLF(Vmregion,file,line,func); /* save info before freeing frames */
753
754	while(Frame) /* free unused frames */
755	{	if(( _stk_down && &array[0] > Frame->head.head.addr) ||
756		   (!_stk_down && &array[0] < Frame->head.head.addr) )
757		{	f = Frame; Frame = f->head.head.next;
758			if((vm = regionof(f)) )
759				(void)(*vm->meth.freef)(vm, f, 0);
760			/* else: something bad happened. just keep going */
761		}
762		else	break;
763	}
764
765	Vmregion->file = file; /* restore file/line info before allocation */
766	Vmregion->line = line;
767	Vmregion->func = func;
768
769	f = (Alloca_t*)(*Vmregion->meth.allocf)(Vmregion, size+sizeof(Alloca_t)-1, 0);
770
771	/* if f is NULL, this mimics a stack overflow with a memory error! */
772	f->head.head.addr = &array[0];
773	f->head.head.next = Frame;
774	Frame = f;
775
776	return (Void_t*)f->data;
777}
778#endif /*!_lib_alloca || _mal_alloca*/
779
780#if _map_malloc
781
782/* not sure of all the implications -- 0 is conservative for now */
783#define USE_NATIVE	0	/* native free/realloc on non-vmalloc ptrs */
784
785#else
786
787#if _malloc_hook
788
789static void vm_free_hook(void* ptr, const void* caller)
790{
791	free(ptr);
792}
793
794static void* vm_malloc_hook(size_t size, const void* caller)
795{
796	void*	r;
797
798	r = malloc(size);
799	return r;
800}
801
802static void* vm_memalign_hook(size_t align, size_t size, const void* caller)
803{
804	void*	r;
805
806	r = memalign(align, size);
807	return r;
808}
809
810static void* vm_realloc_hook(void* ptr, size_t size, const void* caller)
811{
812	void*	r;
813
814	r = realloc(ptr, size);
815	return r;
816}
817
818static void vm_initialize_hook(void)
819{
820	__free_hook = vm_free_hook;
821	__malloc_hook = vm_malloc_hook;
822	__memalign_hook = vm_memalign_hook;
823	__realloc_hook = vm_realloc_hook;
824}
825
826void	(*__malloc_initialize_hook)(void) = vm_initialize_hook;
827
828#if 0 /* 2012-02-29 this may be needed to cover shared libs */
829
830void __attribute__ ((constructor)) vm_initialize_initialize_hook(void)
831{
832	vm_initialize_hook();
833	__malloc_initialize_hook = vm_initialize_hook;
834}
835
836#endif
837
838#else
839
840/* intercept _* __* __libc_* variants */
841
842#if __lib__malloc
843extern Void_t*	F2(_calloc, size_t,n, size_t,m) { return calloc(n, m); }
844extern Void_t	F1(_cfree, Void_t*,p) { free(p); }
845extern Void_t	F1(_free, Void_t*,p) { free(p); }
846extern Void_t*	F1(_malloc, size_t,n) { return malloc(n); }
847#if _lib_memalign
848extern Void_t*	F2(_memalign, size_t,a, size_t,n) { return memalign(a, n); }
849#endif
850#if _lib_pvalloc
851extern Void_t*	F1(_pvalloc, size_t,n) { return pvalloc(n); }
852#endif
853extern Void_t*	F2(_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
854#if _lib_valloc
855extern Void_t*	F1(_valloc, size_t,n) { return valloc(n); }
856#endif
857#endif
858
859#if _lib___malloc
860extern Void_t*	F2(__calloc, size_t,n, size_t,m) { return calloc(n, m); }
861extern Void_t	F1(__cfree, Void_t*,p) { free(p); }
862extern Void_t	F1(__free, Void_t*,p) { free(p); }
863extern Void_t*	F1(__malloc, size_t,n) { return malloc(n); }
864#if _lib_memalign
865extern Void_t*	F2(__memalign, size_t,a, size_t,n) { return memalign(a, n); }
866#endif
867#if _lib_pvalloc
868extern Void_t*	F1(__pvalloc, size_t,n) { return pvalloc(n); }
869#endif
870extern Void_t*	F2(__realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
871#if _lib_valloc
872extern Void_t*	F1(__valloc, size_t,n) { return valloc(n); }
873#endif
874#endif
875
876#if _lib___libc_malloc
877extern Void_t*	F2(__libc_calloc, size_t,n, size_t,m) { return calloc(n, m); }
878extern Void_t	F1(__libc_cfree, Void_t*,p) { free(p); }
879extern Void_t	F1(__libc_free, Void_t*,p) { free(p); }
880extern Void_t*	F1(__libc_malloc, size_t,n) { return malloc(n); }
881#if _lib_memalign
882extern Void_t*	F2(__libc_memalign, size_t,a, size_t,n) { return memalign(a, n); }
883#endif
884#if _lib_pvalloc
885extern Void_t*	F1(__libc_pvalloc, size_t,n) { return pvalloc(n); }
886#endif
887extern Void_t*	F2(__libc_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
888#if _lib_valloc
889extern Void_t*	F1(__libc_valloc, size_t,n) { return valloc(n); }
890#endif
891#endif
892
893#endif /* _malloc_hook */
894
895#endif /* _map_malloc */
896
897#undef	extern
898
899#if _hdr_malloc /* need the mallint interface for statistics, etc. */
900
901#undef	calloc
902#define calloc		______calloc
903#undef	cfree
904#define cfree		______cfree
905#undef	free
906#define free		______free
907#undef	malloc
908#define malloc		______malloc
909#undef	pvalloc
910#define pvalloc		______pvalloc
911#undef	realloc
912#define realloc		______realloc
913#undef	valloc
914#define valloc		______valloc
915
916#if !_UWIN
917
918#include	<malloc.h>
919
920typedef struct mallinfo Mallinfo_t;
921typedef struct mstats Mstats_t;
922
923#endif
924
925#if defined(__EXPORT__)
926#define extern		__EXPORT__
927#endif
928
929#if _lib_mallopt
930#if __STD_C
931extern int mallopt(int cmd, int value)
932#else
933extern int mallopt(cmd, value)
934int	cmd;
935int	value;
936#endif
937{
938	VMFLINIT();
939	return 0;
940}
941#endif /*_lib_mallopt*/
942
943#if _lib_mallinfo && _mem_arena_mallinfo
944#if __STD_C
945extern Mallinfo_t mallinfo(void)
946#else
947extern Mallinfo_t mallinfo()
948#endif
949{
950	Vmstat_t	sb;
951	Mallinfo_t	mi;
952
953	VMFLINIT();
954	memset(&mi,0,sizeof(mi));
955	if(vmstat(Vmregion,&sb) >= 0)
956	{	mi.arena = sb.extent;
957		mi.ordblks = sb.n_busy+sb.n_free;
958		mi.uordblks = sb.s_busy;
959		mi.fordblks = sb.s_free;
960	}
961	return mi;
962}
963#endif /* _lib_mallinfo */
964
965#if _lib_mstats && _mem_bytes_total_mstats
966#if __STD_C
967extern Mstats_t mstats(void)
968#else
969extern Mstats_t mstats()
970#endif
971{
972	Vmstat_t	sb;
973	Mstats_t	ms;
974
975	VMFLINIT();
976	memset(&ms,0,sizeof(ms));
977	if(vmstat(Vmregion,&sb) >= 0)
978	{	ms.bytes_total = sb.extent;
979		ms.chunks_used = sb.n_busy;
980		ms.bytes_used = sb.s_busy;
981		ms.chunks_free = sb.n_free;
982		ms.bytes_free = sb.s_free;
983	}
984	return ms;
985}
986#endif /*_lib_mstats*/
987
988#undef	extern
989
990#endif/*_hdr_malloc*/
991
992#else
993
994/*
995 * even though there is no malloc override, still provide
996 * _ast_* counterparts for object compatibility
997 */
998
999#define setregmax(n)
1000
1001#undef	calloc
1002extern Void_t*	calloc _ARG_((size_t, size_t));
1003
1004#undef	cfree
1005extern void	cfree _ARG_((Void_t*));
1006
1007#undef	free
1008extern void	free _ARG_((Void_t*));
1009
1010#undef	malloc
1011extern Void_t*	malloc _ARG_((size_t));
1012
1013#if _lib_memalign
1014#undef	memalign
1015extern Void_t*	memalign _ARG_((size_t, size_t));
1016#endif
1017
1018#if _lib_pvalloc
1019#undef	pvalloc
1020extern Void_t*	pvalloc _ARG_((size_t));
1021#endif
1022
1023#undef	realloc
1024extern Void_t*	realloc _ARG_((Void_t*, size_t));
1025
1026#if _lib_valloc
1027#undef	valloc
1028extern Void_t*	valloc _ARG_((size_t));
1029#endif
1030
1031#if defined(__EXPORT__)
1032#define extern		__EXPORT__
1033#endif
1034
1035#if !_malloc_hook
1036
1037extern Void_t	F1(_ast_free, Void_t*,p) { free(p); }
1038extern Void_t*	F1(_ast_malloc, size_t,n) { return malloc(n); }
1039#if _lib_memalign
1040extern Void_t*	F2(_ast_memalign, size_t,a, size_t,n) { return memalign(a, n); }
1041#endif
1042extern Void_t*	F2(_ast_realloc, Void_t*,p, size_t,n) { return realloc(p, n); }
1043
1044#endif
1045
1046extern Void_t*	F2(_ast_calloc, size_t,n, size_t,m) { return calloc(n, m); }
1047extern Void_t	F1(_ast_cfree, Void_t*,p) { free(p); }
1048#if _lib_pvalloc
1049extern Void_t*	F1(_ast_pvalloc, size_t,n) { return pvalloc(n); }
1050#endif
1051#if _lib_valloc
1052extern Void_t*	F1(_ast_valloc, size_t,n) { return valloc(n); }
1053#endif
1054
1055#undef	extern
1056
1057#if _hdr_malloc
1058
1059#undef	mallinfo
1060#undef	mallopt
1061#undef	mstats
1062
1063#define calloc		______calloc
1064#define cfree		______cfree
1065#define free		______free
1066#define malloc		______malloc
1067#define pvalloc		______pvalloc
1068#define realloc		______realloc
1069#define valloc		______valloc
1070
1071#if !_UWIN
1072
1073#if !_malloc_hook
1074
1075#include	<malloc.h>
1076
1077#endif
1078
1079typedef struct mallinfo Mallinfo_t;
1080typedef struct mstats Mstats_t;
1081
1082#endif
1083
1084#if defined(__EXPORT__)
1085#define extern		__EXPORT__
1086#endif
1087
1088#if _lib_mallopt
1089extern int	F2(_ast_mallopt, int,cmd, int,value) { return mallopt(cmd, value); }
1090#endif
1091
1092#if _lib_mallinfo && _mem_arena_mallinfo
1093extern Mallinfo_t	F0(_ast_mallinfo, void) { return mallinfo(); }
1094#endif
1095
1096#if _lib_mstats && _mem_bytes_total_mstats
1097extern Mstats_t		F0(_ast_mstats, void) { return mstats(); }
1098#endif
1099
1100#undef	extern
1101
1102#endif /*_hdr_malloc*/
1103
1104#endif /*!_std_malloc*/
1105
1106#if __STD_C
1107static Vmulong_t atou(char** sp)
1108#else
1109static Vmulong_t atou(sp)
1110char**	sp;
1111#endif
1112{
1113	char*		s = *sp;
1114	Vmulong_t	v = 0;
1115
1116	if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X') )
1117	{	for(s += 2; *s; ++s)
1118		{	if(*s >= '0' && *s <= '9')
1119				v = (v << 4) + (*s - '0');
1120			else if(*s >= 'a' && *s <= 'f')
1121				v = (v << 4) + (*s - 'a') + 10;
1122			else if(*s >= 'A' && *s <= 'F')
1123				v = (v << 4) + (*s - 'A') + 10;
1124			else break;
1125		}
1126	}
1127	else
1128	{	for(; *s; ++s)
1129		{	if(*s >= '0' && *s <= '9')
1130				v = v*10 + (*s - '0');
1131			else break;
1132		}
1133	}
1134
1135	*sp = s;
1136	return v;
1137}
1138
1139#if __STD_C
1140static char* insertpid(char* begs, char* ends)
1141#else
1142static char* insertpid(begs,ends)
1143char*	begs;
1144char*	ends;
1145#endif
1146{	int	pid;
1147	char*	s;
1148
1149	if((pid = getpid()) < 0)
1150		return NIL(char*);
1151
1152	s = ends;
1153	do
1154	{	if(s == begs)
1155			return NIL(char*);
1156		*--s = '0' + pid%10;
1157	} while((pid /= 10) > 0);
1158	while(s < ends)
1159		*begs++ = *s++;
1160
1161	return begs;
1162}
1163
1164#define FD_PRIVATE	(3*OPEN_MAX/4)
1165
1166#if __STD_C
1167int _vmfd(int fd)
1168#else
1169int _vmfd(fd)
1170int	fd;
1171#endif
1172{
1173	int	pd;
1174
1175	if (fd >= 0)
1176	{
1177		if (fd < FD_PRIVATE && (pd = fcntl(fd, F_DUPFD, FD_PRIVATE)) >= 0)
1178		{
1179			close(fd);
1180			fd = pd;
1181		}
1182#ifdef FD_CLOEXEC
1183		fcntl(fd,  F_SETFD, FD_CLOEXEC);
1184#endif
1185	}
1186	return fd;
1187}
1188
1189#if __STD_C
1190static int createfile(char* file)
1191#else
1192static int createfile(file)
1193char*	file;
1194#endif
1195{
1196	char	buf[1024];
1197	char	*next, *endb;
1198	int	fd;
1199
1200	next = buf;
1201	endb = buf + sizeof(buf);
1202	while(*file)
1203	{	if(*file == '%')
1204		{	switch(file[1])
1205			{
1206			case 'p' :
1207				if(!(next = insertpid(next,endb)) )
1208					return -1;
1209				file += 2;
1210				break;
1211			default :
1212				goto copy;
1213			}
1214		}
1215		else
1216		{ copy:
1217			*next++ = *file++;
1218		}
1219
1220		if(next >= endb)
1221			return -1;
1222	}
1223
1224	*next = '\0';
1225	file = buf;
1226	if (*file == '&' && *(file += 1) || strncmp(file, "/dev/fd/", 8) == 0 && *(file += 8))
1227		fd = dup((int)atou(&file));
1228	else if (*file)
1229	{
1230#if _PACKAGE_ast
1231		fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, CREAT_MODE);
1232#else
1233		fd = creat(file, CREAT_MODE);
1234#endif
1235		fd = _vmfd(fd);
1236	}
1237	else
1238		return -1;
1239#if _PACKAGE_ast
1240#ifdef FD_CLOEXEC
1241	if (fd >= 0)
1242		fcntl(fd, F_SETFD, FD_CLOEXEC);
1243#endif
1244#endif
1245	return fd;
1246}
1247
1248#if __STD_C
1249static void pfprint(void)
1250#else
1251static void pfprint()
1252#endif
1253{
1254	if(Vmregion->meth.meth == VM_MTPROFILE)
1255		vmprofile(Vmregion,_Vmpffd);
1256}
1257
1258/*
1259 * initialize runtime options from the VMALLOC_OPTIONS env var
1260 */
1261
1262#define COPY(t,e,f)	while ((*t = *f++) && t < e) t++
1263
1264#if __STD_C
1265void _vmoptions(void)
1266#else
1267void _vmoptions()
1268#endif
1269{
1270	Vmalloc_t*	vm = 0;
1271	char*		trace = 0;
1272	char*		s;
1273	char*		t;
1274	char*		v;
1275	Vmulong_t	n;
1276	int		fd;
1277	char		buf[1024];
1278
1279	_Vmoptions = 1;
1280	t = buf;
1281	v = &buf[sizeof(buf)-1];
1282	if (s = getenv("VMALLOC_OPTIONS"))
1283		COPY(t, v, s);
1284	if (t > buf)
1285	{
1286		*t = 0;
1287		s = buf;
1288		for (;;)
1289		{
1290			while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
1291				s++;
1292			if (!*(t = s))
1293				break;
1294			v = 0;
1295			while (*s)
1296				if (*s == ',' || *s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
1297				{
1298					*s++ = 0;
1299					break;
1300				}
1301				else if (!v && *s == '=')
1302				{
1303					*s++ = 0;
1304					if (!*(v = s))
1305						v = 0;
1306				}
1307				else
1308					s++;
1309			if (t[0] == 'n' && t[1] == 'o')
1310				continue;
1311			switch (t[0])
1312			{
1313			case 'a':		/* abort */
1314				if (!vm)
1315					vm = vmopen(Vmdcsystem, Vmdebug, 0);
1316				if (vm && vm->meth.meth == VM_MTDEBUG)
1317					vmset(vm, VM_DBABORT, 1);
1318				else
1319					_Vmassert |= VM_abort;
1320				break;
1321			case 'b':		/* break */
1322				_Vmassert |= VM_break;
1323				break;
1324			case 'c':		/* check */
1325				_Vmassert |= VM_check;
1326				break;
1327			case 'f':		/* free */
1328				_Vmassert |= VM_free;
1329				break;
1330			case 'k':		/* keep */
1331				_Vmassert |= VM_keep;
1332				break;
1333			case 'm':
1334				if (v)
1335					switch (t[1])
1336					{
1337					case 'e': /* method=METHOD */
1338						if (!vm)
1339						{
1340							if ((v[0] == 'V' || v[0] == 'v') && (v[1] == 'M' || v[1] == 'm'))
1341								v += 2;
1342							if (strcmp(v, "debug") == 0)
1343								vm = vmopen(Vmdcsystem, Vmdebug, 0);
1344							else if (strcmp(v, "profile") == 0)
1345								vm = vmopen(Vmdcsystem, Vmprofile, 0);
1346							else if (strcmp(v, "last") == 0)
1347								vm = vmopen(Vmdcsystem, Vmlast, 0);
1348							else if (strcmp(v, "best") == 0)
1349								vm = Vmheap;
1350						}
1351						break;
1352					case 'm': /* mmap */
1353						_Vmassert |= VM_mmap;
1354						break;
1355					}
1356				break;
1357			case 'p':
1358				if (v)
1359					switch (t[1])
1360					{
1361					case 'e':	/* period=<count> */
1362						if (!vm)
1363							vm = vmopen(Vmdcsystem, Vmdebug, 0);
1364						if (vm && vm->meth.meth == VM_MTDEBUG)
1365							_Vmdbcheck = atou(&v);
1366						break;
1367					case 'r':	/* profile=<path> */
1368						if (!vm)
1369							vm = vmopen(Vmdcsystem, Vmprofile, 0);
1370						if (v && vm && vm->meth.meth == VM_MTPROFILE)
1371							_Vmpffd = createfile(v);
1372						break;
1373					}
1374				break;
1375			case 's':		/* start=<count> */
1376				if (!vm)
1377					vm = vmopen(Vmdcsystem, Vmdebug, 0);
1378				if (v && vm && vm->meth.meth == VM_MTDEBUG)
1379					_Vmdbstart = atou(&v);
1380				break;
1381			case 't':		/* trace=<path> */
1382				trace = v;
1383				break;
1384			case 'w':
1385				if (t[1] == 'a')
1386					switch (t[2])
1387					{
1388					case 'r':	/* warn=<path> */
1389						if (!vm)
1390							vm = vmopen(Vmdcsystem, Vmdebug, 0);
1391						if (v && vm && vm->meth.meth == VM_MTDEBUG && (fd = createfile(v)) >= 0)
1392							vmdebug(fd);
1393						break;
1394					case 't':	/* watch=<addr> */
1395						if (!vm)
1396							vm = vmopen(Vmdcsystem, Vmdebug, 0);
1397						if (v && vm && vm->meth.meth == VM_MTDEBUG && (n = atou(&v)) >= 0)
1398							vmdbwatch((Void_t*)n);
1399						break;
1400					}
1401				break;
1402			}
1403		}
1404	}
1405
1406	/* slip in the new region now so that malloc() will work fine */
1407
1408	if (vm)
1409	{
1410		if (vm->meth.meth == VM_MTDEBUG)
1411			_Vmdbcheck = 1;
1412		Vmregion = vm;
1413	}
1414
1415	/* enable tracing -- this currently disables multiple regions */
1416
1417	if (trace)
1418	{
1419		setregmax(0);
1420		if ((fd = createfile(trace)) >= 0)
1421		{
1422			vmset(Vmregion, VM_TRACE, 1);
1423			vmtrace(fd);
1424		}
1425	}
1426	else if (Vmregion != Vmheap || asometh(0, 0)->type == ASO_SIGNAL)
1427		setregmax(0);
1428
1429	/* make sure that profile data is output upon exiting */
1430
1431	if (vm && vm->meth.meth == VM_MTPROFILE)
1432	{
1433		if (_Vmpffd < 0)
1434			_Vmpffd = 2;
1435		/* this may wind up calling malloc(), but region is ok now */
1436		atexit(pfprint);
1437	}
1438	else if (_Vmpffd >= 0)
1439	{
1440		close(_Vmpffd);
1441		_Vmpffd = -1;
1442	}
1443}
1444
1445/*
1446 * ast semi-private workaround for system functions
1447 * that misbehave by passing bogus addresses to free()
1448 *
1449 * not prototyped in any header to keep it ast semi-private
1450 *
1451 * to keep malloc() data by disabling free()
1452 *	extern _vmkeep(int);
1453 *	int r = _vmkeep(1);
1454 * and to restore to the previous state
1455 *	(void)_vmkeep(r);
1456 */
1457
1458int
1459#if __STD_C
1460_vmkeep(int v)
1461#else
1462_vmkeep(v)
1463int	v;
1464#endif
1465{
1466	int	r;
1467
1468	r = !!(_Vmassert & VM_keep);
1469	if (v)
1470		_Vmassert |= VM_keep;
1471	else
1472		_Vmassert &= ~VM_keep;
1473	return r;
1474}
1475
1476#endif /*_UWIN*/
1477