agent/src/os/linux/symtab.c

Fri, 25 Jan 2013 16:50:33 -0800

author
morris
date
Fri, 25 Jan 2013 16:50:33 -0800
changeset 4535
9fae07c31641
parent 2384
0a8e0d4345b3
child 5797
f2512d89ad0c
permissions
-rw-r--r--

6518907: cleanup IA64 specific code in Hotspot
Summary: removed unused IA64 specific code
Reviewed-by: twisti, kvn, dholmes

     1 /*
     2  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.
     8  *
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    12  * version 2 for more details (a copy is included in the LICENSE file that
    13  * accompanied this code).
    14  *
    15  * You should have received a copy of the GNU General Public License version
    16  * 2 along with this work; if not, write to the Free Software Foundation,
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    18  *
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    20  * or visit www.oracle.com if you need additional information or have any
    21  * questions.
    22  *
    23  */
    25 #include <unistd.h>
    26 #include <sys/procfs.h>
    27 #include <search.h>
    28 #include <stdlib.h>
    29 #include <string.h>
    30 #include "symtab.h"
    31 #include "salibelf.h"
    34 // ----------------------------------------------------
    35 // functions for symbol lookups
    36 // ----------------------------------------------------
    38 struct elf_section {
    39   ELF_SHDR   *c_shdr;
    40   void       *c_data;
    41 };
    43 struct elf_symbol {
    44   char *name;
    45   uintptr_t offset;
    46   uintptr_t size;
    47 };
    49 typedef struct symtab {
    50   char *strs;
    51   size_t num_symbols;
    52   struct elf_symbol *symbols;
    53   struct hsearch_data *hash_table;
    54 } symtab_t;
    57 // Directory that contains global debuginfo files.  In theory it
    58 // should be possible to change this, but in a Java environment there
    59 // is no obvious place to put a user interface to do it.  Maybe this
    60 // could be set with an environment variable.
    61 static const char debug_file_directory[] = "/usr/lib/debug";
    63 /* The CRC used in gnu_debuglink, retrieved from
    64    http://sourceware.org/gdb/current/onlinedocs/gdb/Separate-Debug-Files.html#Separate-Debug-Files. */
    65 unsigned int gnu_debuglink_crc32 (unsigned int crc,
    66                                   unsigned char *buf, size_t len)
    67 {
    68   static const unsigned int crc32_table[256] =
    69     {
    70       0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419,
    71       0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4,
    72       0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07,
    73       0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
    74       0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856,
    75       0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    76       0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
    77       0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    78       0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3,
    79       0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a,
    80       0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599,
    81       0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    82       0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190,
    83       0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
    84       0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e,
    85       0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    86       0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed,
    87       0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    88       0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3,
    89       0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
    90       0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
    91       0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5,
    92       0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010,
    93       0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    94       0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17,
    95       0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6,
    96       0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615,
    97       0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
    98       0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344,
    99       0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
   100       0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a,
   101       0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
   102       0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1,
   103       0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c,
   104       0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
   105       0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
   106       0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe,
   107       0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31,
   108       0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c,
   109       0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
   110       0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b,
   111       0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
   112       0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1,
   113       0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
   114       0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278,
   115       0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7,
   116       0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66,
   117       0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
   118       0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
   119       0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8,
   120       0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b,
   121       0x2d02ef8d
   122     };
   123   unsigned char *end;
   125   crc = ~crc & 0xffffffff;
   126   for (end = buf + len; buf < end; ++buf)
   127     crc = crc32_table[(crc ^ *buf) & 0xff] ^ (crc >> 8);
   128   return ~crc & 0xffffffff;
   129 }
   131 /* Open a debuginfo file and check its CRC.  If it exists and the CRC
   132    matches return its fd.  */
   133 static int
   134 open_debug_file (const char *pathname, unsigned int crc)
   135 {
   136   unsigned int file_crc = 0;
   137   unsigned char buffer[8 * 1024];
   139   int fd = pathmap_open(pathname);
   141   if (fd < 0)
   142     return -1;
   144   lseek(fd, 0, SEEK_SET);
   146   for (;;) {
   147     int len = read(fd, buffer, sizeof buffer);
   148     if (len <= 0)
   149       break;
   150     file_crc = gnu_debuglink_crc32(file_crc, buffer, len);
   151   }
   153   if (crc == file_crc)
   154     return fd;
   155   else {
   156     close(fd);
   157     return -1;
   158   }
   159 }
   161 /* Find an ELF section.  */
   162 static struct elf_section *find_section_by_name(char *name,
   163                                                 int fd,
   164                                                 ELF_EHDR *ehdr,
   165                                                 ELF_SHDR *shbuf,
   166                                                 struct elf_section *scn_cache)
   167 {
   168   ELF_SHDR* cursct = NULL;
   169   char *strtab;
   170   int cnt;
   172   if (scn_cache[ehdr->e_shstrndx].c_data == NULL) {
   173     if ((scn_cache[ehdr->e_shstrndx].c_data
   174          = read_section_data(fd, ehdr, cursct)) == NULL) {
   175       return NULL;
   176     }
   177   }
   179   strtab = scn_cache[ehdr->e_shstrndx].c_data;
   181   for (cursct = shbuf, cnt = 0;
   182        cnt < ehdr->e_shnum;
   183        cnt++, cursct++) {
   184     if (strcmp(cursct->sh_name + strtab, name) == 0) {
   185       scn_cache[cnt].c_data = read_section_data(fd, ehdr, cursct);
   186       return &scn_cache[cnt];
   187     }
   188   }
   190   return NULL;
   191 }
   193 /* Look for a ".gnu_debuglink" section.  If one exists, try to open a
   194    suitable debuginfo file.  */
   195 static int open_file_from_debug_link(const char *name,
   196                                      int fd,
   197                                      ELF_EHDR *ehdr,
   198                                      ELF_SHDR *shbuf,
   199                                      struct elf_section *scn_cache)
   200 {
   201   int debug_fd;
   202   struct elf_section *debug_link = find_section_by_name(".gnu_debuglink", fd, ehdr,
   203                                                         shbuf, scn_cache);
   204   if (debug_link == NULL)
   205     return -1;
   206   char *debug_filename = debug_link->c_data;
   207   int offset = (strlen(debug_filename) + 4) >> 2;
   208   static unsigned int crc;
   209   crc = ((unsigned int*)debug_link->c_data)[offset];
   210   char *debug_pathname = malloc(strlen(debug_filename)
   211                                 + strlen(name)
   212                                 + strlen(".debug/")
   213                                 + strlen(debug_file_directory)
   214                                 + 2);
   215   strcpy(debug_pathname, name);
   216   char *last_slash = strrchr(debug_pathname, '/');
   217   if (last_slash == NULL)
   218     return -1;
   220   /* Look in the same directory as the object.  */
   221   strcpy(last_slash+1, debug_filename);
   223   debug_fd = open_debug_file(debug_pathname, crc);
   224   if (debug_fd >= 0) {
   225     free(debug_pathname);
   226     return debug_fd;
   227   }
   229   /* Look in a subdirectory named ".debug".  */
   230   strcpy(last_slash+1, ".debug/");
   231   strcat(last_slash, debug_filename);
   233   debug_fd = open_debug_file(debug_pathname, crc);
   234   if (debug_fd >= 0) {
   235     free(debug_pathname);
   236     return debug_fd;
   237   }
   239   /* Look in /usr/lib/debug + the full pathname.  */
   240   strcpy(debug_pathname, debug_file_directory);
   241   strcat(debug_pathname, name);
   242   last_slash = strrchr(debug_pathname, '/');
   243   strcpy(last_slash+1, debug_filename);
   245   debug_fd = open_debug_file(debug_pathname, crc);
   246   if (debug_fd >= 0) {
   247     free(debug_pathname);
   248     return debug_fd;
   249   }
   251   free(debug_pathname);
   252   return -1;
   253 }
   255 static struct symtab* build_symtab_internal(int fd, const char *filename, bool try_debuginfo);
   257 /* Look for a ".gnu_debuglink" section.  If one exists, try to open a
   258    suitable debuginfo file and read a symbol table from it.  */
   259 static struct symtab *build_symtab_from_debug_link(const char *name,
   260                                      int fd,
   261                                      ELF_EHDR *ehdr,
   262                                      ELF_SHDR *shbuf,
   263                                      struct elf_section *scn_cache)
   264 {
   265   fd = open_file_from_debug_link(name, fd, ehdr, shbuf, scn_cache);
   267   if (fd >= 0) {
   268     struct symtab *symtab = build_symtab_internal(fd, NULL, /* try_debuginfo */ false);
   269     close(fd);
   270     return symtab;
   271   }
   273   return NULL;
   274 }
   276 // Given a build_id, find the associated debuginfo file
   277 static char *
   278 build_id_to_debug_filename (size_t size, unsigned char *data)
   279 {
   280   char *filename, *s;
   282   filename = malloc(strlen (debug_file_directory) + (sizeof "/.build-id/" - 1) + 1
   283                     + 2 * size + (sizeof ".debug" - 1) + 1);
   284   s = filename + sprintf (filename, "%s/.build-id/", debug_file_directory);
   285   if (size > 0)
   286     {
   287       size--;
   288       s += sprintf (s, "%02x", *data++);
   289     }
   290   if (size > 0)
   291     *s++ = '/';
   292   while (size-- > 0)
   293     s += sprintf (s, "%02x", *data++);
   294   strcpy (s, ".debug");
   296   return filename;
   297 }
   299 // Read a build ID note.  Try to open any associated debuginfo file
   300 // and return its symtab
   301 static struct symtab* build_symtab_from_build_id(Elf64_Nhdr *note)
   302 {
   303   int fd;
   304   struct symtab *symtab = NULL;
   306   unsigned char *bytes
   307     = (unsigned char*)(note+1) + note->n_namesz;
   308   unsigned char *filename
   309     = (build_id_to_debug_filename (note->n_descsz, bytes));
   311   fd = pathmap_open(filename);
   312   if (fd >= 0) {
   313     symtab = build_symtab_internal(fd, NULL, /* try_debuginfo */ false);
   314     close(fd);
   315   }
   316   free(filename);
   318   return symtab;
   319 }
   321 // read symbol table from given fd.  If try_debuginfo) is true, also
   322 // try to open an associated debuginfo file
   323 static struct symtab* build_symtab_internal(int fd, const char *filename, bool try_debuginfo) {
   324   ELF_EHDR ehdr;
   325   char *names = NULL;
   326   struct symtab* symtab = NULL;
   328   // Reading of elf header
   329   struct elf_section *scn_cache = NULL;
   330   int cnt = 0;
   331   ELF_SHDR* shbuf = NULL;
   332   ELF_SHDR* cursct = NULL;
   333   ELF_PHDR* phbuf = NULL;
   334   ELF_PHDR* phdr = NULL;
   335   int sym_section = SHT_DYNSYM;
   337   uintptr_t baseaddr = (uintptr_t)-1;
   339   lseek(fd, (off_t)0L, SEEK_SET);
   340   if (! read_elf_header(fd, &ehdr)) {
   341     // not an elf
   342     return NULL;
   343   }
   345   // read ELF header
   346   if ((shbuf = read_section_header_table(fd, &ehdr)) == NULL) {
   347     goto quit;
   348   }
   350   baseaddr = find_base_address(fd, &ehdr);
   352   scn_cache = (struct elf_section *)
   353               calloc(ehdr.e_shnum * sizeof(struct elf_section), 1);
   354   if (scn_cache == NULL) {
   355     goto quit;
   356   }
   358   for (cursct = shbuf, cnt = 0; cnt < ehdr.e_shnum; cnt++) {
   359     scn_cache[cnt].c_shdr = cursct;
   360     if (cursct->sh_type == SHT_SYMTAB || cursct->sh_type == SHT_STRTAB
   361         || cursct->sh_type == SHT_NOTE || cursct->sh_type == SHT_DYNSYM) {
   362       if ( (scn_cache[cnt].c_data = read_section_data(fd, &ehdr, cursct)) == NULL) {
   363          goto quit;
   364       }
   365     }
   366     if (cursct->sh_type == SHT_SYMTAB) {
   367       // Full symbol table available so use that
   368       sym_section = cursct->sh_type;
   369     }
   370     cursct++;
   371   }
   373   for (cnt = 1; cnt < ehdr.e_shnum; cnt++) {
   374     ELF_SHDR *shdr = scn_cache[cnt].c_shdr;
   376     if (shdr->sh_type == sym_section) {
   377       ELF_SYM  *syms;
   378       int j, n, rslt;
   379       size_t size;
   381       // FIXME: there could be multiple data buffers associated with the
   382       // same ELF section. Here we can handle only one buffer. See man page
   383       // for elf_getdata on Solaris.
   385       // guarantee(symtab == NULL, "multiple symtab");
   386       symtab = (struct symtab*)calloc(1, sizeof(struct symtab));
   387       if (symtab == NULL) {
   388          goto quit;
   389       }
   390       // the symbol table
   391       syms = (ELF_SYM *)scn_cache[cnt].c_data;
   393       // number of symbols
   394       n = shdr->sh_size / shdr->sh_entsize;
   396       // create hash table, we use hcreate_r, hsearch_r and hdestroy_r to
   397       // manipulate the hash table.
   398       symtab->hash_table = (struct hsearch_data*) calloc(1, sizeof(struct hsearch_data));
   399       rslt = hcreate_r(n, symtab->hash_table);
   400       // guarantee(rslt, "unexpected failure: hcreate_r");
   402       // shdr->sh_link points to the section that contains the actual strings
   403       // for symbol names. the st_name field in ELF_SYM is just the
   404       // string table index. we make a copy of the string table so the
   405       // strings will not be destroyed by elf_end.
   406       size = scn_cache[shdr->sh_link].c_shdr->sh_size;
   407       symtab->strs = (char *)malloc(size);
   408       memcpy(symtab->strs, scn_cache[shdr->sh_link].c_data, size);
   410       // allocate memory for storing symbol offset and size;
   411       symtab->num_symbols = n;
   412       symtab->symbols = (struct elf_symbol *)calloc(n , sizeof(struct elf_symbol));
   414       // copy symbols info our symtab and enter them info the hash table
   415       for (j = 0; j < n; j++, syms++) {
   416         ENTRY item, *ret;
   417         char *sym_name = symtab->strs + syms->st_name;
   419         // skip non-object and non-function symbols
   420         int st_type = ELF_ST_TYPE(syms->st_info);
   421         if ( st_type != STT_FUNC && st_type != STT_OBJECT)
   422            continue;
   423         // skip empty strings and undefined symbols
   424         if (*sym_name == '\0' || syms->st_shndx == SHN_UNDEF) continue;
   426         symtab->symbols[j].name   = sym_name;
   427         symtab->symbols[j].offset = syms->st_value - baseaddr;
   428         symtab->symbols[j].size   = syms->st_size;
   430         item.key = sym_name;
   431         item.data = (void *)&(symtab->symbols[j]);
   433         hsearch_r(item, ENTER, &ret, symtab->hash_table);
   434       }
   435     }
   436   }
   438   // Look for a separate debuginfo file.
   439   if (try_debuginfo) {
   441     // We prefer a debug symtab to an object's own symtab, so look in
   442     // the debuginfo file.  We stash a copy of the old symtab in case
   443     // there is no debuginfo.
   444     struct symtab* prev_symtab = symtab;
   445     symtab = NULL;
   447 #ifdef NT_GNU_BUILD_ID
   448     // First we look for a Build ID
   449     for (cursct = shbuf, cnt = 0;
   450          symtab == NULL && cnt < ehdr.e_shnum;
   451          cnt++) {
   452       if (cursct->sh_type == SHT_NOTE) {
   453         Elf64_Nhdr *note = (Elf64_Nhdr *)scn_cache[cnt].c_data;
   454         if (note->n_type == NT_GNU_BUILD_ID) {
   455           symtab = build_symtab_from_build_id(note);
   456         }
   457       }
   458       cursct++;
   459     }
   460 #endif
   462     // Then, if that doesn't work, the debug link
   463     if (symtab == NULL) {
   464       symtab = build_symtab_from_debug_link(filename, fd, &ehdr, shbuf,
   465                                             scn_cache);
   466     }
   468     // If we still haven't found a symtab, use the object's own symtab.
   469     if (symtab != NULL) {
   470       if (prev_symtab != NULL)
   471         destroy_symtab(prev_symtab);
   472     } else {
   473       symtab = prev_symtab;
   474     }
   475   }
   477 quit:
   478   if (shbuf) free(shbuf);
   479   if (phbuf) free(phbuf);
   480   if (scn_cache) {
   481     for (cnt = 0; cnt < ehdr.e_shnum; cnt++) {
   482       if (scn_cache[cnt].c_data != NULL) {
   483         free(scn_cache[cnt].c_data);
   484       }
   485     }
   486     free(scn_cache);
   487   }
   488   return symtab;
   489 }
   491 struct symtab* build_symtab(int fd, const char *filename) {
   492   return build_symtab_internal(fd, filename, /* try_debuginfo */ true);
   493 }
   496 void destroy_symtab(struct symtab* symtab) {
   497   if (!symtab) return;
   498   if (symtab->strs) free(symtab->strs);
   499   if (symtab->symbols) free(symtab->symbols);
   500   if (symtab->hash_table) {
   501      hdestroy_r(symtab->hash_table);
   502      free(symtab->hash_table);
   503   }
   504   free(symtab);
   505 }
   507 uintptr_t search_symbol(struct symtab* symtab, uintptr_t base,
   508                       const char *sym_name, int *sym_size) {
   509   ENTRY item;
   510   ENTRY* ret = NULL;
   512   // library does not have symbol table
   513   if (!symtab || !symtab->hash_table)
   514      return (uintptr_t)NULL;
   516   item.key = (char*) strdup(sym_name);
   517   hsearch_r(item, FIND, &ret, symtab->hash_table);
   518   if (ret) {
   519     struct elf_symbol * sym = (struct elf_symbol *)(ret->data);
   520     uintptr_t rslt = (uintptr_t) ((char*)base + sym->offset);
   521     if (sym_size) *sym_size = sym->size;
   522     free(item.key);
   523     return rslt;
   524   }
   526 quit:
   527   free(item.key);
   528   return (uintptr_t) NULL;
   529 }
   531 const char* nearest_symbol(struct symtab* symtab, uintptr_t offset,
   532                            uintptr_t* poffset) {
   533   int n = 0;
   534   if (!symtab) return NULL;
   535   for (; n < symtab->num_symbols; n++) {
   536      struct elf_symbol* sym = &(symtab->symbols[n]);
   537      if (sym->name != NULL &&
   538          offset >= sym->offset && offset < sym->offset + sym->size) {
   539         if (poffset) *poffset = (offset - sym->offset);
   540         return sym->name;
   541      }
   542   }
   543   return NULL;
   544 }

mercurial