|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. ! 3: * ! 4: * @APPLE_LICENSE_HEADER_START@ ! 5: * ! 6: * The contents of this file constitute Original Code as defined in and ! 7: * are subject to the Apple Public Source License Version 1.1 (the ! 8: * "License"). You may not use this file except in compliance with the ! 9: * License. Please obtain a copy of the License at ! 10: * http://www.apple.com/publicsource and read it before using this file. ! 11: * ! 12: * This Original Code and all software distributed under the License are ! 13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER ! 14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, ! 15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, ! 16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the ! 17: * License for the specific language governing rights and limitations ! 18: * under the License. ! 19: * ! 20: * @APPLE_LICENSE_HEADER_END@ ! 21: */ ! 22: /* ! 23: * COPYRIGHT NOTICE ! 24: * ! 25: * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. ! 26: * ! 27: * Permission is hereby granted to use, copy, modify and freely distribute ! 28: * the software in this file and its documentation for any purpose without ! 29: * fee, provided that the above copyright notice appears in all copies and ! 30: * that both the copyright notice and this permission notice appear in ! 31: * supporting documentation. Further, provided that the name of Open ! 32: * Software Foundation, Inc. ("OSF") not be used in advertising or ! 33: * publicity pertaining to distribution of the software without prior ! 34: * written permission from OSF. OSF makes no representations about the ! 35: * suitability of this software for any purpose. It is provided "as is" ! 36: * without express or implied warranty. ! 37: */ ! 38: /* ! 39: * HISTORY ! 40: * ! 41: * Revision 1.1.1.1 1998/09/22 21:05:36 wsanchez ! 42: * Import of Mac OS X kernel (~semeria) ! 43: * ! 44: * Revision 1.1.1.1 1998/03/07 02:25:37 wsanchez ! 45: * Import of OSF Mach kernel (~mburg) ! 46: * ! 47: * Revision 1.2.2.3 1994/01/28 17:23:00 chasb ! 48: * Expand Copyrights ! 49: * [1994/01/27 19:40:16 chasb] ! 50: * ! 51: * Revision 1.2.2.2 1993/06/09 02:27:36 gm ! 52: * Added to OSF/1 R1.3 from NMK15.0. ! 53: * [1993/06/02 21:04:03 jeffc] ! 54: * ! 55: * Revision 1.2 1993/04/19 16:13:10 devrcs ! 56: * pick up file_io.h from bootstrap directory ! 57: * [1993/02/27 15:01:09 david] ! 58: * ! 59: * Added new arguments and a missing one to db_add_symbol_table ! 60: * [[email protected]] ! 61: * [92/12/03 bernadat] ! 62: * ! 63: * Added gcc symbol table handling based on db_aout.c (Revsion 2.4) ! 64: * [91/07/31 tak] ! 65: * ! 66: * Revision 1.1 1992/09/30 02:02:23 robert ! 67: * Initial revision ! 68: * ! 69: * $EndLog$ ! 70: */ ! 71: /* CMU_HIST */ ! 72: /* ! 73: * Revision 2.1 91/07/31 13:13:51 jeffreyh ! 74: * Created. ! 75: * ! 76: * 31-Jul-91 Jeffrey Heller (tak) at Open Software Foundation ! 77: * Added gcc symbol table handling based on db_aout.c (Revsion 2.4) ! 78: * ! 79: */ ! 80: /* CMU_ENDHIST */ ! 81: /* ! 82: * Mach Operating System ! 83: * Copyright (c) 1991,1990 Carnegie Mellon University ! 84: * All Rights Reserved. ! 85: * ! 86: * Permission to use, copy, modify and distribute this software and its ! 87: * documentation is hereby granted, provided that both the copyright ! 88: * notice and this permission notice appear in all copies of the ! 89: * software, derivative works or modified versions, and any portions ! 90: * thereof, and that both notices appear in supporting documentation. ! 91: * ! 92: * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" ! 93: * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR ! 94: * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. ! 95: * ! 96: * Carnegie Mellon requests users of this software to return to ! 97: * ! 98: * Software Distribution Coordinator or [email protected] ! 99: * School of Computer Science ! 100: * Carnegie Mellon University ! 101: * Pittsburgh PA 15213-3890 ! 102: * ! 103: * any improvements or extensions that they make and grant Carnegie Mellon ! 104: * the rights to redistribute these changes. ! 105: */ ! 106: /* ! 107: */ ! 108: /* ! 109: * Symbol table routines for a.out format files. ! 110: */ ! 111: ! 112: #include <mach/boolean.h> ! 113: #include <machine/db_machdep.h> /* data types */ ! 114: #include <ddb/db_sym.h> ! 115: ! 116: #ifdef DB_GCC_AOUT ! 117: ! 118: #include <ddb/nlist.h> /* a.out symbol table */ ! 119: #include <i386/stab.h> ! 120: ! 121: /* ! 122: * An a.out symbol table as loaded into the kernel debugger: ! 123: * ! 124: * symtab -> size of symbol entries, in bytes ! 125: * sp -> first symbol entry ! 126: * ... ! 127: * ep -> last symbol entry + 1 ! 128: * strtab == start of string table ! 129: * size of string table in bytes, ! 130: * including this word ! 131: * -> strings ! 132: */ ! 133: ! 134: /* ! 135: * Find pointers to the start and end of the symbol entries, ! 136: * given a pointer to the start of the symbol table. ! 137: */ ! 138: #define db_get_aout_symtab(symtab, sp, ep) \ ! 139: (sp = (struct nlist *)((symtab) + 1), \ ! 140: ep = (struct nlist *)((char *)sp + *(symtab))) ! 141: ! 142: X_db_sym_init(symtab, esymtab, name) ! 143: int * symtab; /* pointer to start of symbol table */ ! 144: char * esymtab; /* pointer to end of string table, ! 145: for checking - rounded up to integer ! 146: boundary */ ! 147: char * name; ! 148: { ! 149: register struct nlist *sym_start, *sym_end; ! 150: register struct nlist *sp; ! 151: register char * strtab; ! 152: register int strlen; ! 153: ! 154: db_get_aout_symtab(symtab, sym_start, sym_end); ! 155: ! 156: strtab = (char *)sym_end; ! 157: strlen = *(int *)strtab; ! 158: ! 159: if (strtab + ((strlen + sizeof(int) - 1) & ~(sizeof(int)-1)) ! 160: != esymtab) ! 161: { ! 162: db_printf("[ %s symbol table not valid ]\n", name); ! 163: return; ! 164: } ! 165: ! 166: db_printf("[ preserving %#x bytes of %s symbol table ]\n", ! 167: esymtab - (char *)symtab, name); ! 168: ! 169: for (sp = sym_start; sp < sym_end; sp++) { ! 170: register int strx; ! 171: strx = sp->n_un.n_strx; ! 172: if (strx != 0) { ! 173: if (strx > strlen) { ! 174: db_printf("Bad string table index (%#x)\n", strx); ! 175: sp->n_un.n_name = 0; ! 176: continue; ! 177: } ! 178: sp->n_un.n_name = strtab + strx; ! 179: } ! 180: } ! 181: ! 182: db_add_symbol_table(sym_start, sym_end, name, (char *)symtab, ! 183: 0, 0, 0, FALSE); ! 184: } ! 185: ! 186: /* ! 187: * check file name or not (check xxxx.x pattern) ! 188: */ ! 189: boolean_t ! 190: X_db_is_filename(name) ! 191: register char *name; ! 192: { ! 193: while (*name) { ! 194: if (*name == '.') { ! 195: if (name[1]) ! 196: return(TRUE); ! 197: } ! 198: name++; ! 199: } ! 200: return(FALSE); ! 201: } ! 202: ! 203: /* ! 204: * special name comparison routine with a name in the symbol table entry ! 205: */ ! 206: boolean_t ! 207: X_db_eq_name(sp, name) ! 208: struct nlist *sp; ! 209: char *name; ! 210: { ! 211: register char *s1, *s2; ! 212: ! 213: s1 = sp->n_un.n_name; ! 214: s2 = name; ! 215: if (*s1 == '_' && *s2 && *s2 != '_') ! 216: s1++; ! 217: while (*s2) { ! 218: if (*s1++ != *s2++) { ! 219: /* ! 220: * check .c .o file name comparison case ! 221: */ ! 222: if (*s2 == 0 && sp->n_un.n_name <= s1 - 2 ! 223: && s1[-2] == '.' && s1[-1] == 'o') ! 224: return(TRUE); ! 225: return(FALSE); ! 226: } ! 227: } ! 228: /* ! 229: * do special check for ! 230: * xxx:yyy for N_FUN ! 231: * xxx.ttt for N_DATA and N_BSS ! 232: */ ! 233: return(*s1 == 0 || (*s1 == ':' && sp->n_type == N_FUN) || ! 234: (*s1 == '.' && (sp->n_type == N_DATA || sp->n_type == N_BSS))); ! 235: } ! 236: ! 237: /* ! 238: * search a symbol table with name and type ! 239: * fp(in,out): last found text file name symbol entry ! 240: */ ! 241: struct nlist * ! 242: X_db_search_name(sp, ep, name, type, fp) ! 243: register struct nlist *sp; ! 244: struct nlist *ep; ! 245: char *name; ! 246: int type; ! 247: struct nlist **fp; ! 248: { ! 249: struct nlist *file_sp = *fp; ! 250: struct nlist *found_sp = 0; ! 251: ! 252: for ( ; sp < ep; sp++) { ! 253: if (sp->n_type == N_TEXT && X_db_is_filename(sp->n_un.n_name)) ! 254: *fp = sp; ! 255: if (type) { ! 256: if (sp->n_type == type) { ! 257: if (X_db_eq_name(sp, name)) ! 258: return(sp); ! 259: } ! 260: if (sp->n_type == N_SO) ! 261: *fp = sp; ! 262: continue; ! 263: } ! 264: if (sp->n_type & N_STAB) ! 265: continue; ! 266: if (sp->n_un.n_name && X_db_eq_name(sp, name)) { ! 267: /* ! 268: * In case of qaulified search by a file, ! 269: * return it immediately with some check. ! 270: * Otherwise, search external one ! 271: */ ! 272: if (file_sp) { ! 273: if ((file_sp == *fp) || (sp->n_type & N_EXT)) ! 274: return(sp); ! 275: } else if (sp->n_type & N_EXT) ! 276: return(sp); ! 277: else ! 278: found_sp = sp; ! 279: } ! 280: } ! 281: return(found_sp); ! 282: } ! 283: ! 284: /* ! 285: * search a symbol with file, func and line qualification ! 286: */ ! 287: struct nlist * ! 288: X_db_qualified_search(stab, file, sym, line) ! 289: db_symtab_t *stab; ! 290: char *file; ! 291: char *sym; ! 292: int line; ! 293: { ! 294: register struct nlist *sp = (struct nlist *)stab->start; ! 295: struct nlist *ep = (struct nlist *)stab->end; ! 296: struct nlist *fp = 0; ! 297: struct nlist *found_sp; ! 298: unsigned func_top; ! 299: boolean_t in_file; ! 300: ! 301: if (file == 0 && sym == 0) ! 302: return(0); ! 303: if (file) { ! 304: if ((sp = X_db_search_name(sp, ep, file, N_TEXT, &fp)) == 0) ! 305: return(0); ! 306: } ! 307: if (sym) { ! 308: sp = X_db_search_name(sp, ep, sym, (line > 0)? N_FUN: 0, &fp); ! 309: if (sp == 0) ! 310: return(0); ! 311: } ! 312: if (line > 0) { ! 313: if (file && !X_db_eq_name(fp, file)) ! 314: return(0); ! 315: found_sp = 0; ! 316: if (sp->n_type == N_FUN) { ! 317: /* ! 318: * qualfied by function name ! 319: * search backward because line number entries ! 320: * for the function are above it in this case. ! 321: */ ! 322: func_top = sp->n_value; ! 323: for (sp--; sp >= (struct nlist *)stab->start; sp--) { ! 324: if (sp->n_type != N_SLINE) ! 325: continue; ! 326: if (sp->n_value < func_top) ! 327: break; ! 328: if (sp->n_desc <= line) { ! 329: if (found_sp == 0 || found_sp->n_desc < sp->n_desc) ! 330: found_sp = sp; ! 331: if (sp->n_desc == line) ! 332: break; ! 333: } ! 334: } ! 335: if (sp->n_type != N_SLINE || sp->n_value < func_top) ! 336: return(0); ! 337: } else { ! 338: /* ! 339: * qualified by only file name ! 340: * search forward in this case ! 341: */ ! 342: in_file = TRUE; ! 343: for (sp++; sp < ep; sp++) { ! 344: if (sp->n_type == N_TEXT ! 345: && X_db_is_filename(sp->n_un.n_name)) ! 346: break; /* enter into another file */ ! 347: if (sp->n_type == N_SOL) { ! 348: in_file = X_db_eq_name(sp, file); ! 349: continue; ! 350: } ! 351: if (!in_file || sp->n_type != N_SLINE) ! 352: continue; ! 353: if (sp->n_desc <= line) { ! 354: if (found_sp == 0 || found_sp->n_desc < sp->n_desc) ! 355: found_sp = sp; ! 356: if (sp->n_desc == line) ! 357: break; ! 358: } ! 359: } ! 360: } ! 361: sp = found_sp; ! 362: } ! 363: return(sp); ! 364: } ! 365: ! 366: /* ! 367: * lookup symbol by name ! 368: */ ! 369: db_sym_t ! 370: X_db_lookup(stab, symstr) ! 371: db_symtab_t *stab; ! 372: char * symstr; ! 373: { ! 374: register char *p; ! 375: register n; ! 376: int n_name; ! 377: int line_number; ! 378: char *file_name = 0; ! 379: char *sym_name = 0; ! 380: char *component[3]; ! 381: struct nlist *found = 0; ! 382: ! 383: /* ! 384: * disassemble component: [file_name:]symbol[:line_nubmer] ! 385: */ ! 386: component[0] = symstr; ! 387: component[1] = component[2] = 0; ! 388: for (p = symstr, n = 1; *p; p++) { ! 389: if (*p == ':') { ! 390: if (n >= 3) ! 391: break; ! 392: *p = 0; ! 393: component[n++] = p+1; ! 394: } ! 395: } ! 396: if (*p != 0) ! 397: goto out; ! 398: line_number = 0; ! 399: n_name = n; ! 400: p = component[n-1]; ! 401: if (*p >= '0' && *p <= '9') { ! 402: if (n == 1) ! 403: goto out; ! 404: for (line_number = 0; *p; p++) { ! 405: if (*p < '0' || *p > '9') ! 406: goto out; ! 407: line_number = line_number*10 + *p - '0'; ! 408: } ! 409: n_name--; ! 410: } else if (n >= 3) ! 411: goto out; ! 412: if (n_name == 1) { ! 413: if (X_db_is_filename(component[0])) { ! 414: file_name = component[0]; ! 415: sym_name = 0; ! 416: } else { ! 417: file_name = 0; ! 418: sym_name = component[0]; ! 419: } ! 420: } else { ! 421: file_name = component[0]; ! 422: sym_name = component[1]; ! 423: } ! 424: found = X_db_qualified_search(stab, file_name, sym_name, line_number); ! 425: ! 426: out: ! 427: while (--n > 1) ! 428: component[n][-1] = ':'; ! 429: return((db_sym_t) found); ! 430: } ! 431: ! 432: db_sym_t ! 433: X_db_search_symbol(symtab, off, strategy, diffp) ! 434: db_symtab_t * symtab; ! 435: register ! 436: db_addr_t off; ! 437: db_strategy_t strategy; ! 438: db_expr_t *diffp; /* in/out */ ! 439: { ! 440: register unsigned int diff = *diffp; ! 441: register struct nlist *symp = 0; ! 442: register struct nlist *sp, *ep; ! 443: ! 444: sp = (struct nlist *)symtab->start; ! 445: ep = (struct nlist *)symtab->end; ! 446: ! 447: for (; sp < ep; sp++) { ! 448: if (sp->n_un.n_name == 0) ! 449: continue; ! 450: if ((sp->n_type & N_STAB) != 0) ! 451: continue; ! 452: if (off >= sp->n_value) { ! 453: if (off - sp->n_value < diff) { ! 454: diff = off - sp->n_value; ! 455: symp = sp; ! 456: if (diff == 0 && (sp->n_type & N_EXT)) ! 457: break; ! 458: } ! 459: else if (off - sp->n_value == diff) { ! 460: if (symp == 0) ! 461: symp = sp; ! 462: else if ((symp->n_type & N_EXT) == 0 && ! 463: (sp->n_type & N_EXT) != 0) ! 464: symp = sp; /* pick the external symbol */ ! 465: } ! 466: } ! 467: } ! 468: if (symp == 0) { ! 469: *diffp = off; ! 470: } ! 471: else { ! 472: *diffp = diff; ! 473: } ! 474: return ((db_sym_t)symp); ! 475: } ! 476: ! 477: /* ! 478: * Return the name and value for a symbol. ! 479: */ ! 480: void ! 481: X_db_symbol_values(sym, namep, valuep) ! 482: db_sym_t sym; ! 483: char **namep; ! 484: db_expr_t *valuep; ! 485: { ! 486: register struct nlist *sp; ! 487: ! 488: sp = (struct nlist *)sym; ! 489: if (namep) ! 490: *namep = sp->n_un.n_name; ! 491: if (valuep) ! 492: *valuep = sp->n_value; ! 493: } ! 494: ! 495: #define X_DB_MAX_DIFF 8 /* maximum allowable diff at the end of line */ ! 496: ! 497: /* ! 498: * search symbol by value ! 499: */ ! 500: X_db_search_by_addr(stab, addr, file, func, line, diff) ! 501: db_symtab_t *stab; ! 502: register unsigned addr; ! 503: char **file; ! 504: char **func; ! 505: int *line; ! 506: unsigned *diff; ! 507: { ! 508: register struct nlist *sp; ! 509: register struct nlist *line_sp, *func_sp, *file_sp, *line_func; ! 510: register func_diff, line_diff; ! 511: boolean_t found_line = FALSE; ! 512: struct nlist *ep = (struct nlist *)stab->end; ! 513: ! 514: line_sp = func_sp = file_sp = line_func = 0; ! 515: *file = *func = 0; ! 516: *line = 0; ! 517: for (sp = (struct nlist *)stab->start; sp < ep; sp++) { ! 518: switch(sp->n_type) { ! 519: case N_SLINE: ! 520: if (sp->n_value <= addr) { ! 521: if (line_sp == 0 || line_diff >= addr - sp->n_value) { ! 522: if (line_func) ! 523: line_func = 0; ! 524: line_sp = sp; ! 525: line_diff = addr - sp->n_value; ! 526: } ! 527: } ! 528: if (sp->n_value >= addr && line_sp) ! 529: found_line = TRUE; ! 530: continue; ! 531: case N_FUN: ! 532: if ((found_line || (line_sp && line_diff < X_DB_MAX_DIFF)) ! 533: && line_func == 0) ! 534: line_func = sp; ! 535: continue; ! 536: case N_TEXT: ! 537: if (X_db_is_filename(sp->n_un.n_name)) { ! 538: if (sp->n_value > addr) ! 539: continue; ! 540: if (file_sp == 0 || file_sp->n_value < sp->n_value) ! 541: file_sp = sp; ! 542: } else if (sp->n_value <= addr && ! 543: (func_sp == 0 || func_diff > addr - sp->n_value)) { ! 544: func_sp = sp; ! 545: func_diff = addr - sp->n_value; ! 546: } ! 547: continue; ! 548: case N_TEXT|N_EXT: ! 549: if (sp->n_value <= addr && ! 550: (func_sp == 0 || func_diff >= addr - sp->n_value)) { ! 551: func_sp = sp; ! 552: func_diff = addr - sp->n_value; ! 553: if (func_diff == 0 && file_sp && func_sp) ! 554: break; ! 555: } ! 556: default: ! 557: continue; ! 558: } ! 559: break; ! 560: } ! 561: if (line_sp) { ! 562: if (line_func == 0 || func_sp == 0 ! 563: || line_func->n_value != func_sp->n_value) ! 564: line_sp = 0; ! 565: } ! 566: if (file_sp) { ! 567: *diff = addr - file_sp->n_value; ! 568: *file = file_sp->n_un.n_name; ! 569: } ! 570: if (func_sp) { ! 571: *diff = addr - func_sp->n_value; ! 572: *func = (func_sp->n_un.n_name[0] == '_')? ! 573: func_sp->n_un.n_name + 1: func_sp->n_un.n_name; ! 574: } ! 575: if (line_sp) { ! 576: *diff = addr - line_sp->n_value; ! 577: *line = line_sp->n_desc; ! 578: } ! 579: return(file_sp || func_sp || line_sp); ! 580: } ! 581: ! 582: /* ARGSUSED */ ! 583: boolean_t ! 584: X_db_line_at_pc(stab, sym, file, line, pc) ! 585: db_symtab_t *stab; ! 586: db_sym_t sym; ! 587: char **file; ! 588: int *line; ! 589: db_expr_t pc; ! 590: { ! 591: char *func; ! 592: unsigned diff; ! 593: boolean_t found; ! 594: ! 595: found = X_db_search_by_addr(stab,(unsigned)pc,file,&func,line,&diff); ! 596: return(found && func && *file); ! 597: } ! 598: ! 599: /* ! 600: * Initialization routine for a.out files. ! 601: */ ! 602: kdb_init() ! 603: { ! 604: extern char *esym; ! 605: extern int end; ! 606: ! 607: if (esym > (char *)&end) { ! 608: X_db_sym_init((int *)&end, esym, "mach"); ! 609: } ! 610: } ! 611: ! 612: /* ! 613: * Read symbol table from file. ! 614: * (should be somewhere else) ! 615: */ ! 616: #include <bootstrap/file_io.h> ! 617: #include <vm/vm_kern.h> ! 618: ! 619: read_symtab_from_file(fp, symtab_name) ! 620: struct file *fp; ! 621: char * symtab_name; ! 622: { ! 623: vm_size_t resid; ! 624: kern_return_t result; ! 625: vm_offset_t symoff; ! 626: vm_size_t symsize; ! 627: vm_offset_t stroff; ! 628: vm_size_t strsize; ! 629: vm_size_t table_size; ! 630: vm_offset_t symtab; ! 631: ! 632: if (!get_symtab(fp, &symoff, &symsize)) { ! 633: boot_printf("[ error %d reading %s file header ]\n", ! 634: result, symtab_name); ! 635: return; ! 636: } ! 637: ! 638: stroff = symoff + symsize; ! 639: result = read_file(fp, (vm_offset_t)stroff, ! 640: (vm_offset_t)&strsize, sizeof(strsize), &resid); ! 641: if (result || resid) { ! 642: boot_printf("[ no valid symbol table present for %s ]\n", ! 643: symtab_name); ! 644: return; ! 645: } ! 646: ! 647: table_size = sizeof(int) + symsize + strsize; ! 648: table_size = (table_size + sizeof(int)-1) & ~(sizeof(int)-1); ! 649: ! 650: result = kmem_alloc_wired(kernel_map, &symtab, table_size); ! 651: if (result) { ! 652: boot_printf("[ error %d allocating space for %s symbol table ]\n", ! 653: result, symtab_name); ! 654: return; ! 655: } ! 656: ! 657: *(int *)symtab = symsize; ! 658: ! 659: result = read_file(fp, symoff, ! 660: symtab + sizeof(int), symsize, &resid); ! 661: if (result || resid) { ! 662: boot_printf("[ error %d reading %s symbol table ]\n", ! 663: result, symtab_name); ! 664: return; ! 665: } ! 666: ! 667: result = read_file(fp, stroff, ! 668: symtab + sizeof(int) + symsize, strsize, &resid); ! 669: if (result || resid) { ! 670: boot_printf("[ error %d reading %s string table ]\n", ! 671: result, symtab_name); ! 672: return; ! 673: } ! 674: ! 675: X_db_sym_init((int *)symtab, ! 676: (char *)(symtab + table_size), ! 677: symtab_name); ! 678: ! 679: } ! 680: ! 681: #endif /* DB_GCC_AOUT */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.