Contiki 2.5
cle.c
1 /*
2  * Copyright (c) 2006, Swedish Institute of Computer Science
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the Institute nor the names of its contributors
14  * may be used to endorse or promote products derived from this software
15  * without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * @(#)$Id: cle.c,v 1.7 2007/06/04 17:48:19 bg- Exp $
30  */
31 
32 /*
33  * The Contiki dynamic Link Editor (CLE), ELF version.
34  */
35 
36 #include <stdio.h>
37 #include <string.h>
38 
39 #include "contiki.h"
40 
41 #include "loader/elf32.h"
42 #include "loader/cle.h"
43 #include "loader/sym.h"
44 
45 #define NDEBUG
46 #include "lib/assert.h"
47 
48 #ifdef NDEBUG
49 #define PRINTF(...) do {} while (0)
50 #else
51 #define PRINTF(...) printf(__VA_ARGS__)
52 #endif
53 
54 #define NOLL 0
55 
56 #ifdef __AVR__
57 /*
58  * On the AVR, GNU C squeezes function addresses into 16 bits. Some of
59  * this code is explicitly written to deal with this.
60  */
61 #ifndef __GNUC__
62 #eror "You lose!!!"
63 #endif
64 #endif
65 
66 /*
67  * Parse object file located at offset hdr reading data using function
68  * pread. Save what is useful in info.
69  */
70 int
71 cle_read_info(struct cle_info *info,
72  int (*pread)(void *, int, off_t),
73  off_t hdr)
74 {
75  /*
76  * Save stackspace by using a union!
77  *
78  * Beware that the contents of ehdr is gone when shdr is written!!!
79  */
80  union {
81  struct elf32_ehdr ehdr;
82  struct elf32_shdr shdr;
83  } huge;
84 #define ehdr huge.ehdr
85 #define shdr huge.shdr
86 
87  off_t shoff;
88  cle_off strs;
89  cle_half shnum; /* number shdrs */
90  cle_half shentsize; /* sizeof shdr */
91  cle_word strtabsize = 0;
92  int i, ret;
93 
94  memset(info, 0x0, sizeof(*info));
95 
96  ret = pread(&ehdr, sizeof(ehdr), hdr);
97  assert(ret > 0);
98 
99  /* Make sure that we have a correct and compatible ELF header. */
100  if(memcmp(ehdr.e_ident, ELF_MAGIC_HEADER, ELF_MAGIC_HEADER_SIZE) != 0) {
101  return CLE_BAD_HEADER;
102  }
103 
104  shoff = hdr + ehdr.e_shoff;
105  shentsize = ehdr.e_shentsize;
106  shnum = ehdr.e_shnum;
107 
108  /* The string table section: holds the names of the sections. */
109  ret = pread(&shdr, sizeof(shdr), shoff + shentsize*ehdr.e_shstrndx);
110  assert(ret > 0);
111 
112  /* BEWARE THAT ehdr IS NOW OVERWRITTEN!!! */
113 
114  /*
115  * Get a pointer to the actual table of strings. This table holds
116  * the names of the sections, not the names of other symbols in the
117  * file (these are in the symtab section).
118  */
119  strs = shdr.sh_offset;
120 
121  /*
122  * The ".text" segment holds the actual code from the ELF file, the
123  * ".data" segment contains initialized data, the ".bss" segment
124  * holds the size of the unitialized data segment. The ".rela.text"
125  * and ".rela.data" segments contains relocation information for the
126  * contents of the ".text" and ".data" segments, respectively. The
127  * ".symtab" segment contains the symbol table for this file. The
128  * ".strtab" segment points to the actual string names used by the
129  * symbol table.
130  *
131  * In addition to grabbing pointers to the relevant sections, we
132  * also save the section index for resolving addresses in the
133  * relocator code.
134  */
135  for(i = 0; i < shnum; ++i) {
136  ret = pread(&shdr, sizeof(shdr), shoff);
137  assert(ret > 0);
138 
139  /* The name of the section is contained in the strings table. */
140  ret = pread(info->name, sizeof(info->name), hdr + strs + shdr.sh_name);
141  assert(ret > 0);
142 
143  if(strncmp(info->name, ".text", 5) == 0) {
144  info->textoff = shdr.sh_offset;
145  info->textsize = shdr.sh_size;
146  info->text_shndx = i;
147  } else if(strncmp(info->name, ".rela.text", 10) == 0) {
148  info->textrelaoff = shdr.sh_offset;
149  info->textrelasize = shdr.sh_size;
150  } else if(strncmp(info->name, ".data", 5) == 0) {
151  info->dataoff = shdr.sh_offset;
152  info->datasize = shdr.sh_size;
153  info->data_shndx = i;
154  } else if(strncmp(info->name, ".rela.data", 10) == 0) {
155  info->datarelaoff = shdr.sh_offset;
156  info->datarelasize = shdr.sh_size;
157  } else if(strncmp(info->name, ".symtab", 7) == 0) {
158  info->symtaboff = shdr.sh_offset;
159  info->symtabsize = shdr.sh_size;
160  } else if(strncmp(info->name, ".strtab", 7) == 0) {
161  info->strtaboff = shdr.sh_offset;
162  strtabsize = shdr.sh_size;
163  } else if(strncmp(info->name, ".bss", 4) == 0) {
164  info->bsssize = shdr.sh_size;
165  info->bss_shndx = i;
166  } else {
167  info->name[sizeof(info->name) - 1] = 0;
168  PRINTF("cle: unknown section %.12s\n", info->name);
169  }
170 
171  /* Move on to the next section header. */
172  shoff += shentsize;
173  }
174 
175  if(info->symtabsize == 0) {
176  return CLE_NO_SYMTAB;
177  }
178  if(strtabsize == 0) {
179  return CLE_NO_STRTAB;
180  }
181  if(info->textsize == 0) {
182  return CLE_NO_TEXT;
183  }
184 
185  return CLE_OK;
186 }
187 
188 /*
189  * Relocate one segment that has been copied to the location pointed
190  * to by segmem.
191  *
192  * Relocation info is read from offset reloff to (reloff + relsize)
193  * and the start of the object file is at hdr. Data is read using
194  * function pread.
195  */
196 int
197 cle_relocate(struct cle_info *info,
198  int (*pread)(void *, int, off_t),
199  off_t hdr, /* Offset to start of file. */
200  void *segmem, /* Where segment is stored in memory. */
201  cle_off reloff, /* .rela.<segment> start */
202  cle_word relsize) /* .rela.<segment> size */
203 {
204  struct elf32_rela rela;
205  struct elf32_sym s;
206  off_t off;
207  cle_addr addr;
208  int ret;
209 
210  for(off = hdr + reloff;
211  off < hdr + reloff + relsize;
212  off += sizeof(struct elf32_rela)) {
213  ret = pread(&rela, sizeof(rela), off);
214  assert(ret > 0);
215  ret = pread(&s, sizeof(s),
216  hdr + info->symtaboff
217  + sizeof(struct elf32_sym)*ELF32_R_SYM(rela.r_info));
218  assert(ret > 0);
219 
220  if(s.st_shndx == info->bss_shndx) {
221  addr = (cle_addr)(uintptr_t)info->bss;
222  } else if(s.st_shndx == info->data_shndx) {
223  addr = (cle_addr)(uintptr_t)info->data;
224  } else if(s.st_shndx == info->text_shndx) {
225  addr = info->text;
226  } else {
227  addr = NOLL;
228  }
229 
230  if(s.st_name == 0) { /* No name, local symbol? */
231  if(addr == NOLL) {
232  return CLE_UNKNOWN_SEGMENT;
233  }
234  } else {
235  ret = pread(info->name, sizeof(info->name),
236  hdr + info->strtaboff + s.st_name);
237  assert(ret > 0);
238  cle_addr sym = (cle_addr)(uintptr_t)sym_function(info->name);
239 #ifdef __AVR__
240  if(sym != NOLL)
241  sym = sym << 1;
242 #endif
243  if(sym == NOLL)
244  sym = (cle_addr)(uintptr_t)sym_object(info->name);
245 
246  if(addr == NOLL && sym != NOLL) { /* Imported symbol. */
247  addr = sym;
248  } else if(addr != NOLL && sym == NOLL) { /* Exported symbol. */
249  addr = addr + s.st_value;
250  } else if(addr == NOLL && sym == NOLL) {
251  PRINTF("cle: undefined reference to %.32s (%d)\n",
252  info->name, s.st_info);
253  return CLE_UNDEFINED; /* Or COMMON symbol. */
254  } else if(addr != NOLL && sym != NOLL) {
255  PRINTF("cle: multiple definitions of %.32s (%d)\n",
256  info->name, s.st_info);
257  return CLE_MULTIPLY_DEFINED;
258  }
259  }
260 
261  addr += rela.r_addend;
262 
263  ret = cle_write_reloc(segmem + rela.r_offset, &rela, addr, info);
264  if(ret != CLE_OK) {
265  return ret;
266  }
267  }
268  return CLE_OK;
269 }
270 
271 /*
272  * Search object file located at offset hdr using function
273  * pread. Search for symbol named symbol and return its address after
274  * relocation or NULL on failure.
275  */
276 void *
277 cle_lookup(struct cle_info *info,
278  int (*pread)(void *, int, off_t),
279  off_t hdr, /* Offset to start of file. */
280  const char *symbol)
281 
282 {
283  struct elf32_sym s;
284  off_t a;
285  cle_addr addr;
286  int ret;
287 
288  for(a = hdr + info->symtaboff;
289  a < hdr + info->symtaboff + info->symtabsize;
290  a += sizeof(s)) {
291  ret = pread(&s, sizeof(s), a);
292  assert(ret > 0);
293 
294  if(s.st_name != 0) {
295  ret = pread(info->name, sizeof(info->name),
296  hdr + info->strtaboff + s.st_name);
297  assert(ret > 0);
298 
299  if(strcmp(info->name, symbol) == 0) { /* Exported symbol found. */
300  if(s.st_shndx == info->bss_shndx) {
301  addr = (cle_addr)(uintptr_t)info->bss;
302  } else if(s.st_shndx == info->data_shndx) {
303  addr = (cle_addr)(uintptr_t)info->data;
304  } else if(s.st_shndx == info->text_shndx) {
305  addr = info->text;
306 #ifdef __AVR__
307  return (void *)(uintptr_t)((addr + s.st_value) >> 1);
308 #endif
309  } else {
310  return NULL; /* Really an error! */
311  }
312 
313  return (void *)(uintptr_t)(addr + s.st_value);
314  }
315  }
316  }
317  return NULL;
318 }