From: Jan Janssen Date: Tue, 24 May 2022 11:29:43 +0000 (+0200) Subject: boot: Add memcmp/memcpy/memset X-Git-Tag: v252-rc1~892^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f7967716e3da6c2df0673ba9edbf84ae78233429;p=thirdparty%2Fsystemd.git boot: Add memcmp/memcpy/memset --- diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c index 540ee0b223e..1626ccf2049 100644 --- a/src/boot/efi/efi-string.c +++ b/src/boot/efi/efi-string.c @@ -4,7 +4,6 @@ #include #include "efi-string.h" -#include "macro-fundamental.h" /* String functions for both char and char16_t that should behave the same way as their respective * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas @@ -138,3 +137,62 @@ DEFINE_STRCPY(char16_t, strcpy16); DEFINE_STRCHR(char, strchr8); DEFINE_STRCHR(char16_t, strchr16); + +int efi_memcmp(const void *p1, const void *p2, size_t n) { + if (!p1 || !p2) + return CMP(p1, p2); + + const uint8_t *up1 = p1, *up2 = p2; + while (n > 0) { + if (*up1 != *up2) + return *up1 - *up2; + + up1++; + up2++; + n--; + } + + return 0; +} + +void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n) { + if (!dest || !src || n == 0) + return dest; + + uint8_t *d = dest; + const uint8_t *s = src; + + while (n > 0) { + *d = *s; + d++; + s++; + n--; + } + + return dest; +} + +void *efi_memset(void *p, int c, size_t n) { + if (!p || n == 0) + return p; + + uint8_t *q = p; + while (n > 0) { + *q = c; + q++; + n--; + } + + return p; +} + +#ifdef SD_BOOT +# undef memcmp +# undef memcpy +# undef memset +/* Provide the actual implementation for the builtins. To prevent a linker error, we mark memcpy/memset as + * weak, because gnu-efi is currently providing them. */ +__attribute__((alias("efi_memcmp"))) int memcmp(const void *p1, const void *p2, size_t n); +__attribute__((weak, alias("efi_memcpy"))) void *memcpy(void * restrict dest, const void * restrict src, size_t n); +__attribute__((weak, alias("efi_memset"))) void *memset(void *p, int c, size_t n); +#endif diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h index 9307810a5b9..79d81040221 100644 --- a/src/boot/efi/efi-string.h +++ b/src/boot/efi/efi-string.h @@ -5,6 +5,8 @@ #include #include +#include "macro-fundamental.h" + size_t strnlen8(const char *s, size_t n); size_t strnlen16(const char16_t *s, size_t n); @@ -71,3 +73,18 @@ char16_t *strcpy16(char16_t * restrict dest, const char16_t * restrict src); char *strchr8(const char *s, char c); char16_t *strchr16(const char16_t *s, char16_t c); + +#ifdef SD_BOOT +/* The compiler normaly has knowledge about standard functions such as memcmp, but this is not the case when + * compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do + * optimizations again. Note that we still need to provide implementations as the compiler is free to not + * inline its own implementation and instead issue a library call. */ +# define memcmp __builtin_memcmp +# define memcpy __builtin_memcpy +# define memset __builtin_memset +#endif + +/* The actual implementations of builtins with efi_ prefix so we can unit test them. */ +int efi_memcmp(const void *p1, const void *p2, size_t n); +void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n); +void *efi_memset(void *p, int c, size_t n); diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c index bc033d6fc24..087f9d7e995 100644 --- a/src/boot/efi/test-efi-string.c +++ b/src/boot/efi/test-efi-string.c @@ -240,4 +240,55 @@ TEST(strchr16) { assert_se(strchr16(str, 'B') == &str[4]); } +TEST(efi_memcmp) { + assert_se(efi_memcmp(NULL, NULL, 0) == 0); + assert_se(efi_memcmp(NULL, NULL, 1) == 0); + assert_se(efi_memcmp(NULL, "", 1) < 0); + assert_se(efi_memcmp("", NULL, 1) > 0); + assert_se(efi_memcmp("", "", 0) == 0); + assert_se(efi_memcmp("", "", 1) == 0); + assert_se(efi_memcmp("1", "1", 1) == 0); + assert_se(efi_memcmp("1", "2", 1) < 0); + assert_se(efi_memcmp("A", "a", 1) < 0); + assert_se(efi_memcmp("a", "A", 1) > 0); + assert_se(efi_memcmp("abc", "ab", 2) == 0); + assert_se(efi_memcmp("ab", "abc", 3) < 0); + assert_se(efi_memcmp("ab\000bd", "ab\000bd", 6) == 0); + assert_se(efi_memcmp("ab\000b\0", "ab\000bd", 6) < 0); +} + +TEST(efi_memcpy) { + char buf[10]; + + assert_se(!efi_memcpy(NULL, NULL, 0)); + assert_se(!efi_memcpy(NULL, "", 1)); + assert_se(efi_memcpy(buf, NULL, 0) == buf); + assert_se(efi_memcpy(buf, NULL, 1) == buf); + assert_se(efi_memcpy(buf, "a", 0) == buf); + + assert_se(efi_memcpy(buf, "", 1) == buf); + assert_se(memcmp(buf, "", 1) == 0); + assert_se(efi_memcpy(buf, "1", 1) == buf); + assert_se(memcmp(buf, "1", 1) == 0); + assert_se(efi_memcpy(buf, "23", 3) == buf); + assert_se(memcmp(buf, "23", 3) == 0); + assert_se(efi_memcpy(buf, "45\0ab\0\0\0c", 9) == buf); + assert_se(memcmp(buf, "45\0ab\0\0\0c", 9) == 0); +} + +TEST(efi_memset) { + char buf[10]; + + assert_se(!efi_memset(NULL, '1', 0)); + assert_se(!efi_memset(NULL, '1', 1)); + assert_se(efi_memset(buf, '1', 0) == buf); + + assert_se(efi_memset(buf, '2', 1) == buf); + assert_se(memcmp(buf, "2", 1) == 0); + assert_se(efi_memset(buf, '4', 4) == buf); + assert_se(memcmp(buf, "4444", 4) == 0); + assert_se(efi_memset(buf, 'a', 10) == buf); + assert_se(memcmp(buf, "aaaaaaaaaa", 10) == 0); +} + DEFINE_TEST_MAIN(LOG_INFO);