From: "Mark E." To: djgpp-workers AT delorie DOT com Date: Sun, 7 Jan 2001 00:31:13 -0500 MIME-Version: 1.0 Content-type: text/plain; charset=US-ASCII Content-transfer-encoding: 7BIT Subject: memalign and valloc patch Message-ID: <3A57B8D1.31037.75E7A8@localhost> X-mailer: Pegasus Mail for Win32 (v3.12c) Reply-To: djgpp-workers AT delorie DOT com While testing memalign/valloc with gcc and then my own sample test program, I found a subtle problem that caused gcc and my test program to crash. If the amount of memory between the unaligned pointer and the aligned pointer is 8 bytes, freeing that memory caused the tag info to become corrupted (because 8 bytes = 4 bytes for begin tag + 4 bytes for end tag and no memory to use). This patch adds code to account for this. Also, I'd like ask about checkin privs so other people don't have to check my stuff in. *** /dev/null Sun Jan 7 00:14:39 2001 --- include/libc/malloc.h Fri Jan 5 18:39:44 2001 *************** *** 0 **** --- 1,40 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ + #ifndef __dj_include_libc_malloc_h__ + #define __dj_include_libc_malloc_h__ + + #ifdef __cplusplus + extern "C" { + #endif + + #ifndef __dj_ENFORCE_ANSI_FREESTANDING + + #ifndef __STRICT_ANSI__ + + #ifndef _POSIX_SOURCE + + typedef struct BLOCK { + size_t size; + struct BLOCK *next; + int bucket; + } BLOCK; + + #define BEFORE(bp) ((BLOCK *)((char *)bp - *(int *)((char *)bp - 4) - 8)) + #define BEFSZ(bp) (*(size_t *)((char *)bp - 4)) + #define ENDSZ(bp) (*(size_t *)((char *)bp + bp->size + 4)) + #define AFTER(bp) ((BLOCK *)((char *)bp + bp->size + 8)) + #define DATA(bp) ((char *)&(bp->next)) + + #define ALIGN 8 + + #endif /* !_POSIX_SOURCE */ + #endif /* !__STRICT_ANSI__ */ + #endif /* !__dj_ENFORCE_ANSI_FREESTANDING */ + + #ifndef __dj_ENFORCE_FUNCTION_CALLS + #endif /* !__dj_ENFORCE_FUNCTION_CALLS */ + + #ifdef __cplusplus + } + #endif + + #endif /* __dj_include_libc_atexit_h__ */ *** /dev/null Sun Jan 7 00:14:39 2001 --- src/libc/compat/stdlib/valloc.c Fri Jan 5 18:42:04 2001 *************** *** 0 **** --- 1,10 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ + + #include + void *memalign (size_t, size_t); + + void * + valloc (size_t amt) + { + return memalign (amt, getpagesize()); + } *** /dev/null Sun Jan 7 00:14:39 2001 --- src/libc/compat/stdlib/valloc.txh Fri Jan 5 18:27:08 2001 *************** *** 0 **** --- 1,27 ---- + @node valloc, memory + @subheading Syntax + + @example + #include + + void *valloc(size_t size); + @end example + + @subheading Description + + This function is just like @code{malloc} (@pxref{malloc}) except that the + returned pointer is always aligned to a CPU page boundary. + + @subheading Return Value + + A pointer to a newly allocated block of memory. + + @subheading Portability + + @portability !ansi, !posix + + @subheading Example + + @example + char *page = valloc(getpagesize()); + @end example *** /dev/null Sun Jan 7 00:14:39 2001 --- src/libc/compat/stdlib/memalign.c Sun Jan 7 00:14:32 2001 *************** *** 0 **** --- 1,93 ---- + /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ + #include + #include + + /* Make VAL a multiple of ALIGN. */ + static inline + size_t + align_val (size_t val, size_t align) + { + return ((val + (align - 1)) & ~(align - 1)); + } + + /* Split a chunk of previously allocated memory into two chunks so both can + be released with a call to free(). */ + static void * + split_alloc (char *ptr, size_t split_pos) + { + BLOCK *b1, *b2; + char *split_ptr; + + b1 = (BLOCK *)(ptr - 4); + + /* Set location of second pointer and its block info. */ + split_ptr = ptr + split_pos; + b2 = (BLOCK *)(split_ptr - 4); + + /* Temporarily clear chunk-in-use bit so macros work correctly. */ + b1->size &= ~1; + + /* Set up the two blocks. */ + b2->size = b1->size - split_pos; + b1->size = split_pos - 8; + + ENDSZ(b1) = b1->size; + ENDSZ(b2) = b2->size; + + /* Finish up by setting the chunk-in-use bit for the first chunk and + returning a pointer to the chunk just split off. */ + ENDSZ(b1) |= 1; + b1->size |= 1; + + ENDSZ(b2) |= 1; + b2->size |= 1; + + return DATA(b2); + } + + /* Return a block of memory AMT bytes long whose address is a multiple + ALIGN. ALIGN must be a power of 2. */ + + void * + memalign (size_t amt, size_t align) + { + char *ptr, *aligned_ptr; + BLOCK *b; + size_t alloc_size, before_size, after_size; + + if (align != align_val(align, align)) + return NULL; + + if (align < ALIGN) + align = ALIGN; + + amt = align_val (amt, ALIGN); + alloc_size = amt + align; + + ptr = malloc(alloc_size); + if (ptr == NULL) + return ptr; + + aligned_ptr = (char *)align_val((size_t)ptr, align); + + /* Release the space between the malloc'ed pointer + and the aligned pointer, if any. */ + before_size = (aligned_ptr - ptr); + if (before_size) + { + aligned_ptr = split_alloc (ptr, before_size); + free (ptr); + } + + /* Release extra space after the aligned block. Avoid + creating an empty block. */ + after_size = alloc_size - before_size - amt; + if (after_size >= 16) + { + char *after = split_alloc (aligned_ptr, amt + 8); + free (after); + } + + return aligned_ptr; + } + *** /dev/null Sun Jan 7 00:14:39 2001 --- src/libc/compat/stdlib/memalign.txh Fri Jan 5 18:35:14 2001 *************** *** 0 **** --- 1,28 ---- + @node memalign, memory + @subheading Syntax + + @example + #include + + void *memalign(size_t size, size_t alignment); + @end example + + @subheading Description + + This function is just like @code{malloc} (@pxref{malloc}) except that the + returned pointer is always a multiple of @var{alignment}. @var{alignment} + must be a multiple of two. + + @subheading Return Value + + A pointer to a newly allocated block of memory. + + @subheading Portability + + @portability !ansi, !posix + + @subheading Example + + @example + char *page = memalign(getpagesize(), getpagesize()); + @end example *** include/stdlib.h.orig Sun Jan 7 00:05:42 2001 --- include/stdlib.h Sun Jan 7 00:07:18 2001 *************** long long llabs(long long _i); *** 102,107 **** --- 102,108 ---- lldiv_t lldiv(long long _numer, long long _denom); void lcong48(unsigned short param[7]); unsigned long lrand48(void); + void * memalign(size_t _size, size_t _align); long mrand48(void); unsigned long nrand48(unsigned short state[3]); int putenv(const char *_val); *************** long double _strtold(const char *_s, cha *** 113,118 **** --- 114,120 ---- long long strtoll(const char *_s, char **_endptr, int _base); unsigned long long strtoull(const char *_s, char **_endptr, int _base); void swab(const void *from, void *to, int nbytes); + void * valloc(size_t _amount); #ifndef alloca #define alloca __builtin_alloca *** src/libc/compat/stdlib/makefile.orig Sun Jan 7 00:10:42 2001 --- src/libc/compat/stdlib/makefile Sun Jan 7 00:11:38 2001 *************** SRC += fcvtbuf.c *** 22,27 **** SRC += fcvt.c SRC += gcvt.c SRC += rand48.c ! include $(TOP)/../makefile.inc --- 22,28 ---- SRC += fcvt.c SRC += gcvt.c SRC += rand48.c ! SRC += memalign.c ! SRC += valloc.c include $(TOP)/../makefile.inc