Revamp relocation handling.
Move more code to common dl.c. Add missing veneers for arm and arm64. Decreases kernel size by 70 bytes on i386-pc (40-50 compressed)
This commit is contained in:
		
							parent
							
								
									a846dd4b3a
								
							
						
					
					
						commit
						8c534b85f1
					
				
					 18 changed files with 1097 additions and 987 deletions
				
			
		|  | @ -58,23 +58,13 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, | |||
| { | ||||
|   const Elf_Ehdr *e = ehdr; | ||||
|   const Elf_Shdr *s; | ||||
|   unsigned i; | ||||
|   /* FIXME: suboptimal.  */ | ||||
|   grub_size_t gp_size = 0; | ||||
|   unsigned i; | ||||
| 
 | ||||
|   *tramp = 0; | ||||
|   *got = 0; | ||||
| 
 | ||||
|   /* Find a symbol table.  */ | ||||
|   for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); | ||||
|        i < e->e_shnum; | ||||
|        i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) | ||||
|     if (s->sh_type == SHT_SYMTAB) | ||||
|       break; | ||||
| 
 | ||||
|   if (i == e->e_shnum) | ||||
|     return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); | ||||
| 
 | ||||
|   for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff); | ||||
|        i < e->e_shnum; | ||||
|        i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize)) | ||||
|  | @ -106,192 +96,166 @@ grub_arch_dl_get_tramp_got_size (const void *ehdr, grub_size_t *tramp, | |||
| 
 | ||||
| /* Relocate symbols.  */ | ||||
| grub_err_t | ||||
| grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) | ||||
| grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr, | ||||
| 			       Elf_Shdr *s, grub_dl_segment_t seg) | ||||
| { | ||||
|   Elf_Ehdr *e = ehdr; | ||||
|   Elf_Shdr *s; | ||||
|   Elf_Word entsize; | ||||
|   unsigned i; | ||||
|   /* FIXME: suboptimal.  */ | ||||
|   grub_uint32_t *gp, *gpptr; | ||||
|   grub_uint32_t gp0; | ||||
|   Elf_Ehdr *e = ehdr; | ||||
| 
 | ||||
|   /* Find a symbol table.  */ | ||||
|   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); | ||||
|        i < e->e_shnum; | ||||
|        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | ||||
|     if (s->sh_type == SHT_SYMTAB) | ||||
|       break; | ||||
|   if (!mod->reginfo) | ||||
|     { | ||||
|       unsigned i; | ||||
|       Elf_Shdr *ri; | ||||
| 
 | ||||
|   if (i == e->e_shnum) | ||||
|     return grub_error (GRUB_ERR_BAD_MODULE, N_("no symbol table")); | ||||
|       /* Find reginfo. */ | ||||
|       for (i = 0, ri = (Elf_Shdr *) ((char *) ehdr + e->e_shoff); | ||||
| 	   i < e->e_shnum; | ||||
| 	   i++, ri = (Elf_Shdr *) ((char *) ri + e->e_shentsize)) | ||||
| 	if (ri->sh_type == SHT_MIPS_REGINFO) | ||||
| 	  break; | ||||
|       if (i == e->e_shnum) | ||||
| 	return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); | ||||
|       mod->reginfo = (grub_uint32_t *)((char *) ehdr + ri->sh_offset); | ||||
|     } | ||||
| 
 | ||||
|   entsize = s->sh_entsize; | ||||
|   gp0 = mod->reginfo[5]; | ||||
|   Elf_Rel *rel, *max; | ||||
| 
 | ||||
|   /* Find reginfo. */ | ||||
|   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); | ||||
|        i < e->e_shnum; | ||||
|        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | ||||
|     if (s->sh_type == SHT_MIPS_REGINFO) | ||||
|       break; | ||||
|   for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), | ||||
| 	 max = (Elf_Rel *) ((char *) rel + s->sh_size); | ||||
|        rel < max; | ||||
|        rel = (Elf_Rel *) ((char *) rel + s->sh_entsize)) | ||||
|     { | ||||
|       grub_uint8_t *addr; | ||||
|       Elf_Sym *sym; | ||||
|       grub_uint32_t sym_value; | ||||
| 
 | ||||
|   if (i == e->e_shnum) | ||||
|     return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found"); | ||||
|       if (seg->size < rel->r_offset) | ||||
| 	return grub_error (GRUB_ERR_BAD_MODULE, | ||||
| 			   "reloc offset is out of the segment"); | ||||
| 
 | ||||
|   gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5]; | ||||
| 
 | ||||
|   gpptr = gp = mod->got; | ||||
| 
 | ||||
|   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff); | ||||
|        i < e->e_shnum; | ||||
|        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize)) | ||||
|     if (s->sh_type == SHT_REL) | ||||
|       { | ||||
| 	grub_dl_segment_t seg; | ||||
| 
 | ||||
| 	/* Find the target segment.  */ | ||||
| 	for (seg = mod->segment; seg; seg = seg->next) | ||||
| 	  if (seg->section == s->sh_info) | ||||
| 	    break; | ||||
| 
 | ||||
| 	if (seg) | ||||
|       addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); | ||||
|       sym = (Elf_Sym *) ((char *) mod->symtab | ||||
| 			 + mod->symsize * ELF_R_SYM (rel->r_info)); | ||||
|       sym_value = sym->st_value; | ||||
|       if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) | ||||
| 	sym_value = (grub_addr_t) mod->got; | ||||
|       else if (sym_value == (grub_addr_t) &_gp_disp_dummy) | ||||
| 	{ | ||||
| 	  sym_value = (grub_addr_t) mod->got - (grub_addr_t) addr; | ||||
| 	  if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) | ||||
| 	    /* ABI mandates +4 even if partner lui doesn't
 | ||||
| 	       immediately precede addiu.  */ | ||||
| 	    sym_value += 4; | ||||
| 	} | ||||
|       switch (ELF_R_TYPE (rel->r_info)) | ||||
| 	{ | ||||
| 	case R_MIPS_HI16: | ||||
| 	  { | ||||
| 	    Elf_Rel *rel, *max; | ||||
| 
 | ||||
| 	    for (rel = (Elf_Rel *) ((char *) e + s->sh_offset), | ||||
| 		   max = rel + s->sh_size / s->sh_entsize; | ||||
| 		 rel < max; | ||||
| 		 rel++) | ||||
| 	      { | ||||
| 		grub_uint8_t *addr; | ||||
| 		Elf_Sym *sym; | ||||
| 		grub_uint32_t sym_value; | ||||
| 
 | ||||
| 		if (seg->size < rel->r_offset) | ||||
| 		  return grub_error (GRUB_ERR_BAD_MODULE, | ||||
| 				     "reloc offset is out of the segment"); | ||||
| 
 | ||||
| 		addr = (grub_uint8_t *) ((char *) seg->addr + rel->r_offset); | ||||
| 		sym = (Elf_Sym *) ((char *) mod->symtab | ||||
| 				     + entsize * ELF_R_SYM (rel->r_info)); | ||||
| 		sym_value = sym->st_value; | ||||
| 		if (sym_value == (grub_addr_t) &__gnu_local_gp_dummy) | ||||
| 		  sym_value = (grub_addr_t) gp; | ||||
| 		else if (sym_value == (grub_addr_t) &_gp_disp_dummy) | ||||
| 		  { | ||||
| 		    sym_value = (grub_addr_t) gp - (grub_addr_t) addr; | ||||
| 		    if (ELF_R_TYPE (rel->r_info) == R_MIPS_LO16) | ||||
| 		      /* ABI mandates +4 even if partner lui doesn't
 | ||||
| 			 immediately precede addiu.  */ | ||||
| 		      sym_value += 4; | ||||
| 		  } | ||||
| 		switch (ELF_R_TYPE (rel->r_info)) | ||||
| 		  { | ||||
| 		  case R_MIPS_HI16: | ||||
| 		    { | ||||
| 		      grub_uint32_t value; | ||||
| 		      Elf_Rel *rel2; | ||||
| 	    grub_uint32_t value; | ||||
| 	    Elf_Rel *rel2; | ||||
| 
 | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 		      addr += 2; | ||||
| 	    addr += 2; | ||||
| #endif | ||||
| 
 | ||||
| 		      /* Handle partner lo16 relocation. Lower part is
 | ||||
| 			 treated as signed. Hence add 0x8000 to compensate.  | ||||
| 		       */ | ||||
| 		      value = (*(grub_uint16_t *) addr << 16) | ||||
| 			+ sym_value + 0x8000; | ||||
| 		      for (rel2 = rel + 1; rel2 < max; rel2++) | ||||
| 			if (ELF_R_SYM (rel2->r_info) | ||||
| 			    == ELF_R_SYM (rel->r_info) | ||||
| 			    && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) | ||||
| 			  { | ||||
| 			    value += *(grub_int16_t *) | ||||
| 			      ((char *) seg->addr + rel2->r_offset | ||||
| 	    /* Handle partner lo16 relocation. Lower part is
 | ||||
| 	       treated as signed. Hence add 0x8000 to compensate.  | ||||
| 	    */ | ||||
| 	    value = (*(grub_uint16_t *) addr << 16) | ||||
| 	      + sym_value + 0x8000; | ||||
| 	    for (rel2 = rel + 1; rel2 < max; rel2++) | ||||
| 	      if (ELF_R_SYM (rel2->r_info) | ||||
| 		  == ELF_R_SYM (rel->r_info) | ||||
| 		  && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) | ||||
| 		{ | ||||
| 		  value += *(grub_int16_t *) | ||||
| 		    ((char *) seg->addr + rel2->r_offset | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 			       + 2 | ||||
| 		     + 2 | ||||
| #endif | ||||
| 			       ); | ||||
| 			    break; | ||||
| 			  } | ||||
| 		      *(grub_uint16_t *) addr = (value >> 16) & 0xffff; | ||||
| 		    } | ||||
| 		    break; | ||||
| 		  case R_MIPS_LO16: | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 		    addr += 2; | ||||
| #endif | ||||
| 		    *(grub_uint16_t *) addr += sym_value & 0xffff; | ||||
| 		    break; | ||||
| 		  case R_MIPS_32: | ||||
| 		    *(grub_uint32_t *) addr += sym_value; | ||||
| 		    break; | ||||
| 		  case R_MIPS_GPREL32: | ||||
| 		    *(grub_uint32_t *) addr = sym_value | ||||
| 		      + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp; | ||||
| 		    break; | ||||
| 
 | ||||
| 		  case R_MIPS_26: | ||||
| 		    { | ||||
| 		      grub_uint32_t value; | ||||
| 		      grub_uint32_t raw; | ||||
| 		      raw = (*(grub_uint32_t *) addr) & 0x3ffffff; | ||||
| 		      value = raw << 2; | ||||
| 		      value += sym_value; | ||||
| 		      raw = (value >> 2) & 0x3ffffff; | ||||
| 			 | ||||
| 		      *(grub_uint32_t *) addr =  | ||||
| 			raw | ((*(grub_uint32_t *) addr) & 0xfc000000); | ||||
| 		    } | ||||
| 		    break; | ||||
| 		  case R_MIPS_GOT16: | ||||
| 		    if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) | ||||
| 		      { | ||||
| 			Elf_Rel *rel2; | ||||
| 			/* Handle partner lo16 relocation. Lower part is
 | ||||
| 			   treated as signed. Hence add 0x8000 to compensate. | ||||
| 			*/ | ||||
| 			sym_value += (*(grub_uint16_t *) addr << 16) | ||||
| 			  + 0x8000; | ||||
| 			for (rel2 = rel + 1; rel2 < max; rel2++) | ||||
| 			  if (ELF_R_SYM (rel2->r_info) | ||||
| 			      == ELF_R_SYM (rel->r_info) | ||||
| 			      && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) | ||||
| 			    { | ||||
| 			      sym_value += *(grub_int16_t *) | ||||
| 				((char *) seg->addr + rel2->r_offset | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 				 + 2 | ||||
| #endif | ||||
| 				 ); | ||||
| 			      break; | ||||
| 			    } | ||||
| 			sym_value &= 0xffff0000; | ||||
| 			*(grub_uint16_t *) addr = 0; | ||||
| 		      } | ||||
| 		  case R_MIPS_CALL16: | ||||
| 		    /* FIXME: reuse*/ | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 		    addr += 2; | ||||
| #endif | ||||
| 		    *gpptr = sym_value + *(grub_uint16_t *) addr; | ||||
| 		    *(grub_uint16_t *) addr | ||||
| 		      = sizeof (grub_uint32_t) * (gpptr - gp); | ||||
| 		    gpptr++; | ||||
| 		    break; | ||||
| 		  case R_MIPS_JALR: | ||||
| 		    break; | ||||
| 		  default: | ||||
| 		    { | ||||
| 		      return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | ||||
| 					 N_("relocation 0x%x is not implemented yet"), | ||||
| 					 ELF_R_TYPE (rel->r_info)); | ||||
| 		    } | ||||
| 		    break; | ||||
| 		  } | ||||
| 	      } | ||||
| 		     ); | ||||
| 		  break; | ||||
| 		} | ||||
| 	    *(grub_uint16_t *) addr = (value >> 16) & 0xffff; | ||||
| 	  } | ||||
|       } | ||||
| 	  break; | ||||
| 	case R_MIPS_LO16: | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 	  addr += 2; | ||||
| #endif | ||||
| 	  *(grub_uint16_t *) addr += sym_value & 0xffff; | ||||
| 	  break; | ||||
| 	case R_MIPS_32: | ||||
| 	  *(grub_uint32_t *) addr += sym_value; | ||||
| 	  break; | ||||
| 	case R_MIPS_GPREL32: | ||||
| 	  *(grub_uint32_t *) addr = sym_value | ||||
| 	    + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)mod->got; | ||||
| 	  break; | ||||
| 
 | ||||
| 	case R_MIPS_26: | ||||
| 	  { | ||||
| 	    grub_uint32_t value; | ||||
| 	    grub_uint32_t raw; | ||||
| 	    raw = (*(grub_uint32_t *) addr) & 0x3ffffff; | ||||
| 	    value = raw << 2; | ||||
| 	    value += sym_value; | ||||
| 	    raw = (value >> 2) & 0x3ffffff; | ||||
| 			 | ||||
| 	    *(grub_uint32_t *) addr =  | ||||
| 	      raw | ((*(grub_uint32_t *) addr) & 0xfc000000); | ||||
| 	  } | ||||
| 	  break; | ||||
| 	case R_MIPS_GOT16: | ||||
| 	  if (ELF_ST_BIND (sym->st_info) == STB_LOCAL) | ||||
| 	    { | ||||
| 	      Elf_Rel *rel2; | ||||
| 	      /* Handle partner lo16 relocation. Lower part is
 | ||||
| 		 treated as signed. Hence add 0x8000 to compensate. | ||||
| 	      */ | ||||
| 	      sym_value += (*(grub_uint16_t *) addr << 16) | ||||
| 		+ 0x8000; | ||||
| 	      for (rel2 = rel + 1; rel2 < max; rel2++) | ||||
| 		if (ELF_R_SYM (rel2->r_info) | ||||
| 		    == ELF_R_SYM (rel->r_info) | ||||
| 		    && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16) | ||||
| 		  { | ||||
| 		    sym_value += *(grub_int16_t *) | ||||
| 		      ((char *) seg->addr + rel2->r_offset | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 		       + 2 | ||||
| #endif | ||||
| 		       ); | ||||
| 		    break; | ||||
| 		  } | ||||
| 	      sym_value &= 0xffff0000; | ||||
| 	      *(grub_uint16_t *) addr = 0; | ||||
| 	    } | ||||
| 	case R_MIPS_CALL16: | ||||
| 	  { | ||||
| 	    grub_uint32_t *gpptr = mod->gotptr; | ||||
| 	  /* FIXME: reuse*/ | ||||
| #ifdef GRUB_CPU_WORDS_BIGENDIAN | ||||
| 	    addr += 2; | ||||
| #endif | ||||
| 	    *gpptr = sym_value + *(grub_uint16_t *) addr; | ||||
| 	    *(grub_uint16_t *) addr | ||||
| 	      = sizeof (grub_uint32_t) * (gpptr - (grub_uint32_t *) mod->got); | ||||
| 	    mod->gotptr = gpptr + 1; | ||||
| 	    break; | ||||
| 	  } | ||||
| 	case R_MIPS_JALR: | ||||
| 	  break; | ||||
| 	default: | ||||
| 	  { | ||||
| 	    return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, | ||||
| 			       N_("relocation 0x%x is not implemented yet"), | ||||
| 			       ELF_R_TYPE (rel->r_info)); | ||||
| 	  } | ||||
| 	  break; | ||||
| 	} | ||||
|     } | ||||
| 
 | ||||
|   return GRUB_ERR_NONE; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue