elfloader.c

00001 /*
00002  * Copyright (c) 2005, Swedish Institute of Computer Science
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. Neither the name of the Institute nor the names of its contributors
00014  *    may be used to endorse or promote products derived from this software
00015  *    without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
00018  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
00021  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00022  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00023  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00024  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00025  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00026  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00027  * SUCH DAMAGE.
00028  *
00029  * This file is part of the Contiki operating system.
00030  *
00031  * @(#)$Id: elfloader.c,v 1.10 2009/02/27 14:28:02 nvt-se Exp $
00032  */
00033 
00034 #include "contiki.h"
00035 
00036 #include "loader/elfloader.h"
00037 #include "loader/elfloader-arch.h"
00038 
00039 #include "cfs/cfs.h"
00040 #include "loader/symtab.h"
00041 
00042 #include <stddef.h>
00043 #include <string.h>
00044 #include <stdio.h>
00045 
00046 #define DEBUG 0
00047 #if DEBUG
00048 #include <stdio.h>
00049 #define PRINTF(...) printf(__VA_ARGS__)
00050 #else
00051 #define PRINTF(...) do {} while (0)
00052 #endif
00053 
00054 #define EI_NIDENT 16
00055 
00056 
00057 struct elf32_ehdr {
00058   unsigned char e_ident[EI_NIDENT];    /* ident bytes */
00059   elf32_half e_type;                   /* file type */
00060   elf32_half e_machine;                /* target machine */
00061   elf32_word e_version;                /* file version */
00062   elf32_addr e_entry;                  /* start address */
00063   elf32_off e_phoff;                   /* phdr file offset */
00064   elf32_off e_shoff;                   /* shdr file offset */
00065   elf32_word e_flags;                  /* file flags */
00066   elf32_half e_ehsize;                 /* sizeof ehdr */
00067   elf32_half e_phentsize;              /* sizeof phdr */
00068   elf32_half e_phnum;                  /* number phdrs */
00069   elf32_half e_shentsize;              /* sizeof shdr */
00070   elf32_half e_shnum;                  /* number shdrs */
00071   elf32_half e_shstrndx;               /* shdr string index */
00072 };
00073 
00074 /* Values for e_type. */
00075 #define ET_NONE         0       /* Unknown type. */
00076 #define ET_REL          1       /* Relocatable. */
00077 #define ET_EXEC         2       /* Executable. */
00078 #define ET_DYN          3       /* Shared object. */
00079 #define ET_CORE         4       /* Core file. */
00080 
00081 struct elf32_shdr {
00082   elf32_word sh_name;           /* section name */
00083   elf32_word sh_type;           /* SHT_... */
00084   elf32_word sh_flags;          /* SHF_... */
00085   elf32_addr sh_addr;           /* virtual address */
00086   elf32_off sh_offset;          /* file offset */
00087   elf32_word sh_size;           /* section size */
00088   elf32_word sh_link;           /* misc info */
00089   elf32_word sh_info;           /* misc info */
00090   elf32_word sh_addralign;      /* memory alignment */
00091   elf32_word sh_entsize;        /* entry size if table */
00092 };
00093 
00094 /* sh_type */
00095 #define SHT_NULL        0               /* inactive */
00096 #define SHT_PROGBITS    1               /* program defined information */
00097 #define SHT_SYMTAB      2               /* symbol table section */
00098 #define SHT_STRTAB      3               /* string table section */
00099 #define SHT_RELA        4               /* relocation section with addends*/
00100 #define SHT_HASH        5               /* symbol hash table section */
00101 #define SHT_DYNAMIC     6               /* dynamic section */
00102 #define SHT_NOTE        7               /* note section */
00103 #define SHT_NOBITS      8               /* no space section */
00104 #define SHT_REL         9               /* relation section without addends */
00105 #define SHT_SHLIB       10              /* reserved - purpose unknown */
00106 #define SHT_DYNSYM      11              /* dynamic symbol table section */
00107 #define SHT_LOPROC      0x70000000      /* reserved range for processor */
00108 #define SHT_HIPROC      0x7fffffff      /* specific section header types */
00109 #define SHT_LOUSER      0x80000000      /* reserved range for application */
00110 #define SHT_HIUSER      0xffffffff      /* specific indexes */
00111 
00112 struct elf32_rel {
00113   elf32_addr      r_offset;       /* Location to be relocated. */
00114   elf32_word      r_info;         /* Relocation type and symbol index. */
00115 };
00116 
00117 struct elf32_sym {
00118   elf32_word      st_name;        /* String table index of name. */
00119   elf32_addr      st_value;       /* Symbol value. */
00120   elf32_word      st_size;        /* Size of associated object. */
00121   unsigned char   st_info;        /* Type and binding information. */
00122   unsigned char   st_other;       /* Reserved (not used). */
00123   elf32_half      st_shndx;       /* Section index of symbol. */
00124 };
00125 
00126 #define ELF32_R_SYM(info)       ((info) >> 8)
00127 #define ELF32_R_TYPE(info)      ((unsigned char)(info))
00128 
00129 struct relevant_section {
00130   unsigned char number;
00131   unsigned int offset;
00132   char *address;
00133 };
00134 
00135 char elfloader_unknown[30];     /* Name that caused link error. */
00136 
00137 struct process * const * elfloader_autostart_processes;
00138 
00139 static struct relevant_section bss, data, rodata, text;
00140 
00141 static const unsigned char elf_magic_header[] =
00142   {0x7f, 0x45, 0x4c, 0x46,  /* 0x7f, 'E', 'L', 'F' */
00143    0x01,                    /* Only 32-bit objects. */
00144    0x01,                    /* Only LSB data. */
00145    0x01,                    /* Only ELF version 1. */
00146   };
00147 
00148 /*---------------------------------------------------------------------------*/
00149 static void
00150 seek_read(int fd, unsigned int offset, char *buf, int len)
00151 {
00152   cfs_seek(fd, offset, CFS_SEEK_SET);
00153   cfs_read(fd, buf, len);
00154 #if DEBUG
00155   {
00156     int i;
00157     PRINTF("seek_read: Read len %d from offset %d\n",
00158            len, offset);
00159     for(i = 0; i < len; ++i ) {
00160       PRINTF("%02x ", buf[i]);
00161     }
00162     printf("\n");
00163   }
00164 #endif /* DEBUG */
00165 }
00166 /*---------------------------------------------------------------------------*/
00167 /*
00168 static void
00169 seek_write(int fd, unsigned int offset, char *buf, int len)
00170 {
00171   cfs_seek(fd, offset, CFS_SEEK_SET);
00172   cfs_write(fd, buf, len);
00173 }
00174 */
00175 /*---------------------------------------------------------------------------*/
00176 static void *
00177 find_local_symbol(int fd, const char *symbol,
00178                   unsigned int symtab, unsigned short symtabsize,
00179                   unsigned int strtab)
00180 {
00181   struct elf32_sym s;
00182   unsigned int a;
00183   char name[30];
00184   struct relevant_section *sect;
00185   
00186   for(a = symtab; a < symtab + symtabsize; a += sizeof(s)) {
00187     seek_read(fd, a, (char *)&s, sizeof(s));
00188 
00189     if(s.st_name != 0) {
00190       seek_read(fd, strtab + s.st_name, name, sizeof(name));
00191       if(strcmp(name, symbol) == 0) {
00192         if(s.st_shndx == bss.number) {
00193           sect = &bss;
00194         } else if(s.st_shndx == data.number) {
00195           sect = &data;
00196         } else if(s.st_shndx == text.number) {
00197           sect = &text;
00198         } else {
00199           return NULL;
00200         }
00201         return &(sect->address[s.st_value]);
00202       }
00203     }
00204   }
00205   return NULL;
00206 }
00207 /*---------------------------------------------------------------------------*/
00208 static int
00209 relocate_section(int fd,
00210                  unsigned int section, unsigned short size,
00211                  unsigned int sectionaddr,
00212                  char *sectionbase,
00213                  unsigned int strs,
00214                  unsigned int strtab,
00215                  unsigned int symtab, unsigned short symtabsize,
00216                  unsigned char using_relas)
00217 {
00218   /* sectionbase added; runtime start address of current section */
00219   struct elf32_rela rela; /* Now used both for rel and rela data! */
00220   int rel_size = 0;
00221   struct elf32_sym s;
00222   unsigned int a;
00223   char name[30];
00224   char *addr;
00225   struct relevant_section *sect;
00226 
00227   /* determine correct relocation entry sizes */
00228   if(using_relas) {
00229     rel_size = sizeof(struct elf32_rela);
00230   } else {
00231     rel_size = sizeof(struct elf32_rel);
00232   }
00233   
00234   for(a = section; a < section + size; a += rel_size) {
00235     seek_read(fd, a, (char *)&rela, rel_size);
00236     seek_read(fd,
00237               symtab + sizeof(struct elf32_sym) * ELF32_R_SYM(rela.r_info),
00238               (char *)&s, sizeof(s));
00239     if(s.st_name != 0) {
00240       seek_read(fd, strtab + s.st_name, name, sizeof(name));
00241       PRINTF("name: %s\n", name);
00242       addr = (char *)symtab_lookup(name);
00243       /* ADDED */
00244       if(addr == NULL) {
00245         PRINTF("name not found in global: %s\n", name);
00246         addr = find_local_symbol(fd, name, symtab, symtabsize, strtab);
00247         PRINTF("found address %p\n", addr);
00248       }
00249       if(addr == NULL) {
00250         if(s.st_shndx == bss.number) {
00251           sect = &bss;
00252         } else if(s.st_shndx == data.number) {
00253           sect = &data;
00254         } else if(s.st_shndx == rodata.number) {
00255           sect = &rodata;
00256         } else if(s.st_shndx == text.number) {
00257           sect = &text;
00258         } else {
00259           PRINTF("elfloader unknown name: '%30s'\n", name);
00260           memcpy(elfloader_unknown, name, sizeof(elfloader_unknown));
00261           elfloader_unknown[sizeof(elfloader_unknown) - 1] = 0;
00262           return ELFLOADER_SYMBOL_NOT_FOUND;
00263         }
00264         addr = sect->address;
00265       }
00266     } else {
00267       if(s.st_shndx == bss.number) {
00268         sect = &bss;
00269       } else if(s.st_shndx == data.number) {
00270         sect = &data;
00271       } else if(s.st_shndx == rodata.number) {
00272         sect = &rodata;
00273       } else if(s.st_shndx == text.number) {
00274         sect = &text;
00275       } else {
00276         return ELFLOADER_SEGMENT_NOT_FOUND;
00277       }
00278       
00279       addr = sect->address;
00280     }
00281 
00282     if(!using_relas) {
00283       /* copy addend to rela structure */
00284       seek_read(fd, sectionaddr + rela.r_offset, (char *)&rela.r_addend, 4);
00285     }
00286 
00287     elfloader_arch_relocate(fd, sectionaddr, sectionbase, &rela, addr);
00288   }
00289   return ELFLOADER_OK;
00290 }
00291 /*---------------------------------------------------------------------------*/
00292 static void *
00293 find_program_processes(int fd,
00294                        unsigned int symtab, unsigned short size,
00295                        unsigned int strtab)
00296 {
00297   struct elf32_sym s;
00298   unsigned int a;
00299   char name[30];
00300   
00301   for(a = symtab; a < symtab + size; a += sizeof(s)) {
00302     seek_read(fd, a, (char *)&s, sizeof(s));
00303 
00304     if(s.st_name != 0) {
00305       seek_read(fd, strtab + s.st_name, name, sizeof(name));
00306       if(strcmp(name, "autostart_processes") == 0) {
00307         return &data.address[s.st_value];
00308       }
00309     }
00310   }
00311   return NULL;
00312 /*   return find_local_symbol(fd, "autostart_processes", symtab, size, strtab); */
00313 }
00314 /*---------------------------------------------------------------------------*/
00315 void
00316 elfloader_init(void)
00317 {
00318   elfloader_autostart_processes = NULL;
00319 }
00320 /*---------------------------------------------------------------------------*/
00321 #if 0
00322 static void
00323 print_chars(unsigned char *ptr, int num)
00324 {
00325   int i;
00326   for(i = 0; i < num; ++i) {
00327     PRINTF("%d", ptr[i]);
00328     if(i == num - 1) {
00329       PRINTF("\n");
00330     } else {
00331       PRINTF(", ");
00332     }
00333   }
00334 }
00335 #endif /* 0 */
00336 /*---------------------------------------------------------------------------*/
00337 int
00338 elfloader_load(int fd)
00339 {
00340   struct elf32_ehdr ehdr;
00341   struct elf32_shdr shdr;
00342   struct elf32_shdr strtable;
00343   unsigned int strs;
00344   unsigned int shdrptr;
00345   unsigned int nameptr;
00346   char name[12];
00347   
00348   int i;
00349   unsigned short shdrnum, shdrsize;
00350 
00351   unsigned char using_relas = -1;
00352   unsigned short textoff = 0, textsize, textrelaoff = 0, textrelasize;
00353   unsigned short dataoff = 0, datasize, datarelaoff = 0, datarelasize;
00354   unsigned short rodataoff = 0, rodatasize, rodatarelaoff = 0, rodatarelasize;
00355   unsigned short symtaboff = 0, symtabsize;
00356   unsigned short strtaboff = 0, strtabsize;
00357   unsigned short bsssize = 0;
00358 
00359   struct process **process;
00360   int ret;
00361 
00362   elfloader_unknown[0] = 0;
00363 
00364   /* The ELF header is located at the start of the buffer. */
00365   seek_read(fd, 0, (char *)&ehdr, sizeof(ehdr));
00366 
00367   /*  print_chars(ehdr.e_ident, sizeof(elf_magic_header));
00368       print_chars(elf_magic_header, sizeof(elf_magic_header));*/
00369   /* Make sure that we have a correct and compatible ELF header. */
00370   if(memcmp(ehdr.e_ident, elf_magic_header, sizeof(elf_magic_header)) != 0) {
00371     PRINTF("ELF header problems\n");
00372     return ELFLOADER_BAD_ELF_HEADER;
00373   }
00374 
00375   /* Grab the section header. */
00376   shdrptr = ehdr.e_shoff;
00377   seek_read(fd, shdrptr, (char *)&shdr, sizeof(shdr));
00378   
00379   /* Get the size and number of entries of the section header. */
00380   shdrsize = ehdr.e_shentsize;
00381   shdrnum = ehdr.e_shnum;
00382 
00383   PRINTF("Section header: size %d num %d\n", shdrsize, shdrnum);
00384   
00385   /* The string table section: holds the names of the sections. */
00386   seek_read(fd, ehdr.e_shoff + shdrsize * ehdr.e_shstrndx,
00387             (char *)&strtable, sizeof(strtable));
00388 
00389   /* Get a pointer to the actual table of strings. This table holds
00390      the names of the sections, not the names of other symbols in the
00391      file (these are in the sybtam section). */
00392   strs = strtable.sh_offset;
00393 
00394   PRINTF("Strtable offset %d\n", strs);
00395   
00396   /* Go through all sections and pick out the relevant ones. The
00397      ".text" segment holds the actual code from the ELF file, the
00398      ".data" segment contains initialized data, the ".bss" segment
00399      holds the size of the unitialized data segment. The ".rel[a].text"
00400      and ".rel[a].data" segments contains relocation information for the
00401      contents of the ".text" and ".data" segments, respectively. The
00402      ".symtab" segment contains the symbol table for this file. The
00403      ".strtab" segment points to the actual string names used by the
00404      symbol table.
00405 
00406      In addition to grabbing pointers to the relevant sections, we
00407      also save the section number for resolving addresses in the
00408      relocator code.
00409   */
00410 
00411 
00412   /* Initialize the segment sizes to zero so that we can check if
00413      their sections was found in the file or not. */
00414   textsize = textrelasize = datasize = datarelasize =
00415     rodatasize = rodatarelasize = symtabsize = strtabsize = 0;
00416 
00417   bss.number = data.number = rodata.number = text.number = -1;
00418                 
00419   shdrptr = ehdr.e_shoff;
00420   for(i = 0; i < shdrnum; ++i) {
00421 
00422     seek_read(fd, shdrptr, (char *)&shdr, sizeof(shdr));
00423     
00424     /* The name of the section is contained in the strings table. */
00425     nameptr = strs + shdr.sh_name;
00426     seek_read(fd, nameptr, name, sizeof(name));
00427     PRINTF("Section shdrptr 0x%x, %d + %d type %d\n",
00428            shdrptr,
00429            strs, shdr.sh_name,
00430            (int)shdr.sh_type);
00431     /* Match the name of the section with a predefined set of names
00432        (.text, .data, .bss, .rela.text, .rela.data, .symtab, and
00433        .strtab). */
00434     /* added support for .rodata, .rel.text and .rel.data). */
00435 
00436     if(shdr.sh_type == SHT_SYMTAB/*strncmp(name, ".symtab", 7) == 0*/) {
00437       PRINTF("symtab\n");
00438       symtaboff = shdr.sh_offset;
00439       symtabsize = shdr.sh_size;
00440     } else if(shdr.sh_type == SHT_STRTAB/*strncmp(name, ".strtab", 7) == 0*/) {
00441       PRINTF("strtab\n");
00442       strtaboff = shdr.sh_offset;
00443       strtabsize = shdr.sh_size;
00444     } else if(strncmp(name, ".text", 5) == 0) {
00445       textoff = shdr.sh_offset;
00446       textsize = shdr.sh_size;
00447       text.number = i;
00448       text.offset = textoff;
00449     } else if(strncmp(name, ".rel.text", 9) == 0) {
00450       using_relas = 0;
00451       textrelaoff = shdr.sh_offset;
00452       textrelasize = shdr.sh_size;
00453     } else if(strncmp(name, ".rela.text", 10) == 0) {
00454       using_relas = 1;
00455       textrelaoff = shdr.sh_offset;
00456       textrelasize = shdr.sh_size;
00457     } else if(strncmp(name, ".data", 5) == 0) {
00458       dataoff = shdr.sh_offset;
00459       datasize = shdr.sh_size;
00460       data.number = i;
00461       data.offset = dataoff;
00462     } else if(strncmp(name, ".rodata", 7) == 0) {
00463       /* read-only data handled the same way as regular text section */
00464       rodataoff = shdr.sh_offset;
00465       rodatasize = shdr.sh_size;
00466       rodata.number = i;
00467       rodata.offset = rodataoff;
00468     } else if(strncmp(name, ".rel.rodata", 11) == 0) {
00469       /* using elf32_rel instead of rela */
00470       using_relas = 0;
00471       rodatarelaoff = shdr.sh_offset;
00472       rodatarelasize = shdr.sh_size;
00473     } else if(strncmp(name, ".rela.rodata", 12) == 0) {
00474       using_relas = 1;
00475       rodatarelaoff = shdr.sh_offset;
00476       rodatarelasize = shdr.sh_size;
00477     } else if(strncmp(name, ".rel.data", 9) == 0) {
00478       /* using elf32_rel instead of rela */
00479       using_relas = 0;
00480       datarelaoff = shdr.sh_offset;
00481       datarelasize = shdr.sh_size;
00482     } else if(strncmp(name, ".rela.data", 10) == 0) {
00483       using_relas = 1;
00484       datarelaoff = shdr.sh_offset;
00485       datarelasize = shdr.sh_size;
00486     } else if(strncmp(name, ".bss", 4) == 0) {
00487       bsssize = shdr.sh_size;
00488       bss.number = i;
00489       bss.offset = 0;
00490     }
00491 
00492     /* Move on to the next section header. */
00493     shdrptr += shdrsize;
00494   }
00495 
00496   if(symtabsize == 0) {
00497     return ELFLOADER_NO_SYMTAB;
00498   }
00499   if(strtabsize == 0) {
00500     return ELFLOADER_NO_STRTAB;
00501   }
00502   if(textsize == 0) {
00503     return ELFLOADER_NO_TEXT;
00504   }
00505 
00506   PRINTF("before allocate ram\n");
00507   bss.address = (char *)elfloader_arch_allocate_ram(bsssize + datasize);
00508   data.address = (char *)bss.address + bsssize;
00509   PRINTF("before allocate rom\n");
00510   text.address = (char *)elfloader_arch_allocate_rom(textsize + rodatasize);
00511   rodata.address = (char *)text.address + textsize;
00512   
00513 
00514   PRINTF("bss base address: bss.address = 0x%08x\n", bss.address);
00515   PRINTF("data base address: data.address = 0x%08x\n", data.address);
00516   PRINTF("text base address: text.address = 0x%08x\n", text.address);
00517   PRINTF("rodata base address: rodata.address = 0x%08x\n", rodata.address);
00518 
00519 
00520   /* If we have text segment relocations, we process them. */
00521   PRINTF("elfloader: relocate text\n");
00522   if(textrelasize > 0) {
00523             ret = relocate_section(fd,
00524                            textrelaoff, textrelasize,
00525                            textoff,
00526                            text.address,
00527                            strs,
00528                            strtaboff,
00529                            symtaboff, symtabsize, using_relas);
00530     if(ret != ELFLOADER_OK) {
00531       return ret;
00532     }
00533   }
00534 
00535   /* If we have any rodata segment relocations, we process them too. */
00536   PRINTF("elfloader: relocate rodata\n");
00537   if(rodatarelasize > 0) {
00538     ret = relocate_section(fd,
00539                            rodatarelaoff, rodatarelasize,
00540                            rodataoff,
00541                            rodata.address,
00542                            strs,
00543                            strtaboff,
00544                            symtaboff, symtabsize, using_relas);
00545     if(ret != ELFLOADER_OK) {
00546       PRINTF("elfloader: data failed\n");
00547       return ret;
00548     }
00549   }
00550 
00551   /* If we have any data segment relocations, we process them too. */
00552   PRINTF("elfloader: relocate data\n");
00553   if(datarelasize > 0) {
00554     ret = relocate_section(fd,
00555                            datarelaoff, datarelasize,
00556                            dataoff,
00557                            data.address,
00558                            strs,
00559                            strtaboff,
00560                            symtaboff, symtabsize, using_relas);
00561     if(ret != ELFLOADER_OK) {
00562       PRINTF("elfloader: data failed\n");
00563       return ret;
00564     }
00565   }
00566 
00567   /* Write text and rodata segment into flash and data segment into RAM. */
00568   elfloader_arch_write_rom(fd, textoff, textsize, text.address);
00569   elfloader_arch_write_rom(fd, rodataoff, rodatasize, rodata.address);
00570   
00571   memset(bss.address, 0, bsssize);
00572   seek_read(fd, dataoff, data.address, datasize);
00573 
00574   PRINTF("elfloader: autostart search\n");
00575   process = (struct process **) find_local_symbol(fd, "autostart_processes", symtaboff, symtabsize, strtaboff);
00576   if(process != NULL) {
00577     PRINTF("elfloader: autostart found\n");
00578     elfloader_autostart_processes = process;
00579     return ELFLOADER_OK;
00580   } else {
00581     PRINTF("elfloader: no autostart\n");
00582     process = (struct process **) find_program_processes(fd, symtaboff, symtabsize, strtaboff);
00583     if(process != NULL) {
00584       PRINTF("elfloader: FOUND PRG\n");
00585     }
00586     return ELFLOADER_NO_STARTPOINT;
00587   }
00588 }
00589 /*---------------------------------------------------------------------------*/

Generated on Mon Apr 11 14:23:28 2011 for Contiki 2.5 by  doxygen 1.6.1