Commit eef62ec8 authored by ROOL's avatar ROOL 🤖
Browse files

Add C11/C18 library functions

Detail:
  alloc.c - Add aligned_alloc(). Since softloaded versions of the C library can't rely on OS_Module 24 being available there is a fallback implementation for older kernels.
  stdlib.c - Add quick_exit() and at_quick_exit(). Also fix bug in _Exit; a missing loop pre-decrement led to only checking a single (out of array bounds!) vector.
  time.c - Add timespec_get().
  cl_entry2 - 4 new functions added to the stubs.
  Reduce spare CLib static workspace to absorb the quick_exit() handlers, remove the unused words from armsys.c so they're all accounted for in one space.
Admin:
  In order that the functions can be defined in the stubs now (despite not having a compiler to support C11/C18) they are enabled when 'DDE' is defined so that C library builds work.
parent 948e0838
......@@ -106,6 +106,7 @@
#include <string.h> /* for memset(...), memcpy(...) */
#include <setjmp.h>
#include <signal.h>
#include <stdint.h>
#include "hostsys.h"
#include "alloc.h"
......@@ -178,7 +179,8 @@ static BlockP heapHigh; /* address of heap hole guard at the top of heap */
static BlockP sys_heap_top; /* address of top of system heap, should = heapLow
after _init_user_alloc is called */
static void *RMABase; /* base address of RMA */
static void *RMAEnd; /* end address of RMA */
static void *RMAEnd; /* end address of RMA */
static int OSMAligns; /* OS_Module supports aligned RMA requests */
/*
* amount of heap that user can actually write to, does not include bitmaps
* and block overheads
......@@ -500,6 +502,20 @@ static void ShowStats(void)
statsP->eventNo--;
}
}
static void init_stats(void)
{
/* grab stats record from heap */
statsP = (StatsPtr) _primitive_alloc(BYTESTOWORDS(sizeof(StatsRec)));
statsP->stats.coalesces = 0; statsP->stats.heapExtensions = 0;
statsP->stats.heapHigh = heapHigh; statsP->stats.heapLow = heapLow;
statsP->stats.userHeap = userHeap; statsP->stats.maxHeapRequirement = 0;
statsP->stats.blocksAllocated = 0; statsP->stats.bytesAllocated = 0;
statsP->stats.blocksDeallocated = 0; statsP->stats.bytesDeallocated = 0;
statsP->events[0].allocates = 0; statsP->events[0].deallocates = 0;
statsP->events[0].bytesAllocated = 0; statsP->events[0].bytesDeallocated = 0;
statsP->nextEvent = 1;
}
#endif
#ifdef BLOCKS_GUARDED
......@@ -830,7 +846,7 @@ static int _primitive_alloc(size_t size/*words*/)
#ifdef BLOCKS_GUARDED
if (check.allocates && check_heap() != OK) RELEASEANDRETURN(CORRUPT)
#endif
/* convert size from words to addresss units */
/* convert size from words to address units */
size *= BYTESPERWORD;
F0("!!primitive_alloc: size ")
FD(size, 10)
......@@ -1067,9 +1083,19 @@ extern size_t _byte_size(void *p)
extern void *malloc(size_t size)
{ void *ptr;
if (_kernel_processor_mode() & 0xF) /* not USR26 or USR32 */
if (_kernel_processor_mode() & 0xF) { /* not USR26 or USR32 */
if (OSMAligns) { /* no need to track aligned requests */
return _kernel_RMAalloc(size);
} else { /* prefix a header for free() to cope with aligned allocations too */
ptr = _kernel_RMAalloc(size + sizeof(RMABlock));
if (ptr != NULL) {
RMABlock *bp = (RMABlock *)ptr;
bp->real = ptr; /* remember where it came from */
ptr = (void *)(bp + 1);
}
return ptr;
}
}
ENTRYTOALLOC(ptr);
ptr = (void *)_primitive_alloc(BYTESTOWORDS(size));
if ((unsigned)ptr >= MINHEAPERROR) {
......@@ -1086,7 +1112,12 @@ extern void *malloc(size_t size)
extern void free(void *p)
{ int rc;
if (p >= RMABase && p < RMAEnd) {
_kernel_RMAfree(p); return;
if (!OSMAligns) { /* recover the original RMA pointer the kernel expects */
RMABlock *bp = ((RMABlock *)p) - 1;
p = bp->real;
}
_kernel_RMAfree(p);
return;
}
rc = _primitive_dealloc((BlockP)p);
/* following line may not be correct ANSI - but for the moment we
......@@ -1127,22 +1158,6 @@ extern int __coalesce(void)
return rc;
}
#ifdef STATS
static void init_stats(void)
{
/* grab stats record from heap */
statsP = (StatsPtr) _primitive_alloc(BYTESTOWORDS(sizeof(StatsRec)));
statsP->stats.coalesces = 0; statsP->stats.heapExtensions = 0;
statsP->stats.heapHigh = heapHigh; statsP->stats.heapLow = heapLow;
statsP->stats.userHeap = userHeap; statsP->stats.maxHeapRequirement = 0;
statsP->stats.blocksAllocated = 0; statsP->stats.bytesAllocated = 0;
statsP->stats.blocksDeallocated = 0; statsP->stats.bytesDeallocated = 0;
statsP->events[0].allocates = 0; statsP->events[0].deallocates = 0;
statsP->events[0].bytesAllocated = 0; statsP->events[0].bytesDeallocated = 0;
statsP->nextEvent = 1;
}
#endif
extern void _terminate_user_alloc(void)
{
heapLow = sys_heap_top;
......@@ -1171,6 +1186,7 @@ static int arm2_swp(int newValue, int *location)
extern void _init_alloc(void)
{ int j;
_kernel_swi_regs r;
_kernel_oserror *e;
INITMUTEX;
lastFreeBlockOnHeap = NULL;
mapForExistingHeap = NULL;
......@@ -1197,6 +1213,16 @@ extern void _init_alloc(void)
RMABase = (void *) 0x01800000;
RMAEnd = (void *) 0x01C00000;
}
/* query OS_Module aligned claim */
r.r[0] = 24;
r.r[3] = 128;
r.r[4] = 0; /* expect 'Bad alignment' not 'Unknown subreason' */
e = _kernel_swi(OS_Module,&r,&r);
OSMAligns = !(e && e->errnum == 0x105);
if (!e) { /* succeeded? then free */
r.r[0] = 7;
_kernel_swi(OS_Module,&r,&r);
}
#ifdef STATS
statsP = NULL;
init_stats();
......@@ -1209,9 +1235,24 @@ void *realloc(void *p, size_t size)
{ int rc;
size_t oldsize;
void *newb = NULL;
if (_kernel_processor_mode() & 0xF) /* not USR26 or USR32? */
if (_kernel_processor_mode() & 0xF) { /* not USR26 or USR32 */
if (OSMAligns) { /* allowed to return differently aligned block */
return _kernel_RMAextend(p, size);
} else {
RMABlock *bp;
if (p != NULL) {
bp = ((RMABlock *)p) - 1;
newb = _kernel_RMAextend(bp->real, size + sizeof(RMABlock));
} else
newb = _kernel_RMAalloc(size + sizeof(RMABlock));
if (newb != NULL) {
bp = (RMABlock *)newb;
bp->real = newb; /* remember where it came from */
newb = (void *)(bp + 1);
}
return newb;
}
}
F0("!!realloc\n");
size = BYTESTOWORDS(size)*BYTESPERWORD;
if (p == NULL) return malloc(size);
......@@ -1263,22 +1304,81 @@ extern void *calloc(size_t count, size_t size)
* just so that it can verify that the said product really is in range
* for handing to malloc.
*/
unsigned h = (count>>16)*(size>>16);
unsigned m1 = (count>>16)*(size&0xffff);
unsigned m2 = (count&0xffff)*(size>>16);
unsigned l = (count&0xffff)*(size&0xffff);
h += (m1>>16) + (m2>>16);
m1 = (m1&0xffff) + (m2&0xffff) + (l>>16);
l = (l&0xffff) | (m1<<16);
h += m1>>16;
if (h) l = (unsigned)(-1);
if (l >= MAXBYTES) bad_size(l);
r = malloc(l);
unsigned long long l = count * (unsigned long long)size;
if (l >= MAXBYTES) bad_size((size_t)l);
r = malloc((size_t)l);
#ifdef GC
/* if garbage collecting, the block will already have been zeroed */
if ((r != NULL) && (!garbageCollecting)) memset(r, 0, l);
if ((r != NULL) && !garbageCollecting) memset(r, 0, (size_t)l);
#else
if (r != NULL) memset(r, 0, l);
if (r != NULL) memset(r, 0, (size_t)l);
#endif
return r;
}
extern void *aligned_alloc(size_t align, size_t size)
{ void *ptr;
size_t oversize, excess;
if (!align || (align & (align - 1))) /* ensure power of 2 */
return NULL;
if (_kernel_processor_mode() & 0xF) { /* not USR26 or USR32 */
if (OSMAligns) { /* let the kernel do the hard work */
_kernel_oserror *e;
e = _swix(OS_Module, _IN(0)|_INR(3,4)|_OUT(2), 24, size, align, &ptr);
return (e != NULL) ? NULL : ptr;
} else { /* overallocate to ensure alignment, then return the subset */
if (align < sizeof(RMABlock)) align = sizeof(RMABlock);
oversize = size + align - 1 + sizeof(RMABlock);
ptr = _kernel_RMAalloc(oversize);
if (ptr != NULL) {
RMABlock *bp;
uintptr_t user = (uintptr_t)ptr;
user = (user + (align - 1)) & ~(align - 1); /* aligned as the user wished */
bp = ((RMABlock *)user) - 1;
bp->real = ptr; /* remember where it came from */
ptr = (void *)user;
}
return ptr;
}
}
/* otherwise from the heap */
if (align < OVERHEAD) align = OVERHEAD;
if (size < BYTESPERWORD) size = BYTESPERWORD;
excess = 0;
while (1) {
ENTRYTOALLOC(ptr);
oversize = size + align - 1 + OVERHEAD + excess;
ptr = (void *)_primitive_alloc(BYTESTOWORDS(oversize));
if ((unsigned)ptr >= MINHEAPERROR) {
#ifdef STATS
ShowStats();
#endif
if ((int)ptr == CORRUPT)
_alloc_die(_kernel_getmessage("malloc failed", "C12"), CORRUPT);
else return NULL;
} else {
BlockP keepBlock, tempBlock;
uintptr_t user = (uintptr_t)ptr;
user = (user + (align - 1)) & ~(align - 1); /* aligned as the user wished */
user += excess; /* doubly aligned to ensure excess */
if (user != (uintptr_t)ptr) {
/* define a new block at the split point */
tempBlock = ADDBYTES(ptr, -OVERHEAD);
keepBlock = ADDBYTES(user, -OVERHEAD);
oversize = (uintptr_t)keepBlock - (uintptr_t)ptr;
if ((int)oversize <= 0) { /* bad: after OVERHEADs it's an empty block */
_primitive_dealloc((BlockP)ptr);
excess += align;
continue; /* go again but double up alignment */
}
keepBlock->size = SIZE(tempBlock) - oversize - OVERHEAD;
#ifdef BLOCKS_GUARDED
keepBlock->guard = GUARDCONSTANT;
#endif
tempBlock->size = oversize;
_primitive_dealloc((BlockP)ptr); /* aka tempBlock, now adjusted */
}
return (void *)user;
}
}
}
......@@ -67,8 +67,6 @@ const char *_clib_version(void)
#undef str
#undef xstr
static int unused[15];
/* timing things... */
/* struct bbctime objects are used to hold bbc/brazil 5-byte timer value -
......@@ -716,7 +714,6 @@ void _armsys_lib_init(void)
{ char *stdinfile = TTYFILENAME,
*stdoutfile = TTYFILENAME,
*stderrfile = TTYFILENAME;
(void) unused;
_getenv_value = NULL;
_error_recursion = 0;
#ifdef DDE
......
......@@ -102,23 +102,31 @@ void srand(unsigned int seed)
typedef void (*vprocp)(void);
static union { vprocp p; int i; } _exitvector[EXIT_LIMIT] = { 0 };
static union { vprocp p; int i; } _qxitvector[EXIT_LIMIT] = { 0 };
/* initialised so not in bss (or shared library trouble) */
static struct {
char number_of_exit_functions;
char number_of_qxit_functions;
char alloc_finalised, io_finalised, getenv_finalised;
} exit_s;
void _exit_init(void)
{
if (_kernel_client_is_module()) {
/* leave SWI mode exit handlers in place. number_of_exit_functions
/* leave SVC mode exit handlers in place. number_of_[q]exit_functions
is guaranteed reasonable */
while (exit_s.number_of_exit_functions != 0)
if (_exitvector[--exit_s.number_of_exit_functions].i & 3) {
++exit_s.number_of_exit_functions; break;
}
} else
while (exit_s.number_of_qxit_functions != 0)
if (_qxitvector[--exit_s.number_of_qxit_functions].i & 3) {
++exit_s.number_of_qxit_functions; break;
}
} else {
exit_s.number_of_exit_functions = 0;
exit_s.number_of_qxit_functions = 0;
}
exit_s.alloc_finalised = 0; exit_s.io_finalised = 0; exit_s.getenv_finalised = 0;
}
......@@ -130,11 +138,29 @@ int atexit(vprocp func)
return 0; /* success */
}
int at_quick_exit(vprocp func)
{
if (exit_s.number_of_qxit_functions >= EXIT_LIMIT) return 1; /* failure */
_qxitvector[exit_s.number_of_qxit_functions++].i =
(int) func + ((_kernel_processor_mode() & 0xF) != 0);
return 0; /* success */
}
void _lib_shutdown(void)
{
int mode = ((_kernel_processor_mode() & 0xF) != 0);
int isquick = (exit_s.number_of_qxit_functions != 0) &&
(exit_s.number_of_exit_functions == 0);
int ismodule = _kernel_client_is_module(); /* ie is module app, so not */
/* total shutdown */
while (exit_s.number_of_qxit_functions!=0) {
vprocp fn = _qxitvector[--exit_s.number_of_qxit_functions].p;
int flags = _qxitvector[exit_s.number_of_qxit_functions].i;
if ((flags & 3) != mode) { ++exit_s.number_of_qxit_functions; break; };
/* Take extra care with fn ptr - consider Thumb */
fn = (vprocp) ((unsigned) fn &~ 3);
_call_client_0(fn);
}
while (exit_s.number_of_exit_functions!=0) {
vprocp fn = _exitvector[--exit_s.number_of_exit_functions].p;
int flags = _exitvector[exit_s.number_of_exit_functions].i;
......@@ -148,18 +174,27 @@ void _lib_shutdown(void)
{ exit_s.getenv_finalised = 1; _terminate_getenv(); }
if (!exit_s.alloc_finalised)
{ exit_s.alloc_finalised = 1; _terminate_user_alloc(); }
if (!exit_s.io_finalised)
if (!exit_s.io_finalised && !isquick)
{ exit_s.io_finalised = 1; _terminateio(); }
if (ismodule) /* Want terminateio again for module part */
exit_s.io_finalised = 0;
}
void exit(int n)
{
/* No longer calls _lib_shutdown: that is done as C finalisation called
from _kernel_exit.
*/
{ /* Stop any USR mode at_quick_exit's being called */
while (exit_s.number_of_qxit_functions!=0) {
int flags = _qxitvector[--exit_s.number_of_qxit_functions].i;
if ((flags & 3) != 0) { ++exit_s.number_of_qxit_functions; break; };
}
_exit(n);
}
void quick_exit(int n)
{ /* Stop any USR mode atexit's being called */
while (exit_s.number_of_exit_functions!=0) {
int flags = _exitvector[--exit_s.number_of_exit_functions].i;
if ((flags & 3) != 0) { ++exit_s.number_of_exit_functions; break; };
}
_exit(n);
}
......@@ -169,9 +204,13 @@ void _Exit(int n)
* atexit functions either...
*/
while (exit_s.number_of_exit_functions!=0) {
int flags = _exitvector[exit_s.number_of_exit_functions].i;
int flags = _exitvector[--exit_s.number_of_exit_functions].i;
if ((flags & 3) != 0) { ++exit_s.number_of_exit_functions; break; };
}
while (exit_s.number_of_qxit_functions!=0) {
int flags = _qxitvector[--exit_s.number_of_qxit_functions].i;
if ((flags & 3) != 0) { ++exit_s.number_of_qxit_functions; break; };
}
_exit(n);
}
......
......@@ -178,9 +178,8 @@ time_t mktime(struct tm *timeptr)
/* Adjust for a base at 1 Jan 1970 which is 17 leap years since 1900 */
#define DAYS ((70*365)+17)
t = min + 60*(hour + 24*(v - DAYS));
#undef DAYS
#define days0070 (365*70+17)
t = min + 60*(hour + 24*(v - days0070));
{ int thi = ((int)t >> 16)*60;
int tlo = ((int)t & 0xffff)*60 + sec;
thi += (tlo >> 16) & 0xffff;
......@@ -199,6 +198,23 @@ time_t mktime(struct tm *timeptr)
/* Now I know why Unix didn't have this */
}
int timespec_get(struct timespec *ts, int base)
{ /* ISO9899:2011 7.27.2.5 */
long long cs; int *req = (int *)&cs; /* doubles as an OS_Word block */
long quot, rem;
if (base != TIME_UTC) return 0;
*(char *)req = 3; req[1] = 0; /* read time as UTC 5 byte integer */
_kernel_osword(14, req);
cs = cs - (8640000uLL * days0070); /* using 1970 epoch */
quot = (long)(cs / 100);
rem = (long)(cs % 100);
if (quot < 0) return 0; /* pre 1970 */
ts->tv_sec = (time_t)quot; /* 32b time_t good 'til 2038 */
ts->tv_nsec = (long)rem * 10000000; /* centi to nano */
return base;
}
#undef days0070
char *asctime(const struct tm *timeptr)
{ static char _timebuf[26+(8+3*9+7)]; /* slop in case illegal args */
sprintf(_timebuf, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
......
......@@ -15,13 +15,13 @@
#pragma force_top_level
#pragma include_only_once
/* stdlib.h: ISO 'C' (9899:1999) library header, section 7.20 */
/* stdlib.h: ISO 'C' (9899:2018) library header, section 7.22 */
/* Copyright (C) Codemist Ltd. */
/* Copyright (C) Acorn Computers Ltd., 1990, 1992 */
/* version 2.05 */
/* version 2.06 */
/*
* stdlib.h declares four types, several general purpose functions,
* stdlib.h declares five types, several functions of general utility,
* and defines several macros.
*/
......@@ -319,12 +319,23 @@ void _ANSI_srand(unsigned int /*seed*/);
typedef char *malloc_t;
#if __cplusplus >= 201703
void *aligned_alloc(size_t /*alignment*/, size_t /*size*/);
#endif
char *calloc(size_t /*nmemb*/, size_t /*size*/);
void free(void * /*ptr*/);
char *malloc(size_t /*size*/);
char *realloc(void * /*ptr*/, size_t /*size*/);
#endif
#else
#if defined(DDE) || (__STDC_VERSION__ >= 201112)
void *aligned_alloc(size_t /*alignment*/, size_t /*size*/);
/*
* allocates space for an object whose alignment is specified by alignment,
* whose size is specified by size, and whose value is indeterminate.
* Returns: either a null pointer or a pointer to the allocated space.
*/
#endif
void *calloc(size_t /*nmemb*/, size_t /*size*/);
/*
* allocates space for an array of nmemb objects, each of whose size is
......@@ -380,7 +391,6 @@ int atexit(void (* /*func*/)(void));
* least 32 functions.
* Returns: zero if the registration succeeds, nonzero if it fails.
*/
void exit(int /*status*/);
/*
* causes normal program termination to occur. If more than one call to the
......@@ -397,6 +407,35 @@ void exit(int /*status*/);
* Otherwise the status returned is implementation-defined (the value of
* status is returned under RISC OS).
*/
#ifdef __STDC_VERSION__
#if defined(DDE) || (__STDC_VERSION__ >= 201112)
int at_quick_exit(void (* /*func*/)(void));
/*
* register the function pointed to by func, to be called without arguments
* should quick_exit be called. It is possible to register at
* least 32 functions.
* Returns: zero if the registration succeeds, nonzero if it fails.
*/
void quick_exit(int /*status*/);
/*
* causes normal program termination to occur. No functions registered by
* the atexit function or signal handlers registered by the signal function
* are called. If a program calls the quick_exit function more than once, or
* calls the exit function in addition to the quick_exit function, the
* behavior is undefined.
* If a signal is raised while the quick_exit function is executing, the
* behavior is undefined.
* First, all functions registered by the at_quick_exit function, in the
* reverse order of their registration, except that a function is called
* after any previously registered functions that had already been called
* at the time it was registered. If, during the call to any such function,
* a call to the longjmp function is made that would terminate the call to
* the registered function, the behavior is undefined.
* Finally, control is returned to the host environment by means of the
* function call _Exit(status).
*/
#endif
#endif
void _Exit(int /*status*/);
/*
......
......@@ -15,17 +15,17 @@
#pragma force_top_level
#pragma include_only_once
/* time.h: ISO 'C' (9899:1999) library header, section 7.23 */
/* time.h: ISO 'C' (9899:2018) library header, section 7.27 */
/* Copyright (C) Codemist Ltd. */
/* Copyright (C) Acorn Computers Ltd. 1992 */
/* version 2.02 */
/* version 2.03 */
/*
* time.h declares two macros, four types and several functions for
* manipulating time. Many functions deal with a calendar time that represents
* the current date (according to the Gregorian calendar) and time. Some
* functions deal with local time, which is the caledar time expressed for some
* specific time zone, and with Dalight Savings Time, which is a temporary
* functions deal with local time, which is the calendar time expressed for
* some specific time zone, and with Dalight Savings Time, which is a temporary
* change in the algorithm for determining local time.
*/
......@@ -38,14 +38,12 @@ typedef unsigned int size_t; /* from <stddef.h> */
#endif
#ifndef NULL
# define NULL 0
# define NULL 0 /* see <stddef.h> */
#endif
#ifdef __CLK_TCK
# define CLK_TCK __CLK_TCK /* Pre-Dec 88 Draft; under threat */
# define CLOCKS_PER_SEC __CLK_TCK /* Dec 1988 Draft */
# define CLOCKS_PER_SEC __CLK_TCK /* target clock tick */
#else
# define CLK_TCK 100 /* for the BBC */
# define CLOCKS_PER_SEC 100 /* for the BBC */
/* the number per second of the value returned by the clock function. */
#endif
......@@ -115,7 +113,27 @@ time_t time(time_t * /*timer*/);
* not available. If timer is not a null pointer, the return value
* is also assigned to the object it points to.
*/
#if defined(DDE) || (__STDC_VERSION__ >= 201112)
struct timespec {
time_t tv_sec; /* whole seconds, >= 0 */
long tv_nsec; /* nanoseconds, 0 to 999999999 */
};
/* struct timespec holds an interval specified in seconds and nanoseconds
* (which may represent a calendar time based on a particular epoch).
*/
#define TIME_UTC 1
/* designates the UTC time base */
int timespec_get(struct timespec */*ts*/, int /*base*/);
/*
* sets the interval pointed to by ts to hold the current calendar time
* based on the specified time base. If base is TIME_UTC, the tv_sec member
* is set to the number of seconds since an implementation defined epoch,
* truncated to a whole value and the tv_nsec member is set to the integral
* number of nanoseconds, rounded to the resolution of the system clock.
* Returns: if successful it returns the nonzero value base; otherwise, it
* return zero.
*/
#endif
char *asctime(const struct tm * /*timeptr*/);
/*
* converts the broken-down time in the structure pointed to by timeptr into
......
......@@ -19,6 +19,7 @@
; Add new entries ONLY AT THE END of the list
;
; C99
Entry __fpclassifyf, , , unveneered, , , [FPREGARGS]
Entry __fpclassifyd, , , unveneered, , , [FPREGARGS]
Entry __signbitf, , , unveneered, , , [FPREGARGS]
......@@ -205,6 +206,7 @@
Entry creal, imported, , unveneered
Entry crealf, imported, , unveneered
; Large file 64b offsets
Entry _fgetpos64, imported, , unveneered
Entry _fopen64, imported, , unveneered
Entry _freopen64, imported, , unveneered
......@@ -213,4 +215,10 @@
Entry _ftello64, imported, , unveneered
Entry _tmpfile64, imported, , unveneered
; C11
Entry aligned_alloc, imported, , unveneered
Entry at_quick_exit, imported, , unveneered
Entry quick_exit, imported, , unveneered
Entry timespec_get, imported, , unveneered
END
......@@ -25,7 +25,7 @@
EXPORT |CLib_data_end|
% 123*4
% 103*4
|CLib_data_end|
END
......@@ -42,6 +42,10 @@
*/
#define HEAP_ALLOCATED_IN_ASCENDING_ADDRESS_ORDER 1
typedef struct RMABlockStruct {
void *real;
} RMABlock;
typedef struct BlockStruct {
#ifdef BLOCKS_GUARDED
unsigned int guard; /* guard word should contain GuardConstant if all ok */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment