]> git.proxmox.com Git - grub2.git/blob - grub-core/loader/machoXX.c
Import grub2_2.02+dfsg1.orig.tar.xz
[grub2.git] / grub-core / loader / machoXX.c
1
2 #include <grub/file.h>
3 #include <grub/mm.h>
4 #include <grub/misc.h>
5 #include <grub/i18n.h>
6
7 #define min(a,b) (((a) < (b)) ? (a) : (b))
8
9 static int
10 SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
11 {
12 return macho->offsetXX != -1;
13 }
14
15 void
16 SUFFIX (grub_macho_parse) (grub_macho_t macho, const char *filename)
17 {
18 union {
19 struct grub_macho_lzss_header lzss;
20 grub_macho_header_t macho;
21 } head;
22
23 /* Is there any candidate at all? */
24 if (macho->offsetXX == -1)
25 return;
26
27 /* Read header and check magic. */
28 if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
29 || grub_file_read (macho->file, &head, sizeof (head))
30 != sizeof (head))
31 {
32 if (!grub_errno)
33 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
34 filename);
35 macho->offsetXX = -1;
36 return;
37 }
38 if (grub_memcmp (head.lzss.magic, GRUB_MACHO_LZSS_MAGIC,
39 sizeof (head.lzss.magic)) == 0)
40 {
41 macho->compressed_sizeXX = grub_be_to_cpu32 (head.lzss.compressed_size);
42 macho->uncompressed_sizeXX
43 = grub_be_to_cpu32 (head.lzss.uncompressed_size);
44 if (macho->uncompressed_sizeXX < sizeof (head.macho))
45 {
46 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
47 filename);
48 macho->offsetXX = -1;
49 return;
50 }
51 /* Skip header check. */
52 macho->compressedXX = 1;
53 return;
54 }
55
56 if (head.macho.magic != GRUB_MACHO_MAGIC)
57 {
58 grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O header");
59 macho->offsetXX = -1;
60 return;
61 }
62
63 /* Read commands. */
64 macho->ncmdsXX = head.macho.ncmds;
65 macho->cmdsizeXX = head.macho.sizeofcmds;
66 macho->cmdsXX = grub_malloc (macho->cmdsizeXX);
67 if (! macho->cmdsXX)
68 return;
69 if (grub_file_seek (macho->file, macho->offsetXX
70 + sizeof (grub_macho_header_t)) == (grub_off_t) -1
71 || grub_file_read (macho->file, macho->cmdsXX,
72 (grub_size_t) macho->cmdsizeXX)
73 != (grub_ssize_t) macho->cmdsizeXX)
74 {
75 if (!grub_errno)
76 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
77 filename);
78 macho->offsetXX = -1;
79 }
80 }
81
82 typedef int (*grub_macho_iter_hook_t)
83 (grub_macho_t , struct grub_macho_cmd *,
84 void *);
85
86 static grub_err_t
87 grub_macho_cmds_iterate (grub_macho_t macho,
88 grub_macho_iter_hook_t hook,
89 void *hook_arg,
90 const char *filename)
91 {
92 grub_uint8_t *hdrs;
93 int i;
94
95 if (macho->compressedXX && !macho->uncompressedXX)
96 {
97 grub_uint8_t *tmp;
98 grub_macho_header_t *head;
99 macho->uncompressedXX = grub_malloc (macho->uncompressed_sizeXX);
100 if (!macho->uncompressedXX)
101 return grub_errno;
102 tmp = grub_malloc (macho->compressed_sizeXX);
103 if (!tmp)
104 {
105 grub_free (macho->uncompressedXX);
106 macho->uncompressedXX = 0;
107 return grub_errno;
108 }
109 if (grub_file_seek (macho->file, macho->offsetXX
110 + GRUB_MACHO_LZSS_OFFSET) == (grub_off_t) -1
111 || grub_file_read (macho->file, tmp,
112 (grub_size_t) macho->compressed_sizeXX)
113 != (grub_ssize_t) macho->compressed_sizeXX)
114 {
115 if (!grub_errno)
116 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
117 filename);
118 grub_free (tmp);
119 grub_free (macho->uncompressedXX);
120 macho->uncompressedXX = 0;
121 macho->offsetXX = -1;
122 return grub_errno;
123 }
124 if (grub_decompress_lzss (macho->uncompressedXX,
125 macho->uncompressedXX
126 + macho->uncompressed_sizeXX,
127 tmp, tmp + macho->compressed_sizeXX)
128 != macho->uncompressed_sizeXX)
129 {
130 if (!grub_errno)
131 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
132 filename);
133 grub_free (tmp);
134 grub_free (macho->uncompressedXX);
135 macho->uncompressedXX = 0;
136 macho->offsetXX = -1;
137 return grub_errno;
138 }
139 grub_free (tmp);
140 head = (grub_macho_header_t *) macho->uncompressedXX;
141 macho->ncmdsXX = head->ncmds;
142 macho->cmdsizeXX = head->sizeofcmds;
143 macho->cmdsXX = macho->uncompressedXX + sizeof (grub_macho_header_t);
144 if (sizeof (grub_macho_header_t) + macho->cmdsizeXX
145 >= macho->uncompressed_sizeXX)
146 {
147 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
148 filename);
149 grub_free (macho->uncompressedXX);
150 macho->uncompressedXX = 0;
151 macho->offsetXX = -1;
152 return grub_errno;
153 }
154 }
155
156 if (! macho->cmdsXX)
157 return grub_error (GRUB_ERR_BAD_OS, "couldn't find Mach-O commands");
158 hdrs = macho->cmdsXX;
159 for (i = 0; i < macho->ncmdsXX; i++)
160 {
161 struct grub_macho_cmd *hdr = (struct grub_macho_cmd *) hdrs;
162 if (hook (macho, hdr, hook_arg))
163 break;
164 hdrs += hdr->cmdsize;
165 }
166
167 return grub_errno;
168 }
169
170 grub_size_t
171 SUFFIX (grub_macho_filesize) (grub_macho_t macho)
172 {
173 if (SUFFIX (grub_macho_contains_macho) (macho))
174 return macho->endXX - macho->offsetXX;
175 return 0;
176 }
177
178 grub_err_t
179 SUFFIX (grub_macho_readfile) (grub_macho_t macho,
180 const char *filename,
181 void *dest)
182 {
183 grub_ssize_t read;
184 if (! SUFFIX (grub_macho_contains_macho) (macho))
185 return grub_error (GRUB_ERR_BAD_OS,
186 "couldn't read architecture-specific part");
187
188 if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
189 return grub_errno;
190
191 read = grub_file_read (macho->file, dest,
192 macho->endXX - macho->offsetXX);
193 if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
194 {
195 if (!grub_errno)
196 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
197 filename);
198 return grub_errno;
199 }
200 return GRUB_ERR_NONE;
201 }
202
203 struct calcsize_ctx
204 {
205 int flags;
206 int nr_phdrs;
207 grub_macho_addr_t *segments_start;
208 grub_macho_addr_t *segments_end;
209 };
210
211 /* Run through the program headers to calculate the total memory size we
212 should claim. */
213 static int
214 calcsize (grub_macho_t _macho __attribute__ ((unused)),
215 struct grub_macho_cmd *hdr0,
216 void *_arg)
217 {
218 grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
219 struct calcsize_ctx *ctx = _arg;
220 if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
221 return 0;
222
223 if (! hdr->vmsize)
224 return 0;
225
226 if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
227 return 0;
228
229 ctx->nr_phdrs++;
230 if (hdr->vmaddr < *ctx->segments_start)
231 *ctx->segments_start = hdr->vmaddr;
232 if (hdr->vmaddr + hdr->vmsize > *ctx->segments_end)
233 *ctx->segments_end = hdr->vmaddr + hdr->vmsize;
234 return 0;
235 }
236
237 /* Calculate the amount of memory spanned by the segments. */
238 grub_err_t
239 SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
240 grub_macho_addr_t *segments_end, int flags,
241 const char *filename)
242 {
243 struct calcsize_ctx ctx = {
244 .flags = flags,
245 .nr_phdrs = 0,
246 .segments_start = segments_start,
247 .segments_end = segments_end,
248 };
249
250 *segments_start = (grub_macho_addr_t) -1;
251 *segments_end = 0;
252
253 grub_macho_cmds_iterate (macho, calcsize, &ctx, filename);
254
255 if (ctx.nr_phdrs == 0)
256 return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
257
258 if (*segments_end < *segments_start)
259 /* Very bad addresses. */
260 return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
261
262 return GRUB_ERR_NONE;
263 }
264
265 struct do_load_ctx
266 {
267 int flags;
268 char *offset;
269 const char *filename;
270 int *darwin_version;
271 };
272
273 static int
274 do_load(grub_macho_t _macho,
275 struct grub_macho_cmd *hdr0,
276 void *_arg)
277 {
278 grub_macho_segment_t *hdr = (grub_macho_segment_t *) hdr0;
279 struct do_load_ctx *ctx = _arg;
280
281 if (hdr->cmd != GRUB_MACHO_CMD_SEGMENT)
282 return 0;
283
284 if (! hdr->filesize && (ctx->flags & GRUB_MACHO_NOBSS))
285 return 0;
286 if (! hdr->vmsize)
287 return 0;
288
289 if (hdr->filesize)
290 {
291 grub_ssize_t read, toread = min (hdr->filesize, hdr->vmsize);
292 if (_macho->uncompressedXX)
293 {
294 if (hdr->fileoff + (grub_size_t) toread
295 > _macho->uncompressed_sizeXX)
296 read = -1;
297 else
298 {
299 read = toread;
300 grub_memcpy (ctx->offset + hdr->vmaddr,
301 _macho->uncompressedXX + hdr->fileoff, read);
302 }
303 }
304 else
305 {
306 if (grub_file_seek (_macho->file, hdr->fileoff
307 + _macho->offsetXX) == (grub_off_t) -1)
308 return 1;
309 read = grub_file_read (_macho->file, ctx->offset + hdr->vmaddr,
310 toread);
311 }
312
313 if (read != toread)
314 {
315 /* XXX How can we free memory from `load_hook'? */
316 if (!grub_errno)
317 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
318 ctx->filename);
319
320 return 1;
321 }
322 if (ctx->darwin_version)
323 {
324 const char *ptr = ctx->offset + hdr->vmaddr;
325 const char *end = ptr + min (hdr->filesize, hdr->vmsize)
326 - (sizeof ("Darwin Kernel Version ") - 1);
327 for (; ptr < end; ptr++)
328 if (grub_memcmp (ptr, "Darwin Kernel Version ",
329 sizeof ("Darwin Kernel Version ") - 1) == 0)
330 {
331 ptr += sizeof ("Darwin Kernel Version ") - 1;
332 *ctx->darwin_version = 0;
333 end += (sizeof ("Darwin Kernel Version ") - 1);
334 while (ptr < end && grub_isdigit (*ptr))
335 *ctx->darwin_version = (*ptr++ - '0') + *ctx->darwin_version * 10;
336 break;
337 }
338 }
339 }
340
341 if (hdr->filesize < hdr->vmsize)
342 grub_memset (ctx->offset + hdr->vmaddr + hdr->filesize,
343 0, hdr->vmsize - hdr->filesize);
344 return 0;
345 }
346
347 /* Load every loadable segment into memory specified by `_load_hook'. */
348 grub_err_t
349 SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename,
350 char *offset, int flags, int *darwin_version)
351 {
352 struct do_load_ctx ctx = {
353 .flags = flags,
354 .offset = offset,
355 .filename = filename,
356 .darwin_version = darwin_version
357 };
358
359 if (darwin_version)
360 *darwin_version = 0;
361
362 grub_macho_cmds_iterate (macho, do_load, &ctx, filename);
363
364 return grub_errno;
365 }
366
367 static int
368 find_entry_point (grub_macho_t _macho __attribute__ ((unused)),
369 struct grub_macho_cmd *hdr,
370 void *_arg)
371 {
372 grub_macho_addr_t *entry_point = _arg;
373 if (hdr->cmd == GRUB_MACHO_CMD_THREAD)
374 *entry_point = ((grub_macho_thread_t *) hdr)->entry_point;
375 return 0;
376 }
377
378 grub_macho_addr_t
379 SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho, const char *filename)
380 {
381 grub_macho_addr_t entry_point = 0;
382 grub_macho_cmds_iterate (macho, find_entry_point, &entry_point, filename);
383 return entry_point;
384 }