424 lines · cpp
1//===-- cli-wrapper-mpxtable.cpp --------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// C++ includes10#include <cerrno>11#include <string>12 13#include "cli-wrapper-mpxtable.h"14#include "lldb/API/SBCommandInterpreter.h"15#include "lldb/API/SBCommandReturnObject.h"16#include "lldb/API/SBMemoryRegionInfo.h"17#include "lldb/API/SBProcess.h"18#include "lldb/API/SBTarget.h"19#include "lldb/API/SBThread.h"20 21#include "llvm/ADT/Twine.h"22#include "llvm/TargetParser/Triple.h"23 24static bool GetPtr(char *cptr, uint64_t &ptr, lldb::SBFrame &frame,25 lldb::SBCommandReturnObject &result) {26 if (!cptr) {27 result.SetError("Bad argument.");28 result.SetStatus(lldb::eReturnStatusFailed);29 return false;30 }31 32 lldb::SBValue ptr_addr = frame.GetValueForVariablePath(cptr);33 if (!ptr_addr.IsValid()) {34 result.SetError("Invalid pointer.");35 result.SetStatus(lldb::eReturnStatusFailed);36 return false;37 }38 ptr = ptr_addr.GetLoadAddress();39 return true;40}41 42enum {43 mpx_base_mask_64 = ~(uint64_t)0xFFFULL,44 mpx_bd_mask_64 = 0xFFFFFFF00000ULL,45 bd_r_shift_64 = 20,46 bd_l_shift_64 = 3,47 bt_r_shift_64 = 3,48 bt_l_shift_64 = 5,49 bt_mask_64 = 0x0000000FFFF8ULL,50 51 mpx_base_mask_32 = 0xFFFFFFFFFFFFF000ULL,52 mpx_bd_mask_32 = 0xFFFFF000ULL,53 bd_r_shift_32 = 12,54 bd_l_shift_32 = 2,55 bt_r_shift_32 = 2,56 bt_l_shift_32 = 4,57 bt_mask_32 = 0x00000FFCULL,58};59 60static void PrintBTEntry(lldb::addr_t lbound, lldb::addr_t ubound,61 uint64_t value, uint64_t meta,62 lldb::SBCommandReturnObject &result) {63 const lldb::addr_t one_cmpl64 = ~((lldb::addr_t)0);64 const lldb::addr_t one_cmpl32 = ~((uint32_t)0);65 66 if ((lbound == one_cmpl64 || lbound == one_cmpl32) && ubound == 0) {67 result.Printf("Null bounds on map: pointer value = 0x%" PRIu64 "\n", value);68 } else {69 result.Printf(" lbound = 0x%" PRIu64 ",", lbound);70 result.Printf(" ubound = 0x%" PRIu64 , ubound);71 result.Printf(" (pointer value = 0x%" PRIu64 ",", value);72 result.Printf(" metadata = 0x%" PRIu64 ")\n", meta);73 }74}75 76static bool GetBTEntryAddr(uint64_t bndcfgu, uint64_t ptr,77 lldb::SBTarget &target, llvm::Triple::ArchType arch,78 size_t &size, lldb::addr_t &bt_entry_addr,79 lldb::SBCommandReturnObject &result,80 lldb::SBError &error) {81 lldb::addr_t mpx_base_mask;82 lldb::addr_t mpx_bd_mask;83 lldb::addr_t bd_r_shift;84 lldb::addr_t bd_l_shift;85 lldb::addr_t bt_r_shift;86 lldb::addr_t bt_l_shift;87 lldb::addr_t bt_mask;88 89 if (arch == llvm::Triple::ArchType::x86_64) {90 mpx_base_mask = mpx_base_mask_64;91 mpx_bd_mask = mpx_bd_mask_64;92 bd_r_shift = bd_r_shift_64;93 bd_l_shift = bd_l_shift_64;94 bt_r_shift = bt_r_shift_64;95 bt_l_shift = bt_l_shift_64;96 bt_mask = bt_mask_64;97 } else if (arch == llvm::Triple::ArchType::x86) {98 mpx_base_mask = mpx_base_mask_32;99 mpx_bd_mask = mpx_bd_mask_32;100 bd_r_shift = bd_r_shift_32;101 bd_l_shift = bd_l_shift_32;102 bt_r_shift = bt_r_shift_32;103 bt_l_shift = bt_l_shift_32;104 bt_mask = bt_mask_32;105 } else {106 result.SetError("Invalid arch.");107 result.SetStatus(lldb::eReturnStatusFailed);108 return false;109 }110 111 size = target.GetAddressByteSize();112 lldb::addr_t mpx_bd_base = bndcfgu & mpx_base_mask;113 lldb::addr_t bd_entry_offset = ((ptr & mpx_bd_mask) >> bd_r_shift)114 << bd_l_shift;115 lldb::addr_t bd_entry_addr = mpx_bd_base + bd_entry_offset;116 117 std::vector<uint8_t> bd_entry_v(size);118 size_t ret = target.GetProcess().ReadMemory(119 bd_entry_addr, static_cast<void *>(bd_entry_v.data()), size, error);120 if (ret != size || !error.Success()) {121 result.SetError("Failed access to BD entry.");122 return false;123 }124 125 lldb::SBData data;126 data.SetData(error, bd_entry_v.data(), bd_entry_v.size(),127 target.GetByteOrder(), size);128 lldb::addr_t bd_entry = data.GetAddress(error, 0);129 130 if (!error.Success()) {131 result.SetError("Failed access to BD entry.");132 return false;133 }134 135 if ((bd_entry & 0x01) == 0) {136 result.SetError("Invalid bound directory.");137 result.SetStatus(lldb::eReturnStatusFailed);138 return false;139 }140 141 // Clear status bit.142 //143 bd_entry--;144 145 lldb::addr_t bt_addr = bd_entry & ~bt_r_shift;146 lldb::addr_t bt_entry_offset = ((ptr & bt_mask) >> bt_r_shift) << bt_l_shift;147 bt_entry_addr = bt_addr + bt_entry_offset;148 149 return true;150}151 152static bool GetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::SBTarget &target,153 llvm::Triple::ArchType arch,154 lldb::SBCommandReturnObject &result,155 lldb::SBError &error) {156 lldb::addr_t bt_entry_addr;157 size_t size;158 if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,159 error))160 return false;161 162 // bt_entry_v must have space to store the 4 elements of the BT entry (lower163 // boundary,164 // upper boundary, pointer value and meta data), which all have the same size165 // 'size'.166 //167 std::vector<uint8_t> bt_entry_v(size * 4);168 size_t ret = target.GetProcess().ReadMemory(169 bt_entry_addr, static_cast<void *>(bt_entry_v.data()), size * 4, error);170 171 if ((ret != (size * 4)) || !error.Success()) {172 result.SetError("Unsuccessful. Failed access to BT entry.");173 result.SetStatus(lldb::eReturnStatusFailed);174 return false;175 }176 177 lldb::addr_t lbound;178 lldb::addr_t ubound;179 uint64_t value;180 uint64_t meta;181 lldb::SBData data;182 data.SetData(error, bt_entry_v.data(), bt_entry_v.size(),183 target.GetByteOrder(), size);184 lbound = data.GetAddress(error, size * 0);185 ubound = data.GetAddress(error, size * 1);186 value = data.GetAddress(error, size * 2);187 meta = data.GetAddress(error, size * 3);188 // ubound is stored as one's complement.189 if (arch == llvm::Triple::ArchType::x86) {190 ubound = (~ubound) & 0x00000000FFFFFFFF;191 } else {192 ubound = ~ubound;193 }194 195 if (!error.Success()) {196 result.SetError("Failed access to BT entry.");197 return false;198 }199 200 PrintBTEntry(lbound, ubound, value, meta, result);201 202 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);203 return true;204}205 206static std::vector<uint8_t> uIntToU8(uint64_t input, size_t size) {207 std::vector<uint8_t> output;208 for (size_t i = 0; i < size; i++)209 output.push_back(210 static_cast<uint8_t>((input & (0xFFULL << (i * 8))) >> (i * 8)));211 212 return output;213}214 215static bool SetBTEntry(uint64_t bndcfgu, uint64_t ptr, lldb::addr_t lbound,216 lldb::addr_t ubound, lldb::SBTarget &target,217 llvm::Triple::ArchType arch,218 lldb::SBCommandReturnObject &result,219 lldb::SBError &error) {220 lldb::addr_t bt_entry_addr;221 size_t size;222 223 if (!GetBTEntryAddr(bndcfgu, ptr, target, arch, size, bt_entry_addr, result,224 error))225 return false;226 227 // bt_entry_v must have space to store only 2 elements of the BT Entry, the228 // lower boundary and the upper boundary, which both have size 'size'.229 //230 std::vector<uint8_t> bt_entry_v(size * 2);231 232 std::vector<uint8_t> lbound_v = uIntToU8(lbound, size);233 bt_entry_v.insert(bt_entry_v.begin(), lbound_v.begin(), lbound_v.end());234 std::vector<uint8_t> ubound_v = uIntToU8(~ubound, size);235 bt_entry_v.insert(bt_entry_v.begin() + size, ubound_v.begin(),236 ubound_v.end());237 238 size_t ret = target.GetProcess().WriteMemory(239 bt_entry_addr, (void *)(bt_entry_v.data()), size * 2, error);240 if ((ret != (size * 2)) || !error.Success()) {241 result.SetError("Failed access to BT entry.");242 result.SetStatus(lldb::eReturnStatusFailed);243 return false;244 }245 246 result.SetStatus(lldb::eReturnStatusSuccessFinishResult);247 return true;248}249 250static bool GetInitInfo(lldb::SBDebugger debugger, lldb::SBTarget &target,251 llvm::Triple::ArchType &arch, uint64_t &bndcfgu,252 char *arg, uint64_t &ptr,253 lldb::SBCommandReturnObject &result,254 lldb::SBError &error) {255 target = debugger.GetSelectedTarget();256 if (!target.IsValid()) {257 result.SetError("Invalid target.");258 result.SetStatus(lldb::eReturnStatusFailed);259 return false;260 }261 262 const std::string triple_s(target.GetTriple());263 const llvm::Triple triple(triple_s);264 265 arch = triple.getArch();266 267 if ((arch != llvm::Triple::ArchType::x86) &&268 (arch != llvm::Triple::ArchType::x86_64)) {269 result.SetError("Platform not supported.");270 result.SetStatus(lldb::eReturnStatusFailed);271 return false;272 }273 274 lldb::SBFrame frame =275 target.GetProcess().GetSelectedThread().GetSelectedFrame();276 if (!frame.IsValid()) {277 result.SetError("No valid process, thread or frame.");278 result.SetStatus(lldb::eReturnStatusFailed);279 return false;280 }281 282 lldb::SBValue bndcfgu_val = frame.FindRegister("bndcfgu");283 if (!bndcfgu_val.IsValid()) {284 result.SetError("Cannot access register BNDCFGU. Does the target support "285 "Intel(R) Memory Protection Extensions (Intel(R) MPX)?");286 result.SetStatus(lldb::eReturnStatusFailed);287 return false;288 }289 290 lldb::SBData bndcfgu_data = bndcfgu_val.GetData();291 bndcfgu = bndcfgu_data.GetUnsignedInt64(error, 0);292 if (!error.Success()) {293 result.SetError(error, "Invalid read of register BNDCFGU.");294 return false;295 }296 297 if (!GetPtr(arg, ptr, frame, result))298 return false;299 300 return true;301}302 303class MPXTableShow : public lldb::SBCommandPluginInterface {304public:305 bool DoExecute(lldb::SBDebugger debugger, char **command,306 lldb::SBCommandReturnObject &result) override {307 308 if (command) {309 int arg_c = 0;310 char *arg;311 312 while (*command) {313 if (arg_c >= 1) {314 result.SetError("Too many arguments. See help.");315 result.SetStatus(lldb::eReturnStatusFailed);316 return false;317 }318 arg_c++;319 arg = *command;320 command++;321 }322 323 if (!debugger.IsValid()) {324 result.SetError("Invalid debugger.");325 result.SetStatus(lldb::eReturnStatusFailed);326 return false;327 }328 329 lldb::SBTarget target;330 llvm::Triple::ArchType arch;331 lldb::SBError error;332 uint64_t bndcfgu;333 uint64_t ptr;334 335 if (!GetInitInfo(debugger, target, arch, bndcfgu, arg, ptr, result,336 error))337 return false;338 339 return GetBTEntry(bndcfgu, ptr, target, arch, result, error);340 }341 342 result.SetError("Too few arguments. See help.");343 result.SetStatus(lldb::eReturnStatusFailed);344 return false;345 }346};347 348class MPXTableSet : public lldb::SBCommandPluginInterface {349public:350 bool DoExecute(lldb::SBDebugger debugger, char **command,351 lldb::SBCommandReturnObject &result) override {352 353 if (command) {354 int arg_c = 0;355 char *arg[3];356 357 while (*command) {358 arg[arg_c] = *command;359 command++;360 arg_c++;361 }362 363 if (arg_c != 3) {364 result.SetError("Wrong arguments. See help.");365 return false;366 }367 368 if (!debugger.IsValid()) {369 result.SetError("Invalid debugger.");370 return false;371 }372 373 lldb::SBTarget target;374 llvm::Triple::ArchType arch;375 lldb::SBError error;376 uint64_t bndcfgu;377 uint64_t ptr;378 379 if (!GetInitInfo(debugger, target, arch, bndcfgu, arg[0], ptr, result,380 error))381 return false;382 383 char *endptr;384 errno = 0;385 uint64_t lbound = std::strtoul(arg[1], &endptr, 16);386 if (endptr == arg[1] || errno == ERANGE) {387 result.SetError("Lower Bound: bad argument format.");388 errno = 0;389 return false;390 }391 392 uint64_t ubound = std::strtoul(arg[2], &endptr, 16);393 if (endptr == arg[1] || errno == ERANGE) {394 result.SetError("Upper Bound: bad argument format.");395 errno = 0;396 return false;397 }398 399 return SetBTEntry(bndcfgu, ptr, lbound, ubound, target, arch, result,400 error);401 }402 403 result.SetError("Too few arguments. See help.");404 return false;405 }406};407 408bool MPXPluginInitialize(lldb::SBDebugger &debugger) {409 lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter();410 lldb::SBCommand mpxTable = interpreter.AddMultiwordCommand(411 "mpx-table", "A utility to access the Intel(R) MPX table entries.");412 413 const char *mpx_show_help = "Show the Intel(R) MPX table entry of a pointer."414 "\nmpx-table show <pointer>";415 mpxTable.AddCommand("show", new MPXTableShow(), mpx_show_help);416 417 const char *mpx_set_help =418 "Set the Intel(R) MPX table entry of a pointer.\n"419 "mpx-table set <pointer> <lower bound> <upper bound>";420 mpxTable.AddCommand("set", new MPXTableSet(), mpx_set_help);421 422 return true;423}424