dl.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /* dl-386.c - arch-dependent part of loadable module support */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2002,2005,2007,2009 Free Software Foundation, Inc.
  5. *
  6. * GRUB is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * GRUB is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <grub/dl.h>
  20. #include <grub/elf.h>
  21. #include <grub/misc.h>
  22. #include <grub/err.h>
  23. #include <grub/cpu/types.h>
  24. #include <grub/mm.h>
  25. /* Dummy __gnu_local_gp. Resolved by linker. */
  26. static char __gnu_local_gp_dummy;
  27. /* Check if EHDR is a valid ELF header. */
  28. grub_err_t
  29. grub_arch_dl_check_header (void *ehdr)
  30. {
  31. Elf_Ehdr *e = ehdr;
  32. /* Check the magic numbers. */
  33. #ifdef WORDS_BIGENDIAN
  34. if (e->e_ident[EI_CLASS] != ELFCLASS32
  35. || e->e_ident[EI_DATA] != ELFDATA2MSB
  36. || e->e_machine != EM_MIPS)
  37. #else
  38. if (e->e_ident[EI_CLASS] != ELFCLASS32
  39. || e->e_ident[EI_DATA] != ELFDATA2LSB
  40. || e->e_machine != EM_MIPS)
  41. #endif
  42. return grub_error (GRUB_ERR_BAD_OS, "invalid arch specific ELF magic");
  43. return GRUB_ERR_NONE;
  44. }
  45. /* Relocate symbols. */
  46. grub_err_t
  47. grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
  48. {
  49. Elf_Ehdr *e = ehdr;
  50. Elf_Shdr *s;
  51. Elf_Word entsize;
  52. unsigned i;
  53. grub_size_t gp_size = 0;
  54. /* FIXME: suboptimal. */
  55. grub_uint32_t *gp, *gpptr;
  56. grub_uint32_t gp0;
  57. /* Find a symbol table. */
  58. for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
  59. i < e->e_shnum;
  60. i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
  61. if (s->sh_type == SHT_SYMTAB)
  62. break;
  63. if (i == e->e_shnum)
  64. return grub_error (GRUB_ERR_BAD_MODULE, "no symtab found");
  65. entsize = s->sh_entsize;
  66. /* Find reginfo. */
  67. for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
  68. i < e->e_shnum;
  69. i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
  70. if (s->sh_type == SHT_MIPS_REGINFO)
  71. break;
  72. if (i == e->e_shnum)
  73. return grub_error (GRUB_ERR_BAD_MODULE, "no reginfo found");
  74. gp0 = ((grub_uint32_t *)((char *) e + s->sh_offset))[5];
  75. for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
  76. i < e->e_shnum;
  77. i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
  78. if (s->sh_type == SHT_REL)
  79. {
  80. grub_dl_segment_t seg;
  81. /* Find the target segment. */
  82. for (seg = mod->segment; seg; seg = seg->next)
  83. if (seg->section == s->sh_info)
  84. break;
  85. if (seg)
  86. {
  87. Elf_Rel *rel, *max;
  88. for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
  89. max = rel + s->sh_size / s->sh_entsize;
  90. rel < max;
  91. rel++)
  92. switch (ELF_R_TYPE (rel->r_info))
  93. {
  94. case R_MIPS_GOT16:
  95. case R_MIPS_CALL16:
  96. case R_MIPS_GPREL32:
  97. gp_size += 4;
  98. break;
  99. }
  100. }
  101. }
  102. if (gp_size > 0x08000)
  103. return grub_error (GRUB_ERR_OUT_OF_RANGE, "__gnu_local_gp is too big\n");
  104. gpptr = gp = grub_malloc (gp_size);
  105. if (!gp)
  106. return grub_errno;
  107. for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
  108. i < e->e_shnum;
  109. i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
  110. if (s->sh_type == SHT_REL)
  111. {
  112. grub_dl_segment_t seg;
  113. /* Find the target segment. */
  114. for (seg = mod->segment; seg; seg = seg->next)
  115. if (seg->section == s->sh_info)
  116. break;
  117. if (seg)
  118. {
  119. Elf_Rel *rel, *max;
  120. for (rel = (Elf_Rel *) ((char *) e + s->sh_offset),
  121. max = rel + s->sh_size / s->sh_entsize;
  122. rel < max;
  123. rel++)
  124. {
  125. Elf_Word *addr;
  126. Elf_Sym *sym;
  127. if (seg->size < rel->r_offset)
  128. return grub_error (GRUB_ERR_BAD_MODULE,
  129. "reloc offset is out of the segment");
  130. addr = (Elf_Word *) ((char *) seg->addr + rel->r_offset);
  131. sym = (Elf_Sym *) ((char *) mod->symtab
  132. + entsize * ELF_R_SYM (rel->r_info));
  133. if (sym->st_value == (grub_addr_t) &__gnu_local_gp_dummy)
  134. sym->st_value = (grub_addr_t) gp;
  135. switch (ELF_R_TYPE (rel->r_info))
  136. {
  137. case R_MIPS_HI16:
  138. {
  139. grub_uint32_t value;
  140. Elf_Rel *rel2;
  141. /* Handle partner lo16 relocation. Lower part is
  142. treated as signed. Hence add 0x8000 to compensate.
  143. */
  144. value = (*(grub_uint16_t *) addr << 16)
  145. + sym->st_value + 0x8000;
  146. for (rel2 = rel + 1; rel2 < max; rel2++)
  147. if (ELF_R_SYM (rel2->r_info)
  148. == ELF_R_SYM (rel->r_info)
  149. && ELF_R_TYPE (rel2->r_info) == R_MIPS_LO16)
  150. {
  151. value += *(grub_int16_t *)
  152. ((char *) seg->addr + rel2->r_offset);
  153. break;
  154. }
  155. *(grub_uint16_t *) addr = (value >> 16) & 0xffff;
  156. }
  157. break;
  158. case R_MIPS_LO16:
  159. *(grub_uint16_t *) addr += (sym->st_value) & 0xffff;
  160. break;
  161. case R_MIPS_32:
  162. *(grub_uint32_t *) addr += sym->st_value;
  163. break;
  164. case R_MIPS_GPREL32:
  165. *(grub_uint32_t *) addr = sym->st_value
  166. + *(grub_uint32_t *) addr + gp0 - (grub_uint32_t)gp;
  167. break;
  168. case R_MIPS_26:
  169. {
  170. grub_uint32_t value;
  171. grub_uint32_t raw;
  172. raw = (*(grub_uint32_t *) addr) & 0x3ffffff;
  173. value = raw << 2;
  174. value += sym->st_value;
  175. raw = (value >> 2) & 0x3ffffff;
  176. *(grub_uint32_t *) addr =
  177. raw | ((*(grub_uint32_t *) addr) & 0xfc000000);
  178. }
  179. break;
  180. case R_MIPS_GOT16:
  181. case R_MIPS_CALL16:
  182. /* FIXME: reuse*/
  183. *gpptr = sym->st_value + *(grub_uint16_t *) addr;
  184. *(grub_uint16_t *) addr
  185. = sizeof (grub_uint32_t) * (gpptr - gp);
  186. gpptr++;
  187. break;
  188. default:
  189. {
  190. grub_free (gp);
  191. return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
  192. "Unknown relocation type %d\n",
  193. ELF_R_TYPE (rel->r_info));
  194. }
  195. break;
  196. }
  197. }
  198. }
  199. }
  200. return GRUB_ERR_NONE;
  201. }
  202. void
  203. grub_arch_dl_init_linker (void)
  204. {
  205. grub_dl_register_symbol ("__gnu_local_gp", &__gnu_local_gp_dummy, 0);
  206. }