Example #1
0
__attribute__ ((regparm (3))) attribute_hidden
_dl_tlsdesc_resolve_rel_fixup (struct tlsdesc volatile *td,
			       struct link_map *l,
			       ptrdiff_t entry_check_offset)
{
  const ElfW(Rel) *reloc = td->arg;

  if (_dl_tlsdesc_resolve_early_return_p (td, __builtin_return_address (0)
					  - entry_check_offset))
    return;

  /* The code below was borrowed from _dl_fixup(),
     except for checking for STB_LOCAL.  */
  const ElfW(Sym) *const symtab
    = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
  const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);
  const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
  lookup_t result;

   /* Look up the target symbol.  If the normal lookup rules are not
      used don't look in the global scope.  */
  if (ELFW(ST_BIND) (sym->st_info) != STB_LOCAL
      && __builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
    {
      const struct r_found_version *version = NULL;

      if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
	{
	  const ElfW(Half) *vernum =
	    (const void *) D_PTR (l, l_info[VERSYMIDX (DT_VERSYM)]);
	  ElfW(Half) ndx = vernum[ELFW(R_SYM) (reloc->r_info)] & 0x7fff;
	  version = &l->l_versions[ndx];
	  if (version->hash == 0)
	    version = NULL;
	}

      result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym,
				    l->l_scope, version, ELF_RTYPE_CLASS_PLT,
				    DL_LOOKUP_ADD_DEPENDENCY, NULL);
    }
  else
    {
Example #2
0
File: dl-vdso.c Project: VanL/glibc
internal_function
_dl_vdso_vsym (const char *name, const struct r_found_version *vers)
{
#ifndef __ZRT_SO
  struct link_map *map = GLRO (dl_sysinfo_map);
  void *value = NULL;


  if (map != NULL)
    {
      /* Use a WEAK REF so we don't error out if the symbol is not found.  */
      ElfW (Sym) wsym;
      memset (&wsym, 0, sizeof (ElfW (Sym)));
      wsym.st_info = (unsigned char) ELFW (ST_INFO (STB_WEAK, STT_NOTYPE));

      /* Search the scope of the vdso map.  */
      const ElfW (Sym) *ref = &wsym;
      lookup_t result = GLRO (dl_lookup_symbol_x) (name, map, &ref,
						   map->l_local_scope,
						   vers, 0, 0, NULL);

      if (ref != NULL)
	value = DL_SYMBOL_ADDRESS (result, ref);
    }
Example #3
0
DL_FIXUP_VALUE_TYPE
__attribute ((noinline)) ARCH_FIXUP_ATTRIBUTE
_dl_fixup (
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
	   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
# endif
	   struct link_map *l, ElfW(Word) reloc_arg)
{
  const ElfW(Sym) *const symtab
    = (const void *) D_PTR (l, l_info[DT_SYMTAB]);
  const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]);

  const PLTREL *const reloc
    = (const void *) (D_PTR (l, l_info[DT_JMPREL]) + reloc_offset);
  const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (reloc->r_info)];
  void *const rel_addr = (void *)(l->l_addr + reloc->r_offset);
  lookup_t result;
  DL_FIXUP_VALUE_TYPE value;

  /* Sanity check that we're really looking at a PLT relocation.  */
  assert (ELFW(R_TYPE)(reloc->r_info) == ELF_MACHINE_JMP_SLOT);

   /* Look up the target symbol.  If the normal lookup rules are not
      used don't look in the global scope.  */
  if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0)
    {
      const struct r_found_version *version = NULL;

      if (l->l_info[VERSYMIDX (DT_VERSYM)] != NULL)
	{
Example #4
0
    "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0android_update_LD_LIBRARY_PATH\0android_get_LD_LIBRARY_PATH\0dl_iterate_phdr\0android_dlopen_ext\0dl_unwind_find_exidx\0"
#elif defined(__aarch64__) || defined(__i386__) || defined(__mips__) || defined(__x86_64__)
  // 0000000 00011111 111112 22222222 2333333 3333444444444455555555556666666 6667777777777888888888899999 9999900000000001 1111111112222222222
  // 0123456 78901234 567890 12345678 9012345 6789012345678901234567890123456 7890123456789012345678901234 5678901234567890 1234567890123456789
#  define ANDROID_LIBDL_STRTAB \
    "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0android_update_LD_LIBRARY_PATH\0android_get_LD_LIBRARY_PATH\0dl_iterate_phdr\0android_dlopen_ext\0"
#else
#  error Unsupported architecture. Only arm, arm64, mips, mips64, x86 and x86_64 are presently supported.
#endif

static ElfW(Sym) g_libdl_symtab[] = {
  // Total length of libdl_info.strtab, including trailing 0.
  // This is actually the STH_UNDEF entry. Technically, it's
  // supposed to have st_name == 0, but instead, it points to an index
  // in the strtab with a \0 to make iterating through the symtab easier.
  ELFW(SYM_INITIALIZER)(sizeof(ANDROID_LIBDL_STRTAB) - 1, nullptr, 0),
  ELFW(SYM_INITIALIZER)(  0, &dlopen, 1),
  ELFW(SYM_INITIALIZER)(  7, &dlclose, 1),
  ELFW(SYM_INITIALIZER)( 15, &dlsym, 1),
  ELFW(SYM_INITIALIZER)( 21, &dlerror, 1),
  ELFW(SYM_INITIALIZER)( 29, &dladdr, 1),
  ELFW(SYM_INITIALIZER)( 36, &android_update_LD_LIBRARY_PATH, 1),
  ELFW(SYM_INITIALIZER)( 67, &android_get_LD_LIBRARY_PATH, 1),
  ELFW(SYM_INITIALIZER)( 95, &dl_iterate_phdr, 1),
  ELFW(SYM_INITIALIZER)(111, &android_dlopen_ext, 1),
#if defined(__arm__)
  ELFW(SYM_INITIALIZER)(130, &dl_unwind_find_exidx, 1),
#endif
};

// Fake out a hash table with a single bucket.
int
internal_function
__elfw2(LIBELFBITS,updatemmap) (Elf *elf, int change_bo, size_t shnum)
{
    bool previous_scn_changed = false;

    /* We need the ELF header several times.  */
    ElfW2(LIBELFBITS,Ehdr) *ehdr = elf->state.ELFW(elf,LIBELFBITS).ehdr;

    /* Write out the ELF header.  */
    if ((elf->state.ELFW(elf,LIBELFBITS).ehdr_flags | elf->flags) & ELF_F_DIRTY)
    {
        /* If the type sizes should be different at some time we have to
        rewrite this code.  */
        assert (sizeof (ElfW2(LIBELFBITS,Ehdr))
                == elf_typesize (LIBELFBITS, ELF_T_EHDR, 1));

        if (unlikely (change_bo))
        {
            /* Today there is only one version of the ELF header.  */
#if EV_NUM != 2
            xfct_t fctp;
            fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR];
#else
# undef fctp
# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_EHDR]
#endif

            /* Do the real work.  */
            (*fctp) ((char *) elf->map_address + elf->start_offset, ehdr,
                     sizeof (ElfW2(LIBELFBITS,Ehdr)), 1);
        }
        else if (elf->map_address + elf->start_offset != ehdr)
            memcpy (elf->map_address + elf->start_offset, ehdr,
                    sizeof (ElfW2(LIBELFBITS,Ehdr)));

        elf->state.ELFW(elf,LIBELFBITS).ehdr_flags &= ~ELF_F_DIRTY;

        /* We start writing sections after the ELF header only if there is
        no program header.  */
        previous_scn_changed = elf->state.ELFW(elf,LIBELFBITS).phdr == NULL;
    }

    size_t phnum;
    if (unlikely (__elf_getphdrnum_rdlock (elf, &phnum) != 0))
        return -1;

    /* Write out the program header table.  */
    if (elf->state.ELFW(elf,LIBELFBITS).phdr != NULL
            && ((elf->state.ELFW(elf,LIBELFBITS).phdr_flags | elf->flags)
                & ELF_F_DIRTY))
    {
        /* If the type sizes should be different at some time we have to
        rewrite this code.  */
        assert (sizeof (ElfW2(LIBELFBITS,Phdr))
                == elf_typesize (LIBELFBITS, ELF_T_PHDR, 1));

        /* Maybe the user wants a gap between the ELF header and the program
        header.  */
        if (ehdr->e_phoff > ehdr->e_ehsize)
            memset (elf->map_address + elf->start_offset + ehdr->e_ehsize,
                    __libelf_fill_byte, ehdr->e_phoff - ehdr->e_ehsize);

        if (unlikely (change_bo))
        {
            /* Today there is only one version of the ELF header.  */
#if EV_NUM != 2
            xfct_t fctp;
            fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR];
#else
# undef fctp
# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_PHDR]
#endif

            /* Do the real work.  */
            (*fctp) (elf->map_address + elf->start_offset + ehdr->e_phoff,
                     elf->state.ELFW(elf,LIBELFBITS).phdr,
                     sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum, 1);
        }
        else
            memcpy (elf->map_address + elf->start_offset + ehdr->e_phoff,
                    elf->state.ELFW(elf,LIBELFBITS).phdr,
                    sizeof (ElfW2(LIBELFBITS,Phdr)) * phnum);

        elf->state.ELFW(elf,LIBELFBITS).phdr_flags &= ~ELF_F_DIRTY;

        /* We modified the program header.  Maybe this created a gap so
        we have to write fill bytes, if necessary.  */
        previous_scn_changed = true;
    }

    /* From now on we have to keep track of the last position to eventually
       fill the gaps with the prescribed fill byte.  */
    char *last_position = ((char *) elf->map_address + elf->start_offset
                           + MAX (elf_typesize (LIBELFBITS, ELF_T_EHDR, 1),
                                  ehdr->e_phoff)
                           + elf_typesize (LIBELFBITS, ELF_T_PHDR, phnum));

    /* Write all the sections.  Well, only those which are modified.  */
    if (shnum > 0)
    {
        if (unlikely (shnum > SIZE_MAX / sizeof (Elf_Scn *)))
            return 1;

        Elf_ScnList *list = &elf->state.ELFW(elf,LIBELFBITS).scns;
        Elf_Scn **scns = (Elf_Scn **) malloc (shnum * sizeof (Elf_Scn *));
        if (unlikely (scns == NULL))
        {
            __libelf_seterrno (ELF_E_NOMEM);
            return -1;
        }
        char *const shdr_start = ((char *) elf->map_address + elf->start_offset
                                  + ehdr->e_shoff);
        char *const shdr_end = shdr_start + ehdr->e_shnum * ehdr->e_shentsize;

#if EV_NUM != 2
        xfct_t shdr_fctp = __elf_xfctstom[__libelf_version - 1][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR];
#else
# undef shdr_fctp
# define shdr_fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][ELF_T_SHDR]
#endif
#define shdr_dest ((ElfW2(LIBELFBITS,Shdr) *) shdr_start)

        /* Get all sections into the array and sort them.  */
        sort_sections (scns, list);

        /* We possibly have to copy the section header data because moving
        the sections might overwrite the data.  */
        for (size_t cnt = 0; cnt < shnum; ++cnt)
        {
            Elf_Scn *scn = scns[cnt];

            if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
                    && (scn->shdr_flags & ELF_F_MALLOCED) == 0
                    && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
            {
                assert ((char *) elf->map_address + elf->start_offset
                        < (char *) scn->shdr.ELFW(e,LIBELFBITS));
                assert ((char *) scn->shdr.ELFW(e,LIBELFBITS)
                        < ((char *) elf->map_address + elf->start_offset
                           + elf->maximum_size));

                void *p = malloc (sizeof (ElfW2(LIBELFBITS,Shdr)));
                if (unlikely (p == NULL))
                {
                    __libelf_seterrno (ELF_E_NOMEM);
                    return -1;
                }
                scn->shdr.ELFW(e,LIBELFBITS)
                    = memcpy (p, scn->shdr.ELFW(e,LIBELFBITS),
                              sizeof (ElfW2(LIBELFBITS,Shdr)));
            }

            /* If the file is mmaped and the original position of the
               section in the file is lower than the new position we
               need to save the section content since otherwise it is
               overwritten before it can be copied.  If there are
               multiple data segments in the list only the first can be
               from the file.  */
            if (((char *) elf->map_address + elf->start_offset
                    <= (char  *) scn->data_list.data.d.d_buf)
                    && ((char *) scn->data_list.data.d.d_buf
                        < ((char *) elf->map_address + elf->start_offset
                           + elf->maximum_size))
                    && (((char *) elf->map_address + elf->start_offset
                         + scn->shdr.ELFW(e,LIBELFBITS)->sh_offset)
                        > (char *) scn->data_list.data.d.d_buf))
            {
                void *p = malloc (scn->data_list.data.d.d_size);
                if (unlikely (p == NULL))
                {
                    __libelf_seterrno (ELF_E_NOMEM);
                    return -1;
                }
                scn->data_list.data.d.d_buf = scn->data_base
                                              = memcpy (p, scn->data_list.data.d.d_buf,
                                                        scn->data_list.data.d.d_size);
            }
        }

        /* Iterate over all the section in the order in which they
        appear in the output file.  */
        for (size_t cnt = 0; cnt < shnum; ++cnt)
        {
            Elf_Scn *scn = scns[cnt];
            if (scn->index == 0)
            {
                /* The dummy section header entry.  It should not be
                possible to mark this "section" as dirty.  */
                assert ((scn->flags & ELF_F_DIRTY) == 0);
                continue;
            }

            ElfW2(LIBELFBITS,Shdr) *shdr = scn->shdr.ELFW(e,LIBELFBITS);
            if (shdr->sh_type == SHT_NOBITS)
                goto next;

            char *scn_start = ((char *) elf->map_address
                               + elf->start_offset + shdr->sh_offset);
            Elf_Data_List *dl = &scn->data_list;
            bool scn_changed = false;

            void fill_mmap (size_t offset)
            {
                size_t written = 0;

                if (last_position < shdr_start)
                {
                    written = MIN (scn_start + offset - last_position,
                                   shdr_start - last_position);

                    memset (last_position, __libelf_fill_byte, written);
                }

                if (last_position + written != scn_start + offset
                        && shdr_end < scn_start + offset)
                {
                    char *fill_start = MAX (shdr_end, scn_start);
                    memset (fill_start, __libelf_fill_byte,
                            scn_start + offset - fill_start);
                }
            }

            if (scn->data_list_rear != NULL)
                do
                {
                    assert (dl->data.d.d_off >= 0);
                    assert ((GElf_Off) dl->data.d.d_off <= shdr->sh_size);
                    assert (dl->data.d.d_size <= (shdr->sh_size
                                                  - (GElf_Off) dl->data.d.d_off));

                    /* If there is a gap, fill it.  */
                    if (scn_start + dl->data.d.d_off > last_position
                            && (dl->data.d.d_off == 0
                                || ((scn->flags | dl->flags | elf->flags)
                                    & ELF_F_DIRTY) != 0))
                    {
                        fill_mmap (dl->data.d.d_off);
                        last_position = scn_start + dl->data.d.d_off;
                    }

                    if ((scn->flags | dl->flags | elf->flags) & ELF_F_DIRTY)
                    {
                        /* Let it go backward if the sections use a bogus
                           layout with overlaps.  We'll overwrite the stupid
                           user's section data with the latest one, rather than
                           crashing.  */

                        last_position = scn_start + dl->data.d.d_off;

                        if (unlikely (change_bo))
                        {
#if EV_NUM != 2
                            xfct_t fctp;
                            fctp = __elf_xfctstom[__libelf_version - 1][dl->data.d.d_version - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type];
#else
# undef fctp
# define fctp __elf_xfctstom[0][EV_CURRENT - 1][ELFW(ELFCLASS, LIBELFBITS) - 1][dl->data.d.d_type]
#endif

                            /* Do the real work.  */
                            (*fctp) (last_position, dl->data.d.d_buf,
                                     dl->data.d.d_size, 1);

                            last_position += dl->data.d.d_size;
                        }
                        else if (dl->data.d.d_size != 0)
                            last_position = mempcpy (last_position,
                                                     dl->data.d.d_buf,
                                                     dl->data.d.d_size);

                        scn_changed = true;
                    }
                    else
                        last_position += dl->data.d.d_size;

                    assert (scn_start + dl->data.d.d_off + dl->data.d.d_size
                            == last_position);

                    dl->flags &= ~ELF_F_DIRTY;

                    dl = dl->next;
                }
                while (dl != NULL);
            else
            {
                /* If the previous section (or the ELF/program
                header) changed we might have to fill the gap.  */
                if (scn_start > last_position && previous_scn_changed)
                    fill_mmap (0);

                /* We have to trust the existing section header information.  */
                last_position = scn_start + shdr->sh_size;
            }


            previous_scn_changed = scn_changed;
next:
            scn->flags &= ~ELF_F_DIRTY;
        }

        /* Fill the gap between last section and section header table if
        necessary.  */
        if ((elf->flags & ELF_F_DIRTY)
                && last_position < ((char *) elf->map_address + elf->start_offset
                                    + ehdr->e_shoff))
            memset (last_position, __libelf_fill_byte,
                    (char *) elf->map_address + elf->start_offset + ehdr->e_shoff
                    - last_position);

        /* Write the section header table entry if necessary.  */
        for (size_t cnt = 0; cnt < shnum; ++cnt)
        {
            Elf_Scn *scn = scns[cnt];

            if ((scn->shdr_flags | elf->flags) & ELF_F_DIRTY)
            {
                if (unlikely (change_bo))
                    (*shdr_fctp) (&shdr_dest[scn->index],
                                  scn->shdr.ELFW(e,LIBELFBITS),
                                  sizeof (ElfW2(LIBELFBITS,Shdr)), 1);
                else
                    memcpy (&shdr_dest[scn->index],
                            scn->shdr.ELFW(e,LIBELFBITS),
                            sizeof (ElfW2(LIBELFBITS,Shdr)));

                /* If we previously made a copy of the section header
                entry we now have to adjust the pointer again so
                 point to new place in the mapping.  */
                if (!elf->state.ELFW(elf,LIBELFBITS).shdr_malloced
                        && (scn->shdr_flags & ELF_F_MALLOCED) == 0
                        && scn->shdr.ELFW(e,LIBELFBITS) != &shdr_dest[scn->index])
                {
                    free (scn->shdr.ELFW(e,LIBELFBITS));
                    scn->shdr.ELFW(e,LIBELFBITS) = &shdr_dest[scn->index];
                }

                scn->shdr_flags &= ~ELF_F_DIRTY;
            }
        }
        free (scns);
    }
Example #6
0
    "erate_phdr\0android_dlopen_ext\0android_set_application_target_sdk_version\0android_get_application_tar"
  // 0000000000111111 111122222222223333333333 4444444444555555555566666 6666677 777777778888888888
  // 0123456789012345 678901234567890123456789 0123456789012345678901234 5678901 234567890123456789
    "get_sdk_version\0android_init_namespaces\0android_create_namespace\0dlvsym\0android_dlwarning\0"
#if defined(__arm__)
  // 290
    "dl_unwind_find_exidx\0"
#endif
    ;

static ElfW(Sym) g_libdl_symtab[] = {
  // Total length of libdl_info.strtab, including trailing 0.
  // This is actually the STH_UNDEF entry. Technically, it's
  // supposed to have st_name == 0, but instead, it points to an index
  // in the strtab with a \0 to make iterating through the symtab easier.
  ELFW(SYM_INITIALIZER)(sizeof(ANDROID_LIBDL_STRTAB) - 1, nullptr, 0),
  ELFW(SYM_INITIALIZER)(  0, &dlopen, 1),
  ELFW(SYM_INITIALIZER)(  7, &dlclose, 1),
  ELFW(SYM_INITIALIZER)( 15, &dlsym, 1),
  ELFW(SYM_INITIALIZER)( 21, &dlerror, 1),
  ELFW(SYM_INITIALIZER)( 29, &dladdr, 1),
  ELFW(SYM_INITIALIZER)( 36, &android_update_LD_LIBRARY_PATH, 1),
  ELFW(SYM_INITIALIZER)( 67, &android_get_LD_LIBRARY_PATH, 1),
  ELFW(SYM_INITIALIZER)( 95, &dl_iterate_phdr, 1),
  ELFW(SYM_INITIALIZER)(111, &android_dlopen_ext, 1),
  ELFW(SYM_INITIALIZER)(130, &android_set_application_target_sdk_version, 1),
  ELFW(SYM_INITIALIZER)(173, &android_get_application_target_sdk_version, 1),
  ELFW(SYM_INITIALIZER)(216, &android_init_namespaces, 1),
  ELFW(SYM_INITIALIZER)(240, &android_create_namespace, 1),
  ELFW(SYM_INITIALIZER)(265, &dlvsym, 1),
  ELFW(SYM_INITIALIZER)(272, &android_dlwarning, 1),
Example #7
0
   versioned_sym and num_versions which are modified by check_match during
   the checking process.  */
static const ElfW(Sym) *
check_match (const char *const undef_name,
	     const ElfW(Sym) *const ref,
	     const struct r_found_version *const version,
	     const int flags,
	     const int type_class,
	     const ElfW(Sym) *const sym,
	     const Elf_Symndx symidx,
	     const char *const strtab,
	     const struct link_map *const map,
	     const ElfW(Sym) **const versioned_sym,
	     int *const num_versions)
{
  unsigned int stt = ELFW(ST_TYPE) (sym->st_info);
  assert (ELF_RTYPE_CLASS_PLT == 1);
  if (__glibc_unlikely ((sym->st_value == 0 /* No value.  */
			 && stt != STT_TLS)
			|| ELF_MACHINE_SYM_NO_MATCH (sym)
			|| (type_class & (sym->st_shndx == SHN_UNDEF))))
    return NULL;

  /* Ignore all but STT_NOTYPE, STT_OBJECT, STT_FUNC,
     STT_COMMON, STT_TLS, and STT_GNU_IFUNC since these are no
     code/data definitions.  */
#define ALLOWED_STT \
  ((1 << STT_NOTYPE) | (1 << STT_OBJECT) | (1 << STT_FUNC) \
   | (1 << STT_COMMON) | (1 << STT_TLS) | (1 << STT_GNU_IFUNC))
  if (__glibc_unlikely (((1 << stt) & ALLOWED_STT) == 0))
    return NULL;
/*
 *	Read the symbols in an object and register them in the symbol table.
 *	Return -1 if there is an error.
 */
static int loadobj(const char *objname, int ignore_suffix)
{
	static char *currdir;
	static int currdir_len;
	int fp;
	MODULE *mod;
	SYMBOL *undefs[5000];
	SYMBOL *defsym[5000];
	struct obj_file *f;
	struct obj_section *sect;
	struct obj_symbol *objsym;
	int i;
	int is_2_2;
	int ksymtab;
	int len;
	int n_undefs = 0;
	int n_defsym = 0;
	char *p;
	void *image;
	unsigned long m_size;

	p = strrchr(objname, '/');
	len = 1 + (int)(p - objname);

	if ((fp = open(objname, O_RDONLY)) < 0)
		return 1;

	if (!(f = obj_load(fp, ET_REL, objname))) {
		close(fp);
		return -1;
	}
	close(fp);

	/*
	 * Allow arch code to define _GLOBAL_OFFSET_TABLE_
	 */
	arch_create_got(f);

	/*
	 * If we have changed base directory
	 * then use the defined symbols from modules
	 * in the _same_ directory to resolve whatever
	 * undefined symbols there are.
	 *
	 * This strategy ensures that we will have
	 * as correct dependencies as possible,
	 * even if the same symbol is defined by
	 * other modules in other directories.
	 */
	if (currdir_len != len || currdir == NULL ||
	    strncmp(currdir, objname, len) != 0) {
		if (currdir)
			resolve();
		currdir = bb_xstrdup(objname);
		currdir_len = len;
	}

	mod = modules + n_modules++;
	mod->name = bb_xstrdup(objname);

	if ((sect = obj_find_section(f, "__ksymtab")) != NULL)
		ksymtab = sect->idx; /* Only in 2.2 (or at least not 2.0) */
	else
		ksymtab = -1;

	if (sect ||
	    obj_find_section(f, ".modinfo") ||
	    obj_find_symbol(f, "__this_module"))
		is_2_2 = 1;
	else
		is_2_2 = 0;

	for (i = 0; i < HASH_BUCKETS; ++i) {
		for (objsym = f->symtab[i]; objsym; objsym = objsym->next) {
			if (objsym->secidx == SHN_UNDEF) {
				if (ELFW(ST_BIND)(objsym->info) != STB_WEAK &&
				    objsym->r_type /* assumes that R_arch_NONE is always 0 on all arch */) {
					undefs[n_undefs++] = addsym(objsym->name,
								    mod,
								    SYM_UNDEF,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2 && ksymtab != -1) {
				/* A 2.2 module using EXPORT_SYMBOL */
				if (objsym->secidx == ksymtab &&
				    ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					/* Ignore leading "__ksymtab_" */
					defsym[n_defsym++] = addsym(objsym->name + 10,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			if (is_2_2) {
				/*
				 * A 2.2 module _not_ using EXPORT_SYMBOL
				 * It might still want to export symbols,
				 * although strictly speaking it shouldn't...
				 * (Seen in pcmcia)
				 */
				if (ELFW(ST_BIND)(objsym->info) == STB_GLOBAL) {
					defsym[n_defsym++] = addsym(objsym->name,
								    mod,
								    SYM_DEFINED,
								    ignore_suffix);
				}
				continue;
			}
			/* else */

			/* Not undefined or 2.2 ksymtab entry */
			if (objsym->secidx < SHN_LORESERVE
			/*
			 * The test below is removed for 2.0 compatibility
			 * since some 2.0-modules (correctly) hide the
			 * symbols it exports via register_symtab()
			 */
			/* && ELFW(ST_BIND)(objsym->info) == STB_GLOBAL */
			     ) {
				defsym[n_defsym++] = addsym(objsym->name,
							    mod,
							    SYM_DEFINED,
							    ignore_suffix);
			}
		}
	}

	/*
	 *	Finalize the information about a module.
	 */
	mod->defsym.n_syms = n_defsym;
	if (n_defsym > 0) {
		int size = n_defsym * sizeof(SYMBOL *);

		mod->defsym.symtab = (SYMBOL **)xmalloc(size);
		memcpy(mod->defsym.symtab, defsym, size);
	} else
		mod->defsym.symtab = NULL;

	mod->undefs.n_syms = n_undefs;
	if (n_undefs > 0) {
		int size = n_undefs * sizeof(SYMBOL *);

		mod->undefs.symtab = (SYMBOL **) xmalloc(size);
		memcpy(mod->undefs.symtab, undefs, size);
		mod->resolved = 0;
	} else {
		mod->undefs.symtab = NULL;
		mod->resolved = 1;
	}

	/* Do a pseudo relocation to base address 0x1000 (arbitrary).
	 * All undefined symbols are treated as absolute 0.  This builds
	 * enough of a module to allow extraction of internal data such
	 * as device tables.
	 */
	obj_clear_undefineds(f);
	obj_allocate_commons(f);
	m_size = obj_load_size(f);
	if (!obj_relocate(f, 0x1000)) {
		bb_error_msg("depmod obj_relocate failed\n");
		return(-1);
	}
	extract_generic_string(f, mod);
	image = xmalloc(m_size);
	obj_create_image(f, image);
	free(image);

	obj_free(f);
	return 0;
}