-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathlibsee.c
1516 lines (1318 loc) · 59.9 KB
/
libsee.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
* @file libsee.c
* @brief LibSee is a single-file micro-benchmarking library,
* that tells exactly how much time your program spends on any call,
* and fuzzes their behavior to highlight correctness issues.
* It uses `LD_PRELOAD` to override standard library functions.
*
* @author Ash Vardanian
* @date March 4, 2024
*/
#define LIBSEE_VERSION_MAJOR 1
#define LIBSEE_VERSION_MINOR 1
#define LIBSEE_VERSION_PATCH 0
#if !defined(LIBSEE_MAX_THREADS) || LIBSEE_MAX_THREADS <= 0
#define LIBSEE_MAX_THREADS 1024
#endif
/*
* When enabled, the behaviour of the library is modified for fuzzy testing.
* Some function calls will start failing sporadically, highlighting potential issues
* in the calling application.
*
* Examples include:
* > Weird random generator patterns.
* > Uneven monotnically increasing time.
* > In numerical functions add epsilon-sized error.
* > In `malloc`/`free` return NULL or free the same pointer twice.
* > In `memcpy` abort if the ranges overlap.
* > In `write`/`read` process smaller chunk and return value smaller than the length.
* > In `open`/`close` occasionally return -1.
* > In `fork`/`exec` occasionally return -1.
*/
#if !defined(LIBSEE_FUZZ)
#define LIBSEE_FUZZ 0
#endif
/*
* When enabled, the library will print the name of each function it is about to call,
* and then again, once the function has returned. Only used for educational purposes.
*/
#if !defined(LIBSEE_LOG_EVERYTHING)
#define LIBSEE_LOG_EVERYTHING 0
#endif
/*
* The issue here is that RTLD_NEXT is not defined by the posix standard.
* So the GNU people don't enable it unless you #define _GNU_SOURCE or -D_GNU_SOURCE.
* Other relevant pieces of POSIX are dlfcn.h and dlsym.h.
* Interestingly, the later mentions RTLD_NEXT. Apparently, the GNU people are a bit
* confused about what is an extension and what is not.
* Source: https://stackoverflow.com/a/1777507
*/
#define _GNU_SOURCE 1
/*
* All the `_s` functions are part of the C11 standard, but they are not part of the C99 standard.
* Moreover, you need to define `__STDC_WANT_LIB_EXT1__` to `1` to get the declarations of these functions.
*/
#define __STDC_WANT_LIB_EXT1__ 1
#include <dlfcn.h> // `RTLD_NEXT`
#include <errno.h> // `errno_t`
#include <stddef.h> // `rsize_t`
#if !defined(__STDC_LIB_EXT1__)
typedef int errno_t;
typedef size_t rsize_t;
#endif
#define LIBSEE_MAX_SYMBOLS 97
/**
* @brief Contains the number of times each function was called.
*
* One such structure is created for each thread.
* Every element is a `size_t` intialized to zero, so the entire structure can be zeroed with `memset`,
* or casted to an unsigned integers array and used for element-wise operations.
*/
typedef union thread_local_counters {
struct {
size_t strcpy;
size_t strcpy_s;
size_t strncpy;
size_t strncpy_s;
size_t strcat;
size_t strcat_s;
size_t strncat;
size_t strncat_s;
size_t strxfrm;
size_t strlen;
size_t strnlen_s;
size_t strcmp;
size_t strncmp;
size_t strcoll;
size_t strchr;
size_t strrchr;
size_t strspn;
size_t strcspn;
size_t strpbrk;
size_t strstr;
size_t strtok;
size_t strtok_s;
size_t memchr;
size_t memcmp;
size_t memset;
size_t memset_s;
size_t memcpy;
size_t memcpy_s;
size_t memmove;
size_t memmove_s;
size_t strerror;
size_t strerror_s;
size_t memmem;
size_t memrchr;
size_t wcstombs;
size_t wcswidth;
size_t wcwidth;
size_t malloc;
size_t calloc;
size_t realloc;
size_t free;
size_t aligned_alloc;
size_t qsort;
size_t qsort_s;
size_t bsearch;
size_t bsearch_s;
size_t srand;
size_t rand;
size_t fopen;
size_t freopen;
size_t fclose;
size_t fflush;
size_t setbuf;
size_t setvbuf;
size_t fread;
size_t fwrite;
size_t fseek;
size_t ftell;
size_t fsetpos;
size_t fgetpos;
size_t rewind;
size_t clearerr;
size_t feof;
size_t ferror;
size_t perror;
size_t scanf;
size_t fscanf;
size_t sscanf;
size_t vscanf;
size_t vfscanf;
size_t vsscanf;
size_t printf;
size_t fprintf;
size_t sprintf;
size_t snprintf;
size_t vprintf;
size_t vfprintf;
size_t vsprintf;
size_t vsnprintf;
size_t difftime;
size_t time;
size_t clock;
size_t timespec_get;
size_t timespec_getres;
size_t asctime;
size_t asctime_s;
size_t ctime;
size_t ctime_s;
size_t strftime;
size_t wcsftime;
size_t gmtime;
size_t gmtime_r;
size_t gmtime_s;
size_t localtime;
size_t localtime_r;
size_t localtime_s;
size_t mktime;
} named;
size_t indexed[LIBSEE_MAX_SYMBOLS];
} thread_local_counters;
#pragma region Function Pointers
typedef char *(*api_strcpy_t)(char *dest, char const *src);
typedef errno_t (*api_strcpy_s_t)(char *dest, rsize_t destsz, char const *src);
typedef char *(*api_strncpy_t)(char *dest, char const *src, size_t count);
typedef errno_t (*api_strncpy_s_t)(char *dest, rsize_t destsz, char const *src, rsize_t count);
typedef char *(*api_strcat_t)(char *dest, char const *src);
typedef errno_t (*api_strcat_s_t)(char *dest, rsize_t destsz, char const *src);
typedef char *(*api_strncat_t)(char *dest, char const *src, size_t count);
typedef errno_t (*api_strncat_s_t)(char *dest, rsize_t destsz, char const *src, rsize_t count);
typedef size_t (*api_strxfrm_t)(char *dest, char const *src, size_t count);
typedef size_t (*api_strlen_t)(char const *str);
typedef errno_t (*api_strnlen_s_t)(char const *str, rsize_t strsz, size_t *length);
typedef int (*api_strcmp_t)(char const *lhs, char const *rhs);
typedef int (*api_strncmp_t)(char const *lhs, char const *rhs, size_t count);
typedef int (*api_strcoll_t)(char const *lhs, char const *rhs);
typedef char *(*api_strchr_t)(char const *str, int ch);
typedef char *(*api_strrchr_t)(char const *str, int ch);
typedef size_t (*api_strspn_t)(char const *dest, char const *src);
typedef size_t (*api_strcspn_t)(char const *dest, char const *src);
typedef char *(*api_strpbrk_t)(char const *dest, char const *src);
typedef char *(*api_strstr_t)(char const *haystack, char const *needle);
typedef char *(*api_strtok_t)(char *str, char const *delim);
typedef errno_t (*api_strtok_s_t)(char *s, rsize_t *s_max, char const *delim, char **ptr);
typedef void *(*api_memchr_t)(void const *str, int ch, size_t max);
typedef int (*api_memcmp_t)(void const *lhs, void const *rhs, size_t count);
typedef void *(*api_memset_t)(void *dest, int ch, size_t count);
typedef errno_t (*api_memset_s_t)(void *dest, rsize_t destsz, int ch, rsize_t count);
typedef void *(*api_memcpy_t)(void *dest, void const *src, size_t count);
typedef errno_t (*api_memcpy_s_t)(void *dest, rsize_t destsz, void const *src, rsize_t count);
typedef void *(*api_memmove_t)(void *dest, void const *src, size_t count);
typedef errno_t (*api_memmove_s_t)(void *dest, rsize_t destsz, void const *src, rsize_t count);
typedef char *(*api_strerror_t)(int errnum);
typedef errno_t (*api_strerror_s_t)(char *buf, rsize_t bufsz, errno_t errnum);
typedef void *(*api_memmem_t)(void const *haystack, size_t haystacklen, void const *needle, size_t needlelen);
typedef void *(*api_memrchr_t)(void const *s, int c, size_t n);
typedef size_t (*api_wcstombs_t)(char *dest, wchar_t const *src, size_t max);
typedef int (*api_wcswidth_t)(wchar_t const *wcs, size_t n);
typedef int (*api_wcwidth_t)(wchar_t wc);
typedef void *(*api_malloc_t)(size_t);
typedef void *(*api_calloc_t)(size_t, size_t);
typedef void *(*api_realloc_t)(void *, size_t);
typedef void (*api_free_t)(void *);
typedef void *(*api_aligned_alloc_t)(size_t, size_t);
typedef void (*api_qsort_t)(void *base, size_t count, size_t size, int (*compare)(void const *, void const *));
typedef void (*api_qsort_s_t)(void *base, rsize_t count, rsize_t size,
int (*compare)(void const *, void const *, void *), void *context);
typedef void *(*api_bsearch_t)(void const *key, void const *base, size_t count, size_t size,
int (*compare)(void const *, void const *));
typedef void *(*api_bsearch_s_t)(void const *key, void const *base, rsize_t count, rsize_t size,
int (*compare)(void const *, void const *, void *), void *context);
typedef void (*api_srand_t)(unsigned seed);
typedef int (*api_rand_t)(void);
#pragma region Input / Output // Contents of `stdio.h`
#include <stdio.h> // `FILE`
typedef FILE *(*api_fopen_t)(char const *filename, char const *mode);
typedef FILE *(*api_freopen_t)(char const *filename, char const *mode, FILE *stream);
typedef int (*api_fclose_t)(FILE *stream);
typedef int (*api_fflush_t)(FILE *stream);
typedef void (*api_setbuf_t)(FILE *stream, char *buf);
typedef int (*api_setvbuf_t)(FILE *stream, char *buf, int mode, size_t size);
typedef size_t (*api_fread_t)(void *ptr, size_t size, size_t nmemb, FILE *stream);
typedef size_t (*api_fwrite_t)(void const *ptr, size_t size, size_t nmemb, FILE *stream);
typedef int (*api_fseek_t)(FILE *stream, long offset, int whence);
typedef long (*api_ftell_t)(FILE *stream);
typedef int (*api_fsetpos_t)(FILE *stream, fpos_t const *pos);
typedef int (*api_fgetpos_t)(FILE *stream, fpos_t *pos);
typedef void (*api_rewind_t)(FILE *stream);
typedef void (*api_clearerr_t)(FILE *stream);
typedef int (*api_feof_t)(FILE *stream);
typedef int (*api_ferror_t)(FILE *stream);
typedef void (*api_perror_t)(char const *s);
// Narrow character input
typedef int (*api_scanf_t)(char const *format, ...);
typedef int (*api_fscanf_t)(FILE *stream, char const *format, ...);
typedef int (*api_sscanf_t)(char const *str, char const *format, ...);
typedef int (*api_vscanf_t)(char const *format, va_list arg);
typedef int (*api_vfscanf_t)(FILE *stream, char const *format, va_list arg);
typedef int (*api_vsscanf_t)(char const *str, char const *format, va_list arg);
// Narrow character output
typedef int (*api_printf_t)(char const *format, ...);
typedef int (*api_fprintf_t)(FILE *stream, char const *format, ...);
typedef int (*api_sprintf_t)(char *str, char const *format, ...);
typedef int (*api_snprintf_t)(char *str, size_t size, char const *format, ...);
typedef int (*api_vprintf_t)(char const *format, va_list arg);
typedef int (*api_vfprintf_t)(FILE *stream, char const *format, va_list arg);
typedef int (*api_vsprintf_t)(char *str, char const *format, va_list arg);
typedef int (*api_vsnprintf_t)(char *str, size_t size, char const *format, va_list arg);
#pragma endregion
#pragma region Date and Time // Contents of `time.h`
#include <time.h> // `time_t`
typedef double (*api_difftime_t)(time_t end, time_t beginning);
typedef time_t (*api_time_t)(time_t *arg);
typedef clock_t (*api_clock_t)(void);
typedef int (*api_timespec_get_t)(struct timespec *ts, int base);
typedef int (*api_timespec_getres_t)(struct timespec *res, int base);
typedef char *(*api_asctime_t)(struct tm const *time_ptr);
typedef errno_t (*api_asctime_s_t)(char *buf, rsize_t bufsz, struct tm const *time_ptr);
typedef char *(*api_ctime_t)(time_t const *clock);
typedef errno_t (*api_ctime_s_t)(char *buf, rsize_t bufsz, time_t const *clock);
typedef size_t (*api_strftime_t)(char *s, size_t maxsize, char const *format, struct tm const *timeptr);
typedef size_t (*api_wcsftime_t)(wchar_t *wcs, size_t maxsize, wchar_t const *format, struct tm const *timeptr);
typedef struct tm *(*api_gmtime_t)(time_t const *timer);
typedef struct tm *(*api_gmtime_r_t)(time_t const *timer, struct tm *result);
typedef errno_t (*api_gmtime_s_t)(time_t const *timer, struct tm *result);
typedef struct tm *(*api_localtime_t)(time_t const *timer);
typedef struct tm *(*api_localtime_r_t)(time_t const *timer, struct tm *result);
typedef errno_t (*api_localtime_s_t)(time_t const *timer, struct tm *result);
typedef time_t (*api_mktime_t)(struct tm *timeptr);
#pragma endregion
#pragma endregion
/**
* @brief Lookup table for LibC functionality from the underlying implementation.
* Just one such structure is reused between threads.
*/
typedef struct real_apis {
api_strcpy_t strcpy;
api_strcpy_s_t strcpy_s;
api_strncpy_t strncpy;
api_strncpy_s_t strncpy_s;
api_strcat_t strcat;
api_strcat_s_t strcat_s;
api_strncat_t strncat;
api_strncat_s_t strncat_s;
api_strxfrm_t strxfrm;
api_strlen_t strlen;
api_strnlen_s_t strnlen_s;
api_strcmp_t strcmp;
api_strncmp_t strncmp;
api_strcoll_t strcoll;
api_strchr_t strchr;
api_strrchr_t strrchr;
api_strspn_t strspn;
api_strcspn_t strcspn;
api_strpbrk_t strpbrk;
api_strstr_t strstr;
api_strtok_t strtok;
api_strtok_s_t strtok_s;
api_memchr_t memchr;
api_memcmp_t memcmp;
api_memset_t memset;
api_memset_s_t memset_s;
api_memcpy_t memcpy;
api_memcpy_s_t memcpy_s;
api_memmove_t memmove;
api_memmove_s_t memmove_s;
api_strerror_t strerror;
api_strerror_s_t strerror_s;
api_memmem_t memmem;
api_memrchr_t memrchr;
api_wcstombs_t wcstombs;
api_wcswidth_t wcswidth;
api_wcwidth_t wcwidth;
api_malloc_t malloc;
api_calloc_t calloc;
api_realloc_t realloc;
api_free_t free;
api_aligned_alloc_t aligned_alloc;
api_qsort_t qsort;
api_qsort_s_t qsort_s;
api_bsearch_t bsearch;
api_bsearch_s_t bsearch_s;
api_srand_t srand;
api_rand_t rand;
api_fopen_t fopen;
api_freopen_t freopen;
api_fclose_t fclose;
api_fflush_t fflush;
api_setbuf_t setbuf;
api_setvbuf_t setvbuf;
api_fread_t fread;
api_fwrite_t fwrite;
api_fseek_t fseek;
api_ftell_t ftell;
api_fsetpos_t fsetpos;
api_fgetpos_t fgetpos;
api_rewind_t rewind;
api_clearerr_t clearerr;
api_feof_t feof;
api_ferror_t ferror;
api_perror_t perror;
api_scanf_t scanf;
api_fscanf_t fscanf;
api_sscanf_t sscanf;
api_vscanf_t vscanf;
api_vfscanf_t vfscanf;
api_vsscanf_t vsscanf;
api_printf_t printf;
api_fprintf_t fprintf;
api_sprintf_t sprintf;
api_snprintf_t snprintf;
api_vprintf_t vprintf;
api_vfprintf_t vfprintf;
api_vsprintf_t vsprintf;
api_vsnprintf_t vsnprintf;
api_difftime_t difftime;
api_time_t time;
api_clock_t clock;
api_timespec_get_t timespec_get;
api_timespec_getres_t timespec_getres;
api_asctime_t asctime;
api_asctime_s_t asctime_s;
api_ctime_t ctime;
api_ctime_s_t ctime_s;
api_strftime_t strftime;
api_wcsftime_t wcsftime;
api_gmtime_t gmtime;
api_gmtime_r_t gmtime_r;
api_gmtime_s_t gmtime_s;
api_localtime_t localtime;
api_localtime_r_t localtime_r;
api_localtime_s_t localtime_s;
api_mktime_t mktime;
} real_apis;
#define COMPILE_TIME_ASSERT(predicate, message) typedef char message[(predicate) ? 1 : -1]
COMPILE_TIME_ASSERT(sizeof(real_apis) == sizeof(thread_local_counters), sizes_of_structs_must_be_equal);
static real_apis libsee_apis = {NULL};
static thread_local_counters libsee_thread_cycles[LIBSEE_MAX_THREADS] = {0};
static thread_local_counters libsee_thread_calls[LIBSEE_MAX_THREADS] = {0};
#pragma region Global Helpers
void libsee_initialize_if_not(void);
size_t libsee_get_cpu_index(void) {
size_t cpu_index;
#ifdef __aarch64__
// On 64-bit Arm (Aarch64) we can use the MPIDR_EL1 register to get the CPU index.
// https://developer.arm.com/documentation/ddi0601/2020-12/AArch64-Registers/MPIDR-EL1--Multiprocessor-Affinity-Register
size_t mpidr;
asm volatile("mrs %0, mpidr_el1" : "=r"(mpidr));
cpu_index = mpidr & 0xFF; // Extract Affinity level 0 as CPU index
#elif defined(__x86_64__) || defined(__i386__)
// On x86 we can use RDTSCP to get the CPU index.
// https://en.wikipedia.org/wiki/Time_Stamp_Counter
size_t cycle_count;
asm volatile("rdtscp" : "=A"(cycle_count), "=c"(cpu_index));
#else
cpu_index = 0;
#endif
return cpu_index;
}
size_t libsee_get_cpu_cycle(void) {
size_t cycle_count;
#ifdef __aarch64__
// ARMv8 implementation
asm volatile("mrs %0, cntvct_el0" : "=r"(cycle_count));
#elif defined(__x86_64__) || defined(__i386__)
// x86 implementation, but note __rdtsc does not directly give size_t
unsigned int lo, hi;
asm volatile("rdtsc" : "=a"(lo), "=d"(hi));
cycle_count = ((size_t)hi << 32) | lo;
#else
// Placeholder for other architectures
cycle_count = 0;
#endif
return cycle_count;
}
void syscall_print(char const *buf, size_t count) {
long ret;
#ifdef __aarch64__
// Inline assembly syntax for making a system call in AArch64 Linux.
// Uses the svc #0 instruction (Supervisor Call) to make a system call.
// The system call number is passed in x8, and the arguments are in x0, x1, and x2.
long syscall_write = (long)64; // System call number for write in AArch64 Linux
long file_descriptor = (long)1;
asm volatile( //
"mov x0, %1\n" // First argument: file descriptor
"mov x1, %2\n" // Second argument: buffer address
"mov x2, %3\n" // Third argument: buffer size
"mov x8, %4\n" // System call number: SYS_write (64)
"svc #0\n" // Make the system call
"mov %0, x0" // Store the return value
: "=r"(ret)
: "r"(file_descriptor), "r"(buf), "r"((long)count), "r"(syscall_write)
: "x0", "x1", "x2", "x8", "memory");
#elif defined(__x86_64__) || defined(__i386__)
// Inline assembly syntax for making a system call in x86-64 Linux.
// Uses the syscall instruction, passing the system call number in rax,
// and the call arguments in rdi, rsi, and rdx, respectively.
long syscall_write = (long)1; // System call number for write in x86-64 Linux
unsigned int file_descriptor = (unsigned int)1;
asm volatile( //
"syscall"
: "=a"(ret)
: "a"(syscall_write), "D"(file_descriptor), "S"(buf), "d"(count)
: "rcx", "r11", "memory");
(void)ret;
#endif
(void)buf;
(void)count;
(void)ret;
}
void reopen_stdout(void) {
long ret;
// Typically, opening a file would require a path, which complicates direct syscalls,
// because syscalls don't handle C strings directly. However, here's a conceptual approach.
char const path_to_dev_tty[9] = "/dev/tty";
#ifdef __aarch64__
// For AArch64, the 'openat' syscall is number 56.
asm volatile(
"mov x0, %1\n" // AT_FDCWD: Use -100 (represented as an unsigned value) for the current working directory
"mov x1, %2\n" // Pointer to the file path ("/dev/tty")
"mov w2, %w3\n" // Flags (O_WRONLY=1), use 'w2' to specify a 32-bit register explicitly if needed
"mov w3, %w4\n" // Mode (irrelevant for stdout, typically 0), again 'w3' for 32-bit
"mov x8, 56\n" // Syscall number for 'openat' in AArch64
"svc #0\n" // Make the system call
"mov %0, x0" // Output: File descriptor (or error)
: "=r"(ret) // Output: File descriptor (or error)
: "r"((long)-100), "r"(path_to_dev_tty), "r"(1), "r"(0) // Inputs
: "x0", "x1", "x2", "x3", "x8", "memory"); // Clobbered registers
#elif defined(__x86_64__)
// For x86-64, the open syscall is number 2.
asm volatile("syscall"
: "=a"(ret) // Output: File descriptor (or error)
: "a"(2), "D"(path_to_dev_tty), "S"(1), "d"(0) // Inputs: syscall number, path, flags, mode
: "rcx", "r11", "memory"); // Clobbered registers
#endif
// If ret is a valid file descriptor, FD 1 can be duplicated to it, but this assumes FD 1 was valid and openable.
(void)ret;
}
void close_stdout(void) {
long ret;
#ifdef __aarch64__
asm volatile( //
"mov x0, 1\n" // File descriptor for stdout
"mov x8, 57\n" // Syscall number for 'close' in AArch64
"svc #0\n"
"mov %0, x0"
: "=r"(ret)
: // No inputs besides the syscall number and FD
: "x0", "x8", "memory");
#elif defined(__x86_64__)
asm volatile( //
"syscall"
: "=a"(ret)
: "a"(3), "D"(1) // Inputs: syscall number for 'close', FD for stdout
: "rcx", "r11", "memory");
#endif
(void)ret;
}
#if LIBSEE_LOG_EVERYTHING
#define libsee_log(str, count) syscall_print(str, count)
#else
#define libsee_log(str, count) (void)0
#endif
#define libsee_noreturn(function_name, ...) \
do { \
libsee_initialize_if_not(); \
libsee_log(#function_name "-started\n", sizeof(#function_name) + 9); \
size_t _cpu_index, _cycle_count_start, _cycle_count_end; \
_cpu_index = libsee_get_cpu_index(); \
_cycle_count_start = libsee_get_cpu_cycle(); \
libsee_apis.function_name(__VA_ARGS__); \
_cycle_count_end = libsee_get_cpu_cycle(); \
size_t cycle_count = _cycle_count_end - _cycle_count_start; \
libsee_thread_cycles[_cpu_index].named.function_name += cycle_count; \
libsee_thread_calls[_cpu_index].named.function_name++; \
libsee_log(#function_name "-closed\n", sizeof(#function_name) + 8); \
} while (0)
#define libsee_return(function_name, return_type, ...) \
do { \
libsee_initialize_if_not(); \
libsee_log(#function_name "-started\n", sizeof(#function_name) + 9); \
size_t _cpu_index, _cycle_count_start, _cycle_count_end; \
_cpu_index = libsee_get_cpu_index(); \
_cycle_count_start = libsee_get_cpu_cycle(); \
return_type _result = libsee_apis.function_name(__VA_ARGS__); \
_cycle_count_end = libsee_get_cpu_cycle(); \
size_t cycle_count = _cycle_count_end - _cycle_count_start; \
libsee_thread_cycles[_cpu_index].named.function_name += cycle_count; \
libsee_thread_calls[_cpu_index].named.function_name++; \
libsee_log(#function_name "-closed\n", sizeof(#function_name) + 8); \
return _result; \
} while (0)
#define libsee_assign(returned_value, function_name, ...) \
do { \
libsee_initialize_if_not(); \
libsee_log(#function_name "-started\n", sizeof(#function_name) + 9); \
size_t _cpu_index, _cycle_count_start, _cycle_count_end; \
_cpu_index = libsee_get_cpu_index(); \
_cycle_count_start = libsee_get_cpu_cycle(); \
returned_value = libsee_apis.function_name(__VA_ARGS__); \
_cycle_count_end = libsee_get_cpu_cycle(); \
size_t cycle_count = _cycle_count_end - _cycle_count_start; \
libsee_thread_cycles[_cpu_index].named.function_name += cycle_count; \
libsee_thread_calls[_cpu_index].named.function_name++; \
libsee_log(#function_name "-closed\n", sizeof(#function_name) + 8); \
} while (0)
#if defined(_WIN32) || defined(__CYGWIN__)
#if defined(__GNUC__)
#define libsee_export __attribute__((dllexport))
#else
#define libsee_export __declspec(dllexport)
#endif
#else
#if __GNUC__ >= 4
#define libsee_export __attribute__((visibility("default")))
#else
#define libsee_export
#endif
#endif
#pragma endregion
#pragma region Strings // Contents of `string.h`
/** copies one string to another
* https://en.cppreference.com/w/c/string/byte/strcpy
*/
libsee_export char *strcpy(char *dest, char const *src) { libsee_return(strcpy, char *, dest, src); }
libsee_export errno_t strcpy_s(char *dest, rsize_t destsz, char const *src) {
libsee_return(strcpy_s, errno_t, dest, destsz, src);
}
/** copies a certain amount of characters from one string to another
* https://en.cppreference.com/w/c/string/byte/strncpy
*/
libsee_export char *strncpy(char *dest, char const *src, size_t count) {
libsee_return(strncpy, char *, dest, src, count);
}
libsee_export errno_t strncpy_s(char *dest, rsize_t destsz, char const *src, rsize_t count) {
libsee_return(strncpy_s, errno_t, dest, destsz, src, count);
}
/** concatenates two strings
* https://en.cppreference.com/w/c/string/byte/strcat
*/
libsee_export char *strcat(char *dest, char const *src) { libsee_return(strcat, char *, dest, src); }
libsee_export errno_t strcat_s(char *dest, rsize_t destsz, char const *src) {
libsee_return(strcat_s, errno_t, dest, destsz, src);
}
/** concatenates a certain amount of characters of two strings
* https://en.cppreference.com/w/c/string/byte/strncat
*/
libsee_export char *strncat(char *dest, char const *src, size_t count) {
libsee_return(strncat, char *, dest, src, count);
}
libsee_export errno_t strncat_s(char *dest, rsize_t destsz, char const *src, rsize_t count) {
libsee_return(strncat_s, errno_t, dest, destsz, src, count);
}
/** transform a string so that strcmp would produce the same result as strcoll
* https://en.cppreference.com/w/c/string/byte/strxfrm
*/
libsee_export size_t strxfrm(char *dest, char const *src, size_t count) {
libsee_return(strxfrm, size_t, dest, src, count);
}
/** returns the length of a given string
* https://en.cppreference.com/w/c/string/byte/strlen
*/
libsee_export size_t strlen(char const *str) { libsee_return(strlen, size_t, str); }
libsee_export errno_t strnlen_s(char const *str, rsize_t strsz, size_t *length) {
libsee_return(strnlen_s, errno_t, str, strsz, length);
}
/** compares two strings
* https://en.cppreference.com/w/c/string/byte/strcmp
*/
libsee_export int strcmp(char const *lhs, char const *rhs) { libsee_return(strcmp, int, lhs, rhs); }
/** compares a certain amount of characters of two strings
* https://en.cppreference.com/w/c/string/byte/strncmp
*/
libsee_export int strncmp(char const *lhs, char const *rhs, size_t count) {
libsee_return(strncmp, int, lhs, rhs, count);
}
/** compares two strings in accordance to the current locale
* https://en.cppreference.com/w/c/string/byte/strcoll
*/
libsee_export int strcoll(char const *lhs, char const *rhs) { libsee_return(strcoll, int, lhs, rhs); }
/** finds the first occurrence of a character
* https://en.cppreference.com/w/c/string/byte/strchr
*/
libsee_export char *strchr(char const *str, int ch) { libsee_return(strchr, char *, str, ch); }
/** finds the last occurrence of a character
* https://en.cppreference.com/w/c/string/byte/strrchr
*/
libsee_export char *strrchr(char const *str, int ch) { libsee_return(strrchr, char *, str, ch); }
/** returns the length of the maximum initial segment that consists
* of only the characters found in another byte string
* https://en.cppreference.com/w/c/string/byte/strspn
*/
libsee_export size_t strspn(char const *dest, char const *src) { libsee_return(strspn, size_t, dest, src); }
/** returns the length of the maximum initial segment that consists
* of only the characters not found in another byte string
* https://en.cppreference.com/w/c/string/byte/strcspn
*/
libsee_export size_t strcspn(char const *dest, char const *src) { libsee_return(strcspn, size_t, dest, src); }
/** finds the first location of any character in one string, in another string
* https://en.cppreference.com/w/c/string/byte/strpbrk
*/
libsee_export char *strpbrk(char const *dest, char const *src) { libsee_return(strpbrk, char *, dest, src); }
/** finds the first occurrence of a substring of characters
* https://en.cppreference.com/w/c/string/byte/strstr
*/
libsee_export char *strstr(char const *haystack, char const *needle) {
libsee_return(strstr, char *, haystack, needle);
}
/** finds the next token in a byte string
* https://en.cppreference.com/w/c/string/byte/strtok
*/
libsee_export char *strtok(char *str, char const *delim) { libsee_return(strtok, char *, str, delim); }
libsee_export errno_t strtok_s(char *s, rsize_t *s_max, char const *delim, char **ptr) {
libsee_return(strtok_s, errno_t, s, s_max, delim, ptr);
}
/** searches an array for the first occurrence of a character
* https://en.cppreference.com/w/c/string/byte/memchr
*/
libsee_export void *memchr(void const *str, int ch, size_t max) { libsee_return(memchr, void *, str, ch, max); }
/** compares two buffers
* https://en.cppreference.com/w/c/string/byte/memcmp
*/
libsee_export int memcmp(void const *lhs, void const *rhs, size_t count) {
libsee_return(memcmp, int, lhs, rhs, count);
}
/** fills a buffer with a character
* https://en.cppreference.com/w/c/string/byte/memset
*/
libsee_export void *memset(void *dest, int ch, size_t count) { libsee_return(memset, void *, dest, ch, count); }
libsee_export errno_t memset_s(void *dest, rsize_t destsz, int ch, rsize_t count) {
libsee_return(memset_s, errno_t, dest, destsz, ch, count);
}
/** copies one buffer to another
* https://en.cppreference.com/w/c/string/byte/memcpy
*/
libsee_export void *memcpy(void *dest, void const *src, size_t count) {
libsee_return(memcpy, void *, dest, src, count);
}
libsee_export errno_t memcpy_s(void *dest, rsize_t destsz, void const *src, rsize_t count) {
libsee_return(memcpy_s, errno_t, dest, destsz, src, count);
}
/** moves one buffer to another
* https://en.cppreference.com/w/c/string/byte/memmove
*/
libsee_export void *memmove(void *dest, void const *src, size_t count) {
libsee_return(memmove, void *, dest, src, count);
}
libsee_export errno_t memmove_s(void *dest, rsize_t destsz, void const *src, rsize_t count) {
libsee_return(memmove_s, errno_t, dest, destsz, src, count);
}
/** returns a text version of a given error code
* https://en.cppreference.com/w/c/string/byte/strerror
*/
libsee_export char *strerror(int errnum) { libsee_return(strerror, char *, errnum); }
libsee_export errno_t strerror_s(char *buf, rsize_t bufsz, errno_t errnum) {
libsee_return(strerror_s, errno_t, buf, bufsz, errnum);
}
/** relevant @b extensions for substring and reverse character search
* https://man7.org/linux/man-pages/man3/memmem.3.html
*/
libsee_export void *memmem(void const *haystack, size_t haystacklen, void const *needle, size_t needlelen) {
libsee_return(memmem, void *, haystack, haystacklen, needle, needlelen);
}
libsee_export void *memrchr(void const *s, int c, size_t n) { libsee_return(memrchr, void *, s, c, n); }
#pragma endregion
#pragma region Wide Characters // Contents of `wchar.h`
#include <wchar.h>
libsee_export size_t wcstombs(char *dst, wchar_t const *src, size_t len) {
libsee_return(wcstombs, size_t, dst, src, len);
}
libsee_export int wcwidth(wchar_t c) { libsee_return(wcwidth, int, c); }
libsee_export int wcswidth(wchar_t const *s, size_t n) { libsee_return(wcswidth, int, s, n); }
#pragma endregion
#pragma region Numerics // Contents of `stdlib.h`
libsee_export void srand(unsigned seed) { libsee_noreturn(srand, seed); }
libsee_export int rand(void) { libsee_return(rand, int); }
/** common math functions
* https://en.cppreference.com/w/c/numeric/math
*/
/** type-generic functions
* https://en.cppreference.com/w/c/numeric/tgmath
*/
#pragma endregion
#pragma region Input / Output // Contents of `stdio.h`
#include <stdarg.h> // `va_start`
/** opens a file
* https://en.cppreference.com/w/c/io/fopen
*/
libsee_export FILE *fopen(const char *filename, char const *mode) { libsee_return(fopen, FILE *, filename, mode); }
/** reopens a file stream with a different file or mode
* https://en.cppreference.com/w/c/io/freopen
*/
libsee_export FILE *freopen(char const *filename, char const *mode, FILE *stream) {
libsee_return(freopen, FILE *, filename, mode, stream);
}
/** closes a file
* https://en.cppreference.com/w/c/io/fclose
*/
libsee_export int fclose(FILE *stream) { libsee_return(fclose, int, stream); }
/** synchronizes an output stream with the actual file
* https://en.cppreference.com/w/c/io/fflush
*/
libsee_export int fflush(FILE *stream) { libsee_return(fflush, int, stream); }
/** sets the buffer for a file stream
* https://en.cppreference.com/w/c/io/setbuf
*/
libsee_export void setbuf(FILE *stream, char *buf) { libsee_noreturn(setbuf, stream, buf); }
/** sets the buffer and its size for a file stream
* https://en.cppreference.com/w/c/io/setvbuf
*
* The setvbuf function may be used to specify the buffering for stream.
*/
libsee_export int setvbuf(FILE *stream, char *buf, int mode, size_t size) {
libsee_return(setvbuf, int, stream, buf, mode, size);
}
/** reads from a file
* https://en.cppreference.com/w/c/io/fread
*/
libsee_export size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) {
libsee_return(fread, size_t, ptr, size, nmemb, stream);
}
/** writes to a file
* https://en.cppreference.com/w/c/io/fwrite
*/
libsee_export size_t fwrite(void const *ptr, size_t size, size_t nmemb, FILE *stream) {
libsee_return(fwrite, size_t, ptr, size, nmemb, stream);
}
/** moves the file position indicator to a specific location in a file
* https://en.cppreference.com/w/c/io/fseek
*/
libsee_export int fseek(FILE *stream, long offset, int whence) { libsee_return(fseek, int, stream, offset, whence); }
/** returns the current file position indicator
* https://en.cppreference.com/w/c/io/ftell
*/
libsee_export long ftell(FILE *stream) { libsee_return(ftell, long, stream); }
/** sets the file position of the given stream to the given position
* https://en.cppreference.com/w/c/io/fsetpos
*/
libsee_export int fsetpos(FILE *stream, fpos_t const *pos) { libsee_return(fsetpos, int, stream, pos); }
/** gets the file position indicator
* https://en.cppreference.com/w/c/io/fgetpos
*/
libsee_export int fgetpos(FILE *stream, fpos_t *pos) { libsee_return(fgetpos, int, stream, pos); }
/** moves the file position indicator to the beginning of a file
* https://en.cppreference.com/w/c/io/rewind
*/
libsee_export void rewind(FILE *stream) { libsee_noreturn(rewind, stream); }
/** clears errors
* https://en.cppreference.com/w/c/io/clearerr
*/
libsee_export void clearerr(FILE *stream) { libsee_noreturn(clearerr, stream); }
/** checks for the end-of-file
* https://en.cppreference.com/w/c/io/feof
*/
libsee_export int feof(FILE *stream) { libsee_return(feof, int, stream); }
/** checks for a file error
* https://en.cppreference.com/w/c/io/ferror
*/
libsee_export int ferror(FILE *stream) { libsee_return(ferror, int, stream); }
/** displays a character string corresponding of the current error to stderr
* https://en.cppreference.com/w/c/io/perror
*/
libsee_export void perror(char const *s) { libsee_noreturn(perror, s); }
/** reads formatted input from stdin
* https://en.cppreference.com/w/c/io/fscanf
*/
libsee_export int scanf(char const *format, ...) {
va_list args;
int result;
va_start(args, format);
libsee_assign(result, vscanf, format, args);
va_end(args);
return result;
}
libsee_export int fscanf(FILE *stream, char const *format, ...) {
va_list args;
int result;
va_start(args, format);
libsee_assign(result, vfscanf, stream, format, args);
va_end(args);
return result;
}
libsee_export int sscanf(char const *str, char const *format, ...) {
va_list args;
int result;
va_start(args, format);
libsee_assign(result, vsscanf, str, format, args);
va_end(args);
return result;
}
/** reads formatted input from stdin
* https://en.cppreference.com/w/c/io/vscanf
*/
libsee_export int vscanf(char const *format, va_list vlist) { libsee_return(vscanf, int, format, vlist); }
libsee_export int vfscanf(FILE *stream, char const *format, va_list vlist) {
libsee_return(vfscanf, int, stream, format, vlist);
}
libsee_export int vsscanf(char const *str, char const *format, va_list vlist) {
libsee_return(vsscanf, int, str, format, vlist);
}
/** prints formatted output to stdout
* https://en.cppreference.com/w/c/io/fprintf
*/
libsee_export int printf(char const *format, ...) {
int result;
va_list args;
va_start(args, format);
libsee_assign(result, vprintf, format, args);
va_end(args);
return result;
}
libsee_export int fprintf(FILE *stream, char const *format, ...) {
int result;
va_list args;
va_start(args, format);
libsee_assign(result, vfprintf, stream, format, args);
va_end(args);
return result;
}
#undef sprintf // For MacOS
#undef vsprintf // For MacOS
libsee_export int sprintf(char *str, char const *format, ...) {
int result;
va_list args;
va_start(args, format);
libsee_assign(result, vsprintf, str, format, args);
va_end(args);
return result;