list_for_each_entry(use, &mod->target_list, target_list) sysfs_remove_link(use->target->holders_dir, mod->name); mutex_unlock(&module_mutex);#endif} static int module_add_modinfo_attrs(struct module *mod){ struct module_attribute *attr; struct module_attribute *temp_attr; int error = 0; int i; mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) * (ARRAY_SIZE(modinfo_attrs) + 1)), GFP_KERNEL); if (!mod->modinfo_attrs) return -ENOMEM; temp_attr = mod->modinfo_attrs; for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { if (!attr->test || (attr->test && attr->test(mod))) { memcpy(temp_attr, attr, sizeof(*temp_attr)); sysfs_attr_init(&temp_attr->attr); error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr); ++temp_attr; } } return error;} static void module_remove_modinfo_attrs(struct module *mod){ struct module_attribute *attr; int i; for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) { /* pick a field to test for end of list */ if (!attr->attr.name) break; sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); if (attr->free) attr->free(mod); } kfree(mod->modinfo_attrs);} static int mod_sysfs_init(struct module *mod){ int err; struct kobject *kobj; if (!module_sysfs_initialized) { printk(KERN_ERR "%s: module sysfs not initialized\n", mod->name); err = -EINVAL; goto out; } kobj = kset_find_obj(module_kset, mod->name); if (kobj) { printk(KERN_ERR "%s: module is already loaded\n", mod->name); kobject_put(kobj); err = -EINVAL; goto out; } mod->mkobj.mod = mod; memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj)); mod->mkobj.kobj.kset = module_kset; err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL, "%s", mod->name); if (err) kobject_put(&mod->mkobj.kobj); /* delay uevent until full sysfs population */out: return err;} static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params){ int err; err = mod_sysfs_init(mod); if (err) goto out; mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj); if (!mod->holders_dir) { err = -ENOMEM; goto out_unreg; } err = module_param_sysfs_setup(mod, kparam, num_params); if (err) goto out_unreg_holders; err = module_add_modinfo_attrs(mod); if (err) goto out_unreg_param; add_usage_links(mod); add_sect_attrs(mod, info); add_notes_attrs(mod, info); kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD); return 0; out_unreg_param: module_param_sysfs_remove(mod);out_unreg_holders: kobject_put(mod->holders_dir);out_unreg: kobject_put(&mod->mkobj.kobj);out: return err;} static void mod_sysfs_fini(struct module *mod){ remove_notes_attrs(mod); remove_sect_attrs(mod); kobject_put(&mod->mkobj.kobj);} #else /* !CONFIG_SYSFS */ static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params){ return 0;} static void mod_sysfs_fini(struct module *mod){} static void module_remove_modinfo_attrs(struct module *mod){} static void del_usage_links(struct module *mod){} #endif /* CONFIG_SYSFS */ static void mod_sysfs_teardown(struct module *mod){ del_usage_links(mod); module_remove_modinfo_attrs(mod); module_param_sysfs_remove(mod); kobject_put(mod->mkobj.drivers_dir); kobject_put(mod->holders_dir); mod_sysfs_fini(mod);} /* * unlink the module with the whole machine is stopped with interrupts off * - this defends against kallsyms not taking locks */static int __unlink_module(void *_mod){ struct module *mod = _mod; list_del(&mod->list); module_bug_cleanup(mod); return 0;} #ifdef CONFIG_DEBUG_SET_MODULE_RONX/* * LKM RO/NX protection: protect module's text/ro-data * from modification and any data from execution. */void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages)){ unsigned long begin_pfn = PFN_DOWN((unsigned long)start); unsigned long end_pfn = PFN_DOWN((unsigned long)end); if (end_pfn > begin_pfn) set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn);} static void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size){ /* begin and end PFNs of the current subsection */ unsigned long begin_pfn; unsigned long end_pfn; /* * Set RO for module text and RO-data: * - Always protect first page. * - Do not protect last partial page. */ if (ro_size > 0) set_page_attributes(base, base + ro_size, set_memory_ro); /* * Set NX permissions for module data: * - Do not protect first partial page. * - Always protect last page. */ if (total_size > text_size) { begin_pfn = PFN_UP((unsigned long)base + text_size); end_pfn = PFN_UP((unsigned long)base + total_size); if (end_pfn > begin_pfn) set_memory_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn); }} static void unset_module_core_ro_nx(struct module *mod){ set_page_attributes(mod->module_core + mod->core_text_size, mod->module_core + mod->core_size, set_memory_x); set_page_attributes(mod->module_core, mod->module_core + mod->core_ro_size, set_memory_rw);} static void unset_module_init_ro_nx(struct module *mod){ set_page_attributes(mod->module_init + mod->init_text_size, mod->module_init + mod->init_size, set_memory_x); set_page_attributes(mod->module_init, mod->module_init + mod->init_ro_size, set_memory_rw);} /* Iterate through all modules and set each module's text as RW */void set_all_modules_text_rw(void){ struct module *mod; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, set_memory_rw); } if ((mod->module_init) && (mod->init_text_size)) { set_page_attributes(mod->module_init, mod->module_init + mod->init_text_size, set_memory_rw); } } mutex_unlock(&module_mutex);} /* Iterate through all modules and set each module's text as RO */void set_all_modules_text_ro(void){ struct module *mod; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, set_memory_ro); } if ((mod->module_init) && (mod->init_text_size)) { set_page_attributes(mod->module_init, mod->module_init + mod->init_text_size, set_memory_ro); } } mutex_unlock(&module_mutex);}#elsestatic inline void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { }static void unset_module_core_ro_nx(struct module *mod) { }static void unset_module_init_ro_nx(struct module *mod) { }#endif void __weak module_free(struct module *mod, void *module_region){ vfree(module_region);} void __weak module_arch_cleanup(struct module *mod){} /* Free a module, remove from lists, etc. */static void free_module(struct module *mod){ trace_module_free(mod); /* Delete from various lists */ mutex_lock(&module_mutex); stop_machine(__unlink_module, mod, NULL); mutex_unlock(&module_mutex); mod_sysfs_teardown(mod); /* Remove dynamic debug info */ ddebug_remove_module(mod->name); /* Arch-specific cleanup. */ module_arch_cleanup(mod); /* Module unload stuff */ module_unload_free(mod); /* Free any allocated parameters. */ destroy_params(mod->kp, mod->num_kp); /* This may be NULL, but that's OK */ unset_module_init_ro_nx(mod); module_free(mod, mod->module_init); kfree(mod->args); percpu_modfree(mod); /* Free lock-classes: */ lockdep_free_key_range(mod->module_core, mod->core_size); /* Finally, free the core (containing the module structure) */ unset_module_core_ro_nx(mod); module_free(mod, mod->module_core); #ifdef CONFIG_MPU update_protections(current->mm);#endif} void *__symbol_get(const char *symbol){ struct module *owner; const struct kernel_symbol *sym; preempt_disable(); sym = find_symbol(symbol, &owner, NULL, true, true); if (sym && strong_try_module_get(owner)) sym = NULL; preempt_enable(); return sym ? (void *)sym->value : NULL;}EXPORT_SYMBOL_GPL(__symbol_get); /* * Ensure that an exported symbol [global namespace] does not already exist * in the kernel or in some other module's exported symbol table. * * You must hold the module_mutex. */static int verify_export_symbols(struct module *mod){ unsigned int i; struct module *owner; const struct kernel_symbol *s; struct { const struct kernel_symbol *sym; unsigned int num; } arr[] = { { mod->syms, mod->num_syms }, { mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_future_syms, mod->num_gpl_future_syms },#ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->num_unused_syms }, { mod->unused_gpl_syms, mod->num_unused_gpl_syms },#endif }; for (i = 0; i < ARRAY_SIZE(arr); i++) { for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { if (find_symbol(s->name, &owner, NULL, true, false)) { printk(KERN_ERR "%s: exports duplicate symbol %s" " (owned by %s)\n", mod->name, s->name, module_name(owner)); return -ENOEXEC; } } } return 0;} /* Change all symbols so that st_value encodes the pointer directly. */static int simplify_symbols(struct module *mod, const struct load_info *info){ Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Sym *sym = (void *)symsec->sh_addr; unsigned long secbase; unsigned int i; int ret = 0; const struct kernel_symbol *ksym; for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { const char *name = info->strtab + sym[i].st_name; switch (sym[i].st_shndx) { case SHN_COMMON: /* We compiled with -fno-common. These are not supposed to happen. */ pr_debug("Common symbol: %s\n", name); printk("%s: please compile with -fno-common\n", mod->name); ret = -ENOEXEC; break; case SHN_ABS: /* Don't need to do anything */ pr_debug("Absolute symbol: 0x%08lx\n", (long)sym[i].st_value); break; case SHN_UNDEF: ksym = resolve_symbol_wait(mod, info, name); /* Ok if resolved. */ if (ksym && !IS_ERR(ksym)) { sym[i].st_value = ksym->value; break; } /* Ok if weak. */ if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; printk(KERN_WARNING "%s: Unknown symbol %s (err %li)\n", mod->name, name, PTR_ERR(ksym)); ret = PTR_ERR(ksym) ?: -ENOENT; break; default: /* Divert to percpu allocation if a percpu var. */ if (sym[i].st_shndx == info->index.pcpu) secbase = (unsigned long)mod_percpu(mod); else secbase = info->sechdrs[sym[i].st_shndx].sh_addr; sym[i].st_value += secbase; break; } } return ret;} int __weak apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me){ pr_err("module %s: REL relocation unsupported\n", me->name); return -ENOEXEC;} int __weak apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me){ pr_err("module %s: RELA relocation unsupported\n", me->name); return -ENOEXEC;} static int apply_relocations(struct module *mod, const struct load_info *info){ unsigned int i; int err = 0; /* Now do relocations. */ for (i = 1; i < info->hdr->e_shnum; i++) { unsigned int infosec = info->sechdrs[i].sh_info; /* Not a valid relocation section? */ if (infosec >= info->hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) continue; if (info->sechdrs[i].sh_type == SHT_REL) err = apply_relocate(info->sechdrs, info->strtab, info->index.sym, i, mod); else if (info->sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(info->sechdrs, info->strtab, info->index.sym, i, mod); if (err < 0) break; } return err;} Lines 1501–2000 of 3539. Previous lines Load more
list_for_each_entry(use, &mod->target_list, target_list) sysfs_remove_link(use->target->holders_dir, mod->name); mutex_unlock(&module_mutex); #endif } static int module_add_modinfo_attrs(struct module *mod) { struct module_attribute *attr; struct module_attribute *temp_attr; int error = 0; int i; mod->modinfo_attrs = kzalloc((sizeof(struct module_attribute) * (ARRAY_SIZE(modinfo_attrs) + 1)), GFP_KERNEL); if (!mod->modinfo_attrs) return -ENOMEM; temp_attr = mod->modinfo_attrs; for (i = 0; (attr = modinfo_attrs[i]) && !error; i++) { if (!attr->test || (attr->test && attr->test(mod))) { memcpy(temp_attr, attr, sizeof(*temp_attr)); sysfs_attr_init(&temp_attr->attr); error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr); ++temp_attr; } } return error; } static void module_remove_modinfo_attrs(struct module *mod) { struct module_attribute *attr; int i; for (i = 0; (attr = &mod->modinfo_attrs[i]); i++) { /* pick a field to test for end of list */ if (!attr->attr.name) break; sysfs_remove_file(&mod->mkobj.kobj,&attr->attr); if (attr->free) attr->free(mod); } kfree(mod->modinfo_attrs); } static int mod_sysfs_init(struct module *mod) { int err; struct kobject *kobj; if (!module_sysfs_initialized) { printk(KERN_ERR "%s: module sysfs not initialized\n", mod->name); err = -EINVAL; goto out; } kobj = kset_find_obj(module_kset, mod->name); if (kobj) { printk(KERN_ERR "%s: module is already loaded\n", mod->name); kobject_put(kobj); err = -EINVAL; goto out; } mod->mkobj.mod = mod; memset(&mod->mkobj.kobj, 0, sizeof(mod->mkobj.kobj)); mod->mkobj.kobj.kset = module_kset; err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype, NULL, "%s", mod->name); if (err) kobject_put(&mod->mkobj.kobj); /* delay uevent until full sysfs population */ out: return err; } static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params) { int err; err = mod_sysfs_init(mod); if (err) goto out; mod->holders_dir = kobject_create_and_add("holders", &mod->mkobj.kobj); if (!mod->holders_dir) { err = -ENOMEM; goto out_unreg; } err = module_param_sysfs_setup(mod, kparam, num_params); if (err) goto out_unreg_holders; err = module_add_modinfo_attrs(mod); if (err) goto out_unreg_param; add_usage_links(mod); add_sect_attrs(mod, info); add_notes_attrs(mod, info); kobject_uevent(&mod->mkobj.kobj, KOBJ_ADD); return 0; out_unreg_param: module_param_sysfs_remove(mod); out_unreg_holders: kobject_put(mod->holders_dir); out_unreg: kobject_put(&mod->mkobj.kobj); out: return err; } static void mod_sysfs_fini(struct module *mod) { remove_notes_attrs(mod); remove_sect_attrs(mod); kobject_put(&mod->mkobj.kobj); } #else /* !CONFIG_SYSFS */ static int mod_sysfs_setup(struct module *mod, const struct load_info *info, struct kernel_param *kparam, unsigned int num_params) { return 0; } static void mod_sysfs_fini(struct module *mod) { } static void module_remove_modinfo_attrs(struct module *mod) { } static void del_usage_links(struct module *mod) { } #endif /* CONFIG_SYSFS */ static void mod_sysfs_teardown(struct module *mod) { del_usage_links(mod); module_remove_modinfo_attrs(mod); module_param_sysfs_remove(mod); kobject_put(mod->mkobj.drivers_dir); kobject_put(mod->holders_dir); mod_sysfs_fini(mod); } /* * unlink the module with the whole machine is stopped with interrupts off * - this defends against kallsyms not taking locks */ static int __unlink_module(void *_mod) { struct module *mod = _mod; list_del(&mod->list); module_bug_cleanup(mod); return 0; } #ifdef CONFIG_DEBUG_SET_MODULE_RONX /* * LKM RO/NX protection: protect module's text/ro-data * from modification and any data from execution. */ void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages)) { unsigned long begin_pfn = PFN_DOWN((unsigned long)start); unsigned long end_pfn = PFN_DOWN((unsigned long)end); if (end_pfn > begin_pfn) set(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn); } static void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { /* begin and end PFNs of the current subsection */ unsigned long begin_pfn; unsigned long end_pfn; /* * Set RO for module text and RO-data: * - Always protect first page. * - Do not protect last partial page. */ if (ro_size > 0) set_page_attributes(base, base + ro_size, set_memory_ro); /* * Set NX permissions for module data: * - Do not protect first partial page. * - Always protect last page. */ if (total_size > text_size) { begin_pfn = PFN_UP((unsigned long)base + text_size); end_pfn = PFN_UP((unsigned long)base + total_size); if (end_pfn > begin_pfn) set_memory_nx(begin_pfn << PAGE_SHIFT, end_pfn - begin_pfn); } } static void unset_module_core_ro_nx(struct module *mod) { set_page_attributes(mod->module_core + mod->core_text_size, mod->module_core + mod->core_size, set_memory_x); set_page_attributes(mod->module_core, mod->module_core + mod->core_ro_size, set_memory_rw); } static void unset_module_init_ro_nx(struct module *mod) { set_page_attributes(mod->module_init + mod->init_text_size, mod->module_init + mod->init_size, set_memory_x); set_page_attributes(mod->module_init, mod->module_init + mod->init_ro_size, set_memory_rw); } /* Iterate through all modules and set each module's text as RW */ void set_all_modules_text_rw(void) { struct module *mod; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, set_memory_rw); } if ((mod->module_init) && (mod->init_text_size)) { set_page_attributes(mod->module_init, mod->module_init + mod->init_text_size, set_memory_rw); } } mutex_unlock(&module_mutex); } /* Iterate through all modules and set each module's text as RO */ void set_all_modules_text_ro(void) { struct module *mod; mutex_lock(&module_mutex); list_for_each_entry_rcu(mod, &modules, list) { if ((mod->module_core) && (mod->core_text_size)) { set_page_attributes(mod->module_core, mod->module_core + mod->core_text_size, set_memory_ro); } if ((mod->module_init) && (mod->init_text_size)) { set_page_attributes(mod->module_init, mod->module_init + mod->init_text_size, set_memory_ro); } } mutex_unlock(&module_mutex); } #else static inline void set_section_ro_nx(void *base, unsigned long text_size, unsigned long ro_size, unsigned long total_size) { } static void unset_module_core_ro_nx(struct module *mod) { } static void unset_module_init_ro_nx(struct module *mod) { } #endif void __weak module_free(struct module *mod, void *module_region) { vfree(module_region); } void __weak module_arch_cleanup(struct module *mod) { } /* Free a module, remove from lists, etc. */ static void free_module(struct module *mod) { trace_module_free(mod); /* Delete from various lists */ mutex_lock(&module_mutex); stop_machine(__unlink_module, mod, NULL); mutex_unlock(&module_mutex); mod_sysfs_teardown(mod); /* Remove dynamic debug info */ ddebug_remove_module(mod->name); /* Arch-specific cleanup. */ module_arch_cleanup(mod); /* Module unload stuff */ module_unload_free(mod); /* Free any allocated parameters. */ destroy_params(mod->kp, mod->num_kp); /* This may be NULL, but that's OK */ unset_module_init_ro_nx(mod); module_free(mod, mod->module_init); kfree(mod->args); percpu_modfree(mod); /* Free lock-classes: */ lockdep_free_key_range(mod->module_core, mod->core_size); /* Finally, free the core (containing the module structure) */ unset_module_core_ro_nx(mod); module_free(mod, mod->module_core); #ifdef CONFIG_MPU update_protections(current->mm); #endif } void *__symbol_get(const char *symbol) { struct module *owner; const struct kernel_symbol *sym; preempt_disable(); sym = find_symbol(symbol, &owner, NULL, true, true); if (sym && strong_try_module_get(owner)) sym = NULL; preempt_enable(); return sym ? (void *)sym->value : NULL; } EXPORT_SYMBOL_GPL(__symbol_get); /* * Ensure that an exported symbol [global namespace] does not already exist * in the kernel or in some other module's exported symbol table. * * You must hold the module_mutex. */ static int verify_export_symbols(struct module *mod) { unsigned int i; struct module *owner; const struct kernel_symbol *s; struct { const struct kernel_symbol *sym; unsigned int num; } arr[] = { { mod->syms, mod->num_syms }, { mod->gpl_syms, mod->num_gpl_syms }, { mod->gpl_future_syms, mod->num_gpl_future_syms }, #ifdef CONFIG_UNUSED_SYMBOLS { mod->unused_syms, mod->num_unused_syms }, { mod->unused_gpl_syms, mod->num_unused_gpl_syms }, #endif }; for (i = 0; i < ARRAY_SIZE(arr); i++) { for (s = arr[i].sym; s < arr[i].sym + arr[i].num; s++) { if (find_symbol(s->name, &owner, NULL, true, false)) { printk(KERN_ERR "%s: exports duplicate symbol %s" " (owned by %s)\n", mod->name, s->name, module_name(owner)); return -ENOEXEC; } } } return 0; } /* Change all symbols so that st_value encodes the pointer directly. */ static int simplify_symbols(struct module *mod, const struct load_info *info) { Elf_Shdr *symsec = &info->sechdrs[info->index.sym]; Elf_Sym *sym = (void *)symsec->sh_addr; unsigned long secbase; unsigned int i; int ret = 0; const struct kernel_symbol *ksym; for (i = 1; i < symsec->sh_size / sizeof(Elf_Sym); i++) { const char *name = info->strtab + sym[i].st_name; switch (sym[i].st_shndx) { case SHN_COMMON: /* We compiled with -fno-common. These are not supposed to happen. */ pr_debug("Common symbol: %s\n", name); printk("%s: please compile with -fno-common\n", mod->name); ret = -ENOEXEC; break; case SHN_ABS: /* Don't need to do anything */ pr_debug("Absolute symbol: 0x%08lx\n", (long)sym[i].st_value); break; case SHN_UNDEF: ksym = resolve_symbol_wait(mod, info, name); /* Ok if resolved. */ if (ksym && !IS_ERR(ksym)) { sym[i].st_value = ksym->value; break; } /* Ok if weak. */ if (!ksym && ELF_ST_BIND(sym[i].st_info) == STB_WEAK) break; printk(KERN_WARNING "%s: Unknown symbol %s (err %li)\n", mod->name, name, PTR_ERR(ksym)); ret = PTR_ERR(ksym) ?: -ENOENT; break; default: /* Divert to percpu allocation if a percpu var. */ if (sym[i].st_shndx == info->index.pcpu) secbase = (unsigned long)mod_percpu(mod); else secbase = info->sechdrs[sym[i].st_shndx].sh_addr; sym[i].st_value += secbase; break; } } return ret; } int __weak apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { pr_err("module %s: REL relocation unsupported\n", me->name); return -ENOEXEC; } int __weak apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, unsigned int relsec, struct module *me) { pr_err("module %s: RELA relocation unsupported\n", me->name); return -ENOEXEC; } static int apply_relocations(struct module *mod, const struct load_info *info) { unsigned int i; int err = 0; /* Now do relocations. */ for (i = 1; i < info->hdr->e_shnum; i++) { unsigned int infosec = info->sechdrs[i].sh_info; /* Not a valid relocation section? */ if (infosec >= info->hdr->e_shnum) continue; /* Don't bother with non-allocated sections */ if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC)) continue; if (info->sechdrs[i].sh_type == SHT_REL) err = apply_relocate(info->sechdrs, info->strtab, info->index.sym, i, mod); else if (info->sechdrs[i].sh_type == SHT_RELA) err = apply_relocate_add(info->sechdrs, info->strtab, info->index.sym, i, mod); if (err < 0) break; } return err; }