text: /*
   * Copyright (c) 2015 Cisco and/or its affiliates.
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at:
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  #include <vppinfra/elf_clib.h>
  
  #include <stdlib.h>
  #include <fcntl.h>
  #include <sys/stat.h>
  
  typedef struct
  {
    char **path;
  } path_search_t;
  
  always_inline void
  path_search_free (path_search_t * p)
  {
    uword i;
    for (i = 0; i < vec_len (p->path); i++)
      vec_free (p->path[i]);
    vec_free (p->path);
  }
  
  static char **
  split_string (char *string, u8 delimiter)
  {
    char **result = 0;
    char *p, *start, *s;
  
    p = string;
    while (1)
      {
        start = p;
        while (*p != 0 && *p != delimiter)
  	p++;
        s = 0;
        vec_add (s, start, p - start);
        vec_add1 (s, 0);
        vec_add1 (result, s);
        if (*p == 0)
  	break;
        p++;
      }
  
    return result;
  }
  
  static int
  file_exists_and_is_executable (char *dir, char *file)
  {
    char *path = (char *) format (0, "%s/%s%c", dir, file, 0);
    struct stat s;
    uword yes;
  
    yes = (stat (path, &s) >= 0
  	 && S_ISREG (s.st_mode)
  	 && 0 != (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)));
  
    vec_free (path);
  
    return yes;
  }
  
  static char *
  path_search (char *file)
  {
    path_search_t ps;
    uword i;
    char *result;
  
    /* Relative or absolute path. */
    if (file[0] == '.' || file[0] == '/')
      return file;
  
    if (getenv ("PATH") == 0)
      return file;
  
    ps.path = split_string (getenv ("PATH"), ':');
  
    for (i = 0; i < vec_len (ps.path); i++)
      if (file_exists_and_is_executable (ps.path[i], file))
        break;
  
    result = 0;
    if (i < vec_len (ps.path))
      result = (char *) format (0, "%s/%s%c", ps.path[i], file);
  
    path_search_free (&ps);
  
    return result;
  }
  
  static clib_error_t *
  clib_elf_parse_file (clib_elf_main_t * cem,
  		     char *file_name, void *link_address)
  {
    elf_main_t *em;
    elf_section_t *s;
    int fd;
    struct stat fd_stat;
    uword mmap_length = 0;
    void *data = 0;
    clib_error_t *error = 0;
  
    vec_add2 (cem->elf_mains, em, 1);
  
    fd = open (file_name, 0);
    if (fd < 0)
      {
        error = clib_error_return_unix (0, "open `%s'", file_name);
        goto done;
      }
  
    if (fstat (fd, &fd_stat) < 0)
      {
        error = clib_error_return_unix (0, "fstat `%s'", file_name);
        goto done;
      }
    mmap_length = fd_stat.st_size;
  
    data = mmap (0, mmap_length, PROT_READ, MAP_SHARED, fd, /* offset */ 0);
    if (~pointer_to_uword (data) == 0)
      {
        error = clib_error_return_unix (0, "mmap `%s'", file_name);
        goto done;
      }
  
    error = elf_parse (em, data, mmap_length);
    if (error)
      goto done;
  
    /* Look for CLIB special sections. */
    {
      char *section_name_start = CLIB_ELF_SECTION_ADD_PREFIX ();
      uword section_name_start_len = strlen (section_name_start);
  
      vec_foreach (s, em->sections)
      {
        u8 *name = elf_section_name (em, s);
        uword *p;
        clib_elf_section_t *vs;
        clib_elf_section_bounds_t *b;
  
        /* Section name must begin with CLIB_ELF_SECTION key. */
        if (strcmp ((char *) name, section_name_start))
  	continue;
  
        name += section_name_start_len;
        p = hash_get_mem (cem->section_by_name, name);
        if (p)
  	vs = vec_elt_at_index (cem->sections, p[0]);
        else
  	{
  	  name = format (0, "%s%c", name, 0);
  	  if (!cem->section_by_name)
  	    cem->section_by_name = hash_create_string (0, sizeof (uword));
  	  hash_set_mem (cem->section_by_name, name, vec_len (cem->sections));
  	  vec_add2 (cem->sections, vs, 1);
  	  vs->name = name;
  	}
  
        vec_add2 (vs->bounds, b, 1);
        b->lo = link_address + s->header.exec_address;
        b->hi = b->lo + s->header.file_size;
      }
    }
  
    /* Parse symbols for this file. */
    {
      elf_symbol_table_t *t;
      elf64_symbol_t *s;
  
      elf_parse_symbols (em);
      vec_foreach (t, em->symbol_tables)
      {
        vec_foreach (s, t->symbols)
        {
  	s->value += pointer_to_uword (link_address);
        }
      }
    }
  
    /* No need to keep section contents around. */
    {
      elf_section_t *s;
      vec_foreach (s, em->sections)
      {
        if (s->header.type != ELF_SECTION_STRING_TABLE)
  	vec_free (s->contents);
      }
    }
  
  done:
    if (error)
      elf_main_free (em);
    if (fd >= 0)
      close (fd);
    if (data)
      munmap (data, mmap_length);
    return error;
  }
  
  #define __USE_GNU
  #include <link.h>
  
  static int
  add_section (struct dl_phdr_info *info, size_t size, void *opaque)
  {
    clib_elf_main_t *cem = opaque;
    clib_error_t *error;
    char *name = (char *) info->dlpi_name;
    void *addr = (void *) info->dlpi_addr;
    uword is_main;
  
    is_main = strlen (name) == 0;
    if (is_main)
      {
        static int done;
  
        /* Only do main program once. */
        if (done++)
  	return 0;
  
        name = path_search (cem->exec_path);
        if (!name)
  	{
  	  clib_error ("failed to find %s on PATH", cem->exec_path);
  	  return 0;
  	}
        addr = 0;
      }
  
    error = clib_elf_parse_file (cem, name, addr);
    if (error)
      {
        /* Don't complain about 'linux-vdso.so.1' */
        if (!is_main && name[0] != '/' && error->code == ENOENT)
  	clib_error_free (error);
        else
  	clib_error_report (error);
      }
  
    if (is_main && name != cem->exec_path)
      vec_free (name);
  
    return 0;
  }
  
  static clib_elf_main_t clib_elf_main;
  
  __clib_export void
  clib_elf_main_init (char *exec_path)
  {
    clib_elf_main_t *cem = &clib_elf_main;
  
    cem->exec_path = exec_path;
  
    dl_iterate_phdr (add_section, cem);
  }
  
  clib_elf_section_bounds_t *
  clib_elf_get_section_bounds (char *name)
  {
    clib_elf_main_t *em = &clib_elf_main;
    uword *p = hash_get (em->section_by_name, name);
    return p ? vec_elt_at_index (em->sections, p[0])->bounds : 0;
  }
  
  static uword
  symbol_by_address_or_name (char *by_name,
  			   uword by_address, clib_elf_symbol_t * s)
  {
    clib_elf_main_t *cem = &clib_elf_main;
    elf_main_t *em;
  
    vec_foreach (em, cem->elf_mains)
    {
      elf_symbol_table_t *t;
      s->elf_main_index = em - cem->elf_mains;
      vec_foreach (t, em->symbol_tables)
      {
        s->symbol_table_index = t - em->symbol_tables;
        if (by_name)
  	{
  	  uword *p = hash_get (t->symbol_by_name, by_name);
  	  if (p)
  	    {
  	      s->symbol = vec_elt (t->symbols, p[0]);
  	      return 1;
  	    }
  	}
        else
  	{
  	  elf64_symbol_t *x;
  	  /* FIXME linear search. */
  	  vec_foreach (x, t->symbols)
  	  {
  	    if (by_address >= x->value && by_address < x->value + x->size)
  	      {
  		s->symbol = x[0];
  		return 1;
  	      }
  	  }
  	}
      }
    }
  
    return 0;
  }
  
  uword
  clib_elf_symbol_by_name (char *by_name, clib_elf_symbol_t * s)
  {
    return symbol_by_address_or_name (by_name, /* by_address */ 0, s);
  }
  
  uword
  clib_elf_symbol_by_address (uword by_address, clib_elf_symbol_t * s)
  {
    return symbol_by_address_or_name ( /* by_name */ 0, by_address, s);
  }
  
  u8 *
  format_clib_elf_symbol (u8 * s, va_list * args)
  {
    clib_elf_main_t *cem = &clib_elf_main;
    clib_elf_symbol_t *sym = va_arg (*args, clib_elf_symbol_t *);
    elf_main_t *em;
    elf_symbol_table_t *t;
  
    if (!sym)
      /* Just print table headings. */
      return format (s, "%U", format_elf_symbol, 0, 0, 0);
  
    else
      {
        em = vec_elt_at_index (cem->elf_mains, sym->elf_main_index);
        t = vec_elt_at_index (em->symbol_tables, sym->symbol_table_index);
        return format (s, "%U", format_elf_symbol, em, t, &sym->symbol);
      }
  }
  
  __clib_export u8 *
  format_clib_elf_symbol_with_address (u8 * s, va_list * args)
  {
    uword address = va_arg (*args, uword);
    clib_elf_main_t *cem = &clib_elf_main;
    clib_elf_symbol_t sym;
    elf_main_t *em;
    elf_symbol_table_t *t;
  
    if (clib_elf_symbol_by_address (address, &sym))
      {
        em = vec_elt_at_index (cem->elf_mains, sym.elf_main_index);
        t = vec_elt_at_index (em->symbol_tables, sym.symbol_table_index);
        s = format (s, "%s + 0x%wx",
  		  elf_symbol_name (t, &sym.symbol),
  		  address - sym.symbol.value);
      }
    else
      s = format (s, "0x%wx", address);
  
    return s;
  }
  
  /*
   * fd.io coding-style-patch-verification: ON
   *
   * Local Variables:
   * eval: (c-set-style "gnu")
   * End:
   */
  