Commit ebb5e78c authored by Alex Smith's avatar Alex Smith Committed by Ralf Baechle

MIPS: Initial implementation of a VDSO

Add an initial implementation of a proper (i.e. an ELF shared library)
VDSO. With this commit it does not export any symbols, it only replaces
the current signal return trampoline page. A later commit will add user
implementations of gettimeofday()/clock_gettime().

To support both new toolchains and old ones which don't generate ABI
flags section, we define its content manually and then use a tool
(genvdso) to patch up the section to have the correct name and type.
genvdso also extracts symbol offsets ({,rt_}sigreturn) needed by the
kernel, and generates a C file containing a "struct mips_vdso_image"
containing both the VDSO data and these offsets. This C file is
compiled into the kernel.

On 64-bit kernels we require a different VDSO for each supported ABI,
so we may build up to 3 different VDSOs. The VDSO to use is selected by
the mips_abi structure.

A kernel/user shared data page is created and mapped below the VDSO
image. This is currently empty, but will be used by the user time
function implementations which are added later.

[markos.chandras@imgtec.com:
- Add more comments
- Move abi detection in genvdso.h since it's the get_symbol function
that needs it.
- Add an R6 specific way to calculate the base address of VDSO in order
to avoid the branch instruction which affects performance.
- Do not patch .gnu.attributes since it's not needed for dynamic linking.
- Simplify Makefile a little bit.
- checkpatch fixes
- Restrict VDSO support for binutils < 2.25 for pre-R6
- Include atomic64.h for O32 variant on MIPS64]
Signed-off-by: default avatarAlex Smith <alex.smith@imgtec.com>
Signed-off-by: default avatarMarkos Chandras <markos.chandras@imgtec.com>
Cc: Matthew Fortune <matthew.fortune@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/11337/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 22773aa9
...@@ -17,6 +17,7 @@ obj- := $(platform-) ...@@ -17,6 +17,7 @@ obj- := $(platform-)
obj-y += kernel/ obj-y += kernel/
obj-y += mm/ obj-y += mm/
obj-y += net/ obj-y += net/
obj-y += vdso/
ifdef CONFIG_KVM ifdef CONFIG_KVM
obj-y += kvm/ obj-y += kvm/
......
...@@ -11,19 +11,20 @@ ...@@ -11,19 +11,20 @@
#include <asm/signal.h> #include <asm/signal.h>
#include <asm/siginfo.h> #include <asm/siginfo.h>
#include <asm/vdso.h>
struct mips_abi { struct mips_abi {
int (* const setup_frame)(void *sig_return, struct ksignal *ksig, int (* const setup_frame)(void *sig_return, struct ksignal *ksig,
struct pt_regs *regs, sigset_t *set); struct pt_regs *regs, sigset_t *set);
const unsigned long signal_return_offset;
int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig, int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig,
struct pt_regs *regs, sigset_t *set); struct pt_regs *regs, sigset_t *set);
const unsigned long rt_signal_return_offset;
const unsigned long restart; const unsigned long restart;
unsigned off_sc_fpregs; unsigned off_sc_fpregs;
unsigned off_sc_fpc_csr; unsigned off_sc_fpc_csr;
unsigned off_sc_used_math; unsigned off_sc_used_math;
struct mips_vdso_image *vdso;
}; };
#endif /* _ASM_ABI_H */ #endif /* _ASM_ABI_H */
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#ifndef _ASM_ELF_H #ifndef _ASM_ELF_H
#define _ASM_ELF_H #define _ASM_ELF_H
#include <linux/auxvec.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <uapi/linux/elf.h> #include <uapi/linux/elf.h>
...@@ -419,6 +420,12 @@ extern const char *__elf_platform; ...@@ -419,6 +420,12 @@ extern const char *__elf_platform;
#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2) #define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
#endif #endif
#define ARCH_DLINFO \
do { \
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
(unsigned long)current->mm->context.vdso); \
} while (0)
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1 #define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
struct linux_binprm; struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm, extern int arch_setup_additional_pages(struct linux_binprm *bprm,
......
...@@ -36,12 +36,6 @@ extern unsigned int vced_count, vcei_count; ...@@ -36,12 +36,6 @@ extern unsigned int vced_count, vcei_count;
*/ */
#define HAVE_ARCH_PICK_MMAP_LAYOUT 1 #define HAVE_ARCH_PICK_MMAP_LAYOUT 1
/*
* A special page (the vdso) is mapped into all processes at the very
* top of the virtual memory space.
*/
#define SPECIAL_PAGES_SIZE PAGE_SIZE
#ifdef CONFIG_32BIT #ifdef CONFIG_32BIT
#ifdef CONFIG_KVM_GUEST #ifdef CONFIG_KVM_GUEST
/* User space process size is limited to 1GB in KVM Guest Mode */ /* User space process size is limited to 1GB in KVM Guest Mode */
...@@ -80,7 +74,7 @@ extern unsigned int vced_count, vcei_count; ...@@ -80,7 +74,7 @@ extern unsigned int vced_count, vcei_count;
#endif #endif
#define STACK_TOP ((TASK_SIZE & PAGE_MASK) - SPECIAL_PAGES_SIZE) #define STACK_TOP (TASK_SIZE & PAGE_MASK)
/* /*
* This decides where the kernel will search for a free chunk of vm * This decides where the kernel will search for a free chunk of vm
......
/* /*
* This file is subject to the terms and conditions of the GNU General Public * Copyright (C) 2015 Imagination Technologies
* License. See the file "COPYING" in the main directory of this archive * Author: Alex Smith <alex.smith@imgtec.com>
* for more details.
* *
* Copyright (C) 2009 Cavium Networks * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/ */
#ifndef __ASM_VDSO_H #ifndef __ASM_VDSO_H
#define __ASM_VDSO_H #define __ASM_VDSO_H
#include <linux/types.h> #include <linux/mm_types.h>
/**
* struct mips_vdso_image - Details of a VDSO image.
* @data: Pointer to VDSO image data (page-aligned).
* @size: Size of the VDSO image data (page-aligned).
* @off_sigreturn: Offset of the sigreturn() trampoline.
* @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline.
* @mapping: Special mapping structure.
*
* This structure contains details of a VDSO image, including the image data
* and offsets of certain symbols required by the kernel. It is generated as
* part of the VDSO build process, aside from the mapping page array, which is
* populated at runtime.
*/
struct mips_vdso_image {
void *data;
unsigned long size;
#ifdef CONFIG_32BIT unsigned long off_sigreturn;
struct mips_vdso { unsigned long off_rt_sigreturn;
u32 signal_trampoline[2];
u32 rt_signal_trampoline[2]; struct vm_special_mapping mapping;
}; };
#else /* !CONFIG_32BIT */
struct mips_vdso { /*
u32 o32_signal_trampoline[2]; * The following structures are auto-generated as part of the build for each
u32 o32_rt_signal_trampoline[2]; * ABI by genvdso, see arch/mips/vdso/Makefile.
u32 rt_signal_trampoline[2]; */
u32 n32_rt_signal_trampoline[2];
extern struct mips_vdso_image vdso_image;
#ifdef CONFIG_MIPS32_O32
extern struct mips_vdso_image vdso_image_o32;
#endif
#ifdef CONFIG_MIPS32_N32
extern struct mips_vdso_image vdso_image_n32;
#endif
/**
* union mips_vdso_data - Data provided by the kernel for the VDSO.
*
* This structure contains data needed by functions within the VDSO. It is
* populated by the kernel and mapped read-only into user memory.
*
* Note: Care should be taken when modifying as the layout must remain the same
* for both 64- and 32-bit (for 32-bit userland on 64-bit kernel).
*/
union mips_vdso_data {
struct {
};
u8 page[PAGE_SIZE];
}; };
#endif /* CONFIG_32BIT */
#endif /* __ASM_VDSO_H */ #endif /* __ASM_VDSO_H */
# UAPI Header export list # UAPI Header export list
include include/uapi/asm-generic/Kbuild.asm include include/uapi/asm-generic/Kbuild.asm
generic-y += auxvec.h
generic-y += ipcbuf.h generic-y += ipcbuf.h
header-y += auxvec.h
header-y += bitfield.h header-y += bitfield.h
header-y += bitsperlong.h header-y += bitsperlong.h
header-y += break.h header-y += break.h
......
/*
* Copyright (C) 2015 Imagination Technologies
* Author: Alex Smith <alex.smith@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#ifndef __ASM_AUXVEC_H
#define __ASM_AUXVEC_H
/* Location of VDSO image. */
#define AT_SYSINFO_EHDR 33
#endif /* __ASM_AUXVEC_H */
...@@ -36,7 +36,6 @@ ...@@ -36,7 +36,6 @@
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/cpu-features.h> #include <asm/cpu-features.h>
#include <asm/war.h> #include <asm/war.h>
#include <asm/vdso.h>
#include <asm/dsp.h> #include <asm/dsp.h>
#include <asm/inst.h> #include <asm/inst.h>
#include <asm/msa.h> #include <asm/msa.h>
...@@ -752,16 +751,15 @@ static int setup_rt_frame(void *sig_return, struct ksignal *ksig, ...@@ -752,16 +751,15 @@ static int setup_rt_frame(void *sig_return, struct ksignal *ksig,
struct mips_abi mips_abi = { struct mips_abi mips_abi = {
#ifdef CONFIG_TRAD_SIGNALS #ifdef CONFIG_TRAD_SIGNALS
.setup_frame = setup_frame, .setup_frame = setup_frame,
.signal_return_offset = offsetof(struct mips_vdso, signal_trampoline),
#endif #endif
.setup_rt_frame = setup_rt_frame, .setup_rt_frame = setup_rt_frame,
.rt_signal_return_offset =
offsetof(struct mips_vdso, rt_signal_trampoline),
.restart = __NR_restart_syscall, .restart = __NR_restart_syscall,
.off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
.off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
.off_sc_used_math = offsetof(struct sigcontext, sc_used_math), .off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
.vdso = &vdso_image,
}; };
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
...@@ -801,11 +799,11 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs) ...@@ -801,11 +799,11 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
} }
if (sig_uses_siginfo(&ksig->ka)) if (sig_uses_siginfo(&ksig->ka))
ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset, ret = abi->setup_rt_frame(vdso + abi->vdso->off_rt_sigreturn,
ksig, regs, oldset); ksig, regs, oldset);
else else
ret = abi->setup_frame(vdso + abi->signal_return_offset, ksig, ret = abi->setup_frame(vdso + abi->vdso->off_sigreturn,
regs, oldset); ksig, regs, oldset);
signal_setup_done(ret, ksig, 0); signal_setup_done(ret, ksig, 0);
} }
......
...@@ -31,7 +31,6 @@ ...@@ -31,7 +31,6 @@
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/fpu.h> #include <asm/fpu.h>
#include <asm/war.h> #include <asm/war.h>
#include <asm/vdso.h>
#include <asm/dsp.h> #include <asm/dsp.h>
#include "signal-common.h" #include "signal-common.h"
...@@ -406,14 +405,12 @@ static int setup_rt_frame_32(void *sig_return, struct ksignal *ksig, ...@@ -406,14 +405,12 @@ static int setup_rt_frame_32(void *sig_return, struct ksignal *ksig,
*/ */
struct mips_abi mips_abi_32 = { struct mips_abi mips_abi_32 = {
.setup_frame = setup_frame_32, .setup_frame = setup_frame_32,
.signal_return_offset =
offsetof(struct mips_vdso, o32_signal_trampoline),
.setup_rt_frame = setup_rt_frame_32, .setup_rt_frame = setup_rt_frame_32,
.rt_signal_return_offset =
offsetof(struct mips_vdso, o32_rt_signal_trampoline),
.restart = __NR_O32_restart_syscall, .restart = __NR_O32_restart_syscall,
.off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs), .off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs),
.off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr), .off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr),
.off_sc_used_math = offsetof(struct sigcontext32, sc_used_math), .off_sc_used_math = offsetof(struct sigcontext32, sc_used_math),
.vdso = &vdso_image_o32,
}; };
...@@ -38,7 +38,6 @@ ...@@ -38,7 +38,6 @@
#include <asm/fpu.h> #include <asm/fpu.h>
#include <asm/cpu-features.h> #include <asm/cpu-features.h>
#include <asm/war.h> #include <asm/war.h>
#include <asm/vdso.h>
#include "signal-common.h" #include "signal-common.h"
...@@ -151,11 +150,11 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig, ...@@ -151,11 +150,11 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig,
struct mips_abi mips_abi_n32 = { struct mips_abi mips_abi_n32 = {
.setup_rt_frame = setup_rt_frame_n32, .setup_rt_frame = setup_rt_frame_n32,
.rt_signal_return_offset =
offsetof(struct mips_vdso, n32_rt_signal_trampoline),
.restart = __NR_N32_restart_syscall, .restart = __NR_N32_restart_syscall,
.off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs), .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
.off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr), .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
.off_sc_used_math = offsetof(struct sigcontext, sc_used_math), .off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
.vdso = &vdso_image_n32,
}; };
/* /*
* This file is subject to the terms and conditions of the GNU General Public * Copyright (C) 2015 Imagination Technologies
* License. See the file "COPYING" in the main directory of this archive * Author: Alex Smith <alex.smith@imgtec.com>
* for more details.
* *
* Copyright (C) 2009, 2010 Cavium Networks, Inc. * This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/ */
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/vmalloc.h> #include <linux/err.h>
#include <linux/unistd.h> #include <linux/init.h>
#include <linux/random.h> #include <linux/mm.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <asm/abi.h>
#include <asm/vdso.h> #include <asm/vdso.h>
#include <asm/uasm.h>
#include <asm/processor.h> /* Kernel-provided data used by the VDSO. */
static union mips_vdso_data vdso_data __page_aligned_data;
/* /*
* Including <asm/unistd.h> would give use the 64-bit syscall numbers ... * Mapping for the VDSO data pages. The real pages are mapped manually, as
* what we map and where within the area they are mapped is determined at
* runtime.
*/ */
#define __NR_O32_sigreturn 4119 static struct page *no_pages[] = { NULL };
#define __NR_O32_rt_sigreturn 4193 static struct vm_special_mapping vdso_vvar_mapping = {
#define __NR_N32_rt_sigreturn 6211 .name = "[vvar]",
.pages = no_pages,
};
static struct page *vdso_page; static void __init init_vdso_image(struct mips_vdso_image *image)
static void __init install_trampoline(u32 *tramp, unsigned int sigreturn)
{ {
uasm_i_addiu(&tramp, 2, 0, sigreturn); /* li v0, sigreturn */ unsigned long num_pages, i;
uasm_i_syscall(&tramp, 0);
BUG_ON(!PAGE_ALIGNED(image->data));
BUG_ON(!PAGE_ALIGNED(image->size));
num_pages = image->size / PAGE_SIZE;
for (i = 0; i < num_pages; i++) {
image->mapping.pages[i] =
virt_to_page(image->data + (i * PAGE_SIZE));
}
} }
static int __init init_vdso(void) static int __init init_vdso(void)
{ {
struct mips_vdso *vdso; init_vdso_image(&vdso_image);
vdso_page = alloc_page(GFP_KERNEL); #ifdef CONFIG_MIPS32_O32
if (!vdso_page) init_vdso_image(&vdso_image_o32);
panic("Cannot allocate vdso");
vdso = vmap(&vdso_page, 1, 0, PAGE_KERNEL);
if (!vdso)
panic("Cannot map vdso");
clear_page(vdso);
install_trampoline(vdso->rt_signal_trampoline, __NR_rt_sigreturn);
#ifdef CONFIG_32BIT
install_trampoline(vdso->signal_trampoline, __NR_sigreturn);
#else
install_trampoline(vdso->n32_rt_signal_trampoline,
__NR_N32_rt_sigreturn);
install_trampoline(vdso->o32_signal_trampoline, __NR_O32_sigreturn);
install_trampoline(vdso->o32_rt_signal_trampoline,
__NR_O32_rt_sigreturn);
#endif #endif
vunmap(vdso); #ifdef CONFIG_MIPS32_N32
init_vdso_image(&vdso_image_n32);
#endif
return 0; return 0;
} }
subsys_initcall(init_vdso); subsys_initcall(init_vdso);
static unsigned long vdso_addr(unsigned long start)
{
unsigned long offset = 0UL;
if (current->flags & PF_RANDOMIZE) {
offset = get_random_int();
offset <<= PAGE_SHIFT;
if (TASK_IS_32BIT_ADDR)
offset &= 0xfffffful;
else
offset &= 0xffffffful;
}
return STACK_TOP + offset;
}
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{ {
int ret; struct mips_vdso_image *image = current->thread.abi->vdso;
unsigned long addr;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
unsigned long base, vdso_addr;
struct vm_area_struct *vma;
int ret;
down_write(&mm->mmap_sem); down_write(&mm->mmap_sem);
addr = vdso_addr(mm->start_stack); base = get_unmapped_area(NULL, 0, PAGE_SIZE + image->size, 0, 0);
if (IS_ERR_VALUE(base)) {
addr = get_unmapped_area(NULL, addr, PAGE_SIZE, 0, 0); ret = base;
if (IS_ERR_VALUE(addr)) { goto out;
ret = addr;
goto up_fail;
} }
ret = install_special_mapping(mm, addr, PAGE_SIZE, vdso_addr = base + PAGE_SIZE;
VM_READ|VM_EXEC|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, vma = _install_special_mapping(mm, base, PAGE_SIZE,
&vdso_page); VM_READ | VM_MAYREAD,
&vdso_vvar_mapping);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto out;
}
/* Map data page. */
ret = remap_pfn_range(vma, base,
virt_to_phys(&vdso_data) >> PAGE_SHIFT,
PAGE_SIZE, PAGE_READONLY);
if (ret) if (ret)
goto up_fail; goto out;
/* Map VDSO image. */
vma = _install_special_mapping(mm, vdso_addr, image->size,
VM_READ | VM_EXEC |
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
&image->mapping);
if (IS_ERR(vma)) {
ret = PTR_ERR(vma);
goto out;
}
mm->context.vdso = (void *)addr; mm->context.vdso = (void *)vdso_addr;
ret = 0;
up_fail: out:
up_write(&mm->mmap_sem); up_write(&mm->mmap_sem);
return ret; return ret;
} }
const char *arch_vma_name(struct vm_area_struct *vma)
{
if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso)
return "[vdso]";
return NULL;
}
*.so*
vdso-*image.c
genvdso
vdso*.lds
# Objects to go into the VDSO.
obj-vdso-y := elf.o gettimeofday.o sigreturn.o
# Common compiler flags between ABIs.
ccflags-vdso := \
$(filter -I%,$(KBUILD_CFLAGS)) \
$(filter -E%,$(KBUILD_CFLAGS)) \
$(filter -march=%,$(KBUILD_CFLAGS))
cflags-vdso := $(ccflags-vdso) \
$(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \
-O2 -g -fPIC -fno-common -fno-builtin -G 0 -DDISABLE_BRANCH_PROFILING \
$(call cc-option, -fno-stack-protector)
aflags-vdso := $(ccflags-vdso) \
$(filter -I%,$(KBUILD_CFLAGS)) \
$(filter -E%,$(KBUILD_CFLAGS)) \
-D__ASSEMBLY__ -Wa,-gdwarf-2
#
# For the pre-R6 code in arch/mips/vdso/vdso.h for locating
# the base address of VDSO, the linker will emit a R_MIPS_PC32
# relocation in binutils > 2.25 but it will fail with older versions
# because that relocation is not supported for that symbol. As a result
# of which we are forced to disable the VDSO symbols when building
# with < 2.25 binutils on pre-R6 kernels. For more references on why we
# can't use other methods to get the base address of VDSO please refer to
# the comments on that file.
#
ifndef CONFIG_CPU_MIPSR6
ifeq ($(call ld-ifversion, -gt, 22400000, y),)
$(warning MIPS VDSO requires binutils > 2.24)
obj-vdso-y := $(filter-out gettimeofday.o, $(obj-vdso-y))
ccflags-vdso += -DDISABLE_MIPS_VDSO
endif
endif
# VDSO linker flags.
VDSO_LDFLAGS := \
-Wl,-Bsymbolic -Wl,--no-undefined -Wl,-soname=linux-vdso.so.1 \
-nostdlib -shared \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv) \
$(call cc-ldoption, -Wl$(comma)--build-id)
GCOV_PROFILE := n
#
# Shared build commands.
#
quiet_cmd_vdsold = VDSO $@
cmd_vdsold = $(CC) $(c_flags) $(VDSO_LDFLAGS) \
-Wl,-T $(filter %.lds,$^) $(filter %.o,$^) -o $@
hostprogs-y := genvdso
quiet_cmd_genvdso = GENVDSO $@
define cmd_genvdso
cp $< $(<:%.dbg=%) && \
$(OBJCOPY) -S $< $(<:%.dbg=%) && \
$(obj)/genvdso $< $(<:%.dbg=%) $@ $(VDSO_NAME)
endef
#
# Build native VDSO.
#
native-abi := $(filter -mabi=%,$(KBUILD_CFLAGS))
targets += $(obj-vdso-y)
targets += vdso.lds vdso.so.dbg vdso.so vdso-image.c
obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o)
$(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi)
$(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi)
$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(native-abi)
$(obj)/vdso.so.dbg: $(obj)/vdso.lds $(obj-vdso) FORCE
$(call if_changed,vdsold)
$(obj)/vdso-image.c: $(obj)/vdso.so.dbg $(obj)/genvdso FORCE
$(call if_changed,genvdso)
obj-y += vdso-image.o
#
# Build O32 VDSO.
#
# Define these outside the ifdef to ensure they are picked up by clean.
targets += $(obj-vdso-y:%.o=%-o32.o)
targets += vdso-o32.lds vdso-o32.so.dbg vdso-o32.so vdso-o32-image.c
ifdef CONFIG_MIPS32_O32