mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
[PATCH] FDPIC: Adjust the ELF-FDPIC driver to conform more to the CodingStyle
Adjust the ELF-FDPIC binfmt driver to conform much more to the CodingStyle, silly though it may be. Further changes: (*) Drop the casts to long for addresses in kdebug() statements (they're unsigned long already). (*) Use extra variables to avoid expressions longer than 80 chars by splitting the statement into multiple statements and letting the compiler optimise them back together. (*) Eliminate duplicate call of ksize() when working out how much space was actually allocated for the stack. (*) Discard the commented-out load_shlib prototype and op pointer as this will not be supported in ELF-FDPIC for the foreseeable future. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
21ff821630
commit
8a2ab7f5df
1 changed files with 167 additions and 136 deletions
|
@ -1,6 +1,6 @@
|
||||||
/* binfmt_elf_fdpic.c: FDPIC ELF binary format
|
/* binfmt_elf_fdpic.c: FDPIC ELF binary format
|
||||||
*
|
*
|
||||||
* Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved.
|
* Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved.
|
||||||
* Written by David Howells (dhowells@redhat.com)
|
* Written by David Howells (dhowells@redhat.com)
|
||||||
* Derived from binfmt_elf.c
|
* Derived from binfmt_elf.c
|
||||||
*
|
*
|
||||||
|
@ -50,43 +50,45 @@ typedef char *elf_caddr_t;
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
||||||
static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs);
|
static int load_elf_fdpic_binary(struct linux_binprm *, struct pt_regs *);
|
||||||
//static int load_elf_fdpic_library(struct file *);
|
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *, struct file *);
|
||||||
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file);
|
static int elf_fdpic_map_file(struct elf_fdpic_params *, struct file *,
|
||||||
static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
struct mm_struct *, const char *);
|
||||||
struct file *file,
|
|
||||||
struct mm_struct *mm,
|
|
||||||
const char *what);
|
|
||||||
|
|
||||||
static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
static int create_elf_fdpic_tables(struct linux_binprm *, struct mm_struct *,
|
||||||
struct mm_struct *mm,
|
struct elf_fdpic_params *,
|
||||||
struct elf_fdpic_params *exec_params,
|
struct elf_fdpic_params *);
|
||||||
struct elf_fdpic_params *interp_params);
|
|
||||||
|
|
||||||
#ifndef CONFIG_MMU
|
#ifndef CONFIG_MMU
|
||||||
static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp);
|
static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *,
|
||||||
static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
|
unsigned long *);
|
||||||
struct file *file,
|
static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *,
|
||||||
struct mm_struct *mm);
|
struct file *,
|
||||||
|
struct mm_struct *);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
|
||||||
struct file *file,
|
struct file *, struct mm_struct *);
|
||||||
struct mm_struct *mm);
|
|
||||||
|
|
||||||
static struct linux_binfmt elf_fdpic_format = {
|
static struct linux_binfmt elf_fdpic_format = {
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.load_binary = load_elf_fdpic_binary,
|
.load_binary = load_elf_fdpic_binary,
|
||||||
// .load_shlib = load_elf_fdpic_library,
|
|
||||||
// .core_dump = elf_fdpic_core_dump,
|
// .core_dump = elf_fdpic_core_dump,
|
||||||
.min_coredump = ELF_EXEC_PAGESIZE,
|
.min_coredump = ELF_EXEC_PAGESIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init init_elf_fdpic_binfmt(void) { return register_binfmt(&elf_fdpic_format); }
|
static int __init init_elf_fdpic_binfmt(void)
|
||||||
static void __exit exit_elf_fdpic_binfmt(void) { unregister_binfmt(&elf_fdpic_format); }
|
{
|
||||||
|
return register_binfmt(&elf_fdpic_format);
|
||||||
|
}
|
||||||
|
|
||||||
module_init(init_elf_fdpic_binfmt)
|
static void __exit exit_elf_fdpic_binfmt(void)
|
||||||
module_exit(exit_elf_fdpic_binfmt)
|
{
|
||||||
|
unregister_binfmt(&elf_fdpic_format);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(init_elf_fdpic_binfmt);
|
||||||
|
module_exit(exit_elf_fdpic_binfmt);
|
||||||
|
|
||||||
static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
|
static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
|
||||||
{
|
{
|
||||||
|
@ -105,7 +107,8 @@ static int is_elf_fdpic(struct elfhdr *hdr, struct file *file)
|
||||||
/*
|
/*
|
||||||
* read the program headers table into memory
|
* read the program headers table into memory
|
||||||
*/
|
*/
|
||||||
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file)
|
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params,
|
||||||
|
struct file *file)
|
||||||
{
|
{
|
||||||
struct elf32_phdr *phdr;
|
struct elf32_phdr *phdr;
|
||||||
unsigned long size;
|
unsigned long size;
|
||||||
|
@ -121,7 +124,8 @@ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *f
|
||||||
if (!params->phdrs)
|
if (!params->phdrs)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
retval = kernel_read(file, params->hdr.e_phoff, (char *) params->phdrs, size);
|
retval = kernel_read(file, params->hdr.e_phoff,
|
||||||
|
(char *) params->phdrs, size);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -141,17 +145,24 @@ static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *f
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} /* end elf_fdpic_fetch_phdrs() */
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
* load an fdpic binary into various bits of memory
|
* load an fdpic binary into various bits of memory
|
||||||
*/
|
*/
|
||||||
static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs)
|
static int load_elf_fdpic_binary(struct linux_binprm *bprm,
|
||||||
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct elf_fdpic_params exec_params, interp_params;
|
struct elf_fdpic_params exec_params, interp_params;
|
||||||
struct elf_phdr *phdr;
|
struct elf_phdr *phdr;
|
||||||
unsigned long stack_size;
|
unsigned long stack_size, entryaddr;
|
||||||
|
#ifndef CONFIG_MMU
|
||||||
|
unsigned long fullsize;
|
||||||
|
#endif
|
||||||
|
#ifdef ELF_FDPIC_PLAT_INIT
|
||||||
|
unsigned long dynaddr;
|
||||||
|
#endif
|
||||||
struct file *interpreter = NULL; /* to shut gcc up */
|
struct file *interpreter = NULL; /* to shut gcc up */
|
||||||
char *interpreter_name = NULL;
|
char *interpreter_name = NULL;
|
||||||
int executable_stack;
|
int executable_stack;
|
||||||
|
@ -212,7 +223,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE);
|
retval = kernel_read(interpreter, 0, bprm->buf,
|
||||||
|
BINPRM_BUF_SIZE);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
@ -295,7 +307,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
¤t->mm->start_stack,
|
¤t->mm->start_stack,
|
||||||
¤t->mm->start_brk);
|
¤t->mm->start_brk);
|
||||||
|
|
||||||
retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack);
|
retval = setup_arg_pages(bprm, current->mm->start_stack,
|
||||||
|
executable_stack);
|
||||||
if (retval < 0) {
|
if (retval < 0) {
|
||||||
send_sig(SIGKILL, current, 0);
|
send_sig(SIGKILL, current, 0);
|
||||||
goto error_kill;
|
goto error_kill;
|
||||||
|
@ -303,7 +316,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* load the executable and interpreter into memory */
|
/* load the executable and interpreter into memory */
|
||||||
retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, "executable");
|
retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm,
|
||||||
|
"executable");
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto error_kill;
|
goto error_kill;
|
||||||
|
|
||||||
|
@ -324,7 +338,8 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
if (!current->mm->start_brk)
|
if (!current->mm->start_brk)
|
||||||
current->mm->start_brk = current->mm->end_data;
|
current->mm->start_brk = current->mm->end_data;
|
||||||
|
|
||||||
current->mm->brk = current->mm->start_brk = PAGE_ALIGN(current->mm->start_brk);
|
current->mm->brk = current->mm->start_brk =
|
||||||
|
PAGE_ALIGN(current->mm->start_brk);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* create a stack and brk area big enough for everyone
|
/* create a stack and brk area big enough for everyone
|
||||||
|
@ -336,47 +351,45 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
stack_size = PAGE_SIZE * 2;
|
stack_size = PAGE_SIZE * 2;
|
||||||
|
|
||||||
down_write(¤t->mm->mmap_sem);
|
down_write(¤t->mm->mmap_sem);
|
||||||
current->mm->start_brk = do_mmap(NULL,
|
current->mm->start_brk = do_mmap(NULL, 0, stack_size,
|
||||||
0,
|
|
||||||
stack_size,
|
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC,
|
PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||||
MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN,
|
MAP_PRIVATE | MAP_ANON | MAP_GROWSDOWN,
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (IS_ERR((void *) current->mm->start_brk)) {
|
if (IS_ERR_VALUE(current->mm->start_brk)) {
|
||||||
up_write(¤t->mm->mmap_sem);
|
up_write(¤t->mm->mmap_sem);
|
||||||
retval = current->mm->start_brk;
|
retval = current->mm->start_brk;
|
||||||
current->mm->start_brk = 0;
|
current->mm->start_brk = 0;
|
||||||
goto error_kill;
|
goto error_kill;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_mremap(current->mm->start_brk,
|
/* expand the stack mapping to use up the entire allocation granule */
|
||||||
stack_size,
|
fullsize = ksize((char *) current->mm->start_brk);
|
||||||
ksize((char *) current->mm->start_brk),
|
if (!IS_ERR_VALUE(do_mremap(current->mm->start_brk, stack_size,
|
||||||
0, 0
|
fullsize, 0, 0)))
|
||||||
) == current->mm->start_brk
|
stack_size = fullsize;
|
||||||
)
|
|
||||||
stack_size = ksize((char *) current->mm->start_brk);
|
|
||||||
up_write(¤t->mm->mmap_sem);
|
up_write(¤t->mm->mmap_sem);
|
||||||
|
|
||||||
current->mm->brk = current->mm->start_brk;
|
current->mm->brk = current->mm->start_brk;
|
||||||
current->mm->context.end_brk = current->mm->start_brk;
|
current->mm->context.end_brk = current->mm->start_brk;
|
||||||
current->mm->context.end_brk += (stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0;
|
current->mm->context.end_brk +=
|
||||||
|
(stack_size > PAGE_SIZE) ? (stack_size - PAGE_SIZE) : 0;
|
||||||
current->mm->start_stack = current->mm->start_brk + stack_size;
|
current->mm->start_stack = current->mm->start_brk + stack_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
compute_creds(bprm);
|
compute_creds(bprm);
|
||||||
current->flags &= ~PF_FORKNOEXEC;
|
current->flags &= ~PF_FORKNOEXEC;
|
||||||
if (create_elf_fdpic_tables(bprm, current->mm, &exec_params, &interp_params) < 0)
|
if (create_elf_fdpic_tables(bprm, current->mm,
|
||||||
|
&exec_params, &interp_params) < 0)
|
||||||
goto error_kill;
|
goto error_kill;
|
||||||
|
|
||||||
kdebug("- start_code %lx", (long) current->mm->start_code);
|
kdebug("- start_code %lx", current->mm->start_code);
|
||||||
kdebug("- end_code %lx", (long) current->mm->end_code);
|
kdebug("- end_code %lx", current->mm->end_code);
|
||||||
kdebug("- start_data %lx", (long) current->mm->start_data);
|
kdebug("- start_data %lx", current->mm->start_data);
|
||||||
kdebug("- end_data %lx", (long) current->mm->end_data);
|
kdebug("- end_data %lx", current->mm->end_data);
|
||||||
kdebug("- start_brk %lx", (long) current->mm->start_brk);
|
kdebug("- start_brk %lx", current->mm->start_brk);
|
||||||
kdebug("- brk %lx", (long) current->mm->brk);
|
kdebug("- brk %lx", current->mm->brk);
|
||||||
kdebug("- start_stack %lx", (long) current->mm->start_stack);
|
kdebug("- start_stack %lx", current->mm->start_stack);
|
||||||
|
|
||||||
#ifdef ELF_FDPIC_PLAT_INIT
|
#ifdef ELF_FDPIC_PLAT_INIT
|
||||||
/*
|
/*
|
||||||
|
@ -385,21 +398,18 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
* example. This macro performs whatever initialization to
|
* example. This macro performs whatever initialization to
|
||||||
* the regs structure is required.
|
* the regs structure is required.
|
||||||
*/
|
*/
|
||||||
ELF_FDPIC_PLAT_INIT(regs,
|
dynaddr = interp_params.dynamic_addr ?: exec_params.dynamic_addr;
|
||||||
exec_params.map_addr,
|
ELF_FDPIC_PLAT_INIT(regs, exec_params.map_addr, interp_params.map_addr,
|
||||||
interp_params.map_addr,
|
dynaddr);
|
||||||
interp_params.dynamic_addr ?: exec_params.dynamic_addr
|
|
||||||
);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* everything is now ready... get the userspace context ready to roll */
|
/* everything is now ready... get the userspace context ready to roll */
|
||||||
start_thread(regs,
|
entryaddr = interp_params.entry_addr ?: exec_params.entry_addr;
|
||||||
interp_params.entry_addr ?: exec_params.entry_addr,
|
start_thread(regs, entryaddr, current->mm->start_stack);
|
||||||
current->mm->start_stack);
|
|
||||||
|
|
||||||
if (unlikely(current->ptrace & PT_PTRACED)) {
|
if (unlikely(current->ptrace & PT_PTRACED)) {
|
||||||
if (current->ptrace & PT_TRACE_EXEC)
|
if (current->ptrace & PT_TRACE_EXEC)
|
||||||
ptrace_notify ((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
|
ptrace_notify((PTRACE_EVENT_EXEC << 8) | SIGTRAP);
|
||||||
else
|
else
|
||||||
send_sig(SIGTRAP, current, 0);
|
send_sig(SIGTRAP, current, 0);
|
||||||
}
|
}
|
||||||
|
@ -419,11 +429,11 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm, struct pt_regs *regs
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* unrecoverable error - kill the process */
|
/* unrecoverable error - kill the process */
|
||||||
error_kill:
|
error_kill:
|
||||||
send_sig(SIGSEGV, current, 0);
|
send_sig(SIGSEGV, current, 0);
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
} /* end load_elf_fdpic_binary() */
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
|
@ -471,11 +481,11 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
|
|
||||||
#if defined(__i386__) && defined(CONFIG_SMP)
|
#if defined(__i386__) && defined(CONFIG_SMP)
|
||||||
/* in some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
|
/* in some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
|
||||||
* by the processes running on the same package. One thing we can do
|
* by the processes running on the same package. One thing we can do is
|
||||||
* is to shuffle the initial stack for them.
|
* to shuffle the initial stack for them.
|
||||||
*
|
*
|
||||||
* the conditionals here are unneeded, but kept in to make the
|
* the conditionals here are unneeded, but kept in to make the code
|
||||||
* code behaviour the same as pre change unless we have hyperthreaded
|
* behaviour the same as pre change unless we have hyperthreaded
|
||||||
* processors. This keeps Mr Marcelo Person happier but should be
|
* processors. This keeps Mr Marcelo Person happier but should be
|
||||||
* removed for 2.5
|
* removed for 2.5
|
||||||
*/
|
*/
|
||||||
|
@ -498,11 +508,13 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
|
|
||||||
if (interp_params->loadmap) {
|
if (interp_params->loadmap) {
|
||||||
len = sizeof(struct elf32_fdpic_loadmap);
|
len = sizeof(struct elf32_fdpic_loadmap);
|
||||||
len += sizeof(struct elf32_fdpic_loadseg) * interp_params->loadmap->nsegs;
|
len += sizeof(struct elf32_fdpic_loadseg) *
|
||||||
|
interp_params->loadmap->nsegs;
|
||||||
sp = (sp - len) & ~7UL;
|
sp = (sp - len) & ~7UL;
|
||||||
interp_params->map_addr = sp;
|
interp_params->map_addr = sp;
|
||||||
|
|
||||||
if (copy_to_user((void __user *) sp, interp_params->loadmap, len) != 0)
|
if (copy_to_user((void __user *) sp, interp_params->loadmap,
|
||||||
|
len) != 0)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
current->mm->context.interp_fdpic_loadmap = (unsigned long) sp;
|
current->mm->context.interp_fdpic_loadmap = (unsigned long) sp;
|
||||||
|
@ -526,34 +538,37 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
sp -= sp & 15UL;
|
sp -= sp & 15UL;
|
||||||
|
|
||||||
/* put the ELF interpreter info on the stack */
|
/* put the ELF interpreter info on the stack */
|
||||||
#define NEW_AUX_ENT(nr, id, val) \
|
#define NEW_AUX_ENT(nr, id, val) \
|
||||||
do { \
|
do { \
|
||||||
struct { unsigned long _id, _val; } __user *ent = (void __user *) csp; \
|
struct { unsigned long _id, _val; } __user *ent; \
|
||||||
__put_user((id), &ent[nr]._id); \
|
\
|
||||||
__put_user((val), &ent[nr]._val); \
|
ent = (void __user *) csp; \
|
||||||
|
__put_user((id), &ent[nr]._id); \
|
||||||
|
__put_user((val), &ent[nr]._val); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
csp -= 2 * sizeof(unsigned long);
|
csp -= 2 * sizeof(unsigned long);
|
||||||
NEW_AUX_ENT(0, AT_NULL, 0);
|
NEW_AUX_ENT(0, AT_NULL, 0);
|
||||||
if (k_platform) {
|
if (k_platform) {
|
||||||
csp -= 2 * sizeof(unsigned long);
|
csp -= 2 * sizeof(unsigned long);
|
||||||
NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform);
|
NEW_AUX_ENT(0, AT_PLATFORM,
|
||||||
|
(elf_addr_t) (unsigned long) u_platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
|
csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
|
||||||
NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
|
NEW_AUX_ENT( 0, AT_HWCAP, hwcap);
|
||||||
NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE);
|
NEW_AUX_ENT( 1, AT_PAGESZ, PAGE_SIZE);
|
||||||
NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
|
NEW_AUX_ENT( 2, AT_CLKTCK, CLOCKS_PER_SEC);
|
||||||
NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr);
|
NEW_AUX_ENT( 3, AT_PHDR, exec_params->ph_addr);
|
||||||
NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr));
|
NEW_AUX_ENT( 4, AT_PHENT, sizeof(struct elf_phdr));
|
||||||
NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum);
|
NEW_AUX_ENT( 5, AT_PHNUM, exec_params->hdr.e_phnum);
|
||||||
NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr);
|
NEW_AUX_ENT( 6, AT_BASE, interp_params->elfhdr_addr);
|
||||||
NEW_AUX_ENT( 7, AT_FLAGS, 0);
|
NEW_AUX_ENT( 7, AT_FLAGS, 0);
|
||||||
NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr);
|
NEW_AUX_ENT( 8, AT_ENTRY, exec_params->entry_addr);
|
||||||
NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
|
NEW_AUX_ENT( 9, AT_UID, (elf_addr_t) current->uid);
|
||||||
NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
|
NEW_AUX_ENT(10, AT_EUID, (elf_addr_t) current->euid);
|
||||||
NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
|
NEW_AUX_ENT(11, AT_GID, (elf_addr_t) current->gid);
|
||||||
NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
|
NEW_AUX_ENT(12, AT_EGID, (elf_addr_t) current->egid);
|
||||||
|
|
||||||
#ifdef ARCH_DLINFO
|
#ifdef ARCH_DLINFO
|
||||||
/* ARCH_DLINFO must come last so platform specific code can enforce
|
/* ARCH_DLINFO must come last so platform specific code can enforce
|
||||||
|
@ -579,7 +594,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
current->mm->arg_start = bprm->p;
|
current->mm->arg_start = bprm->p;
|
||||||
#else
|
#else
|
||||||
current->mm->arg_start = current->mm->start_stack - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p);
|
current->mm->arg_start = current->mm->start_stack -
|
||||||
|
(MAX_ARG_PAGES * PAGE_SIZE - bprm->p);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
p = (char __user *) current->mm->arg_start;
|
p = (char __user *) current->mm->arg_start;
|
||||||
|
@ -607,7 +623,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
|
|
||||||
mm->start_stack = (unsigned long) sp;
|
mm->start_stack = (unsigned long) sp;
|
||||||
return 0;
|
return 0;
|
||||||
} /* end create_elf_fdpic_tables() */
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
|
@ -615,7 +631,8 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
* the stack
|
* the stack
|
||||||
*/
|
*/
|
||||||
#ifndef CONFIG_MMU
|
#ifndef CONFIG_MMU
|
||||||
static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned long *_sp)
|
static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm,
|
||||||
|
unsigned long *_sp)
|
||||||
{
|
{
|
||||||
unsigned long index, stop, sp;
|
unsigned long index, stop, sp;
|
||||||
char *src;
|
char *src;
|
||||||
|
@ -636,9 +653,9 @@ static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *bprm, unsigned
|
||||||
|
|
||||||
*_sp = (*_sp - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p)) & ~15;
|
*_sp = (*_sp - (MAX_ARG_PAGES * PAGE_SIZE - bprm->p)) & ~15;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
} /* end elf_fdpic_transfer_args_to_stack() */
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -713,17 +730,18 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
seg = loadmap->segs;
|
seg = loadmap->segs;
|
||||||
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
||||||
if (params->hdr.e_entry >= seg->p_vaddr &&
|
if (params->hdr.e_entry >= seg->p_vaddr &&
|
||||||
params->hdr.e_entry < seg->p_vaddr + seg->p_memsz
|
params->hdr.e_entry < seg->p_vaddr + seg->p_memsz) {
|
||||||
) {
|
|
||||||
params->entry_addr =
|
params->entry_addr =
|
||||||
(params->hdr.e_entry - seg->p_vaddr) + seg->addr;
|
(params->hdr.e_entry - seg->p_vaddr) +
|
||||||
|
seg->addr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* determine where the program header table has wound up if mapped */
|
/* determine where the program header table has wound up if mapped */
|
||||||
stop = params->hdr.e_phoff + params->hdr.e_phnum * sizeof (struct elf_phdr);
|
stop = params->hdr.e_phoff;
|
||||||
|
stop += params->hdr.e_phnum * sizeof (struct elf_phdr);
|
||||||
phdr = params->phdrs;
|
phdr = params->phdrs;
|
||||||
|
|
||||||
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
|
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
|
||||||
|
@ -737,9 +755,11 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
seg = loadmap->segs;
|
seg = loadmap->segs;
|
||||||
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
||||||
if (phdr->p_vaddr >= seg->p_vaddr &&
|
if (phdr->p_vaddr >= seg->p_vaddr &&
|
||||||
phdr->p_vaddr + phdr->p_filesz <= seg->p_vaddr + seg->p_memsz
|
phdr->p_vaddr + phdr->p_filesz <=
|
||||||
) {
|
seg->p_vaddr + seg->p_memsz) {
|
||||||
params->ph_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr +
|
params->ph_addr =
|
||||||
|
(phdr->p_vaddr - seg->p_vaddr) +
|
||||||
|
seg->addr +
|
||||||
params->hdr.e_phoff - phdr->p_offset;
|
params->hdr.e_phoff - phdr->p_offset;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -756,18 +776,22 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
seg = loadmap->segs;
|
seg = loadmap->segs;
|
||||||
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
for (loop = loadmap->nsegs; loop > 0; loop--, seg++) {
|
||||||
if (phdr->p_vaddr >= seg->p_vaddr &&
|
if (phdr->p_vaddr >= seg->p_vaddr &&
|
||||||
phdr->p_vaddr + phdr->p_memsz <= seg->p_vaddr + seg->p_memsz
|
phdr->p_vaddr + phdr->p_memsz <=
|
||||||
) {
|
seg->p_vaddr + seg->p_memsz) {
|
||||||
params->dynamic_addr = (phdr->p_vaddr - seg->p_vaddr) + seg->addr;
|
params->dynamic_addr =
|
||||||
|
(phdr->p_vaddr - seg->p_vaddr) +
|
||||||
|
seg->addr;
|
||||||
|
|
||||||
/* check the dynamic section contains at least one item, and that
|
/* check the dynamic section contains at least
|
||||||
* the last item is a NULL entry */
|
* one item, and that the last item is a NULL
|
||||||
|
* entry */
|
||||||
if (phdr->p_memsz == 0 ||
|
if (phdr->p_memsz == 0 ||
|
||||||
phdr->p_memsz % sizeof(Elf32_Dyn) != 0)
|
phdr->p_memsz % sizeof(Elf32_Dyn) != 0)
|
||||||
goto dynamic_error;
|
goto dynamic_error;
|
||||||
|
|
||||||
tmp = phdr->p_memsz / sizeof(Elf32_Dyn);
|
tmp = phdr->p_memsz / sizeof(Elf32_Dyn);
|
||||||
if (((Elf32_Dyn *) params->dynamic_addr)[tmp - 1].d_tag != 0)
|
if (((Elf32_Dyn *)
|
||||||
|
params->dynamic_addr)[tmp - 1].d_tag != 0)
|
||||||
goto dynamic_error;
|
goto dynamic_error;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -776,8 +800,8 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* now elide adjacent segments in the load map on MMU linux
|
/* now elide adjacent segments in the load map on MMU linux
|
||||||
* - on uClinux the holes between may actually be filled with system stuff or stuff from
|
* - on uClinux the holes between may actually be filled with system
|
||||||
* other processes
|
* stuff or stuff from other processes
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
nloads = loadmap->nsegs;
|
nloads = loadmap->nsegs;
|
||||||
|
@ -788,7 +812,9 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) {
|
if (seg->p_vaddr - mseg->p_vaddr == seg->addr - mseg->addr) {
|
||||||
load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz);
|
load_addr = PAGE_ALIGN(mseg->addr + mseg->p_memsz);
|
||||||
if (load_addr == (seg->addr & PAGE_MASK)) {
|
if (load_addr == (seg->addr & PAGE_MASK)) {
|
||||||
mseg->p_memsz += load_addr - (mseg->addr + mseg->p_memsz);
|
mseg->p_memsz +=
|
||||||
|
load_addr -
|
||||||
|
(mseg->addr + mseg->p_memsz);
|
||||||
mseg->p_memsz += seg->addr & ~PAGE_MASK;
|
mseg->p_memsz += seg->addr & ~PAGE_MASK;
|
||||||
mseg->p_memsz += seg->p_memsz;
|
mseg->p_memsz += seg->p_memsz;
|
||||||
loadmap->nsegs--;
|
loadmap->nsegs--;
|
||||||
|
@ -816,20 +842,21 @@ static int elf_fdpic_map_file(struct elf_fdpic_params *params,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
dynamic_error:
|
dynamic_error:
|
||||||
printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n",
|
printk("ELF FDPIC %s with invalid DYNAMIC section (inode=%lu)\n",
|
||||||
what, file->f_dentry->d_inode->i_ino);
|
what, file->f_dentry->d_inode->i_ino);
|
||||||
return -ELIBBAD;
|
return -ELIBBAD;
|
||||||
} /* end elf_fdpic_map_file() */
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/*
|
/*
|
||||||
* map a file with constant displacement under uClinux
|
* map a file with constant displacement under uClinux
|
||||||
*/
|
*/
|
||||||
#ifndef CONFIG_MMU
|
#ifndef CONFIG_MMU
|
||||||
static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *params,
|
static int elf_fdpic_map_file_constdisp_on_uclinux(
|
||||||
struct file *file,
|
struct elf_fdpic_params *params,
|
||||||
struct mm_struct *mm)
|
struct file *file,
|
||||||
|
struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
struct elf32_fdpic_loadseg *seg;
|
struct elf32_fdpic_loadseg *seg;
|
||||||
struct elf32_phdr *phdr;
|
struct elf32_phdr *phdr;
|
||||||
|
@ -840,7 +867,8 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
|
||||||
load_addr = params->load_addr;
|
load_addr = params->load_addr;
|
||||||
seg = params->loadmap->segs;
|
seg = params->loadmap->segs;
|
||||||
|
|
||||||
/* determine the bounds of the contiguous overall allocation we must make */
|
/* determine the bounds of the contiguous overall allocation we must
|
||||||
|
* make */
|
||||||
phdr = params->phdrs;
|
phdr = params->phdrs;
|
||||||
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
|
for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) {
|
||||||
if (params->phdrs[loop].p_type != PT_LOAD)
|
if (params->phdrs[loop].p_type != PT_LOAD)
|
||||||
|
@ -861,7 +889,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
|
||||||
maddr = do_mmap(NULL, load_addr, top - base,
|
maddr = do_mmap(NULL, load_addr, top - base,
|
||||||
PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0);
|
PROT_READ | PROT_WRITE | PROT_EXEC, mflags, 0);
|
||||||
up_write(&mm->mmap_sem);
|
up_write(&mm->mmap_sem);
|
||||||
if (IS_ERR((void *) maddr))
|
if (IS_ERR_VALUE(maddr))
|
||||||
return (int) maddr;
|
return (int) maddr;
|
||||||
|
|
||||||
if (load_addr != 0)
|
if (load_addr != 0)
|
||||||
|
@ -879,7 +907,8 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
|
||||||
seg->p_vaddr = phdr->p_vaddr;
|
seg->p_vaddr = phdr->p_vaddr;
|
||||||
seg->p_memsz = phdr->p_memsz;
|
seg->p_memsz = phdr->p_memsz;
|
||||||
|
|
||||||
ret = file->f_op->read(file, (void *) seg->addr, phdr->p_filesz, &fpos);
|
ret = file->f_op->read(file, (void *) seg->addr,
|
||||||
|
phdr->p_filesz, &fpos);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -896,8 +925,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
|
||||||
if (phdr->p_flags & PF_X) {
|
if (phdr->p_flags & PF_X) {
|
||||||
mm->start_code = seg->addr;
|
mm->start_code = seg->addr;
|
||||||
mm->end_code = seg->addr + phdr->p_memsz;
|
mm->end_code = seg->addr + phdr->p_memsz;
|
||||||
}
|
} else if (!mm->start_data) {
|
||||||
else if (!mm->start_data) {
|
|
||||||
mm->start_data = seg->addr;
|
mm->start_data = seg->addr;
|
||||||
#ifndef CONFIG_MMU
|
#ifndef CONFIG_MMU
|
||||||
mm->end_data = seg->addr + phdr->p_memsz;
|
mm->end_data = seg->addr + phdr->p_memsz;
|
||||||
|
@ -914,7 +942,7 @@ static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *para
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} /* end elf_fdpic_map_file_constdisp_on_uclinux() */
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
@ -975,14 +1003,14 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
|
|
||||||
case ELF_FDPIC_FLAG_CONSTDISP:
|
case ELF_FDPIC_FLAG_CONSTDISP:
|
||||||
/* constant displacement
|
/* constant displacement
|
||||||
* - can be mapped anywhere, but must be mapped as a unit
|
* - can be mapped anywhere, but must be mapped as a
|
||||||
|
* unit
|
||||||
*/
|
*/
|
||||||
if (!dvset) {
|
if (!dvset) {
|
||||||
maddr = load_addr;
|
maddr = load_addr;
|
||||||
delta_vaddr = phdr->p_vaddr;
|
delta_vaddr = phdr->p_vaddr;
|
||||||
dvset = 1;
|
dvset = 1;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
maddr = load_addr + phdr->p_vaddr - delta_vaddr;
|
maddr = load_addr + phdr->p_vaddr - delta_vaddr;
|
||||||
flags |= MAP_FIXED;
|
flags |= MAP_FIXED;
|
||||||
}
|
}
|
||||||
|
@ -1006,13 +1034,14 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
up_write(&mm->mmap_sem);
|
up_write(&mm->mmap_sem);
|
||||||
|
|
||||||
kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx",
|
kdebug("mmap[%d] <file> sz=%lx pr=%x fl=%x of=%lx --> %08lx",
|
||||||
loop, phdr->p_memsz + disp, prot, flags, phdr->p_offset - disp,
|
loop, phdr->p_memsz + disp, prot, flags,
|
||||||
maddr);
|
phdr->p_offset - disp, maddr);
|
||||||
|
|
||||||
if (IS_ERR((void *) maddr))
|
if (IS_ERR_VALUE(maddr))
|
||||||
return (int) maddr;
|
return (int) maddr;
|
||||||
|
|
||||||
if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) == ELF_FDPIC_FLAG_CONTIGUOUS)
|
if ((params->flags & ELF_FDPIC_FLAG_ARRANGEMENT) ==
|
||||||
|
ELF_FDPIC_FLAG_CONTIGUOUS)
|
||||||
load_addr += PAGE_ALIGN(phdr->p_memsz + disp);
|
load_addr += PAGE_ALIGN(phdr->p_memsz + disp);
|
||||||
|
|
||||||
seg->addr = maddr + disp;
|
seg->addr = maddr + disp;
|
||||||
|
@ -1023,7 +1052,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
if (phdr->p_offset == 0)
|
if (phdr->p_offset == 0)
|
||||||
params->elfhdr_addr = seg->addr;
|
params->elfhdr_addr = seg->addr;
|
||||||
|
|
||||||
/* clear the bit between beginning of mapping and beginning of PT_LOAD */
|
/* clear the bit between beginning of mapping and beginning of
|
||||||
|
* PT_LOAD */
|
||||||
if (prot & PROT_WRITE && disp > 0) {
|
if (prot & PROT_WRITE && disp > 0) {
|
||||||
kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp);
|
kdebug("clear[%d] ad=%lx sz=%lx", loop, maddr, disp);
|
||||||
clear_user((void __user *) maddr, disp);
|
clear_user((void __user *) maddr, disp);
|
||||||
|
@ -1039,19 +1069,20 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK);
|
excess1 = PAGE_SIZE - ((maddr + phdr->p_filesz) & ~PAGE_MASK);
|
||||||
|
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
|
|
||||||
if (excess > excess1) {
|
if (excess > excess1) {
|
||||||
unsigned long xaddr = maddr + phdr->p_filesz + excess1;
|
unsigned long xaddr = maddr + phdr->p_filesz + excess1;
|
||||||
unsigned long xmaddr;
|
unsigned long xmaddr;
|
||||||
|
|
||||||
flags |= MAP_FIXED | MAP_ANONYMOUS;
|
flags |= MAP_FIXED | MAP_ANONYMOUS;
|
||||||
down_write(&mm->mmap_sem);
|
down_write(&mm->mmap_sem);
|
||||||
xmaddr = do_mmap(NULL, xaddr, excess - excess1, prot, flags, 0);
|
xmaddr = do_mmap(NULL, xaddr, excess - excess1,
|
||||||
|
prot, flags, 0);
|
||||||
up_write(&mm->mmap_sem);
|
up_write(&mm->mmap_sem);
|
||||||
|
|
||||||
kdebug("mmap[%d] <anon>"
|
kdebug("mmap[%d] <anon>"
|
||||||
" ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx",
|
" ad=%lx sz=%lx pr=%x fl=%x of=0 --> %08lx",
|
||||||
loop, xaddr, excess - excess1, prot, flags, xmaddr);
|
loop, xaddr, excess - excess1, prot, flags,
|
||||||
|
xmaddr);
|
||||||
|
|
||||||
if (xmaddr != xaddr)
|
if (xmaddr != xaddr)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1060,7 +1091,8 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
if (prot & PROT_WRITE && excess1 > 0) {
|
if (prot & PROT_WRITE && excess1 > 0) {
|
||||||
kdebug("clear[%d] ad=%lx sz=%lx",
|
kdebug("clear[%d] ad=%lx sz=%lx",
|
||||||
loop, maddr + phdr->p_filesz, excess1);
|
loop, maddr + phdr->p_filesz, excess1);
|
||||||
clear_user((void __user *) maddr + phdr->p_filesz, excess1);
|
clear_user((void __user *) maddr + phdr->p_filesz,
|
||||||
|
excess1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
@ -1075,8 +1107,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
if (phdr->p_flags & PF_X) {
|
if (phdr->p_flags & PF_X) {
|
||||||
mm->start_code = maddr;
|
mm->start_code = maddr;
|
||||||
mm->end_code = maddr + phdr->p_memsz;
|
mm->end_code = maddr + phdr->p_memsz;
|
||||||
}
|
} else if (!mm->start_data) {
|
||||||
else if (!mm->start_data) {
|
|
||||||
mm->start_data = maddr;
|
mm->start_data = maddr;
|
||||||
mm->end_data = maddr + phdr->p_memsz;
|
mm->end_data = maddr + phdr->p_memsz;
|
||||||
}
|
}
|
||||||
|
@ -1086,4 +1117,4 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *params,
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} /* end elf_fdpic_map_file_by_direct_mmap() */
|
}
|
||||||
|
|
Loading…
Reference in a new issue