elf.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. /* elf.c - load ELF files */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2003,2004,2005,2006,2007,2008,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/err.h>
  20. #include <grub/elf.h>
  21. #include <grub/elfload.h>
  22. #include <grub/file.h>
  23. #include <grub/gzio.h>
  24. #include <grub/misc.h>
  25. #include <grub/mm.h>
  26. GRUB_EXPORT(grub_elf32_load);
  27. GRUB_EXPORT(grub_elf64_load);
  28. GRUB_EXPORT(grub_elf_open);
  29. GRUB_EXPORT(grub_elf_close);
  30. GRUB_EXPORT(grub_elf_file);
  31. GRUB_EXPORT(grub_elf_is_elf32);
  32. GRUB_EXPORT(grub_elf_is_elf64);
  33. GRUB_EXPORT(grub_elf32_size);
  34. GRUB_EXPORT(grub_elf64_size);
  35. /* Check if EHDR is a valid ELF header. */
  36. static grub_err_t
  37. grub_elf_check_header (grub_elf_t elf)
  38. {
  39. Elf32_Ehdr *e = &elf->ehdr.ehdr32;
  40. if (e->e_ident[EI_MAG0] != ELFMAG0
  41. || e->e_ident[EI_MAG1] != ELFMAG1
  42. || e->e_ident[EI_MAG2] != ELFMAG2
  43. || e->e_ident[EI_MAG3] != ELFMAG3
  44. || e->e_ident[EI_VERSION] != EV_CURRENT
  45. || e->e_version != EV_CURRENT)
  46. return grub_error (GRUB_ERR_BAD_OS, "invalid arch independent ELF magic");
  47. return GRUB_ERR_NONE;
  48. }
  49. grub_err_t
  50. grub_elf_close (grub_elf_t elf)
  51. {
  52. grub_file_t file = elf->file;
  53. grub_free (elf->phdrs);
  54. grub_free (elf);
  55. if (file)
  56. grub_file_close (file);
  57. return grub_errno;
  58. }
  59. grub_elf_t
  60. grub_elf_file (grub_file_t file)
  61. {
  62. grub_elf_t elf;
  63. elf = grub_zalloc (sizeof (*elf));
  64. if (! elf)
  65. return 0;
  66. elf->file = file;
  67. if (grub_file_seek (elf->file, 0) == (grub_off_t) -1)
  68. goto fail;
  69. if (grub_file_read (elf->file, &elf->ehdr, sizeof (elf->ehdr))
  70. != sizeof (elf->ehdr))
  71. {
  72. grub_error_push ();
  73. grub_error (GRUB_ERR_READ_ERROR, "cannot read ELF header");
  74. goto fail;
  75. }
  76. if (grub_elf_check_header (elf))
  77. goto fail;
  78. return elf;
  79. fail:
  80. grub_free (elf->phdrs);
  81. grub_free (elf);
  82. return 0;
  83. }
  84. grub_elf_t
  85. grub_elf_open (const char *name)
  86. {
  87. grub_file_t file;
  88. grub_elf_t elf;
  89. file = grub_gzfile_open (name, 1);
  90. if (! file)
  91. return 0;
  92. elf = grub_elf_file (file);
  93. if (! elf)
  94. grub_file_close (file);
  95. return elf;
  96. }
  97. /* 32-bit */
  98. int
  99. grub_elf_is_elf32 (grub_elf_t elf)
  100. {
  101. return elf->ehdr.ehdr32.e_ident[EI_CLASS] == ELFCLASS32;
  102. }
  103. static grub_err_t
  104. grub_elf32_load_phdrs (grub_elf_t elf)
  105. {
  106. grub_ssize_t phdrs_size;
  107. phdrs_size = elf->ehdr.ehdr32.e_phnum * elf->ehdr.ehdr32.e_phentsize;
  108. grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
  109. (unsigned long long) elf->ehdr.ehdr32.e_phoff,
  110. (unsigned long) phdrs_size);
  111. elf->phdrs = grub_malloc (phdrs_size);
  112. if (! elf->phdrs)
  113. return grub_errno;
  114. if ((grub_file_seek (elf->file, elf->ehdr.ehdr32.e_phoff) == (grub_off_t) -1)
  115. || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
  116. {
  117. grub_error_push ();
  118. return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
  119. }
  120. return GRUB_ERR_NONE;
  121. }
  122. static grub_err_t
  123. grub_elf32_phdr_iterate (grub_elf_t elf,
  124. int (*hook) (grub_elf_t, Elf32_Phdr *, void *closure),
  125. void *closure)
  126. {
  127. Elf32_Phdr *phdrs;
  128. unsigned int i;
  129. if (! elf->phdrs)
  130. if (grub_elf32_load_phdrs (elf))
  131. return grub_errno;
  132. phdrs = elf->phdrs;
  133. for (i = 0; i < elf->ehdr.ehdr32.e_phnum; i++)
  134. {
  135. Elf32_Phdr *phdr = phdrs + i;
  136. grub_dprintf ("elf",
  137. "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
  138. "filesz %lx\n",
  139. i, phdr->p_type,
  140. (unsigned long) phdr->p_paddr,
  141. (unsigned long) phdr->p_memsz,
  142. (unsigned long) phdr->p_filesz);
  143. if (hook (elf, phdr, closure))
  144. break;
  145. }
  146. return grub_errno;
  147. }
  148. struct grub_elf32_size_closure
  149. {
  150. Elf32_Addr segments_start;
  151. Elf32_Addr segments_end;
  152. int nr_phdrs;
  153. };
  154. /* Run through the program headers to calculate the total memory size we
  155. * should claim. */
  156. static int
  157. grub_elf32_calcsize (grub_elf_t _elf __attribute__ ((unused)),
  158. Elf32_Phdr *phdr, void *closure)
  159. {
  160. struct grub_elf32_size_closure *c = closure;
  161. /* Only consider loadable segments. */
  162. if (phdr->p_type != PT_LOAD)
  163. return 0;
  164. c->nr_phdrs++;
  165. if (phdr->p_paddr < c->segments_start)
  166. c->segments_start = phdr->p_paddr;
  167. if (phdr->p_paddr + phdr->p_memsz > c->segments_end)
  168. c->segments_end = phdr->p_paddr + phdr->p_memsz;
  169. return 0;
  170. }
  171. /* Calculate the amount of memory spanned by the segments. */
  172. grub_size_t
  173. grub_elf32_size (grub_elf_t elf, Elf32_Addr *base)
  174. {
  175. struct grub_elf32_size_closure c;
  176. c.segments_start = (Elf32_Addr) -1;
  177. c.segments_end = 0;
  178. c.nr_phdrs = 0;
  179. grub_elf32_phdr_iterate (elf, grub_elf32_calcsize, &c);
  180. if (base)
  181. *base = 0;
  182. if (c.nr_phdrs == 0)
  183. {
  184. grub_error (GRUB_ERR_BAD_OS, "no program headers present");
  185. return 0;
  186. }
  187. if (c.segments_end < c.segments_start)
  188. {
  189. /* Very bad addresses. */
  190. grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
  191. return 0;
  192. }
  193. if (base)
  194. *base = c.segments_start;
  195. return c.segments_end - c.segments_start;
  196. }
  197. struct grub_elf32_load_closure
  198. {
  199. grub_addr_t load_base;
  200. grub_size_t load_size;
  201. grub_elf32_load_hook_t hook;
  202. void *closure;
  203. };
  204. static int
  205. grub_elf32_load_segment (grub_elf_t elf, Elf32_Phdr *phdr, void *closure)
  206. {
  207. struct grub_elf32_load_closure *c = closure;
  208. grub_addr_t load_addr;
  209. int do_load = 1;
  210. load_addr = phdr->p_paddr;
  211. if (c->hook && c->hook (phdr, &load_addr, &do_load, c->closure))
  212. return 1;
  213. if (! do_load)
  214. return 0;
  215. if (load_addr < c->load_base)
  216. c->load_base = load_addr;
  217. grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
  218. (unsigned long long) load_addr,
  219. (unsigned long long) phdr->p_memsz);
  220. if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
  221. {
  222. grub_error_push ();
  223. return grub_error (GRUB_ERR_BAD_OS,
  224. "invalid offset in program header.");
  225. }
  226. if (phdr->p_filesz)
  227. {
  228. grub_ssize_t read;
  229. read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
  230. if (read != (grub_ssize_t) phdr->p_filesz)
  231. {
  232. /* XXX How can we free memory from `load_hook'? */
  233. grub_error_push ();
  234. return grub_error (GRUB_ERR_BAD_OS,
  235. "couldn't read segment from file: "
  236. "wanted 0x%lx bytes; read 0x%lx bytes.",
  237. phdr->p_filesz, read);
  238. }
  239. }
  240. if (phdr->p_filesz < phdr->p_memsz)
  241. grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
  242. 0, phdr->p_memsz - phdr->p_filesz);
  243. c->load_size += phdr->p_memsz;
  244. return 0;
  245. }
  246. /* Load every loadable segment into memory specified by `_load_hook'. */
  247. grub_err_t
  248. grub_elf32_load (grub_elf_t _elf, grub_elf32_load_hook_t hook, void *closure,
  249. grub_addr_t *base, grub_size_t *size)
  250. {
  251. grub_err_t err;
  252. struct grub_elf32_load_closure c;
  253. c.hook = hook;
  254. c.closure = closure;
  255. c.load_base = (grub_addr_t) -1ULL;
  256. c.load_size = 0;
  257. err = grub_elf32_phdr_iterate (_elf, grub_elf32_load_segment, &c);
  258. if (base)
  259. *base = c.load_base;
  260. if (size)
  261. *size = c.load_size;
  262. return err;
  263. }
  264. /* 64-bit */
  265. int
  266. grub_elf_is_elf64 (grub_elf_t elf)
  267. {
  268. return elf->ehdr.ehdr64.e_ident[EI_CLASS] == ELFCLASS64;
  269. }
  270. static grub_err_t
  271. grub_elf64_load_phdrs (grub_elf_t elf)
  272. {
  273. grub_ssize_t phdrs_size;
  274. phdrs_size = elf->ehdr.ehdr64.e_phnum * elf->ehdr.ehdr64.e_phentsize;
  275. grub_dprintf ("elf", "Loading program headers at 0x%llx, size 0x%lx.\n",
  276. (unsigned long long) elf->ehdr.ehdr64.e_phoff,
  277. (unsigned long) phdrs_size);
  278. elf->phdrs = grub_malloc (phdrs_size);
  279. if (! elf->phdrs)
  280. return grub_errno;
  281. if ((grub_file_seek (elf->file, elf->ehdr.ehdr64.e_phoff) == (grub_off_t) -1)
  282. || (grub_file_read (elf->file, elf->phdrs, phdrs_size) != phdrs_size))
  283. {
  284. grub_error_push ();
  285. return grub_error (GRUB_ERR_READ_ERROR, "cannot read program headers");
  286. }
  287. return GRUB_ERR_NONE;
  288. }
  289. static grub_err_t
  290. grub_elf64_phdr_iterate (grub_elf_t elf,
  291. int (*hook) (grub_elf_t, Elf64_Phdr *, void *closure),
  292. void *closure)
  293. {
  294. Elf64_Phdr *phdrs;
  295. unsigned int i;
  296. if (! elf->phdrs)
  297. if (grub_elf64_load_phdrs (elf))
  298. return grub_errno;
  299. phdrs = elf->phdrs;
  300. for (i = 0; i < elf->ehdr.ehdr64.e_phnum; i++)
  301. {
  302. Elf64_Phdr *phdr = phdrs + i;
  303. grub_dprintf ("elf",
  304. "Segment %u: type 0x%x paddr 0x%lx memsz 0x%lx "
  305. "filesz %lx\n",
  306. i, phdr->p_type,
  307. (unsigned long) phdr->p_paddr,
  308. (unsigned long) phdr->p_memsz,
  309. (unsigned long) phdr->p_filesz);
  310. if (hook (elf, phdr, closure))
  311. break;
  312. }
  313. return grub_errno;
  314. }
  315. struct grub_elf64_size_closure
  316. {
  317. Elf64_Addr segments_start;
  318. Elf64_Addr segments_end;
  319. int nr_phdrs;
  320. };
  321. /* Run through the program headers to calculate the total memory size we
  322. * should claim. */
  323. static int
  324. grub_elf64_calcsize (grub_elf_t _elf __attribute__ ((unused)),
  325. Elf64_Phdr *phdr, void *closure)
  326. {
  327. struct grub_elf64_size_closure *c = closure;
  328. /* Only consider loadable segments. */
  329. if (phdr->p_type != PT_LOAD)
  330. return 0;
  331. c->nr_phdrs++;
  332. if (phdr->p_paddr < c->segments_start)
  333. c->segments_start = phdr->p_paddr;
  334. if (phdr->p_paddr + phdr->p_memsz > c->segments_end)
  335. c->segments_end = phdr->p_paddr + phdr->p_memsz;
  336. return 0;
  337. }
  338. /* Calculate the amount of memory spanned by the segments. */
  339. grub_size_t
  340. grub_elf64_size (grub_elf_t elf, Elf64_Addr *base)
  341. {
  342. struct grub_elf64_size_closure c;
  343. c.segments_start = (Elf64_Addr) -1;
  344. c.segments_end = 0;
  345. c.nr_phdrs = 0;
  346. grub_elf64_phdr_iterate (elf, grub_elf64_calcsize, &c);
  347. if (base)
  348. *base = 0;
  349. if (c.nr_phdrs == 0)
  350. {
  351. grub_error (GRUB_ERR_BAD_OS, "no program headers present");
  352. return 0;
  353. }
  354. if (c.segments_end < c.segments_start)
  355. {
  356. /* Very bad addresses. */
  357. grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
  358. return 0;
  359. }
  360. if (base)
  361. *base = c.segments_start;
  362. return c.segments_end - c.segments_start;
  363. }
  364. struct grub_elf64_load_closure
  365. {
  366. grub_addr_t load_base;
  367. grub_size_t load_size;
  368. grub_elf64_load_hook_t hook;
  369. void *closure;
  370. };
  371. static int
  372. grub_elf64_load_segment (grub_elf_t elf, Elf64_Phdr *phdr, void *closure)
  373. {
  374. struct grub_elf64_load_closure *c = closure;
  375. grub_addr_t load_addr;
  376. int do_load = 1;
  377. load_addr = phdr->p_paddr;
  378. if (c->hook && c->hook (phdr, &load_addr, &do_load, c->closure))
  379. return 1;
  380. if (! do_load)
  381. return 0;
  382. if (load_addr < c->load_base)
  383. c->load_base = load_addr;
  384. grub_dprintf ("elf", "Loading segment at 0x%llx, size 0x%llx\n",
  385. (unsigned long long) load_addr,
  386. (unsigned long long) phdr->p_memsz);
  387. if (grub_file_seek (elf->file, phdr->p_offset) == (grub_off_t) -1)
  388. {
  389. grub_error_push ();
  390. return grub_error (GRUB_ERR_BAD_OS,
  391. "invalid offset in program header.");
  392. }
  393. if (phdr->p_filesz)
  394. {
  395. grub_ssize_t read;
  396. read = grub_file_read (elf->file, (void *) load_addr, phdr->p_filesz);
  397. if (read != (grub_ssize_t) phdr->p_filesz)
  398. {
  399. /* XXX How can we free memory from `load_hook'? */
  400. grub_error_push ();
  401. return grub_error (GRUB_ERR_BAD_OS,
  402. "couldn't read segment from file: "
  403. "wanted 0x%lx bytes; read 0x%lx bytes.",
  404. phdr->p_filesz, read);
  405. }
  406. }
  407. if (phdr->p_filesz < phdr->p_memsz)
  408. grub_memset ((void *) (long) (load_addr + phdr->p_filesz),
  409. 0, phdr->p_memsz - phdr->p_filesz);
  410. c->load_size += phdr->p_memsz;
  411. return 0;
  412. }
  413. /* Load every loadable segment into memory specified by `_load_hook'. */
  414. grub_err_t
  415. grub_elf64_load (grub_elf_t _elf, grub_elf64_load_hook_t hook, void *closure,
  416. grub_addr_t *base, grub_size_t *size)
  417. {
  418. grub_err_t err;
  419. struct grub_elf64_load_closure c;
  420. c.hook = hook;
  421. c.closure = closure;
  422. c.load_base = (grub_addr_t) -1ULL;
  423. c.load_size = 0;
  424. err = grub_elf64_phdr_iterate (_elf, grub_elf64_load_segment, &c);
  425. if (base)
  426. *base = c.load_base;
  427. if (size)
  428. *size = c.load_size;
  429. return err;
  430. }