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