CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
223 lines
8.1 KiB
C++
223 lines
8.1 KiB
C++
//===-- xray_hexagon.cpp --------------------------------------*- C++ ---*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file is a part of XRay, a dynamic runtime instrumentation system.
|
|
//
|
|
// Implementation of hexagon-specific routines (32-bit).
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "sanitizer_common/sanitizer_common.h"
|
|
#include "xray_defs.h"
|
|
#include "xray_interface_internal.h"
|
|
#include <assert.h>
|
|
#include <atomic>
|
|
|
|
namespace __xray {
|
|
|
|
// The machine codes for some instructions used in runtime patching.
|
|
//
|
|
// J2_jump encoding: bits [27:16] and [13:1] hold the PC-relative byte offset
|
|
// divided by 4, with bits [15:14] = 0b11 for packet-end parse bits.
|
|
// Formula: 0x5800c000 | ((ByteOffset >> 2) << 1)
|
|
enum PatchOpcodes : uint32_t {
|
|
PO_JUMPI_1C = 0x5800c00e, // jump #0x01c (entry/exit sled, 28 bytes)
|
|
PO_JUMPI_30 = 0x5800c018, // jump #0x030 (custom event sled, 48 bytes)
|
|
PO_JUMPI_3C = 0x5800c01e, // jump #0x03c (typed event sled, 60 bytes)
|
|
PO_NOP = 0x7f00c000, // { nop } with packet-end parse bits
|
|
PO_CALLR_R6 = 0x50a6c000, // indirect call: callr r6
|
|
PO_TFR_IMM = 0x78000000, // transfer immed
|
|
// ICLASS 0x7 - S2-type A-type
|
|
PO_IMMEXT = 0x00000000, // constant extender
|
|
PO_ALLOCFRAME_0 = 0xa09dc000, // allocframe(#0)
|
|
PO_DEALLOCFRAME = 0x901ec01e, // deallocframe
|
|
};
|
|
|
|
enum PacketWordParseBits : uint32_t {
|
|
PP_DUPLEX = 0x00 << 14,
|
|
PP_NOT_END = 0x01 << 14,
|
|
PP_PACKET_END = 0x03 << 14,
|
|
};
|
|
|
|
enum RegNum : uint32_t {
|
|
RN_R6 = 0x6,
|
|
RN_R7 = 0x7,
|
|
};
|
|
|
|
inline static uint32_t
|
|
encodeExtendedTransferImmediate(uint32_t Imm, RegNum DestReg,
|
|
bool PacketEnd = false) XRAY_NEVER_INSTRUMENT {
|
|
static const uint32_t REG_MASK = 0x1f;
|
|
assert((DestReg & (~REG_MASK)) == 0);
|
|
// The constant-extended register transfer encodes the 6 least
|
|
// significant bits of the effective constant:
|
|
Imm = Imm & 0x03f;
|
|
const PacketWordParseBits ParseBits = PacketEnd ? PP_PACKET_END : PP_NOT_END;
|
|
|
|
return PO_TFR_IMM | ParseBits | (Imm << 5) | (DestReg & REG_MASK);
|
|
}
|
|
|
|
inline static uint32_t
|
|
encodeConstantExtender(uint32_t Imm) XRAY_NEVER_INSTRUMENT {
|
|
// Bits Name Description
|
|
// ----- ------- ------------------------------------------
|
|
// 31:28 ICLASS Instruction class = 0000
|
|
// 27:16 high High 12 bits of 26-bit constant extension
|
|
// 15:14 Parse Parse bits
|
|
// 13:0 low Low 14 bits of 26-bit constant extension
|
|
static const uint32_t IMM_MASK_LOW = 0x03fff;
|
|
static const uint32_t IMM_MASK_HIGH = 0x00fff << 14;
|
|
|
|
// The extender encodes the 26 most significant bits of the effective
|
|
// constant:
|
|
Imm = Imm >> 6;
|
|
|
|
const uint32_t high = (Imm & IMM_MASK_HIGH) << 16;
|
|
const uint32_t low = Imm & IMM_MASK_LOW;
|
|
|
|
return PO_IMMEXT | high | PP_NOT_END | low;
|
|
}
|
|
|
|
static void WriteInstFlushCache(void *Addr, uint32_t NewInstruction) {
|
|
asm volatile("icinva(%[inst_addr])\n\t"
|
|
"isync\n\t"
|
|
"memw(%[inst_addr]) = %[new_inst]\n\t"
|
|
"dccleaninva(%[inst_addr])\n\t"
|
|
"syncht\n\t"
|
|
:
|
|
: [ inst_addr ] "r"(Addr), [ new_inst ] "r"(NewInstruction)
|
|
: "memory");
|
|
}
|
|
|
|
inline static bool patchSled(const bool Enable, const uint32_t FuncId,
|
|
const XRaySledEntry &Sled,
|
|
void (*TracingHook)()) XRAY_NEVER_INSTRUMENT {
|
|
// When |Enable| == true,
|
|
// We replace the following compile-time stub (sled):
|
|
//
|
|
// .L_xray_sled_N:
|
|
// <xray_sled_base>:
|
|
// { jump .Ltmp0 }
|
|
// { nop } x 6
|
|
// .Ltmp0:
|
|
//
|
|
// With the following runtime patch:
|
|
//
|
|
// <xray_sled_n>:
|
|
// { allocframe(#0) }
|
|
// { immext(#...) // upper 26-bits of func id
|
|
// r7 = ##... // lower 6-bits of func id
|
|
// immext(#...) // upper 26-bits of trampoline
|
|
// r6 = ##... } // lower 6-bits of trampoline
|
|
// { callr r6 }
|
|
// { deallocframe }
|
|
//
|
|
// allocframe(#0) saves the caller's r31:30 (LR:FP) before the callr
|
|
// clobbers r31, and deallocframe restores them afterward. This ensures
|
|
// the instrumented function's allocframe later saves the correct return
|
|
// address.
|
|
//
|
|
// Replacement of the first 4-byte instruction should be the last and
|
|
// atomic operation, so that user code reaching the sled concurrently
|
|
// either jumps over the whole sled, or executes the whole sled when it
|
|
// is ready.
|
|
//
|
|
// When |Enable|==false, we set back the first instruction in the sled to be
|
|
// { jump .Ltmp0 }
|
|
|
|
uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address());
|
|
if (Enable) {
|
|
uint32_t *CurAddress = FirstAddress + 1;
|
|
// Word 1: immext for r7 = FuncId
|
|
*CurAddress = encodeConstantExtender(FuncId);
|
|
CurAddress++;
|
|
// Word 2: r7 = ##FuncId (low 6 bits)
|
|
*CurAddress = encodeExtendedTransferImmediate(FuncId, RN_R7);
|
|
CurAddress++;
|
|
// Word 3: immext for r6 = TracingHook
|
|
*CurAddress =
|
|
encodeConstantExtender(reinterpret_cast<uint32_t>(TracingHook));
|
|
CurAddress++;
|
|
// Word 4: r6 = ##TracingHook (low 6 bits), packet end
|
|
*CurAddress = encodeExtendedTransferImmediate(
|
|
reinterpret_cast<uint32_t>(TracingHook), RN_R6, true);
|
|
CurAddress++;
|
|
// Word 5: callr r6
|
|
*CurAddress = uint32_t(PO_CALLR_R6);
|
|
CurAddress++;
|
|
// Word 6: deallocframe
|
|
*CurAddress = uint32_t(PO_DEALLOCFRAME);
|
|
|
|
// Word 0 (written last, atomically): allocframe(#0) replaces jump
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_ALLOCFRAME_0));
|
|
} else {
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_JUMPI_1C));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool patchFunctionEntry(const bool Enable, const uint32_t FuncId,
|
|
const XRaySledEntry &Sled,
|
|
const XRayTrampolines &Trampolines,
|
|
bool LogArgs) XRAY_NEVER_INSTRUMENT {
|
|
auto Trampoline =
|
|
LogArgs ? Trampolines.LogArgsTrampoline : Trampolines.EntryTrampoline;
|
|
return patchSled(Enable, FuncId, Sled, Trampoline);
|
|
}
|
|
|
|
bool patchFunctionExit(
|
|
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
|
|
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
|
|
return patchSled(Enable, FuncId, Sled, Trampolines.ExitTrampoline);
|
|
}
|
|
|
|
bool patchFunctionTailExit(
|
|
const bool Enable, const uint32_t FuncId, const XRaySledEntry &Sled,
|
|
const XRayTrampolines &Trampolines) XRAY_NEVER_INSTRUMENT {
|
|
return patchSled(Enable, FuncId, Sled, Trampolines.TailExitTrampoline);
|
|
}
|
|
|
|
bool patchCustomEvent(const bool Enable, const uint32_t FuncId,
|
|
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
|
|
// The custom event sled (2 args) is 12 words = 48 bytes:
|
|
// .Lxray_sled_N:
|
|
// { jump .Lend } <-- first word: jump over (disabled) / nop (enabled)
|
|
// allocframe, sp adjust, 2 saves, 2 moves, call, 2 restores,
|
|
// sp adjust, deallocframe
|
|
// .Lend:
|
|
uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address());
|
|
if (Enable) {
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_NOP));
|
|
} else {
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_JUMPI_30));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool patchTypedEvent(const bool Enable, const uint32_t FuncId,
|
|
const XRaySledEntry &Sled) XRAY_NEVER_INSTRUMENT {
|
|
// The typed event sled (3 args) is 15 words = 60 bytes:
|
|
// .Lxray_sled_N:
|
|
// { jump .Lend } <-- first word: jump over (disabled) / nop (enabled)
|
|
// allocframe, sp adjust, 3 saves, 3 moves, call, 3 restores,
|
|
// sp adjust, deallocframe
|
|
// .Lend:
|
|
uint32_t *FirstAddress = reinterpret_cast<uint32_t *>(Sled.address());
|
|
if (Enable) {
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_NOP));
|
|
} else {
|
|
WriteInstFlushCache(FirstAddress, uint32_t(PO_JUMPI_3C));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
} // namespace __xray
|
|
|
|
extern "C" void __xray_ArgLoggerEntry() XRAY_NEVER_INSTRUMENT {
|
|
// FIXME: this will have to be implemented in the trampoline assembly file
|
|
}
|