From 220a95cdfbfb7e7c274d1ce34e62430bcc3011cf Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Tue, 9 Feb 2021 16:22:13 -0500 Subject: [PATCH] commit bash-20210206 snapshot --- CWRU/CWRU.chlog | 19 +++++ config.h.in | 3 + configure | 33 ++++++++ configure.ac | 1 + general.c | 3 + lib/malloc/malloc.c | 196 ++++++++++++++++++++++++++++++-------------- lib/malloc/mstats.h | 2 +- tests/RUN-ONE-TEST | 2 +- 8 files changed, 195 insertions(+), 64 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 308bb7ca..f78ee80f 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -9536,3 +9536,22 @@ array.h,variables.c variables.c - pop_args: a couple of code simplifications + + 2/7 + --- +lib/malloc/malloc.c + - pagesz: at least MALLOC_PAGESIZE_MIN (4096) bytes + - union mhead: now 16-byte aligned on all systems, 32-bit and 64-bit + pointers + - binsizes: since the smallest allocation overhead is now 16 bytes, + redo the buckets so binsizes[0] == 32; adjust the thresholds for + split/coalesce/prepopulate/mmap (NBUCKETS = 28; STARTBUCK = 0). + Sizes stay pretty much the same; indices change + - consistently use MALLOC_SIZE_T instead of long/unsigned int/int + - use MAGIC8_NUMBYTES as the length of the mh_magic8 buffer, in case + it changes later for alignment + - internal_remap: new function, calls mremap to reallocate a chunk of + memory allocated using mmap(); called from internal_realloc if the + old size and new size are both bigger than the mmap threshold + - internal_realloc: call internal_remap if the old size and new size + are both above the threshold where we use mmap for allocation diff --git a/config.h.in b/config.h.in index ab316d46..872a7fb0 100644 --- a/config.h.in +++ b/config.h.in @@ -233,6 +233,9 @@ /* The number of bytes in a pointer to char. */ #undef SIZEOF_CHAR_P +/* The number of bytes in a size_t. */ +#undef SIZEOF_SIZE_T + /* The number of bytes in a double (hopefully 8). */ #undef SIZEOF_DOUBLE diff --git a/configure b/configure index c449b254..704b62ed 100755 --- a/configure +++ b/configure @@ -16440,6 +16440,39 @@ cat >>confdefs.h <<_ACEOF _ACEOF +# The cast to long int works around a bug in the HP C Compiler +# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects +# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. +# This bug is HP SR number 8606223364. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5 +$as_echo_n "checking size of size_t... " >&6; } +if ${ac_cv_sizeof_size_t+:} false; then : + $as_echo_n "(cached) " >&6 +else + if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"; then : + +else + if test "$ac_cv_type_size_t" = yes; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot compute sizeof (size_t) +See \`config.log' for more details" "$LINENO" 5; } + else + ac_cv_sizeof_size_t=0 + fi +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5 +$as_echo "$ac_cv_sizeof_size_t" >&6; } + + + +cat >>confdefs.h <<_ACEOF +#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t +_ACEOF + + # The cast to long int works around a bug in the HP C Compiler # version HP92453-01 B.11.11.23709.GP, which incorrectly rejects # declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'. diff --git a/configure.ac b/configure.ac index 959ca097..e47aed24 100644 --- a/configure.ac +++ b/configure.ac @@ -970,6 +970,7 @@ AC_CHECK_SIZEOF(short, 2) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(long, 4) AC_CHECK_SIZEOF(char *, 4) +AC_CHECK_SIZEOF(size_t, 4) AC_CHECK_SIZEOF(double, 8) AC_CHECK_SIZEOF([long long], 8) diff --git a/general.c b/general.c index 50d52167..87211f22 100644 --- a/general.c +++ b/general.c @@ -683,6 +683,9 @@ check_binary_file (sample, sample_len) register int i; unsigned char c; + if (sample_len >= 4 && sample[0] == 0x7f && sample[1] == 'E' && sample[2] == 'L' && sample[3] == 'F') + return 1; + for (i = 0; i < sample_len; i++) { c = sample[i]; diff --git a/lib/malloc/malloc.c b/lib/malloc/malloc.c index 439f8ef1..60d0fa5f 100644 --- a/lib/malloc/malloc.c +++ b/lib/malloc/malloc.c @@ -47,10 +47,10 @@ */ /* - * nextf[i] is the pointer to the next free block of size 2^(i+3). The - * smallest allocatable block is 8 bytes. The overhead information will - * go in the first int of the block, and the returned pointer will point - * to the second. + * nextf[i] is the pointer to the next free block of size 2^(i+5). The + * smallest allocatable block is 32 bytes. The overhead information will + * go in the first 16 bytes of the block, and the returned pointer will point + * to the rest. */ /* Define MEMSCRAMBLE to have free() write 0xcf into memory as it's freed, to @@ -121,8 +121,8 @@ # define NO_VALLOC #endif -/* SIZEOF_LONG * 4 - 2, usable bins from 1..NBUCKETS-1 */ -#define NBUCKETS 30 +#define MALLOC_PAGESIZE_MIN 4096 +#define MALLOC_INCR_PAGES 8192 #define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */ #define ISFREE ((char) 0x54) /* magic byte that implies free block */ @@ -140,20 +140,14 @@ enough room in the block for the new size. Range checking is always done. */ union mhead { -#if SIZEOF_CHAR_P == 8 bits64_t mh_align[2]; /* 16 */ -#else - bits64_t mh_align; /* 8 */ -#endif struct { char mi_alloc; /* ISALLOC or ISFREE */ /* 1 */ char mi_index; /* index in nextf[] */ /* 1 */ /* Remainder are valid only when block is allocated */ u_bits16_t mi_magic2; /* should be == MAGIC2 */ /* 2 */ u_bits32_t mi_nbytes; /* # of bytes allocated */ /* 4 */ -#if SIZEOF_CHAR_P == 8 char mi_magic8[8]; /* MAGIC1 guard bytes */ /* 8 */ -#endif } minfo; }; #define mh_alloc minfo.mi_alloc @@ -162,14 +156,14 @@ union mhead { #define mh_magic2 minfo.mi_magic2 #define mh_magic8 minfo.mi_magic8 +#define MAGIC8_NUMBYTES 8 +#define MALLOC_SIZE_T u_bits32_t + #define MOVERHEAD sizeof(union mhead) -#if SIZEOF_CHAR_P == 8 -#define MALIGN_MASK 15 -#else -#define MALIGN_MASK 7 /* one less than desired alignment */ -#endif +#define MALIGN_MASK 15 /* one less than desired alignment */ +/* Guard bytes we write at the end of the allocation, encoding the size. */ typedef union _malloc_guard { char s[4]; u_bits32_t i; @@ -181,6 +175,8 @@ typedef union _malloc_guard { because we want sizeof (union mhead) to describe the overhead for when the block is in use, and we do not want the free-list pointer to count in that. */ +/* If we have mmap, this is not used for chunks larger than mmap_threshold, + since we munmap immediately on free(). */ /* If SIZEOF_CHAR_P == 8, this goes into the mh_magic8 buffer at the end of the rest of the struct. This may need adjusting. */ @@ -194,10 +190,11 @@ typedef union _malloc_guard { /* Written in the bytes before the block's real space (-SIZEOF_CHAR_P bytes) */ #define MAGIC1 0x55 #define MAGIC2 0x5555 -#define MSLOP 4 /* 4 bytes extra for u_bits32_t size */ + +#define MSLOP 4 /* 4 bytes extra for u_bits32_t end guard size */ /* How many bytes are actually allocated for a request of size N -- - rounded up to nearest multiple of 2*SIZEOF_CHAR_P after accounting for + rounded up to nearest multiple of 16 (alignment) after accounting for malloc overhead. */ #define ALLOCATED_BYTES(n) \ (((n) + MOVERHEAD + MSLOP + MALIGN_MASK) & ~MALIGN_MASK) @@ -211,18 +208,22 @@ typedef union _malloc_guard { /* Minimum and maximum bucket indices for block splitting (and to bound the search for a block to split). */ -#define SPLIT_MIN 2 /* XXX - was 3 */ -#define SPLIT_MID 11 -#define SPLIT_MAX 14 +#define SPLIT_MIN 1 /* 64 */ +#define SPLIT_MID 9 /* 16384 */ +#define SPLIT_MAX 12 /* 131072 */ /* Minimum and maximum bucket indices for block coalescing. */ -#define COMBINE_MIN 2 -#define COMBINE_MAX (pagebucket - 1) /* XXX */ +#define COMBINE_MIN 1 /* 64 */ +#define COMBINE_MAX (pagebucket - 1) /* 2048 for 4096-byte pages */ -#define LESSCORE_MIN 10 -#define LESSCORE_FRC 13 +#define LESSCORE_MIN 8 /* 8192 */ +#define LESSCORE_FRC 11 /* 65536 */ -#define STARTBUCK 1 +/* Which bin do we prepopulate with the initial sbrk memory? */ +#define PREPOP_BIN 1 +#define PREPOP_SIZE 64 + +#define STARTBUCK 0 /* Should we use mmap for large allocations? */ #if defined (HAVE_MMAP) @@ -232,15 +233,19 @@ typedef union _malloc_guard { #endif #if defined (HAVE_MMAP) && defined (MAP_ANONYMOUS) -# define USE_MMAP +# define USE_MMAP 1 #endif #if defined (USE_MMAP) -# define MMAP_THRESHOLD 14 /* must be >= SPLIT_MAX, COMBINE_MAX */ +# define MMAP_THRESHOLD 12 /* must be >= SPLIT_MAX, COMBINE_MAX */ #else # define MMAP_THRESHOLD (8 * SIZEOF_LONG) #endif +/* usable bins from STARTBUCK..NBUCKETS-1 */ + +#define NBUCKETS 28 + /* Flags for the internal functions. */ #define MALLOC_WRAPPER 0x01 /* wrapper function */ #define MALLOC_INTERNAL 0x02 /* internal function calling another */ @@ -265,7 +270,7 @@ typedef union _malloc_guard { #define RIGHT_BUCKET(nb, nu) \ (((nb) > binsizes[(nu)-1]) && ((nb) <= binsizes[(nu)])) -/* nextf[i] is free list of blocks of size 2**(i + 3) */ +/* nextf[i] is free list of blocks of size 2**(i + 5) */ static union mhead *nextf[NBUCKETS]; @@ -280,16 +285,18 @@ static int maxbuck; /* highest bucket receiving allocation request. */ static char *memtop; /* top of heap */ static const unsigned long binsizes[NBUCKETS] = { - 8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL, + 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL, 8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL, 1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL, 67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL, 2147483648UL, 4294967295UL }; -/* binsizes[x] == (1 << ((x) + 3)) */ +/* binsizes[x] == (1 << ((x) + 5)) */ #define binsize(x) binsizes[(x)] +#define MAXALLOC_SIZE binsizes[NBUCKETS-1] + #if !defined (errno) extern int errno; #endif @@ -306,6 +313,7 @@ static void internal_cfree PARAMS((PTR_T, const char *, int, int)); #ifndef NO_VALLOC static PTR_T internal_valloc PARAMS((size_t, const char *, int, int)); #endif +static PTR_T internal_remap PARAMS((PTR_T, size_t, int, int)); #if defined (botch) extern void botch (); @@ -741,14 +749,6 @@ malloc_debug_dummy () write (1, "malloc_debug_dummy\n", 19); } -#if SIZEOF_CHAR_P == 8 -#define PREPOP_BIN 3 -#define PREPOP_SIZE 64 -#else -#define PREPOP_BIN 2 -#define PREPOP_SIZE 32 -#endif - static int pagealign () { @@ -758,8 +758,8 @@ pagealign () char *curbrk; pagesz = getpagesize (); - if (pagesz < 1024) - pagesz = 1024; + if (pagesz < MALLOC_PAGESIZE_MIN) + pagesz = MALLOC_PAGESIZE_MIN; /* OK, how much do we need to allocate to make things page-aligned? Some of this partial page will be wasted space, but we'll use as @@ -825,7 +825,7 @@ internal_malloc (n, file, line, flags) /* get a block */ register union mhead *p; register int nunits; register char *m, *z; - long nbytes; + MALLOC_SIZE_T nbytes; mguard_t mg; /* Get the system page size and align break pointer so future sbrks will @@ -839,6 +839,10 @@ internal_malloc (n, file, line, flags) /* get a block */ multiple of 8, then figure out which nextf[] area to use. Try to be smart about where to start searching -- if the number of bytes needed is greater than the page size, we can start at pagebucket. */ +#if SIZEOF_SIZE_T == 8 + if (ALLOCATED_BYTES(n) > MAXALLOC_SIZE) + return ((PTR_T) NULL); +#endif nbytes = ALLOCATED_BYTES(n); nunits = (nbytes <= (pagesz >> 1)) ? STARTBUCK : pagebucket; for ( ; nunits < NBUCKETS; nunits++) @@ -886,10 +890,8 @@ internal_malloc (n, file, line, flags) /* get a block */ p->mh_magic2 = MAGIC2; p->mh_nbytes = n; -#if SIZEOF_CHAR_P == 8 /* Begin guard */ - MALLOC_MEMSET ((char *)p->mh_magic8, MAGIC1, 8); -#endif + MALLOC_MEMSET ((char *)p->mh_magic8, MAGIC1, MAGIC8_NUMBYTES); /* End guard */ mg.i = n; @@ -945,8 +947,8 @@ internal_free (mem, file, line, flags) register union mhead *p; register char *ap, *z; register int nunits; - register unsigned int nbytes; - int ubytes; /* caller-requested size */ + register MALLOC_SIZE_T nbytes; + MALLOC_SIZE_T ubytes; /* caller-requested size */ mguard_t mg; if ((ap = (char *)mem) == 0) @@ -979,28 +981,25 @@ internal_free (mem, file, line, flags) nunits = p->mh_index; nbytes = ALLOCATED_BYTES(p->mh_nbytes); - /* Since the sizeof(u_bits32_t) bytes before the memory handed to the user - are now used for the number of bytes allocated, a simple check of - mh_magic2 is no longer sufficient to catch things like p[-1] = 'x'. + /* The MAGIC8_NUMBYTES bytes before the memory handed to the user are now + used for a simple check to catch things like p[-1] = 'x'. We sanity-check the value of mh_nbytes against the size of the blocks in the appropriate bucket before we use it. This can still cause problems and obscure errors if mh_nbytes is wrong but still within range; the checks against the size recorded at the end of the chunk will probably - fail then. Using MALLOC_REGISTER will help here, since it saves the + fail then. Using MALLOC_REGISTER will help here, since it saves the original number of bytes requested. */ if (IN_BUCKET(nbytes, nunits) == 0) xbotch (mem, ERR_UNDERFLOW, _("free: underflow detected; mh_nbytes out of range"), file, line); -#if SIZEOF_CHAR_P == 8 { int i; - for (i = 0, z = p->mh_magic8; i < 8; i++) + for (i = 0, z = p->mh_magic8; i < MAGIC8_NUMBYTES; i++) if (*z++ != MAGIC1) xbotch (mem, ERR_UNDERFLOW, _("free: underflow detected; magic8 corrupted"), file, line); } -#endif ap += p->mh_nbytes; z = mg.s; @@ -1084,6 +1083,58 @@ free_return: #endif } +#if USE_MMAP == 1 && defined (HAVE_MREMAP) +/* Assume the caller (internal_realloc) has already performed the sanity and + overflow tests. Basically we kill the old guard information, determine the + new size, call mremap with the new size, and add the bookkeeping and guard + information back in. */ +static PTR_T +internal_remap (mem, n, nunits, flags) + PTR_T mem; + register size_t n; + int nunits; + int flags; +{ + register union mhead *p, *np; + char *m, *z; + mguard_t mg; + MALLOC_SIZE_T nbytes; + + if (nunits >= NBUCKETS) /* Uh oh */ + return ((PTR_T) NULL); + + p = (union mhead *)mem - 1; + + m = (char *)mem + p->mh_nbytes; + z = mg.s; + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; /* erase guard */ + + nbytes = ALLOCATED_BYTES(n); + + busy[nunits] = 1; + np = (union mhead *)mremap (p, binsize (p->mh_index), binsize (nunits), MREMAP_MAYMOVE); + busy[nunits] = 0; + if (np == MAP_FAILED) + return (PTR_T)NULL; + + if (np != p) + { + np->mh_alloc = ISALLOC; + np->mh_magic2 = MAGIC2; + MALLOC_MEMSET ((char *)np->mh_magic8, MAGIC1, MAGIC8_NUMBYTES); + } + np->mh_index = nunits; + np->mh_nbytes = n; + + mg.i = n; + z = mg.s; + m = (char *)(np + 1) + n; + *m++ = *z++, *m++ = *z++, *m++ = *z++, *m++ = *z++; + + return ((PTR_T)(np + 1)); +} +#endif + static PTR_T internal_realloc (mem, n, file, line, flags) PTR_T mem; @@ -1092,9 +1143,9 @@ internal_realloc (mem, n, file, line, flags) int line, flags; { register union mhead *p; - register u_bits32_t tocopy; - register unsigned int nbytes; - register int nunits; + register MALLOC_SIZE_T tocopy; + register MALLOC_SIZE_T nbytes; + register int newunits, nunits; register char *m, *z; mguard_t mg; @@ -1132,16 +1183,14 @@ internal_realloc (mem, n, file, line, flags) if (IN_BUCKET(nbytes, nunits) == 0) xbotch (mem, ERR_UNDERFLOW, _("realloc: underflow detected; mh_nbytes out of range"), file, line); -#if SIZEOF_CHAR_P == 8 { int i; - for (i = 0, z = p->mh_magic8; i < 8; i++) + for (i = 0, z = p->mh_magic8; i < MAGIC8_NUMBYTES; i++) if (*z++ != MAGIC1) xbotch (mem, ERR_UNDERFLOW, _("realloc: underflow detected; magic8 corrupted"), file, line); } -#endif m = (char *)mem + (tocopy = p->mh_nbytes); z = mg.s; @@ -1161,6 +1210,10 @@ internal_realloc (mem, n, file, line, flags) if (n == p->mh_nbytes) return mem; +#if SIZEOF_SIZE_T == 8 + if (ALLOCATED_BYTES(n) > MAXALLOC_SIZE) + return ((PTR_T) NULL); +#endif /* See if desired size rounds to same power of 2 as actual size. */ nbytes = ALLOCATED_BYTES(n); @@ -1187,12 +1240,31 @@ internal_realloc (mem, n, file, line, flags) _mstats.nrcopy++; #endif - /* If we are using mmap and have mremap, we could use it here. */ + /* If we are using mmap and have mremap, we use it here. Make sure that + the old size and new size are above the threshold where we use mmap */ +#if USE_MMAP == 1 && defined (HAVE_MREMAP) + if (nbytes > p->mh_nbytes) + newunits = nunits; + else + newunits = (nbytes <= (pagesz >> 1)) ? STARTBUCK : pagebucket; + for ( ; newunits < NBUCKETS; newunits++) + if (nbytes <= binsize(newunits)) + break; + if (nunits > malloc_mmap_threshold && newunits > malloc_mmap_threshold) + { + m = internal_remap (mem, n, newunits, MALLOC_INTERNAL); + if (m == 0) + return 0; + } + else +#endif + { if ((m = internal_malloc (n, file, line, MALLOC_INTERNAL|MALLOC_NOTRACE|MALLOC_NOREG)) == 0) return 0; FASTCOPY (mem, m, tocopy); internal_free (mem, file, line, MALLOC_INTERNAL); + } #ifdef MALLOC_TRACE if (malloc_trace && (flags & MALLOC_NOTRACE) == 0) diff --git a/lib/malloc/mstats.h b/lib/malloc/mstats.h index ce8aaeca..07dffcc9 100644 --- a/lib/malloc/mstats.h +++ b/lib/malloc/mstats.h @@ -27,7 +27,7 @@ /* This needs to change if the definition in malloc.c changes */ #ifndef NBUCKETS -# define NBUCKETS 30 +# define NBUCKETS 28 #endif /* diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index c8bef8dd..0b063810 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/bash/bash-current +BUILD_DIR=/usr/local/build/chet/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR