1 // Basic test for LZ4_FREESTANDING
2
3 // $ gcc -ffreestanding -nostdlib freestanding.c && ./a.out || echo $?
4
5 // $ strace ./a.out
6 // execve("./a.out", ["./a.out"], 0x7fffaf5fa580 /* 22 vars */) = 0
7 // brk(NULL) = 0x56536f4fe000
8 // arch_prctl(0x3001 /* ARCH_??? */, 0x7fffc9e74950) = -1 EINVAL (Invalid argument)
9 // mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd5c9c2b000
10 // access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
11 // arch_prctl(ARCH_SET_FS, 0x7fd5c9c2bc40) = 0
12 // set_tid_address(0x7fd5c9c2bf10) = 381
13 // set_robust_list(0x7fd5c9c2bf20, 24) = 0
14 // rseq(0x7fd5c9c2c5e0, 0x20, 0, 0x53053053) = 0
15 // mprotect(0x56536ea63000, 4096, PROT_READ) = 0
16 // exit(0) = ?
17 // +++ exited with 0 +++
18
19 // $ ltrace ./a.out
20 // +++ exited (status 0) +++
21
22 #include <stddef.h>
23 #include <stdint.h>
24
25 #if defined(__cplusplus)
26 # define EXTERN_C extern "C"
27 #else
28 # define EXTERN_C
29 #endif
30
31
32 #if !defined(__x86_64__) || !defined(__linux__)
main(int argc,char ** argv)33 int main(int argc, char** argv) { return 0; }
34 #else
35
36 static void MY_exit(int exitCode);
37 static void MY_abort(void);
38 EXTERN_C void *memmove(void *dst, const void *src, size_t n);
39 EXTERN_C void *memcpy(void * __restrict__ dst, const void * __restrict__ src, size_t n);
40 EXTERN_C void *memset(void *s, int c, size_t n);
41 EXTERN_C int memcmp(const void *s1, const void *s2, size_t n);
42
43 // LZ4/HC basic freestanding setup
44 #define LZ4_FREESTANDING 1
45 #define LZ4_memmove(dst, src, size) memmove((dst),(src),(size))
46 #define LZ4_memcpy(dst, src, size) memcpy((dst),(src),(size))
47 #define LZ4_memset(p,v,s) memset((p),(v),(s))
48
49 #include "../lib/lz4.c"
50 #include "../lib/lz4hc.c"
51
52 // Test for LZ4
test_lz4(const uint8_t * srcData,int srcSize)53 static void test_lz4(const uint8_t* srcData, int srcSize) {
54 // Compress
55 static uint8_t compressBuffer[1024 * 1024];
56 const int compressedSize = LZ4_compress_default(
57 (const char*) srcData,
58 (char*) compressBuffer,
59 srcSize,
60 sizeof(compressBuffer)
61 );
62 if (compressedSize <= 0) {
63 MY_exit(__LINE__);
64 }
65
66 // Decompress
67 static uint8_t decompressBuffer[1024 * 1024];
68 const int decompressedSize = LZ4_decompress_safe(
69 (const char*) compressBuffer,
70 (char*) decompressBuffer,
71 compressedSize,
72 sizeof(decompressBuffer)
73 );
74 if (decompressedSize <= 0) {
75 MY_exit(__LINE__);
76 }
77
78 // Verify
79 if (decompressedSize != srcSize) {
80 MY_exit(__LINE__);
81 }
82 if (memcmp(srcData, decompressBuffer, srcSize) != 0) {
83 MY_exit(__LINE__);
84 }
85 }
86
87
88 // Test for LZ4HC
test_lz4hc(const uint8_t * srcData,int srcSize)89 static void test_lz4hc(const uint8_t* srcData, int srcSize) {
90 // Compress
91 static uint8_t compressBuffer[1024 * 1024];
92 const int compressedSize = LZ4_compress_HC(
93 (const char*) srcData,
94 (char*) compressBuffer,
95 srcSize,
96 sizeof(compressBuffer),
97 LZ4HC_CLEVEL_DEFAULT
98 );
99 if (compressedSize <= 0) {
100 MY_exit(__LINE__);
101 }
102
103 // Decompress
104 static uint8_t decompressBuffer[1024 * 1024];
105 const int decompressedSize = LZ4_decompress_safe(
106 (const char*) compressBuffer,
107 (char*) decompressBuffer,
108 compressedSize,
109 sizeof(decompressBuffer)
110 );
111 if (decompressedSize <= 0) {
112 MY_exit(__LINE__);
113 }
114
115 // Verify
116 if (decompressedSize != srcSize) {
117 MY_exit(__LINE__);
118 }
119 if (memcmp(srcData, decompressBuffer, srcSize) != 0) {
120 MY_exit(__LINE__);
121 }
122 }
123
124
test(void)125 static void test(void) {
126 // First 256 bytes of lz4/README.md
127 static const uint8_t README_md[] = {
128 0x4c, 0x5a, 0x34, 0x20, 0x2d, 0x20, 0x45, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65, 0x6c, 0x79, 0x20,
129 0x66, 0x61, 0x73, 0x74, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
130 0x0a, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
131 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d,
132 0x3d, 0x0a, 0x0a, 0x4c, 0x5a, 0x34, 0x20, 0x69, 0x73, 0x20, 0x6c, 0x6f, 0x73, 0x73, 0x6c, 0x65,
133 0x73, 0x73, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x61,
134 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x2c, 0x0a, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64,
135 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x20,
136 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x3e, 0x20, 0x35, 0x30, 0x30, 0x20, 0x4d, 0x42, 0x2f, 0x73,
137 0x20, 0x70, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x72, 0x65, 0x2c, 0x0a, 0x73, 0x63, 0x61, 0x6c, 0x61,
138 0x62, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x2d, 0x63,
139 0x6f, 0x72, 0x65, 0x73, 0x20, 0x43, 0x50, 0x55, 0x2e, 0x0a, 0x49, 0x74, 0x20, 0x66, 0x65, 0x61,
140 0x74, 0x75, 0x72, 0x65, 0x73, 0x20, 0x61, 0x6e, 0x20, 0x65, 0x78, 0x74, 0x72, 0x65, 0x6d, 0x65,
141 0x6c, 0x79, 0x20, 0x66, 0x61, 0x73, 0x74, 0x20, 0x64, 0x65, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2c,
142 0x0a, 0x77, 0x69, 0x74, 0x68, 0x20, 0x73, 0x70, 0x65, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x20, 0x6d,
143 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x20, 0x47, 0x42, 0x2f, 0x73, 0x20, 0x70, 0x65, 0x72,
144 };
145
146 static const uint8_t* srcData = README_md;
147 static const int srcSize = (int) sizeof(README_md);
148 test_lz4 (srcData, srcSize);
149 test_lz4hc(srcData, srcSize);
150 }
151
152
153 // low level syscall
154 #define SYS_exit (60)
155
os_syscall1(long n,long a1)156 static __inline long os_syscall1(long n, long a1) {
157 register long rax __asm__ ("rax") = n;
158 register long rdi __asm__ ("rdi") = a1;
159 __asm__ __volatile__ ("syscall" : "+r"(rax) : "r"(rdi) : "rcx", "r11", "memory");
160 return rax;
161 }
162
MY_exit(int exitCode)163 static void MY_exit(int exitCode) {
164 (void) os_syscall1(SYS_exit, exitCode);
165 __builtin_unreachable(); // suppress "warning: 'noreturn' function does return"
166 }
167
MY_abort(void)168 static void MY_abort(void) {
169 MY_exit(-1);
170 }
171
172 // https://refspecs.linuxbase.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---assert-fail-1.html
__assert_fail(const char * assertion,const char * file,unsigned int line,const char * function)173 void __assert_fail(const char * assertion, const char * file, unsigned int line, const char * function) {
174 MY_abort();
175 }
176
177
178 // GCC requires memcpy, memmove, memset and memcmp.
179 // https://gcc.gnu.org/onlinedocs/gcc/Standards.html
180 // > GCC requires the freestanding environment provide memcpy, memmove, memset and memcmp.
memmove(void * dst,const void * src,size_t n)181 EXTERN_C void *memmove(void *dst, const void *src, size_t n) {
182 uint8_t* d = (uint8_t*) dst;
183 const uint8_t* s = (const uint8_t*) src;
184
185 if (d > s) {
186 d += n;
187 s += n;
188 while (n--) {
189 *--d = *--s;
190 }
191 } else {
192 while (n--) {
193 *d++ = *s++;
194 }
195 }
196 return dst;
197 }
198
memcpy(void * __restrict__ dst,const void * __restrict__ src,size_t n)199 EXTERN_C void *memcpy(void * __restrict__ dst, const void * __restrict__ src, size_t n) {
200 return memmove(dst, src, n);
201 }
202
memset(void * s,int c,size_t n)203 EXTERN_C void *memset(void *s, int c, size_t n) {
204 uint8_t* p = (uint8_t*) s;
205 while (n--) {
206 *p++ = (uint8_t) c;
207 }
208 return s;
209 }
210
memcmp(const void * s1,const void * s2,size_t n)211 EXTERN_C int memcmp(const void *s1, const void *s2, size_t n) {
212 const uint8_t* p1 = (const uint8_t*) s1;
213 const uint8_t* p2 = (const uint8_t*) s2;
214 while (n--) {
215 const uint8_t c1 = *p1++;
216 const uint8_t c2 = *p2++;
217 if (c1 < c2) {
218 return -1;
219 } else if (c1 > c2) {
220 return 1;
221 }
222 }
223 return 0;
224 }
225
226
227 //
_start(void)228 EXTERN_C void _start(void) {
229 test();
230 MY_exit(0);
231 }
232
main(int argc,char ** argv)233 int main(int argc, char** argv) {
234 test();
235 MY_exit(0);
236 return 0;
237 }
238 #endif
239