_dl_map_object_from_fd (const char *name, int fd, char *realname,
			struct link_map *loader, int l_type)
{
  /* This is the expected ELF header.  */
#define ELF32_CLASS ELFCLASS32
#define ELF64_CLASS ELFCLASS64
#ifndef VALID_ELF_HEADER
# define VALID_ELF_HEADER(hdr,exp,size)	(memcmp (hdr, exp, size) == 0)
# define VALID_ELF_OSABI(osabi)		(osabi == ELFOSABI_SYSV)
# define VALID_ELF_ABIVERSION(ver)	(ver == 0)
#endif
  static const unsigned char expected[EI_PAD] =
  {
    [EI_MAG0] = ELFMAG0,
    [EI_MAG1] = ELFMAG1,
    [EI_MAG2] = ELFMAG2,
    [EI_MAG3] = ELFMAG3,
    [EI_CLASS] = ELFW(CLASS),
    [EI_DATA] = byteorder,
    [EI_VERSION] = EV_CURRENT,
    [EI_OSABI] = ELFOSABI_SYSV,
    [EI_ABIVERSION] = 0
  };
  struct link_map *l = NULL;

  inline caddr_t map_segment (ElfW(Addr) mapstart, size_t len,
			      int prot, int fixed, off_t offset)
    {
      caddr_t mapat = __mmap ((caddr_t) mapstart, len, prot,
			      fixed|MAP_COPY|MAP_FILE,
			      fd, offset);
      if (mapat == MAP_FAILED)
	LOSE (errno, "failed to map segment from shared object");
      return mapat;
    }

  const ElfW(Ehdr) *header;
  const ElfW(Phdr) *phdr;
  const ElfW(Phdr) *ph;
  size_t maplength;
  int type;
  char *readbuf;
  ssize_t readlength;
  struct stat st;

  /* Get file information.  */
  if (__fxstat (_STAT_VER, fd, &st) < 0)
    LOSE (errno, "cannot stat shared object");

  /* Look again to see if the real name matched another already loaded.  */
  for (l = _dl_loaded; l; l = l->l_next)
    if (l->l_ino == st.st_ino && l->l_dev == st.st_dev)
      {
	/* The object is already loaded.
	   Just bump its reference count and return it.  */
	__close (fd);

	/* If the name is not in the list of names for this object add
	   it.  */
	free (realname);
	add_name_to_object (l, name);
	++l->l_opencount;
	return l;
      }

  /* Print debugging message.  */
  if (_dl_debug_files)
    _dl_debug_message (1, "file=", name, ";  generating link map\n", NULL);

  /* Read the header directly.  */
  readbuf = alloca (_dl_pagesize);
  readlength = __libc_read (fd, readbuf, _dl_pagesize);
  if (readlength < (ssize_t) sizeof (*header))
    LOSE (errno, "cannot read file data");
  header = (void *) readbuf;

  /* Check the header for basic validity.  */
  if (__builtin_expect (!VALID_ELF_HEADER (header->e_ident, expected, EI_PAD),
			0))
    {
      /* Something is wrong.  */
      if (*(Elf32_Word *) &header->e_ident !=
#if BYTE_ORDER == LITTLE_ENDIAN
	  ((ELFMAG0 << (EI_MAG0 * 8)) |
	   (ELFMAG1 << (EI_MAG1 * 8)) |
	   (ELFMAG2 << (EI_MAG2 * 8)) |
	   (ELFMAG3 << (EI_MAG3 * 8)))
#else
	  ((ELFMAG0 << (EI_MAG3 * 8)) |
	   (ELFMAG1 << (EI_MAG2 * 8)) |
	   (ELFMAG2 << (EI_MAG1 * 8)) |
	   (ELFMAG3 << (EI_MAG0 * 8)))
#endif
	  )
	LOSE (0, "invalid ELF header");
      if (header->e_ident[EI_CLASS] != ELFW(CLASS))
	LOSE (0, "ELF file class not " STRING(__ELF_NATIVE_CLASS) "-bit");
      if (header->e_ident[EI_DATA] != byteorder)
	LOSE (0, "ELF file data encoding not " byteorder_name);
      if (header->e_ident[EI_VERSION] != EV_CURRENT)
	LOSE (0, "ELF file version ident not " STRING(EV_CURRENT));
      /* XXX We should be able so set system specific versions which are
	 allowed here.  */
      if (!VALID_ELF_OSABI (header->e_ident[EI_OSABI]))
	LOSE (0, "ELF file OS ABI invalid.");
      if (!VALID_ELF_ABIVERSION (header->e_ident[EI_ABIVERSION]))
	LOSE (0, "ELF file ABI version invalid.");
      LOSE (0, "internal error");
    }

  if (__builtin_expect (header->e_version, EV_CURRENT) != EV_CURRENT)
    LOSE (0, "ELF file version not " STRING(EV_CURRENT));
  if (! __builtin_expect (elf_machine_matches_host (header->e_machine), 1))
    LOSE (0, "ELF file machine architecture not " ELF_MACHINE_NAME);
  if (__builtin_expect (header->e_phentsize, sizeof (ElfW(Phdr)))
      != sizeof (ElfW(Phdr)))
    LOSE (0, "ELF file's phentsize not the expected size");

#ifndef MAP_ANON
# define MAP_ANON 0
  if (_dl_zerofd == -1)
    {
      _dl_zerofd = _dl_sysdep_open_zero_fill ();
      if (_dl_zerofd == -1)
	{
	  __close (fd);
	  _dl_signal_error (errno, NULL, "cannot open zero fill device");
	}
    }
#endif

  /* Enter the new object in the list of loaded objects.  */
  l = _dl_new_object (realname, name, l_type, loader);
  if (__builtin_expect (! l, 0))
    LOSE (ENOMEM, "cannot create shared object descriptor");
  l->l_opencount = 1;

  /* Extract the remaining details we need from the ELF header
     and then read in the program header table.  */
  l->l_entry = header->e_entry;
  type = header->e_type;
  l->l_phnum = header->e_phnum;

  maplength = header->e_phnum * sizeof (ElfW(Phdr));
  if (header->e_phoff + maplength <= readlength)
    phdr = (void *) (readbuf + header->e_phoff);
  else
    {
      phdr = alloca (maplength);
      __lseek (fd, SEEK_SET, header->e_phoff);
      if (__libc_read (fd, (void *) phdr, maplength) != maplength)
        LOSE (errno, "cannot read file data");
    }

  {
    /* Scan the program header table, collecting its load commands.  */
    struct loadcmd
      {
	ElfW(Addr) mapstart, mapend, dataend, allocend;
	off_t mapoff;
	int prot;
      } loadcmds[l->l_phnum], *c;
    size_t nloadcmds = 0;

    /* The struct is initialized to zero so this is not necessary:
    l->l_ld = 0;
    l->l_phdr = 0;
    l->l_addr = 0; */
    for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
      switch (ph->p_type)
	{
	  /* These entries tell us where to find things once the file's
	     segments are mapped in.  We record the addresses it says
	     verbatim, and later correct for the run-time load address.  */
	case PT_DYNAMIC:
	  l->l_ld = (void *) ph->p_vaddr;
	  break;
	case PT_PHDR:
	  l->l_phdr = (void *) ph->p_vaddr;
	  break;

	case PT_LOAD:
	  /* A load command tells us to map in part of the file.
	     We record the load commands and process them all later.  */
	  if (ph->p_align % _dl_pagesize != 0)
	    LOSE (0, "ELF load command alignment not page-aligned");
	  if ((ph->p_vaddr - ph->p_offset) % ph->p_align)
	    LOSE (0, "ELF load command address/offset not properly aligned");
	  {
	    struct loadcmd *c = &loadcmds[nloadcmds++];
	    c->mapstart = ph->p_vaddr & ~(ph->p_align - 1);
	    c->mapend = ((ph->p_vaddr + ph->p_filesz + _dl_pagesize - 1)
			 & ~(_dl_pagesize - 1));
	    c->dataend = ph->p_vaddr + ph->p_filesz;
	    c->allocend = ph->p_vaddr + ph->p_memsz;
	    c->mapoff = ph->p_offset & ~(ph->p_align - 1);

	    /* Optimize a common case.  */
	    if ((PF_R | PF_W | PF_X) == 7
		&& (PROT_READ | PROT_WRITE | PROT_EXEC) == 7)
	      c->prot = _dl_pf_to_prot[ph->p_flags & (PF_R | PF_W | PF_X)];
	    else
	      {
		c->prot = 0;
		if (ph->p_flags & PF_R)
		  c->prot |= PROT_READ;
		if (ph->p_flags & PF_W)
		  c->prot |= PROT_WRITE;
		if (ph->p_flags & PF_X)
		  c->prot |= PROT_EXEC;
	      }
	    break;
	  }
	}

    /* Now process the load commands and map segments into memory.  */
    c = loadcmds;

    /* Length of the sections to be loaded.  */
    maplength = loadcmds[nloadcmds - 1].allocend - c->mapstart;

    if (type == ET_DYN || type == ET_REL)
      {
	/* This is a position-independent shared object.  We can let the
	   kernel map it anywhere it likes, but we must have space for all
	   the segments in their specified positions relative to the first.
	   So we map the first segment without MAP_FIXED, but with its
	   extent increased to cover all the segments.  Then we remove
	   access from excess portion, and there is known sufficient space
	   there to remap from the later segments.

	   As a refinement, sometimes we have an address that we would
	   prefer to map such objects at; but this is only a preference,
	   the OS can do whatever it likes. */
 	caddr_t mapat;
	ElfW(Addr) mappref;
	mappref = (ELF_PREFERRED_ADDRESS (loader, maplength, c->mapstart)
		   - MAP_BASE_ADDR (l));
	mapat = map_segment (mappref, maplength, c->prot, 0, c->mapoff);
	l->l_addr = (ElfW(Addr)) mapat - c->mapstart;

	/* Change protection on the excess portion to disallow all access;
	   the portions we do not remap later will be inaccessible as if
	   unallocated.  Then jump into the normal segment-mapping loop to
	   handle the portion of the segment past the end of the file
	   mapping.  */
	__mprotect ((caddr_t) (l->l_addr + c->mapend),
		    loadcmds[nloadcmds - 1].allocend - c->mapend,
		    0);

	/* Remember which part of the address space this object uses.  */
	l->l_map_start = c->mapstart + l->l_addr;
	l->l_map_end = l->l_map_start + maplength;

	goto postmap;
      }
    else
      {
	/* Notify ELF_PREFERRED_ADDRESS that we have to load this one
	   fixed.  */
	ELF_FIXED_ADDRESS (loader, c->mapstart);
      }

    /* Remember which part of the address space this object uses.  */
    l->l_map_start = c->mapstart + l->l_addr;
    l->l_map_end = l->l_map_start + maplength;

    while (c < &loadcmds[nloadcmds])
      {
	if (c->mapend > c->mapstart)
	  /* Map the segment contents from the file.  */
	  map_segment (l->l_addr + c->mapstart, c->mapend - c->mapstart,
		       c->prot, MAP_FIXED, c->mapoff);

      postmap:
	if (l->l_phdr == 0
	    && c->mapoff <= header->e_phoff
	    && (c->mapend - c->mapstart + c->mapoff
		>= header->e_phoff + header->e_phnum * sizeof (ElfW(Phdr))))
	  /* Found the program header in this segment.  */
	  l->l_phdr = (void *) (c->mapstart + header->e_phoff - c->mapoff);

	if (c->allocend > c->dataend)
	  {
	    /* Extra zero pages should appear at the end of this segment,
	       after the data mapped from the file.   */
	    ElfW(Addr) zero, zeroend, zeropage;

	    zero = l->l_addr + c->dataend;
	    zeroend = l->l_addr + c->allocend;
	    zeropage = (zero + _dl_pagesize - 1) & ~(_dl_pagesize - 1);

	    if (zeroend < zeropage)
	      /* All the extra data is in the last page of the segment.
		 We can just zero it.  */
	      zeropage = zeroend;

	    if (zeropage > zero)
	      {
		/* Zero the final part of the last page of the segment.  */
		if ((c->prot & PROT_WRITE) == 0)
		  {
		    /* Dag nab it.  */
		    if (__mprotect ((caddr_t) (zero & ~(_dl_pagesize - 1)),
				    _dl_pagesize, c->prot|PROT_WRITE) < 0)
		      LOSE (errno, "cannot change memory protections");
		  }
		memset ((void *) zero, 0, zeropage - zero);
		if ((c->prot & PROT_WRITE) == 0)
		  __mprotect ((caddr_t) (zero & ~(_dl_pagesize - 1)),
			      _dl_pagesize, c->prot);
	      }

	    if (zeroend > zeropage)
	      {
		/* Map the remaining zero pages in from the zero fill FD.  */
		caddr_t mapat;
		mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
				c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
				ANONFD, 0);
		if (mapat == MAP_FAILED)
		  LOSE (errno, "cannot map zero-fill pages");
	      }
	  }

	++c;
      }

    if (l->l_phdr == NULL)
      {
	/* The program header is not contained in any of the segmenst.
	   We have to allocate memory ourself and copy it over from
	   out temporary place.  */
	ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum
						  * sizeof (ElfW(Phdr)));
	if (newp == NULL)
	  LOSE (ENOMEM, "cannot allocate memory for program header");

	l->l_phdr = memcpy (newp, phdr,
			    (header->e_phnum * sizeof (ElfW(Phdr))));
	l->l_phdr_allocated = 1;
      }
    else
      /* Adjust the PT_PHDR value by the runtime load address.  */
      (ElfW(Addr)) l->l_phdr += l->l_addr;
  }

  /* We are done mapping in the file.  We no longer need the descriptor.  */
  __close (fd);

  if (l->l_type == lt_library && type == ET_EXEC)
    l->l_type = lt_executable;

  if (l->l_ld == 0)
    {
      if (type == ET_DYN)
	LOSE (0, "object file has no dynamic section");
    }
  else
    (ElfW(Addr)) l->l_ld += l->l_addr;

  l->l_entry += l->l_addr;

  if (_dl_debug_files)
    {
      const size_t nibbles = sizeof (void *) * 2;
      char buf1[nibbles + 1];
      char buf2[nibbles + 1];
      char buf3[nibbles + 1];

      buf1[nibbles] = '\0';
      buf2[nibbles] = '\0';
      buf3[nibbles] = '\0';

      memset (buf1, '0', nibbles);
      memset (buf2, '0', nibbles);
      memset (buf3, '0', nibbles);
      _itoa_word ((unsigned long int) l->l_ld, &buf1[nibbles], 16, 0);
      _itoa_word ((unsigned long int) l->l_addr, &buf2[nibbles], 16, 0);
      _itoa_word (maplength, &buf3[nibbles], 16, 0);

      _dl_debug_message (1, "  dynamic: 0x", buf1, "  base: 0x", buf2,
			 "   size: 0x", buf3, "\n", NULL);
      memset (buf1, '0', nibbles);
      memset (buf2, '0', nibbles);
      memset (buf3, ' ', nibbles);
      _itoa_word ((unsigned long int) l->l_entry, &buf1[nibbles], 16, 0);
      _itoa_word ((unsigned long int) l->l_phdr, &buf2[nibbles], 16, 0);
      _itoa_word (l->l_phnum, &buf3[nibbles], 10, 0);
      _dl_debug_message (1, "    entry: 0x", buf1, "  phdr: 0x", buf2,
			 "  phnum:   ", buf3, "\n\n", NULL);
    }

  elf_get_dynamic_info (l);
  if (l->l_info[DT_HASH])
    _dl_setup_hash (l);

  /* If this object has DT_SYMBOLIC set modify now its scope.  We don't
     have to do this for the main map.  */
  if (l->l_info[DT_SYMBOLIC] && &l->l_searchlist != l->l_scope[0])
    {
      /* Create an appropriate searchlist.  It contains only this map.

	 XXX This is the definition of DT_SYMBOLIC in SysVr4.  The old
	 GNU ld.so implementation had a different interpretation which
	 is more reasonable.  We are prepared to add this possibility
	 back as part of a GNU extension of the ELF format.  */
      l->l_symbolic_searchlist.r_list =
	(struct link_map **) malloc (sizeof (struct link_map *));

      if (l->l_symbolic_searchlist.r_list == NULL)
	LOSE (ENOMEM, "cannot create searchlist");

      l->l_symbolic_searchlist.r_list[0] = l;
      l->l_symbolic_searchlist.r_nlist = 1;
      l->l_symbolic_searchlist.r_duplist = l->l_symbolic_searchlist.r_list;
      l->l_symbolic_searchlist.r_nduplist = 1;

      /* Now move the existing entries one back.  */
      memmove (&l->l_scope[1], &l->l_scope[0],
	       sizeof (l->l_scope) - sizeof (l->l_scope[0]));

      /* Now add the new entry.  */
      l->l_scope[0] = &l->l_symbolic_searchlist;
    }

  /* Finally the file information.  */
  l->l_dev = st.st_dev;
  l->l_ino = st.st_ino;

  return l;
}