+/* Get the program boundaries. Because not everything is covered by
+ sections in ELF, notably the program headers, we use the program
+ headers instead. */
+
+static void
+cris_get_progbounds (struct bfd *abfd, struct progbounds *pbp)
+{
+ Elf_Internal_Phdr *phdr;
+ int n_hdrs;
+ int i;
+
+ pbp->startmem = 0xffffffff;
+ pbp->endmem = 0;
+ pbp->end_loadmem = 0;
+ pbp->start_nonloadmem = 0xffffffff;
+
+ /* In case we're ever used for something other than ELF, use the
+ generic method. */
+ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+ {
+ bfd_map_over_sections (abfd, get_progbounds_iterator, pbp);
+ return;
+ }
+
+ phdr = elf_tdata (abfd)->phdr;
+ n_hdrs = elf_elfheader (abfd)->e_phnum;
+
+ /* We're only interested in PT_LOAD; all necessary information
+ should be covered by that. */
+ for (i = 0; i < n_hdrs; i++)
+ {
+ if (phdr[i].p_type != PT_LOAD)
+ continue;
+
+ if (phdr[i].p_paddr < pbp->startmem)
+ pbp->startmem = phdr[i].p_paddr;
+
+ if (phdr[i].p_paddr + phdr[i].p_memsz > pbp->endmem)
+ pbp->endmem = phdr[i].p_paddr + phdr[i].p_memsz;
+
+ if (phdr[i].p_paddr + phdr[i].p_filesz > pbp->end_loadmem)
+ pbp->end_loadmem = phdr[i].p_paddr + phdr[i].p_filesz;
+
+ if (phdr[i].p_memsz > phdr[i].p_filesz
+ && phdr[i].p_paddr + phdr[i].p_filesz < pbp->start_nonloadmem)
+ pbp->start_nonloadmem = phdr[i].p_paddr + phdr[i].p_filesz;
+ }
+}
+
+/* Parameter communication by static variables, hmm... Oh well, for
+ simplicity. */
+static bfd_vma exec_load_addr;
+static bfd_vma interp_load_addr;
+static bfd_vma interp_start_addr;
+
+/* Supposed to mimic Linux' "NEW_AUX_ENT (AT_PHDR, load_addr + exec->e_phoff)". */
+
+static USI
+aux_ent_phdr (struct bfd *ebfd)
+{
+ return elf_elfheader (ebfd)->e_phoff + exec_load_addr;
+}
+
+/* We just pass on the header info; we don't have our own idea of the
+ program header entry size. */
+
+static USI
+aux_ent_phent (struct bfd *ebfd)
+{
+ return elf_elfheader (ebfd)->e_phentsize;
+}
+
+/* Like "NEW_AUX_ENT(AT_PHNUM, exec->e_phnum)". */
+
+static USI
+aux_ent_phnum (struct bfd *ebfd)
+{
+ return elf_elfheader (ebfd)->e_phnum;
+}
+
+/* Like "NEW_AUX_ENT(AT_BASE, interp_load_addr)". */
+
+static USI
+aux_ent_base (struct bfd *ebfd)
+{
+ return interp_load_addr;
+}
+
+/* Like "NEW_AUX_ENT(AT_ENTRY, exec->e_entry)". */
+
+static USI
+aux_ent_entry (struct bfd *ebfd)
+{
+ ASSERT (elf_elfheader (ebfd)->e_entry == bfd_get_start_address (ebfd));
+ return elf_elfheader (ebfd)->e_entry;
+}
+
+/* Helper for cris_handle_interpreter: like sim_write, but load at
+ interp_load_addr offset. */
+
+static int
+cris_write_interp (SIM_DESC sd, SIM_ADDR mem, unsigned char *buf, int length)
+{
+ return sim_write (sd, mem + interp_load_addr, buf, length);
+}
+
+/* Cater to the presence of an interpreter: load it and set
+ interp_start_addr. Return FALSE if there was an error, TRUE if
+ everything went fine, including an interpreter being absent and
+ the program being in a non-ELF format. */
+
+static bfd_boolean
+cris_handle_interpreter (SIM_DESC sd, struct bfd *abfd)
+{
+ int i, n_hdrs;
+ bfd_vma phaddr;
+ bfd_byte buf[4];
+ char *interp = NULL;
+ struct bfd *ibfd;
+ bfd_boolean ok = FALSE;
+ Elf_Internal_Phdr *phdr;
+
+ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+ return TRUE;
+
+ phdr = elf_tdata (abfd)->phdr;
+ n_hdrs = aux_ent_phnum (abfd);
+
+ /* Check the program headers for presence of an interpreter. */
+ for (i = 0; i < n_hdrs; i++)
+ {
+ int interplen;
+ bfd_size_type interpsiz, interp_filesiz;
+ struct progbounds interp_bounds;
+
+ if (phdr[i].p_type != PT_INTERP)
+ continue;
+
+ /* Get the name of the interpreter, prepended with the sysroot
+ (empty if absent). */
+ interplen = phdr[i].p_filesz;
+ interp = xmalloc (interplen + strlen (simulator_sysroot));
+ strcpy (interp, simulator_sysroot);
+
+ /* Read in the name. */
+ if (bfd_seek (abfd, phdr[i].p_offset, SEEK_SET) != 0
+ || (bfd_bread (interp + strlen (simulator_sysroot), interplen, abfd)
+ != interplen))
+ goto interpname_failed;
+
+ /* Like Linux, require the string to be 0-terminated. */
+ if (interp[interplen + strlen (simulator_sysroot) - 1] != 0)
+ goto interpname_failed;
+
+ /* Inspect the interpreter. */
+ ibfd = bfd_openr (interp, STATE_TARGET (sd));
+ if (ibfd == NULL)
+ goto interpname_failed;
+
+ /* The interpreter is at least something readable to BFD; make
+ sure it's an ELF non-archive file. */
+ if (!bfd_check_format (ibfd, bfd_object)
+ || bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
+ goto interp_failed;
+
+ /* Check the layout of the interpreter. */
+ cris_get_progbounds (ibfd, &interp_bounds);
+
+ /* Round down to pagesize the start page and up the endpage.
+ Don't round the *load and *nonload members. */
+ interp_bounds.startmem &= ~8191;
+ interp_bounds.endmem = (interp_bounds.endmem + 8191) & ~8191;
+
+ /* Until we need a more dynamic solution, assume we can put the
+ interpreter at this fixed location. NB: this is not what
+ happens for Linux 2008-12-28, but it could and might and
+ perhaps should. */
+ interp_load_addr = 0x40000;
+ interpsiz = interp_bounds.endmem - interp_bounds.startmem;
+ interp_filesiz = interp_bounds.end_loadmem - interp_bounds.startmem;
+
+ /* If we have a non-DSO or interpreter starting at the wrong
+ address, bail. */
+ if (interp_bounds.startmem != 0
+ || interpsiz + interp_load_addr >= exec_load_addr)
+ goto interp_failed;
+
+ /* We don't have the API to get the address of a simulator
+ memory area, so we go via a temporary area. Luckily, the
+ interpreter is supposed to be small, less than 0x40000
+ bytes. */
+ sim_do_commandf (sd, "memory region 0x%lx,0x%lx",
+ interp_load_addr, interpsiz);
+
+ /* Now that memory for the interpreter is defined, load it. */
+ if (!cris_load_elf_file (sd, ibfd, cris_write_interp))
+ goto interp_failed;
+
+ /* It's no use setting STATE_START_ADDR, because it gets
+ overwritten by a sim_analyze_program call in sim_load. Let's
+ just store it locally. */
+ interp_start_addr
+ = (bfd_get_start_address (ibfd)
+ - interp_bounds.startmem + interp_load_addr);
+
+ /* Linux cares only about the first PT_INTERP, so let's ignore
+ the rest. */
+ goto all_done;
+ }
+
+ /* Register R10 should hold 0 at static start (no finifunc), but
+ that's the default, so don't bother. */
+ return TRUE;
+
+ all_done:
+ ok = TRUE;
+
+ interp_failed:
+ bfd_close (ibfd);
+
+ interpname_failed:
+ if (!ok)
+ sim_io_eprintf (sd,
+ "%s: could not load ELF interpreter `%s' for program `%s'\n",
+ STATE_MY_NAME (sd),
+ interp == NULL ? "(what's-its-name)" : interp,
+ bfd_get_filename (abfd));
+ free (interp);
+ return ok;
+}
+