]> git.proxmox.com Git - grub2.git/blame - grub-core/loader/machoXX.c
* grub-core/loader/machoXX.c: Remove nested functions.
[grub2.git] / grub-core / loader / machoXX.c
CommitLineData
ab6e34cc 1
2#include <grub/file.h>
3#include <grub/mm.h>
4#include <grub/misc.h>
9c4b5c13 5#include <grub/i18n.h>
ab6e34cc 6
7#define min(a,b) (((a) < (b)) ? (a) : (b))
8
99ce1597 9static int
ab6e34cc 10SUFFIX (grub_macho_contains_macho) (grub_macho_t macho)
11{
12 return macho->offsetXX != -1;
13}
14
15void
9c4b5c13 16SUFFIX (grub_macho_parse) (grub_macho_t macho, const char *filename)
ab6e34cc 17{
99ce1597
VS
18 union {
19 struct grub_macho_lzss_header lzss;
20 grub_macho_header_t macho;
21 } head;
ab6e34cc 22
23 /* Is there any candidate at all? */
24 if (macho->offsetXX == -1)
25 return;
26
99ce1597 27 /* Read header and check magic. */
ab6e34cc 28 if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1
29 || grub_file_read (macho->file, &head, sizeof (head))
99ce1597 30 != sizeof (head))
ab6e34cc 31 {
9c4b5c13
VS
32 if (!grub_errno)
33 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
34 filename);
ab6e34cc 35 macho->offsetXX = -1;
36 return;
37 }
99ce1597
VS
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)
ab6e34cc 57 {
7fd0baee 58 grub_error (GRUB_ERR_BAD_OS, "invalid Mach-O " XX "-bit header");
ab6e34cc 59 macho->offsetXX = -1;
60 return;
61 }
62
63 /* Read commands. */
99ce1597
VS
64 macho->ncmdsXX = head.macho.ncmds;
65 macho->cmdsizeXX = head.macho.sizeofcmds;
66 macho->cmdsXX = grub_malloc (macho->cmdsizeXX);
ab6e34cc 67 if (! macho->cmdsXX)
9c4b5c13 68 return;
99ce1597
VS
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,
ab6e34cc 72 (grub_size_t) macho->cmdsizeXX)
73 != (grub_ssize_t) macho->cmdsizeXX)
74 {
9c4b5c13
VS
75 if (!grub_errno)
76 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
77 filename);
ab6e34cc 78 macho->offsetXX = -1;
79 }
80}
81
43c91882 82typedef int (*grub_macho_iter_hook_t)
ab6e34cc 83(grub_macho_t , struct grub_macho_cmd *,
84 void *);
85
86static grub_err_t
87grub_macho_cmds_iterate (grub_macho_t macho,
88 grub_macho_iter_hook_t hook,
99ce1597
VS
89 void *hook_arg,
90 const char *filename)
ab6e34cc 91{
99ce1597 92 grub_uint8_t *hdrs;
ab6e34cc 93 int i;
99ce1597
VS
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
ab6e34cc 156 if (! macho->cmdsXX)
7fd0baee 157 return grub_error (GRUB_ERR_BAD_OS, "couldn't find " XX "-bit Mach-O");
99ce1597 158 hdrs = macho->cmdsXX;
ab6e34cc 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
170grub_size_t
171SUFFIX (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
178grub_err_t
9c4b5c13
VS
179SUFFIX (grub_macho_readfile) (grub_macho_t macho,
180 const char *filename,
181 void *dest)
ab6e34cc 182{
183 grub_ssize_t read;
184 if (! SUFFIX (grub_macho_contains_macho) (macho))
185 return grub_error (GRUB_ERR_BAD_OS,
7fd0baee 186 "couldn't read architecture-specific part");
ab6e34cc 187
e4eff4ed 188 if (grub_file_seek (macho->file, macho->offsetXX) == (grub_off_t) -1)
9c4b5c13 189 return grub_errno;
ab6e34cc 190
191 read = grub_file_read (macho->file, dest,
e4eff4ed
VS
192 macho->endXX - macho->offsetXX);
193 if (read != (grub_ssize_t) (macho->endXX - macho->offsetXX))
ab6e34cc 194 {
9c4b5c13
VS
195 if (!grub_errno)
196 grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
197 filename);
198 return grub_errno;
ab6e34cc 199 }
200 return GRUB_ERR_NONE;
201}
202
43c91882
VS
203struct 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. */
213static int
214calcsize (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
ab6e34cc 237/* Calculate the amount of memory spanned by the segments. */
238grub_err_t
239SUFFIX (grub_macho_size) (grub_macho_t macho, grub_macho_addr_t *segments_start,
99ce1597
VS
240 grub_macho_addr_t *segments_end, int flags,
241 const char *filename)
ab6e34cc 242{
43c91882
VS
243 struct calcsize_ctx ctx = {
244 .flags = flags,
245 .nr_phdrs = 0,
246 .segments_start = segments_start,
247 .segments_end = segments_end,
248 };
ab6e34cc 249
250 *segments_start = (grub_macho_addr_t) -1;
251 *segments_end = 0;
252
43c91882 253 grub_macho_cmds_iterate (macho, calcsize, &ctx, filename);
ab6e34cc 254
43c91882 255 if (ctx.nr_phdrs == 0)
7fd0baee 256 return grub_error (GRUB_ERR_BAD_OS, "no program headers present");
ab6e34cc 257
258 if (*segments_end < *segments_start)
259 /* Very bad addresses. */
7fd0baee 260 return grub_error (GRUB_ERR_BAD_OS, "bad program header load addresses");
ab6e34cc 261
262 return GRUB_ERR_NONE;
263}
264
43c91882
VS
265struct do_load_ctx
266{
267 int flags;
268 char *offset;
269 const char *filename;
270 int *darwin_version;
271};
272
273static int
274do_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
ab6e34cc 347/* Load every loadable segment into memory specified by `_load_hook'. */
348grub_err_t
9c4b5c13 349SUFFIX (grub_macho_load) (grub_macho_t macho, const char *filename,
83ddae23 350 char *offset, int flags, int *darwin_version)
ab6e34cc 351{
43c91882
VS
352 struct do_load_ctx ctx = {
353 .flags = flags,
354 .offset = offset,
355 .filename = filename,
356 .darwin_version = darwin_version
357 };
ab6e34cc 358
83ddae23
VS
359 if (darwin_version)
360 *darwin_version = 0;
361
43c91882 362 grub_macho_cmds_iterate (macho, do_load, &ctx, filename);
ab6e34cc 363
9c4b5c13 364 return grub_errno;
ab6e34cc 365}
366
43c91882
VS
367static int
368find_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
ab6e34cc 378grub_macho_addr_t
99ce1597 379SUFFIX (grub_macho_get_entry_point) (grub_macho_t macho, const char *filename)
ab6e34cc 380{
381 grub_macho_addr_t entry_point = 0;
43c91882 382 grub_macho_cmds_iterate (macho, find_entry_point, &entry_point, filename);
ab6e34cc 383 return entry_point;
384}