elfXX.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. int
  2. grub_elf_is_elfXX (grub_elf_t elf)
  3. {
  4. return elf->ehdr.ehdrXX.e_ident[EI_CLASS] == ELFCLASSXX;
  5. }
  6. grub_err_t
  7. grub_elfXX_load_phdrs (grub_elf_t elf)
  8. {
  9. grub_ssize_t phdrs_size;
  10. if (elf->phdrs)
  11. return GRUB_ERR_NONE;
  12. phdrs_size = (grub_uint32_t) elf->ehdr.ehdrXX.e_phnum * elf->ehdr.ehdrXX.e_phentsize;
  13. grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
  14. (unsigned long long) elf->ehdr.ehdrXX.e_phoff,
  15. (unsigned long) phdrs_size);
  16. elf->phdrs = grub_malloc (phdrs_size);
  17. if (! elf->phdrs)
  18. return grub_errno;
  19. if ((grub_file_seek (elf->file, elf->ehdr.ehdrXX.e_phoff) == (grub_off_t) -1)
  20. || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
  21. {
  22. if (!grub_errno)
  23. grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
  24. elf->filename);
  25. return grub_errno;
  26. }
  27. #if GRUB_ELF_ENABLE_BI_ENDIAN
  28. if (elf->ehdr.ehdrXX.e_ident[EI_DATA] == GRUB_ELF_OPPOSITE_ENDIANNESS)
  29. {
  30. ElfXX_Phdr *phdr;
  31. for (phdr = elf->phdrs; (char *) phdr < (char *) elf->phdrs + phdrs_size;
  32. phdr = (ElfXX_Phdr *) ((char *) phdr + elf->ehdr.ehdrXX.e_phentsize))
  33. {
  34. phdr->p_type = grub_swap_bytes_wordXX (phdr->p_type);
  35. phdr->p_flags = grub_swap_bytes_wordXX (phdr->p_flags);
  36. phdr->p_offset = grub_swap_bytes_offXX (phdr->p_offset);
  37. phdr->p_vaddr = grub_swap_bytes_addrXX (phdr->p_vaddr);
  38. phdr->p_paddr = grub_swap_bytes_addrXX (phdr->p_paddr);
  39. phdr->p_filesz = grub_swap_bytes_XwordXX (phdr->p_filesz);
  40. phdr->p_memsz = grub_swap_bytes_XwordXX (phdr->p_memsz);
  41. phdr->p_align = grub_swap_bytes_XwordXX (phdr->p_align);
  42. }
  43. }
  44. #endif /* GRUB_ELF_ENABLE_BI_ENDIAN */
  45. return GRUB_ERR_NONE;
  46. }
  47. /* Calculate the amount of memory spanned by the segments. */
  48. grub_size_t
  49. grub_elfXX_size (grub_elf_t elf,
  50. ElfXX_Addr *base, grub_uintXX_t *max_align)
  51. {
  52. ElfXX_Addr segments_start = (ElfXX_Addr) -1;
  53. ElfXX_Addr segments_end = 0;
  54. int nr_phdrs = 0;
  55. grub_uint32_t curr_align = 1;
  56. ElfXX_Phdr *phdr;
  57. /* Run through the program headers to calculate the total memory size we
  58. * should claim. */
  59. FOR_ELFXX_PHDRS (elf, phdr)
  60. {
  61. /* Only consider loadable segments. */
  62. if (phdr->p_type != PT_LOAD)
  63. continue;
  64. nr_phdrs++;
  65. if (phdr->p_paddr < segments_start)
  66. segments_start = phdr->p_paddr;
  67. if (phdr->p_paddr + phdr->p_memsz > segments_end)
  68. segments_end = phdr->p_paddr + phdr->p_memsz;
  69. if (curr_align < phdr->p_align)
  70. curr_align = phdr->p_align;
  71. }
  72. if (base)
  73. *base = 0;
  74. if (nr_phdrs == 0)
  75. {
  76. grub_error (GRUB_ERR_BAD_OS, "no program headers present");
  77. return 0;
  78. }
  79. if (segments_end < segments_start)
  80. {
  81. /* Very bad addresses. */
  82. grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
  83. return 0;
  84. }
  85. if (base)
  86. *base = segments_start;
  87. if (max_align)
  88. *max_align = curr_align;
  89. return segments_end - segments_start;
  90. }
  91. grub_err_t
  92. grub_elfXX_load (grub_elf_t elf, const char *filename,
  93. void *load_offset, enum grub_elf_load_flags load_flags,
  94. grub_addr_t *base, grub_size_t *size)
  95. {
  96. grub_addr_t load_base = (grub_addr_t) -1ULL;
  97. grub_size_t load_size = 0;
  98. ElfXX_Phdr *phdr;
  99. FOR_ELFXX_PHDRS(elf, phdr)
  100. {
  101. grub_addr_t load_addr;
  102. if (phdr->p_type != PT_LOAD && !((load_flags & GRUB_ELF_LOAD_FLAGS_LOAD_PT_DYNAMIC) && phdr->p_type == PT_DYNAMIC))
  103. continue;
  104. load_addr = (grub_addr_t) phdr->p_paddr;
  105. switch (load_flags & GRUB_ELF_LOAD_FLAGS_BITS)
  106. {
  107. case GRUB_ELF_LOAD_FLAGS_ALL_BITS:
  108. break;
  109. case GRUB_ELF_LOAD_FLAGS_28BITS:
  110. load_addr &= 0xFFFFFFF;
  111. break;
  112. case GRUB_ELF_LOAD_FLAGS_30BITS:
  113. load_addr &= 0x3FFFFFFF;
  114. break;
  115. case GRUB_ELF_LOAD_FLAGS_62BITS:
  116. load_addr &= 0x3FFFFFFFFFFFFFFFULL;
  117. break;
  118. }
  119. load_addr += (grub_addr_t) load_offset;
  120. if (load_addr < load_base)
  121. load_base = load_addr;
  122. grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
  123. (unsigned long long) load_addr,
  124. (unsigned long long) phdr->p_memsz);
  125. if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
  126. return grub_errno;
  127. if (phdr->p_filesz)
  128. {
  129. grub_ssize_t read;
  130. read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
  131. if (read != (grub_ssize_t) phdr->p_filesz)
  132. {
  133. if (!grub_errno)
  134. grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
  135. filename);
  136. return grub_errno;
  137. }
  138. }
  139. if (phdr->p_filesz < phdr->p_memsz)
  140. grub_memset ((void *) (grub_addr_t) (load_addr + phdr->p_filesz),
  141. 0, phdr->p_memsz - phdr->p_filesz);
  142. load_size += phdr->p_memsz;
  143. }
  144. if (base)
  145. *base = load_base;
  146. if (size)
  147. *size = load_size;
  148. return grub_errno;
  149. }
  150. static int
  151. grub_elfXX_check_endianess_and_bswap_ehdr (grub_elf_t elf)
  152. {
  153. ElfXX_Ehdr *e = &(elf->ehdr.ehdrXX);
  154. if (e->e_ident[EI_DATA] == GRUB_ELF_NATIVE_ENDIANNESS)
  155. {
  156. return 1;
  157. }
  158. #if GRUB_ELF_ENABLE_BI_ENDIAN
  159. if (e->e_ident[EI_DATA] == GRUB_ELF_OPPOSITE_ENDIANNESS)
  160. {
  161. e->e_type = grub_swap_bytes_halfXX (e->e_type);
  162. e->e_machine = grub_swap_bytes_halfXX (e->e_machine);
  163. e->e_version = grub_swap_bytes_wordXX (e->e_version);
  164. e->e_entry = grub_swap_bytes_addrXX (e->e_entry);
  165. e->e_phoff = grub_swap_bytes_offXX (e->e_phoff);
  166. e->e_shoff = grub_swap_bytes_offXX (e->e_shoff);
  167. e->e_flags = grub_swap_bytes_wordXX (e->e_flags);
  168. e->e_ehsize = grub_swap_bytes_halfXX (e->e_ehsize);
  169. e->e_phentsize = grub_swap_bytes_halfXX (e->e_phentsize);
  170. e->e_phnum = grub_swap_bytes_halfXX (e->e_phnum);
  171. e->e_shentsize = grub_swap_bytes_halfXX (e->e_shentsize);
  172. e->e_shnum = grub_swap_bytes_halfXX (e->e_shnum);
  173. e->e_shstrndx = grub_swap_bytes_halfXX (e->e_shstrndx);
  174. return 1;
  175. }
  176. #endif /* GRUB_ELF_ENABLE_BI_ENDIAN */
  177. return 0;
  178. }
  179. grub_err_t
  180. grub_elfXX_get_shnum (ElfXX_Ehdr *e, ElfXX_Shnum *shnum)
  181. {
  182. ElfXX_Shdr *s;
  183. if (shnum == NULL)
  184. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for shnum"));
  185. /* Set *shnum to 0 so that shnum doesn't return junk on error */
  186. *shnum = 0;
  187. if (e == NULL)
  188. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header"));
  189. *shnum = e->e_shnum;
  190. if (*shnum == SHN_UNDEF)
  191. {
  192. if (e->e_shoff == 0)
  193. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff"));
  194. s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff);
  195. *shnum = s->sh_size;
  196. if (*shnum < SHN_LORESERVE)
  197. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in sh_size: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum);
  198. }
  199. else
  200. {
  201. if (*shnum >= SHN_LORESERVE)
  202. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of section header table entries in e_shnum: %" PRIuGRUB_UINT64_T), (grub_uint64_t) *shnum);
  203. }
  204. return GRUB_ERR_NONE;
  205. }
  206. grub_err_t
  207. grub_elfXX_get_shstrndx (ElfXX_Ehdr *e, ElfXX_Word *shstrndx)
  208. {
  209. ElfXX_Shdr *s;
  210. if (shstrndx == NULL)
  211. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for shstrndx"));
  212. /* Set *shstrndx to 0 so that shstrndx doesn't return junk on error */
  213. *shstrndx = 0;
  214. if (e == NULL)
  215. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header"));
  216. *shstrndx = e->e_shstrndx;
  217. if (*shstrndx == SHN_XINDEX)
  218. {
  219. if (e->e_shoff == 0)
  220. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff"));
  221. s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff);
  222. *shstrndx = s->sh_link;
  223. if (*shstrndx < SHN_LORESERVE)
  224. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table index in sh_link: %d"), *shstrndx);
  225. }
  226. else
  227. {
  228. if (*shstrndx >= SHN_LORESERVE)
  229. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table index in e_shstrndx: %d"), *shstrndx);
  230. }
  231. return GRUB_ERR_NONE;
  232. }
  233. grub_err_t
  234. grub_elfXX_get_phnum (ElfXX_Ehdr *e, ElfXX_Word *phnum)
  235. {
  236. ElfXX_Shdr *s;
  237. if (phnum == NULL)
  238. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for phnum"));
  239. /* Set *phnum to 0 so that phnum doesn't return junk on error */
  240. *phnum = 0;
  241. if (e == NULL)
  242. return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("NULL pointer passed for elf header"));
  243. *phnum = e->e_phnum;
  244. if (*phnum == PN_XNUM)
  245. {
  246. if (e->e_shoff == 0)
  247. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid section header table offset in e_shoff"));
  248. s = (ElfXX_Shdr *) ((grub_uint8_t *) e + e->e_shoff);
  249. *phnum = s->sh_info;
  250. if (*phnum < PN_XNUM)
  251. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of program header table entries in sh_info: %d"), *phnum);
  252. }
  253. else
  254. {
  255. if (*phnum >= PN_XNUM)
  256. return grub_error (GRUB_ERR_BAD_NUMBER, N_("invalid number of program header table entries in e_phnum: %d"), *phnum);
  257. }
  258. return GRUB_ERR_NONE;
  259. }