Contiki 3.x
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  */
30 
31 /*
32  * The Contiki dynamic Link Editor (CLE), ELF version.
33  */
34 
35 #include <stdio.h>
36 #include <string.h>
37 
38 #include "contiki.h"
39 
40 #include "loader/elf32.h"
41 #include "loader/cle.h"
42 #include "loader/sym.h"
43 
44 #define NDEBUG
45 #include "lib/assert.h"
46 
47 #ifdef NDEBUG
48 #define PRINTF(...) do {} while (0)
49 #else
50 #define PRINTF(...) printf(__VA_ARGS__)
51 #endif
52 
53 #define NOLL 0
54 
55 #ifdef __AVR__
56 /*
57  * On the AVR, GNU C squeezes function addresses into 16 bits. Some of
58  * this code is explicitly written to deal with this.
59  */
60 #ifndef __GNUC__
61 #eror "You lose!!!"
62 #endif
63 #endif
64 
65 /*
66  * Parse object file located at offset hdr reading data using function
67  * pread. Save what is useful in info.
68  */
69 int
70 cle_read_info(struct cle_info *info,
71  int (*pread)(void *, int, off_t),
72  off_t hdr)
73 {
74  /*
75  * Save stackspace by using a union!
76  *
77  * Beware that the contents of ehdr is gone when shdr is written!!!
78  */
79  union {
80  struct elf32_ehdr ehdr;
81  struct elf32_shdr shdr;
82  } huge;
83 #define ehdr huge.ehdr
84 #define shdr huge.shdr
85 
86  off_t shoff;
87  cle_off strs;
88  cle_half shnum; /* number shdrs */
89  cle_half shentsize; /* sizeof shdr */
90  cle_word strtabsize = 0;
91  int i, ret;
92 
93  memset(info, 0x0, sizeof(*info));
94 
95  ret = pread(&ehdr, sizeof(ehdr), hdr);
96  assert(ret > 0);
97 
98  /* Make sure that we have a correct and compatible ELF header. */
99  if(memcmp(ehdr.e_ident, ELF_MAGIC_HEADER, ELF_MAGIC_HEADER_SIZE) != 0) {
100  return CLE_BAD_HEADER;
101  }
102 
103  shoff = hdr + ehdr.e_shoff;
104  shentsize = ehdr.e_shentsize;
105  shnum = ehdr.e_shnum;
106 
107  /* The string table section: holds the names of the sections. */
108  ret = pread(&shdr, sizeof(shdr), shoff + shentsize*ehdr.e_shstrndx);
109  assert(ret > 0);
110 
111  /* BEWARE THAT ehdr IS NOW OVERWRITTEN!!! */
112 
113  /*
114  * Get a pointer to the actual table of strings. This table holds
115  * the names of the sections, not the names of other symbols in the
116  * file (these are in the symtab section).
117  */
118  strs = shdr.sh_offset;
119 
120  /*
121  * The ".text" segment holds the actual code from the ELF file, the
122  * ".data" segment contains initialized data, the ".bss" segment
123  * holds the size of the unitialized data segment. The ".rela.text"
124  * and ".rela.data" segments contains relocation information for the
125  * contents of the ".text" and ".data" segments, respectively. The
126  * ".symtab" segment contains the symbol table for this file. The
127  * ".strtab" segment points to the actual string names used by the
128  * symbol table.
129  *
130  * In addition to grabbing pointers to the relevant sections, we
131  * also save the section index for resolving addresses in the
132  * relocator code.
133  */
134  for(i = 0; i < shnum; ++i) {
135  ret = pread(&shdr, sizeof(shdr), shoff);
136  assert(ret > 0);
137 
138  /* The name of the section is contained in the strings table. */
139  ret = pread(info->name, sizeof(info->name), hdr + strs + shdr.sh_name);
140  assert(ret > 0);
141 
142  if(strncmp(info->name, ".text", 5) == 0) {
143  info->textoff = shdr.sh_offset;
144  info->textsize = shdr.sh_size;
145  info->text_shndx = i;
146  } else if(strncmp(info->name, ".rela.text", 10) == 0) {
147  info->textrelaoff = shdr.sh_offset;
148  info->textrelasize = shdr.sh_size;
149  } else if(strncmp(info->name, ".data", 5) == 0) {
150  info->dataoff = shdr.sh_offset;
151  info->datasize = shdr.sh_size;
152  info->data_shndx = i;
153  } else if(strncmp(info->name, ".rela.data", 10) == 0) {
154  info->datarelaoff = shdr.sh_offset;
155  info->datarelasize = shdr.sh_size;
156  } else if(strncmp(info->name, ".symtab", 7) == 0) {
157  info->symtaboff = shdr.sh_offset;
158  info->symtabsize = shdr.sh_size;
159  } else if(strncmp(info->name, ".strtab", 7) == 0) {
160  info->strtaboff = shdr.sh_offset;
161  strtabsize = shdr.sh_size;
162  } else if(strncmp(info->name, ".bss", 4) == 0) {
163  info->bsssize = shdr.sh_size;
164  info->bss_shndx = i;
165  } else {
166  info->name[sizeof(info->name) - 1] = 0;
167  PRINTF("cle: unknown section %.12s\n", info->name);
168  }
169 
170  /* Move on to the next section header. */
171  shoff += shentsize;
172  }
173 
174  if(info->symtabsize == 0) {
175  return CLE_NO_SYMTAB;
176  }
177  if(strtabsize == 0) {
178  return CLE_NO_STRTAB;
179  }
180  if(info->textsize == 0) {
181  return CLE_NO_TEXT;
182  }
183 
184  return CLE_OK;
185 }
186 
187 /*
188  * Relocate one segment that has been copied to the location pointed
189  * to by segmem.
190  *
191  * Relocation info is read from offset reloff to (reloff + relsize)
192  * and the start of the object file is at hdr. Data is read using
193  * function pread.
194  */
195 int
196 cle_relocate(struct cle_info *info,
197  int (*pread)(void *, int, off_t),
198  off_t hdr, /* Offset to start of file. */
199  void *segmem, /* Where segment is stored in memory. */
200  cle_off reloff, /* .rela.<segment> start */
201  cle_word relsize) /* .rela.<segment> size */
202 {
203  struct elf32_rela rela;
204  struct elf32_sym s;
205  off_t off;
206  cle_addr addr;
207  int ret;
208 
209  for(off = hdr + reloff;
210  off < hdr + reloff + relsize;
211  off += sizeof(struct elf32_rela)) {
212  ret = pread(&rela, sizeof(rela), off);
213  assert(ret > 0);
214  ret = pread(&s, sizeof(s),
215  hdr + info->symtaboff
216  + sizeof(struct elf32_sym)*ELF32_R_SYM(rela.r_info));
217  assert(ret > 0);
218 
219  if(s.st_shndx == info->bss_shndx) {
220  addr = (cle_addr)(uintptr_t)info->bss;
221  } else if(s.st_shndx == info->data_shndx) {
222  addr = (cle_addr)(uintptr_t)info->data;
223  } else if(s.st_shndx == info->text_shndx) {
224  addr = info->text;
225  } else {
226  addr = NOLL;
227  }
228 
229  if(s.st_name == 0) { /* No name, local symbol? */
230  if(addr == NOLL) {
231  return CLE_UNKNOWN_SEGMENT;
232  }
233  } else {
234  ret = pread(info->name, sizeof(info->name),
235  hdr + info->strtaboff + s.st_name);
236  assert(ret > 0);
237  cle_addr sym = (cle_addr)(uintptr_t)sym_function(info->name);
238 #ifdef __AVR__
239  if(sym != NOLL)
240  sym = sym << 1;
241 #endif
242  if(sym == NOLL)
243  sym = (cle_addr)(uintptr_t)sym_object(info->name);
244 
245  if(addr == NOLL && sym != NOLL) { /* Imported symbol. */
246  addr = sym;
247  } else if(addr != NOLL && sym == NOLL) { /* Exported symbol. */
248  addr = addr + s.st_value;
249  } else if(addr == NOLL && sym == NOLL) {
250  PRINTF("cle: undefined reference to %.32s (%d)\n",
251  info->name, s.st_info);
252  return CLE_UNDEFINED; /* Or COMMON symbol. */
253  } else if(addr != NOLL && sym != NOLL) {
254  PRINTF("cle: multiple definitions of %.32s (%d)\n",
255  info->name, s.st_info);
256  return CLE_MULTIPLY_DEFINED;
257  }
258  }
259 
260  addr += rela.r_addend;
261 
262  ret = cle_write_reloc(segmem + rela.r_offset, &rela, addr, info);
263  if(ret != CLE_OK) {
264  return ret;
265  }
266  }
267  return CLE_OK;
268 }
269 
270 /*
271  * Search object file located at offset hdr using function
272  * pread. Search for symbol named symbol and return its address after
273  * relocation or NULL on failure.
274  */
275 void *
276 cle_lookup(struct cle_info *info,
277  int (*pread)(void *, int, off_t),
278  off_t hdr, /* Offset to start of file. */
279  const char *symbol)
280 
281 {
282  struct elf32_sym s;
283  off_t a;
284  cle_addr addr;
285  int ret;
286 
287  for(a = hdr + info->symtaboff;
288  a < hdr + info->symtaboff + info->symtabsize;
289  a += sizeof(s)) {
290  ret = pread(&s, sizeof(s), a);
291  assert(ret > 0);
292 
293  if(s.st_name != 0) {
294  ret = pread(info->name, sizeof(info->name),
295  hdr + info->strtaboff + s.st_name);
296  assert(ret > 0);
297 
298  if(strcmp(info->name, symbol) == 0) { /* Exported symbol found. */
299  if(s.st_shndx == info->bss_shndx) {
300  addr = (cle_addr)(uintptr_t)info->bss;
301  } else if(s.st_shndx == info->data_shndx) {
302  addr = (cle_addr)(uintptr_t)info->data;
303  } else if(s.st_shndx == info->text_shndx) {
304  addr = info->text;
305 #ifdef __AVR__
306  return (void *)(uintptr_t)((addr + s.st_value) >> 1);
307 #endif
308  } else {
309  return NULL; /* Really an error! */
310  }
311 
312  return (void *)(uintptr_t)(addr + s.st_value);
313  }
314  }
315  }
316  return NULL;
317 }
#define NULL
The null pointer.