1 | /****************************************************************************/ |
2 | /* |
3 | * QEMU bFLT binary loader. Based on linux/fs/binfmt_flat.c |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License as published by |
7 | * the Free Software Foundation; either version 2 of the License, or |
8 | * (at your option) any later version. |
9 | * |
10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU General Public License |
16 | * along with this program; if not, see <http://www.gnu.org/licenses/>. |
17 | * |
18 | * Copyright (C) 2006 CodeSourcery. |
19 | * Copyright (C) 2000-2003 David McCullough <davidm@snapgear.com> |
20 | * Copyright (C) 2002 Greg Ungerer <gerg@snapgear.com> |
21 | * Copyright (C) 2002 SnapGear, by Paul Dale <pauli@snapgear.com> |
22 | * Copyright (C) 2000, 2001 Lineo, by David McCullough <davidm@lineo.com> |
23 | * based heavily on: |
24 | * |
25 | * linux/fs/binfmt_aout.c: |
26 | * Copyright (C) 1991, 1992, 1996 Linus Torvalds |
27 | * linux/fs/binfmt_flat.c for 2.0 kernel |
28 | * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> |
29 | * JAN/99 -- coded full program relocation (gerg@snapgear.com) |
30 | */ |
31 | |
32 | /* ??? ZFLAT and shared library support is currently disabled. */ |
33 | |
34 | /****************************************************************************/ |
35 | |
36 | #include "qemu/osdep.h" |
37 | |
38 | #include "qemu.h" |
39 | #include "flat.h" |
40 | #include <target_flat.h> |
41 | |
42 | //#define DEBUG |
43 | |
44 | #ifdef DEBUG |
45 | #define DBG_FLT(...) printf(__VA_ARGS__) |
46 | #else |
47 | #define DBG_FLT(...) |
48 | #endif |
49 | |
50 | #define RELOC_FAILED 0xff00ff01 /* Relocation incorrect somewhere */ |
51 | #define UNLOADED_LIB 0x7ff000ff /* Placeholder for unused library */ |
52 | |
53 | struct lib_info { |
54 | abi_ulong start_code; /* Start of text segment */ |
55 | abi_ulong start_data; /* Start of data segment */ |
56 | abi_ulong end_data; /* Start of bss section */ |
57 | abi_ulong start_brk; /* End of data segment */ |
58 | abi_ulong text_len; /* Length of text segment */ |
59 | abi_ulong entry; /* Start address for this module */ |
60 | abi_ulong build_date; /* When this one was compiled */ |
61 | short loaded; /* Has this library been loaded? */ |
62 | }; |
63 | |
64 | #ifdef CONFIG_BINFMT_SHARED_FLAT |
65 | static int load_flat_shared_library(int id, struct lib_info *p); |
66 | #endif |
67 | |
68 | struct linux_binprm; |
69 | |
70 | /****************************************************************************/ |
71 | /* |
72 | * create_flat_tables() parses the env- and arg-strings in new user |
73 | * memory and creates the pointer tables from them, and puts their |
74 | * addresses on the "stack", returning the new stack pointer value. |
75 | */ |
76 | |
77 | /* Push a block of strings onto the guest stack. */ |
78 | static abi_ulong copy_strings(abi_ulong p, int n, char **s) |
79 | { |
80 | int len; |
81 | |
82 | while (n-- > 0) { |
83 | len = strlen(s[n]) + 1; |
84 | p -= len; |
85 | memcpy_to_target(p, s[n], len); |
86 | } |
87 | |
88 | return p; |
89 | } |
90 | |
91 | static int target_pread(int fd, abi_ulong ptr, abi_ulong len, |
92 | abi_ulong offset) |
93 | { |
94 | void *buf; |
95 | int ret; |
96 | |
97 | buf = lock_user(VERIFY_WRITE, ptr, len, 0); |
98 | if (!buf) { |
99 | return -EFAULT; |
100 | } |
101 | ret = pread(fd, buf, len, offset); |
102 | if (ret < 0) { |
103 | ret = -errno; |
104 | } |
105 | unlock_user(buf, ptr, len); |
106 | return ret; |
107 | } |
108 | /****************************************************************************/ |
109 | |
110 | #ifdef CONFIG_BINFMT_ZFLAT |
111 | |
112 | #include <linux/zlib.h> |
113 | |
114 | #define LBUFSIZE 4000 |
115 | |
116 | /* gzip flag byte */ |
117 | #define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */ |
118 | #define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ |
119 | #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ |
120 | #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ |
121 | #define COMMENT 0x10 /* bit 4 set: file comment present */ |
122 | #define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ |
123 | #define RESERVED 0xC0 /* bit 6,7: reserved */ |
124 | |
125 | static int decompress_exec( |
126 | struct linux_binprm *bprm, |
127 | unsigned long offset, |
128 | char *dst, |
129 | long len, |
130 | int fd) |
131 | { |
132 | unsigned char *buf; |
133 | z_stream strm; |
134 | loff_t fpos; |
135 | int ret, retval; |
136 | |
137 | DBG_FLT("decompress_exec(offset=%x,buf=%x,len=%x)\n" ,(int)offset, (int)dst, (int)len); |
138 | |
139 | memset(&strm, 0, sizeof(strm)); |
140 | strm.workspace = kmalloc(zlib_inflate_workspacesize(), GFP_KERNEL); |
141 | if (strm.workspace == NULL) { |
142 | DBG_FLT("binfmt_flat: no memory for decompress workspace\n" ); |
143 | return -ENOMEM; |
144 | } |
145 | buf = kmalloc(LBUFSIZE, GFP_KERNEL); |
146 | if (buf == NULL) { |
147 | DBG_FLT("binfmt_flat: no memory for read buffer\n" ); |
148 | retval = -ENOMEM; |
149 | goto out_free; |
150 | } |
151 | |
152 | /* Read in first chunk of data and parse gzip header. */ |
153 | fpos = offset; |
154 | ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); |
155 | |
156 | strm.next_in = buf; |
157 | strm.avail_in = ret; |
158 | strm.total_in = 0; |
159 | |
160 | retval = -ENOEXEC; |
161 | |
162 | /* Check minimum size -- gzip header */ |
163 | if (ret < 10) { |
164 | DBG_FLT("binfmt_flat: file too small?\n" ); |
165 | goto out_free_buf; |
166 | } |
167 | |
168 | /* Check gzip magic number */ |
169 | if ((buf[0] != 037) || ((buf[1] != 0213) && (buf[1] != 0236))) { |
170 | DBG_FLT("binfmt_flat: unknown compression magic?\n" ); |
171 | goto out_free_buf; |
172 | } |
173 | |
174 | /* Check gzip method */ |
175 | if (buf[2] != 8) { |
176 | DBG_FLT("binfmt_flat: unknown compression method?\n" ); |
177 | goto out_free_buf; |
178 | } |
179 | /* Check gzip flags */ |
180 | if ((buf[3] & ENCRYPTED) || (buf[3] & CONTINUATION) || |
181 | (buf[3] & RESERVED)) { |
182 | DBG_FLT("binfmt_flat: unknown flags?\n" ); |
183 | goto out_free_buf; |
184 | } |
185 | |
186 | ret = 10; |
187 | if (buf[3] & EXTRA_FIELD) { |
188 | ret += 2 + buf[10] + (buf[11] << 8); |
189 | if (unlikely(LBUFSIZE == ret)) { |
190 | DBG_FLT("binfmt_flat: buffer overflow (EXTRA)?\n" ); |
191 | goto out_free_buf; |
192 | } |
193 | } |
194 | if (buf[3] & ORIG_NAME) { |
195 | for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) |
196 | ; |
197 | if (unlikely(LBUFSIZE == ret)) { |
198 | DBG_FLT("binfmt_flat: buffer overflow (ORIG_NAME)?\n" ); |
199 | goto out_free_buf; |
200 | } |
201 | } |
202 | if (buf[3] & COMMENT) { |
203 | for (; ret < LBUFSIZE && (buf[ret] != 0); ret++) |
204 | ; |
205 | if (unlikely(LBUFSIZE == ret)) { |
206 | DBG_FLT("binfmt_flat: buffer overflow (COMMENT)?\n" ); |
207 | goto out_free_buf; |
208 | } |
209 | } |
210 | |
211 | strm.next_in += ret; |
212 | strm.avail_in -= ret; |
213 | |
214 | strm.next_out = dst; |
215 | strm.avail_out = len; |
216 | strm.total_out = 0; |
217 | |
218 | if (zlib_inflateInit2(&strm, -MAX_WBITS) != Z_OK) { |
219 | DBG_FLT("binfmt_flat: zlib init failed?\n" ); |
220 | goto out_free_buf; |
221 | } |
222 | |
223 | while ((ret = zlib_inflate(&strm, Z_NO_FLUSH)) == Z_OK) { |
224 | ret = bprm->file->f_op->read(bprm->file, buf, LBUFSIZE, &fpos); |
225 | if (ret <= 0) |
226 | break; |
227 | if (is_error(ret)) { |
228 | break; |
229 | } |
230 | len -= ret; |
231 | |
232 | strm.next_in = buf; |
233 | strm.avail_in = ret; |
234 | strm.total_in = 0; |
235 | } |
236 | |
237 | if (ret < 0) { |
238 | DBG_FLT("binfmt_flat: decompression failed (%d), %s\n" , |
239 | ret, strm.msg); |
240 | goto out_zlib; |
241 | } |
242 | |
243 | retval = 0; |
244 | out_zlib: |
245 | zlib_inflateEnd(&strm); |
246 | out_free_buf: |
247 | kfree(buf); |
248 | out_free: |
249 | kfree(strm.workspace); |
250 | out: |
251 | return retval; |
252 | } |
253 | |
254 | #endif /* CONFIG_BINFMT_ZFLAT */ |
255 | |
256 | /****************************************************************************/ |
257 | |
258 | static abi_ulong |
259 | calc_reloc(abi_ulong r, struct lib_info *p, int curid, int internalp) |
260 | { |
261 | abi_ulong addr; |
262 | int id; |
263 | abi_ulong start_brk; |
264 | abi_ulong start_data; |
265 | abi_ulong text_len; |
266 | abi_ulong start_code; |
267 | |
268 | #ifdef CONFIG_BINFMT_SHARED_FLAT |
269 | #error needs checking |
270 | if (r == 0) |
271 | id = curid; /* Relocs of 0 are always self referring */ |
272 | else { |
273 | id = (r >> 24) & 0xff; /* Find ID for this reloc */ |
274 | r &= 0x00ffffff; /* Trim ID off here */ |
275 | } |
276 | if (id >= MAX_SHARED_LIBS) { |
277 | fprintf(stderr, "BINFMT_FLAT: reference 0x%x to shared library %d\n" , |
278 | (unsigned) r, id); |
279 | goto failed; |
280 | } |
281 | if (curid != id) { |
282 | if (internalp) { |
283 | fprintf(stderr, "BINFMT_FLAT: reloc address 0x%x not " |
284 | "in same module (%d != %d)\n" , |
285 | (unsigned) r, curid, id); |
286 | goto failed; |
287 | } else if (!p[id].loaded && is_error(load_flat_shared_library(id, p))) { |
288 | fprintf(stderr, "BINFMT_FLAT: failed to load library %d\n" , id); |
289 | goto failed; |
290 | } |
291 | /* Check versioning information (i.e. time stamps) */ |
292 | if (p[id].build_date && p[curid].build_date |
293 | && p[curid].build_date < p[id].build_date) { |
294 | fprintf(stderr, "BINFMT_FLAT: library %d is younger than %d\n" , |
295 | id, curid); |
296 | goto failed; |
297 | } |
298 | } |
299 | #else |
300 | id = 0; |
301 | #endif |
302 | |
303 | start_brk = p[id].start_brk; |
304 | start_data = p[id].start_data; |
305 | start_code = p[id].start_code; |
306 | text_len = p[id].text_len; |
307 | |
308 | if (!flat_reloc_valid(r, start_brk - start_data + text_len)) { |
309 | fprintf(stderr, "BINFMT_FLAT: reloc outside program 0x%x " |
310 | "(0 - 0x%x/0x%x)\n" , |
311 | (int) r,(int)(start_brk-start_code),(int)text_len); |
312 | goto failed; |
313 | } |
314 | |
315 | if (r < text_len) /* In text segment */ |
316 | addr = r + start_code; |
317 | else /* In data segment */ |
318 | addr = r - text_len + start_data; |
319 | |
320 | /* Range checked already above so doing the range tests is redundant...*/ |
321 | return(addr); |
322 | |
323 | failed: |
324 | abort(); |
325 | return RELOC_FAILED; |
326 | } |
327 | |
328 | /****************************************************************************/ |
329 | |
330 | /* ??? This does not handle endianness correctly. */ |
331 | static void old_reloc(struct lib_info *libinfo, uint32_t rl) |
332 | { |
333 | #ifdef DEBUG |
334 | const char *segment[] = { "TEXT" , "DATA" , "BSS" , "*UNKNOWN*" }; |
335 | #endif |
336 | uint32_t *ptr; |
337 | uint32_t offset; |
338 | int reloc_type; |
339 | |
340 | offset = rl & 0x3fffffff; |
341 | reloc_type = rl >> 30; |
342 | /* ??? How to handle this? */ |
343 | #if defined(CONFIG_COLDFIRE) |
344 | ptr = (uint32_t *) ((unsigned long) libinfo->start_code + offset); |
345 | #else |
346 | ptr = (uint32_t *) ((unsigned long) libinfo->start_data + offset); |
347 | #endif |
348 | |
349 | #ifdef DEBUG |
350 | fprintf(stderr, "Relocation of variable at DATASEG+%x " |
351 | "(address %p, currently %x) into segment %s\n" , |
352 | offset, ptr, (int)*ptr, segment[reloc_type]); |
353 | #endif |
354 | |
355 | switch (reloc_type) { |
356 | case OLD_FLAT_RELOC_TYPE_TEXT: |
357 | *ptr += libinfo->start_code; |
358 | break; |
359 | case OLD_FLAT_RELOC_TYPE_DATA: |
360 | *ptr += libinfo->start_data; |
361 | break; |
362 | case OLD_FLAT_RELOC_TYPE_BSS: |
363 | *ptr += libinfo->end_data; |
364 | break; |
365 | default: |
366 | fprintf(stderr, "BINFMT_FLAT: Unknown relocation type=%x\n" , |
367 | reloc_type); |
368 | break; |
369 | } |
370 | DBG_FLT("Relocation became %x\n" , (int)*ptr); |
371 | } |
372 | |
373 | /****************************************************************************/ |
374 | |
375 | static int load_flat_file(struct linux_binprm * bprm, |
376 | struct lib_info *libinfo, int id, abi_ulong *) |
377 | { |
378 | struct flat_hdr * hdr; |
379 | abi_ulong textpos = 0, datapos = 0; |
380 | abi_long result; |
381 | abi_ulong realdatastart = 0; |
382 | abi_ulong text_len, data_len, bss_len, stack_len, flags; |
383 | abi_ulong ; |
384 | abi_ulong reloc = 0, rp; |
385 | int i, rev, relocs = 0; |
386 | abi_ulong fpos; |
387 | abi_ulong start_code; |
388 | abi_ulong indx_len; |
389 | |
390 | hdr = ((struct flat_hdr *) bprm->buf); /* exec-header */ |
391 | |
392 | text_len = ntohl(hdr->data_start); |
393 | data_len = ntohl(hdr->data_end) - ntohl(hdr->data_start); |
394 | bss_len = ntohl(hdr->bss_end) - ntohl(hdr->data_end); |
395 | stack_len = ntohl(hdr->stack_size); |
396 | if (extra_stack) { |
397 | stack_len += *extra_stack; |
398 | *extra_stack = stack_len; |
399 | } |
400 | relocs = ntohl(hdr->reloc_count); |
401 | flags = ntohl(hdr->flags); |
402 | rev = ntohl(hdr->rev); |
403 | |
404 | DBG_FLT("BINFMT_FLAT: Loading file: %s\n" , bprm->filename); |
405 | |
406 | if (rev != FLAT_VERSION && rev != OLD_FLAT_VERSION) { |
407 | fprintf(stderr, "BINFMT_FLAT: bad magic/rev (0x%x, need 0x%x)\n" , |
408 | rev, (int) FLAT_VERSION); |
409 | return -ENOEXEC; |
410 | } |
411 | |
412 | /* Don't allow old format executables to use shared libraries */ |
413 | if (rev == OLD_FLAT_VERSION && id != 0) { |
414 | fprintf(stderr, "BINFMT_FLAT: shared libraries are not available\n" ); |
415 | return -ENOEXEC; |
416 | } |
417 | |
418 | /* |
419 | * fix up the flags for the older format, there were all kinds |
420 | * of endian hacks, this only works for the simple cases |
421 | */ |
422 | if (rev == OLD_FLAT_VERSION && flat_old_ram_flag(flags)) |
423 | flags = FLAT_FLAG_RAM; |
424 | |
425 | #ifndef CONFIG_BINFMT_ZFLAT |
426 | if (flags & (FLAT_FLAG_GZIP|FLAT_FLAG_GZDATA)) { |
427 | fprintf(stderr, "Support for ZFLAT executables is not enabled\n" ); |
428 | return -ENOEXEC; |
429 | } |
430 | #endif |
431 | |
432 | /* |
433 | * calculate the extra space we need to map in |
434 | */ |
435 | extra = relocs * sizeof(abi_ulong); |
436 | if (extra < bss_len + stack_len) |
437 | extra = bss_len + stack_len; |
438 | |
439 | /* Add space for library base pointers. Make sure this does not |
440 | misalign the doesn't misalign the data segment. */ |
441 | indx_len = MAX_SHARED_LIBS * sizeof(abi_ulong); |
442 | indx_len = (indx_len + 15) & ~(abi_ulong)15; |
443 | |
444 | /* |
445 | * there are a couple of cases here, the separate code/data |
446 | * case, and then the fully copied to RAM case which lumps |
447 | * it all together. |
448 | */ |
449 | if ((flags & (FLAT_FLAG_RAM|FLAT_FLAG_GZIP)) == 0) { |
450 | /* |
451 | * this should give us a ROM ptr, but if it doesn't we don't |
452 | * really care |
453 | */ |
454 | DBG_FLT("BINFMT_FLAT: ROM mapping of file (we hope)\n" ); |
455 | |
456 | textpos = target_mmap(0, text_len, PROT_READ|PROT_EXEC, |
457 | MAP_PRIVATE, bprm->fd, 0); |
458 | if (textpos == -1) { |
459 | fprintf(stderr, "Unable to mmap process text\n" ); |
460 | return -1; |
461 | } |
462 | |
463 | realdatastart = target_mmap(0, data_len + extra + indx_len, |
464 | PROT_READ|PROT_WRITE|PROT_EXEC, |
465 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
466 | |
467 | if (realdatastart == -1) { |
468 | fprintf(stderr, "Unable to allocate RAM for process data\n" ); |
469 | return realdatastart; |
470 | } |
471 | datapos = realdatastart + indx_len; |
472 | |
473 | DBG_FLT("BINFMT_FLAT: Allocated data+bss+stack (%d bytes): %x\n" , |
474 | (int)(data_len + bss_len + stack_len), (int)datapos); |
475 | |
476 | fpos = ntohl(hdr->data_start); |
477 | #ifdef CONFIG_BINFMT_ZFLAT |
478 | if (flags & FLAT_FLAG_GZDATA) { |
479 | result = decompress_exec(bprm, fpos, (char *) datapos, |
480 | data_len + (relocs * sizeof(abi_ulong))) |
481 | } else |
482 | #endif |
483 | { |
484 | result = target_pread(bprm->fd, datapos, |
485 | data_len + (relocs * sizeof(abi_ulong)), |
486 | fpos); |
487 | } |
488 | if (result < 0) { |
489 | fprintf(stderr, "Unable to read data+bss\n" ); |
490 | return result; |
491 | } |
492 | |
493 | reloc = datapos + (ntohl(hdr->reloc_start) - text_len); |
494 | |
495 | } else { |
496 | |
497 | textpos = target_mmap(0, text_len + data_len + extra + indx_len, |
498 | PROT_READ | PROT_EXEC | PROT_WRITE, |
499 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
500 | if (textpos == -1 ) { |
501 | fprintf(stderr, "Unable to allocate RAM for process text/data\n" ); |
502 | return -1; |
503 | } |
504 | |
505 | realdatastart = textpos + ntohl(hdr->data_start); |
506 | datapos = realdatastart + indx_len; |
507 | reloc = (textpos + ntohl(hdr->reloc_start) + indx_len); |
508 | |
509 | #ifdef CONFIG_BINFMT_ZFLAT |
510 | #error code needs checking |
511 | /* |
512 | * load it all in and treat it like a RAM load from now on |
513 | */ |
514 | if (flags & FLAT_FLAG_GZIP) { |
515 | result = decompress_exec(bprm, sizeof (struct flat_hdr), |
516 | (((char *) textpos) + sizeof (struct flat_hdr)), |
517 | (text_len + data_len + (relocs * sizeof(unsigned long)) |
518 | - sizeof (struct flat_hdr)), |
519 | 0); |
520 | memmove((void *) datapos, (void *) realdatastart, |
521 | data_len + (relocs * sizeof(unsigned long))); |
522 | } else if (flags & FLAT_FLAG_GZDATA) { |
523 | fpos = 0; |
524 | result = bprm->file->f_op->read(bprm->file, |
525 | (char *) textpos, text_len, &fpos); |
526 | if (!is_error(result)) { |
527 | result = decompress_exec(bprm, text_len, (char *) datapos, |
528 | data_len + (relocs * sizeof(unsigned long)), 0); |
529 | } |
530 | } |
531 | else |
532 | #endif |
533 | { |
534 | result = target_pread(bprm->fd, textpos, |
535 | text_len, 0); |
536 | if (result >= 0) { |
537 | result = target_pread(bprm->fd, datapos, |
538 | data_len + (relocs * sizeof(abi_ulong)), |
539 | ntohl(hdr->data_start)); |
540 | } |
541 | } |
542 | if (result < 0) { |
543 | fprintf(stderr, "Unable to read code+data+bss\n" ); |
544 | return result; |
545 | } |
546 | } |
547 | |
548 | DBG_FLT("Mapping is 0x%x, Entry point is 0x%x, data_start is 0x%x\n" , |
549 | (int)textpos, 0x00ffffff&ntohl(hdr->entry), |
550 | ntohl(hdr->data_start)); |
551 | |
552 | /* The main program needs a little extra setup in the task structure */ |
553 | start_code = textpos + sizeof (struct flat_hdr); |
554 | |
555 | DBG_FLT("%s %s: TEXT=%x-%x DATA=%x-%x BSS=%x-%x\n" , |
556 | id ? "Lib" : "Load" , bprm->filename, |
557 | (int) start_code, (int) (textpos + text_len), |
558 | (int) datapos, |
559 | (int) (datapos + data_len), |
560 | (int) (datapos + data_len), |
561 | (int) (((datapos + data_len + bss_len) + 3) & ~3)); |
562 | |
563 | text_len -= sizeof(struct flat_hdr); /* the real code len */ |
564 | |
565 | /* Store the current module values into the global library structure */ |
566 | libinfo[id].start_code = start_code; |
567 | libinfo[id].start_data = datapos; |
568 | libinfo[id].end_data = datapos + data_len; |
569 | libinfo[id].start_brk = datapos + data_len + bss_len; |
570 | libinfo[id].text_len = text_len; |
571 | libinfo[id].loaded = 1; |
572 | libinfo[id].entry = (0x00ffffff & ntohl(hdr->entry)) + textpos; |
573 | libinfo[id].build_date = ntohl(hdr->build_date); |
574 | |
575 | /* |
576 | * We just load the allocations into some temporary memory to |
577 | * help simplify all this mumbo jumbo |
578 | * |
579 | * We've got two different sections of relocation entries. |
580 | * The first is the GOT which resides at the beginning of the data segment |
581 | * and is terminated with a -1. This one can be relocated in place. |
582 | * The second is the extra relocation entries tacked after the image's |
583 | * data segment. These require a little more processing as the entry is |
584 | * really an offset into the image which contains an offset into the |
585 | * image. |
586 | */ |
587 | if (flags & FLAT_FLAG_GOTPIC) { |
588 | rp = datapos; |
589 | while (1) { |
590 | abi_ulong addr; |
591 | if (get_user_ual(addr, rp)) |
592 | return -EFAULT; |
593 | if (addr == -1) |
594 | break; |
595 | if (addr) { |
596 | addr = calc_reloc(addr, libinfo, id, 0); |
597 | if (addr == RELOC_FAILED) |
598 | return -ENOEXEC; |
599 | if (put_user_ual(addr, rp)) |
600 | return -EFAULT; |
601 | } |
602 | rp += sizeof(abi_ulong); |
603 | } |
604 | } |
605 | |
606 | /* |
607 | * Now run through the relocation entries. |
608 | * We've got to be careful here as C++ produces relocatable zero |
609 | * entries in the constructor and destructor tables which are then |
610 | * tested for being not zero (which will always occur unless we're |
611 | * based from address zero). This causes an endless loop as __start |
612 | * is at zero. The solution used is to not relocate zero addresses. |
613 | * This has the negative side effect of not allowing a global data |
614 | * reference to be statically initialised to _stext (I've moved |
615 | * __start to address 4 so that is okay). |
616 | */ |
617 | if (rev > OLD_FLAT_VERSION) { |
618 | abi_ulong persistent = 0; |
619 | for (i = 0; i < relocs; i++) { |
620 | abi_ulong addr, relval; |
621 | |
622 | /* Get the address of the pointer to be |
623 | relocated (of course, the address has to be |
624 | relocated first). */ |
625 | if (get_user_ual(relval, reloc + i * sizeof(abi_ulong))) |
626 | return -EFAULT; |
627 | relval = ntohl(relval); |
628 | if (flat_set_persistent(relval, &persistent)) |
629 | continue; |
630 | addr = flat_get_relocate_addr(relval); |
631 | rp = calc_reloc(addr, libinfo, id, 1); |
632 | if (rp == RELOC_FAILED) |
633 | return -ENOEXEC; |
634 | |
635 | /* Get the pointer's value. */ |
636 | if (get_user_ual(addr, rp)) |
637 | return -EFAULT; |
638 | addr = flat_get_addr_from_rp(addr, relval, flags, &persistent); |
639 | if (addr != 0) { |
640 | /* |
641 | * Do the relocation. PIC relocs in the data section are |
642 | * already in target order |
643 | */ |
644 | if ((flags & FLAT_FLAG_GOTPIC) == 0) |
645 | addr = ntohl(addr); |
646 | addr = calc_reloc(addr, libinfo, id, 0); |
647 | if (addr == RELOC_FAILED) |
648 | return -ENOEXEC; |
649 | |
650 | /* Write back the relocated pointer. */ |
651 | if (flat_put_addr_at_rp(rp, addr, relval)) |
652 | return -EFAULT; |
653 | } |
654 | } |
655 | } else { |
656 | for (i = 0; i < relocs; i++) { |
657 | abi_ulong relval; |
658 | if (get_user_ual(relval, reloc + i * sizeof(abi_ulong))) |
659 | return -EFAULT; |
660 | old_reloc(&libinfo[0], relval); |
661 | } |
662 | } |
663 | |
664 | /* zero the BSS. */ |
665 | memset(g2h(datapos + data_len), 0, bss_len); |
666 | |
667 | return 0; |
668 | } |
669 | |
670 | |
671 | /****************************************************************************/ |
672 | #ifdef CONFIG_BINFMT_SHARED_FLAT |
673 | |
674 | /* |
675 | * Load a shared library into memory. The library gets its own data |
676 | * segment (including bss) but not argv/argc/environ. |
677 | */ |
678 | |
679 | static int load_flat_shared_library(int id, struct lib_info *libs) |
680 | { |
681 | struct linux_binprm bprm; |
682 | int res; |
683 | char buf[16]; |
684 | |
685 | /* Create the file name */ |
686 | sprintf(buf, "/lib/lib%d.so" , id); |
687 | |
688 | /* Open the file up */ |
689 | bprm.filename = buf; |
690 | bprm.file = open_exec(bprm.filename); |
691 | res = PTR_ERR(bprm.file); |
692 | if (IS_ERR(bprm.file)) |
693 | return res; |
694 | |
695 | res = prepare_binprm(&bprm); |
696 | |
697 | if (!is_error(res)) { |
698 | res = load_flat_file(&bprm, libs, id, NULL); |
699 | } |
700 | if (bprm.file) { |
701 | allow_write_access(bprm.file); |
702 | fput(bprm.file); |
703 | bprm.file = NULL; |
704 | } |
705 | return(res); |
706 | } |
707 | |
708 | #endif /* CONFIG_BINFMT_SHARED_FLAT */ |
709 | |
710 | int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) |
711 | { |
712 | struct lib_info libinfo[MAX_SHARED_LIBS]; |
713 | abi_ulong p; |
714 | abi_ulong stack_len; |
715 | abi_ulong start_addr; |
716 | abi_ulong sp; |
717 | int res; |
718 | int i, j; |
719 | |
720 | memset(libinfo, 0, sizeof(libinfo)); |
721 | /* |
722 | * We have to add the size of our arguments to our stack size |
723 | * otherwise it's too easy for users to create stack overflows |
724 | * by passing in a huge argument list. And yes, we have to be |
725 | * pedantic and include space for the argv/envp array as it may have |
726 | * a lot of entries. |
727 | */ |
728 | stack_len = 0; |
729 | for (i = 0; i < bprm->argc; ++i) { |
730 | /* the argv strings */ |
731 | stack_len += strlen(bprm->argv[i]); |
732 | } |
733 | for (i = 0; i < bprm->envc; ++i) { |
734 | /* the envp strings */ |
735 | stack_len += strlen(bprm->envp[i]); |
736 | } |
737 | stack_len += (bprm->argc + 1) * 4; /* the argv array */ |
738 | stack_len += (bprm->envc + 1) * 4; /* the envp array */ |
739 | |
740 | |
741 | res = load_flat_file(bprm, libinfo, 0, &stack_len); |
742 | if (is_error(res)) { |
743 | return res; |
744 | } |
745 | |
746 | /* Update data segment pointers for all libraries */ |
747 | for (i=0; i<MAX_SHARED_LIBS; i++) { |
748 | if (libinfo[i].loaded) { |
749 | abi_ulong p; |
750 | p = libinfo[i].start_data; |
751 | for (j=0; j<MAX_SHARED_LIBS; j++) { |
752 | p -= 4; |
753 | /* FIXME - handle put_user() failures */ |
754 | if (put_user_ual(libinfo[j].loaded |
755 | ? libinfo[j].start_data |
756 | : UNLOADED_LIB, |
757 | p)) |
758 | return -EFAULT; |
759 | } |
760 | } |
761 | } |
762 | |
763 | p = ((libinfo[0].start_brk + stack_len + 3) & ~3) - 4; |
764 | DBG_FLT("p=%x\n" , (int)p); |
765 | |
766 | /* Copy argv/envp. */ |
767 | p = copy_strings(p, bprm->envc, bprm->envp); |
768 | p = copy_strings(p, bprm->argc, bprm->argv); |
769 | /* Align stack. */ |
770 | sp = p & ~(abi_ulong)(sizeof(abi_ulong) - 1); |
771 | /* Enforce final stack alignment of 16 bytes. This is sufficient |
772 | for all current targets, and excess alignment is harmless. */ |
773 | stack_len = bprm->envc + bprm->argc + 2; |
774 | stack_len += flat_argvp_envp_on_stack() ? 2 : 0; /* arvg, argp */ |
775 | stack_len += 1; /* argc */ |
776 | stack_len *= sizeof(abi_ulong); |
777 | sp -= (sp - stack_len) & 15; |
778 | sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, |
779 | flat_argvp_envp_on_stack()); |
780 | |
781 | /* Fake some return addresses to ensure the call chain will |
782 | * initialise library in order for us. We are required to call |
783 | * lib 1 first, then 2, ... and finally the main program (id 0). |
784 | */ |
785 | start_addr = libinfo[0].entry; |
786 | |
787 | #ifdef CONFIG_BINFMT_SHARED_FLAT |
788 | #error here |
789 | for (i = MAX_SHARED_LIBS-1; i>0; i--) { |
790 | if (libinfo[i].loaded) { |
791 | /* Push previos first to call address */ |
792 | --sp; |
793 | if (put_user_ual(start_addr, sp)) |
794 | return -EFAULT; |
795 | start_addr = libinfo[i].entry; |
796 | } |
797 | } |
798 | #endif |
799 | |
800 | /* Stash our initial stack pointer into the mm structure */ |
801 | info->start_code = libinfo[0].start_code; |
802 | info->end_code = libinfo[0].start_code = libinfo[0].text_len; |
803 | info->start_data = libinfo[0].start_data; |
804 | info->end_data = libinfo[0].end_data; |
805 | info->start_brk = libinfo[0].start_brk; |
806 | info->start_stack = sp; |
807 | info->stack_limit = libinfo[0].start_brk; |
808 | info->entry = start_addr; |
809 | info->code_offset = info->start_code; |
810 | info->data_offset = info->start_data - libinfo[0].text_len; |
811 | |
812 | DBG_FLT("start_thread(entry=0x%x, start_stack=0x%x)\n" , |
813 | (int)info->entry, (int)info->start_stack); |
814 | |
815 | return 0; |
816 | } |
817 | |