From f88f02f78c6c609c912ce0ee1773ee07d6e2e7ac Mon Sep 17 00:00:00 2001 From: Takayuki Matsuoka Date: Sun, 7 Aug 2022 19:08:59 +0900 Subject: Add LZ4_FREESTANDING test on Linux x86-64 platform Also added tests/Makefile entry "test-freestanding". --- tests/.gitignore | 1 + tests/Makefile | 15 +++- tests/freestanding.c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 3 deletions(-) create mode 100644 tests/freestanding.c diff --git a/tests/.gitignore b/tests/.gitignore index 5337fdb..c7d8f19 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -14,6 +14,7 @@ checkFrame decompress-partial decompress-partial-usingDict abiTest +freestanding # test artefacts tmp* diff --git a/tests/Makefile b/tests/Makefile index 4b6ea48..33309b4 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -56,7 +56,7 @@ NB_LOOPS ?= -i1 .PHONY: default default: all -all: fullbench fuzzer frametest roundTripTest datagen checkFrame decompress-partial +all: fullbench fuzzer frametest roundTripTest datagen checkFrame decompress-partial freestanding all32: CFLAGS+=-m32 all32: all @@ -115,6 +115,9 @@ decompress-partial: lz4.o decompress-partial.c decompress-partial-usingDict: lz4.o decompress-partial-usingDict.c $(CC) $(FLAGS) $^ -o $@$(EXT) +freestanding: freestanding.c + $(CC) -ffreestanding -nostdlib $^ -o $@$(EXT) + .PHONY: clean clean: @$(MAKE) -C $(LZ4DIR) $@ > $(VOID) @@ -127,7 +130,7 @@ clean: fasttest$(EXT) roundTripTest$(EXT) \ datagen$(EXT) checkTag$(EXT) \ frameTest$(EXT) decompress-partial$(EXT) \ - abiTest$(EXT) \ + abiTest$(EXT) freestanding$(EXT) \ lz4_all.c @$(RM) -rf $(TESTDIR) @echo Cleaning completed @@ -179,7 +182,7 @@ list: check: test-lz4-essentials .PHONY: test -test: test-lz4 test-lz4c test-frametest test-fullbench test-fuzzer test-amalgamation listTest test-decompress-partial +test: test-lz4 test-lz4c test-frametest test-fullbench test-fuzzer test-amalgamation listTest test-decompress-partial test-freestanding .PHONY: test32 test32: CFLAGS+=-m32 @@ -606,4 +609,10 @@ test-decompress-partial : decompress-partial decompress-partial-usingDict @echo "\n ---- test decompress-partial-usingDict ----" ./decompress-partial-usingDict$(EXT) +test-freestanding: freestanding + @echo "\n ---- test freestanding ----" + ./freestanding$(EXT) + strace ./freestanding$(EXT) + ltrace ./freestanding$(EXT) + endif diff --git a/tests/freestanding.c b/tests/freestanding.c new file mode 100644 index 0000000..2781358 --- /dev/null +++ b/tests/freestanding.c @@ -0,0 +1,231 @@ +// Basic test for LZ4_FREESTANDING + +// $ gcc -ffreestanding -nostdlib freestanding.c && ./a.out || echo $? + +// $ strace ./a.out +// execve("./a.out", ["./a.out"], 0x7fffaf5fa580 /* 22 vars */) = 0 +// brk(NULL) = 0x56536f4fe000 +// arch_prctl(0x3001 /* ARCH_??? */, 0x7fffc9e74950) = -1 EINVAL (Invalid argument) +// mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd5c9c2b000 +// access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) +// arch_prctl(ARCH_SET_FS, 0x7fd5c9c2bc40) = 0 +// set_tid_address(0x7fd5c9c2bf10) = 381 +// set_robust_list(0x7fd5c9c2bf20, 24) = 0 +// rseq(0x7fd5c9c2c5e0, 0x20, 0, 0x53053053) = 0 +// mprotect(0x56536ea63000, 4096, PROT_READ) = 0 +// exit(0) = ? +// +++ exited with 0 +++ + +// $ ltrace ./a.out +// +++ exited (status 0) +++ + +#if !defined(__x86_64__) || !defined(__linux__) +# error This test only supports Linux __x86_64__ +#endif + +#include +#include + +static void MY_exit(int exitCode); +static void MY_abort(void); +void *memmove(void *dst, const void *src, size_t n); +void *memcpy(void * restrict dst, const void * restrict src, size_t n); +void *memset(void *s, int c, size_t n); +int memcmp(const void *s1, const void *s2, size_t n); + +// LZ4/HC basic freestanding setup +#define LZ4_FREESTANDING 1 +#define LZ4_memmove(dst, src, size) memmove((dst),(src),(size)) +#define LZ4_memcpy(dst, src, size) memcpy((dst),(src),(size)) +#define LZ4_memset(p,v,s) memset((p),(v),(s)) + +#include "../lib/lz4.c" +#include "../lib/lz4hc.c" + + +// Test for LZ4 +static void test_lz4(const uint8_t* srcData, int srcSize) { + // Compress + static uint8_t compressBuffer[1024 * 1024]; + const int compressedSize = LZ4_compress_default( + srcData, + compressBuffer, + srcSize, + sizeof(compressBuffer) + ); + if (compressedSize <= 0) { + MY_exit(__LINE__); + } + + // Decompress + static uint8_t decompressBuffer[1024 * 1024]; + const int decompressedSize = LZ4_decompress_safe( + compressBuffer, + decompressBuffer, + compressedSize, + sizeof(decompressBuffer) + ); + if (decompressedSize <= 0) { + MY_exit(__LINE__); + } + + // Verify + if (decompressedSize != srcSize) { + MY_exit(__LINE__); + } + if (memcmp(srcData, decompressBuffer, srcSize) != 0) { + MY_exit(__LINE__); + } +} + + +// Test for LZ4HC +static void test_lz4hc(const uint8_t* srcData, int srcSize) { + // Compress + static uint8_t compressBuffer[1024 * 1024]; + const int compressedSize = LZ4_compress_HC( + srcData, + compressBuffer, + srcSize, + sizeof(compressBuffer), + LZ4HC_CLEVEL_DEFAULT + ); + if (compressedSize <= 0) { + MY_exit(__LINE__); + } + + // Decompress + static uint8_t decompressBuffer[1024 * 1024]; + const int decompressedSize = LZ4_decompress_safe( + compressBuffer, + decompressBuffer, + compressedSize, + sizeof(decompressBuffer) + ); + if (decompressedSize <= 0) { + MY_exit(__LINE__); + } + + // Verify + if (decompressedSize != srcSize) { + MY_exit(__LINE__); + } + if (memcmp(srcData, decompressBuffer, srcSize) != 0) { + MY_exit(__LINE__); + } +} + + +static void test(void) { + // First 256 bytes of lz4/README.md + static const uint8_t README_md[] = { + 0x4c, 0x5a, 0x34, 0x20, 0x2d, 0x20, 0x45, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x6c, 0x79, 0x20, + 0x66, 0x61, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x0a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x0a, 0x0a, 0x4c, 0x5a, 0x34, 0x20, 0x69, 0x73, 0x20, 0x6c, 0x6f, 0x73, 0x73, 0x6c, 0x65, + 0x73, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61, + 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2c, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, + 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, + 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x3e, 0x20, 0x35, 0x30, 0x30, 0x20, 0x4d, 0x42, 0x2f, 0x73, + 0x20, 0x70, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x2c, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x61, + 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x2d, 0x63, + 0x6f, 0x72, 0x65, 0x73, 0x20, 0x43, 0x50, 0x55, 0x2e, 0x0a, 0x49, 0x74, 0x20, 0x66, 0x65, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, + 0x6c, 0x79, 0x20, 0x66, 0x61, 0x73, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2c, + 0x0a, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x6d, + 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x47, 0x42, 0x2f, 0x73, 0x20, 0x70, 0x65, 0x72, + }; + + static const uint8_t* srcData = README_md; + static const int srcSize = (int) sizeof(README_md); + test_lz4 (srcData, srcSize); + test_lz4hc(srcData, srcSize); +} + + +// low level syscall +#define SYS_exit (60) + +static __inline long os_syscall1(long n, long a1) { + register long rax __asm__ ("rax") = n; + register long rdi __asm__ ("rdi") = a1; + __asm__ __volatile__ ("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory"); + return rax; +} + +static void MY_exit(int exitCode) { + (void) os_syscall1(SYS_exit, exitCode); + __builtin_unreachable(); // suppress "warning: 'noreturn' function does return" +} + +static void MY_abort(void) { + MY_exit(-1); +} + +// https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---assert-fail-1.html +void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function) { + MY_abort(); +} + + +// GCC requires memcpy, memmove, memset and memcmp. +// https://gcc.gnu.org/onlinedocs/gcc/Standards.html +// > GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp. +void *memmove(void *dst, const void *src, size_t n) { + uint8_t* d = dst; + const uint8_t* s = src; + + if (d > s) { + d += n; + s += n; + while (n--) { + *--d = *--s; + } + } else { + while (n--) { + *d++ = *s++; + } + } + return dst; +} + +void *memcpy(void * restrict dst, const void * restrict src, size_t n) { + return memmove(dst, src, n); +} + +void *memset(void *s, int c, size_t n) { + uint8_t* p = s; + while (n--) { + *p++ = (uint8_t) c; + } + return s; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + const uint8_t* p1 = (const uint8_t*) s1; + const uint8_t* p2 = (const uint8_t*) s2; + while (n--) { + const uint8_t c1 = *p1++; + const uint8_t c2 = *p2++; + if (c1 < c2) { + return -1; + } else if (c1 > c2) { + return 1; + } + } + return 0; +} + + +// +void _start(void) { + test(); + MY_exit(0); +} + +int main(int argc, char** argv) { + test(); + MY_exit(0); + return 0; +} -- cgit v0.12