Contiki 2.6
|
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 }