On Sun, Apr 08, 2018 at 11:15:36AM +0200, Dominik Brodowski wrote: > On a somewhat related note: I'll try to prepare a patch this evening which > lets us build just the __ia32_sys and __x32_compat_sys stubs we actually > need. We have that information already in entry/syscalls/syscall_{32,64}.tbl, > it just needs to be extracted into another header file (in the form of > #define NEED_IA32_sys_xyzzz 1 > ) and then tested within the stubs. After some randconfig testing, this > might be worthwile to add on top of the patches already in tip-asm and the > three renaming patches currently under discussion. So this got a bit more complicated than I though, for a -5k text size decrease. I have my doubts whether the increased code complexity is really worth that minor size decrease. I'll send another patch shortly, though, which fixes up the naming of a few macros in <asm/syscall_wrapper.h>. -------------------------------------------------------------------------- From: Dominik Brodowski <linux@xxxxxxxxxxxxxxxxxxxx> Date: Sun, 8 Apr 2018 21:38:54 +0200 Subject: [PATCH] syscalls/x86: only build stubs which are actually needed A new script arch/x86/entry/syscalls/syscallstubs.sh generates defines in <asm/syscall_stubs.h> for all __ia32_sys_ stubs which are actually needed for IA32_EMULATION, and for all __x32_compat_sys_ stubs which are actually needed for X32. By omitting to build the remaining stubs (which are defined as __SYSCALL_STUBx_UNUSED), there is a measurable decrease in kernel size (-5k): text data bss dec hex filename 12892057 7094202 13570252 33556511 200081f vmlinux-orig 12886397 7087514 13570252 33544163 1ffd7e3 vmlinux Further size cuttings could be achieved by not building stubs for syscalls and compat_syscalls which are not referenced in the syscall tables, such as (at least) sys_gethostname sys_sync_file_range2 sys_send sys_recv compat_sys_mbind compat_sys_set_mempolicy compat_sys_migrate_pages compat_sys_sendtimedop compat_sys_msgrcv compat_sys_semctl compat_sys_send compat_sys_recv Not-yet-signed-off-by: Dominik Brodowski <linux@xxxxxxxxxxxxxxxxxxxx> diff --git a/arch/x86/entry/syscalls/Makefile b/arch/x86/entry/syscalls/Makefile index 6fb9b57ed5ba..036199136513 100644 --- a/arch/x86/entry/syscalls/Makefile +++ b/arch/x86/entry/syscalls/Makefile @@ -11,6 +11,7 @@ syscall64 := $(srctree)/$(src)/syscall_64.tbl syshdr := $(srctree)/$(src)/syscallhdr.sh systbl := $(srctree)/$(src)/syscalltbl.sh +sysstubs := $(srctree)/$(src)/syscallstubs.sh quiet_cmd_syshdr = SYSHDR $@ cmd_syshdr = $(CONFIG_SHELL) '$(syshdr)' '$<' '$@' \ @@ -20,6 +21,11 @@ quiet_cmd_syshdr = SYSHDR $@ quiet_cmd_systbl = SYSTBL $@ cmd_systbl = $(CONFIG_SHELL) '$(systbl)' $< $@ +quiet_cmd_sysstubs = SYSSTUBS $@ + cmd_sysstubs = $(CONFIG_SHELL) '$(sysstubs)' \ + '$(syscall32)' '$(syscall64)' \ + $@ + quiet_cmd_hypercalls = HYPERCALLS $@ cmd_hypercalls = $(CONFIG_SHELL) '$<' $@ $(filter-out $<,$^) @@ -51,6 +57,9 @@ $(out)/syscalls_32.h: $(syscall32) $(systbl) $(out)/syscalls_64.h: $(syscall64) $(systbl) $(call if_changed,systbl) +$(out)/syscall_stubs.h: $(syscall32) $(syscall64) $(sysstubs) + $(call if_changed,sysstubs) + $(out)/xen-hypercalls.h: $(srctree)/scripts/xen-hypercalls.sh $(call if_changed,hypercalls) @@ -60,6 +69,7 @@ uapisyshdr-y += unistd_32.h unistd_64.h unistd_x32.h syshdr-y += syscalls_32.h syshdr-$(CONFIG_X86_64) += unistd_32_ia32.h unistd_64_x32.h syshdr-$(CONFIG_X86_64) += syscalls_64.h +syshdr-$(CONFIG_X86_64) += syscall_stubs.h syshdr-$(CONFIG_XEN) += xen-hypercalls.h targets += $(uapisyshdr-y) $(syshdr-y) diff --git a/arch/x86/entry/syscalls/syscallstubs.sh b/arch/x86/entry/syscalls/syscallstubs.sh new file mode 100644 index 000000000000..4db64d4db75a --- /dev/null +++ b/arch/x86/entry/syscalls/syscallstubs.sh @@ -0,0 +1,110 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 + +in32="$1" +in64="$2" +out="$3" + +emit_stub() { + entry="$1" + if [ "${entry}" != "${entry#__ia32_sys_}" ]; then + # We need a stub named __ia32_sys which is common to 64-bit + # except for a different pt_regs layout. + stubname=${entry#__ia32_sys_} + echo "#define __IA32_SYS_STUBx_${stubname} __IA32_SYS_STUBx" + echo "#define ASM_X86_HAS__ia32_sys_${stubname} 1" + elif [ "$entry" != "${entry#__x32_compat_sys}" ]; then + # We need a stub named __x32_compat_sys_ which decodes a + # 64-bit pt_regs and then calls the real syscall function + stubname="${entry%%/*}" # handle qualifier + stubname=${stubname#__x32_compat_sys_} # handle prefix + echo "#define __X32_COMPAT_SYS_STUBx_${stubname} __X32_COMPAT_SYS_STUBx" + echo "#define ASM_X86_HAS__x32_compat_sys_${stubname} 1" + elif [ "$entry" != "${entry#__ia32_compat_sys_x86}" ]; then + # The compat entry starts with __ia32_compat_sys_x86, so it + # is a specific x86 compat syscall; no need for __ia32_sys_*() + stubname=${entry#__ia32_compat_sys_x86_} + echo "#define __IA32_SYS_STUBx_${stubname} __SYSCALL_STUBx_UNUSED" + echo "#define ASM_X86_HAS__ia32_sys_${stubname} 1" + elif [ "$entry" != "${entry#__ia32_compat_sys_}" ]; then + # The compat entry starts with __ia32_compat_sys, so it is + # is a generic x86 compat syscall; no need for __ia32_sys_*() + stubname=${entry#__ia32_compat_sys_} + echo "#define __IA32_SYS_STUBx_${stubname} __SYSCALL_STUBx_UNUSED" + echo "#define ASM_X86_HAS__ia32_sys_${stubname} 1" + fi; +} + +# First, we need to check which stubs we *need*. While at it, we can determine +# quite many ia32 stubs we do *not* need as the syscall is handled by a compat +# syscall +grep '^[0-9]' "$in32" | sort -n | ( + while read nr abi name entry compat; do + abi=`echo "$abi" | tr '[a-z]' '[A-Z]'` + if [ "$abi" = "I386" -a -n "$compat" ]; then + emit_stub "$compat" + fi + done +) > "$out" + +grep '^[0-9]' "$in64" | sort -n | ( + while read nr abi name entry compat; do + abi=`echo "$abi" | tr '[a-z]' '[A-Z]'` + if [ "$abi" = "X32" -a -n "$entry" ]; then + emit_stub "$entry" + fi + done +) >> "$out" + +# Then, we need to determine all (remaining) stubs we *do not* need. +grep '^[0-9]' "$in64" | sort -n | ( + while read nr abi name entry compat; do + abi=`echo "$abi" | tr '[a-z]' '[A-Z]'` + if [ "$abi" = "COMMON" -o "$abi" = "64" ]; then + if [ -n "$entry" -a "$entry" != "${entry#__x64_sys_}" ]; then + # what's the actual stubname? + stubname="${entry%%/*}" # handle qualifier + stubname="${stubname#__x64_sys_}" # handle prefix + + # syscalls only referenced for 64-bit do not need a stub for + # IA32_EMULATION + echo "#ifndef ASM_X86_HAS__ia32_sys_${stubname}" + echo "#define __IA32_SYS_STUBx_${stubname} __SYSCALL_STUBx_UNUSED" + echo "#endif" + + # A number of compat syscalls are built in even though they are + # completely unused -- e.g. mbind set_mempolicy migrate_pages + # sendtimedop msgrcv semctl. We probably define more compat + # syscalls than exist, but better be safe than sorry... + echo "#ifndef ASM_X86_HAS__x32_compat_sys_${stubname}" + echo "#define __X32_COMPAT_SYS_STUBx_${stubname} __SYSCALL_STUBx_UNUSED" + echo "#endif" + fi + fi + done +) >> "$out" + +grep '^[0-9]' "$in32" | sort -n | ( + while read nr abi name entry compat; do + abi=`echo "$abi" | tr '[a-z]' '[A-Z]'` + if [ "$abi" = "I386" -a -n "$compat" ]; then + if [ "$compat" != "${compat#__ia32_compat_sys}" ]; then + stubname="${compat#__ia32_compat_sys_}" + # If a compat syscall is not needed for x32 (see above), + # we need to assert that the stub is defined as unused + echo "#ifndef ASM_X86_HAS__x32_compat_sys_${stubname}" + echo "#define __X32_COMPAT_SYS_STUBx_${stubname} __SYSCALL_STUBx_UNUSED" + echo "#endif" + fi + fi + done +) >> "$out" + +# FIXME: These syscalls get build even though they are completely unused on x86 +( for unused_syscall in gethostname sync_file_range2 send recv; do + echo "#define __IA32_SYS_STUBx_${unused_syscall} __SYSCALL_STUBx_UNUSED" +done ) >> "$out" +( for unused_compat_syscall in send recv; do + echo "#define __X32_COMPAT_SYS_STUBx_${unused_compat_syscall} __SYSCALL_STUBx_UNUSED" +done ) >> "$out" + diff --git a/arch/x86/include/asm/Kbuild b/arch/x86/include/asm/Kbuild index de690c2d2e33..3aeb3a794da4 100644 --- a/arch/x86/include/asm/Kbuild +++ b/arch/x86/include/asm/Kbuild @@ -5,6 +5,7 @@ generated-y += syscalls_64.h generated-y += unistd_32_ia32.h generated-y += unistd_64_x32.h generated-y += xen-hypercalls.h +generated-y += syscall_stubs.h generic-y += dma-contiguous.h generic-y += early_ioremap.h diff --git a/arch/x86/include/asm/syscall_wrapper.h b/arch/x86/include/asm/syscall_wrapper.h index bad0295739bf..0b847f422bf6 100644 --- a/arch/x86/include/asm/syscall_wrapper.h +++ b/arch/x86/include/asm/syscall_wrapper.h @@ -4,7 +4,10 @@ */ #ifndef _ASM_X86_SYSCALL_WRAPPER_H -#define _ASM_X86_SYSCALL_WRAPPER_H +#define _ASM_X86_SYSCALL_WRAPPER_H 1 + +#define __SYSCALL_STUBx_UNUSED(x, name, ...) +#include <asm/syscall_stubs.h> /* Mapping of registers to parameters for syscalls on x86-64 and x32 */ #define SC_X86_64_REGS_TO_ARGS(x, ...) \ @@ -28,15 +31,15 @@ * kernel/sys_ni.c and SYS_NI in kernel/time/posix-stubs.c to cover this * case as well. */ -#define COMPAT_SC_IA32_STUBx(x, name, ...) \ +#define __IA32_COMPAT_SYS_STUBx(x, name, ...) \ asmlinkage long __ia32_compat_sys##name(const struct pt_regs *regs);\ ALLOW_ERROR_INJECTION(__ia32_compat_sys##name, ERRNO); \ asmlinkage long __ia32_compat_sys##name(const struct pt_regs *regs)\ { \ return __do_compat_sys##name(SC_IA32_REGS_TO_ARGS(x,__VA_ARGS__));\ - } \ + } -#define SC_IA32_WRAPPERx(x, name, ...) \ +#define __IA32_SYS_STUBx(x, name, ...) \ asmlinkage long __ia32_sys##name(const struct pt_regs *regs); \ ALLOW_ERROR_INJECTION(__ia32_sys##name, ERRNO); \ asmlinkage long __ia32_sys##name(const struct pt_regs *regs) \ @@ -64,8 +67,8 @@ SYSCALL_ALIAS(__ia32_sys_##name, sys_ni_posix_timers) #else /* CONFIG_IA32_EMULATION */ -#define COMPAT_SC_IA32_STUBx(x, name, ...) -#define SC_IA32_WRAPPERx(x, fullname, name, ...) +#define __IA32_COMPAT_SYS_STUBx(x, name, ...) +#define __IA32_SYS_STUBx(x, name, ...) #endif /* CONFIG_IA32_EMULATION */ @@ -75,7 +78,7 @@ * of the x86-64-style parameter ordering of x32 syscalls. The syscalls common * with x86_64 obviously do not need such care. */ -#define COMPAT_SC_X32_STUBx(x, name, ...) \ +#define __X32_COMPAT_SYS_STUBx(x, name, ...) \ asmlinkage long __x32_compat_sys##name(const struct pt_regs *regs);\ ALLOW_ERROR_INJECTION(__x32_compat_sys##name, ERRNO); \ asmlinkage long __x32_compat_sys##name(const struct pt_regs *regs)\ @@ -84,7 +87,7 @@ } \ #else /* CONFIG_X86_X32 */ -#define COMPAT_SC_X32_STUBx(x, name, ...) +#define __X32_COMPAT_SYS_STUBx(x, name, ...) #endif /* CONFIG_X86_X32 */ @@ -97,8 +100,8 @@ #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ static long __do_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ static inline long __in_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ - COMPAT_SC_IA32_STUBx(x, name, __VA_ARGS__) \ - COMPAT_SC_X32_STUBx(x, name, __VA_ARGS__) \ + __IA32_COMPAT_SYS_STUBx(x, name, __VA_ARGS__) \ + __X32_COMPAT_SYS_STUBx##name(x, name, __VA_ARGS__) \ static long __do_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ return __in_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));\ @@ -120,7 +123,6 @@ #endif /* CONFIG_COMPAT */ - /* * Instead of the generic __SYSCALL_DEFINEx() definition, this macro takes * struct pt_regs *regs as the only argument of the syscall stub named @@ -163,7 +165,7 @@ { \ return __do_sys##name(SC_X86_64_REGS_TO_ARGS(x,__VA_ARGS__));\ } \ - SC_IA32_WRAPPERx(x, name, __VA_ARGS__) \ + __IA32_SYS_STUBx##name(x, name, __VA_ARGS__) \ static long __do_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \ long ret = __in_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\