History

KVM: selftests: Implement memcmp(), memcpy(), and memset() for guest use

Implement memcmp(), memcpy(), and memset() to override the compiler's built-in versions in order to guarantee that the compiler won't generate out-of-line calls to external functions via the PLT. This allows the helpers to be safely used in guest code, as KVM selftests don't support dynamic loading of guest code. Steal the implementations from the kernel's generic versions, sans the optimizations in memcmp() for unaligned accesses. Put the utilities in a separate compilation unit and build with -ffreestanding to fudge around a gcc "feature" where it will optimize memset(), memcpy(), etc... by generating a recursive call. I.e. the compiler optimizes itself into infinite recursion. Alternatively, the individual functions could be tagged with optimize("no-tree-loop-distribute-patterns"), but using "optimize" for anything but debug is discouraged, and Linus NAK'd the use of the flag in the kernel proper[*]. https://lore.kernel.org/lkml/CAHk-=wik-oXnUpfZ6Hw37uLykc-_P0Apyn2XuX-odh-3Nzop8w@mail.gmail.com

Sean Christopherson committed 4 years ago

Commit 6b6f7141 parent aae2e722 files · +49 −1Browse files

Unified · Split

2 files
  1. tools/testing/selftests/kvm/Makefile +10 −1
  2. tools/testing/selftests/kvm/lib/string_override.c +39 −0
tools/testing/selftests/kvm/Makefile+10−1
@@ -48,6 +48,8 @@ LIBKVM += lib/rbtree.c · 47 unmodified lines
48LIBKVM += lib/sparsebit.c48LIBKVM += lib/sparsebit.c
49LIBKVM += lib/test_util.c49LIBKVM += lib/test_util.c
50 50
51LIBKVM_STRING += lib/string_override.c
52
51LIBKVM_x86_64 += lib/x86_64/apic.c53LIBKVM_x86_64 += lib/x86_64/apic.c
52LIBKVM_x86_64 += lib/x86_64/handlers.S54LIBKVM_x86_64 += lib/x86_64/handlers.S
53LIBKVM_x86_64 += lib/x86_64/perf_test_util.c55LIBKVM_x86_64 += lib/x86_64/perf_test_util.c
@@ -220,7 +222,8 @@ LIBKVM_C := $(filter %.c,$(LIBKVM)) · 166 unmodified lines
220LIBKVM_S := $(filter %.S,$(LIBKVM))222LIBKVM_S := $(filter %.S,$(LIBKVM))
221LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))223LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C))
222LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))224LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S))
223LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)225LIBKVM_STRING_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_STRING))
226LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(LIBKVM_STRING_OBJ)
224 227
225EXTRA_CLEAN += $(LIBKVM_OBJS) cscope.*228EXTRA_CLEAN += $(LIBKVM_OBJS) cscope.*
226 229
@@ -231,6 +234,12 @@ $(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c · 4 unmodified lines
231$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S234$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S
232 $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@235 $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@
233 236
237# Compile the string overrides as freestanding to prevent the compiler from
238# generating self-referential code, e.g. without "freestanding" the compiler may
239# "optimize" memcmp() by invoking memcmp(), thus causing infinite recursion.
240$(LIBKVM_STRING_OBJ): $(OUTPUT)/%.o: %.c
241 $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -ffreestanding $< -o $@
242
234x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))243x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS))))
235$(TEST_GEN_PROGS): $(LIBKVM_OBJS)244$(TEST_GEN_PROGS): $(LIBKVM_OBJS)
236$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)245$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS)
tools/testing/selftests/kvm/lib/string_override.c+39−0
@@ -0,0 +1,39 @@
1// SPDX-License-Identifier: GPL-2.0-only
2#include <stddef.h>
3
4/*
5 * Override the "basic" built-in string helpers so that they can be used in
6 * guest code. KVM selftests don't support dynamic loading in guest code and
7 * will jump into the weeds if the compiler decides to insert an out-of-line
8 * call via the PLT.
9 */
10int memcmp(const void *cs, const void *ct, size_t count)
11{
12 const unsigned char *su1, *su2;
13 int res = 0;
14
15 for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) {
16 if ((res = *su1 - *su2) != 0)
17 break;
18 }
19 return res;
20}
21
22void *memcpy(void *dest, const void *src, size_t count)
23{
24 char *tmp = dest;
25 const char *s = src;
26
27 while (count--)
28 *tmp++ = *s++;
29 return dest;
30}
31
32void *memset(void *s, int c, size_t count)
33{
34 char *xs = s;
35
36 while (count--)
37 *xs++ = c;
38 return s;
39}