| /* |
| * 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_export 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 + |
| (u64) 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, |
| (u64) fh->segment_header_file_offset + |
| (u64) fh->segment_header_count * |
| (u64) 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)); |
| clib_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; |
| clib_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) __attribute__ ((unused)); |
| 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) __attribute__ ((unused)); |
| 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 = 0; |
| 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 = 0; |
| 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_export 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; |
| } |
| |
| CLIB_MEM_UNPOISON (data, mmap_length); |
| |
| 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) |
| __attribute__ ((unused)); |
| 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) |
| __attribute__ ((unused)); |
| static void |
| string_table_init (string_table_builder_t * b, u8 * old_table) |
| { |
| clib_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) |
| __attribute__ ((unused)); |
| 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; |
| /* note: rebuild is always zero. Intent lost in the sands of time */ |
| #if 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); |
| } |
| #endif /* dead code */ |
| |
| /* 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 += (u64) 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 0 |
| if (rebuild) |
| elf_set_dynamic_entries (em); |
| #endif |
| |
| /* 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; |
| /* *INDENT-OFF* */ |
| 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; |
| } |
| } |
| /* *INDENT-ON* */ |
| |
| 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) |
| { |
| fclose (f); |
| 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) |
| { |
| fclose (f); |
| 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 = 0; |
| 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); |
| clib_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 */ |
| |
| /* |
| * fd.io coding-style-patch-verification: ON |
| * |
| * Local Variables: |
| * eval: (c-set-style "gnu") |
| * End: |
| */ |