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-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 // C++ includes
10 #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/Triple.h"
22 #include "llvm/ADT/Twine.h"
23
GetPtr(char * cptr,uint64_t & ptr,lldb::SBFrame & frame,lldb::SBCommandReturnObject & result)24 static 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
42 enum {
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
PrintBTEntry(lldb::addr_t lbound,lldb::addr_t ubound,uint64_t value,uint64_t meta,lldb::SBCommandReturnObject & result)60 static 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 || one_cmpl32) && ubound == 0) {
67 result.Printf("Null bounds on map: pointer value = 0x%lx\n", value);
68 } else {
69 result.Printf(" lbound = 0x%lx,", lbound);
70 result.Printf(" ubound = 0x%lx", ubound);
71 result.Printf(" (pointer value = 0x%lx,", value);
72 result.Printf(" metadata = 0x%lx)\n", meta);
73 }
74 }
75
GetBTEntryAddr(uint64_t bndcfgu,uint64_t ptr,lldb::SBTarget & target,llvm::Triple::ArchType arch,size_t & size,lldb::addr_t & bt_entry_addr,lldb::SBCommandReturnObject & result,lldb::SBError & error)76 static 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
GetBTEntry(uint64_t bndcfgu,uint64_t ptr,lldb::SBTarget & target,llvm::Triple::ArchType arch,lldb::SBCommandReturnObject & result,lldb::SBError & error)152 static 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 (lower
163 // boundary,
164 // upper boundary, pointer value and meta data), which all have the same size
165 // '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
uIntToU8(uint64_t input,size_t size)206 static 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
SetBTEntry(uint64_t bndcfgu,uint64_t ptr,lldb::addr_t lbound,lldb::addr_t ubound,lldb::SBTarget & target,llvm::Triple::ArchType arch,lldb::SBCommandReturnObject & result,lldb::SBError & error)215 static 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, the
228 // 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
GetInitInfo(lldb::SBDebugger debugger,lldb::SBTarget & target,llvm::Triple::ArchType & arch,uint64_t & bndcfgu,char * arg,uint64_t & ptr,lldb::SBCommandReturnObject & result,lldb::SBError & error)250 static 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
303 class MPXTableShow : public lldb::SBCommandPluginInterface {
304 public:
DoExecute(lldb::SBDebugger debugger,char ** command,lldb::SBCommandReturnObject & result)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
348 class MPXTableSet : public lldb::SBCommandPluginInterface {
349 public:
DoExecute(lldb::SBDebugger debugger,char ** command,lldb::SBCommandReturnObject & result)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
MPXPluginInitialize(lldb::SBDebugger & debugger)408 bool 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