/* Postprocess module symbol versions * * Copyright 2003 Kai Germaschewski * Copyright 2002-2004 Rusty Russell, IBM Corporation * Copyright 2006-2008 Sam Ravnborg * Based in part on module-init-tools/depmod.c,file2alias * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * Usage: modpost vmlinux module1.o module2.o ... */ #define _GNU_SOURCE#include <elf.h>#include <fnmatch.h>#include <stdio.h>#include <ctype.h>#include <string.h>#include <limits.h>#include <stdbool.h>#include <errno.h> #include <hash.h>#include <hashtable.h>#include <list.h>#include <xalloc.h>#include "modpost.h"#include "../../include/linux/license.h" #define MODULE_NS_PREFIX "module:" static bool module_enabled;/* Are we using CONFIG_MODVERSIONS? */static bool modversions;/* Is CONFIG_MODULE_SRCVERSION_ALL set? */static bool all_versions;/* Is CONFIG_BASIC_MODVERSIONS set? */static bool basic_modversions;/* Is CONFIG_EXTENDED_MODVERSIONS set? */static bool extended_modversions;/* If we are modposting external module set to 1 */static bool external_module;/* Only warn about unresolved symbols */static bool warn_unresolved; static int sec_mismatch_count;static bool sec_mismatch_warn_only = true;/* Trim EXPORT_SYMBOLs that are unused by in-tree modules */static bool trim_unused_exports; /* ignore missing files */static bool ignore_missing_files;/* If set to 1, only warn (instead of error) about missing ns imports */static bool allow_missing_ns_imports; static bool error_occurred; static bool extra_warn; bool target_is_big_endian;bool host_is_big_endian; /* * Cut off the warnings when there are too many. This typically occurs when * vmlinux is missing. ('make modules' without building vmlinux.) */#define MAX_UNRESOLVED_REPORTS 10static unsigned int nr_unresolved; /* In kernel, this size is defined in linux/module.h; * here we use Elf_Addr instead of long for covering cross-compile */ #define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) void modpost_log(bool is_error, const char *fmt, ...){ va_list arglist; if (is_error) { fprintf(stderr, "ERROR: "); error_occurred = true; } else { fprintf(stderr, "WARNING: "); } fprintf(stderr, "modpost: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist);} static inline bool strends(const char *str, const char *postfix){ if (strlen(str) < strlen(postfix)) return false; return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;} /** * get_basename - return the last part of a pathname. * * @path: path to extract the filename from. */const char *get_basename(const char *path){ const char *tail = strrchr(path, '/'); return tail ? tail + 1 : path;} char *read_text_file(const char *filename){ struct stat st; size_t nbytes; int fd; char *buf; fd = open(filename, O_RDONLY); if (fd < 0) { perror(filename); exit(1); } if (fstat(fd, &st) < 0) { perror(filename); exit(1); } buf = xmalloc(st.st_size + 1); nbytes = st.st_size; while (nbytes) { ssize_t bytes_read; bytes_read = read(fd, buf, nbytes); if (bytes_read < 0) { perror(filename); exit(1); } nbytes -= bytes_read; } buf[st.st_size] = '\0'; close(fd); return buf;} char *get_line(char **stringp){ char *orig = *stringp, *next; /* do not return the unwanted extra line at EOF */ if (!orig || *orig == '\0') return NULL; /* don't use strsep here, it is not available everywhere */ next = strchr(orig, '\n'); if (next) *next++ = '\0'; *stringp = next; return orig;} /* A list of all modules we processed */LIST_HEAD(modules); static struct module *find_module(const char *filename, const char *modname){ struct module *mod; list_for_each_entry(mod, &modules, list) { if (!strcmp(mod->dump_file, filename) && !strcmp(mod->name, modname)) return mod; } return NULL;} static struct module *new_module(const char *name, size_t namelen){ struct module *mod; mod = xmalloc(sizeof(*mod) + namelen + 1); memset(mod, 0, sizeof(*mod)); INIT_LIST_HEAD(&mod->exported_symbols); INIT_LIST_HEAD(&mod->unresolved_symbols); INIT_LIST_HEAD(&mod->missing_namespaces); INIT_LIST_HEAD(&mod->imported_namespaces); INIT_LIST_HEAD(&mod->aliases); memcpy(mod->name, name, namelen); mod->name[namelen] = '\0'; mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); /* * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() * is missing, do not check the use for EXPORT_SYMBOL_GPL() because * modpost will exit with an error anyway. */ mod->is_gpl_compatible = true; list_add_tail(&mod->list, &modules); return mod;} struct symbol { struct hlist_node hnode;/* link to hash table */ struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; char *namespace; unsigned int crc; bool crc_valid; bool weak; bool is_func; bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ bool used; /* there exists a user of this symbol */ char name[];}; static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10); /** * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/static struct symbol *alloc_symbol(const char *name){ struct symbol *s = xmalloc(sizeof(*s) + strlen(name) + 1); memset(s, 0, sizeof(*s)); strcpy(s->name, name); return s;} /* For the hash of exported symbols */static void hash_add_symbol(struct symbol *sym){ hash_add(symbol_hashtable, &sym->hnode, hash_str(sym->name));} static void sym_add_unresolved(const char *name, struct module *mod, bool weak){ struct symbol *sym; sym = alloc_symbol(name); sym->weak = weak; list_add_tail(&sym->list, &mod->unresolved_symbols);} static struct symbol *sym_find_with_module(const char *name, struct module *mod){ struct symbol *s; /* For our purposes, .foo matches foo. PPC64 needs this. */ if (name[0] == '.') name++; hash_for_each_possible(symbol_hashtable, s, hnode, hash_str(name)) { if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } return NULL;} static struct symbol *find_symbol(const char *name){ return sym_find_with_module(name, NULL);} struct namespace_list { struct list_head list; char namespace[];}; static bool contains_namespace(struct list_head *head, const char *namespace){ struct namespace_list *list; /* * The default namespace is null string "", which is always implicitly * contained. */ if (!namespace[0]) return true; list_for_each_entry(list, head, list) { if (!strcmp(list->namespace, namespace)) return true; } return false;} static void add_namespace(struct list_head *head, const char *namespace){ struct namespace_list *ns_entry; if (!contains_namespace(head, namespace)) { ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1); strcpy(ns_entry->namespace, namespace); list_add_tail(&ns_entry->list, head); }} static void *sym_get_data_by_offset(const struct elf_info *info, unsigned int secindex, unsigned long offset){ Elf_Shdr *sechdr = &info->sechdrs[secindex]; return (void *)info->hdr + sechdr->sh_offset + offset;} void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym){ return sym_get_data_by_offset(info, get_secindex(info, sym), sym->st_value);} static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr){ return sym_get_data_by_offset(info, info->secindex_strings, sechdr->sh_name);} static const char *sec_name(const struct elf_info *info, unsigned int secindex){ /* * If sym->st_shndx is a special section index, there is no * corresponding section header. * Return "" if the index is out of range of info->sechdrs[] array. */ if (secindex >= info->num_sections) return ""; return sech_name(info, &info->sechdrs[secindex]);} static struct symbol *sym_add_exported(const char *name, struct module *mod, bool gpl_only, const char *namespace){ struct symbol *s = find_symbol(name); if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { error("%s: '%s' exported twice. Previous export was in %s%s\n", mod->name, name, s->module->name, s->module->is_vmlinux ? "" : ".ko"); } s = alloc_symbol(name); s->module = mod; s->is_gpl_only = gpl_only; s->namespace = xstrdup(namespace); list_add_tail(&s->list, &mod->exported_symbols); hash_add_symbol(s); return s;} static void sym_set_crc(struct symbol *sym, unsigned int crc){ sym->crc = crc; sym->crc_valid = true;} static void *grab_file(const char *filename, size_t *size){ struct stat st; void *map = MAP_FAILED; int fd; fd = open(filename, O_RDONLY); if (fd < 0) return NULL; if (fstat(fd, &st)) goto failed; *size = st.st_size; map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); failed: close(fd); if (map == MAP_FAILED) return NULL; return map;} static void release_file(void *file, size_t size){ munmap(file, size);} static int parse_elf(struct elf_info *info, const char *filename){ unsigned int i; Elf_Ehdr *hdr; Elf_Shdr *sechdrs; Elf_Sym *sym; const char *secstrings; unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U; hdr = grab_file(filename, &info->size); if (!hdr) { if (ignore_missing_files) { fprintf(stderr, "%s: %s (ignored)\n", filename, strerror(errno)); return 0; } perror(filename); exit(1); } info->hdr = hdr; if (info->size < sizeof(*hdr)) { /* file too small, assume this is an empty .o file */ return 0; } /* Is this a valid ELF file? */ if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || (hdr->e_ident[EI_MAG1] != ELFMAG1) || (hdr->e_ident[EI_MAG2] != ELFMAG2) || (hdr->e_ident[EI_MAG3] != ELFMAG3)) { /* Not an ELF file - silently ignore it */ return 0; } switch (hdr->e_ident[EI_DATA]) { case ELFDATA2LSB: target_is_big_endian = false; break; case ELFDATA2MSB: target_is_big_endian = true; break; default: fatal("target endian is unknown\n"); } /* Fix endianness in ELF header */ hdr->e_type = TO_NATIVE(hdr->e_type); hdr->e_machine = TO_NATIVE(hdr->e_machine); hdr->e_version = TO_NATIVE(hdr->e_version); hdr->e_entry = TO_NATIVE(hdr->e_entry); hdr->e_phoff = TO_NATIVE(hdr->e_phoff); hdr->e_shoff = TO_NATIVE(hdr->e_shoff); hdr->e_flags = TO_NATIVE(hdr->e_flags); hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize); hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize); hdr->e_phnum = TO_NATIVE(hdr->e_phnum); hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize); hdr->e_shnum = TO_NATIVE(hdr->e_shnum); hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); sechdrs = (void *)hdr + hdr->e_shoff; info->sechdrs = sechdrs; /* modpost only works for relocatable objects */ if (hdr->e_type != ET_REL) fatal("%s: not relocatable object.", filename); /* Check if file offset is correct */ if (hdr->e_shoff > info->size) fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", (unsigned long)hdr->e_shoff, filename, info->size); if (hdr->e_shnum == SHN_UNDEF) { /* * There are more than 64k sections, * read count from .sh_size. */ info->num_sections = TO_NATIVE(sechdrs[0].sh_size); } else { info->num_sections = hdr->e_shnum; } if (hdr->e_shstrndx == SHN_XINDEX) { info->secindex_strings = TO_NATIVE(sechdrs[0].sh_link); } else { info->secindex_strings = hdr->e_shstrndx; } /* Fix endianness in section headers */ for (i = 0; i < info->num_sections; i++) { sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags); sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link);Lines 1–500 of 2387. Load more
/* Postprocess module symbol versions * * Copyright 2003 Kai Germaschewski * Copyright 2002-2004 Rusty Russell, IBM Corporation * Copyright 2006-2008 Sam Ravnborg * Based in part on module-init-tools/depmod.c,file2alias * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * Usage: modpost vmlinux module1.o module2.o ... */ #define _GNU_SOURCE #include <elf.h> #include <fnmatch.h> #include <stdio.h> #include <ctype.h> #include <string.h> #include <limits.h> #include <stdbool.h> #include <errno.h> #include <hash.h> #include <hashtable.h> #include <list.h> #include <xalloc.h> #include "modpost.h" #include "../../include/linux/license.h" #define MODULE_NS_PREFIX "module:" static bool module_enabled; /* Are we using CONFIG_MODVERSIONS? */ static bool modversions; /* Is CONFIG_MODULE_SRCVERSION_ALL set? */ static bool all_versions; /* Is CONFIG_BASIC_MODVERSIONS set? */ static bool basic_modversions; /* Is CONFIG_EXTENDED_MODVERSIONS set? */ static bool extended_modversions; /* If we are modposting external module set to 1 */ static bool external_module; /* Only warn about unresolved symbols */ static bool warn_unresolved; static int sec_mismatch_count; static bool sec_mismatch_warn_only = true; /* Trim EXPORT_SYMBOLs that are unused by in-tree modules */ static bool trim_unused_exports; /* ignore missing files */ static bool ignore_missing_files; /* If set to 1, only warn (instead of error) about missing ns imports */ static bool allow_missing_ns_imports; static bool error_occurred; static bool extra_warn; bool target_is_big_endian; bool host_is_big_endian; /* * Cut off the warnings when there are too many. This typically occurs when * vmlinux is missing. ('make modules' without building vmlinux.) */ #define MAX_UNRESOLVED_REPORTS 10 static unsigned int nr_unresolved; /* In kernel, this size is defined in linux/module.h; * here we use Elf_Addr instead of long for covering cross-compile */ #define MODULE_NAME_LEN (64 - sizeof(Elf_Addr)) void modpost_log(bool is_error, const char *fmt, ...) { va_list arglist; if (is_error) { fprintf(stderr, "ERROR: "); error_occurred = true; } else { fprintf(stderr, "WARNING: "); } fprintf(stderr, "modpost: "); va_start(arglist, fmt); vfprintf(stderr, fmt, arglist); va_end(arglist); } static inline bool strends(const char *str, const char *postfix) { if (strlen(str) < strlen(postfix)) return false; return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0; } /** * get_basename - return the last part of a pathname. * * @path: path to extract the filename from. */ const char *get_basename(const char *path) { const char *tail = strrchr(path, '/'); return tail ? tail + 1 : path; } char *read_text_file(const char *filename) { struct stat st; size_t nbytes; int fd; char *buf; fd = open(filename, O_RDONLY); if (fd < 0) { perror(filename); exit(1); } if (fstat(fd, &st) < 0) { perror(filename); exit(1); } buf = xmalloc(st.st_size + 1); nbytes = st.st_size; while (nbytes) { ssize_t bytes_read; bytes_read = read(fd, buf, nbytes); if (bytes_read < 0) { perror(filename); exit(1); } nbytes -= bytes_read; } buf[st.st_size] = '\0'; close(fd); return buf; } char *get_line(char **stringp) { char *orig = *stringp, *next; /* do not return the unwanted extra line at EOF */ if (!orig || *orig == '\0') return NULL; /* don't use strsep here, it is not available everywhere */ next = strchr(orig, '\n'); if (next) *next++ = '\0'; *stringp = next; return orig; } /* A list of all modules we processed */ LIST_HEAD(modules); static struct module *find_module(const char *filename, const char *modname) { struct module *mod; list_for_each_entry(mod, &modules, list) { if (!strcmp(mod->dump_file, filename) && !strcmp(mod->name, modname)) return mod; } return NULL; } static struct module *new_module(const char *name, size_t namelen) { struct module *mod; mod = xmalloc(sizeof(*mod) + namelen + 1); memset(mod, 0, sizeof(*mod)); INIT_LIST_HEAD(&mod->exported_symbols); INIT_LIST_HEAD(&mod->unresolved_symbols); INIT_LIST_HEAD(&mod->missing_namespaces); INIT_LIST_HEAD(&mod->imported_namespaces); INIT_LIST_HEAD(&mod->aliases); memcpy(mod->name, name, namelen); mod->name[namelen] = '\0'; mod->is_vmlinux = (strcmp(mod->name, "vmlinux") == 0); /* * Set mod->is_gpl_compatible to true by default. If MODULE_LICENSE() * is missing, do not check the use for EXPORT_SYMBOL_GPL() because * modpost will exit with an error anyway. */ mod->is_gpl_compatible = true; list_add_tail(&mod->list, &modules); return mod; } struct symbol { struct hlist_node hnode;/* link to hash table */ struct list_head list; /* link to module::exported_symbols or module::unresolved_symbols */ struct module *module; char *namespace; unsigned int crc; bool crc_valid; bool weak; bool is_func; bool is_gpl_only; /* exported by EXPORT_SYMBOL_GPL */ bool used; /* there exists a user of this symbol */ char name[]; }; static HASHTABLE_DEFINE(symbol_hashtable, 1U << 10); /** * Allocate a new symbols for use in the hash of exported symbols or * the list of unresolved symbols per module **/ static struct symbol *alloc_symbol(const char *name) { struct symbol *s = xmalloc(sizeof(*s) + strlen(name) + 1); memset(s, 0, sizeof(*s)); strcpy(s->name, name); return s; } /* For the hash of exported symbols */ static void hash_add_symbol(struct symbol *sym) { hash_add(symbol_hashtable, &sym->hnode, hash_str(sym->name)); } static void sym_add_unresolved(const char *name, struct module *mod, bool weak) { struct symbol *sym; sym = alloc_symbol(name); sym->weak = weak; list_add_tail(&sym->list, &mod->unresolved_symbols); } static struct symbol *sym_find_with_module(const char *name, struct module *mod) { struct symbol *s; /* For our purposes, .foo matches foo. PPC64 needs this. */ if (name[0] == '.') name++; hash_for_each_possible(symbol_hashtable, s, hnode, hash_str(name)) { if (strcmp(s->name, name) == 0 && (!mod || s->module == mod)) return s; } return NULL; } static struct symbol *find_symbol(const char *name) { return sym_find_with_module(name, NULL); } struct namespace_list { struct list_head list; char namespace[]; }; static bool contains_namespace(struct list_head *head, const char *namespace) { struct namespace_list *list; /* * The default namespace is null string "", which is always implicitly * contained. */ if (!namespace[0]) return true; list_for_each_entry(list, head, list) { if (!strcmp(list->namespace, namespace)) return true; } return false; } static void add_namespace(struct list_head *head, const char *namespace) { struct namespace_list *ns_entry; if (!contains_namespace(head, namespace)) { ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1); strcpy(ns_entry->namespace, namespace); list_add_tail(&ns_entry->list, head); } } static void *sym_get_data_by_offset(const struct elf_info *info, unsigned int secindex, unsigned long offset) { Elf_Shdr *sechdr = &info->sechdrs[secindex]; return (void *)info->hdr + sechdr->sh_offset + offset; } void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym) { return sym_get_data_by_offset(info, get_secindex(info, sym), sym->st_value); } static const char *sech_name(const struct elf_info *info, Elf_Shdr *sechdr) { return sym_get_data_by_offset(info, info->secindex_strings, sechdr->sh_name); } static const char *sec_name(const struct elf_info *info, unsigned int secindex) { /* * If sym->st_shndx is a special section index, there is no * corresponding section header. * Return "" if the index is out of range of info->sechdrs[] array. */ if (secindex >= info->num_sections) return ""; return sech_name(info, &info->sechdrs[secindex]); } static struct symbol *sym_add_exported(const char *name, struct module *mod, bool gpl_only, const char *namespace) { struct symbol *s = find_symbol(name); if (s && (!external_module || s->module->is_vmlinux || s->module == mod)) { error("%s: '%s' exported twice. Previous export was in %s%s\n", mod->name, name, s->module->name, s->module->is_vmlinux ? "" : ".ko"); } s = alloc_symbol(name); s->module = mod; s->is_gpl_only = gpl_only; s->namespace = xstrdup(namespace); list_add_tail(&s->list, &mod->exported_symbols); hash_add_symbol(s); return s; } static void sym_set_crc(struct symbol *sym, unsigned int crc) { sym->crc = crc; sym->crc_valid = true; } static void *grab_file(const char *filename, size_t *size) { struct stat st; void *map = MAP_FAILED; int fd; fd = open(filename, O_RDONLY); if (fd < 0) return NULL; if (fstat(fd, &st)) goto failed; *size = st.st_size; map = mmap(NULL, *size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); failed: close(fd); if (map == MAP_FAILED) return NULL; return map; } static void release_file(void *file, size_t size) { munmap(file, size); } static int parse_elf(struct elf_info *info, const char *filename) { unsigned int i; Elf_Ehdr *hdr; Elf_Shdr *sechdrs; Elf_Sym *sym; const char *secstrings; unsigned int symtab_idx = ~0U, symtab_shndx_idx = ~0U; hdr = grab_file(filename, &info->size); if (!hdr) { if (ignore_missing_files) { fprintf(stderr, "%s: %s (ignored)\n", filename, strerror(errno)); return 0; } perror(filename); exit(1); } info->hdr = hdr; if (info->size < sizeof(*hdr)) { /* file too small, assume this is an empty .o file */ return 0; } /* Is this a valid ELF file? */ if ((hdr->e_ident[EI_MAG0] != ELFMAG0) || (hdr->e_ident[EI_MAG1] != ELFMAG1) || (hdr->e_ident[EI_MAG2] != ELFMAG2) || (hdr->e_ident[EI_MAG3] != ELFMAG3)) { /* Not an ELF file - silently ignore it */ return 0; } switch (hdr->e_ident[EI_DATA]) { case ELFDATA2LSB: target_is_big_endian = false; break; case ELFDATA2MSB: target_is_big_endian = true; break; default: fatal("target endian is unknown\n"); } /* Fix endianness in ELF header */ hdr->e_type = TO_NATIVE(hdr->e_type); hdr->e_machine = TO_NATIVE(hdr->e_machine); hdr->e_version = TO_NATIVE(hdr->e_version); hdr->e_entry = TO_NATIVE(hdr->e_entry); hdr->e_phoff = TO_NATIVE(hdr->e_phoff); hdr->e_shoff = TO_NATIVE(hdr->e_shoff); hdr->e_flags = TO_NATIVE(hdr->e_flags); hdr->e_ehsize = TO_NATIVE(hdr->e_ehsize); hdr->e_phentsize = TO_NATIVE(hdr->e_phentsize); hdr->e_phnum = TO_NATIVE(hdr->e_phnum); hdr->e_shentsize = TO_NATIVE(hdr->e_shentsize); hdr->e_shnum = TO_NATIVE(hdr->e_shnum); hdr->e_shstrndx = TO_NATIVE(hdr->e_shstrndx); sechdrs = (void *)hdr + hdr->e_shoff; info->sechdrs = sechdrs; /* modpost only works for relocatable objects */ if (hdr->e_type != ET_REL) fatal("%s: not relocatable object.", filename); /* Check if file offset is correct */ if (hdr->e_shoff > info->size) fatal("section header offset=%lu in file '%s' is bigger than filesize=%zu\n", (unsigned long)hdr->e_shoff, filename, info->size); if (hdr->e_shnum == SHN_UNDEF) { /* * There are more than 64k sections, * read count from .sh_size. */ info->num_sections = TO_NATIVE(sechdrs[0].sh_size); } else { info->num_sections = hdr->e_shnum; } if (hdr->e_shstrndx == SHN_XINDEX) { info->secindex_strings = TO_NATIVE(sechdrs[0].sh_link); } else { info->secindex_strings = hdr->e_shstrndx; } /* Fix endianness in section headers */ for (i = 0; i < info->num_sections; i++) { sechdrs[i].sh_name = TO_NATIVE(sechdrs[i].sh_name); sechdrs[i].sh_type = TO_NATIVE(sechdrs[i].sh_type); sechdrs[i].sh_flags = TO_NATIVE(sechdrs[i].sh_flags); sechdrs[i].sh_addr = TO_NATIVE(sechdrs[i].sh_addr); sechdrs[i].sh_offset = TO_NATIVE(sechdrs[i].sh_offset); sechdrs[i].sh_size = TO_NATIVE(sechdrs[i].sh_size); sechdrs[i].sh_link = TO_NATIVE(sechdrs[i].sh_link);