bump version
[AROS.git] / rom / dos / internalloadseg_aos.c
blobd950fa6aee3388afa3639b509f733450770d9497
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #include <exec/execbase.h>
10 #include <exec/memory.h>
11 #include <dos/dosasl.h>
12 #include <dos/doshunks.h>
13 #include <dos/dosextens.h>
14 #include <proto/exec.h>
15 #include <proto/dos.h>
16 #include <proto/arossupport.h>
17 #include <aros/asmcall.h>
18 #include <aros/debug.h>
19 #include <aros/macros.h>
21 #include "dos_intern.h"
22 #include "internalloadseg.h"
23 #include "include/loadseg.h"
25 #include <proto/dos.h>
27 #define GETHUNKPTR(x) ((UBYTE*)(BADDR(hunktab[x]) + sizeof(BPTR)))
29 #define LOADSEG_HUNK_BUFFER 2048
31 struct SRBuffer
33 APTR buffer;
34 ULONG offset;
35 ULONG avail;
38 static int read_block_buffered(BPTR file, APTR buffer, ULONG size, SIPTR * funcarray, struct SRBuffer * srb, struct DosLibrary * DOSBase)
40 if (!srb) {
41 return ilsRead(file, buffer, size) == size ? 0 : 1;
43 if (!srb->buffer) {
44 srb->buffer = ilsAllocMem(LOADSEG_HUNK_BUFFER, 0);
45 if (!srb->buffer)
46 return 1;
47 srb->avail = 0;
49 while (size) {
50 ULONG tocopy;
51 if (srb->avail == 0) {
52 srb->offset = 0;
53 srb->avail = ilsRead(file, srb->buffer, LOADSEG_HUNK_BUFFER);
55 if (srb->avail == 0)
56 return 1;
57 tocopy = srb->avail > size ? size : srb->avail;
58 CopyMem(srb->buffer + srb->offset, buffer, tocopy);
59 size -= tocopy;
60 buffer += tocopy;
61 srb->avail -= tocopy;
62 srb->offset += tocopy;
64 return 0;
67 /* Seek forward by count ULONGs.
68 * Returns 0 on success, 1 on failure.
70 static int seek_forward(BPTR fd, ULONG count, SIPTR *funcarray, struct SRBuffer *srb, struct DosLibrary *DOSBase)
72 int err = 0;
73 ULONG tmp;
75 /* For AOS compatibility, we can't use DOS/Seek() here,
76 * as AOS callers to InternalLoadSeg will not pass
77 * in a Seek element of the funcarray, and the read
78 * callback of funcarray may be for reading in-memory
79 * instead of pointing to DOS/Read.
81 * Luckily, reading HUNKs is linear, so we can just
82 * read ahead.
84 while (count && !(err = read_block_buffered(fd, &tmp, sizeof(tmp), funcarray, srb, DOSBase)))
85 count--;
87 return err;
90 static BOOL allowed_hunk(BPTR seglist);
92 BPTR InternalLoadSeg_AOS(BPTR fh,
93 BPTR table,
94 SIPTR * funcarray,
95 LONG * stacksize,
96 struct DosLibrary * DOSBase)
98 #define ERROR(a) { *error=a; goto end; }
101 BPTR *hunktab = BADDR(table);
102 BPTR firsthunk = BNULL, prevhunk = BNULL;
103 ULONG hunktype, count, first, last, curhunk, lasthunk, numhunks;
104 LONG t;
105 UBYTE name_buf[255];
106 register int i;
107 BPTR last_p = 0;
108 UBYTE *overlaytable = NULL;
109 ULONG tmp, req;
110 SIPTR dummy;
111 struct SRBuffer srbbuf, *srb;
112 #if DEBUG
113 static STRPTR segtypes[] = { "CODE", "DATA", "BSS", };
114 #endif
116 SIPTR *error = &dummy;
118 if (DOSBase) {
119 struct Process *me = (struct Process *)FindTask(NULL);
120 ASSERT_VALID_PROCESS(me);
122 error =&me->pr_Result2;
124 srb = &srbbuf;
125 srb->buffer = NULL;
127 curhunk = lasthunk = 0; /* keep GCC quiet */
128 /* start point is HUNK_HEADER + 4 */
129 while (1)
131 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
132 goto end;
133 if (count == 0L)
134 break;
135 count = AROS_BE2LONG(count);
136 count *= 4;
137 if (read_block_buffered(fh, name_buf, count, funcarray, srb, DOSBase))
138 goto end;
139 D(bug("\tlibname: \"%.*s\"\n", count, name_buf));
141 if (read_block_buffered(fh, &numhunks, sizeof(numhunks), funcarray, srb, DOSBase))
142 goto end;
144 numhunks = AROS_BE2LONG(numhunks);
146 D(bug("\tHunk count: %ld\n", numhunks));
148 if (!hunktab) {
149 hunktab = ilsAllocVec(sizeof(BPTR) * (numhunks + 1 + 1), MEMF_CLEAR);
150 if (hunktab == NULL)
151 ERROR(ERROR_NO_FREE_STORE);
154 if (read_block_buffered(fh, &first, sizeof(first), funcarray, srb, DOSBase))
155 goto end;
157 first = AROS_BE2LONG(first);
159 D(bug("\tFirst hunk: %ld\n", first));
160 curhunk = first;
161 if (read_block_buffered(fh, &last, sizeof(last), funcarray, srb, DOSBase))
162 goto end;
164 last = AROS_BE2LONG(last);
166 D(bug("\tLast hunk: %ld\n", last));
168 for (i = first; i <= numhunks; i++) {
169 UBYTE *hunkptr;
170 ULONG hunksize;
172 if (i > last) {
173 hunktab[i] = BNULL;
174 continue;
177 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
178 goto end;
180 count = AROS_BE2LONG(count);
181 tmp = count & (HUNKF_FAST | HUNKF_CHIP);
182 count &= 0xFFFFFF;
183 D(bug("\tHunk %d size: 0x%06lx bytes in ", i, count*4));
184 req = MEMF_CLEAR | MEMF_PUBLIC;
185 if (tmp == (HUNKF_FAST | HUNKF_CHIP)) {
186 if (read_block_buffered(fh, &req, sizeof(req), funcarray, srb, DOSBase))
187 goto end;
188 req = AROS_BE2LONG(req);
189 D(bug("FLAGS=%08x", req));
190 } else if (tmp == HUNKF_FAST) {
191 D(bug("FAST"));
192 req |= MEMF_FAST;
193 } else if (tmp == HUNKF_CHIP) {
194 D(bug("CHIP"));
195 req |= MEMF_CHIP;
197 D(bug(" memory"));
200 * We need space for the code, the length of this hunk and
201 * for a pointer to the next hunk.
202 * Note also MEMF_31BIT flag appended to memory requirements.
203 * This is important on 64-bit machines where AmigaDOS hunk
204 * files need to be loaded into low memory (<2GB). This is needed
205 * for correct interpretation of pointers in these files.
206 * Yes, they can't be executed in such environments, but they still can
207 * be read as data files. This allows to use Amiga bitmap fonts on AROS.
209 hunksize = count * 4 + sizeof(ULONG) + sizeof(BPTR);
210 hunkptr = ilsAllocVec(hunksize, req | MEMF_31BIT);
211 if (!hunkptr)
212 ERROR(ERROR_NO_FREE_STORE);
213 hunktab[i] = MKBADDR(hunkptr);
214 D(bug(" @%p\n", hunkptr));
215 if (!firsthunk)
216 firsthunk = hunktab[i];
217 /* Link hunks
218 if this is not the first hunk that is loaded, then connect
219 it to the previous one (pointer to the field where the
220 pointer to the next hunk is located)
222 if (prevhunk)
223 ((BPTR *)(BADDR(prevhunk)))[0] = hunktab[i];
224 prevhunk = hunktab[i];
227 while(!read_block_buffered(fh, &hunktype, sizeof(hunktype), funcarray, srb, DOSBase))
229 hunktype = AROS_BE2LONG(hunktype);
230 D(bug("Hunk Type: %d\n", hunktype & 0xFFFFFF));
232 switch(hunktype & 0xFFFFFF)
234 case HUNK_SYMBOL:
235 /* The SYMBOL_HUNK looks like this:
236 ---------------------
237 | n = size of | This
238 | symbol in longs | may
239 |-------------------| be
240 | n longwords = name| repeated
241 | of symbol | any
242 |-------------------| number
243 | value (1 long) | of times
244 --------------------|
245 | 0 = end of HUNK_ |
246 | SYMBOL |
247 -------------------- */
249 D(bug("HUNK_SYMBOL (skipping)\n"));
250 while(!read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase) && count)
252 count = AROS_BE2LONG(count) ;
254 if (seek_forward(fh, count+1, funcarray, srb, DOSBase))
255 goto end;
257 break;
259 case HUNK_UNIT:
261 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
262 goto end;
264 count = AROS_BE2LONG(count) ;
266 count *= 4;
267 if (read_block_buffered(fh, name_buf, count, funcarray, srb, DOSBase))
268 goto end;
269 D(bug("HUNK_UNIT: \"%.*s\"\n", count, name_buf));
270 break;
272 case HUNK_CODE:
273 case HUNK_DATA:
274 case HUNK_BSS:
276 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
277 goto end;
279 count = AROS_BE2LONG(count);
281 D(bug("HUNK_%s(%d): Length: 0x%06lx bytes in ",
282 segtypes[(hunktype & 0xFFFFFF)-HUNK_CODE], curhunk, count*4));
284 if ((hunktype & 0xFFFFFF) != HUNK_BSS && count)
286 if (read_block_buffered(fh, GETHUNKPTR(curhunk), count*4, funcarray, srb, DOSBase))
287 goto end;
291 lasthunk = curhunk;
292 ++curhunk;
293 break;
295 case HUNK_RELOC32:
296 case HUNK_RELRELOC32:
297 D(bug("HUNK_RELOC32:\n"));
298 while (1)
300 ULONG *addr;
301 ULONG offset;
303 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
304 goto end;
305 if (count == 0L)
306 break;
308 count = AROS_BE2LONG(count);
310 i = count;
311 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
312 goto end;
314 count = AROS_BE2LONG(count);
316 D(bug("\tHunk #%ld:\n", count));
317 while (i > 0)
319 if (read_block_buffered(fh, &offset, sizeof(offset), funcarray, srb, DOSBase))
320 goto end;
322 offset = AROS_BE2LONG(offset);
324 //D(bug("\t\t0x%06lx\n", offset));
325 addr = (ULONG *)(GETHUNKPTR(lasthunk) + offset);
327 /* See the above MEMF_31 explanation for why this
328 * works on AROS 64-bit.
330 if ((hunktype & 0xFFFFFF) == HUNK_RELRELOC32)
331 *addr = (ULONG)(AROS_BE2LONG(*addr) + (IPTR)(GETHUNKPTR(count) - GETHUNKPTR(curhunk)));
332 else
333 *addr = (ULONG)(AROS_BE2LONG(*addr) + (IPTR)GETHUNKPTR(count));
335 --i;
338 break;
340 case HUNK_DREL32: /* For compatibility with V37 */
341 case HUNK_RELOC32SHORT:
343 ULONG Wordcount = 0;
344 ULONG offset;
346 while (1)
348 ULONG *addr;
349 UWORD word;
351 Wordcount++;
353 if (read_block_buffered(fh, &word, sizeof(word), funcarray, srb, DOSBase))
354 goto end;
355 if (word == 0L)
356 break;
358 word = AROS_BE2LONG(word);
360 i = word;
361 Wordcount++;
362 if (read_block_buffered(fh, &word, sizeof(word), funcarray, srb, DOSBase))
363 goto end;
365 word = AROS_BE2WORD(word);
367 count = word;
368 D(bug("\tHunk #%ld @%p: %ld relocations\n", count, GETHUNKPTR(lasthunk), i));
369 while (i > 0)
371 Wordcount++;
372 /* read a 16bit number (2 bytes) */
373 if (read_block_buffered(fh, &word, sizeof(word), funcarray, srb, DOSBase))
374 goto end;
376 /* offset now contains the byte offset in it`s 16 highest bits.
377 These 16 highest bits have to become the 16 lowest bits so
378 we get the word we need. */
379 offset = AROS_BE2WORD(word);
381 D(bug("\t\t0x%06lx += 0x%lx\n", offset, GETHUNKPTR(count)));
382 addr = (ULONG *)(GETHUNKPTR(lasthunk) + offset);
384 /* See the above MEMF_31 explanation for why this
385 * works on AROS 64-bit.
387 *addr = (ULONG)(AROS_BE2LONG(*addr) + (IPTR)GETHUNKPTR(count));
389 --i;
390 } /* while (i > 0)*/
391 } /* while (1) */
393 /* if the amount of words read was odd, then skip the following
394 16-bit word */
395 if (0x1 == (Wordcount & 0x1)) {
396 UWORD word;
397 read_block_buffered(fh, &word, sizeof(word), funcarray, srb, DOSBase);
400 break;
402 case HUNK_END:
404 D(bug("HUNK_END\n"));
405 /* DOSBase == NULL: Called from RDB filesystem loader which does not
406 * know filesystem's original size. Exit if last HUNK_END. This can't
407 * be done normally because it would break overlayed executables.
409 if (!DOSBase && curhunk > last)
410 goto done;
412 break;
414 case HUNK_RELOC16:
415 bug("HUNK_RELOC16 not implemented\n");
416 ERROR(ERROR_BAD_HUNK);
418 case HUNK_RELOC8:
419 bug("HUNK_RELOC8 not implemented\n");
420 ERROR(ERROR_BAD_HUNK);
422 case HUNK_NAME:
423 bug("HUNK_NAME not implemented\n");
424 ERROR(ERROR_BAD_HUNK);
426 case HUNK_EXT:
427 bug("HUNK_EXT not implemented\n");
428 ERROR(ERROR_BAD_HUNK);
430 case HUNK_DEBUG:
431 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
432 goto end;
434 count = AROS_BE2LONG(count);
436 D(bug("HUNK_DEBUG (%x Bytes)\n",count));
437 if (seek_forward(fh, count, funcarray, srb, DOSBase))
438 goto end;
439 break;
441 case HUNK_OVERLAY:
443 D(bug("HUNK_OVERLAY:\n"));
444 if (table) /* overlay inside overlay? */
445 ERROR(ERROR_BAD_HUNK);
446 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
447 goto end;
448 count = AROS_BE2LONG(count);
449 D(bug("Overlay table size: %d\n", count));
451 /* See above for MEMF_31BIT explanation */
452 count = count * 4 + sizeof(ULONG) + sizeof(ULONG);
453 overlaytable = ilsAllocVec(count, MEMF_CLEAR | MEMF_31BIT);
454 if (overlaytable == NULL)
455 ERROR(ERROR_NO_FREE_STORE);
456 if (read_block_buffered(fh, overlaytable, count - sizeof(ULONG), funcarray, srb, DOSBase))
457 goto end;
458 goto done;
461 case HUNK_BREAK:
462 D(bug("HUNK_BREAK\n"));
463 if (!table)
464 ERROR(ERROR_BAD_HUNK);
465 goto done;
467 default:
468 if (hunktype & HUNKF_ADVISORY) {
469 D(bug("Unknown hunk 0x%06lx with advisory flag skipped\n", hunktype & 0xFFFFFF));
470 if (read_block_buffered(fh, &count, sizeof(count), funcarray, srb, DOSBase))
471 goto end;
472 count = AROS_BE2LONG(count);
473 if (seek_forward(fh, count * 4, funcarray, srb, DOSBase))
474 goto end;
475 } else {
476 bug("Hunk type 0x%06lx not implemented\n", hunktype & 0xFFFFFF);
477 ERROR(ERROR_BAD_HUNK);
479 } /* switch */
480 } /* while */
481 done:
482 if (firsthunk && !allowed_hunk(firsthunk))
484 ERROR(ERROR_NOT_EXECUTABLE);
485 goto end;
488 if (hunktab)
490 ULONG hunksize;
492 #ifdef __mc68000
494 * On non-m68k systems, hunk files are not executable.
495 * And even if AROS ever gets m68k emulator, they are still data files.
496 * So we flush caches only on m68k.
498 if (SysBase->LibNode.lib_Version >= 36)
500 /* Clear caches */
501 for (t = first; t < numhunks && t <= last; t++)
503 hunksize = *((ULONG*)BADDR(hunktab[t]) - 1);
504 if (hunksize)
505 CacheClearE(BADDR(hunktab[t]), hunksize, CACRF_ClearI | CACRF_ClearD);
508 #endif
510 if (table)
511 return firsthunk;
513 hunksize = *((ULONG*)BADDR(hunktab[0]) - 1);
514 if (last > first && hunksize >= 32 / 4)
516 /* NOTE: HUNK_OVERLAY is not required for overlay mode. */
517 ULONG *h = (ULONG*)(BADDR(hunktab[first]));
519 if (h[2] == 0x0000abcd)
521 #if __WORDSIZE != 32
522 D(bug("overlay not supported!\n"));
523 ERROR(ERROR_BAD_HUNK);
524 #else
525 /* overlay executable */
526 h[3] = (ULONG)fh;
527 h[4] = (ULONG)overlaytable;
528 h[5] = (ULONG)MKBADDR(hunktab);
529 D(bug("overlay loaded!\n"));
530 return (BPTR)(-(LONG)MKBADDR(h));
531 #endif
535 if (overlaytable) {
536 ilsFreeVec(overlaytable);
537 ERROR(ERROR_BAD_HUNK);
540 last_p = firsthunk;
542 register_hunk(fh, firsthunk, NULL, DOSBase);
544 ilsFreeVec(hunktab);
545 hunktab = NULL;
548 end:
549 if (hunktab != NULL)
551 for (t = 0 /* first */; t < numhunks /* last */; t++)
552 ilsFreeVec(BADDR(hunktab[t]));
553 ilsFreeVec(hunktab);
555 ilsFreeMem(srb->buffer, LOADSEG_HUNK_BUFFER);
556 return last_p;
557 } /* InternalLoadSeg */
559 #ifdef __mc68000
560 static AROS_UFH4(LONG, ReadFunc,
561 AROS_UFHA(BPTR, file, D1),
562 AROS_UFHA(APTR, buffer, D2),
563 AROS_UFHA(LONG, length, D3),
564 AROS_UFHA(struct DosLibrary *, DOSBase, A6)
567 AROS_USERFUNC_INIT
569 return FRead(file, buffer, 1, length);
571 AROS_USERFUNC_EXIT
574 static AROS_UFH3(APTR, AllocFunc,
575 AROS_UFHA(ULONG, length, D0),
576 AROS_UFHA(ULONG, flags, D1),
577 AROS_UFHA(struct ExecBase *, SysBase, A6)
580 AROS_USERFUNC_INIT
582 return AllocMem(length, flags);
584 AROS_USERFUNC_EXIT
587 static AROS_UFH3(void, FreeFunc,
588 AROS_UFHA(APTR, buffer, A1),
589 AROS_UFHA(ULONG, length, D0),
590 AROS_UFHA(struct ExecBase *, SysBase, A6)
593 AROS_USERFUNC_INIT
595 FreeMem(buffer, length);
597 AROS_USERFUNC_EXIT
600 AROS_UFH4(BPTR, LoadSeg_Overlay,
601 AROS_UFHA(UBYTE*, name, D1),
602 AROS_UFHA(BPTR, hunktable, D2),
603 AROS_UFHA(BPTR, fh, D3),
604 AROS_UFHA(struct DosLibrary *, DosBase, A6))
606 AROS_USERFUNC_INIT
608 void (*FunctionArray[3])();
609 ULONG hunktype;
611 FunctionArray[0] = (APTR)ReadFunc;
612 FunctionArray[1] = (APTR)AllocFunc;
613 FunctionArray[2] = (APTR)FreeFunc;
615 D(bug("LoadSeg_Overlay. table=%x fh=%x\n", hunktable, fh));
616 if (read_block_buffered(fh, &hunktype, sizeof(hunktype), (SIPTR*)FunctionArray, NULL, DosBase))
617 return BNULL;
618 hunktype = AROS_BE2LONG(hunktype);
619 if (hunktype != HUNK_HEADER)
620 return BNULL;
621 return InternalLoadSeg_AOS(fh, hunktable, (SIPTR*)FunctionArray, NULL, DosBase);
623 AROS_USERFUNC_EXIT
626 #endif
628 static BOOL allowed_hunk(BPTR seglist)
630 #ifdef __mc68000
631 return TRUE;
632 #else
633 /* deadwood: This is a not-so-great solution to the problem of crashes/reboots
634 * when users accidentally try running m68k hunk executables.
635 * I think the better solution would be to load the hunk (or non-native elf)
636 * but stop it from executing or redirect to emulator by mechanism similar
637 * to OS4 GetSegListInfo() and struct PseudoSegList. This way also runtime
638 * generated seglists would be handled properly.
640 UBYTE * ptr = (UBYTE *)BADDR(seglist) + sizeof(BPTR);
642 /* Allow bitmap fonts */
643 if (ptr[18] == 0x0f && ptr[19] == 0x80)
644 return TRUE;
646 return FALSE;
647 #endif