boot: Add memcmp/memcpy/memset
This commit is contained in:
@@ -4,7 +4,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <stddef.h>
|
||||
#include <uchar.h>
|
||||
|
||||
#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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user