PageRenderTime 75ms CodeModel.GetById 19ms app.highlight 46ms RepoModel.GetById 2ms app.codeStats 0ms

/vendor/gc/dyn_load.c

http://github.com/feyeleanor/RubyGoLightly
C | 1231 lines | 882 code | 165 blank | 184 comment | 257 complexity | 60fabcb7d28efffbacac1d46e3c9603f MD5 | raw file
   1/*
   2 * Copyright (c) 1991-1994 by Xerox Corporation.  All rights reserved.
   3 * Copyright (c) 1997 by Silicon Graphics.  All rights reserved.
   4 *
   5 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
   6 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
   7 *
   8 * Permission is hereby granted to use or copy this program
   9 * for any purpose,  provided the above notices are retained on all copies.
  10 * Permission to modify the code and to distribute modified code is granted,
  11 * provided the above notices are retained, and a notice that the code was
  12 * modified is included with the above copyright notice.
  13 *
  14 * Original author: Bill Janssen
  15 * Heavily modified by Hans Boehm and others
  16 */
  17
  18/*
  19 * This is incredibly OS specific code for tracking down data sections in
  20 * dynamic libraries.  There appears to be no way of doing this quickly
  21 * without groveling through undocumented data structures.  We would argue
  22 * that this is a bug in the design of the dlopen interface.  THIS CODE
  23 * MAY BREAK IN FUTURE OS RELEASES.  If this matters to you, don't hesitate
  24 * to let your vendor know ...
  25 *
  26 * None of this is safe with dlclose and incremental collection.
  27 * But then not much of anything is safe in the presence of dlclose.
  28 */
  29#if (defined(__linux__) || defined(__GLIBC__) || defined(__GNU__)) \
  30     && !defined(_GNU_SOURCE)
  31    /* Can't test LINUX, since this must be defined before other includes */
  32#   define _GNU_SOURCE
  33#endif
  34#if !defined(MACOS) && !defined(_WIN32_WCE)
  35#  include <sys/types.h>
  36#endif
  37#include "private/gc_priv.h"
  38
  39/* BTL: avoid circular redefinition of dlopen if GC_SOLARIS_THREADS defined */
  40# if (defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS)) \
  41      && defined(dlopen) && !defined(GC_USE_LD_WRAP)
  42    /* To support threads in Solaris, gc.h interposes on dlopen by       */
  43    /* defining "dlopen" to be "GC_dlopen", which is implemented below.  */
  44    /* However, both GC_FirstDLOpenedLinkMap() and GC_dlopen() use the   */
  45    /* real system dlopen() in their implementation. We first remove     */
  46    /* gc.h's dlopen definition and restore it later, after GC_dlopen(). */
  47#   undef dlopen
  48#   define GC_must_restore_redefined_dlopen
  49# else
  50#   undef GC_must_restore_redefined_dlopen
  51# endif
  52
  53/* A user-supplied routine that is called to determine if a DSO must
  54   be scanned by the gc.  */
  55static int (*GC_has_static_roots)(const char *, void *, size_t);
  56
  57
  58#if (defined(DYNAMIC_LOADING) || defined(MSWIN32) || defined(MSWINCE)) \
  59    && !defined(PCR)
  60#if !defined(SOLARISDL) && !defined(IRIX5) && \
  61    !defined(MSWIN32) && !defined(MSWINCE) && \
  62    !(defined(ALPHA) && defined(OSF1)) && \
  63    !defined(HPUX) && !(defined(LINUX) && defined(__ELF__)) && \
  64    !defined(AIX) && !defined(SCO_ELF) && !defined(DGUX) && \
  65    !(defined(FREEBSD) && defined(__ELF__)) && \
  66    !(defined(NETBSD) && defined(__ELF__)) && !defined(HURD) && \
  67    !defined(DARWIN) && !defined(CYGWIN32)
  68 --> We only know how to find data segments of dynamic libraries for the
  69 --> above.  Additional SVR4 variants might not be too
  70 --> hard to add.
  71#endif
  72
  73#include <stdio.h>
  74#ifdef SOLARISDL
  75#   include <sys/elf.h>
  76#   include <dlfcn.h>
  77#   include <link.h>
  78#endif
  79
  80#if defined(NETBSD)
  81#   include <machine/elf_machdep.h>
  82#   define ELFSIZE ARCH_ELFSIZE
  83#endif
  84
  85#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
  86    (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
  87    (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
  88#   include <stddef.h>
  89#   include <elf.h>
  90#   include <link.h>
  91#endif
  92
  93/* Newer versions of GNU/Linux define this macro.  We
  94 * define it similarly for any ELF systems that don't.  */
  95#  ifndef ElfW
  96#    if defined(FREEBSD)
  97#      if __ELF_WORD_SIZE == 32
  98#        define ElfW(type) Elf32_##type
  99#      else
 100#        define ElfW(type) Elf64_##type
 101#      endif
 102#    elif defined(NETBSD)
 103#      if ELFSIZE == 32
 104#        define ElfW(type) Elf32_##type
 105#      else
 106#        define ElfW(type) Elf64_##type
 107#      endif
 108#    else
 109#      if !defined(ELF_CLASS) || ELF_CLASS == ELFCLASS32
 110#        define ElfW(type) Elf32_##type
 111#      else
 112#        define ElfW(type) Elf64_##type
 113#      endif
 114#    endif
 115#  endif
 116
 117#if defined(SOLARISDL) && !defined(USE_PROC_FOR_LIBRARIES)
 118
 119#ifdef LINT
 120    Elf32_Dyn _DYNAMIC;
 121#endif
 122
 123static struct link_map *
 124GC_FirstDLOpenedLinkMap()
 125{
 126    extern ElfW(Dyn) _DYNAMIC;
 127    ElfW(Dyn) *dp;
 128    struct r_debug *r;
 129    static struct link_map * cachedResult = 0;
 130    static ElfW(Dyn) *dynStructureAddr = 0;
 131    			/* BTL: added to avoid Solaris 5.3 ld.so _DYNAMIC bug */
 132
 133#   ifdef SUNOS53_SHARED_LIB
 134	/* BTL: Avoid the Solaris 5.3 bug that _DYNAMIC isn't being set	*/
 135	/* up properly in dynamically linked .so's. This means we have	*/
 136	/* to use its value in the set of original object files loaded	*/
 137	/* at program startup.						*/
 138	if( dynStructureAddr == 0 ) {
 139	  void* startupSyms = dlopen(0, RTLD_LAZY);
 140	  dynStructureAddr = (ElfW(Dyn)*)dlsym(startupSyms, "_DYNAMIC");
 141		}
 142#   else
 143	dynStructureAddr = &_DYNAMIC;
 144#   endif
 145
 146    if( dynStructureAddr == 0) {
 147        return(0);
 148    }
 149    if( cachedResult == 0 ) {
 150        int tag;
 151        for( dp = ((ElfW(Dyn) *)(&_DYNAMIC)); (tag = dp->d_tag) != 0; dp++ ) {
 152            if( tag == DT_DEBUG ) {
 153                struct link_map *lm
 154                        = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
 155                if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
 156                break;
 157            }
 158        }
 159    }
 160    return cachedResult;
 161}
 162
 163#endif /* SOLARISDL ... */
 164
 165/* BTL: added to fix circular dlopen definition if GC_SOLARIS_THREADS defined */
 166# if defined(GC_must_restore_redefined_dlopen)
 167#   define dlopen GC_dlopen
 168# endif
 169
 170# if defined(SOLARISDL)
 171/* Add dynamic library data sections to the root set.		*/
 172# if !defined(PCR) && !defined(GC_SOLARIS_THREADS) && defined(THREADS)
 173	--> fix mutual exclusion with dlopen
 174# endif
 175
 176# ifndef USE_PROC_FOR_LIBRARIES
 177void GC_register_dynamic_libraries()
 178{
 179  struct link_map *lm = GC_FirstDLOpenedLinkMap();
 180  
 181
 182  for (lm = GC_FirstDLOpenedLinkMap();
 183       lm != (struct link_map *) 0;  lm = lm->l_next)
 184    {
 185	ElfW(Ehdr) * e;
 186        ElfW(Phdr) * p;
 187        unsigned long offset;
 188        char * start;
 189        register int i;
 190        
 191	e = (ElfW(Ehdr) *) lm->l_addr;
 192        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
 193        offset = ((unsigned long)(lm->l_addr));
 194        for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
 195          switch( p->p_type ) {
 196            case PT_LOAD:
 197              {
 198                if( !(p->p_flags & PF_W) ) break;
 199                start = ((char *)(p->p_vaddr)) + offset;
 200                GC_add_roots_inner(
 201                  start,
 202                  start + p->p_memsz,
 203                  TRUE
 204                );
 205              }
 206              break;
 207            default:
 208              break;
 209          }
 210	}
 211    }
 212}
 213
 214# endif /* !USE_PROC ... */
 215# endif /* SOLARISDL */
 216
 217#if defined(LINUX) && defined(__ELF__) || defined(SCO_ELF) || \
 218    (defined(FREEBSD) && defined(__ELF__)) || defined(DGUX) || \
 219    (defined(NETBSD) && defined(__ELF__)) || defined(HURD)
 220
 221
 222#ifdef USE_PROC_FOR_LIBRARIES
 223
 224#include <string.h>
 225
 226#include <sys/stat.h>
 227#include <fcntl.h>
 228#include <unistd.h>
 229
 230#define MAPS_BUF_SIZE (32*1024)
 231
 232extern ssize_t GC_repeat_read(int fd, char *buf, size_t count);
 233	/* Repeatedly read until buffer is filled, or EOF is encountered */
 234	/* Defined in os_dep.c.  					 */
 235
 236char *GC_parse_map_entry(char *buf_ptr, ptr_t *start, ptr_t *end,
 237                         char **prot, unsigned int *maj_dev,
 238			 char **mapping_name);
 239char *GC_get_maps(void);
 240	/* From os_dep.c	*/
 241
 242/* Sort an array of HeapSects by start address.				*/
 243/* Unfortunately at least some versions of				*/
 244/* Linux qsort end up calling malloc by way of sysconf, and hence can't */
 245/* be used in the colector.  Hence we roll our own.  Should be		*/
 246/* reasonably fast if the array is already mostly sorted, as we expect	*/
 247/* it to be.								*/
 248void sort_heap_sects(struct HeapSect *base, size_t number_of_elements)
 249{
 250    signed_word n = (signed_word)number_of_elements;
 251    signed_word nsorted = 1;
 252    signed_word i;
 253
 254    while (nsorted < n) {
 255      while (nsorted < n &&
 256    	     base[nsorted-1].hs_start < base[nsorted].hs_start)
 257          ++nsorted;
 258      if (nsorted == n) break;
 259      GC_ASSERT(base[nsorted-1].hs_start > base[nsorted].hs_start);
 260      i = nsorted - 1;
 261      while (i >= 0 && base[i].hs_start > base[i+1].hs_start) {
 262        struct HeapSect tmp = base[i];
 263	base[i] = base[i+1];
 264	base[i+1] = tmp;
 265	--i;
 266      }
 267      GC_ASSERT(base[nsorted-1].hs_start < base[nsorted].hs_start);
 268      ++nsorted;
 269    }
 270}
 271
 272word GC_register_map_entries(char *maps)
 273{
 274    char *prot;
 275    char *buf_ptr = maps;
 276    int count;
 277    ptr_t start, end;
 278    unsigned int maj_dev;
 279    ptr_t least_ha, greatest_ha;
 280    unsigned i;
 281    ptr_t datastart = (ptr_t)(DATASTART);
 282
 283    GC_ASSERT(I_HOLD_LOCK());
 284    sort_heap_sects(GC_our_memory, GC_n_memory);
 285    least_ha = GC_our_memory[0].hs_start;
 286    greatest_ha = GC_our_memory[GC_n_memory-1].hs_start
 287    		  + GC_our_memory[GC_n_memory-1].hs_bytes;
 288
 289    for (;;) {
 290        buf_ptr = GC_parse_map_entry(buf_ptr, &start, &end, &prot, &maj_dev, 0);
 291	if (buf_ptr == NULL) return 1;
 292	if (prot[1] == 'w') {
 293	    /* This is a writable mapping.  Add it to		*/
 294	    /* the root set unless it is already otherwise	*/
 295	    /* accounted for.					*/
 296	    if (start <= GC_stackbottom && end >= GC_stackbottom) {
 297		/* Stack mapping; discard	*/
 298		continue;
 299	    }
 300#	    ifdef THREADS
 301	      /* This may fail, since a thread may already be 		*/
 302	      /* unregistered, but its thread stack may still be there.	*/
 303	      /* That can fail because the stack may disappear while	*/
 304	      /* we're marking.  Thus the marker is, and has to be	*/
 305	      /* prepared to recover from segmentation faults.		*/
 306
 307	      if (GC_segment_is_thread_stack(start, end)) continue;
 308
 309	      /* FIXME: NPTL squirrels					*/
 310	      /* away pointers in pieces of the stack segment that we	*/
 311	      /* don't scan.  We work around this			*/
 312	      /* by treating anything allocated by libpthread as	*/
 313	      /* uncollectable, as we do in some other cases.		*/
 314	      /* A specifically identified problem is that		*/ 
 315	      /* thread stacks contain pointers to dynamic thread	*/
 316	      /* vectors, which may be reused due to thread caching.	*/
 317	      /* They may not be marked if the thread is still live.	*/
 318	      /* This specific instance should be addressed by 		*/
 319	      /* INCLUDE_LINUX_THREAD_DESCR, but that doesn't quite	*/
 320	      /* seem to suffice.					*/
 321	      /* We currently trace entire thread stacks, if they are	*/
 322	      /* are currently cached but unused.  This is		*/
 323	      /* very suboptimal for performance reasons.		*/
 324#	    endif
 325	    /* We no longer exclude the main data segment.		*/
 326	    if (end <= least_ha || start >= greatest_ha) {
 327	      /* The easy case; just trace entire segment */
 328	      GC_add_roots_inner((char *)start, (char *)end, TRUE);
 329	      continue;
 330	    }
 331	    /* Add sections that dont belong to us. */
 332	      i = 0;
 333	      while (GC_our_memory[i].hs_start + GC_our_memory[i].hs_bytes
 334	             < start)
 335		  ++i;
 336	      GC_ASSERT(i < GC_n_memory);
 337	      if (GC_our_memory[i].hs_start <= start) {
 338	          start = GC_our_memory[i].hs_start
 339		  	  + GC_our_memory[i].hs_bytes;
 340		  ++i;
 341	      }
 342	      while (i < GC_n_memory && GC_our_memory[i].hs_start < end
 343		     && start < end) {
 344		  if ((char *)start < GC_our_memory[i].hs_start)
 345		    GC_add_roots_inner((char *)start,
 346				       GC_our_memory[i].hs_start, TRUE);
 347		  start = GC_our_memory[i].hs_start
 348			  + GC_our_memory[i].hs_bytes;
 349		  ++i;
 350	      }
 351	      if (start < end)
 352	          GC_add_roots_inner((char *)start, (char *)end, TRUE);
 353	}
 354    }
 355    return 1;
 356}
 357
 358void GC_register_dynamic_libraries()
 359{
 360    if (!GC_register_map_entries(GC_get_maps()))
 361        ABORT("Failed to read /proc for library registration.");
 362}
 363
 364/* We now take care of the main data segment ourselves: */
 365GC_bool GC_register_main_static_data()
 366{
 367    return FALSE;
 368}
 369  
 370# define HAVE_REGISTER_MAIN_STATIC_DATA
 371
 372#endif /* USE_PROC_FOR_LIBRARIES */
 373
 374#if !defined(USE_PROC_FOR_LIBRARIES)
 375/* The following is the preferred way to walk dynamic libraries	*/
 376/* For glibc 2.2.4+.  Unfortunately, it doesn't work for older	*/
 377/* versions.  Thanks to Jakub Jelinek for most of the code.	*/
 378
 379# if (defined(LINUX) || defined (__GLIBC__)) /* Are others OK here, too? */ \
 380     && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) \
 381         || (__GLIBC__ == 2 && __GLIBC_MINOR__ == 2 && defined(DT_CONFIG))) 
 382
 383/* We have the header files for a glibc that includes dl_iterate_phdr.	*/
 384/* It may still not be available in the library on the target system.   */
 385/* Thus we also treat it as a weak symbol.				*/
 386#define HAVE_DL_ITERATE_PHDR
 387
 388static int GC_register_dynlib_callback(info, size, ptr)
 389     struct dl_phdr_info * info;
 390     size_t size;
 391     void * ptr;
 392{
 393  const ElfW(Phdr) * p;
 394  char * start;
 395  register int i;
 396
 397  /* Make sure struct dl_phdr_info is at least as big as we need.  */
 398  if (size < offsetof (struct dl_phdr_info, dlpi_phnum)
 399      + sizeof (info->dlpi_phnum))
 400    return -1;
 401
 402  p = info->dlpi_phdr;
 403  for( i = 0; i < (int)(info->dlpi_phnum); ((i++),(p++)) ) {
 404    switch( p->p_type ) {
 405      case PT_LOAD:
 406	{
 407	  if( !(p->p_flags & PF_W) ) break;
 408	  start = ((char *)(p->p_vaddr)) + info->dlpi_addr;
 409
 410	  if (GC_has_static_roots
 411	      && !GC_has_static_roots(info->dlpi_name, start, p->p_memsz))
 412	    break;
 413
 414	  GC_add_roots_inner(start, start + p->p_memsz, TRUE);
 415	}
 416      break;
 417      default:
 418	break;
 419    }
 420  }
 421
 422  * (int *)ptr = 1;	/* Signal that we were called */
 423  return 0;
 424}     
 425
 426/* Return TRUE if we succeed, FALSE if dl_iterate_phdr wasn't there. */
 427
 428#pragma weak dl_iterate_phdr
 429
 430GC_bool GC_register_dynamic_libraries_dl_iterate_phdr()
 431{
 432  if (dl_iterate_phdr) {
 433    int did_something = 0;
 434    dl_iterate_phdr(GC_register_dynlib_callback, &did_something);
 435    if (!did_something) {
 436	/* dl_iterate_phdr may forget the static data segment in	*/
 437	/* statically linked executables.				*/
 438	GC_add_roots_inner(DATASTART, (char *)(DATAEND), TRUE);
 439#       if defined(DATASTART2)
 440          GC_add_roots_inner(DATASTART2, (char *)(DATAEND2), TRUE);
 441#       endif
 442    }
 443
 444    return TRUE;
 445  } else {
 446    return FALSE;
 447  }
 448}
 449
 450/* Do we need to separately register the main static data segment? */
 451GC_bool GC_register_main_static_data()
 452{
 453  return (dl_iterate_phdr == 0);
 454}
 455
 456#define HAVE_REGISTER_MAIN_STATIC_DATA
 457
 458# else /* !LINUX || version(glibc) < 2.2.4 */
 459
 460/* Dynamic loading code for Linux running ELF. Somewhat tested on
 461 * Linux/x86, untested but hopefully should work on Linux/Alpha. 
 462 * This code was derived from the Solaris/ELF support. Thanks to
 463 * whatever kind soul wrote that.  - Patrick Bridges */
 464
 465/* This doesn't necessarily work in all cases, e.g. with preloaded
 466 * dynamic libraries.						*/
 467
 468#if defined(NETBSD)
 469#  include <sys/exec_elf.h>
 470/* for compatibility with 1.4.x */
 471#  ifndef DT_DEBUG
 472#  define DT_DEBUG     21
 473#  endif
 474#  ifndef PT_LOAD
 475#  define PT_LOAD      1
 476#  endif
 477#  ifndef PF_W
 478#  define PF_W         2
 479#  endif
 480#else
 481#  include <elf.h>
 482#endif
 483#include <link.h>
 484
 485# endif
 486
 487#ifdef __GNUC__
 488# pragma weak _DYNAMIC
 489#endif
 490extern ElfW(Dyn) _DYNAMIC[];
 491
 492static struct link_map *
 493GC_FirstDLOpenedLinkMap()
 494{
 495    ElfW(Dyn) *dp;
 496    static struct link_map *cachedResult = 0;
 497
 498    if( _DYNAMIC == 0) {
 499        return(0);
 500    }
 501    if( cachedResult == 0 ) {
 502        int tag;
 503        for( dp = _DYNAMIC; (tag = dp->d_tag) != 0; dp++ ) {
 504            if( tag == DT_DEBUG ) {
 505                struct link_map *lm
 506                        = ((struct r_debug *)(dp->d_un.d_ptr))->r_map;
 507                if( lm != 0 ) cachedResult = lm->l_next; /* might be NIL */
 508                break;
 509            }
 510        }
 511    }
 512    return cachedResult;
 513}
 514
 515
 516void GC_register_dynamic_libraries()
 517{
 518  struct link_map *lm;
 519  
 520
 521# ifdef HAVE_DL_ITERATE_PHDR
 522    if (GC_register_dynamic_libraries_dl_iterate_phdr()) {
 523	return;
 524    }
 525# endif
 526  lm = GC_FirstDLOpenedLinkMap();
 527  for (lm = GC_FirstDLOpenedLinkMap();
 528       lm != (struct link_map *) 0;  lm = lm->l_next)
 529    {
 530	ElfW(Ehdr) * e;
 531        ElfW(Phdr) * p;
 532        unsigned long offset;
 533        char * start;
 534        register int i;
 535        
 536	e = (ElfW(Ehdr) *) lm->l_addr;
 537        p = ((ElfW(Phdr) *)(((char *)(e)) + e->e_phoff));
 538        offset = ((unsigned long)(lm->l_addr));
 539        for( i = 0; i < (int)(e->e_phnum); ((i++),(p++)) ) {
 540          switch( p->p_type ) {
 541            case PT_LOAD:
 542              {
 543                if( !(p->p_flags & PF_W) ) break;
 544                start = ((char *)(p->p_vaddr)) + offset;
 545                GC_add_roots_inner(start, start + p->p_memsz, TRUE);
 546              }
 547              break;
 548            default:
 549              break;
 550          }
 551	}
 552    }
 553}
 554
 555#endif /* !USE_PROC_FOR_LIBRARIES */
 556
 557#endif /* LINUX */
 558
 559#if defined(IRIX5) || (defined(USE_PROC_FOR_LIBRARIES) && !defined(LINUX))
 560
 561#include <sys/procfs.h>
 562#include <sys/stat.h>
 563#include <fcntl.h>
 564#include <elf.h>
 565#include <errno.h>
 566#include <signal.h>  /* Only for the following test. */
 567#ifndef _sigargs
 568# define IRIX6
 569#endif
 570
 571extern void * GC_roots_present();
 572	/* The type is a lie, since the real type doesn't make sense here, */
 573	/* and we only test for NULL.					   */
 574
 575
 576/* We use /proc to track down all parts of the address space that are	*/
 577/* mapped by the process, and throw out regions we know we shouldn't	*/
 578/* worry about.  This may also work under other SVR4 variants.		*/
 579void GC_register_dynamic_libraries()
 580{
 581    static int fd = -1;
 582    char buf[30];
 583    static prmap_t * addr_map = 0;
 584    static int current_sz = 0;	/* Number of records currently in addr_map */
 585    static int needed_sz;	/* Required size of addr_map		*/
 586    int i;
 587    long flags;
 588    ptr_t start;
 589    ptr_t limit;
 590    ptr_t heap_start = (ptr_t)HEAP_START;
 591    ptr_t heap_end = heap_start;
 592
 593#   ifdef SOLARISDL
 594#     define MA_PHYS 0
 595#   endif /* SOLARISDL */
 596
 597    if (fd < 0) {
 598      sprintf(buf, "/proc/%d", getpid());
 599	/* The above generates a lint complaint, since pid_t varies.	*/
 600	/* It's unclear how to improve this.				*/
 601      fd = open(buf, O_RDONLY);
 602      if (fd < 0) {
 603    	ABORT("/proc open failed");
 604      }
 605    }
 606    if (ioctl(fd, PIOCNMAP, &needed_sz) < 0) {
 607	GC_err_printf("fd = %d, errno = %d\n", fd, errno);
 608    	ABORT("/proc PIOCNMAP ioctl failed");
 609    }
 610    if (needed_sz >= current_sz) {
 611        current_sz = needed_sz * 2 + 1;
 612        		/* Expansion, plus room for 0 record */
 613        addr_map = (prmap_t *)GC_scratch_alloc((word)
 614						(current_sz * sizeof(prmap_t)));
 615    }
 616    if (ioctl(fd, PIOCMAP, addr_map) < 0) {
 617        GC_err_printf("fd = %d, errno = %d, needed_sz = %d, addr_map = 0x%X\n",
 618                        fd, errno, needed_sz, addr_map);
 619    	ABORT("/proc PIOCMAP ioctl failed");
 620    };
 621    if (GC_n_heap_sects > 0) {
 622    	heap_end = GC_heap_sects[GC_n_heap_sects-1].hs_start
 623    			+ GC_heap_sects[GC_n_heap_sects-1].hs_bytes;
 624    	if (heap_end < GC_scratch_last_end_ptr) heap_end = GC_scratch_last_end_ptr; 
 625    }
 626    for (i = 0; i < needed_sz; i++) {
 627        flags = addr_map[i].pr_mflags;
 628	if ((flags & (MA_BREAK | MA_STACK | MA_PHYS
 629		      | MA_FETCHOP | MA_NOTCACHED)) != 0) goto irrelevant;
 630        if ((flags & (MA_READ | MA_WRITE)) != (MA_READ | MA_WRITE))
 631            goto irrelevant;
 632          /* The latter test is empirically useless in very old Irix	*/
 633	  /* versions.  Other than the					*/
 634          /* main data and stack segments, everything appears to be	*/
 635          /* mapped readable, writable, executable, and shared(!!).	*/
 636          /* This makes no sense to me.	- HB				*/
 637        start = (ptr_t)(addr_map[i].pr_vaddr);
 638        if (GC_roots_present(start)) goto irrelevant;
 639        if (start < heap_end && start >= heap_start)
 640        	goto irrelevant;
 641#	ifdef MMAP_STACKS
 642	  if (GC_is_thread_stack(start)) goto irrelevant;
 643#	endif /* MMAP_STACKS */
 644
 645        limit = start + addr_map[i].pr_size;
 646	/* The following seemed to be necessary for very old versions 	*/
 647	/* of Irix, but it has been reported to discard relevant	*/
 648	/* segments under Irix 6.5.  					*/
 649#	ifndef IRIX6
 650	  if (addr_map[i].pr_off == 0 && strncmp(start, ELFMAG, 4) == 0) {
 651	    /* Discard text segments, i.e. 0-offset mappings against	*/
 652	    /* executable files which appear to have ELF headers.	*/
 653	    caddr_t arg;
 654	    int obj;
 655#	    define MAP_IRR_SZ 10
 656	    static ptr_t map_irr[MAP_IRR_SZ];
 657	    				/* Known irrelevant map entries	*/
 658	    static int n_irr = 0;
 659	    struct stat buf;
 660	    register int i;
 661	    
 662	    for (i = 0; i < n_irr; i++) {
 663	        if (map_irr[i] == start) goto irrelevant;
 664	    }
 665	    arg = (caddr_t)start;
 666	    obj = ioctl(fd, PIOCOPENM, &arg);
 667	    if (obj >= 0) {
 668	        fstat(obj, &buf);
 669	        close(obj);
 670	        if ((buf.st_mode & 0111) != 0) {
 671	            if (n_irr < MAP_IRR_SZ) {
 672	                map_irr[n_irr++] = start;
 673	            }
 674	            goto irrelevant;
 675	        }
 676	    }
 677	  }
 678#	endif /* !IRIX6 */
 679        GC_add_roots_inner(start, limit, TRUE);
 680      irrelevant: ;
 681    }
 682    /* Dont keep cached descriptor, for now.  Some kernels don't like us */
 683    /* to keep a /proc file descriptor around during kill -9.		 */
 684    	if (close(fd) < 0) ABORT("Couldnt close /proc file");
 685	fd = -1;
 686}
 687
 688# endif /* USE_PROC || IRIX5 */
 689
 690# if defined(MSWIN32) || defined(MSWINCE) || defined(CYGWIN32)
 691
 692# define WIN32_LEAN_AND_MEAN
 693# define NOSERVICE
 694# include <windows.h>
 695# include <stdlib.h>
 696
 697  /* We traverse the entire address space and register all segments 	*/
 698  /* that could possibly have been written to.				*/
 699  
 700  extern GC_bool GC_is_heap_base (ptr_t p);
 701
 702# ifdef GC_WIN32_THREADS
 703    extern void GC_get_next_stack(char *start, char **lo, char **hi);
 704    void GC_cond_add_roots(char *base, char * limit)
 705    {
 706      char * curr_base = base;
 707      char * next_stack_lo;
 708      char * next_stack_hi;
 709   
 710      if (base == limit) return;
 711      for(;;) {
 712	  GC_get_next_stack(curr_base, &next_stack_lo, &next_stack_hi);
 713	  if (next_stack_lo >= limit) break;
 714	  GC_add_roots_inner(curr_base, next_stack_lo, TRUE);
 715	  curr_base = next_stack_hi;
 716      }
 717      if (curr_base < limit) GC_add_roots_inner(curr_base, limit, TRUE);
 718    }
 719# else
 720    void GC_cond_add_roots(char *base, char * limit)
 721    {
 722      char dummy;
 723      char * stack_top
 724	 = (char *) ((word)(&dummy) & ~(GC_sysinfo.dwAllocationGranularity-1));
 725      if (base == limit) return;
 726      if (limit > stack_top && base < GC_stackbottom) {
 727    	  /* Part of the stack; ignore it. */
 728    	  return;
 729      }
 730      GC_add_roots_inner(base, limit, TRUE);
 731    }
 732# endif
 733
 734# ifdef MSWINCE
 735  /* Do we need to separately register the main static data segment? */
 736  GC_bool GC_register_main_static_data()
 737  {
 738    return FALSE;
 739  }
 740# else /* win32 */
 741  extern GC_bool GC_no_win32_dlls;
 742
 743  GC_bool GC_register_main_static_data()
 744  {
 745    return GC_no_win32_dlls;
 746  }
 747# endif /* win32 */
 748  
 749# define HAVE_REGISTER_MAIN_STATIC_DATA
 750
 751# ifdef DEBUG_VIRTUALQUERY
 752  void GC_dump_meminfo(MEMORY_BASIC_INFORMATION *buf)
 753  {
 754    GC_printf("BaseAddress = %lx, AllocationBase = %lx, RegionSize = %lx(%lu)\n",
 755	       buf -> BaseAddress, buf -> AllocationBase, buf -> RegionSize,
 756	       buf -> RegionSize);
 757    GC_printf("\tAllocationProtect = %lx, State = %lx, Protect = %lx, "
 758	       "Type = %lx\n",
 759	       buf -> AllocationProtect, buf -> State, buf -> Protect,
 760	       buf -> Type);
 761  }
 762# endif /* DEBUG_VIRTUALQUERY */
 763
 764  extern GC_bool GC_wnt;  /* Is Windows NT derivative.		*/
 765  			  /* Defined and set in os_dep.c.	*/
 766
 767  void GC_register_dynamic_libraries()
 768  {
 769    MEMORY_BASIC_INFORMATION buf;
 770    size_t result;
 771    DWORD protect;
 772    LPVOID p;
 773    char * base;
 774    char * limit, * new_limit;
 775
 776#   ifdef MSWIN32
 777      if (GC_no_win32_dlls) return;
 778#   endif
 779    base = limit = p = GC_sysinfo.lpMinimumApplicationAddress;
 780#   if defined(MSWINCE) && !defined(_WIN32_WCE_EMULATION)
 781      /* Only the first 32 MB of address space belongs to the current process */
 782      while (p < (LPVOID)0x02000000) {
 783        result = VirtualQuery(p, &buf, sizeof(buf));
 784	if (result == 0) {
 785	    /* Page is free; advance to the next possible allocation base */
 786	    new_limit = (char *)
 787		(((DWORD) p + GC_sysinfo.dwAllocationGranularity)
 788		 & ~(GC_sysinfo.dwAllocationGranularity-1));
 789	} else
 790#   else
 791      while (p < GC_sysinfo.lpMaximumApplicationAddress) {
 792        result = VirtualQuery(p, &buf, sizeof(buf));
 793#   endif
 794	{
 795	    if (result != sizeof(buf)) {
 796		ABORT("Weird VirtualQuery result");
 797	    }
 798	    new_limit = (char *)p + buf.RegionSize;
 799	    protect = buf.Protect;
 800	    if (buf.State == MEM_COMMIT
 801		&& (protect == PAGE_EXECUTE_READWRITE
 802		    || protect == PAGE_READWRITE)
 803		&& !GC_is_heap_base(buf.AllocationBase)
 804 		/* There is some evidence that we cannot always
 805 		 * ignore MEM_PRIVATE sections under Windows ME
 806 		 * and predecessors.  Hence we now also check for
 807 		 * that case.	*/
 808 		&& (buf.Type == MEM_IMAGE ||
 809 		    !GC_wnt && buf.Type == MEM_PRIVATE)) {
 810#	        ifdef DEBUG_VIRTUALQUERY
 811	          GC_dump_meminfo(&buf);
 812#	        endif
 813		if ((char *)p != limit) {
 814		    GC_cond_add_roots(base, limit);
 815		    base = p;
 816		}
 817		limit = new_limit;
 818	    }
 819	}
 820        if (p > (LPVOID)new_limit /* overflow */) break;
 821        p = (LPVOID)new_limit;
 822    }
 823    GC_cond_add_roots(base, limit);
 824  }
 825
 826#endif /* MSWIN32 || MSWINCE || CYGWIN32 */
 827  
 828#if defined(ALPHA) && defined(OSF1)
 829
 830#include <loader.h>
 831
 832void GC_register_dynamic_libraries()
 833{
 834  int status;
 835  ldr_process_t mypid;
 836
 837  /* module */
 838    ldr_module_t moduleid = LDR_NULL_MODULE;
 839    ldr_module_info_t moduleinfo;
 840    size_t moduleinfosize = sizeof(moduleinfo);
 841    size_t modulereturnsize;    
 842
 843  /* region */
 844    ldr_region_t region; 
 845    ldr_region_info_t regioninfo;
 846    size_t regioninfosize = sizeof(regioninfo);
 847    size_t regionreturnsize;
 848
 849  /* Obtain id of this process */
 850    mypid = ldr_my_process();
 851  
 852  /* For each module */
 853    while (TRUE) {
 854
 855      /* Get the next (first) module */
 856        status = ldr_next_module(mypid, &moduleid);
 857
 858      /* Any more modules? */
 859        if (moduleid == LDR_NULL_MODULE)
 860            break;    /* No more modules */
 861
 862      /* Check status AFTER checking moduleid because */
 863      /* of a bug in the non-shared ldr_next_module stub */
 864        if (status != 0 ) {
 865            GC_printf("dynamic_load: status = %d\n", status);
 866            {
 867                extern char *sys_errlist[];
 868                extern int sys_nerr;
 869                extern int errno;
 870                if (errno <= sys_nerr) {
 871                    GC_printf("dynamic_load: %s\n", sys_errlist[errno]);
 872               } else {
 873                    GC_printf("dynamic_load: %d\n", errno);
 874                }
 875        }
 876            ABORT("ldr_next_module failed");
 877         }
 878
 879      /* Get the module information */
 880        status = ldr_inq_module(mypid, moduleid, &moduleinfo,
 881                                moduleinfosize, &modulereturnsize); 
 882        if (status != 0 )
 883            ABORT("ldr_inq_module failed");
 884
 885      /* is module for the main program (i.e. nonshared portion)? */
 886          if (moduleinfo.lmi_flags & LDR_MAIN)
 887              continue;    /* skip the main module */
 888
 889#     ifdef DL_VERBOSE
 890          GC_printf("---Module---\n");
 891          GC_printf("Module ID            = %16ld\n", moduleinfo.lmi_modid);
 892          GC_printf("Count of regions     = %16d\n", moduleinfo.lmi_nregion);
 893          GC_printf("flags for module     = %16lx\n", moduleinfo.lmi_flags); 
 894          GC_printf("pathname of module   = \"%s\"\n", moduleinfo.lmi_name);
 895#     endif
 896
 897      /* For each region in this module */
 898        for (region = 0; region < moduleinfo.lmi_nregion; region++) {
 899
 900          /* Get the region information */
 901            status = ldr_inq_region(mypid, moduleid, region, &regioninfo,
 902                                    regioninfosize, &regionreturnsize);
 903            if (status != 0 )
 904                ABORT("ldr_inq_region failed");
 905
 906          /* only process writable (data) regions */
 907            if (! (regioninfo.lri_prot & LDR_W))
 908                continue;
 909
 910#         ifdef DL_VERBOSE
 911              GC_printf("--- Region ---\n");
 912              GC_printf("Region number    = %16ld\n",
 913              	        regioninfo.lri_region_no);
 914              GC_printf("Protection flags = %016x\n",  regioninfo.lri_prot);
 915              GC_printf("Virtual address  = %16p\n",   regioninfo.lri_vaddr);
 916              GC_printf("Mapped address   = %16p\n",   regioninfo.lri_mapaddr);
 917              GC_printf("Region size      = %16ld\n",  regioninfo.lri_size);
 918              GC_printf("Region name      = \"%s\"\n", regioninfo.lri_name);
 919#         endif
 920
 921          /* register region as a garbage collection root */
 922            GC_add_roots_inner (
 923                (char *)regioninfo.lri_mapaddr,
 924                (char *)regioninfo.lri_mapaddr + regioninfo.lri_size,
 925                TRUE);
 926
 927        }
 928    }
 929}
 930#endif
 931
 932#if defined(HPUX)
 933
 934#include <errno.h>
 935#include <dl.h>
 936
 937extern char *sys_errlist[];
 938extern int sys_nerr;
 939
 940void GC_register_dynamic_libraries()
 941{
 942  int status;
 943  int index = 1; /* Ordinal position in shared library search list */
 944  struct shl_descriptor *shl_desc; /* Shared library info, see dl.h */
 945
 946  /* For each dynamic library loaded */
 947    while (TRUE) {
 948
 949      /* Get info about next shared library */
 950        status = shl_get(index, &shl_desc);
 951
 952      /* Check if this is the end of the list or if some error occured */
 953        if (status != 0) {
 954#	 ifdef GC_HPUX_THREADS
 955	   /* I've seen errno values of 0.  The man page is not clear	*/
 956	   /* as to whether errno should get set on a -1 return.	*/
 957	   break;
 958#	 else
 959          if (errno == EINVAL) {
 960              break; /* Moved past end of shared library list --> finished */
 961          } else {
 962              if (errno <= sys_nerr) {
 963                    GC_printf("dynamic_load: %s\n", sys_errlist[errno]);
 964              } else {
 965                    GC_printf("dynamic_load: %d\n", errno);
 966	      }
 967              ABORT("shl_get failed");
 968          }
 969#	 endif
 970        }
 971
 972#     ifdef DL_VERBOSE
 973          GC_printf("---Shared library---\n");
 974          GC_printf("\tfilename        = \"%s\"\n", shl_desc->filename);
 975          GC_printf("\tindex           = %d\n", index);
 976          GC_printf("\thandle          = %08x\n",
 977					(unsigned long) shl_desc->handle);
 978          GC_printf("\ttext seg. start = %08x\n", shl_desc->tstart);
 979          GC_printf("\ttext seg. end   = %08x\n", shl_desc->tend);
 980          GC_printf("\tdata seg. start = %08x\n", shl_desc->dstart);
 981          GC_printf("\tdata seg. end   = %08x\n", shl_desc->dend);
 982          GC_printf("\tref. count      = %lu\n", shl_desc->ref_count);
 983#     endif
 984
 985      /* register shared library's data segment as a garbage collection root */
 986        GC_add_roots_inner((char *) shl_desc->dstart,
 987			   (char *) shl_desc->dend, TRUE);
 988
 989        index++;
 990    }
 991}
 992#endif /* HPUX */
 993
 994#ifdef AIX
 995#pragma alloca
 996#include <sys/ldr.h>
 997#include <sys/errno.h>
 998void GC_register_dynamic_libraries()
 999{
1000	int len;
1001	char *ldibuf;
1002	int ldibuflen;
1003	struct ld_info *ldi;
1004
1005	ldibuf = alloca(ldibuflen = 8192);
1006
1007	while ( (len = loadquery(L_GETINFO,ldibuf,ldibuflen)) < 0) {
1008		if (errno != ENOMEM) {
1009			ABORT("loadquery failed");
1010		}
1011		ldibuf = alloca(ldibuflen *= 2);
1012	}
1013
1014	ldi = (struct ld_info *)ldibuf;
1015	while (ldi) {
1016		len = ldi->ldinfo_next;
1017		GC_add_roots_inner(
1018				ldi->ldinfo_dataorg,
1019				(ptr_t)(unsigned long)ldi->ldinfo_dataorg
1020			        + ldi->ldinfo_datasize,
1021				TRUE);
1022		ldi = len ? (struct ld_info *)((char *)ldi + len) : 0;
1023	}
1024}
1025#endif /* AIX */
1026
1027#ifdef DARWIN
1028
1029/* __private_extern__ hack required for pre-3.4 gcc versions.	*/
1030#ifndef __private_extern__
1031# define __private_extern__ extern
1032# include <mach-o/dyld.h>
1033# undef __private_extern__
1034#else
1035# include <mach-o/dyld.h>
1036#endif
1037#include <mach-o/getsect.h>
1038
1039/*#define DARWIN_DEBUG*/
1040
1041const static struct { 
1042        const char *seg;
1043        const char *sect;
1044} GC_dyld_sections[] = {
1045        { SEG_DATA, SECT_DATA },
1046        { SEG_DATA, SECT_BSS },
1047        { SEG_DATA, SECT_COMMON }
1048};
1049    
1050#ifdef DARWIN_DEBUG
1051static const char *GC_dyld_name_for_hdr(const struct GC_MACH_HEADER *hdr) {
1052    unsigned long i,c;
1053    c = _dyld_image_count();
1054    for(i=0;i<c;i++) if(_dyld_get_image_header(i) == hdr)
1055        return _dyld_get_image_name(i);
1056    return NULL;
1057}
1058#endif
1059        
1060/* This should never be called by a thread holding the lock */
1061static void GC_dyld_image_add(const struct GC_MACH_HEADER *hdr, intptr_t slide)
1062{
1063    unsigned long start,end,i;
1064    const struct GC_MACH_SECTION *sec;
1065    if (GC_no_dls) return;
1066    for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1067      sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
1068			     GC_dyld_sections[i].sect);
1069      if(sec == NULL || sec->size == 0) continue;
1070      start = slide + sec->addr;
1071      end = start + sec->size;
1072#   ifdef DARWIN_DEBUG
1073      GC_printf("Adding section at %p-%p (%lu bytes) from image %s\n",
1074		start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1075#   endif
1076      GC_add_roots((char*)start,(char*)end);
1077    }
1078#   ifdef DARWIN_DEBUG
1079       GC_print_static_roots();
1080#   endif
1081}
1082
1083/* This should never be called by a thread holding the lock */
1084static void GC_dyld_image_remove(const struct GC_MACH_HEADER *hdr,
1085				 intptr_t slide)
1086{
1087    unsigned long start,end,i;
1088    const struct GC_MACH_SECTION *sec;
1089    for(i=0;i<sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++) {
1090      sec = GC_GETSECTBYNAME(hdr, GC_dyld_sections[i].seg,
1091			     GC_dyld_sections[i].sect);
1092      if(sec == NULL || sec->size == 0) continue;
1093      start = slide + sec->addr;
1094      end = start + sec->size;
1095#   ifdef DARWIN_DEBUG
1096      GC_printf("Removing section at %p-%p (%lu bytes) from image %s\n",
1097		start,end,sec->size,GC_dyld_name_for_hdr(hdr));
1098#   endif
1099      GC_remove_roots((char*)start,(char*)end);
1100    }
1101#   ifdef DARWIN_DEBUG
1102	GC_print_static_roots();
1103#   endif
1104}
1105
1106void GC_register_dynamic_libraries() {
1107    /* Currently does nothing. The callbacks are setup by GC_init_dyld() 
1108    The dyld library takes it from there. */
1109}
1110
1111/* The _dyld_* functions have an internal lock so no _dyld functions
1112   can be called while the world is stopped without the risk of a deadlock.
1113   Because of this we MUST setup callbacks BEFORE we ever stop the world.
1114   This should be called BEFORE any thread in created and WITHOUT the
1115   allocation lock held. */
1116   
1117void GC_init_dyld() {
1118  static GC_bool initialized = FALSE;
1119  char *bind_fully_env = NULL;
1120  
1121  if(initialized) return;
1122  
1123#   ifdef DARWIN_DEBUG
1124      GC_printf("Registering dyld callbacks...\n");
1125#   endif
1126  
1127  /* Apple's Documentation:
1128     When you call _dyld_register_func_for_add_image, the dynamic linker runtime
1129     calls the specified callback (func) once for each of the images that is
1130     currently loaded into the program. When a new image is added to the program,
1131     your callback is called again with the mach_header for the new image, and the 	
1132     virtual memory slide amount of the new image. 
1133     
1134     This WILL properly register already linked libraries and libraries 
1135     linked in the future
1136  */
1137  
1138    _dyld_register_func_for_add_image(GC_dyld_image_add);
1139    _dyld_register_func_for_remove_image(GC_dyld_image_remove);
1140
1141    /* Set this early to avoid reentrancy issues. */
1142    initialized = TRUE;
1143
1144    bind_fully_env = getenv("DYLD_BIND_AT_LAUNCH");
1145    
1146    if (bind_fully_env == NULL) {
1147#   ifdef DARWIN_DEBUG
1148      GC_printf("Forcing full bind of GC code...\n");
1149#   endif
1150      
1151      if(!_dyld_bind_fully_image_containing_address((unsigned long*)GC_malloc))
1152        GC_abort("_dyld_bind_fully_image_containing_address failed");
1153    }
1154
1155}
1156
1157#define HAVE_REGISTER_MAIN_STATIC_DATA
1158GC_bool GC_register_main_static_data()
1159{
1160  /* Already done through dyld callbacks */
1161  return FALSE;
1162}
1163
1164#endif /* DARWIN */
1165
1166#else /* !DYNAMIC_LOADING */
1167
1168#ifdef PCR
1169
1170#   include "il/PCR_IL.h"
1171#   include "th/PCR_ThCtl.h"
1172#   include "mm/PCR_MM.h"
1173
1174void GC_register_dynamic_libraries()
1175{
1176    /* Add new static data areas of dynamically loaded modules.	*/
1177        {
1178          PCR_IL_LoadedFile * p = PCR_IL_GetLastLoadedFile();
1179          PCR_IL_LoadedSegment * q;
1180          
1181          /* Skip uncommited files */
1182          while (p != NIL && !(p -> lf_commitPoint)) {
1183              /* The loading of this file has not yet been committed	*/
1184              /* Hence its description could be inconsistent.  		*/
1185              /* Furthermore, it hasn't yet been run.  Hence its data	*/
1186              /* segments can't possibly reference heap allocated	*/
1187              /* objects.						*/
1188              p = p -> lf_prev;
1189          }
1190          for (; p != NIL; p = p -> lf_prev) {
1191            for (q = p -> lf_ls; q != NIL; q = q -> ls_next) {
1192              if ((q -> ls_flags & PCR_IL_SegFlags_Traced_MASK)
1193                  == PCR_IL_SegFlags_Traced_on) {
1194                GC_add_roots_inner
1195                	((char *)(q -> ls_addr), 
1196                	 (char *)(q -> ls_addr) + q -> ls_bytes,
1197                	 TRUE);
1198              }
1199            }
1200          }
1201        }
1202}
1203
1204
1205#else /* !PCR */
1206
1207void GC_register_dynamic_libraries(){}
1208
1209int GC_no_dynamic_loading;
1210
1211#endif /* !PCR */
1212
1213#endif /* !DYNAMIC_LOADING */
1214
1215#ifndef HAVE_REGISTER_MAIN_STATIC_DATA
1216
1217/* Do we need to separately register the main static data segment? */
1218GC_bool GC_register_main_static_data()
1219{
1220  return TRUE;
1221}
1222
1223/* Register a routine to filter dynamic library registration.  */
1224void
1225GC_register_has_static_roots_callback
1226  (int (*callback)(const char *, void *, size_t)) {
1227  GC_has_static_roots = callback;
1228}
1229
1230#endif /* HAVE_REGISTER_MAIN_STATIC_DATA */
1231