#include <stdatomic.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
struct kcov_ctx {
uintptr_t base;
uint64_t words;
uint8_t wordsize;
int mode;
};
extern bool syd_kcov_get_ctx(struct kcov_ctx *out_ctx) __attribute__((weak));
static _Thread_local atomic_bool tls_rec = false;
static _Thread_local long tls_sys = -1;
__attribute__((no_sanitize("coverage"))) void syd_kcov_set_syscall(long nr)
{
tls_sys = nr;
}
static inline uint64_t mix_syscall(uint64_t pc)
{
if (tls_sys > 0) {
pc ^= (uint64_t)tls_sys * 0x517cc1b727220a95ULL;
}
return pc;
}
extern char __executable_start[] __attribute__((weak));
static uint64_t base_addr;
static atomic_bool base_init = false;
static inline uint64_t get_base_addr(void)
{
if (__builtin_expect(!atomic_load_explicit(&base_init, memory_order_acquire),
0)) {
base_addr = __executable_start ? (uint64_t)__executable_start : 0;
atomic_store_explicit(&base_init, true, memory_order_release);
}
return base_addr;
}
static inline uint64_t pc_rel(uint64_t raw_pc)
{
return raw_pc - get_base_addr();
}
static uint64_t to_canon_pc(uint64_t v)
{
v &= ~0xFULL; #if __SIZEOF_POINTER__ == 8
return 0xFFFFFFFF80000000ULL | (v & 0x3FFFFFF0ULL);
#else
uint32_t x = ((uint32_t)v) & 0x0FFFFFF0U;
return (uint64_t)(0x80000000U | x);
#endif
}
static void record_pc_impl(uint64_t pc)
{
bool expected = false;
if (!atomic_compare_exchange_strong(&tls_rec, &expected, true)) {
return;
}
if (!syd_kcov_get_ctx) {
atomic_store(&tls_rec, false);
return;
}
struct kcov_ctx ctx;
if (!syd_kcov_get_ctx(&ctx)) {
atomic_store(&tls_rec, false);
return;
}
if (ctx.mode != 0 || ctx.words <= 1) {
atomic_store(&tls_rec, false);
return;
}
uint64_t cap = ctx.words - 1;
uint64_t cov = to_canon_pc(mix_syscall(pc));
if (ctx.wordsize == 4) {
uint32_t *area = (uint32_t *)ctx.base;
for (;;) {
uint32_t cnt = __atomic_load_n(&area[0], __ATOMIC_ACQUIRE);
if (cnt >= cap)
break;
__atomic_store_n(&area[1 + cnt], (uint32_t)cov,
__ATOMIC_RELAXED);
if (__atomic_compare_exchange_n(&area[0], &cnt, cnt + 1,
true, __ATOMIC_ACQ_REL,
__ATOMIC_ACQUIRE))
break;
}
} else {
uint64_t *area = (uint64_t *)ctx.base;
for (;;) {
uint64_t cnt = __atomic_load_n(&area[0], __ATOMIC_ACQUIRE);
if (cnt >= cap)
break;
__atomic_store_n(&area[1 + cnt], cov, __ATOMIC_RELAXED);
if (__atomic_compare_exchange_n(&area[0], &cnt, cnt + 1,
true, __ATOMIC_ACQ_REL,
__ATOMIC_ACQUIRE))
break;
}
}
atomic_store(&tls_rec, false);
}
static inline uint64_t kcov_cmp_type(uint8_t sz, bool is_const)
{
uint64_t size_code;
switch (sz) {
case 1:
size_code = 0;
break;
case 2:
size_code = 2;
break;
case 4:
size_code = 4;
break;
case 8:
size_code = 6;
break;
default:
size_code = 6;
break;
}
return size_code | (is_const ? 1 : 0);
}
static void record_cmp_impl(uint8_t sz, bool is_const, uint64_t a, uint64_t b,
uint64_t ip)
{
bool expected = false;
if (!atomic_compare_exchange_strong(&tls_rec, &expected, true)) {
return;
}
if (!syd_kcov_get_ctx) {
atomic_store(&tls_rec, false);
return;
}
struct kcov_ctx ctx;
if (syd_kcov_get_ctx(&ctx)) {
uint64_t slots = ((uint64_t)ctx.words * ctx.wordsize) / 8;
uint64_t cap = slots > 1 ? (slots - 1) / 4 : 0;
if (ctx.mode == 1 && cap > 0) { uint64_t *area = (uint64_t *)ctx.base;
for (;;) {
uint64_t cnt = __atomic_load_n(&area[0],
__ATOMIC_ACQUIRE);
if (cnt >= cap)
break;
uint64_t rec = 1 + cnt * 4;
__atomic_store_n(&area[rec + 0],
kcov_cmp_type(sz, is_const),
__ATOMIC_RELAXED);
__atomic_store_n(&area[rec + 1], a, __ATOMIC_RELAXED);
__atomic_store_n(&area[rec + 2], b, __ATOMIC_RELAXED);
__atomic_store_n(&area[rec + 3],
to_canon_pc(mix_syscall(ip)),
__ATOMIC_RELAXED);
if (__atomic_compare_exchange_n(&area[0], &cnt,
cnt + 1, true,
__ATOMIC_ACQ_REL,
__ATOMIC_ACQUIRE))
break;
}
}
}
atomic_store(&tls_rec, false);
}
void __sanitizer_cov_trace_pc(void)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_pc_impl(pc);
}
void __sanitizer_cov_trace_cmp1(uint8_t a, uint8_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(1, false, a, b, pc);
}
void __sanitizer_cov_trace_cmp2(uint16_t a, uint16_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(2, false, a, b, pc);
}
void __sanitizer_cov_trace_cmp4(uint32_t a, uint32_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(4, false, a, b, pc);
}
void __sanitizer_cov_trace_cmp8(uint64_t a, uint64_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(8, false, a, b, pc);
}
void __sanitizer_cov_trace_const_cmp1(uint8_t a, uint8_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(1, true, a, b, pc);
}
void __sanitizer_cov_trace_const_cmp2(uint16_t a, uint16_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(2, true, a, b, pc);
}
void __sanitizer_cov_trace_const_cmp4(uint32_t a, uint32_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(4, true, a, b, pc);
}
void __sanitizer_cov_trace_const_cmp8(uint64_t a, uint64_t b)
{
uint64_t pc = pc_rel((uint64_t)__builtin_return_address(0));
record_cmp_impl(8, true, a, b, pc);
}
void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases)
{
uint8_t sz;
switch (cases[1]) {
case 8:
sz = 1;
break;
case 16:
sz = 2;
break;
case 32:
sz = 4;
break;
case 64:
sz = 8;
break;
default:
return;
}
uint64_t ip = pc_rel((uint64_t)__builtin_return_address(0));
for (uint64_t i = 0; i < cases[0]; i++)
record_cmp_impl(sz, true, cases[i + 2], val, ip);
}