Contiki 2.6

cle.c

00001 /*
00002  * Copyright (c) 2006, 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  * @(#)$Id: cle.c,v 1.7 2007/06/04 17:48:19 bg- Exp $
00030  */
00031 
00032 /*
00033  * The Contiki dynamic Link Editor (CLE), ELF version.
00034  */
00035 
00036 #include <stdio.h>
00037 #include <string.h>
00038 
00039 #include "contiki.h"
00040 
00041 #include "loader/elf32.h"
00042 #include "loader/cle.h"
00043 #include "loader/sym.h"
00044 
00045 #define NDEBUG
00046 #include "lib/assert.h"
00047 
00048 #ifdef NDEBUG
00049 #define PRINTF(...) do {} while (0)
00050 #else
00051 #define PRINTF(...) printf(__VA_ARGS__)
00052 #endif
00053 
00054 #define NOLL 0
00055 
00056 #ifdef __AVR__
00057 /*
00058  * On the AVR, GNU C squeezes function addresses into 16 bits. Some of
00059  * this code is explicitly written to deal with this.
00060  */
00061 #ifndef __GNUC__
00062 #eror "You lose!!!"
00063 #endif
00064 #endif
00065 
00066 /*
00067  * Parse object file located at offset hdr reading data using function
00068  * pread. Save what is useful in info.
00069  */
00070 int
00071 cle_read_info(struct cle_info *info,
00072               int (*pread)(void *, int, off_t),
00073               off_t hdr)
00074 {
00075   /*
00076    * Save stackspace by using a union!
00077    *
00078    * Beware that the contents of ehdr is gone when shdr is written!!!
00079    */
00080   union {
00081     struct elf32_ehdr ehdr;
00082     struct elf32_shdr shdr;
00083   } huge;
00084 #define ehdr huge.ehdr
00085 #define shdr huge.shdr
00086 
00087   off_t shoff; 
00088   cle_off strs;
00089   cle_half shnum;               /* number shdrs */
00090   cle_half shentsize;           /* sizeof shdr */
00091   cle_word strtabsize = 0;
00092   int i, ret;
00093 
00094   memset(info, 0x0, sizeof(*info));
00095 
00096   ret = pread(&ehdr, sizeof(ehdr), hdr);
00097   assert(ret > 0);
00098 
00099   /* Make sure that we have a correct and compatible ELF header. */
00100   if(memcmp(ehdr.e_ident, ELF_MAGIC_HEADER, ELF_MAGIC_HEADER_SIZE) != 0) {
00101     return CLE_BAD_HEADER;
00102   }
00103 
00104   shoff = hdr + ehdr.e_shoff;
00105   shentsize = ehdr.e_shentsize;
00106   shnum = ehdr.e_shnum;
00107 
00108   /* The string table section: holds the names of the sections. */
00109   ret = pread(&shdr, sizeof(shdr), shoff + shentsize*ehdr.e_shstrndx);
00110   assert(ret > 0);
00111 
00112   /* BEWARE THAT ehdr IS NOW OVERWRITTEN!!! */
00113 
00114   /*
00115    * Get a pointer to the actual table of strings. This table holds
00116    * the names of the sections, not the names of other symbols in the
00117    * file (these are in the symtab section).
00118    */
00119   strs = shdr.sh_offset;
00120 
00121   /*
00122    * The ".text" segment holds the actual code from the ELF file, the
00123    * ".data" segment contains initialized data, the ".bss" segment
00124    * holds the size of the unitialized data segment. The ".rela.text"
00125    * and ".rela.data" segments contains relocation information for the
00126    * contents of the ".text" and ".data" segments, respectively. The
00127    * ".symtab" segment contains the symbol table for this file. The
00128    * ".strtab" segment points to the actual string names used by the
00129    * symbol table.
00130    *
00131    * In addition to grabbing pointers to the relevant sections, we
00132    * also save the section index for resolving addresses in the
00133    * relocator code.
00134    */
00135   for(i = 0; i < shnum; ++i) {
00136     ret = pread(&shdr, sizeof(shdr), shoff);
00137     assert(ret > 0);
00138     
00139     /* The name of the section is contained in the strings table. */
00140     ret = pread(info->name, sizeof(info->name), hdr + strs + shdr.sh_name);
00141     assert(ret > 0);
00142 
00143     if(strncmp(info->name, ".text", 5) == 0) {
00144       info->textoff = shdr.sh_offset;
00145       info->textsize = shdr.sh_size;
00146       info->text_shndx = i;
00147     } else if(strncmp(info->name, ".rela.text", 10) == 0) {
00148       info->textrelaoff = shdr.sh_offset;
00149       info->textrelasize = shdr.sh_size;
00150     } else if(strncmp(info->name, ".data", 5) == 0) {
00151       info->dataoff = shdr.sh_offset;
00152       info->datasize = shdr.sh_size;
00153       info->data_shndx = i;
00154     } else if(strncmp(info->name, ".rela.data", 10) == 0) {
00155       info->datarelaoff = shdr.sh_offset;
00156       info->datarelasize = shdr.sh_size;
00157     } else if(strncmp(info->name, ".symtab", 7) == 0) {
00158       info->symtaboff = shdr.sh_offset;
00159       info->symtabsize = shdr.sh_size;
00160     } else if(strncmp(info->name, ".strtab", 7) == 0) {
00161       info->strtaboff = shdr.sh_offset;
00162       strtabsize = shdr.sh_size;
00163     } else if(strncmp(info->name, ".bss", 4) == 0) {
00164       info->bsssize = shdr.sh_size;
00165       info->bss_shndx = i;
00166     } else {
00167       info->name[sizeof(info->name) - 1] = 0;
00168       PRINTF("cle: unknown section %.12s\n", info->name);
00169     }
00170 
00171     /* Move on to the next section header. */
00172     shoff += shentsize;
00173   }
00174 
00175   if(info->symtabsize == 0) {
00176     return CLE_NO_SYMTAB;
00177   }
00178   if(strtabsize == 0) {
00179     return CLE_NO_STRTAB;
00180   }
00181   if(info->textsize == 0) {
00182     return CLE_NO_TEXT;
00183   }
00184 
00185   return CLE_OK;
00186 }
00187 
00188 /*
00189  * Relocate one segment that has been copied to the location pointed
00190  * to by segmem.
00191  *
00192  * Relocation info is read from offset reloff to (reloff + relsize)
00193  * and the start of the object file is at hdr. Data is read using
00194  * function pread.
00195  */
00196 int
00197 cle_relocate(struct cle_info *info,
00198              int (*pread)(void *, int, off_t),
00199              off_t hdr,         /* Offset to start of file. */
00200              void *segmem,      /* Where segment is stored in memory. */
00201              cle_off reloff,    /* .rela.<segment> start */
00202              cle_word relsize)  /* .rela.<segment> size */
00203 {
00204   struct elf32_rela rela;
00205   struct elf32_sym s;
00206   off_t off;
00207   cle_addr addr;
00208   int ret;
00209   
00210   for(off = hdr + reloff;
00211       off < hdr + reloff + relsize;
00212       off += sizeof(struct elf32_rela)) {
00213     ret = pread(&rela, sizeof(rela), off);
00214     assert(ret > 0);
00215     ret = pread(&s, sizeof(s),
00216                hdr + info->symtaboff
00217                + sizeof(struct elf32_sym)*ELF32_R_SYM(rela.r_info));
00218     assert(ret > 0);
00219 
00220     if(s.st_shndx == info->bss_shndx) {
00221       addr = (cle_addr)(uintptr_t)info->bss;
00222     } else if(s.st_shndx == info->data_shndx) {
00223       addr = (cle_addr)(uintptr_t)info->data;
00224     } else if(s.st_shndx == info->text_shndx) {
00225       addr = info->text;
00226     } else {
00227       addr = NOLL;
00228     }
00229 
00230     if(s.st_name == 0) {        /* No name, local symbol? */
00231       if(addr == NOLL) {
00232         return CLE_UNKNOWN_SEGMENT;
00233       }
00234     } else {
00235       ret = pread(info->name, sizeof(info->name),
00236                   hdr + info->strtaboff + s.st_name);
00237       assert(ret > 0);
00238       cle_addr sym = (cle_addr)(uintptr_t)sym_function(info->name);
00239 #ifdef __AVR__
00240       if(sym != NOLL)
00241         sym = sym << 1;
00242 #endif
00243       if(sym == NOLL)
00244         sym = (cle_addr)(uintptr_t)sym_object(info->name);
00245 
00246       if(addr == NOLL && sym != NOLL) { /* Imported symbol. */
00247         addr = sym;
00248       } else if(addr != NOLL && sym == NOLL) { /* Exported symbol. */
00249         addr = addr + s.st_value;
00250       } else if(addr == NOLL && sym == NOLL) {
00251         PRINTF("cle: undefined reference to %.32s (%d)\n",
00252                info->name, s.st_info);
00253         return CLE_UNDEFINED;   /* Or COMMON symbol. */
00254       } else if(addr != NOLL && sym != NOLL) {
00255         PRINTF("cle: multiple definitions of %.32s (%d)\n",
00256                info->name, s.st_info);
00257         return CLE_MULTIPLY_DEFINED;
00258       }
00259     }
00260 
00261     addr += rela.r_addend;
00262 
00263     ret = cle_write_reloc(segmem + rela.r_offset, &rela, addr, info);
00264     if(ret != CLE_OK) {
00265       return ret;
00266     }
00267   }
00268   return CLE_OK;
00269 }
00270 
00271 /*
00272  * Search object file located at offset hdr using function
00273  * pread. Search for symbol named symbol and return its address after
00274  * relocation or NULL on failure.
00275  */
00276 void *
00277 cle_lookup(struct cle_info *info,
00278            int (*pread)(void *, int, off_t),
00279            off_t hdr,           /* Offset to start of file. */
00280            const char *symbol)
00281 
00282 {
00283   struct elf32_sym s;
00284   off_t a;
00285   cle_addr addr;
00286   int ret;
00287 
00288   for(a = hdr + info->symtaboff;
00289       a < hdr + info->symtaboff + info->symtabsize;
00290       a += sizeof(s)) {
00291     ret = pread(&s, sizeof(s), a);
00292     assert(ret > 0);
00293 
00294     if(s.st_name != 0) {
00295       ret = pread(info->name, sizeof(info->name),
00296                  hdr + info->strtaboff + s.st_name);
00297       assert(ret > 0);
00298 
00299       if(strcmp(info->name, symbol) == 0) { /* Exported symbol found. */
00300         if(s.st_shndx == info->bss_shndx) {
00301           addr = (cle_addr)(uintptr_t)info->bss;
00302         } else if(s.st_shndx == info->data_shndx) {
00303           addr = (cle_addr)(uintptr_t)info->data;
00304         } else if(s.st_shndx == info->text_shndx) {
00305           addr = info->text;
00306 #ifdef __AVR__
00307           return (void *)(uintptr_t)((addr + s.st_value) >> 1);
00308 #endif
00309         } else {
00310           return NULL;          /* Really an error! */
00311         }
00312 
00313         return (void *)(uintptr_t)(addr + s.st_value);
00314       }
00315     }
00316   }
00317   return NULL;
00318 }