e49d0b6ff17e02e22e09b306d0a6a32a3b083b78
[grub.git] / grub-core / efiemu / i386 / loadcore64.c
1 /* i386 CPU-specific part of loadcore.c for 32-bit mode */
2 /*
3  *  GRUB  --  GRand Unified Bootloader
4  *  Copyright (C) 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
20 #include <grub/err.h>
21 #include <grub/mm.h>
22 #include <grub/misc.h>
23 #include <grub/efiemu/efiemu.h>
24 #include <grub/cpu/efiemu.h>
25 #include <grub/elf.h>
26 #include <grub/i18n.h>
27
28 /* Check if EHDR is a valid ELF header.  */
29 int
30 grub_arch_efiemu_check_header64 (void *ehdr)
31 {
32   Elf64_Ehdr *e = ehdr;
33
34   return (e->e_ident[EI_CLASS] == ELFCLASS64
35           && e->e_ident[EI_DATA] == ELFDATA2LSB
36           && e->e_machine == EM_X86_64);
37 }
38
39 /* Relocate symbols.  */
40 grub_err_t
41 grub_arch_efiemu_relocate_symbols64 (grub_efiemu_segment_t segs,
42                                      struct grub_efiemu_elf_sym *elfsyms,
43                                      void *ehdr)
44 {
45   unsigned i;
46   Elf64_Ehdr *e = ehdr;
47   Elf64_Shdr *s;
48   grub_err_t err;
49
50   for (i = 0, s = (Elf64_Shdr *) ((char *) e + e->e_shoff);
51        i < e->e_shnum;
52        i++, s = (Elf64_Shdr *) ((char *) s + e->e_shentsize))
53     if (s->sh_type == SHT_RELA)
54       {
55         grub_efiemu_segment_t seg;
56         grub_dprintf ("efiemu", "shtrel\n");
57
58         /* Find the target segment.  */
59         for (seg = segs; seg; seg = seg->next)
60           if (seg->section == s->sh_info)
61             break;
62
63         if (seg)
64           {
65             Elf64_Rela *rel, *max;
66
67             for (rel = (Elf64_Rela *) ((char *) e + s->sh_offset),
68                    max = rel + (unsigned long) s->sh_size
69                    / (unsigned long)s->sh_entsize;
70                  rel < max;
71                  rel++)
72               {
73                 void *addr;
74                 grub_uint32_t *addr32;
75                 grub_uint64_t *addr64;
76                 struct grub_efiemu_elf_sym sym;
77                 if (seg->size < rel->r_offset)
78                   return grub_error (GRUB_ERR_BAD_MODULE,
79                                      "reloc offset is out of the segment");
80
81                 addr =
82                   ((char *) grub_efiemu_mm_obtain_request (seg->handle)
83                    + seg->off + rel->r_offset);
84                 addr32 = (grub_uint32_t *) addr;
85                 addr64 = (grub_uint64_t *) addr;
86                 sym = elfsyms[ELF64_R_SYM (rel->r_info)];
87
88                 switch (ELF64_R_TYPE (rel->r_info))
89                   {
90                   case R_X86_64_64:
91                     err = grub_efiemu_write_value (addr,
92                                                    *addr64 + rel->r_addend
93                                                    + sym.off, sym.handle,
94                                                    0, seg->ptv_rel_needed,
95                                                    sizeof (grub_uint64_t));
96                     if (err)
97                       return err;
98                     break;
99
100                   case R_X86_64_PC32:
101                     err = grub_efiemu_write_value (addr,
102                                                    *addr32 + rel->r_addend
103                                                    + sym.off
104                                                    - rel->r_offset - seg->off,
105                                                    sym.handle, seg->handle,
106                                                    seg->ptv_rel_needed,
107                                                    sizeof (grub_uint32_t));
108                     if (err)
109                       return err;
110                     break;
111
112                   case R_X86_64_32:
113                   case R_X86_64_32S:
114                     err = grub_efiemu_write_value (addr,
115                                                    *addr32 + rel->r_addend
116                                                    + sym.off, sym.handle,
117                                                    0, seg->ptv_rel_needed,
118                                                    sizeof (grub_uint32_t));
119                     if (err)
120                       return err;
121                     break;
122                   default:
123                     return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
124                                        N_("relocation 0x%x is not implemented yet"),
125                                        ELF_R_TYPE (rel->r_info));
126                   }
127               }
128           }
129       }
130
131   return GRUB_ERR_NONE;
132 }