1 /*
2 ** relinject.c for libelfsh
3 **
4 ** Original SPARC relocation function by thegrugq
5 **
6 ** Started on  Fri Mar 28 14:55:37 2003 mayhem
7 ** Last update Thu Aug 21 03:33:53 2003 mayhem
8 */
9 #include "libelfsh.h"
10 
11 
12 
13 /* Perform relocation on entry for INTEL architecture */
elfsh_relocate_i386(elfshsect_t * new,Elf32_Rel * cur,u_long * dword,u_long addr)14 static int      elfsh_relocate_i386(elfshsect_t		*new,
15 				    Elf32_Rel		*cur,
16 				    u_long		*dword,
17 				    u_long		addr)
18 {
19   switch (elfsh_get_reltype(cur))
20     {
21 
22       /* Absolute reference */
23     case R_386_32:
24       *dword = addr;
25       break;
26 
27       /* Relative reference */
28     case R_386_PC32:
29       *dword = addr - (new->shdr->sh_addr + cur->r_offset + sizeof(u_long));
30       break;
31 
32     default:
33       ELFSH_SETERROR("[libelfsh:relocate_etrel_section] "
34 		     "Unsupported relocation type\n", -1);
35     }
36   return (0);
37 }
38 
39 
40 
41 /* Perform relocation on entry for SPARC architecture */
elfsh_relocate_sparc(elfshsect_t * new,Elf32_Rela * cur,u_long * dword,u_long addr)42 static int       elfsh_relocate_sparc(elfshsect_t       * new,
43                                       Elf32_Rela        * cur,
44                                       u_long            * dword,
45                                       u_long              addr)
46 {
47   int		retval;
48   u_long	result;
49 
50 #define ADD     (cur->r_addend)
51 #define BAS     (elfsh_get_object_baseaddr(new->parent))
52 #define GOT     0 /* GOT isn't available ... */
53 #define PLT     0 /* PLT isn't available ... */
54 #define PLA     (new->shdr->sh_addr + cur->r_offset)
55 #define SYM     (addr)
56 
57 #define VERIFY8(x)      (((x) & (0xFFFFFFFF <<  8)) ? 1 : 0)
58 #define VERIFY13(x)     (((x) & (0xFFFFFFFF << 13)) ? 1 : 0)
59 #define VERIFY16(x)     (((x) & (0xFFFFFFFF << 16)) ? 1 : 0)
60 #define VERIFY22(x)     (((x) & (0xFFFFFFFF << 22)) ? 1 : 0)
61 #define VERIFY30(x)     (((x) & (0xFFFFFFFF << 30)) ? 1 : 0)
62 #define VERIFY32(x)     (((x) & (0xFFFFFFFF <<  0)) ? 1 : 0)
63 
64 #define TRUNCATE8(x)    ((x) & ~(0xFFFFFFFF <<  8))
65 #define TRUNCATE13(x)   ((x) & ~(0xFFFFFFFF << 13))
66 #define TRUNCATE16(x)   ((x) & ~(0xFFFFFFFF << 16))
67 #define TRUNCATE22(x)   ((x) & ~(0xFFFFFFFF << 22))
68 #define TRUNCATE30(x)   ((x) & ~(0xFFFFFFFF << 30))
69 #define TRUNCATE32(x)   (x) /* XXX just assume its 32bit */
70 
71   retval = result = 0;
72   switch (elfsh_get_reltype((Elf32_Rel *)cur))
73     {
74     case R_SPARC_NONE:
75       break;
76     case R_SPARC_8:
77       result = SYM + ADD;
78       if (VERIFY8(result))
79 	; // XXX error condition.
80       break;
81     case R_SPARC_16:
82       result = SYM + ADD;
83       if (VERIFY16(result))
84 	; // XXX error condition
85       break;
86     case R_SPARC_32:
87       result = SYM + ADD;
88       if (VERIFY32(result))
89 	; // XXX error condition
90       break;
91     case R_SPARC_DISP8:
92       result = SYM + ADD - PLA;
93       if (VERIFY8(result))
94 	; // XXX error condition
95       break;
96     case R_SPARC_DISP16:
97       result = SYM + ADD - PLA;
98       if (VERIFY16(result))
99 	; // XXX error condition
100       break;
101     case R_SPARC_DISP32:
102       result = SYM + ADD - PLA;
103       if (VERIFY32(result))
104 	; // XXX error condition
105       break;
106     case R_SPARC_WDISP30:
107       result = (SYM + ADD - PLA) >> 2;
108       if (VERIFY30(result))
109 	; // XXX error condition
110       break;
111     case R_SPARC_WDISP22:
112       result = (SYM + ADD - PLA) >> 10;
113       if (VERIFY22(result))
114 	; // XXX error condition
115       break;
116     case R_SPARC_HI22:
117       result = TRUNCATE22(((SYM + ADD) >> 10));
118       break;
119     case R_SPARC_22:
120       result = SYM + ADD;
121       if (VERIFY22(result))
122 	; // XXX error condition
123       break;
124     case R_SPARC_13:
125       result = SYM + ADD;
126       if (VERIFY13(result))
127 	; // XXX error condition
128       break;
129     case R_SPARC_LO10:
130       result = TRUNCATE13(((SYM + ADD) & 0x3FF));
131       break;
132     case R_SPARC_GOT10:
133     case R_SPARC_GOT13:
134     case R_SPARC_GOT22:
135       // unsupported
136       retval = -1;
137       break;
138     case R_SPARC_PC10:
139       result = TRUNCATE13(((SYM + ADD - PLA) & 0x3FF));
140       break;
141     case R_SPARC_PC22:
142       result = (SYM + ADD - PLA) >> 10;
143       if (VERIFY22(result))
144 	; // XXX error condition
145       break;
146     case R_SPARC_WPLT30:
147       // unsupported
148       retval = -1;
149       break;
150     case R_SPARC_COPY:
151       break;
152     case R_SPARC_GLOB_DAT:
153       result = SYM + ADD;
154       if (VERIFY32(result))
155 	; // XXX error condition
156       break;
157     case R_SPARC_JMP_SLOT:
158       retval = -1;
159       break;
160     case R_SPARC_RELATIVE:
161       result = BAS + ADD;
162       if (VERIFY32(result))
163 	; // XXX error condition
164       break;
165     case R_SPARC_UA32:
166       result = SYM + ADD;
167       if (VERIFY32(result))
168 	; // XXX error condition
169       break;
170     default:
171       ELFSH_SETERROR("[libelfsh:relocate_sparc] Unsupported architecture\n",
172 		     -1);
173       break;
174     }
175 
176 #undef ADD
177 #undef BAS
178 #undef GOT
179 #undef PLT
180 #undef PLA
181 #undef SYM
182   *dword += result;
183   return (retval);
184 }
185 
186 
187 
188 
189 /* Perform relocation on entry */
elfsh_relocate_entry(elfshsect_t * new,void * reloc,u_long * dword,u_long addr)190 static int      elfsh_relocate_entry(elfshsect_t        *new,
191                                      void               *reloc,
192                                      u_long             *dword,
193                                      u_long             addr)
194 {
195   int retval;
196 
197   switch (elfsh_get_arch(new->parent->hdr))
198     {
199     case EM_386:
200     /* XXX: case EM_486: */
201        retval = elfsh_relocate_i386(new, (Elf32_Rel *) reloc, dword, addr);
202        break;
203     case EM_SPARC:
204     case EM_SPARC32PLUS:
205       retval = elfsh_relocate_sparc(new, (Elf32_Rela *) reloc, dword, addr);
206       break;
207     case EM_SPARCV9: /* can't support just yet.. */
208     default:
209       ELFSH_SETERROR("[libelfsh:relocate_entry] Unsupported architecture\n",
210 		     -1);
211       break;
212     }
213   return retval;
214 }
215 
216 
217 
218 /* Relocate the just injected section */
elfsh_relocate_etrel_section(elfshsect_t * new,elfshsect_t * reltab)219 static int	elfsh_relocate_etrel_section(elfshsect_t *new,
220 					     elfshsect_t *reltab)
221 {
222   Elf32_Rel	*cur;
223   u_int		index;
224   Elf32_Sym	*sym;
225   u_int		size;
226   long		*dword;
227   long		addr;
228   char		*name;
229   char		tmpname[BUFSIZ];
230   elfshsect_t	*sect;
231 
232 
233 
234 #if __DEBUG_RELADD__
235   printf("[DEBUG_RELADD] Using reloc table from %s [%s] data at %p \n",
236 	 reltab->parent->name, 	 reltab->name, reltab->data);
237 #endif
238 
239   /* Loop on the relocation table entries */
240   size = (reltab->shdr->sh_type == SHT_RELA ?
241 	  sizeof(Elf32_Rela) : sizeof(Elf32_Rel));
242   size = reltab->shdr->sh_size / size;
243 
244   for (index = 0; index < size; index++)
245     {
246 
247       /* Get symbol value in ET_REL */
248       cur = (reltab->shdr->sh_type == SHT_RELA ?
249 	     (void *) (((Elf32_Rela *) reltab->data) + index):
250 	     (void *) (((Elf32_Rel  *) reltab->data) + index));
251       sym  = elfsh_get_symbol_from_reloc(reltab->parent, cur);
252       name = elfsh_get_symname_from_reloc(reltab->parent, cur);
253       if (sym == NULL || name == NULL)
254 	return (-1);
255 
256       /* Grab a pointer on the dword that need to be relocated */
257       dword = (long *) ((char *) new->data + cur->r_offset);
258 
259       /*
260       ** If symbol type is NOTYPE, we use ET_EXEC symtab, else if
261       ** symbol link is COMMON, we use ET_REL symbol inserted in ET_EXEC
262       ** during BSS sizescan in bss.c:elfsh_find_bsslen()
263       */
264       if (elfsh_get_symbol_type(sym) == STT_NOTYPE ||
265 	  elfsh_get_symbol_link(sym) == SHN_COMMON)
266 	{
267 
268 	  sym = elfsh_get_metasym_by_name(new->parent, name);
269 	  if (sym == NULL)
270 	    ELFSH_SETERROR("[libelfsh:relocate_etrel_section] "
271 			   "Cant find requested symbol in ET_EXEC\n", -1);
272 	  addr = sym->st_value;
273 
274 #if __DEBUG_RELADD__
275 	  printf("[DEBUG_RELADD] Relocate using existing symbol %-20s [%08X]\n",
276 		 name, (u_int) addr);
277 #endif
278 
279 	}
280 
281 
282       /* Compute addr giving the injected section's vaddr in ET_EXEC */
283       else
284 	{
285 
286 	  /* Find target section in ET_REL */
287 	  sect = elfsh_get_section_by_index(reltab->parent, sym->st_shndx,
288 					    NULL, NULL);
289 	  if (sect == NULL)
290 	    ELFSH_SETERROR("[libelfsh:relocate_etrel_section] "
291 			   "Cant find extracted section in ET_REL\n", -1);
292 
293 	  /* Find corresponding inserted section in ET_EXEC */
294 	  snprintf(tmpname, sizeof(tmpname), "%s%s", reltab->parent->name, sect->name);
295 	  sect = elfsh_get_section_by_name(new->parent, tmpname, NULL, NULL, NULL);
296 	  if (sect == NULL)
297 	    ELFSH_SETERROR("[libelfsh:relocate_etrel_section] "
298 			   "Cant find inserted section in ET_EXEC\n", -1);
299 
300 	  /* Compute pointer value */
301 	  addr = sect->shdr->sh_addr;
302 	  addr += (elfsh_get_symbol_type(sym) == STT_SECTION &&
303 		   !FILE_IS_SPARC(sect->parent) ? *dword : sym->st_value);
304 
305 
306 #if __DEBUG_RELADD__
307 	  printf("[DEBUG_RELADD] Relocate using section %-20s base [-> %08X] \n",
308 		 sect->name, (unsigned int) addr);
309 #endif
310 
311 
312 	}
313 
314 
315       /* Perform relocation */
316       if (elfsh_relocate_entry(new, cur, dword, addr) < 0)
317 	return (-1);
318 
319     }
320   return (0);
321 }
322 
323 
324 
325 /* Inject a section from ET_REL object into ET_EXEC */
elfsh_inject_etrel_section(elfshobj_t * file,elfshsect_t * sect)326 static int	elfsh_inject_etrel_section(elfshobj_t *file, elfshsect_t *sect)
327 {
328   Elf32_Shdr	hdr;
329   elfshsect_t	*new;
330   char		*newname;
331   char		writable;
332   int		mode;
333   char		*data;
334 
335   /* else create a new section */
336   hdr = elfsh_create_shdr(0, sect->shdr->sh_type, sect->shdr->sh_flags,
337 			  0, 0, sect->shdr->sh_size, 0, 0, 0, 0);
338   XALLOC(newname, strlen(sect->parent->name) + strlen(sect->name) + 2, -1);
339   sprintf(newname, "%s%s", sect->parent->name, sect->name);
340   new = elfsh_create_section(newname);
341 
342   /* Copy the data */
343   XALLOC(data, sect->shdr->sh_size, -1);
344   memcpy(data, sect->data, sect->shdr->sh_size);
345 
346   /* Inject new section by top or after bss depending on its type */
347   writable = elfsh_get_section_writableflag(sect->shdr);
348 
349   /* FreeBSD is incompatible with pre-interp injection */
350 #if defined __FreeBSD__ || defined __DragonFly__
351   mode     = ELFSH_DATA_INJECTION;
352 #else
353   mode     = (writable ? ELFSH_DATA_INJECTION : ELFSH_CODE_INJECTION);
354 #endif
355 
356   if (elfsh_insert_mapped_section(file, new, hdr, data, mode) < 0)
357     goto bad;
358   new = elfsh_get_section_by_name(file, newname, NULL, NULL, NULL);
359   if (new == NULL)
360     goto bad;
361 
362   return (0);
363  bad:
364   free(newname);
365   return (-1);
366 }
367 
368 
369 
370 /* Inject a ET_REL object into a ET_EXEC object */
elfsh_inject_etrel(elfshobj_t * file,elfshobj_t * rel)371 int		elfsh_inject_etrel(elfshobj_t *file, elfshobj_t *rel)
372 {
373   elfshsect_t	*sect;
374   elfshsect_t	*reltab;
375   char		sctname[BUFSIZ];
376   Elf32_Sym	newsym;
377   u_int		symnbr;
378   u_int		index;
379   static Elf32_Sym	*sym;
380   u_char	type;
381   elfshsect_t	*bss;
382   elfshzone_t	*zone;
383   int		bss_size;
384 
385 
386   /* Sanity checks */
387   if (file == NULL || file->hdr == NULL || rel == NULL || rel->hdr == NULL)
388     ELFSH_SETERROR("[libelfsh:inject_etrel] Invalid NULL parameter\n", -1);
389   if (file->hdr->e_type != ET_EXEC || rel->hdr->e_type != ET_REL)
390     ELFSH_SETERROR("[libelfsh:inject_etrel] Bad object types\n", -1);
391 
392   /* First fuzion BSS */
393   bss = elfsh_fixup_bss(file);
394   if (bss == NULL)
395     return (-1);
396   bss_size = elfsh_find_bsslen(file, rel);
397   if (bss_size < 0)
398     return (-1);
399   zone = elfsh_create_bsszone(rel->name, bss->shdr->sh_size, bss_size);
400   if (zone == NULL)
401     return (-1);
402   if (elfsh_add_bsszone(bss, zone) < 0)
403     return (-1);
404 
405 
406   /* First pass : find and inject all allocatable sections */
407   for (index = 0; index < rel->hdr->e_shnum; index++)
408     {
409 
410       /* Get the current section */
411       sect = elfsh_get_section_by_index(rel, index, NULL, NULL);
412       if (sect == NULL)
413 	ELFSH_SETERROR("[libelfsh:inject_etrel] Cant read section in ET_REL\n", -1);
414 
415       /* Check if the current section need to be mapped */
416       if (elfsh_get_section_allocflag(sect->shdr) &&
417 	  sect->shdr->sh_size &&
418 	  sect->shdr->sh_type == SHT_PROGBITS &&
419 	  elfsh_inject_etrel_section(file, sect) < 0)
420       return (-1);
421     }
422 
423   /* Do a copy of the procedure linkage table for eventual redirection */
424   if (elfsh_copy_plt(file) < 0)
425     return (-1);
426 
427 #if __DEBUG_RELADD__
428   printf("[DEBUG_RELADD] Entering intermediate symbol injection loop\n");
429 #endif
430 
431 
432   /* Intermediate pass : Inject ET_REL symbol table into host file */
433   sym = elfsh_get_symtab(rel, &symnbr);
434   for (index = 0; index < symnbr; index++)
435     {
436 
437       type = elfsh_get_symbol_type(sym + index);
438 
439       /* Avoid non-injectable symbols */
440       if (type != STT_FUNC && type != STT_OBJECT)
441 	continue;
442       if (sym[index].st_shndx >= rel->hdr->e_shnum)
443 	continue;
444 
445       /* Find target section in ET_REL */
446       sect = elfsh_get_section_by_index(rel, sym[index].st_shndx, NULL, NULL);
447       if (sect == NULL)
448 	ELFSH_SETERROR("[libelfsh:inject_etrel] "
449 		       "Cant find extracted section in ET_REL\n", -1);
450 
451       /* Filter symbols using source section */
452       /* XXX: not sure it is necessary */
453       if (sect->shdr->sh_type != SHT_PROGBITS || !sect->shdr->sh_size ||
454 	  !elfsh_get_section_allocflag(sect->shdr))
455 	continue;
456 
457       /* Find corresponding inserted section in ET_EXEC */
458       snprintf(sctname, sizeof(sctname), "%s%s", rel->name, sect->name);
459       sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL);
460       if (sect == NULL)
461 	ELFSH_SETERROR("[libelfsh:inject_etrel] "
462 		       "Cant find inserted section in ET_EXEC\n", -1);
463 
464 #if __DEBUG_RELADD__
465       printf("[DEBUG_RELADD] Injected ET_REL symbol %-20s [%08X] \n",
466 	     elfsh_get_symbol_name(rel, sym + index),
467 	     (u_int) (sect->shdr->sh_addr + sym[index].st_value));
468 #endif
469 
470       /* Add symbol in host file */
471       newsym = elfsh_create_symbol(sect->shdr->sh_addr + sym[index].st_value,
472 				   sym[index].st_size,
473 				   elfsh_get_symbol_type(sym + index),
474 				   elfsh_get_symbol_bind(sym + index),
475 				   0, sect->index);
476       if (elfsh_insert_symbol(file->secthash[ELFSH_SECTION_SYMTAB], &newsym,
477 			      elfsh_get_symbol_name(rel, sym + index)) < 0)
478 	return (-1);
479     }
480 
481   /* Resynchronize sorted instances of symbol table */
482   if (elfsh_sync_sorted_symtab(file->secthash[ELFSH_SECTION_SYMTAB]) < 0)
483     return (-1);
484 
485 
486 #if __DEBUG_RELADD__
487   printf("[DEBUG_RELADD] Entering final relocation loop\n");
488 #endif
489 
490 
491   /* Last pass : relocate each inserted section */
492   for (index = 0; index < rel->hdr->e_shnum; index++)
493     {
494       sect = elfsh_get_section_by_index(rel, index, NULL, NULL);
495       if (sect == NULL)
496 	ELFSH_SETERROR("[libelfsh:inject_etrel] Cant get section in ET_REL\n", -1);
497 
498       /* Check if the section is mapped */
499       if (elfsh_get_section_allocflag(sect->shdr) && sect->shdr->sh_size &&
500 	  sect->shdr->sh_type == SHT_PROGBITS)
501 	{
502 
503 	  /* Find the associate relocation section */
504 	  snprintf(sctname, sizeof(sctname), "%s%s",
505 		   (IS_REL(sect) ? ".rel" : ".rela"), sect->name);
506 	  reltab = elfsh_get_section_by_name(rel, sctname, NULL, NULL, NULL);
507 	  if (reltab == NULL)
508 	    continue;
509 
510 	  /* Find the injected instance of this allocatable section in the ET_EXEC */
511 	  snprintf(sctname, sizeof(sctname), "%s%s", sect->parent->name, sect->name);
512 	  sect = elfsh_get_section_by_name(file, sctname, NULL, NULL, NULL);
513 	  if (sect == NULL)
514 	    ELFSH_SETERROR("[libelfsh:inject_etrel] Cant get section in ET_EXEC\n", -1);
515 	  if (elfsh_relocate_etrel_section(sect, reltab) < 0)
516 	    return (-1);
517 	}
518     }
519 
520   /* Everything ran OK */
521   return (0);
522 }
523 
524