
This patch adds code generation for RISCV64 instrumentation.The work involved includes the following three points: a) Implements support for instrumenting direct function call and jump on RISC-V which relies on , Atomic instructions (used to increment counters) are only available on RISC-V when the A extension is used. b) Implements support for instrumenting direct function inderect call by implementing the createInstrumentedIndCallHandlerEntryBB and createInstrumentedIndCallHandlerExitBB interfaces. In this process, we need to accurately record the target address and IndCallID to ensure the correct recording of the indirect call counters. c)Implemented the RISCV64 Bolt runtime library, implemented some system call interfaces through embedded assembly. Get the difference between runtime addrress of .text section andstatic address in section header table, which in turn can be used to search for indirect call description. However, the community code currently has problems with relocation in some scenarios, but this has nothing to do with instrumentation. We may continue to submit patches to fix the related bugs.
460 lines
19 KiB
C
460 lines
19 KiB
C
#ifndef LLVM_TOOLS_LLVM_BOLT_SYS_RISCV
|
|
#define LLVM_TOOLS_LLVM_BOLT_SYS_RISCV
|
|
|
|
// Save all registers while keeping 16B stack alignment
|
|
#define SAVE_ALL \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x0, 0(sp)\n" \
|
|
"sd x1, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x2, 0(sp)\n" \
|
|
"sd x3, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x4, 0(sp)\n" \
|
|
"sd x5, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x6, 0(sp)\n" \
|
|
"sd x7, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x8, 0(sp)\n" \
|
|
"sd x9, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x10, 0(sp)\n" \
|
|
"sd x11, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x12, 0(sp)\n" \
|
|
"sd x13, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x14, 0(sp)\n" \
|
|
"sd x15, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x16, 0(sp)\n" \
|
|
"sd x17, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x18, 0(sp)\n" \
|
|
"sd x19, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x20, 0(sp)\n" \
|
|
"sd x21, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x22, 0(sp)\n" \
|
|
"sd x23, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x24, 0(sp)\n" \
|
|
"sd x25, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x26, 0(sp)\n" \
|
|
"sd x27, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x28, 0(sp)\n" \
|
|
"sd x29, 8(sp)\n" \
|
|
"addi sp, sp, -16\n" \
|
|
"sd x30, 0(sp)\n" \
|
|
"sd x31, 8(sp)\n"
|
|
// Mirrors SAVE_ALL
|
|
#define RESTORE_ALL \
|
|
"ld x30, 0(sp)\n" \
|
|
"ld x31, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x28, 0(sp)\n" \
|
|
"ld x29, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x26, 0(sp)\n" \
|
|
"ld x27, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x24, 0(sp)\n" \
|
|
"ld x25, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x22, 0(sp)\n" \
|
|
"ld x23, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x20, 0(sp)\n" \
|
|
"ld x21, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x18, 0(sp)\n" \
|
|
"ld x19, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x16, 0(sp)\n" \
|
|
"ld x17, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x14, 0(sp)\n" \
|
|
"ld x15, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x12, 0(sp)\n" \
|
|
"ld x13, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x10, 0(sp)\n" \
|
|
"ld x11, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x8, 0(sp)\n" \
|
|
"ld x9, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x6, 0(sp)\n" \
|
|
"ld x7, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x4, 0(sp)\n" \
|
|
"ld x5, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x2, 0(sp)\n" \
|
|
"ld x3, 8(sp)\n" \
|
|
"addi sp, sp, 16\n" \
|
|
"ld x0, 0(sp)\n" \
|
|
"ld x1, 8(sp)\n" \
|
|
"addi sp, sp, 16\n"
|
|
|
|
// Anonymous namespace covering everything but our library entry point
|
|
namespace {
|
|
|
|
// Get the difference between runtime addrress of .text section and
|
|
// static address in section header table. Can be extracted from arbitrary
|
|
// pc value recorded at runtime to get the corresponding static address, which
|
|
// in turn can be used to search for indirect call description. Needed because
|
|
// indirect call descriptions are read-only non-relocatable data.
|
|
uint64_t getTextBaseAddress() {
|
|
uint64_t DynAddr;
|
|
uint64_t StaticAddr;
|
|
__asm__ volatile("lla %0, __hot_end\n\t"
|
|
"lui %1, %%hi(__hot_end)\n\t"
|
|
"addi %1, %1, %%lo(__hot_end)\n\t"
|
|
: "=r"(DynAddr), "=r"(StaticAddr));
|
|
return DynAddr - StaticAddr;
|
|
}
|
|
|
|
uint64_t __read(uint64_t fd, const void *buf, uint64_t count) {
|
|
uint64_t ret;
|
|
register uint64_t a0 __asm__("a0") = fd;
|
|
register const void *a1 __asm__("a1") = buf;
|
|
register uint64_t a2 __asm__("a2") = count;
|
|
register uint64_t a7 __asm__("a7") =
|
|
63; // Assuming 63 is the syscall number for read
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a0), "r"(a1), "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __write(uint64_t fd, const void *buf, uint64_t count) {
|
|
uint64_t ret;
|
|
register uint64_t a0 __asm__("a0") = fd;
|
|
register const void *a1 __asm__("a1") = buf;
|
|
register uint64_t a2 __asm__("a2") = count;
|
|
register uint32_t a7 __asm__("a7") =
|
|
64; // Assuming 64 is the syscall number for write
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a0), "r"(a1), "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
void *__mmap(uint64_t addr, uint64_t size, uint64_t prot, uint64_t flags,
|
|
uint64_t fd, uint64_t offset) {
|
|
void *ret;
|
|
register uint64_t a0 __asm__("a0") = addr;
|
|
register uint64_t a1 __asm__("a1") = size;
|
|
register uint64_t a2 __asm__("a2") = prot;
|
|
register uint64_t a3 __asm__("a3") = flags;
|
|
register uint64_t a4 __asm__("a4") = fd;
|
|
register uint64_t a5 __asm__("a5") = offset;
|
|
register uint32_t a7 __asm__("a7") =
|
|
222; // Assuming 222 is the syscall number for mmap
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5),
|
|
"r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __munmap(void *addr, uint64_t size) {
|
|
uint64_t ret;
|
|
register void *a0 __asm__("a0") = addr;
|
|
register uint64_t a1 __asm__("a1") = size;
|
|
register uint32_t a7 __asm__("a7") = 215;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __exit(uint64_t code) {
|
|
uint64_t ret;
|
|
register uint64_t a0 __asm__("a0") = code;
|
|
register uint32_t a7 __asm__("a7") = 94;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __open(const char *pathname, uint64_t flags, uint64_t mode) {
|
|
uint64_t ret;
|
|
register int a0 __asm__("a0") =
|
|
-100; // Assuming -100 is an invalid file descriptor
|
|
register const char *a1 __asm__("a1") = pathname;
|
|
register uint64_t a2 __asm__("a2") = flags;
|
|
register uint64_t a3 __asm__("a3") = mode;
|
|
register uint64_t a7 __asm__("a7") =
|
|
56; // Assuming 56 is the syscall number for open
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
long __getdents64(unsigned int fd, dirent64 *dirp, size_t count) {
|
|
long ret;
|
|
register unsigned int a0 __asm__("a0") = fd;
|
|
register dirent64 *a1 __asm__("a1") = dirp;
|
|
register size_t a2 __asm__("a2") = count;
|
|
register uint32_t a7 __asm__("a7") = 61;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __readlink(const char *pathname, char *buf, size_t bufsize) {
|
|
uint64_t ret;
|
|
register int a0 __asm__("a0") = -100;
|
|
register const char *a1 __asm__("a1") = pathname;
|
|
register char *a2 __asm__("a2") = buf;
|
|
register size_t a3 __asm__("a3") = bufsize;
|
|
register uint32_t a7 __asm__("a7") = 78; // readlinkat
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a3), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __lseek(uint64_t fd, uint64_t pos, uint64_t whence) {
|
|
uint64_t ret;
|
|
register uint64_t a0 __asm__("a0") = fd;
|
|
register uint64_t a1 __asm__("a1") = pos;
|
|
register uint64_t a2 __asm__("a2") = whence;
|
|
register uint32_t a7 __asm__("a7") = 62;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __ftruncate(uint64_t fd, uint64_t length) {
|
|
int ret;
|
|
register uint64_t a0 __asm__("a0") = fd;
|
|
register uint64_t a1 __asm__("a1") = length;
|
|
register uint32_t a7 __asm__("a7") = 46;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __close(uint64_t fd) {
|
|
int ret;
|
|
register uint64_t a0 __asm__("a0") = fd;
|
|
register uint32_t a7 __asm__("a7") =
|
|
57; // Assuming 57 is the syscall number for close
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a0), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __madvise(void *addr, size_t length, int advice) {
|
|
int ret;
|
|
register void *a0 __asm__("a0") = addr;
|
|
register size_t a1 __asm__("a1") = length;
|
|
register int a2 __asm__("a2") = advice;
|
|
register uint32_t a7 __asm__("a7") = 233;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __uname(struct UtsNameTy *buf) {
|
|
int ret;
|
|
register UtsNameTy *a0 __asm__("a0") = buf;
|
|
register uint32_t a7 __asm__("a7") = 160;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __nanosleep(const timespec *req, timespec *rem) {
|
|
uint64_t ret;
|
|
register const timespec *a0 __asm__("a0") = req;
|
|
register timespec *a1 __asm__("a1") = rem;
|
|
register uint32_t a7 __asm__("a7") = 101;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int64_t __fork() {
|
|
uint64_t ret;
|
|
// clone instead of fork with flags
|
|
// "CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD"
|
|
register uint64_t a0 __asm__("a0") = 0x1200011;
|
|
register uint64_t a1 __asm__("a1") = 0;
|
|
register uint64_t a2 __asm__("a2") = 0;
|
|
register uint64_t a3 __asm__("a3") = 0;
|
|
register uint64_t a4 __asm__("a4") = 0;
|
|
register uint32_t a7 __asm__("a7") = 220;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a3), "r"(a4), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __mprotect(void *addr, size_t len, int prot) {
|
|
int ret;
|
|
register void *a0 __asm__("a0") = addr;
|
|
register size_t a1 __asm__("a1") = len;
|
|
register int a2 __asm__("a2") = prot;
|
|
register uint32_t a7 __asm__("a7") = 226;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __getpid() {
|
|
uint64_t ret;
|
|
register uint32_t a7 __asm__("a7") = 172;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __getppid() {
|
|
uint64_t ret;
|
|
register uint32_t a7 __asm__("a7") = 173;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __setpgid(uint64_t pid, uint64_t pgid) {
|
|
int ret;
|
|
register uint64_t a0 __asm__("a0") = pid;
|
|
register uint64_t a1 __asm__("a1") = pgid;
|
|
register uint32_t a7 __asm__("a7") = 154;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __getpgid(uint64_t pid) {
|
|
uint64_t ret;
|
|
register uint64_t a0 __asm__("a0") = pid;
|
|
register uint32_t a7 __asm__("a7") = 155;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __kill(uint64_t pid, int sig) {
|
|
int ret;
|
|
register uint64_t a0 __asm__("a0") = pid;
|
|
register int a1 __asm__("a1") = sig;
|
|
register uint32_t a7 __asm__("a7") = 129;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __fsync(int fd) {
|
|
int ret;
|
|
register int a0 __asm__("a0") = fd;
|
|
register uint32_t a7 __asm__("a7") = 82;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0)
|
|
: "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
uint64_t __sigprocmask(int how, const void *set, void *oldset) {
|
|
uint64_t ret;
|
|
register int a0 __asm__("a0") = how;
|
|
register const void *a1 __asm__("a1") = set;
|
|
register void *a2 __asm__("a2") = oldset;
|
|
register long a3 asm("a3") = 8;
|
|
register uint32_t a7 __asm__("a7") = 135;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a3), "r"(a7)
|
|
: "memory");
|
|
return ret;
|
|
}
|
|
|
|
int __prctl(int option, unsigned long arg2, unsigned long arg3,
|
|
unsigned long arg4, unsigned long arg5) {
|
|
int ret;
|
|
register int a0 __asm__("a0") = option;
|
|
register unsigned long a1 __asm__("a1") = arg2;
|
|
register unsigned long a2 __asm__("a2") = arg3;
|
|
register unsigned long a3 __asm__("a3") = arg4;
|
|
register unsigned long a4 __asm__("a4") = arg5;
|
|
register uint32_t a7 __asm__("a7") = 167;
|
|
__asm__ __volatile__("ecall\n\t"
|
|
"mv %0, a0"
|
|
: "=r"(ret), "+r"(a0), "+r"(a1)
|
|
: "r"(a2), "r"(a3), "r"(a4), "r"(a7)
|
|
: "cc", "memory");
|
|
return ret;
|
|
}
|
|
} // anonymous namespace
|
|
|
|
#endif |