// SPDX-License-Identifier: GPL-2.0 #include "util/jitdump.h" #include #include #include #include #include #include #include #include #include #include #include #include "../tests.h" #ifndef HAVE_GETTID #include static inline pid_t gettid(void) { return (pid_t)syscall(SYS_gettid); } #endif #define CHK_BYTE 0x5a static inline uint64_t get_timestamp(void) { #if defined(__x86_64__) || defined(__i386__) unsigned int low, high; asm volatile("rdtsc" : "=a"(low), "=d"(high)); return low | ((uint64_t)high) << 32; #else struct timespec ts; int ret; ret = clock_gettime(CLOCK_MONOTONIC, &ts); if (ret) return 0; return ((uint64_t)ts.tv_sec * 1000000000) + ts.tv_nsec; #endif } static FILE *open_jitdump(void) { struct jitheader header = { .magic = JITHEADER_MAGIC, .version = JITHEADER_VERSION, .total_size = sizeof(header), .pid = getpid(), .timestamp = get_timestamp(), .flags = #if defined(__x86_64__) || defined(__i386__) JITDUMP_FLAGS_ARCH_TIMESTAMP, #else 0, #endif }; char filename[256]; int fd; FILE *f; void *m; snprintf(filename, sizeof(filename), "jit-%d.dump", getpid()); /* Securely open using O_CREAT | O_EXCL to prevent symlink attacks. */ fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644); if (fd < 0) { pr_err("Failed to open jitdump '%s': %s\n", filename, strerror(errno)); return NULL; } f = fdopen(fd, "w+"); if (!f) { pr_err("Failed to associate stream with fd for '%s'\n", filename); close(fd); unlink(filename); return NULL; } /* Create an MMAP event for the jitdump file. That is how perf tool finds it. */ m = mmap(0, getpagesize(), PROT_READ | PROT_EXEC, MAP_PRIVATE, fileno(f), 0); if (m == MAP_FAILED) { pr_err("mmap failed: %s\n", strerror(errno)); fclose(f); unlink(filename); return NULL; } munmap(m, getpagesize()); if (fwrite(&header, sizeof(header), 1, f) != 1) { pr_err("Error writing jitdump header\n"); fclose(f); unlink(filename); return NULL; } return f; } static int write_jitdump(FILE *f, void *addr, const void *dat, size_t sz, uint64_t *idx) { const char *sym = "jit_workload"; size_t sym_len = strlen(sym) + 1; struct jr_code_load rec = { .p.id = JIT_CODE_LOAD, .p.total_size = sizeof(rec) + sym_len + sz, .p.timestamp = get_timestamp(), .pid = getpid(), .tid = gettid(), .vma = (unsigned long)addr, .code_addr = (unsigned long)addr, .code_size = sz, .code_index = ++*idx, }; if (fwrite(&rec, sizeof(rec), 1, f) != 1 || fwrite(sym, sym_len, 1, f) != 1 || fwrite(dat, sz, 1, f) != 1) return -1; return 0; } static void close_jitdump(FILE *f) { fclose(f); } static int jitdump(int argc __maybe_unused, const char **argv __maybe_unused) { #if defined(__x86_64__) || defined(__i386__) /* Code to execute: mov CHK_BYTE, %eax ; ret */ uint8_t dat[] = { 0xb8, CHK_BYTE, 0x00, 0x00, 0x00, 0xc3 }; #elif defined(__aarch64__) /* Code to execute: mov w0, #CHK_BYTE ; ret */ uint8_t dat[] = { (CHK_BYTE << 5) & 0xff, (CHK_BYTE >> 3) & 0xff, 0x80, 0x52, 0xc0, 0x03, 0x5f, 0xd6 }; #elif defined(__riscv) /* Code to execute: li a0, CHK_BYTE ; ret */ uint8_t dat[] = { 0x13, 0x05, (CHK_BYTE << 4) & 0xff, (CHK_BYTE >> 4) & 0xff, 0x67, 0x80, 0x00, 0x00 }; #elif defined(__powerpc__) /* Code to execute: li r3, CHK_BYTE ; blr */ uint32_t dat[] = { 0x38600000 | (CHK_BYTE & 0xffff), 0x4e800020 }; #elif defined(__s390x__) /* Code to execute: lhi %r2, CHK_BYTE ; br %r14 */ uint8_t dat[] = { 0xa7, 0x28, (CHK_BYTE >> 8) & 0xff, CHK_BYTE & 0xff, 0x07, 0xfe }; #elif defined(__arm__) /* Code to execute: mov r0, #CHK_BYTE ; bx lr */ uint8_t dat[] = { CHK_BYTE & 0xff, 0x00, 0xa0, 0xe3, 0x1e, 0xff, 0x2f, 0xe1 }; #elif defined(__mips__) /* Code to execute: addiu $v0, $zero, CHK_BYTE ; jr $ra ; nop */ uint32_t dat[] = { 0x24020000 | (CHK_BYTE & 0xffff), 0x03e00008, 0x00000000 }; #elif defined(__loongarch__) /* Code to execute: addi.w $a0, $zero, CHK_BYTE ; jirl $zero, $ra, 0 */ uint32_t dat[] = { 0x02800004 | ((CHK_BYTE & 0xfff) << 10), 0x4c000020 }; #else uint32_t dat[0]; #endif void *addr; FILE *f; uint64_t idx = 0; int ret = 1; /* Reachable fallback check for unsupported architectures right at start. */ if (sizeof(dat) == 0) { pr_err("JITDUMP workload not supported on this architecture\n"); return 1; } /* Get a memory page to store executable code. */ addr = mmap(0, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (addr == MAP_FAILED) { pr_err("Failed to map 1 -rwx page\n"); return 1; } f = open_jitdump(); if (!f) { pr_err("Failed to open JITDUMP\n"); munmap(addr, getpagesize()); return 1; } /* Copy executable code to executable memory page. */ memcpy(addr, dat, sizeof(dat)); /* Synchronize the Instruction and Data caches. */ __builtin___clear_cache(addr, (char *)addr + sizeof(dat)); /* Record it in the jitdump file */ if (write_jitdump(f, addr, dat, sizeof(dat), &idx) == 0) { int (*fn)(void) = addr; /* Call the function. */ ret = fn() - CHK_BYTE; } close_jitdump(f); munmap(addr, getpagesize()); return ret; } DEFINE_WORKLOAD(jitdump);