/* * 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: */