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