Initial commit of vpp code.
[vpp.git] / vppinfra / vppinfra / elf.c
diff --git a/vppinfra/vppinfra/elf.c b/vppinfra/vppinfra/elf.c
new file mode 100644 (file)
index 0000000..bbd321b
--- /dev/null
@@ -0,0 +1,1936 @@
+/*
+ * 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/bitmap.h>
+#include <vppinfra/byte_order.h>
+#include <vppinfra/error.h>
+#include <vppinfra/hash.h>
+#include <vppinfra/vec.h>
+#include <vppinfra/elf.h>
+
+always_inline void
+elf_swap_first_header (elf_main_t * em, elf_first_header_t * h)
+{
+  h->architecture = elf_swap_u16 (em, h->architecture);
+  h->file_type = elf_swap_u16 (em, h->file_type);
+  h->file_version = elf_swap_u32 (em, h->file_version);
+}
+
+always_inline void
+elf_swap_verneed (elf_dynamic_version_need_t * n)
+{
+#define _(t,f) n->f = clib_byte_swap_##t (n->f);
+  foreach_elf_dynamic_version_need_field
+#undef _
+}
+
+always_inline void
+elf_swap_verneed_aux (elf_dynamic_version_need_aux_t * n)
+{
+#define _(t,f) n->f = clib_byte_swap_##t (n->f);
+  foreach_elf_dynamic_version_need_aux_field
+#undef _
+}
+
+clib_error_t *
+elf_get_section_by_name (elf_main_t * em, char * section_name, elf_section_t ** result)
+{
+  uword * p;
+
+  p = hash_get_mem (em->section_by_name, section_name);
+  if (! p)
+    return clib_error_return (0, "no such section `%s'", section_name);
+
+  *result = vec_elt_at_index (em->sections, p[0]);
+  return 0;
+}
+
+elf_section_t *
+elf_get_section_by_start_address_no_check (elf_main_t * em, uword start_address)
+{
+  uword * p = hash_get (em->section_by_start_address, start_address);
+  return p ? vec_elt_at_index (em->sections, p[0]) : 0;
+}
+
+clib_error_t *
+elf_get_section_by_start_address (elf_main_t * em, uword start_address, elf_section_t ** result)
+{
+  elf_section_t * s = elf_get_section_by_start_address_no_check (em, start_address);
+  if (! s)
+    return clib_error_return (0, "no section with address 0x%wx", start_address);
+  *result = s;
+  return 0;
+}
+
+static u8 *
+format_elf_section_type (u8 * s, va_list * args)
+{
+  elf_section_type_t type = va_arg (*args, elf_section_type_t);
+  char * t = 0;
+
+  switch (type)
+    {
+#define _(f,i) case ELF_SECTION_##f: t = #f; break;
+      foreach_elf_section_type
+#undef _
+    }
+
+  if (! t)
+    s = format (s, "unknown 0x%x", type);
+  else
+    s = format (s, "%s", t);
+  return s;
+}
+
+static u8 *
+format_elf_section (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  elf_section_t * es = va_arg (*args, elf_section_t *);
+  elf64_section_header_t * h = &es->header;
+
+  if (! h)
+    return format (s, "%=40s%=10s%=20s%=8s%=16s%=16s%=16s",
+                  "Name", "Index", "Type", "Size", "Align", "Address", "File offset");
+
+  s = format (s, "%-40s%10d%=20U%8Lx%16d%16Lx %Lx-%Lx",
+             elf_section_name (em, es),
+             es->index,
+             format_elf_section_type, h->type,
+             h->file_size,
+             h->align,
+             h->exec_address,
+             h->file_offset, h->file_offset + h->file_size);
+
+  if (h->flags != 0)
+    {
+#define _(f,i) \
+  if (h->flags & ELF_SECTION_FLAG_##f) s = format (s, " %s", #f);
+      foreach_elf_section_flag;
+#undef _
+    }
+
+  return s;
+}
+
+static u8 *
+format_elf_segment_type (u8 * s, va_list * args)
+{
+  elf_segment_type_t type = va_arg (*args, elf_segment_type_t);
+  char * t = 0;
+
+  switch (type)
+    {
+#define _(f,i) case ELF_SEGMENT_##f: t = #f; break;
+      foreach_elf_segment_type
+#undef _
+    }
+
+  if (! t)
+    s = format (s, "unknown 0x%x", type);
+  else
+    s = format (s, "%s", t);
+  return s;
+}
+
+static u8 *
+format_elf_segment (u8 * s, va_list * args)
+{
+  elf_segment_t * es = va_arg (*args, elf_segment_t *);
+  elf64_segment_header_t * h = &es->header;
+
+  if (! h)
+    return format (s, "%=16s%=16s%=16s%=16s",
+                  "Type", "Virt. Address", "Phys. Address", "Size");
+
+  s = format (s, "%=16U%16Lx%16Lx%16Lx%16Lx",
+             format_elf_segment_type, h->type,
+             h->virtual_address,
+             h->physical_address,
+             h->memory_size,
+             h->file_offset);
+
+  if (h->flags != 0)
+    {
+#define _(f,i) \
+  if (h->flags & ELF_SEGMENT_FLAG_##f) s = format (s, " %s", #f);
+      foreach_elf_segment_flag;
+#undef _
+    }
+
+  return s;
+}
+
+static u8 *
+format_elf_symbol_binding_and_type (u8 * s, va_list * args)
+{
+  int bt = va_arg (*args, int);
+  int b, t;
+  char * type_string = 0;
+  char * binding_string = 0;
+  
+  switch ((b = ((bt >> 4) & 0xf)))
+    {
+#define _(f,n) case n: binding_string = #f; break;
+      foreach_elf_symbol_binding;
+#undef _
+    default: break;
+    }
+
+  switch ((t = ((bt >> 0) & 0xf)))
+    {
+#define _(f,n) case n: type_string = #f; break;
+      foreach_elf_symbol_type;
+#undef _
+    default: break;
+    }
+
+  if (binding_string)
+    s = format (s, "%s", binding_string);
+  else
+    s = format (s, "binding 0x%x", b);
+
+  if (type_string)
+    s = format (s, " %s", type_string);
+  else
+    s = format (s, " type 0x%x", t);
+
+  return s;
+}
+
+static u8 *
+format_elf_symbol_visibility (u8 * s, va_list * args)
+{
+  int visibility = va_arg (*args, int);
+  char * t = 0;
+
+  switch (visibility)
+    {
+#define _(f,n) case n: t = #f; break;
+      foreach_elf_symbol_visibility
+#undef _
+    }
+
+  if (t)
+    return format (s, "%s", t);
+  else
+    return format (s, "unknown 0x%x", visibility);
+}
+
+static u8 *
+format_elf_symbol_section_name (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  int si = va_arg (*args, int);
+  char * t = 0;
+
+  if (si < vec_len (em->sections))
+    {
+      elf_section_t * es = vec_elt_at_index (em->sections, si);
+      return format (s, "%s", elf_section_name (em, es));
+    }
+
+  if (si >= ELF_SYMBOL_SECTION_RESERVED_LO
+      && si <= ELF_SYMBOL_SECTION_RESERVED_HI)
+    {
+      switch (si)
+       {
+#define _(f,n) case n: t = #f; break;
+         foreach_elf_symbol_reserved_section_index
+#undef _
+       default:
+         break;
+       }
+    }
+
+  if (t)
+    return format (s, "%s", t);
+  else
+    return format (s, "unknown 0x%x", si);
+}
+
+u8 * format_elf_symbol (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  elf_symbol_table_t * t = va_arg (*args, elf_symbol_table_t *);
+  elf64_symbol_t * sym = va_arg (*args, elf64_symbol_t *);
+
+  if (! sym)
+    return format (s, "%=32s%=16s%=16s%=16s%=16s%=16s",
+                  "Symbol", "Size", "Value", "Type", "Visibility", "Section");
+
+  s = format (s, "%-32s%16Ld%16Lx%=16U%=16U%U",
+             elf_symbol_name (t, sym),
+             sym->size, sym->value,
+             format_elf_symbol_binding_and_type, sym->binding_and_type,
+             format_elf_symbol_visibility, sym->visibility,
+             format_elf_symbol_section_name, em, sym->section_index);
+
+  return s;
+}
+
+static u8 *
+format_elf_relocation_type (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  int type = va_arg (*args, int);
+  char * t = 0;
+
+  switch (em->first_header.architecture)
+    {
+#define _(f,i) [i] = #f,
+
+    case ELF_ARCH_X86_64:
+      {
+       static char * tab[] = {
+         foreach_elf_x86_64_relocation_type
+       };
+
+#undef _
+       if (type < ARRAY_LEN (tab))
+         t = tab[type];
+       break;
+      }
+
+    default:
+      break;
+    }
+
+  if (! t)
+    s = format (s, "0x%02x", type);
+  else
+    s = format (s, "%s", t);
+
+  return s;
+}
+
+static u8 *
+format_elf_relocation (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  elf_relocation_with_addend_t * r = va_arg (*args, elf_relocation_with_addend_t *);
+  elf_symbol_table_t * t;
+  elf64_symbol_t * sym;
+
+  if (! r)
+    return format (s, "%=16s%=16s%=16s", "Address", "Type", "Symbol");
+
+  t = vec_elt_at_index (em->symbol_tables, 0);
+  sym = vec_elt_at_index (t->symbols, r->symbol_and_type >> 32);
+
+  s = format (s, "%16Lx%16U",
+             r->address,
+             format_elf_relocation_type, em, r->symbol_and_type & 0xff);
+
+  if (sym->section_index != 0)
+    {
+      elf_section_t * es;
+      es = vec_elt_at_index (em->sections, sym->section_index);
+      s = format (s, " (section %s)", elf_section_name (em, es));
+    }
+
+  if (sym->name != 0)
+    s = format (s, " %s", elf_symbol_name (t, sym));
+
+  {
+    i64 a = r->addend;
+    if (a != 0)
+      s = format (s, " %c 0x%Lx",
+                 a > 0 ? '+' : '-',
+                 a > 0 ? a : -a);
+  }
+
+  return s;
+}
+
+static u8 *
+format_elf_dynamic_entry_type (u8 * s, va_list * args)
+{
+  u32 type = va_arg (*args, u32);
+  char * t = 0;
+  switch (type)
+    {
+#define _(f,n) case n: t = #f; break;
+      foreach_elf_dynamic_entry_type;
+#undef _
+    default: break;
+    }
+  if (t)
+    return format (s, "%s", t);
+  else
+    return format (s, "unknown 0x%x", type);
+}
+
+static u8 *
+format_elf_dynamic_entry (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  elf64_dynamic_entry_t * e = va_arg (*args, elf64_dynamic_entry_t *);
+
+  if (! e)
+    return format (s, "%=40s%=16s", "Type", "Data");
+
+  s = format (s, "%=40U",
+             format_elf_dynamic_entry_type, (u32) e->type);
+  switch (e->type)
+    {
+    case ELF_DYNAMIC_ENTRY_NEEDED_LIBRARY:
+    case ELF_DYNAMIC_ENTRY_RPATH:
+    case ELF_DYNAMIC_ENTRY_RUN_PATH:
+      s = format (s, "%s", em->dynamic_string_table + e->data);
+      break;
+
+    case ELF_DYNAMIC_ENTRY_INIT_FUNCTION:
+    case ELF_DYNAMIC_ENTRY_FINI_FUNCTION:
+    case ELF_DYNAMIC_ENTRY_SYMBOL_HASH:
+    case ELF_DYNAMIC_ENTRY_GNU_HASH:
+    case ELF_DYNAMIC_ENTRY_STRING_TABLE:
+    case ELF_DYNAMIC_ENTRY_SYMBOL_TABLE:
+    case ELF_DYNAMIC_ENTRY_PLT_GOT:
+    case ELF_DYNAMIC_ENTRY_PLT_RELOCATION_ADDRESS:
+    case ELF_DYNAMIC_ENTRY_RELA_ADDRESS:
+    case ELF_DYNAMIC_ENTRY_VERSION_NEED:
+    case ELF_DYNAMIC_ENTRY_VERSYM:
+      {
+       elf_section_t * es = elf_get_section_by_start_address_no_check (em, e->data);
+       if (es)
+         s = format (s, "section %s", elf_section_name (em, es));
+       else
+         s = format (s, "0x%Lx", e->data);
+       break;
+      }
+
+    default:
+      s = format (s, "0x%Lx", e->data);
+      break;
+    }
+
+  return s;
+}
+
+static u8 * format_elf_architecture (u8 * s, va_list * args)
+{
+  int a = va_arg (*args, int);
+  char * t;
+
+  switch (a)
+    {
+#define _(f,n) case n: t = #f; break;
+      foreach_elf_architecture;
+#undef _
+    default:
+      return format (s, "unknown 0x%x", a);
+    }
+
+  return format (s, "%s", t);
+}
+
+static u8 * format_elf_abi (u8 * s, va_list * args)
+{
+  int a = va_arg (*args, int);
+  char * t;
+
+  switch (a)
+    {
+#define _(f,n) case n: t = #f; break;
+      foreach_elf_abi;
+#undef _
+    default:
+      return format (s, "unknown 0x%x", a);
+    }
+
+  return format (s, "%s", t);
+}
+
+static u8 * format_elf_file_class (u8 * s, va_list * args)
+{
+  int a = va_arg (*args, int);
+  char * t;
+
+  switch (a)
+    {
+#define _(f) case ELF_##f: t = #f; break;
+      foreach_elf_file_class;
+#undef _
+    default:
+      return format (s, "unknown 0x%x", a);
+    }
+
+  return format (s, "%s", t);
+}
+
+static u8 * format_elf_file_type (u8 * s, va_list * args)
+{
+  int a = va_arg (*args, int);
+  char * t;
+
+  if (a >= ELF_ARCH_SPECIFIC_LO && a <= ELF_ARCH_SPECIFIC_HI)
+    return format (s, "arch-specific 0x%x", a - ELF_ARCH_SPECIFIC_LO);
+
+  if (a >= ELF_OS_SPECIFIC_LO && a <= ELF_OS_SPECIFIC_HI)
+    return format (s, "os-specific 0x%x", a - ELF_OS_SPECIFIC_LO);
+
+  switch (a)
+    {
+#define _(f,n) case n: t = #f; break;
+      foreach_elf_file_type;
+#undef _
+    default:
+      return format (s, "unknown 0x%x", a);
+    }
+
+  return format (s, "%s", t);
+}
+
+static u8 * format_elf_data_encoding (u8 * s, va_list * args)
+{
+  int a = va_arg (*args, int);
+  char * t;
+
+  switch (a)
+    {
+#define _(f) case ELF_##f: t = #f; break;
+      foreach_elf_data_encoding;
+#undef _
+    default:
+      return format (s, "unknown 0x%x", a);
+    }
+
+  return format (s, "%s", t);
+}
+
+static int elf_section_offset_compare (void *a1, void *a2)
+{
+  elf_section_t *s1 = a1;
+  elf_section_t *s2 = a2;
+  
+  return ((i64)s1->header.file_offset - (i64)s2->header.file_offset);
+}
+
+static int elf_segment_va_compare (void *a1, void *a2)
+{
+  elf_segment_t *s1 = a1;
+  elf_segment_t *s2 = a2;
+  
+  return ((i64)s1->header.virtual_address - (i64)s2->header.virtual_address);
+}
+
+u8 *
+format_elf_main (u8 * s, va_list * args)
+{
+  elf_main_t * em = va_arg (*args, elf_main_t *);
+  u32 verbose = va_arg (*args, u32);
+  elf64_file_header_t * fh = &em->file_header;
+
+  s = format (s, "File header: machine: %U, file type/class %U/%U, data-encoding: %U, abi: %U version %d\n",
+             format_elf_architecture, em->first_header.architecture,
+             format_elf_file_type, em->first_header.file_type,
+             format_elf_file_class, em->first_header.file_class,
+             format_elf_data_encoding, em->first_header.data_encoding,
+             format_elf_abi, em->first_header.abi, em->first_header.abi_version);
+
+  s = format (s, "  entry 0x%Lx, arch-flags 0x%x",
+             em->file_header.entry_point, em->file_header.flags);
+
+  if (em->interpreter)
+    s = format (s, "\n  interpreter: %s", em->interpreter);
+
+  {
+    elf_section_t * h, * copy;
+
+    copy = 0;
+    vec_foreach (h, em->sections)
+      if (h->header.type != ~0)
+       vec_add1 (copy, h[0]);
+
+    vec_sort_with_function (copy, elf_section_offset_compare);
+
+    s = format (s, "\nSections %d at file offset 0x%Lx-0x%Lx:\n",
+               fh->section_header_count,
+               fh->section_header_file_offset,
+               fh->section_header_file_offset + fh->section_header_count * fh->section_header_size);
+    s = format (s, "%U\n", format_elf_section, em, 0);
+    vec_foreach (h, copy)
+      s = format (s, "%U\n", format_elf_section, em, h);
+
+    vec_free (copy);
+  }
+
+  {
+    elf_segment_t * h, * copy;
+
+    copy = 0;
+    vec_foreach (h, em->segments)
+      if (h->header.type != ELF_SEGMENT_UNUSED && h->header.type != ~0)
+       vec_add1 (copy, h[0]);
+
+    /* Sort segments by address. */
+    vec_sort_with_function (copy, elf_segment_va_compare);
+
+    s = format (s, "\nSegments: %d at file offset 0x%Lx-0x%Lx:\n",
+               fh->segment_header_count,
+               fh->segment_header_file_offset,
+               fh->segment_header_file_offset + fh->segment_header_count * fh->segment_header_size);
+               
+    s = format (s, "%U\n", format_elf_segment, 0);
+    vec_foreach (h, copy)
+      s = format (s, "%U\n", format_elf_segment, h);
+
+    vec_free (copy);
+  }
+
+  if ((verbose & FORMAT_ELF_MAIN_SYMBOLS)
+      && vec_len (em->symbol_tables) > 0)
+    {
+      elf_symbol_table_t * t;
+      elf64_symbol_t * sym;
+      elf_section_t * es;
+
+      vec_foreach (t, em->symbol_tables)
+       {
+         es = vec_elt_at_index (em->sections, t->section_index);
+         s = format (s, "\nSymbols for section %s:\n", elf_section_name (em, es));
+
+         s = format (s, "%U\n", format_elf_symbol, em, 0, 0);
+         vec_foreach (sym, t->symbols)
+           s = format (s, "%U\n", format_elf_symbol, em, t, sym);
+       }
+    }
+
+  if ((verbose & FORMAT_ELF_MAIN_RELOCATIONS)
+      && vec_len (em->relocation_tables) > 0)
+    {
+      elf_relocation_table_t * t;
+      elf_relocation_with_addend_t * r;
+      elf_section_t * es;
+
+      vec_foreach (t, em->relocation_tables)
+       {
+         es = vec_elt_at_index (em->sections, t->section_index);
+         r = t->relocations;
+         s = format (s, "\nRelocations for section %s:\n",
+                     elf_section_name (em, es));
+
+         s = format (s, "%U\n", format_elf_relocation, em, 0);
+         vec_foreach (r, t->relocations)
+           {
+             s = format (s, "%U\n",
+                         format_elf_relocation, em, r);
+           }
+       }
+    }
+
+  if ((verbose & FORMAT_ELF_MAIN_DYNAMIC)
+      && vec_len (em->dynamic_entries) > 0)
+    {
+      elf64_dynamic_entry_t * es, * e;
+      s = format (s, "\nDynamic linker information:\n");
+      es = vec_dup (em->dynamic_entries);
+      s = format (s, "%U\n", format_elf_dynamic_entry, em, 0);
+      vec_foreach (e, es)
+       s = format (s, "%U\n", format_elf_dynamic_entry, em, e);
+    }
+
+  return s;
+}
+
+static void
+elf_parse_segments (elf_main_t * em, void * data)
+{
+  void * d = data + em->file_header.segment_header_file_offset;
+  uword n = em->file_header.segment_header_count;
+  uword i;
+
+  vec_resize (em->segments, n);
+
+  for (i = 0; i < n; i++)
+    {
+      em->segments[i].index = i;
+
+      if (em->first_header.file_class == ELF_64BIT)
+       {
+         elf64_segment_header_t * h = d;
+#define _(t,f) em->segments[i].header.f = elf_swap_##t (em, h->f);
+         foreach_elf64_segment_header
+#undef _
+         d = (h + 1);
+       }
+      else
+       {
+         elf32_segment_header_t * h = d;
+#define _(t,f) em->segments[i].header.f = elf_swap_##t (em, h->f);
+         foreach_elf32_segment_header
+#undef _
+         d = (h + 1);
+       }
+    }
+}
+
+static void
+elf_parse_sections (elf_main_t * em, void * data)
+{
+  elf64_file_header_t * fh = &em->file_header;
+  elf_section_t * s;
+  void * d = data + fh->section_header_file_offset;
+  uword n = fh->section_header_count;
+  uword i;
+
+  vec_resize (em->sections, n);
+
+  for (i = 0; i < n; i++)
+    {
+      s = em->sections + i;
+
+      s->index = i;
+
+      if (em->first_header.file_class == ELF_64BIT)
+       {
+         elf64_section_header_t * h = d;
+#define _(t,f) em->sections[i].header.f = elf_swap_##t (em, h->f);
+         foreach_elf64_section_header
+#undef _
+         d = (h + 1);
+       }
+      else
+       {
+         elf32_section_header_t * h = d;
+#define _(t,f) em->sections[i].header.f = elf_swap_##t (em, h->f);
+         foreach_elf32_section_header
+#undef _
+         d = (h + 1);
+       }
+
+      if (s->header.type != ELF_SECTION_NO_BITS)
+       vec_add (s->contents, data + s->header.file_offset, s->header.file_size);
+    }
+
+  s = vec_elt_at_index (em->sections, fh->section_header_string_table_index);
+
+  em->section_by_name
+    = hash_create_string (/* # elts */ vec_len (em->sections),
+                         /* sizeof of value */ sizeof (uword));
+
+  vec_foreach (s, em->sections)
+    {
+      hash_set_mem (em->section_by_name,
+                   elf_section_name (em, s),
+                   s - em->sections);
+      hash_set (em->section_by_start_address,
+               s->header.exec_address,
+               s - em->sections);
+    }
+}
+
+static void
+add_symbol_table (elf_main_t * em, elf_section_t * s)
+{
+  elf_symbol_table_t * tab;
+  elf32_symbol_t * sym32;
+  elf64_symbol_t * sym64;
+  uword i;
+
+  if (s->header.type == ELF_SECTION_DYNAMIC_SYMBOL_TABLE)
+    em->dynamic_symbol_table_index = vec_len (em->symbol_tables);
+
+  vec_add2 (em->symbol_tables, tab, 1);
+
+  tab->section_index = s->index;
+
+  if (em->first_header.file_class == ELF_64BIT)
+    {
+      tab->symbols = elf_get_section_contents (em, s - em->sections, sizeof (tab->symbols[0]));
+      for (i = 0; i < vec_len (tab->symbols); i++)
+       {
+#define _(t,f) tab->symbols[i].f = elf_swap_##t (em, tab->symbols[i].f);
+         foreach_elf64_symbol_header;
+#undef _
+       }
+    }
+  else
+    {
+      sym32 = elf_get_section_contents (em, s - em->sections, sizeof (sym32[0]));
+      vec_clone (tab->symbols, sym32);
+      for (i = 0; i < vec_len (tab->symbols); i++)
+       {
+#define _(t,f) tab->symbols[i].f = elf_swap_##t (em, sym32[i].f);
+         foreach_elf32_symbol_header;
+#undef _
+       }
+    }
+
+  if (s->header.link == 0)
+    return;
+
+  tab->string_table =
+    elf_get_section_contents (em, s->header.link, sizeof (tab->string_table[0]));
+  tab->symbol_by_name
+    = hash_create_string (/* # elts */ vec_len (tab->symbols),
+                         /* sizeof of value */ sizeof (uword));
+
+  vec_foreach (sym64, tab->symbols)
+    {
+      if (sym64->name != 0)
+       hash_set_mem (tab->symbol_by_name,
+                     tab->string_table + sym64->name,
+                     sym64 - tab->symbols);
+    }
+}
+
+static void
+add_relocation_table (elf_main_t * em, elf_section_t * s)
+{
+  uword has_addend = s->header.type == ELF_SECTION_RELOCATION_ADD;
+  elf_relocation_table_t * t;
+  uword i;
+
+  vec_add2 (em->relocation_tables, t, 1);
+  t->section_index = s - em->sections;
+
+  if (em->first_header.file_class == ELF_64BIT)
+    {
+      elf64_relocation_t * r, * rs;
+
+      rs = elf_get_section_contents (em, t->section_index, 
+                                    sizeof (rs[0]) + has_addend * sizeof (rs->addend[0]));
+
+      if (em->need_byte_swap)
+       {
+         r = rs;
+         for (i = 0; i < vec_len (r); i++)
+           {
+             r->address = elf_swap_u64 (em, r->address);
+             r->symbol_and_type = elf_swap_u32 (em, r->symbol_and_type);
+             if (has_addend)
+               r->addend[0] = elf_swap_u64 (em, r->addend[0]);
+             r = elf_relocation_next (r, s->header.type);
+           }
+       }
+
+      vec_resize (t->relocations, vec_len (rs));
+      memcpy (t->relocations, rs, vec_bytes (t->relocations));
+      vec_free (rs);
+    }
+  else
+    {
+      elf_relocation_with_addend_t * r;
+      elf32_relocation_t * r32, * r32s;
+
+      r32s = elf_get_section_contents (em, t->section_index, 
+                                      sizeof (r32s[0]) + has_addend * sizeof (r32s->addend[0]));
+      vec_resize (t->relocations, vec_len (r32s));
+
+      r32 = r32s;
+      vec_foreach (r, t->relocations)
+       {
+         r->address = elf_swap_u32 (em, r32->address);
+         r->symbol_and_type = elf_swap_u32 (em, r->symbol_and_type);
+         r->addend = has_addend ? elf_swap_u32 (em, r32->addend[0]) : 0;
+         r32 = elf_relocation_next (r32, s->header.type);
+       }
+
+      vec_free (r32s);
+    }
+}
+
+void elf_parse_symbols (elf_main_t * em)
+{
+  elf_section_t * s;
+
+  /* No need to parse symbols twice. */
+  if (em->parsed_symbols)
+    return;
+  em->parsed_symbols = 1;
+
+  vec_foreach (s, em->sections)
+    {
+      switch (s->header.type)
+       {
+       case ELF_SECTION_SYMBOL_TABLE:
+       case ELF_SECTION_DYNAMIC_SYMBOL_TABLE:
+         add_symbol_table (em, s);
+         break;
+
+       case ELF_SECTION_RELOCATION_ADD:
+       case ELF_SECTION_RELOCATION:
+         add_relocation_table (em, s);
+         break;
+
+       default:
+         break;
+       }
+    }
+}
+
+void elf_set_dynamic_entries (elf_main_t * em)
+{
+  uword i;
+
+  /* Start address for sections may have changed. */
+  {
+    elf64_dynamic_entry_t * e;
+
+    vec_foreach (e, em->dynamic_entries)
+      {
+       switch (e->type)
+         {
+         case ELF_DYNAMIC_ENTRY_INIT_FUNCTION:
+         case ELF_DYNAMIC_ENTRY_FINI_FUNCTION:
+         case ELF_DYNAMIC_ENTRY_SYMBOL_HASH:
+         case ELF_DYNAMIC_ENTRY_GNU_HASH:
+         case ELF_DYNAMIC_ENTRY_STRING_TABLE:
+         case ELF_DYNAMIC_ENTRY_SYMBOL_TABLE:
+         case ELF_DYNAMIC_ENTRY_PLT_GOT:
+         case ELF_DYNAMIC_ENTRY_PLT_RELOCATION_ADDRESS:
+         case ELF_DYNAMIC_ENTRY_RELA_ADDRESS:
+         case ELF_DYNAMIC_ENTRY_VERSION_NEED:
+         case ELF_DYNAMIC_ENTRY_VERSYM:
+           {
+             elf_section_t * es = elf_get_section_by_start_address_no_check (em, e->data);
+             /* If section is not found just leave e->data alone. */
+             if (es)
+               e->data = es->header.exec_address;
+             break;
+           }
+
+         default:
+           break;
+         }
+      }
+  }
+
+  if (em->first_header.file_class == ELF_64BIT)
+    {
+      elf64_dynamic_entry_t * e, * es;
+
+      es = em->dynamic_entries;
+      if (em->need_byte_swap)
+       {
+         es = vec_dup (es);
+         vec_foreach (e, es)
+           {
+             e->type = elf_swap_u64 (em, e->type);
+             e->data = elf_swap_u64 (em, e->data);
+           }
+       }
+
+      elf_set_section_contents (em, em->dynamic_section_index, es, vec_bytes (es));
+      if (es != em->dynamic_entries)
+       vec_free (es);
+    }
+  else
+    {
+      elf32_dynamic_entry_t * es;
+
+      vec_clone (es, em->dynamic_entries);
+      if (em->need_byte_swap)
+       {
+         for (i = 0; i < vec_len (es); i++)
+           {
+             es[i].type = elf_swap_u32 (em, em->dynamic_entries[i].type);
+             es[i].data = elf_swap_u32 (em, em->dynamic_entries[i].data);
+           }
+       }
+
+      elf_set_section_contents (em, em->dynamic_section_index, es, vec_bytes (es));
+      vec_free (es);
+    }
+}
+
+clib_error_t *
+elf_parse (elf_main_t * em,
+          void * data,
+          uword data_bytes)
+{
+  elf_first_header_t * h = data;
+  elf64_file_header_t * fh = &em->file_header;
+  clib_error_t * error = 0;
+
+  {
+    char * save = em->file_name;
+    memset (em, 0, sizeof (em[0]));
+    em->file_name = save;
+  }
+
+  em->first_header = h[0];
+  em->need_byte_swap = 
+      CLIB_ARCH_IS_BIG_ENDIAN != (h->data_encoding == ELF_TWOS_COMPLEMENT_BIG_ENDIAN);
+  elf_swap_first_header (em, &em->first_header);
+
+  if (! (h->magic[0] == 0x7f
+        && h->magic[1] == 'E'
+        && h->magic[2] == 'L'
+        && h->magic[3] == 'F'))
+    return clib_error_return (0, "`%s': bad magic", em->file_name);
+
+  if (h->file_class == ELF_64BIT)
+    {
+      elf64_file_header_t * h64 = (void *) (h + 1);
+#define _(t,f) fh->f = elf_swap_##t (em, h64->f);
+      foreach_elf64_file_header
+#undef _
+    }
+  else
+    {
+      elf32_file_header_t * h32 = (void *) (h + 1);
+
+#define _(t,f) fh->f = elf_swap_##t (em, h32->f);
+      foreach_elf32_file_header
+#undef _
+    }
+
+  elf_parse_segments (em, data);
+  elf_parse_sections (em, data);
+
+  /* Figure which sections are contained in each segment. */
+  {
+    elf_segment_t * g;
+    elf_section_t * s;
+    vec_foreach (g, em->segments)
+      {
+       u64 g_lo, g_hi;
+       u64 s_lo, s_hi;
+
+       if (g->header.memory_size == 0)
+         continue;
+
+       g_lo = g->header.virtual_address;
+       g_hi = g_lo + g->header.memory_size;
+
+       vec_foreach (s, em->sections)
+         {
+           s_lo = s->header.exec_address;
+           s_hi = s_lo + s->header.file_size;
+
+           if (s_lo >= g_lo && s_hi <= g_hi)
+             {
+               g->section_index_bitmap = clib_bitmap_ori (g->section_index_bitmap, s->index);
+               s->segment_index_bitmap = clib_bitmap_ori (s->segment_index_bitmap, g->index);
+             }
+         }
+      }
+  }
+
+  return error;
+}
+
+#ifdef CLIB_UNIX
+
+static void
+add_dynamic_entries (elf_main_t * em, elf_section_t * s)
+{
+  uword i;
+
+  /* Can't have more than one dynamic section. */
+  ASSERT (em->dynamic_section_index == 0);
+  em->dynamic_section_index = s->index;
+
+  if (em->first_header.file_class == ELF_64BIT)
+    {
+      elf64_dynamic_entry_t * e;
+
+      e = elf_get_section_contents (em, s - em->sections, sizeof (e[0]));
+      if (em->need_byte_swap)
+       for (i = 0; i < vec_len (e); i++)
+         {
+           e[i].type = elf_swap_u64 (em, e[i].type);
+           e[i].data = elf_swap_u64 (em, e[i].data);
+         }
+
+      em->dynamic_entries = e;
+    }
+  else
+    {
+      elf32_dynamic_entry_t * e;
+
+      e = elf_get_section_contents (em, s - em->sections, sizeof (e[0]));
+      vec_clone (em->dynamic_entries, e);
+      if (em->need_byte_swap)
+       for (i = 0; i < vec_len (e); i++)
+         {
+           em->dynamic_entries[i].type = elf_swap_u32 (em, e[i].type);
+           em->dynamic_entries[i].data = elf_swap_u32 (em, e[i].data);
+         }
+
+      vec_free (e);
+    }
+}
+
+static void byte_swap_verneed (elf_main_t * em,
+                              elf_dynamic_version_need_union_t * vus)
+{
+  uword * entries_swapped = 0;
+  uword i, j;
+
+  for (i = 0; i < vec_len (vus); i++)
+    {
+      elf_dynamic_version_need_union_t * n = vec_elt_at_index (vus, i);
+      elf_dynamic_version_need_union_t * a;
+
+      if (clib_bitmap_get (entries_swapped, i))
+       continue;
+
+      elf_swap_verneed (&n->need);
+      entries_swapped = clib_bitmap_set (entries_swapped, i, 1);
+
+      if (n->need.first_aux_offset != 0)
+       {
+         ASSERT (n->need.first_aux_offset % sizeof (n[0]) == 0);
+         j = i + (n->need.first_aux_offset / sizeof (n[0]));
+         while (1)
+           {
+             a = vec_elt_at_index (vus, j);
+             if (! clib_bitmap_get (entries_swapped, j))
+               {
+                 entries_swapped = clib_bitmap_set (entries_swapped, j, 1);
+                 elf_swap_verneed_aux (&a->aux);
+               }
+             if (a->aux.next_offset == 0)
+               break;
+             ASSERT (a->aux.next_offset % sizeof (a->aux) == 0);
+             j += (a->aux.next_offset / sizeof (a->aux));
+           } 
+       }
+    }
+
+  clib_bitmap_free (entries_swapped);
+}
+
+static void
+set_dynamic_verneed (elf_main_t * em)
+{
+  elf_dynamic_version_need_union_t * vus = em->verneed;
+
+  if (em->need_byte_swap)
+    {
+      vus = vec_dup (vus);
+      byte_swap_verneed (em, vus);
+    }
+
+  elf_set_section_contents (em, em->verneed_section_index, vus, vec_bytes (vus));
+  if (vus != em->verneed)
+    vec_free (vus);
+}
+
+static void
+set_symbol_table (elf_main_t * em, u32 table_index)
+{
+  elf_symbol_table_t * tab = vec_elt_at_index (em->symbol_tables, table_index);
+
+  if (em->first_header.file_class == ELF_64BIT)
+    {
+      elf64_symbol_t * s, * syms;
+      
+      syms = vec_dup (tab->symbols);
+      vec_foreach (s, syms)
+       {
+#define _(t,f) s->f = elf_swap_##t (em, s->f);
+         foreach_elf64_symbol_header;
+#undef _
+       }
+
+      elf_set_section_contents (em, tab->section_index,
+                               syms, vec_bytes (syms));
+    }
+  else
+    {
+      elf32_symbol_t * syms;
+      uword i;
+      vec_clone (syms, tab->symbols);
+      for (i = 0; i < vec_len (tab->symbols); i++)
+       {
+#define _(t,f) syms[i].f = elf_swap_##t (em, tab->symbols[i].f);
+         foreach_elf32_symbol_header;
+#undef _
+       }
+
+      elf_set_section_contents (em, tab->section_index,
+                               syms, vec_bytes (syms));
+    }
+}
+
+static char *
+elf_find_interpreter (elf_main_t * em, void * data)
+{
+  elf_segment_t * g;
+  elf_section_t * s;
+  uword * p;
+
+  vec_foreach (g, em->segments)
+    {
+      if (g->header.type == ELF_SEGMENT_INTERP)
+       break;
+    }
+
+  if (g >= vec_end (em->segments))
+    return 0;
+
+  p = hash_get (em->section_by_start_address, g->header.virtual_address);
+  if (! p)
+    return 0;
+
+  s = vec_elt_at_index (em->sections, p[0]);
+  return (char *) vec_dup (s->contents);
+}
+
+static void *
+elf_get_section_contents_with_starting_address (elf_main_t * em,
+                                               uword start_address,
+                                               uword elt_size,
+                                               u32 * section_index_result)
+{
+  elf_section_t * s;
+  clib_error_t * error;
+
+  error = elf_get_section_by_start_address (em, start_address, &s);
+  if (error)
+    {
+      clib_error_report (error);
+      return 0;
+    }
+
+  if (section_index_result)
+    *section_index_result = s->index;
+
+  return elf_get_section_contents (em, s->index, elt_size);
+}
+
+static void elf_parse_dynamic (elf_main_t * em)
+{
+  elf_section_t * s;
+  elf64_dynamic_entry_t * e;
+
+  vec_foreach (s, em->sections)
+    {
+      switch (s->header.type)
+       {
+       case ELF_SECTION_DYNAMIC:
+         add_dynamic_entries (em, s);
+         break;
+
+       default:
+         break;
+       }
+    }
+
+  em->dynamic_string_table_section_index = ~0;
+  em->dynamic_string_table = 0;
+
+  vec_foreach (e, em->dynamic_entries)
+    {
+      switch (e->type)
+       {
+       case ELF_DYNAMIC_ENTRY_STRING_TABLE:
+         ASSERT (vec_len (em->dynamic_string_table) == 0);
+         em->dynamic_string_table
+           = elf_get_section_contents_with_starting_address (em, e->data, sizeof (u8),
+                                                             &em->dynamic_string_table_section_index);
+         break;
+
+       case ELF_DYNAMIC_ENTRY_SYMBOL_TABLE:
+         {
+           elf_section_t * s;
+           clib_error_t * error;
+
+           error = elf_get_section_by_start_address (em, e->data, &s);
+           if (error)
+             {
+               clib_error_report (error);
+               return;
+             }
+
+           em->dynamic_symbol_table_section_index = s - em->sections;
+         }
+         break;
+
+       case ELF_DYNAMIC_ENTRY_VERSYM:
+         em->versym
+           = elf_get_section_contents_with_starting_address (em, e->data, sizeof (em->versym[0]),
+                                                             &em->versym_section_index);
+         if (em->need_byte_swap)
+           {
+             uword i;
+             for (i = 0; i < vec_len (em->versym); i++)
+               em->versym[i] = clib_byte_swap_u16 (em->versym[i]);
+           }
+         break;
+
+       case ELF_DYNAMIC_ENTRY_VERSION_NEED:
+         em->verneed
+           = elf_get_section_contents_with_starting_address (em, e->data, sizeof (em->verneed[0]),
+                                                             &em->verneed_section_index);
+         if (em->need_byte_swap)
+           byte_swap_verneed (em, em->verneed);
+         break;
+
+       default:
+         break;
+       }
+    }
+}
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+clib_error_t * elf_read_file (elf_main_t * em, char * file_name)
+{
+  int fd;
+  struct stat fd_stat;
+  uword mmap_length = 0;
+  void * data = 0;
+  clib_error_t * error = 0;
+
+  elf_main_init (em);
+
+  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;
+    }
+
+  em->file_name = file_name;
+
+  error = elf_parse (em, data, mmap_length);
+  if (error)
+    goto done;
+
+  elf_parse_symbols (em);
+  elf_parse_dynamic (em);
+
+  em->interpreter = elf_find_interpreter (em, data);
+
+  munmap (data, mmap_length);
+  close (fd);
+
+  return /* no error */ 0;
+
+ done:
+  elf_main_free (em);
+  if (fd >= 0)
+    close (fd);
+  if (data)
+    munmap (data, mmap_length);
+  return error;
+}
+
+typedef struct {
+  u8 * new_table;
+
+  u8 * old_table;
+
+  uword * hash;
+} string_table_builder_t;
+
+static u32 string_table_add_name (string_table_builder_t * b, u8 * n)
+{
+  uword * p, i, j, l;
+
+  p = hash_get_mem (b->hash, n);
+  if (p) return p[0];
+
+  l = strlen ((char *) n);
+  i = vec_len (b->new_table);
+  vec_add (b->new_table, n, l + 1);
+
+  for (j = 0; j <= l; j++)
+    {
+      if (j > 0)
+       {
+         p = hash_get_mem (b->hash, n + j);
+
+         /* Sub-string already in table? */
+         if (p)
+           continue;
+       }
+
+      hash_set_mem (b->hash, n + j, i + j);
+    }
+
+  return i;
+}
+
+static u32 string_table_add_name_index (string_table_builder_t * b, u32 index)
+{
+  u8 * n = b->old_table + index;
+  return string_table_add_name (b, n);
+}
+
+static void string_table_init (string_table_builder_t * b, u8 * old_table)
+{
+  memset (b, 0, sizeof (b[0]));
+  b->old_table = old_table;
+  b->hash = hash_create_string (0, sizeof (uword));
+}
+
+static u8 * string_table_done (string_table_builder_t * b)
+{
+  hash_free (b->hash);
+  return b->new_table;
+}
+
+static void layout_sections (elf_main_t * em)
+{
+  elf_section_t * s;
+  u32 n_sections_with_changed_exec_address = 0;
+  u32 * deferred_symbol_and_string_sections = 0;
+  u32 n_deleted_sections = 0;
+  int rebuild = 0;
+
+  /* Re-build section string table (sections may have been deleted). */
+  if (rebuild)
+    {
+      u8 * st = 0;
+
+      vec_foreach (s, em->sections)
+       {
+         u8 * name;
+         if (s->header.type == ~0)
+           continue;
+         name = elf_section_name (em, s);
+         s->header.name = vec_len (st);
+         vec_add (st, name, strlen ((char *) name) + 1);
+       }
+
+      s = vec_elt_at_index (em->sections, em->file_header.section_header_string_table_index);
+
+      vec_free (s->contents);
+      s->contents = st;
+    }
+
+  /* Re-build dynamic string table. */
+  if (rebuild && em->dynamic_string_table_section_index != ~0)
+    {
+      string_table_builder_t b;
+    
+      string_table_init (&b, em->dynamic_string_table);
+
+      /* Add all dynamic symbols. */
+      {
+       elf_symbol_table_t * symtab;
+       elf64_symbol_t * sym;
+
+       symtab = vec_elt_at_index (em->symbol_tables, em->dynamic_symbol_table_index);
+       vec_foreach (sym, symtab->symbols)
+         {
+           u8 * name = elf_symbol_name (symtab, sym);
+           sym->name = string_table_add_name (&b, name);
+         }
+
+       set_symbol_table (em, em->dynamic_symbol_table_index);
+      }
+
+      /* Add all dynamic entries. */
+      {
+       elf64_dynamic_entry_t * e;
+
+       vec_foreach (e, em->dynamic_entries)
+         {
+           switch (e->type)
+             {
+             case ELF_DYNAMIC_ENTRY_NEEDED_LIBRARY:
+             case ELF_DYNAMIC_ENTRY_RPATH:
+             case ELF_DYNAMIC_ENTRY_RUN_PATH:
+               e->data = string_table_add_name_index (&b, e->data);
+               break;
+             }
+         }
+      }
+
+      /* Add all version needs. */
+      if (vec_len (em->verneed) > 0)
+       {
+         elf_dynamic_version_need_union_t * n, * a;
+
+         n = em->verneed;
+         while (1)
+           {
+             n->need.file_name_offset = string_table_add_name_index (&b, n->need.file_name_offset);
+
+             if (n->need.first_aux_offset != 0)
+               {
+                 a = n + n->need.first_aux_offset / sizeof (n[0]);
+                 while (1)
+                   {
+                     a->aux.name = string_table_add_name_index (&b, a->aux.name);
+                     if (a->aux.next_offset == 0)
+                       break;
+                     a += a->aux.next_offset / sizeof (a[0]);
+                   }
+               }
+
+             if (n->need.next_offset == 0)
+               break;
+
+             n += n->need.next_offset / sizeof (n[0]);
+           }
+
+         set_dynamic_verneed (em);
+       }
+
+      s = vec_elt_at_index (em->sections, em->dynamic_string_table_section_index);
+
+      vec_free (s->contents);
+      s->contents = string_table_done (&b);
+    }
+
+  /* Figure file offsets and exec addresses for sections. */
+  {
+    u64 exec_address = 0, file_offset = 0;
+    u64 file_size, align_size;
+
+    vec_foreach (s, em->sections)
+      {
+       /* Ignore deleted and unused sections. */
+       switch (s->header.type)
+         {
+         case ~0:
+           n_deleted_sections++;
+         case ELF_SECTION_UNUSED:
+           continue;
+
+         case ELF_SECTION_STRING_TABLE:
+         case ELF_SECTION_SYMBOL_TABLE:
+           if (! (s->index == em->dynamic_string_table_section_index
+                  || s->index == em->file_header.section_header_string_table_index))
+             {
+               vec_add1 (deferred_symbol_and_string_sections, s->index);
+               continue;
+             }
+           break;
+
+         default:
+           break;
+         }
+
+       exec_address = round_pow2_u64 (exec_address, s->header.align);
+
+        /* Put sections we added at end of file. */
+        if (s->header.file_offset == ~0)
+          s->header.file_offset = file_offset;
+
+       /* Follow gaps in original file. */
+       if (s->header.exec_address > exec_address)
+         {
+           exec_address = s->header.exec_address;
+           file_offset = s->header.file_offset;
+         }
+
+       if (s->header.flags & ELF_SECTION_FLAG_ALLOC)
+         {
+           s->exec_address_change = exec_address - s->header.exec_address;
+           n_sections_with_changed_exec_address += s->exec_address_change != 0;
+           s->header.exec_address = exec_address;
+         }
+
+       if (s->header.type == ELF_SECTION_NO_BITS)
+         file_size = s->header.file_size;
+       else
+         file_size = vec_len (s->contents);
+
+       {
+         u64 align;
+
+         if (s + 1 >= vec_end (em->sections))
+           align = 16;
+         else if (s[1].header.type == ELF_SECTION_NO_BITS)
+           align = 8;
+         else
+           align = s[1].header.align;
+
+         if (s->header.flags & ELF_SECTION_FLAG_ALLOC)
+           {
+             u64 v = round_pow2_u64 (exec_address + file_size, align);
+             align_size = v - exec_address;
+           }
+         else
+           {
+             u64 v = round_pow2_u64 (file_offset + file_size, align);
+             align_size = v - file_offset;
+           }
+       }
+
+       s->header.file_offset = file_offset;
+       s->header.file_size = file_size;
+       s->align_size = align_size;
+
+       if (s->header.type != ELF_SECTION_NO_BITS)
+         file_offset += align_size;
+       exec_address += align_size;
+      }
+
+    /* Section headers go after last section but before symbol/string
+       tables. */
+    {
+      elf64_file_header_t * fh = &em->file_header;
+
+      fh->section_header_file_offset = file_offset;
+      fh->section_header_count = vec_len (em->sections) - n_deleted_sections;
+      file_offset += fh->section_header_count * fh->section_header_size;
+    }
+
+    {
+      int i;
+      for (i = 0; i < vec_len (deferred_symbol_and_string_sections); i++)
+       {
+         s = vec_elt_at_index (em->sections, deferred_symbol_and_string_sections[i]);
+
+         s->header.file_offset = file_offset;
+         s->header.file_size = vec_len (s->contents);
+
+         align_size = round_pow2 (vec_len (s->contents), 16);
+         s->align_size = align_size;
+         file_offset += align_size;
+       }
+      vec_free (deferred_symbol_and_string_sections);
+    }
+  }
+
+  /* Update dynamic entries now that sections have been assigned
+     possibly new addresses. */
+  if (rebuild)
+    elf_set_dynamic_entries (em);
+
+  /* Update segments for changed section addresses. */
+  {
+    elf_segment_t * g;
+    uword si;
+
+    vec_foreach (g, em->segments)
+      {
+       u64 s_lo, s_hi, f_lo = 0;
+       u32 n_sections = 0;
+
+       if (g->header.memory_size == 0)
+         continue;
+
+       s_lo = s_hi = 0;
+       clib_bitmap_foreach (si, g->section_index_bitmap, ({
+         u64 lo, hi;
+
+         s = vec_elt_at_index (em->sections, si);
+         lo = s->header.exec_address;
+         hi = lo + s->align_size;
+         if (n_sections == 0)
+           {
+             s_lo = lo;
+             s_hi = hi;
+             f_lo = s->header.file_offset;
+             n_sections++;
+           }
+         else
+           {
+             if (lo < s_lo)
+               {
+                 s_lo = lo;
+                 f_lo = s->header.file_offset;
+               }
+             if (hi > s_hi)
+               s_hi = hi;
+           }
+       }));
+
+       if (n_sections == 0)
+         continue;
+
+       /* File offset zero includes ELF headers/segment headers.
+          Don't change that. */
+       if (g->header.file_offset == 0
+           && g->header.type == ELF_SEGMENT_LOAD)
+         {
+           s_lo = g->header.virtual_address;
+           f_lo = g->header.file_offset;
+         }
+
+       g->header.virtual_address = s_lo;
+       g->header.physical_address = s_lo;
+       g->header.file_offset = f_lo;
+       g->header.memory_size = s_hi - s_lo;
+      }
+  }
+}
+
+clib_error_t * elf_write_file (elf_main_t * em, char * file_name)
+{
+  int fd;
+  FILE * f;
+  clib_error_t * error = 0;
+
+  fd = open (file_name, O_CREAT | O_RDWR | O_TRUNC, 0755);
+  if (fd < 0)
+    return clib_error_return_unix (0, "open `%s'", file_name);
+
+  f = fdopen (fd, "w");
+
+  /* Section contents may have changed.  So, we need to update
+     stuff to reflect this. */
+  layout_sections (em);
+
+  /* Write first header. */
+  {
+    elf_first_header_t h = em->first_header;
+
+    elf_swap_first_header (em, &h);
+    if (fwrite (&h, sizeof (h), 1, f) != 1)
+      {
+       error = clib_error_return_unix (0, "write first header");
+       goto error;
+      }
+  }
+
+  /* Write file header. */
+  {
+    elf64_file_header_t h = em->file_header;
+
+    /* Segment headers are after first header. */
+    h.segment_header_file_offset = sizeof (elf_first_header_t);
+    if (em->first_header.file_class == ELF_64BIT)
+      h.segment_header_file_offset += sizeof (elf64_file_header_t);
+    else
+      h.segment_header_file_offset += sizeof (elf32_file_header_t);
+
+    if (em->first_header.file_class == ELF_64BIT)
+      {
+#define _(t,field) h.field = elf_swap_##t (em, h.field);
+       foreach_elf64_file_header;
+#undef _
+
+      if (fwrite (&h, sizeof (h), 1, f) != 1)
+       {
+         error = clib_error_return_unix (0, "write file header");
+         goto error;
+       }
+    }
+  else
+    {
+      elf32_file_header_t h32;
+
+#define _(t,field) h32.field = elf_swap_##t (em, h.field);
+      foreach_elf32_file_header;
+#undef _
+
+      if (fwrite (&h32, sizeof (h32), 1, f) != 1)
+       {
+         error = clib_error_return_unix (0, "write file header");
+         goto error;
+       }
+    }
+  }
+
+  /* Write segment headers. */
+  {
+    elf_segment_t * s;
+
+    vec_foreach (s, em->segments)
+      {
+       elf64_segment_header_t h;
+
+       if (s->header.type == ~0)
+         continue;
+
+       h = s->header;
+
+       if (em->first_header.file_class == ELF_64BIT)
+         {
+#define _(t,field) h.field = elf_swap_##t (em, h.field);
+           foreach_elf64_segment_header;
+#undef _
+           
+           if (fwrite (&h, sizeof (h), 1, f) != 1)
+             {
+               error = clib_error_return_unix (0, "write segment header %U", format_elf_segment, em, s);
+               goto error;
+             }
+         }
+       else
+         {
+           elf32_segment_header_t h32;
+
+#define _(t,field) h32.field = elf_swap_##t (em, h.field);
+           foreach_elf32_segment_header;
+#undef _
+
+           if (fwrite (&h32, sizeof (h32), 1, f) != 1)
+             {
+               error = clib_error_return_unix (0, "write segment header %U", format_elf_segment, em, s);
+               goto error;
+             }
+         }
+      }
+  }
+
+  /* Write contents for all sections. */
+  {
+    elf_section_t * s;
+
+    vec_foreach (s, em->sections)
+      {
+       if (s->header.file_size == 0)
+         continue;
+
+       if (fseek (f, s->header.file_offset, SEEK_SET) < 0)
+         return clib_error_return_unix (0, "fseek 0x%Lx", s->header.file_offset);
+
+       if (s->header.type == ELF_SECTION_NO_BITS)
+         /* don't write for .bss sections */;
+       else if (fwrite (s->contents, vec_len (s->contents), 1, f) != 1)
+         {
+           error = clib_error_return_unix (0, "write %s section contents", elf_section_name (em, s));
+           goto error;
+         }
+      }
+
+    /* Finally write section headers. */
+    if (fseek (f, em->file_header.section_header_file_offset, SEEK_SET) < 0)
+      return clib_error_return_unix (0, "fseek 0x%Lx", em->file_header.section_header_file_offset);
+
+    vec_foreach (s, em->sections)
+      {
+       elf64_section_header_t h;
+
+       if (s->header.type == ~0)
+         continue;
+
+       h = s->header;
+
+       if (em->first_header.file_class == ELF_64BIT)
+         {
+#define _(t,field) h.field = elf_swap_##t (em, h.field);
+           foreach_elf64_section_header;
+#undef _
+           
+           if (fwrite (&h, sizeof (h), 1, f) != 1)
+             {
+               error = clib_error_return_unix (0, "write %s section header", elf_section_name (em, s));
+               goto error;
+             }
+         }
+       else
+         {
+           elf32_section_header_t h32;
+
+#define _(t,field) h32.field = elf_swap_##t (em, h.field);
+           foreach_elf32_section_header;
+#undef _
+
+           if (fwrite (&h32, sizeof (h32), 1, f) != 1)
+             {
+               error = clib_error_return_unix (0, "write %s section header", elf_section_name (em, s));
+               goto error;
+             }
+         }
+      }
+  }
+
+ error:
+  fclose (f);
+  return error;
+}
+
+clib_error_t * elf_delete_named_section (elf_main_t * em, char * section_name)
+{
+  elf_section_t * s;
+  clib_error_t * error;
+
+  error = elf_get_section_by_name (em, section_name, &s);
+  if (error)
+    return error;
+  
+  s->header.type = ~0;
+
+  return 0;
+}
+
+void
+elf_create_section_with_contents (elf_main_t * em,
+                                 char * section_name,
+                                 elf64_section_header_t * header,
+                                 void * contents,
+                                 uword n_content_bytes)
+{
+  elf_section_t * s, * sts;
+  u8 * st, * c;
+  uword * p, is_new_section;
+
+  /* See if section already exists with given name.
+     If so, just replace contents. */
+  is_new_section = 0;
+  if ((p = hash_get_mem (em->section_by_name, section_name)))
+    {
+      s = vec_elt_at_index (em->sections, p[0]);
+      _vec_len (s->contents) = 0;
+      c = s->contents;
+    }
+  else
+    {
+      vec_add2 (em->sections, s, 1);
+      is_new_section = 1;
+      c = 0;
+    }
+
+  sts = vec_elt_at_index (em->sections, em->file_header.section_header_string_table_index);
+  st = sts->contents;
+
+  s->header = header[0];
+
+  s->header.file_offset = ~0;
+  s->header.file_size = n_content_bytes;
+  s->index = s - em->sections;
+
+  /* Add name to string table. */
+  s->header.name = vec_len (st);
+  vec_add (st, section_name, strlen (section_name));
+  vec_add1 (st, 0);
+  sts->contents = st;
+
+  vec_resize (c, n_content_bytes);
+  memcpy (c, contents, n_content_bytes);
+  s->contents = c;
+
+  em->file_header.section_header_count += is_new_section && s->header.type != ~0;
+}
+
+uword elf_delete_segment_with_type (elf_main_t * em, elf_segment_type_t segment_type)
+{
+  uword n_deleted = 0;
+  elf_segment_t * s;
+
+  vec_foreach (s, em->segments)
+    if (s->header.type == segment_type)
+      {
+       s->header.type = ~0;
+       n_deleted += 1;
+      }
+
+  ASSERT (em->file_header.segment_header_count >= n_deleted);
+  em->file_header.segment_header_count -= n_deleted;
+
+  return n_deleted;
+}
+
+#endif /* CLIB_UNIX */