|
6 | 6 | // |
7 | 7 | //===----------------------------------------------------------------------===// |
8 | 8 | #include "../Target.h" |
| 9 | +#include "../Error.h" |
| 10 | +#include "../MmapUtils.h" |
| 11 | +#include "../SerialSnippetGenerator.h" |
| 12 | +#include "../SnippetGenerator.h" |
| 13 | +#include "../SubprocessMemory.h" |
9 | 14 | #include "AArch64.h" |
10 | 15 | #include "AArch64RegisterInfo.h" |
| 16 | +#include "llvm/CodeGen/MachineInstrBuilder.h" |
| 17 | +#include "llvm/MC/MCInstBuilder.h" |
| 18 | +#include "llvm/MC/MCRegisterInfo.h" |
| 19 | +#include <vector> |
| 20 | +#define DEBUG_TYPE "exegesis-aarch64-target" |
11 | 21 |
|
12 | 22 | #if defined(__aarch64__) && defined(__linux__) |
13 | | -#include <sys/prctl.h> // For PR_PAC_* constants |
| 23 | +#include <sys/mman.h> |
| 24 | +#include <sys/syscall.h> |
| 25 | +#include <unistd.h> // for getpagesize() |
| 26 | +#ifdef HAVE_LIBPFM |
| 27 | +#include <perfmon/perf_event.h> |
| 28 | +#endif // HAVE_LIBPFM |
| 29 | +#include <linux/prctl.h> // For PR_PAC_* constants |
| 30 | +#include <sys/prctl.h> |
| 31 | +#ifndef PR_PAC_SET_ENABLED_KEYS |
| 32 | +#define PR_PAC_SET_ENABLED_KEYS 60 |
| 33 | +#endif |
| 34 | +#ifndef PR_PAC_GET_ENABLED_KEYS |
| 35 | +#define PR_PAC_GET_ENABLED_KEYS 61 |
| 36 | +#endif |
14 | 37 | #ifndef PR_PAC_APIAKEY |
15 | 38 | #define PR_PAC_APIAKEY (1UL << 0) |
16 | 39 | #endif |
@@ -197,7 +220,39 @@ class ExegesisAArch64Target : public ExegesisTarget { |
197 | 220 | ExegesisAArch64Target() |
198 | 221 | : ExegesisTarget(AArch64CpuPfmCounters, AArch64_MC::isOpcodeAvailable) {} |
199 | 222 |
|
| 223 | + enum ArgumentRegisters { |
| 224 | + CodeSize = AArch64::X12, |
| 225 | + AuxiliaryMemoryFD = AArch64::X13, |
| 226 | + TempRegister = AArch64::X16, |
| 227 | + }; |
| 228 | + |
| 229 | + std::vector<MCInst> _generateRegisterStackPop(MCRegister Reg, |
| 230 | + int imm = 0) const override { |
| 231 | + std::vector<MCInst> Insts; |
| 232 | + if (AArch64::GPR32RegClass.contains(Reg) || |
| 233 | + AArch64::GPR64RegClass.contains(Reg)) { |
| 234 | + generateRegisterStackPop(Reg, Insts, imm); |
| 235 | + return Insts; |
| 236 | + } |
| 237 | + return {}; |
| 238 | + } |
| 239 | + |
200 | 240 | private: |
| 241 | +#ifdef __linux__ |
| 242 | + std::vector<MCInst> generateExitSyscall(unsigned ExitCode) const override; |
| 243 | + std::vector<MCInst> |
| 244 | + generateMmap(uintptr_t Address, size_t Length, |
| 245 | + uintptr_t FileDescriptorAddress) const override; |
| 246 | + void generateMmapAuxMem(std::vector<MCInst> &GeneratedCode) const override; |
| 247 | + std::vector<MCInst> generateMemoryInitialSetup() const override; |
| 248 | + std::vector<MCInst> setStackRegisterToAuxMem() const override; |
| 249 | + uintptr_t getAuxiliaryMemoryStartAddress() const override; |
| 250 | + std::vector<MCInst> configurePerfCounter(long Request, |
| 251 | + bool SaveRegisters) const override; |
| 252 | + std::vector<MCRegister> getArgumentRegisters() const override; |
| 253 | + std::vector<MCRegister> getRegistersNeedSaving() const override; |
| 254 | +#endif // __linux__ |
| 255 | + |
201 | 256 | std::vector<MCInst> setRegTo(const MCSubtargetInfo &STI, MCRegister Reg, |
202 | 257 | const APInt &Value) const override { |
203 | 258 | if (AArch64::GPR32RegClass.contains(Reg)) |
@@ -237,6 +292,156 @@ class ExegesisAArch64Target : public ExegesisTarget { |
237 | 292 |
|
238 | 293 | } // namespace |
239 | 294 |
|
| 295 | +#ifdef __linux__ |
| 296 | +// true : let use of fixed address to Virtual Address Space Ceiling |
| 297 | +// false: let kernel choose the address of the auxiliary memory |
| 298 | +bool UseFixedAddress = true; |
| 299 | + |
| 300 | +static constexpr const uintptr_t VAddressSpaceCeiling = 0x0000800000000000; |
| 301 | + |
| 302 | +static void generateRoundToNearestPage(unsigned int TargetRegister, |
| 303 | + std::vector<MCInst> &GeneratedCode) { |
| 304 | + int PageSizeShift = static_cast<int>(round(log2(getpagesize()))); |
| 305 | + // Round down to the nearest page by getting rid of the least significant bits |
| 306 | + // representing location in the page. |
| 307 | + |
| 308 | + // Single instruction using AND with inverted mask (effectively BIC) |
| 309 | + uint64_t BitsToClearMask = (1ULL << PageSizeShift) - 1; // 0xFFF |
| 310 | + uint64_t AndMask = ~BitsToClearMask; // ...FFFFFFFFFFFF000 |
| 311 | + GeneratedCode.push_back(MCInstBuilder(AArch64::ANDXri) |
| 312 | + .addReg(TargetRegister) // Xd |
| 313 | + .addReg(TargetRegister) // Xn |
| 314 | + .addImm(AndMask) // imm bitmask |
| 315 | + ); |
| 316 | +} |
| 317 | + |
| 318 | +std::vector<MCInst> |
| 319 | +ExegesisAArch64Target::generateExitSyscall(unsigned ExitCode) const { |
| 320 | + std::vector<MCInst> ExitCallCode; |
| 321 | + ExitCallCode.push_back(loadImmediate(AArch64::X0, 64, APInt(64, ExitCode))); |
| 322 | + generateSysCall(SYS_exit, ExitCallCode); // SYS_exit is 93 |
| 323 | + return ExitCallCode; |
| 324 | +} |
| 325 | + |
| 326 | +std::vector<MCInst> |
| 327 | +ExegesisAArch64Target::generateMmap(uintptr_t Address, size_t Length, |
| 328 | + uintptr_t FileDescriptorAddress) const { |
| 329 | + // mmap(address, length, prot, flags, fd, offset=0) |
| 330 | + int flags = MAP_SHARED; |
| 331 | + if (Address != 0) { |
| 332 | + flags |= MAP_FIXED_NOREPLACE; |
| 333 | + } |
| 334 | + std::vector<MCInst> MmapCode; |
| 335 | + MmapCode.push_back( |
| 336 | + loadImmediate(AArch64::X0, 64, APInt(64, Address))); // map adr |
| 337 | + MmapCode.push_back( |
| 338 | + loadImmediate(AArch64::X1, 64, APInt(64, Length))); // length |
| 339 | + MmapCode.push_back(loadImmediate(AArch64::X2, 64, |
| 340 | + APInt(64, PROT_READ | PROT_WRITE))); // prot |
| 341 | + MmapCode.push_back(loadImmediate(AArch64::X3, 64, APInt(64, flags))); // flags |
| 342 | + // FIXME: File descriptor address is not initialized. |
| 343 | + // Copy file descriptor location from aux memory into X4 |
| 344 | + MmapCode.push_back( |
| 345 | + loadImmediate(AArch64::X4, 64, APInt(64, FileDescriptorAddress))); // fd |
| 346 | + // Dereference file descriptor into FD argument register |
| 347 | + // MmapCode.push_back(MCInstBuilder(AArch64::LDRWui) |
| 348 | + // .addReg(AArch64::W4) // Destination register |
| 349 | + // .addReg(AArch64::X4) // Base register (address) |
| 350 | + // .addImm(0)); // Offset (-byte words) |
| 351 | + // FIXME: This is not correct. |
| 352 | + MmapCode.push_back(loadImmediate(AArch64::X5, 64, APInt(64, 0))); // offset |
| 353 | + generateSysCall(SYS_mmap, MmapCode); // SYS_mmap is 222 |
| 354 | + return MmapCode; |
| 355 | +} |
| 356 | + |
| 357 | +void ExegesisAArch64Target::generateMmapAuxMem( |
| 358 | + std::vector<MCInst> &GeneratedCode) const { |
| 359 | + int fd = -1; |
| 360 | + int flags = MAP_SHARED; |
| 361 | + uintptr_t address = getAuxiliaryMemoryStartAddress(); |
| 362 | + if (fd == -1) |
| 363 | + flags |= MAP_ANONYMOUS; |
| 364 | + if (address != 0) |
| 365 | + flags |= MAP_FIXED_NOREPLACE; |
| 366 | + int prot = PROT_READ | PROT_WRITE; |
| 367 | + |
| 368 | + GeneratedCode.push_back( |
| 369 | + loadImmediate(AArch64::X0, 64, APInt(64, address))); // map adr |
| 370 | + GeneratedCode.push_back(loadImmediate( |
| 371 | + AArch64::X1, 64, |
| 372 | + APInt(64, SubprocessMemory::AuxiliaryMemorySize))); // length |
| 373 | + GeneratedCode.push_back( |
| 374 | + loadImmediate(AArch64::X2, 64, APInt(64, prot))); // prot |
| 375 | + GeneratedCode.push_back( |
| 376 | + loadImmediate(AArch64::X3, 64, APInt(64, flags))); // flags |
| 377 | + GeneratedCode.push_back(loadImmediate(AArch64::X4, 64, APInt(64, fd))); // fd |
| 378 | + GeneratedCode.push_back( |
| 379 | + loadImmediate(AArch64::X5, 64, APInt(64, 0))); // offset |
| 380 | + generateSysCall(SYS_mmap, GeneratedCode); // SYS_mmap is 222 |
| 381 | +} |
| 382 | + |
| 383 | +std::vector<MCInst> ExegesisAArch64Target::generateMemoryInitialSetup() const { |
| 384 | + std::vector<MCInst> MemoryInitialSetupCode; |
| 385 | + generateMmapAuxMem(MemoryInitialSetupCode); // FIXME: Uninit file descriptor |
| 386 | + |
| 387 | + // If using fixed address for auxiliary memory skip this step, |
| 388 | + // When using dynamic memory allocation (non-fixed address), we must preserve |
| 389 | + // the mmap return value (X0) which contains the allocated memory address. |
| 390 | + // This value is saved to the stack to ensure registers requiring memory |
| 391 | + // access can retrieve the correct address even if X0 is modified by |
| 392 | + // intermediate code. |
| 393 | + generateRegisterStackPush(AArch64::X0, MemoryInitialSetupCode); |
| 394 | + // FIXME: Ensure stack pointer remains stable to prevent loss of saved address |
| 395 | + return MemoryInitialSetupCode; |
| 396 | +} |
| 397 | + |
| 398 | +std::vector<MCInst> ExegesisAArch64Target::setStackRegisterToAuxMem() const { |
| 399 | + std::vector<MCInst> instructions; // NOP |
| 400 | + // TODO: Implement this, Found no need for this in AArch64. |
| 401 | + return instructions; |
| 402 | +} |
| 403 | + |
| 404 | +uintptr_t ExegesisAArch64Target::getAuxiliaryMemoryStartAddress() const { |
| 405 | + if (!UseFixedAddress) |
| 406 | + // Allow kernel to select an appropriate memory address |
| 407 | + return 0; |
| 408 | + // Return the second to last page in the virtual address space |
| 409 | + // to try and prevent interference with memory annotations in the snippet |
| 410 | + // VAddressSpaceCeiling = 0x0000800000000000 |
| 411 | + // FIXME: Why 2 pages? |
| 412 | + return VAddressSpaceCeiling - (2 * getpagesize()); |
| 413 | +} |
| 414 | + |
| 415 | +std::vector<MCInst> |
| 416 | +ExegesisAArch64Target::configurePerfCounter(long Request, |
| 417 | + bool SaveRegisters) const { |
| 418 | + std::vector<MCInst> ConfigurePerfCounterCode; // NOP |
| 419 | + // FIXME: SYSCALL exits with EBADF error - file descriptor is invalid |
| 420 | + // No file is opened previosly to add as file descriptor |
| 421 | + return ConfigurePerfCounterCode; |
| 422 | +} |
| 423 | + |
| 424 | +std::vector<MCRegister> ExegesisAArch64Target::getArgumentRegisters() const { |
| 425 | + return {AArch64::X0, AArch64::X1}; |
| 426 | +} |
| 427 | + |
| 428 | +std::vector<MCRegister> ExegesisAArch64Target::getRegistersNeedSaving() const { |
| 429 | + return { |
| 430 | + AArch64::X0, |
| 431 | + AArch64::X1, |
| 432 | + AArch64::X2, |
| 433 | + AArch64::X3, |
| 434 | + AArch64::X4, |
| 435 | + AArch64::X5, |
| 436 | + AArch64::X8, |
| 437 | + ArgumentRegisters::TempRegister, |
| 438 | + ArgumentRegisters::CodeSize, |
| 439 | + ArgumentRegisters::AuxiliaryMemoryFD, |
| 440 | + }; |
| 441 | +} |
| 442 | + |
| 443 | +#endif // __linux__ |
| 444 | + |
240 | 445 | static ExegesisTarget *getTheExegesisAArch64Target() { |
241 | 446 | static ExegesisAArch64Target Target; |
242 | 447 | return &Target; |
|
0 commit comments