]>
Commit | Line | Data |
---|---|---|
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 | 9 | static int |
ab6e34cc | 10 | SUFFIX (grub_macho_contains_macho) (grub_macho_t macho) |
11 | { | |
12 | return macho->offsetXX != -1; | |
13 | } | |
14 | ||
15 | void | |
9c4b5c13 | 16 | SUFFIX (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 | 82 | typedef int (*grub_macho_iter_hook_t) |
ab6e34cc | 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, | |
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 | ||
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 | |
9c4b5c13 VS |
179 | SUFFIX (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 |
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 | ||
ab6e34cc | 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, | |
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 |
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 | ||
ab6e34cc | 347 | /* Load every loadable segment into memory specified by `_load_hook'. */ |
348 | grub_err_t | |
9c4b5c13 | 349 | SUFFIX (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 |
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 | ||
ab6e34cc | 378 | grub_macho_addr_t |
99ce1597 | 379 | SUFFIX (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 | } |