| // 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) +++ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #if defined(__cplusplus) |
| # define EXTERN_C extern "C" |
| #else |
| # define EXTERN_C |
| #endif |
| |
| |
| #if !defined(__x86_64__) || !defined(__linux__) |
| EXTERN_C void _start(void) { } |
| int main(int argc, char** argv) { return 0; } |
| #else |
| |
| static void MY_exit(int exitCode); |
| static void MY_abort(void); |
| EXTERN_C void *memmove(void *dst, const void *src, size_t n); |
| EXTERN_C void *memcpy(void * __restrict__ dst, const void * __restrict__ src, size_t n); |
| EXTERN_C void *memset(void *s, int c, size_t n); |
| EXTERN_C 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( |
| (const char*) srcData, |
| (char*) compressBuffer, |
| srcSize, |
| sizeof(compressBuffer) |
| ); |
| if (compressedSize <= 0) { |
| MY_exit(__LINE__); |
| } |
| |
| // Decompress |
| static uint8_t decompressBuffer[1024 * 1024]; |
| const int decompressedSize = LZ4_decompress_safe( |
| (const char*) compressBuffer, |
| (char*) 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( |
| (const char*) srcData, |
| (char*) 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( |
| (const char*) compressBuffer, |
| (char*) 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. |
| EXTERN_C void *memmove(void *dst, const void *src, size_t n) { |
| uint8_t* d = (uint8_t*) dst; |
| const uint8_t* s = (const uint8_t*) src; |
| |
| if (d > s) { |
| d += n; |
| s += n; |
| while (n--) { |
| *--d = *--s; |
| } |
| } else { |
| while (n--) { |
| *d++ = *s++; |
| } |
| } |
| return dst; |
| } |
| |
| EXTERN_C void *memcpy(void * __restrict__ dst, const void * __restrict__ src, size_t n) { |
| return memmove(dst, src, n); |
| } |
| |
| EXTERN_C void *memset(void *s, int c, size_t n) { |
| uint8_t* p = (uint8_t*) s; |
| while (n--) { |
| *p++ = (uint8_t) c; |
| } |
| return s; |
| } |
| |
| EXTERN_C 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; |
| } |
| |
| |
| // |
| EXTERN_C void _start(void) { |
| test(); |
| MY_exit(0); |
| } |
| |
| int main(int argc, char** argv) { |
| test(); |
| MY_exit(0); |
| return 0; |
| } |
| #endif |