mirror of
https://github.com/torvalds/linux.git
synced 2024-11-23 04:31:50 +00:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching
Pull livepatching updates from Jiri Kosina: - RO/NX attribute fixes for patch module relocations from Josh Poimboeuf. As part of this effort, module.c has been cleaned up as well and livepatching is piggy-backing on this cleanup. Rusty is OK with this whole lot going through livepatching tree. - symbol disambiguation support from Chris J Arges. That series is also Reviewed-by: Miroslav Benes <mbenes@suse.cz> but this came in only after I've alredy pushed out. Didn't want to rebase because of that, hence I am mentioning it here. - symbol lookup fix from Miroslav Benes * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/livepatching: livepatch: Cleanup module page permission changes module: keep percpu symbols in module's symtab module: clean up RO/NX handling. module: use a structure to encapsulate layout. gcov: use within_module() helper. module: Use the same logic for setting and unsetting RO/NX livepatch: function,sympos scheme in livepatch sysfs directory livepatch: add sympos as disambiguator field to klp_reloc livepatch: add old_sympos as disambiguator field to klp_func
This commit is contained in:
commit
0f0836b7eb
@ -33,7 +33,7 @@ Description:
|
||||
The object directory contains subdirectories for each function
|
||||
that is patched within the object.
|
||||
|
||||
What: /sys/kernel/livepatch/<patch>/<object>/<function>
|
||||
What: /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
|
||||
Date: Nov 2014
|
||||
KernelVersion: 3.19.0
|
||||
Contact: live-patching@vger.kernel.org
|
||||
@ -41,4 +41,8 @@ Description:
|
||||
The function directory contains attributes regarding the
|
||||
properties and state of the patched function.
|
||||
|
||||
The directory name contains the patched function name and a
|
||||
sympos number corresponding to the nth occurrence of the symbol
|
||||
name in kallsyms for the patched object.
|
||||
|
||||
There are currently no such attributes.
|
||||
|
@ -160,7 +160,7 @@ apply_relocate_add(Elf64_Shdr *sechdrs, const char *strtab,
|
||||
|
||||
/* The small sections were sorted to the end of the segment.
|
||||
The following should definitely cover them. */
|
||||
gp = (u64)me->module_core + me->core_size - 0x8000;
|
||||
gp = (u64)me->core_layout.base + me->core_layout.size - 0x8000;
|
||||
got = sechdrs[me->arch.gotsecindex].sh_addr;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
|
@ -385,8 +385,8 @@ void *unwind_add_table(struct module *module, const void *table_start,
|
||||
return NULL;
|
||||
|
||||
init_unwind_table(table, module->name,
|
||||
module->module_core, module->core_size,
|
||||
module->module_init, module->init_size,
|
||||
module->core_layout.base, module->core_layout.size,
|
||||
module->init_layout.base, module->init_layout.size,
|
||||
table_start, table_size,
|
||||
NULL, 0);
|
||||
|
||||
|
@ -32,7 +32,7 @@ struct plt_entries {
|
||||
|
||||
static bool in_init(const struct module *mod, u32 addr)
|
||||
{
|
||||
return addr - (u32)mod->module_init < mod->init_size;
|
||||
return addr - (u32)mod->init_layout.base < mod->init_layout.size;
|
||||
}
|
||||
|
||||
u32 get_module_plt(struct module *mod, unsigned long loc, Elf32_Addr val)
|
||||
|
@ -118,9 +118,9 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
|
||||
* Increase core size to make room for GOT and set start
|
||||
* offset for GOT.
|
||||
*/
|
||||
module->core_size = ALIGN(module->core_size, 4);
|
||||
module->arch.got_offset = module->core_size;
|
||||
module->core_size += module->arch.got_size;
|
||||
module->core_layout.size = ALIGN(module->core_layout.size, 4);
|
||||
module->arch.got_offset = module->core_layout.size;
|
||||
module->core_layout.size += module->arch.got_size;
|
||||
|
||||
return 0;
|
||||
|
||||
@ -177,7 +177,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
|
||||
if (!info->got_initialized) {
|
||||
Elf32_Addr *gotent;
|
||||
|
||||
gotent = (module->module_core
|
||||
gotent = (module->core_layout.base
|
||||
+ module->arch.got_offset
|
||||
+ info->got_offset);
|
||||
*gotent = relocation;
|
||||
@ -255,8 +255,8 @@ int apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
|
||||
*/
|
||||
pr_debug("GOTPC: PC=0x%x, got_offset=0x%lx, core=0x%p\n",
|
||||
relocation, module->arch.got_offset,
|
||||
module->module_core);
|
||||
relocation -= ((unsigned long)module->module_core
|
||||
module->core_layout.base);
|
||||
relocation -= ((unsigned long)module->core_layout.base
|
||||
+ module->arch.got_offset);
|
||||
*location = relocation;
|
||||
break;
|
||||
|
@ -486,13 +486,13 @@ module_frob_arch_sections (Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, char *secstrings,
|
||||
static inline int
|
||||
in_init (const struct module *mod, uint64_t addr)
|
||||
{
|
||||
return addr - (uint64_t) mod->module_init < mod->init_size;
|
||||
return addr - (uint64_t) mod->init_layout.base < mod->init_layout.size;
|
||||
}
|
||||
|
||||
static inline int
|
||||
in_core (const struct module *mod, uint64_t addr)
|
||||
{
|
||||
return addr - (uint64_t) mod->module_core < mod->core_size;
|
||||
return addr - (uint64_t) mod->core_layout.base < mod->core_layout.size;
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -675,7 +675,7 @@ do_reloc (struct module *mod, uint8_t r_type, Elf64_Sym *sym, uint64_t addend,
|
||||
break;
|
||||
|
||||
case RV_BDREL:
|
||||
val -= (uint64_t) (in_init(mod, val) ? mod->module_init : mod->module_core);
|
||||
val -= (uint64_t) (in_init(mod, val) ? mod->init_layout.base : mod->core_layout.base);
|
||||
break;
|
||||
|
||||
case RV_LTV:
|
||||
@ -810,15 +810,15 @@ apply_relocate_add (Elf64_Shdr *sechdrs, const char *strtab, unsigned int symind
|
||||
* addresses have been selected...
|
||||
*/
|
||||
uint64_t gp;
|
||||
if (mod->core_size > MAX_LTOFF)
|
||||
if (mod->core_layout.size > MAX_LTOFF)
|
||||
/*
|
||||
* This takes advantage of fact that SHF_ARCH_SMALL gets allocated
|
||||
* at the end of the module.
|
||||
*/
|
||||
gp = mod->core_size - MAX_LTOFF / 2;
|
||||
gp = mod->core_layout.size - MAX_LTOFF / 2;
|
||||
else
|
||||
gp = mod->core_size / 2;
|
||||
gp = (uint64_t) mod->module_core + ((gp + 7) & -8);
|
||||
gp = mod->core_layout.size / 2;
|
||||
gp = (uint64_t) mod->core_layout.base + ((gp + 7) & -8);
|
||||
mod->arch.gp = gp;
|
||||
DEBUGP("%s: placing gp at 0x%lx\n", __func__, gp);
|
||||
}
|
||||
|
@ -176,8 +176,8 @@ static uint32_t do_plt_call(void *location, Elf32_Addr val,
|
||||
tramp[1] = 0xac000001 | ((val & 0x0000ffff) << 3);
|
||||
|
||||
/* Init, or core PLT? */
|
||||
if (location >= mod->module_core
|
||||
&& location < mod->module_core + mod->core_size)
|
||||
if (location >= mod->core_layout.base
|
||||
&& location < mod->core_layout.base + mod->core_layout.size)
|
||||
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
|
||||
else
|
||||
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
|
||||
|
@ -205,11 +205,11 @@ static void layout_sections(struct module *mod, const Elf_Ehdr *hdr,
|
||||
|| s->sh_entsize != ~0UL)
|
||||
continue;
|
||||
s->sh_entsize =
|
||||
get_offset((unsigned long *)&mod->core_size, s);
|
||||
get_offset((unsigned long *)&mod->core_layout.size, s);
|
||||
}
|
||||
|
||||
if (m == 0)
|
||||
mod->core_text_size = mod->core_size;
|
||||
mod->core_layout.text_size = mod->core_layout.size;
|
||||
|
||||
}
|
||||
}
|
||||
@ -641,7 +641,7 @@ static int vpe_elfload(struct vpe *v)
|
||||
layout_sections(&mod, hdr, sechdrs, secstrings);
|
||||
}
|
||||
|
||||
v->load_addr = alloc_progmem(mod.core_size);
|
||||
v->load_addr = alloc_progmem(mod.core_layout.size);
|
||||
if (!v->load_addr)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -42,9 +42,9 @@
|
||||
* We are not doing SEGREL32 handling correctly. According to the ABI, we
|
||||
* should do a value offset, like this:
|
||||
* if (in_init(me, (void *)val))
|
||||
* val -= (uint32_t)me->module_init;
|
||||
* val -= (uint32_t)me->init_layout.base;
|
||||
* else
|
||||
* val -= (uint32_t)me->module_core;
|
||||
* val -= (uint32_t)me->core_layout.base;
|
||||
* However, SEGREL32 is used only for PARISC unwind entries, and we want
|
||||
* those entries to have an absolute address, and not just an offset.
|
||||
*
|
||||
@ -100,14 +100,14 @@
|
||||
* or init pieces the location is */
|
||||
static inline int in_init(struct module *me, void *loc)
|
||||
{
|
||||
return (loc >= me->module_init &&
|
||||
loc <= (me->module_init + me->init_size));
|
||||
return (loc >= me->init_layout.base &&
|
||||
loc <= (me->init_layout.base + me->init_layout.size));
|
||||
}
|
||||
|
||||
static inline int in_core(struct module *me, void *loc)
|
||||
{
|
||||
return (loc >= me->module_core &&
|
||||
loc <= (me->module_core + me->core_size));
|
||||
return (loc >= me->core_layout.base &&
|
||||
loc <= (me->core_layout.base + me->core_layout.size));
|
||||
}
|
||||
|
||||
static inline int in_local(struct module *me, void *loc)
|
||||
@ -367,13 +367,13 @@ int module_frob_arch_sections(CONST Elf_Ehdr *hdr,
|
||||
}
|
||||
|
||||
/* align things a bit */
|
||||
me->core_size = ALIGN(me->core_size, 16);
|
||||
me->arch.got_offset = me->core_size;
|
||||
me->core_size += gots * sizeof(struct got_entry);
|
||||
me->core_layout.size = ALIGN(me->core_layout.size, 16);
|
||||
me->arch.got_offset = me->core_layout.size;
|
||||
me->core_layout.size += gots * sizeof(struct got_entry);
|
||||
|
||||
me->core_size = ALIGN(me->core_size, 16);
|
||||
me->arch.fdesc_offset = me->core_size;
|
||||
me->core_size += fdescs * sizeof(Elf_Fdesc);
|
||||
me->core_layout.size = ALIGN(me->core_layout.size, 16);
|
||||
me->arch.fdesc_offset = me->core_layout.size;
|
||||
me->core_layout.size += fdescs * sizeof(Elf_Fdesc);
|
||||
|
||||
me->arch.got_max = gots;
|
||||
me->arch.fdesc_max = fdescs;
|
||||
@ -391,7 +391,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
|
||||
|
||||
BUG_ON(value == 0);
|
||||
|
||||
got = me->module_core + me->arch.got_offset;
|
||||
got = me->core_layout.base + me->arch.got_offset;
|
||||
for (i = 0; got[i].addr; i++)
|
||||
if (got[i].addr == value)
|
||||
goto out;
|
||||
@ -409,7 +409,7 @@ static Elf64_Word get_got(struct module *me, unsigned long value, long addend)
|
||||
#ifdef CONFIG_64BIT
|
||||
static Elf_Addr get_fdesc(struct module *me, unsigned long value)
|
||||
{
|
||||
Elf_Fdesc *fdesc = me->module_core + me->arch.fdesc_offset;
|
||||
Elf_Fdesc *fdesc = me->core_layout.base + me->arch.fdesc_offset;
|
||||
|
||||
if (!value) {
|
||||
printk(KERN_ERR "%s: zero OPD requested!\n", me->name);
|
||||
@ -427,7 +427,7 @@ static Elf_Addr get_fdesc(struct module *me, unsigned long value)
|
||||
|
||||
/* Create new one */
|
||||
fdesc->addr = value;
|
||||
fdesc->gp = (Elf_Addr)me->module_core + me->arch.got_offset;
|
||||
fdesc->gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
|
||||
return (Elf_Addr)fdesc;
|
||||
}
|
||||
#endif /* CONFIG_64BIT */
|
||||
@ -839,7 +839,7 @@ register_unwind_table(struct module *me,
|
||||
|
||||
table = (unsigned char *)sechdrs[me->arch.unwind_section].sh_addr;
|
||||
end = table + sechdrs[me->arch.unwind_section].sh_size;
|
||||
gp = (Elf_Addr)me->module_core + me->arch.got_offset;
|
||||
gp = (Elf_Addr)me->core_layout.base + me->arch.got_offset;
|
||||
|
||||
DEBUGP("register_unwind_table(), sect = %d at 0x%p - 0x%p (gp=0x%lx)\n",
|
||||
me->arch.unwind_section, table, end, gp);
|
||||
|
@ -188,8 +188,8 @@ static uint32_t do_plt_call(void *location,
|
||||
|
||||
pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsigned int)location);
|
||||
/* Init, or core PLT? */
|
||||
if (location >= mod->module_core
|
||||
&& location < mod->module_core + mod->core_size)
|
||||
if (location >= mod->core_layout.base
|
||||
&& location < mod->core_layout.base + mod->core_layout.size)
|
||||
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr;
|
||||
else
|
||||
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
|
||||
@ -296,7 +296,7 @@ int apply_relocate_add(Elf32_Shdr *sechdrs,
|
||||
}
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
module->arch.tramp =
|
||||
do_plt_call(module->module_core,
|
||||
do_plt_call(module->core_layout.base,
|
||||
(unsigned long)ftrace_caller,
|
||||
sechdrs, module);
|
||||
#endif
|
||||
|
@ -159,11 +159,11 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
|
||||
|
||||
/* Increase core size by size of got & plt and set start
|
||||
offsets for got and plt. */
|
||||
me->core_size = ALIGN(me->core_size, 4);
|
||||
me->arch.got_offset = me->core_size;
|
||||
me->core_size += me->arch.got_size;
|
||||
me->arch.plt_offset = me->core_size;
|
||||
me->core_size += me->arch.plt_size;
|
||||
me->core_layout.size = ALIGN(me->core_layout.size, 4);
|
||||
me->arch.got_offset = me->core_layout.size;
|
||||
me->core_layout.size += me->arch.got_size;
|
||||
me->arch.plt_offset = me->core_layout.size;
|
||||
me->core_layout.size += me->arch.plt_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
if (info->got_initialized == 0) {
|
||||
Elf_Addr *gotent;
|
||||
|
||||
gotent = me->module_core + me->arch.got_offset +
|
||||
gotent = me->core_layout.base + me->arch.got_offset +
|
||||
info->got_offset;
|
||||
*gotent = val;
|
||||
info->got_initialized = 1;
|
||||
@ -302,7 +302,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
rc = apply_rela_bits(loc, val, 0, 64, 0);
|
||||
else if (r_type == R_390_GOTENT ||
|
||||
r_type == R_390_GOTPLTENT) {
|
||||
val += (Elf_Addr) me->module_core - loc;
|
||||
val += (Elf_Addr) me->core_layout.base - loc;
|
||||
rc = apply_rela_bits(loc, val, 1, 32, 1);
|
||||
}
|
||||
break;
|
||||
@ -315,7 +315,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */
|
||||
if (info->plt_initialized == 0) {
|
||||
unsigned int *ip;
|
||||
ip = me->module_core + me->arch.plt_offset +
|
||||
ip = me->core_layout.base + me->arch.plt_offset +
|
||||
info->plt_offset;
|
||||
ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */
|
||||
ip[1] = 0x100a0004;
|
||||
@ -334,7 +334,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
val - loc + 0xffffUL < 0x1ffffeUL) ||
|
||||
(r_type == R_390_PLT32DBL &&
|
||||
val - loc + 0xffffffffULL < 0x1fffffffeULL)))
|
||||
val = (Elf_Addr) me->module_core +
|
||||
val = (Elf_Addr) me->core_layout.base +
|
||||
me->arch.plt_offset +
|
||||
info->plt_offset;
|
||||
val += rela->r_addend - loc;
|
||||
@ -356,7 +356,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
case R_390_GOTOFF32: /* 32 bit offset to GOT. */
|
||||
case R_390_GOTOFF64: /* 64 bit offset to GOT. */
|
||||
val = val + rela->r_addend -
|
||||
((Elf_Addr) me->module_core + me->arch.got_offset);
|
||||
((Elf_Addr) me->core_layout.base + me->arch.got_offset);
|
||||
if (r_type == R_390_GOTOFF16)
|
||||
rc = apply_rela_bits(loc, val, 0, 16, 0);
|
||||
else if (r_type == R_390_GOTOFF32)
|
||||
@ -366,7 +366,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
|
||||
break;
|
||||
case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */
|
||||
case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */
|
||||
val = (Elf_Addr) me->module_core + me->arch.got_offset +
|
||||
val = (Elf_Addr) me->core_layout.base + me->arch.got_offset +
|
||||
rela->r_addend - loc;
|
||||
if (r_type == R_390_GOTPC)
|
||||
rc = apply_rela_bits(loc, val, 1, 32, 0);
|
||||
|
@ -20,8 +20,6 @@
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/page_types.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/livepatch.h>
|
||||
|
||||
@ -38,11 +36,10 @@
|
||||
int klp_write_module_reloc(struct module *mod, unsigned long type,
|
||||
unsigned long loc, unsigned long value)
|
||||
{
|
||||
int ret, numpages, size = 4;
|
||||
bool readonly;
|
||||
size_t size = 4;
|
||||
unsigned long val;
|
||||
unsigned long core = (unsigned long)mod->module_core;
|
||||
unsigned long core_size = mod->core_size;
|
||||
unsigned long core = (unsigned long)mod->core_layout.base;
|
||||
unsigned long core_size = mod->core_layout.size;
|
||||
|
||||
switch (type) {
|
||||
case R_X86_64_NONE:
|
||||
@ -69,23 +66,5 @@ int klp_write_module_reloc(struct module *mod, unsigned long type,
|
||||
/* loc does not point to any symbol inside the module */
|
||||
return -EINVAL;
|
||||
|
||||
readonly = false;
|
||||
|
||||
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
|
||||
if (loc < core + mod->core_ro_size)
|
||||
readonly = true;
|
||||
#endif
|
||||
|
||||
/* determine if the relocation spans a page boundary */
|
||||
numpages = ((loc & PAGE_MASK) == ((loc + size) & PAGE_MASK)) ? 1 : 2;
|
||||
|
||||
if (readonly)
|
||||
set_memory_rw(loc & PAGE_MASK, numpages);
|
||||
|
||||
ret = probe_kernel_write((void *)loc, &val, size);
|
||||
|
||||
if (readonly)
|
||||
set_memory_ro(loc & PAGE_MASK, numpages);
|
||||
|
||||
return ret;
|
||||
return probe_kernel_write((void *)loc, &val, size);
|
||||
}
|
||||
|
@ -37,8 +37,9 @@ enum klp_state {
|
||||
* struct klp_func - function structure for live patching
|
||||
* @old_name: name of the function to be patched
|
||||
* @new_func: pointer to the patched function code
|
||||
* @old_addr: a hint conveying at what address the old function
|
||||
* can be found (optional, vmlinux patches only)
|
||||
* @old_sympos: a hint indicating which symbol position the old function
|
||||
* can be found (optional)
|
||||
* @old_addr: the address of the function being patched
|
||||
* @kobj: kobject for sysfs resources
|
||||
* @state: tracks function-level patch application state
|
||||
* @stack_node: list node for klp_ops func_stack list
|
||||
@ -48,16 +49,16 @@ struct klp_func {
|
||||
const char *old_name;
|
||||
void *new_func;
|
||||
/*
|
||||
* The old_addr field is optional and can be used to resolve
|
||||
* duplicate symbol names in the vmlinux object. If this
|
||||
* information is not present, the symbol is located by name
|
||||
* with kallsyms. If the name is not unique and old_addr is
|
||||
* not provided, the patch application fails as there is no
|
||||
* way to resolve the ambiguity.
|
||||
* The old_sympos field is optional and can be used to resolve
|
||||
* duplicate symbol names in livepatch objects. If this field is zero,
|
||||
* it is expected the symbol is unique, otherwise patching fails. If
|
||||
* this value is greater than zero then that occurrence of the symbol
|
||||
* in kallsyms for the given object is used.
|
||||
*/
|
||||
unsigned long old_addr;
|
||||
unsigned long old_sympos;
|
||||
|
||||
/* internal */
|
||||
unsigned long old_addr;
|
||||
struct kobject kobj;
|
||||
enum klp_state state;
|
||||
struct list_head stack_node;
|
||||
@ -66,8 +67,7 @@ struct klp_func {
|
||||
/**
|
||||
* struct klp_reloc - relocation structure for live patching
|
||||
* @loc: address where the relocation will be written
|
||||
* @val: address of the referenced symbol (optional,
|
||||
* vmlinux patches only)
|
||||
* @sympos: position in kallsyms to disambiguate symbols (optional)
|
||||
* @type: ELF relocation type
|
||||
* @name: name of the referenced symbol (for lookup/verification)
|
||||
* @addend: offset from the referenced symbol
|
||||
@ -75,7 +75,7 @@ struct klp_func {
|
||||
*/
|
||||
struct klp_reloc {
|
||||
unsigned long loc;
|
||||
unsigned long val;
|
||||
unsigned long sympos;
|
||||
unsigned long type;
|
||||
const char *name;
|
||||
int addend;
|
||||
|
@ -302,6 +302,28 @@ struct mod_tree_node {
|
||||
struct latch_tree_node node;
|
||||
};
|
||||
|
||||
struct module_layout {
|
||||
/* The actual code + data. */
|
||||
void *base;
|
||||
/* Total size. */
|
||||
unsigned int size;
|
||||
/* The size of the executable code. */
|
||||
unsigned int text_size;
|
||||
/* Size of RO section of the module (text+rodata) */
|
||||
unsigned int ro_size;
|
||||
|
||||
#ifdef CONFIG_MODULES_TREE_LOOKUP
|
||||
struct mod_tree_node mtn;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_MODULES_TREE_LOOKUP
|
||||
/* Only touch one cacheline for common rbtree-for-core-layout case. */
|
||||
#define __module_layout_align ____cacheline_aligned
|
||||
#else
|
||||
#define __module_layout_align
|
||||
#endif
|
||||
|
||||
struct module {
|
||||
enum module_state state;
|
||||
|
||||
@ -366,37 +388,9 @@ struct module {
|
||||
/* Startup function. */
|
||||
int (*init)(void);
|
||||
|
||||
/*
|
||||
* If this is non-NULL, vfree() after init() returns.
|
||||
*
|
||||
* Cacheline align here, such that:
|
||||
* module_init, module_core, init_size, core_size,
|
||||
* init_text_size, core_text_size and mtn_core::{mod,node[0]}
|
||||
* are on the same cacheline.
|
||||
*/
|
||||
void *module_init ____cacheline_aligned;
|
||||
|
||||
/* Here is the actual code + data, vfree'd on unload. */
|
||||
void *module_core;
|
||||
|
||||
/* Here are the sizes of the init and core sections */
|
||||
unsigned int init_size, core_size;
|
||||
|
||||
/* The size of the executable code in each section. */
|
||||
unsigned int init_text_size, core_text_size;
|
||||
|
||||
#ifdef CONFIG_MODULES_TREE_LOOKUP
|
||||
/*
|
||||
* We want mtn_core::{mod,node[0]} to be in the same cacheline as the
|
||||
* above entries such that a regular lookup will only touch one
|
||||
* cacheline.
|
||||
*/
|
||||
struct mod_tree_node mtn_core;
|
||||
struct mod_tree_node mtn_init;
|
||||
#endif
|
||||
|
||||
/* Size of RO sections of the module (text+rodata) */
|
||||
unsigned int init_ro_size, core_ro_size;
|
||||
/* Core layout: rbtree is accessed frequently, so keep together. */
|
||||
struct module_layout core_layout __module_layout_align;
|
||||
struct module_layout init_layout;
|
||||
|
||||
/* Arch-specific module values */
|
||||
struct mod_arch_specific arch;
|
||||
@ -505,15 +499,15 @@ bool is_module_text_address(unsigned long addr);
|
||||
static inline bool within_module_core(unsigned long addr,
|
||||
const struct module *mod)
|
||||
{
|
||||
return (unsigned long)mod->module_core <= addr &&
|
||||
addr < (unsigned long)mod->module_core + mod->core_size;
|
||||
return (unsigned long)mod->core_layout.base <= addr &&
|
||||
addr < (unsigned long)mod->core_layout.base + mod->core_layout.size;
|
||||
}
|
||||
|
||||
static inline bool within_module_init(unsigned long addr,
|
||||
const struct module *mod)
|
||||
{
|
||||
return (unsigned long)mod->module_init <= addr &&
|
||||
addr < (unsigned long)mod->module_init + mod->init_size;
|
||||
return (unsigned long)mod->init_layout.base <= addr &&
|
||||
addr < (unsigned long)mod->init_layout.base + mod->init_layout.size;
|
||||
}
|
||||
|
||||
static inline bool within_module(unsigned long addr, const struct module *mod)
|
||||
@ -768,9 +762,13 @@ extern int module_sysfs_initialized;
|
||||
#ifdef CONFIG_DEBUG_SET_MODULE_RONX
|
||||
extern void set_all_modules_text_rw(void);
|
||||
extern void set_all_modules_text_ro(void);
|
||||
extern void module_enable_ro(const struct module *mod);
|
||||
extern void module_disable_ro(const struct module *mod);
|
||||
#else
|
||||
static inline void set_all_modules_text_rw(void) { }
|
||||
static inline void set_all_modules_text_ro(void) { }
|
||||
static inline void module_enable_ro(const struct module *mod) { }
|
||||
static inline void module_disable_ro(const struct module *mod) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GENERIC_BUG
|
||||
|
@ -2021,7 +2021,7 @@ static int kdb_lsmod(int argc, const char **argv)
|
||||
continue;
|
||||
|
||||
kdb_printf("%-20s%8u 0x%p ", mod->name,
|
||||
mod->core_size, (void *)mod);
|
||||
mod->core_layout.size, (void *)mod);
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
kdb_printf("%4d ", module_refcount(mod));
|
||||
#endif
|
||||
@ -2031,7 +2031,7 @@ static int kdb_lsmod(int argc, const char **argv)
|
||||
kdb_printf(" (Loading)");
|
||||
else
|
||||
kdb_printf(" (Live)");
|
||||
kdb_printf(" 0x%p", mod->module_core);
|
||||
kdb_printf(" 0x%p", mod->core_layout.base);
|
||||
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
{
|
||||
|
@ -123,11 +123,6 @@ void gcov_enable_events(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
static inline int within(void *addr, void *start, unsigned long size)
|
||||
{
|
||||
return ((addr >= start) && (addr < start + size));
|
||||
}
|
||||
|
||||
/* Update list and generate events when modules are unloaded. */
|
||||
static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
|
||||
void *data)
|
||||
@ -142,7 +137,7 @@ static int gcov_module_notifier(struct notifier_block *nb, unsigned long event,
|
||||
|
||||
/* Remove entries located in module from linked list. */
|
||||
while ((info = gcov_info_next(info))) {
|
||||
if (within(info, mod->module_core, mod->core_size)) {
|
||||
if (within_module((unsigned long)info, mod)) {
|
||||
gcov_info_unlink(prev, info);
|
||||
if (gcov_events_enabled)
|
||||
gcov_event(GCOV_REMOVE, info);
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/livepatch.h>
|
||||
#include <asm/cacheflush.h>
|
||||
|
||||
/**
|
||||
* struct klp_ops - structure for tracking registered ftrace ops structs
|
||||
@ -135,13 +136,8 @@ struct klp_find_arg {
|
||||
const char *objname;
|
||||
const char *name;
|
||||
unsigned long addr;
|
||||
/*
|
||||
* If count == 0, the symbol was not found. If count == 1, a unique
|
||||
* match was found and addr is set. If count > 1, there is
|
||||
* unresolvable ambiguity among "count" number of symbols with the same
|
||||
* name in the same object.
|
||||
*/
|
||||
unsigned long count;
|
||||
unsigned long pos;
|
||||
};
|
||||
|
||||
static int klp_find_callback(void *data, const char *name,
|
||||
@ -158,37 +154,48 @@ static int klp_find_callback(void *data, const char *name,
|
||||
if (args->objname && strcmp(args->objname, mod->name))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* args->addr might be overwritten if another match is found
|
||||
* but klp_find_object_symbol() handles this and only returns the
|
||||
* addr if count == 1.
|
||||
*/
|
||||
args->addr = addr;
|
||||
args->count++;
|
||||
|
||||
/*
|
||||
* Finish the search when the symbol is found for the desired position
|
||||
* or the position is not defined for a non-unique symbol.
|
||||
*/
|
||||
if ((args->pos && (args->count == args->pos)) ||
|
||||
(!args->pos && (args->count > 1)))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int klp_find_object_symbol(const char *objname, const char *name,
|
||||
unsigned long *addr)
|
||||
unsigned long sympos, unsigned long *addr)
|
||||
{
|
||||
struct klp_find_arg args = {
|
||||
.objname = objname,
|
||||
.name = name,
|
||||
.addr = 0,
|
||||
.count = 0
|
||||
.count = 0,
|
||||
.pos = sympos,
|
||||
};
|
||||
|
||||
mutex_lock(&module_mutex);
|
||||
kallsyms_on_each_symbol(klp_find_callback, &args);
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
if (args.count == 0)
|
||||
/*
|
||||
* Ensure an address was found. If sympos is 0, ensure symbol is unique;
|
||||
* otherwise ensure the symbol position count matches sympos.
|
||||
*/
|
||||
if (args.addr == 0)
|
||||
pr_err("symbol '%s' not found in symbol table\n", name);
|
||||
else if (args.count > 1)
|
||||
else if (args.count > 1 && sympos == 0) {
|
||||
pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
|
||||
args.count, name, objname);
|
||||
else {
|
||||
} else if (sympos != args.count && sympos > 0) {
|
||||
pr_err("symbol position %lu for symbol '%s' in object '%s' not found\n",
|
||||
sympos, name, objname ? objname : "vmlinux");
|
||||
} else {
|
||||
*addr = args.addr;
|
||||
return 0;
|
||||
}
|
||||
@ -197,66 +204,6 @@ static int klp_find_object_symbol(const char *objname, const char *name,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct klp_verify_args {
|
||||
const char *name;
|
||||
const unsigned long addr;
|
||||
};
|
||||
|
||||
static int klp_verify_callback(void *data, const char *name,
|
||||
struct module *mod, unsigned long addr)
|
||||
{
|
||||
struct klp_verify_args *args = data;
|
||||
|
||||
if (!mod &&
|
||||
!strcmp(args->name, name) &&
|
||||
args->addr == addr)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int klp_verify_vmlinux_symbol(const char *name, unsigned long addr)
|
||||
{
|
||||
struct klp_verify_args args = {
|
||||
.name = name,
|
||||
.addr = addr,
|
||||
};
|
||||
int ret;
|
||||
|
||||
mutex_lock(&module_mutex);
|
||||
ret = kallsyms_on_each_symbol(klp_verify_callback, &args);
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
if (!ret) {
|
||||
pr_err("symbol '%s' not found at specified address 0x%016lx, kernel mismatch?\n",
|
||||
name, addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int klp_find_verify_func_addr(struct klp_object *obj,
|
||||
struct klp_func *func)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if defined(CONFIG_RANDOMIZE_BASE)
|
||||
/* If KASLR has been enabled, adjust old_addr accordingly */
|
||||
if (kaslr_enabled() && func->old_addr)
|
||||
func->old_addr += kaslr_offset();
|
||||
#endif
|
||||
|
||||
if (!func->old_addr || klp_is_module(obj))
|
||||
ret = klp_find_object_symbol(obj->name, func->old_name,
|
||||
&func->old_addr);
|
||||
else
|
||||
ret = klp_verify_vmlinux_symbol(func->old_name,
|
||||
func->old_addr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* external symbols are located outside the parent object (where the parent
|
||||
* object is either vmlinux or the kmod being patched).
|
||||
@ -276,14 +223,18 @@ static int klp_find_external_symbol(struct module *pmod, const char *name,
|
||||
}
|
||||
preempt_enable();
|
||||
|
||||
/* otherwise check if it's in another .o within the patch module */
|
||||
return klp_find_object_symbol(pmod->name, name, addr);
|
||||
/*
|
||||
* Check if it's in another .o within the patch module. This also
|
||||
* checks that the external symbol is unique.
|
||||
*/
|
||||
return klp_find_object_symbol(pmod->name, name, 0, addr);
|
||||
}
|
||||
|
||||
static int klp_write_object_relocations(struct module *pmod,
|
||||
struct klp_object *obj)
|
||||
{
|
||||
int ret;
|
||||
int ret = 0;
|
||||
unsigned long val;
|
||||
struct klp_reloc *reloc;
|
||||
|
||||
if (WARN_ON(!klp_is_object_loaded(obj)))
|
||||
@ -292,41 +243,38 @@ static int klp_write_object_relocations(struct module *pmod,
|
||||
if (WARN_ON(!obj->relocs))
|
||||
return -EINVAL;
|
||||
|
||||
for (reloc = obj->relocs; reloc->name; reloc++) {
|
||||
if (!klp_is_module(obj)) {
|
||||
module_disable_ro(pmod);
|
||||
|
||||
for (reloc = obj->relocs; reloc->name; reloc++) {
|
||||
/* discover the address of the referenced symbol */
|
||||
if (reloc->external) {
|
||||
if (reloc->sympos > 0) {
|
||||
pr_err("non-zero sympos for external reloc symbol '%s' is not supported\n",
|
||||
reloc->name);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = klp_find_external_symbol(pmod, reloc->name, &val);
|
||||
} else
|
||||
ret = klp_find_object_symbol(obj->name,
|
||||
reloc->name,
|
||||
reloc->sympos,
|
||||
&val);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
#if defined(CONFIG_RANDOMIZE_BASE)
|
||||
/* If KASLR has been enabled, adjust old value accordingly */
|
||||
if (kaslr_enabled())
|
||||
reloc->val += kaslr_offset();
|
||||
#endif
|
||||
ret = klp_verify_vmlinux_symbol(reloc->name,
|
||||
reloc->val);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
/* module, reloc->val needs to be discovered */
|
||||
if (reloc->external)
|
||||
ret = klp_find_external_symbol(pmod,
|
||||
reloc->name,
|
||||
&reloc->val);
|
||||
else
|
||||
ret = klp_find_object_symbol(obj->mod->name,
|
||||
reloc->name,
|
||||
&reloc->val);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = klp_write_module_reloc(pmod, reloc->type, reloc->loc,
|
||||
reloc->val + reloc->addend);
|
||||
val + reloc->addend);
|
||||
if (ret) {
|
||||
pr_err("relocation failed for symbol '%s' at 0x%016lx (%d)\n",
|
||||
reloc->name, reloc->val, ret);
|
||||
return ret;
|
||||
reloc->name, val, ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
module_enable_ro(pmod);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void notrace klp_ftrace_handler(unsigned long ip,
|
||||
@ -593,7 +541,7 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
|
||||
* /sys/kernel/livepatch/<patch>
|
||||
* /sys/kernel/livepatch/<patch>/enabled
|
||||
* /sys/kernel/livepatch/<patch>/<object>
|
||||
* /sys/kernel/livepatch/<patch>/<object>/<func>
|
||||
* /sys/kernel/livepatch/<patch>/<object>/<function,sympos>
|
||||
*/
|
||||
|
||||
static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
|
||||
@ -738,8 +686,14 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
|
||||
INIT_LIST_HEAD(&func->stack_node);
|
||||
func->state = KLP_DISABLED;
|
||||
|
||||
/* The format for the sysfs directory is <function,sympos> where sympos
|
||||
* is the nth occurrence of this symbol in kallsyms for the patched
|
||||
* object. If the user selects 0 for old_sympos, then 1 will be used
|
||||
* since a unique symbol will be the first occurrence.
|
||||
*/
|
||||
return kobject_init_and_add(&func->kobj, &klp_ktype_func,
|
||||
&obj->kobj, "%s", func->old_name);
|
||||
&obj->kobj, "%s,%lu", func->old_name,
|
||||
func->old_sympos ? func->old_sympos : 1);
|
||||
}
|
||||
|
||||
/* parts of the initialization that is done only when the object is loaded */
|
||||
@ -756,7 +710,9 @@ static int klp_init_object_loaded(struct klp_patch *patch,
|
||||
}
|
||||
|
||||
klp_for_each_func(obj, func) {
|
||||
ret = klp_find_verify_func_addr(obj, func);
|
||||
ret = klp_find_object_symbol(obj->name, func->old_name,
|
||||
func->old_sympos,
|
||||
&func->old_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
355
kernel/module.c
355
kernel/module.c
@ -80,15 +80,6 @@
|
||||
# define debug_align(X) (X)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Given BASE and SIZE this macro calculates the number of pages the
|
||||
* memory regions occupies
|
||||
*/
|
||||
#define MOD_NUMBER_OF_PAGES(BASE, SIZE) (((SIZE) > 0) ? \
|
||||
(PFN_DOWN((unsigned long)(BASE) + (SIZE) - 1) - \
|
||||
PFN_DOWN((unsigned long)BASE) + 1) \
|
||||
: (0UL))
|
||||
|
||||
/* If this is set, the section belongs in the init part of the module */
|
||||
#define INIT_OFFSET_MASK (1UL << (BITS_PER_LONG-1))
|
||||
|
||||
@ -108,13 +99,6 @@ static LIST_HEAD(modules);
|
||||
* Use a latched RB-tree for __module_address(); this allows us to use
|
||||
* RCU-sched lookups of the address from any context.
|
||||
*
|
||||
* Because modules have two address ranges: init and core, we need two
|
||||
* latch_tree_nodes entries. Therefore we need the back-pointer from
|
||||
* mod_tree_node.
|
||||
*
|
||||
* Because init ranges are short lived we mark them unlikely and have placed
|
||||
* them outside the critical cacheline in struct module.
|
||||
*
|
||||
* This is conditional on PERF_EVENTS || TRACING because those can really hit
|
||||
* __module_address() hard by doing a lot of stack unwinding; potentially from
|
||||
* NMI context.
|
||||
@ -122,24 +106,16 @@ static LIST_HEAD(modules);
|
||||
|
||||
static __always_inline unsigned long __mod_tree_val(struct latch_tree_node *n)
|
||||
{
|
||||
struct mod_tree_node *mtn = container_of(n, struct mod_tree_node, node);
|
||||
struct module *mod = mtn->mod;
|
||||
struct module_layout *layout = container_of(n, struct module_layout, mtn.node);
|
||||
|
||||
if (unlikely(mtn == &mod->mtn_init))
|
||||
return (unsigned long)mod->module_init;
|
||||
|
||||
return (unsigned long)mod->module_core;
|
||||
return (unsigned long)layout->base;
|
||||
}
|
||||
|
||||
static __always_inline unsigned long __mod_tree_size(struct latch_tree_node *n)
|
||||
{
|
||||
struct mod_tree_node *mtn = container_of(n, struct mod_tree_node, node);
|
||||
struct module *mod = mtn->mod;
|
||||
struct module_layout *layout = container_of(n, struct module_layout, mtn.node);
|
||||
|
||||
if (unlikely(mtn == &mod->mtn_init))
|
||||
return (unsigned long)mod->init_size;
|
||||
|
||||
return (unsigned long)mod->core_size;
|
||||
return (unsigned long)layout->size;
|
||||
}
|
||||
|
||||
static __always_inline bool
|
||||
@ -197,23 +173,23 @@ static void __mod_tree_remove(struct mod_tree_node *node)
|
||||
*/
|
||||
static void mod_tree_insert(struct module *mod)
|
||||
{
|
||||
mod->mtn_core.mod = mod;
|
||||
mod->mtn_init.mod = mod;
|
||||
mod->core_layout.mtn.mod = mod;
|
||||
mod->init_layout.mtn.mod = mod;
|
||||
|
||||
__mod_tree_insert(&mod->mtn_core);
|
||||
if (mod->init_size)
|
||||
__mod_tree_insert(&mod->mtn_init);
|
||||
__mod_tree_insert(&mod->core_layout.mtn);
|
||||
if (mod->init_layout.size)
|
||||
__mod_tree_insert(&mod->init_layout.mtn);
|
||||
}
|
||||
|
||||
static void mod_tree_remove_init(struct module *mod)
|
||||
{
|
||||
if (mod->init_size)
|
||||
__mod_tree_remove(&mod->mtn_init);
|
||||
if (mod->init_layout.size)
|
||||
__mod_tree_remove(&mod->init_layout.mtn);
|
||||
}
|
||||
|
||||
static void mod_tree_remove(struct module *mod)
|
||||
{
|
||||
__mod_tree_remove(&mod->mtn_core);
|
||||
__mod_tree_remove(&mod->core_layout.mtn);
|
||||
mod_tree_remove_init(mod);
|
||||
}
|
||||
|
||||
@ -267,9 +243,9 @@ static void __mod_update_bounds(void *base, unsigned int size)
|
||||
|
||||
static void mod_update_bounds(struct module *mod)
|
||||
{
|
||||
__mod_update_bounds(mod->module_core, mod->core_size);
|
||||
if (mod->init_size)
|
||||
__mod_update_bounds(mod->module_init, mod->init_size);
|
||||
__mod_update_bounds(mod->core_layout.base, mod->core_layout.size);
|
||||
if (mod->init_layout.size)
|
||||
__mod_update_bounds(mod->init_layout.base, mod->init_layout.size);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KGDB_KDB
|
||||
@ -1214,7 +1190,7 @@ struct module_attribute module_uevent =
|
||||
static ssize_t show_coresize(struct module_attribute *mattr,
|
||||
struct module_kobject *mk, char *buffer)
|
||||
{
|
||||
return sprintf(buffer, "%u\n", mk->mod->core_size);
|
||||
return sprintf(buffer, "%u\n", mk->mod->core_layout.size);
|
||||
}
|
||||
|
||||
static struct module_attribute modinfo_coresize =
|
||||
@ -1223,7 +1199,7 @@ static struct module_attribute modinfo_coresize =
|
||||
static ssize_t show_initsize(struct module_attribute *mattr,
|
||||
struct module_kobject *mk, char *buffer)
|
||||
{
|
||||
return sprintf(buffer, "%u\n", mk->mod->init_size);
|
||||
return sprintf(buffer, "%u\n", mk->mod->init_layout.size);
|
||||
}
|
||||
|
||||
static struct module_attribute modinfo_initsize =
|
||||
@ -1873,64 +1849,75 @@ static void mod_sysfs_teardown(struct module *mod)
|
||||
/*
|
||||
* LKM RO/NX protection: protect module's text/ro-data
|
||||
* from modification and any data from execution.
|
||||
*
|
||||
* General layout of module is:
|
||||
* [text] [read-only-data] [writable data]
|
||||
* text_size -----^ ^ ^
|
||||
* ro_size ------------------------| |
|
||||
* size -------------------------------------------|
|
||||
*
|
||||
* These values are always page-aligned (as is base)
|
||||
*/
|
||||
void set_page_attributes(void *start, void *end, int (*set)(unsigned long start, int num_pages))
|
||||
static void frob_text(const struct module_layout *layout,
|
||||
int (*set_memory)(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);
|
||||
BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
|
||||
BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
|
||||
set_memory((unsigned long)layout->base,
|
||||
layout->text_size >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
static void set_section_ro_nx(void *base,
|
||||
unsigned long text_size,
|
||||
unsigned long ro_size,
|
||||
unsigned long total_size)
|
||||
static void frob_rodata(const struct module_layout *layout,
|
||||
int (*set_memory)(unsigned long start, int num_pages))
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
|
||||
BUG_ON((unsigned long)layout->text_size & (PAGE_SIZE-1));
|
||||
BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
|
||||
set_memory((unsigned long)layout->base + layout->text_size,
|
||||
(layout->ro_size - layout->text_size) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
static void unset_module_core_ro_nx(struct module *mod)
|
||||
static void frob_writable_data(const struct module_layout *layout,
|
||||
int (*set_memory)(unsigned long start, int num_pages))
|
||||
{
|
||||
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);
|
||||
BUG_ON((unsigned long)layout->base & (PAGE_SIZE-1));
|
||||
BUG_ON((unsigned long)layout->ro_size & (PAGE_SIZE-1));
|
||||
BUG_ON((unsigned long)layout->size & (PAGE_SIZE-1));
|
||||
set_memory((unsigned long)layout->base + layout->ro_size,
|
||||
(layout->size - layout->ro_size) >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
static void unset_module_init_ro_nx(struct module *mod)
|
||||
/* livepatching wants to disable read-only so it can frob module. */
|
||||
void module_disable_ro(const 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);
|
||||
frob_text(&mod->core_layout, set_memory_rw);
|
||||
frob_rodata(&mod->core_layout, set_memory_rw);
|
||||
frob_text(&mod->init_layout, set_memory_rw);
|
||||
frob_rodata(&mod->init_layout, set_memory_rw);
|
||||
}
|
||||
|
||||
void module_enable_ro(const struct module *mod)
|
||||
{
|
||||
frob_text(&mod->core_layout, set_memory_ro);
|
||||
frob_rodata(&mod->core_layout, set_memory_ro);
|
||||
frob_text(&mod->init_layout, set_memory_ro);
|
||||
frob_rodata(&mod->init_layout, set_memory_ro);
|
||||
}
|
||||
|
||||
static void module_enable_nx(const struct module *mod)
|
||||
{
|
||||
frob_rodata(&mod->core_layout, set_memory_nx);
|
||||
frob_writable_data(&mod->core_layout, set_memory_nx);
|
||||
frob_rodata(&mod->init_layout, set_memory_nx);
|
||||
frob_writable_data(&mod->init_layout, set_memory_nx);
|
||||
}
|
||||
|
||||
static void module_disable_nx(const struct module *mod)
|
||||
{
|
||||
frob_rodata(&mod->core_layout, set_memory_x);
|
||||
frob_writable_data(&mod->core_layout, set_memory_x);
|
||||
frob_rodata(&mod->init_layout, set_memory_x);
|
||||
frob_writable_data(&mod->init_layout, set_memory_x);
|
||||
}
|
||||
|
||||
/* Iterate through all modules and set each module's text as RW */
|
||||
@ -1942,16 +1929,9 @@ void set_all_modules_text_rw(void)
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (mod->state == MODULE_STATE_UNFORMED)
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
|
||||
frob_text(&mod->core_layout, set_memory_rw);
|
||||
frob_text(&mod->init_layout, set_memory_rw);
|
||||
}
|
||||
mutex_unlock(&module_mutex);
|
||||
}
|
||||
@ -1965,23 +1945,25 @@ void set_all_modules_text_ro(void)
|
||||
list_for_each_entry_rcu(mod, &modules, list) {
|
||||
if (mod->state == MODULE_STATE_UNFORMED)
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
|
||||
frob_text(&mod->core_layout, set_memory_ro);
|
||||
frob_text(&mod->init_layout, set_memory_ro);
|
||||
}
|
||||
mutex_unlock(&module_mutex);
|
||||
}
|
||||
|
||||
static void disable_ro_nx(const struct module_layout *layout)
|
||||
{
|
||||
frob_text(layout, set_memory_rw);
|
||||
frob_rodata(layout, set_memory_rw);
|
||||
frob_rodata(layout, set_memory_x);
|
||||
frob_writable_data(layout, set_memory_x);
|
||||
}
|
||||
|
||||
#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) { }
|
||||
static void disable_ro_nx(const struct module_layout *layout) { }
|
||||
static void module_enable_nx(const struct module *mod) { }
|
||||
static void module_disable_nx(const struct module *mod) { }
|
||||
#endif
|
||||
|
||||
void __weak module_memfree(void *module_region)
|
||||
@ -2033,19 +2015,19 @@ static void free_module(struct module *mod)
|
||||
synchronize_sched();
|
||||
mutex_unlock(&module_mutex);
|
||||
|
||||
/* This may be NULL, but that's OK */
|
||||
unset_module_init_ro_nx(mod);
|
||||
/* This may be empty, but that's OK */
|
||||
disable_ro_nx(&mod->init_layout);
|
||||
module_arch_freeing_init(mod);
|
||||
module_memfree(mod->module_init);
|
||||
module_memfree(mod->init_layout.base);
|
||||
kfree(mod->args);
|
||||
percpu_modfree(mod);
|
||||
|
||||
/* Free lock-classes; relies on the preceding sync_rcu(). */
|
||||
lockdep_free_key_range(mod->module_core, mod->core_size);
|
||||
lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);
|
||||
|
||||
/* Finally, free the core (containing the module structure) */
|
||||
unset_module_core_ro_nx(mod);
|
||||
module_memfree(mod->module_core);
|
||||
disable_ro_nx(&mod->core_layout);
|
||||
module_memfree(mod->core_layout.base);
|
||||
|
||||
#ifdef CONFIG_MPU
|
||||
update_protections(current->mm);
|
||||
@ -2248,20 +2230,20 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
||||
|| s->sh_entsize != ~0UL
|
||||
|| strstarts(sname, ".init"))
|
||||
continue;
|
||||
s->sh_entsize = get_offset(mod, &mod->core_size, s, i);
|
||||
s->sh_entsize = get_offset(mod, &mod->core_layout.size, s, i);
|
||||
pr_debug("\t%s\n", sname);
|
||||
}
|
||||
switch (m) {
|
||||
case 0: /* executable */
|
||||
mod->core_size = debug_align(mod->core_size);
|
||||
mod->core_text_size = mod->core_size;
|
||||
mod->core_layout.size = debug_align(mod->core_layout.size);
|
||||
mod->core_layout.text_size = mod->core_layout.size;
|
||||
break;
|
||||
case 1: /* RO: text and ro-data */
|
||||
mod->core_size = debug_align(mod->core_size);
|
||||
mod->core_ro_size = mod->core_size;
|
||||
mod->core_layout.size = debug_align(mod->core_layout.size);
|
||||
mod->core_layout.ro_size = mod->core_layout.size;
|
||||
break;
|
||||
case 3: /* whole core */
|
||||
mod->core_size = debug_align(mod->core_size);
|
||||
mod->core_layout.size = debug_align(mod->core_layout.size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2277,21 +2259,21 @@ static void layout_sections(struct module *mod, struct load_info *info)
|
||||
|| s->sh_entsize != ~0UL
|
||||
|| !strstarts(sname, ".init"))
|
||||
continue;
|
||||
s->sh_entsize = (get_offset(mod, &mod->init_size, s, i)
|
||||
s->sh_entsize = (get_offset(mod, &mod->init_layout.size, s, i)
|
||||
| INIT_OFFSET_MASK);
|
||||
pr_debug("\t%s\n", sname);
|
||||
}
|
||||
switch (m) {
|
||||
case 0: /* executable */
|
||||
mod->init_size = debug_align(mod->init_size);
|
||||
mod->init_text_size = mod->init_size;
|
||||
mod->init_layout.size = debug_align(mod->init_layout.size);
|
||||
mod->init_layout.text_size = mod->init_layout.size;
|
||||
break;
|
||||
case 1: /* RO: text and ro-data */
|
||||
mod->init_size = debug_align(mod->init_size);
|
||||
mod->init_ro_size = mod->init_size;
|
||||
mod->init_layout.size = debug_align(mod->init_layout.size);
|
||||
mod->init_layout.ro_size = mod->init_layout.size;
|
||||
break;
|
||||
case 3: /* whole init */
|
||||
mod->init_size = debug_align(mod->init_size);
|
||||
mod->init_layout.size = debug_align(mod->init_layout.size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2401,7 +2383,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
|
||||
}
|
||||
if (sym->st_shndx == SHN_UNDEF)
|
||||
return 'U';
|
||||
if (sym->st_shndx == SHN_ABS)
|
||||
if (sym->st_shndx == SHN_ABS || sym->st_shndx == info->index.pcpu)
|
||||
return 'a';
|
||||
if (sym->st_shndx >= SHN_LORESERVE)
|
||||
return '?';
|
||||
@ -2430,7 +2412,7 @@ static char elf_type(const Elf_Sym *sym, const struct load_info *info)
|
||||
}
|
||||
|
||||
static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
|
||||
unsigned int shnum)
|
||||
unsigned int shnum, unsigned int pcpundx)
|
||||
{
|
||||
const Elf_Shdr *sec;
|
||||
|
||||
@ -2439,6 +2421,11 @@ static bool is_core_symbol(const Elf_Sym *src, const Elf_Shdr *sechdrs,
|
||||
|| !src->st_name)
|
||||
return false;
|
||||
|
||||
#ifdef CONFIG_KALLSYMS_ALL
|
||||
if (src->st_shndx == pcpundx)
|
||||
return true;
|
||||
#endif
|
||||
|
||||
sec = sechdrs + src->st_shndx;
|
||||
if (!(sec->sh_flags & SHF_ALLOC)
|
||||
#ifndef CONFIG_KALLSYMS_ALL
|
||||
@ -2466,7 +2453,7 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
||||
|
||||
/* Put symbol section at end of init part of module. */
|
||||
symsect->sh_flags |= SHF_ALLOC;
|
||||
symsect->sh_entsize = get_offset(mod, &mod->init_size, symsect,
|
||||
symsect->sh_entsize = get_offset(mod, &mod->init_layout.size, symsect,
|
||||
info->index.sym) | INIT_OFFSET_MASK;
|
||||
pr_debug("\t%s\n", info->secstrings + symsect->sh_name);
|
||||
|
||||
@ -2476,23 +2463,24 @@ static void layout_symtab(struct module *mod, struct load_info *info)
|
||||
/* Compute total space required for the core symbols' strtab. */
|
||||
for (ndst = i = 0; i < nsrc; i++) {
|
||||
if (i == 0 ||
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
|
||||
info->index.pcpu)) {
|
||||
strtab_size += strlen(&info->strtab[src[i].st_name])+1;
|
||||
ndst++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Append room for core symbols at end of core part. */
|
||||
info->symoffs = ALIGN(mod->core_size, symsect->sh_addralign ?: 1);
|
||||
info->stroffs = mod->core_size = info->symoffs + ndst * sizeof(Elf_Sym);
|
||||
mod->core_size += strtab_size;
|
||||
mod->core_size = debug_align(mod->core_size);
|
||||
info->symoffs = ALIGN(mod->core_layout.size, symsect->sh_addralign ?: 1);
|
||||
info->stroffs = mod->core_layout.size = info->symoffs + ndst * sizeof(Elf_Sym);
|
||||
mod->core_layout.size += strtab_size;
|
||||
mod->core_layout.size = debug_align(mod->core_layout.size);
|
||||
|
||||
/* Put string table section at end of init part of module. */
|
||||
strsect->sh_flags |= SHF_ALLOC;
|
||||
strsect->sh_entsize = get_offset(mod, &mod->init_size, strsect,
|
||||
strsect->sh_entsize = get_offset(mod, &mod->init_layout.size, strsect,
|
||||
info->index.str) | INIT_OFFSET_MASK;
|
||||
mod->init_size = debug_align(mod->init_size);
|
||||
mod->init_layout.size = debug_align(mod->init_layout.size);
|
||||
pr_debug("\t%s\n", info->secstrings + strsect->sh_name);
|
||||
}
|
||||
|
||||
@ -2513,12 +2501,13 @@ static void add_kallsyms(struct module *mod, const struct load_info *info)
|
||||
for (i = 0; i < mod->num_symtab; i++)
|
||||
mod->symtab[i].st_info = elf_type(&mod->symtab[i], info);
|
||||
|
||||
mod->core_symtab = dst = mod->module_core + info->symoffs;
|
||||
mod->core_strtab = s = mod->module_core + info->stroffs;
|
||||
mod->core_symtab = dst = mod->core_layout.base + info->symoffs;
|
||||
mod->core_strtab = s = mod->core_layout.base + info->stroffs;
|
||||
src = mod->symtab;
|
||||
for (ndst = i = 0; i < mod->num_symtab; i++) {
|
||||
if (i == 0 ||
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum)) {
|
||||
is_core_symbol(src+i, info->sechdrs, info->hdr->e_shnum,
|
||||
info->index.pcpu)) {
|
||||
dst[ndst] = src[i];
|
||||
dst[ndst++].st_name = s - mod->core_strtab;
|
||||
s += strlcpy(s, &mod->strtab[src[i].st_name],
|
||||
@ -2964,7 +2953,7 @@ static int move_module(struct module *mod, struct load_info *info)
|
||||
void *ptr;
|
||||
|
||||
/* Do the allocs. */
|
||||
ptr = module_alloc(mod->core_size);
|
||||
ptr = module_alloc(mod->core_layout.size);
|
||||
/*
|
||||
* The pointer to this block is stored in the module structure
|
||||
* which is inside the block. Just mark it as not being a
|
||||
@ -2974,11 +2963,11 @@ static int move_module(struct module *mod, struct load_info *info)
|
||||
if (!ptr)
|
||||
return -ENOMEM;
|
||||
|
||||
memset(ptr, 0, mod->core_size);
|
||||
mod->module_core = ptr;
|
||||
memset(ptr, 0, mod->core_layout.size);
|
||||
mod->core_layout.base = ptr;
|
||||
|
||||
if (mod->init_size) {
|
||||
ptr = module_alloc(mod->init_size);
|
||||
if (mod->init_layout.size) {
|
||||
ptr = module_alloc(mod->init_layout.size);
|
||||
/*
|
||||
* The pointer to this block is stored in the module structure
|
||||
* which is inside the block. This block doesn't need to be
|
||||
@ -2987,13 +2976,13 @@ static int move_module(struct module *mod, struct load_info *info)
|
||||
*/
|
||||
kmemleak_ignore(ptr);
|
||||
if (!ptr) {
|
||||
module_memfree(mod->module_core);
|
||||
module_memfree(mod->core_layout.base);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(ptr, 0, mod->init_size);
|
||||
mod->module_init = ptr;
|
||||
memset(ptr, 0, mod->init_layout.size);
|
||||
mod->init_layout.base = ptr;
|
||||
} else
|
||||
mod->module_init = NULL;
|
||||
mod->init_layout.base = NULL;
|
||||
|
||||
/* Transfer each section which specifies SHF_ALLOC */
|
||||
pr_debug("final section addresses:\n");
|
||||
@ -3005,10 +2994,10 @@ static int move_module(struct module *mod, struct load_info *info)
|
||||
continue;
|
||||
|
||||
if (shdr->sh_entsize & INIT_OFFSET_MASK)
|
||||
dest = mod->module_init
|
||||
dest = mod->init_layout.base
|
||||
+ (shdr->sh_entsize & ~INIT_OFFSET_MASK);
|
||||
else
|
||||
dest = mod->module_core + shdr->sh_entsize;
|
||||
dest = mod->core_layout.base + shdr->sh_entsize;
|
||||
|
||||
if (shdr->sh_type != SHT_NOBITS)
|
||||
memcpy(dest, (void *)shdr->sh_addr, shdr->sh_size);
|
||||
@ -3070,12 +3059,12 @@ static void flush_module_icache(const struct module *mod)
|
||||
* Do it before processing of module parameters, so the module
|
||||
* can provide parameter accessor functions of its own.
|
||||
*/
|
||||
if (mod->module_init)
|
||||
flush_icache_range((unsigned long)mod->module_init,
|
||||
(unsigned long)mod->module_init
|
||||
+ mod->init_size);
|
||||
flush_icache_range((unsigned long)mod->module_core,
|
||||
(unsigned long)mod->module_core + mod->core_size);
|
||||
if (mod->init_layout.base)
|
||||
flush_icache_range((unsigned long)mod->init_layout.base,
|
||||
(unsigned long)mod->init_layout.base
|
||||
+ mod->init_layout.size);
|
||||
flush_icache_range((unsigned long)mod->core_layout.base,
|
||||
(unsigned long)mod->core_layout.base + mod->core_layout.size);
|
||||
|
||||
set_fs(old_fs);
|
||||
}
|
||||
@ -3133,8 +3122,8 @@ static void module_deallocate(struct module *mod, struct load_info *info)
|
||||
{
|
||||
percpu_modfree(mod);
|
||||
module_arch_freeing_init(mod);
|
||||
module_memfree(mod->module_init);
|
||||
module_memfree(mod->module_core);
|
||||
module_memfree(mod->init_layout.base);
|
||||
module_memfree(mod->core_layout.base);
|
||||
}
|
||||
|
||||
int __weak module_finalize(const Elf_Ehdr *hdr,
|
||||
@ -3221,7 +3210,7 @@ static noinline int do_init_module(struct module *mod)
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
freeinit->module_init = mod->module_init;
|
||||
freeinit->module_init = mod->init_layout.base;
|
||||
|
||||
/*
|
||||
* We want to find out whether @mod uses async during init. Clear
|
||||
@ -3279,12 +3268,12 @@ static noinline int do_init_module(struct module *mod)
|
||||
mod->strtab = mod->core_strtab;
|
||||
#endif
|
||||
mod_tree_remove_init(mod);
|
||||
unset_module_init_ro_nx(mod);
|
||||
disable_ro_nx(&mod->init_layout);
|
||||
module_arch_freeing_init(mod);
|
||||
mod->module_init = NULL;
|
||||
mod->init_size = 0;
|
||||
mod->init_ro_size = 0;
|
||||
mod->init_text_size = 0;
|
||||
mod->init_layout.base = NULL;
|
||||
mod->init_layout.size = 0;
|
||||
mod->init_layout.ro_size = 0;
|
||||
mod->init_layout.text_size = 0;
|
||||
/*
|
||||
* We want to free module_init, but be aware that kallsyms may be
|
||||
* walking this with preempt disabled. In all the failure paths, we
|
||||
@ -3373,17 +3362,9 @@ static int complete_formation(struct module *mod, struct load_info *info)
|
||||
/* This relies on module_mutex for list integrity. */
|
||||
module_bug_finalize(info->hdr, info->sechdrs, mod);
|
||||
|
||||
/* Set RO and NX regions for core */
|
||||
set_section_ro_nx(mod->module_core,
|
||||
mod->core_text_size,
|
||||
mod->core_ro_size,
|
||||
mod->core_size);
|
||||
|
||||
/* Set RO and NX regions for init */
|
||||
set_section_ro_nx(mod->module_init,
|
||||
mod->init_text_size,
|
||||
mod->init_ro_size,
|
||||
mod->init_size);
|
||||
/* Set RO and NX regions */
|
||||
module_enable_ro(mod);
|
||||
module_enable_nx(mod);
|
||||
|
||||
/* Mark state as coming so strong_try_module_get() ignores us,
|
||||
* but kallsyms etc. can see us. */
|
||||
@ -3548,8 +3529,8 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
MODULE_STATE_GOING, mod);
|
||||
|
||||
/* we can't deallocate the module until we clear memory protection */
|
||||
unset_module_init_ro_nx(mod);
|
||||
unset_module_core_ro_nx(mod);
|
||||
module_disable_ro(mod);
|
||||
module_disable_nx(mod);
|
||||
|
||||
ddebug_cleanup:
|
||||
dynamic_debug_remove(info->debug);
|
||||
@ -3578,7 +3559,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
|
||||
*/
|
||||
ftrace_release_mod(mod);
|
||||
/* Free lock-classes; relies on the preceding sync_rcu() */
|
||||
lockdep_free_key_range(mod->module_core, mod->core_size);
|
||||
lockdep_free_key_range(mod->core_layout.base, mod->core_layout.size);
|
||||
|
||||
module_deallocate(mod, info);
|
||||
free_copy:
|
||||
@ -3656,9 +3637,9 @@ static const char *get_ksymbol(struct module *mod,
|
||||
|
||||
/* At worse, next value is at end of module */
|
||||
if (within_module_init(addr, mod))
|
||||
nextval = (unsigned long)mod->module_init+mod->init_text_size;
|
||||
nextval = (unsigned long)mod->init_layout.base+mod->init_layout.text_size;
|
||||
else
|
||||
nextval = (unsigned long)mod->module_core+mod->core_text_size;
|
||||
nextval = (unsigned long)mod->core_layout.base+mod->core_layout.text_size;
|
||||
|
||||
/* Scan for closest preceding symbol, and next symbol. (ELF
|
||||
starts real symbols at 1). */
|
||||
@ -3905,7 +3886,7 @@ static int m_show(struct seq_file *m, void *p)
|
||||
return 0;
|
||||
|
||||
seq_printf(m, "%s %u",
|
||||
mod->name, mod->init_size + mod->core_size);
|
||||
mod->name, mod->init_layout.size + mod->core_layout.size);
|
||||
print_unload_info(m, mod);
|
||||
|
||||
/* Informative for users. */
|
||||
@ -3914,7 +3895,7 @@ static int m_show(struct seq_file *m, void *p)
|
||||
mod->state == MODULE_STATE_COMING ? "Loading" :
|
||||
"Live");
|
||||
/* Used by oprofile and other similar tools. */
|
||||
seq_printf(m, " 0x%pK", mod->module_core);
|
||||
seq_printf(m, " 0x%pK", mod->core_layout.base);
|
||||
|
||||
/* Taints info */
|
||||
if (mod->taints)
|
||||
@ -4057,8 +4038,8 @@ struct module *__module_text_address(unsigned long addr)
|
||||
struct module *mod = __module_address(addr);
|
||||
if (mod) {
|
||||
/* Make sure it's within the text section. */
|
||||
if (!within(addr, mod->module_init, mod->init_text_size)
|
||||
&& !within(addr, mod->module_core, mod->core_text_size))
|
||||
if (!within(addr, mod->init_layout.base, mod->init_layout.text_size)
|
||||
&& !within(addr, mod->core_layout.base, mod->core_layout.text_size))
|
||||
mod = NULL;
|
||||
}
|
||||
return mod;
|
||||
|
Loading…
Reference in New Issue
Block a user