210 lines · 5.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0#include "util/jitdump.h" #include <errno.h>#include <fcntl.h>#include <stddef.h>#include <stdint.h>#include <stdio.h>#include <string.h>#include <time.h> #include <sys/mman.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h> #include "../tests.h" #ifndef HAVE_GETTID#include <sys/syscall.h>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);