LLDB  mainline
NSString.cpp
Go to the documentation of this file.
1 //===-- NSString.cpp ------------------------------------------------------===//
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 #include "NSString.h"
10 
11 #include "lldb/Core/ValueObject.h"
15 #include "lldb/Target/Language.h"
16 #include "lldb/Target/Target.h"
19 #include "lldb/Utility/Endian.h"
20 #include "lldb/Utility/Status.h"
21 #include "lldb/Utility/Stream.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 using namespace lldb_private::formatters;
26 
27 std::map<ConstString, CXXFunctionSummaryFormat::Callback> &
28 NSString_Additionals::GetAdditionalSummaries() {
29  static std::map<ConstString, CXXFunctionSummaryFormat::Callback> g_map;
30  return g_map;
31 }
32 
34  ValueObject &valobj, Stream &stream,
35  const TypeSummaryOptions &summary_options) {
36  static ConstString g_TypeHint("NSString");
37 
38  ProcessSP process_sp = valobj.GetProcessSP();
39  if (!process_sp)
40  return false;
41 
42  ObjCLanguageRuntime *runtime = ObjCLanguageRuntime::Get(*process_sp);
43 
44  if (!runtime)
45  return false;
46 
48  runtime->GetClassDescriptor(valobj));
49 
50  if (!descriptor.get() || !descriptor->IsValid())
51  return false;
52 
53  uint32_t ptr_size = process_sp->GetAddressByteSize();
54 
55  lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0);
56 
57  if (!valobj_addr)
58  return false;
59 
60  ConstString class_name_cs = descriptor->GetClassName();
61  llvm::StringRef class_name = class_name_cs.GetStringRef();
62 
63  if (class_name.empty())
64  return false;
65 
66  bool is_tagged_ptr = class_name == "NSTaggedPointerString" &&
67  descriptor->GetTaggedPointerInfo();
68  // for a tagged pointer, the descriptor has everything we need
69  if (is_tagged_ptr)
70  return NSTaggedString_SummaryProvider(valobj, descriptor, stream,
71  summary_options);
72 
73  auto &additionals_map(NSString_Additionals::GetAdditionalSummaries());
74  auto iter = additionals_map.find(class_name_cs), end = additionals_map.end();
75  if (iter != end)
76  return iter->second(valobj, stream, summary_options);
77 
78  // if not a tagged pointer that we know about, try the normal route
79  uint64_t info_bits_location = valobj_addr + ptr_size;
80  if (process_sp->GetByteOrder() != lldb::eByteOrderLittle)
81  info_bits_location += 3;
82 
83  Status error;
84 
85  uint8_t info_bits = process_sp->ReadUnsignedIntegerFromMemory(
86  info_bits_location, 1, 0, error);
87  if (error.Fail())
88  return false;
89 
90  bool is_mutable = (info_bits & 1) == 1;
91  bool is_inline = (info_bits & 0x60) == 0;
92  bool has_explicit_length = (info_bits & (1 | 4)) != 4;
93  bool is_unicode = (info_bits & 0x10) == 0x10;
94  bool is_path_store = class_name == "NSPathStore2";
95  bool has_null = (info_bits & 8) == 8;
96 
97  size_t explicit_length = 0;
98  if (!has_null && has_explicit_length && !is_path_store) {
99  lldb::addr_t explicit_length_offset = 2 * ptr_size;
100  if (is_mutable && !is_inline)
101  explicit_length_offset =
102  explicit_length_offset + ptr_size; // notInlineMutable.length;
103  else if (is_inline)
104  explicit_length = explicit_length + 0; // inline1.length;
105  else if (!is_inline && !is_mutable)
106  explicit_length_offset =
107  explicit_length_offset + ptr_size; // notInlineImmutable1.length;
108  else
109  explicit_length_offset = 0;
110 
111  if (explicit_length_offset) {
112  explicit_length_offset = valobj_addr + explicit_length_offset;
113  explicit_length = process_sp->ReadUnsignedIntegerFromMemory(
114  explicit_length_offset, 4, 0, error);
115  }
116  }
117 
118  const llvm::StringSet<> supported_string_classes = {
119  "NSString", "CFMutableStringRef",
120  "CFStringRef", "__NSCFConstantString",
121  "__NSCFString", "NSCFConstantString",
122  "NSCFString", "NSPathStore2"};
123  if (supported_string_classes.count(class_name) == 0) {
124  // not one of us - but tell me class name
125  stream.Printf("class name = %s", class_name_cs.GetCString());
126  return true;
127  }
128 
129  std::string prefix, suffix;
130  if (Language *language =
131  Language::FindPlugin(summary_options.GetLanguage())) {
132  if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
133  suffix)) {
134  prefix.clear();
135  suffix.clear();
136  }
137  }
138 
140  options.SetPrefixToken(prefix);
141  options.SetSuffixToken(suffix);
142 
143  if (is_mutable) {
144  uint64_t location = 2 * ptr_size + valobj_addr;
145  location = process_sp->ReadPointerFromMemory(location, error);
146  if (error.Fail())
147  return false;
148  if (has_explicit_length && is_unicode) {
149  options.SetLocation(location);
150  options.SetTargetSP(valobj.GetTargetSP());
151  options.SetStream(&stream);
152  options.SetQuote('"');
153  options.SetSourceSize(explicit_length);
154  options.SetHasSourceSize(has_explicit_length);
155  options.SetNeedsZeroTermination(false);
156  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
158  options.SetBinaryZeroIsTerminator(false);
159  return StringPrinter::ReadStringAndDumpToStream<
160  StringPrinter::StringElementType::UTF16>(options);
161  } else {
162  options.SetLocation(location + 1);
163  options.SetTargetSP(valobj.GetTargetSP());
164  options.SetStream(&stream);
165  options.SetSourceSize(explicit_length);
166  options.SetHasSourceSize(has_explicit_length);
167  options.SetNeedsZeroTermination(false);
168  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
170  options.SetBinaryZeroIsTerminator(false);
171  return StringPrinter::ReadStringAndDumpToStream<
172  StringPrinter::StringElementType::ASCII>(options);
173  }
174  } else if (is_inline && has_explicit_length && !is_unicode &&
175  !is_path_store && !is_mutable) {
176  uint64_t location = 3 * ptr_size + valobj_addr;
177 
178  options.SetLocation(location);
179  options.SetTargetSP(valobj.GetTargetSP());
180  options.SetStream(&stream);
181  options.SetQuote('"');
182  options.SetSourceSize(explicit_length);
183  options.SetHasSourceSize(has_explicit_length);
184  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
186  return StringPrinter::ReadStringAndDumpToStream<
187  StringPrinter::StringElementType::ASCII>(options);
188  } else if (is_unicode) {
189  uint64_t location = valobj_addr + 2 * ptr_size;
190  if (is_inline) {
191  if (!has_explicit_length) {
192  return false;
193  } else
194  location += ptr_size;
195  } else {
196  location = process_sp->ReadPointerFromMemory(location, error);
197  if (error.Fail())
198  return false;
199  }
200  options.SetLocation(location);
201  options.SetTargetSP(valobj.GetTargetSP());
202  options.SetStream(&stream);
203  options.SetQuote('"');
204  options.SetSourceSize(explicit_length);
205  options.SetHasSourceSize(has_explicit_length);
206  options.SetNeedsZeroTermination(!has_explicit_length);
207  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
209  options.SetBinaryZeroIsTerminator(!has_explicit_length);
210  return StringPrinter::ReadStringAndDumpToStream<
211  StringPrinter::StringElementType::UTF16>(options);
212  } else if (is_path_store) {
213  // _lengthAndRefCount is the first ivar of NSPathStore2 (after the isa).
214  uint64_t length_ivar_offset = 1 * ptr_size;
215  CompilerType length_type = valobj.GetCompilerType().GetBasicTypeFromAST(
217  ValueObjectSP length_valobj_sp =
218  valobj.GetSyntheticChildAtOffset(length_ivar_offset, length_type, true,
219  ConstString("_lengthAndRefCount"));
220  if (!length_valobj_sp)
221  return false;
222  // Get the length out of _lengthAndRefCount.
223  explicit_length = length_valobj_sp->GetValueAsUnsigned(0) >> 20;
224  lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4;
225 
226  options.SetLocation(location);
227  options.SetTargetSP(valobj.GetTargetSP());
228  options.SetStream(&stream);
229  options.SetQuote('"');
230  options.SetSourceSize(explicit_length);
231  options.SetHasSourceSize(has_explicit_length);
232  options.SetNeedsZeroTermination(!has_explicit_length);
233  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
235  options.SetBinaryZeroIsTerminator(!has_explicit_length);
236  return StringPrinter::ReadStringAndDumpToStream<
237  StringPrinter::StringElementType::UTF16>(options);
238  } else if (is_inline) {
239  uint64_t location = valobj_addr + 2 * ptr_size;
240  if (!has_explicit_length) {
241  // in this kind of string, the byte before the string content is a length
242  // byte so let's try and use it to handle the embedded NUL case
243  Status error;
244  explicit_length =
245  process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error);
246  has_explicit_length = !(error.Fail() || explicit_length == 0);
247  location++;
248  }
249  options.SetLocation(location);
250  options.SetTargetSP(valobj.GetTargetSP());
251  options.SetStream(&stream);
252  options.SetSourceSize(explicit_length);
253  options.SetHasSourceSize(has_explicit_length);
254  options.SetNeedsZeroTermination(!has_explicit_length);
255  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
257  options.SetBinaryZeroIsTerminator(!has_explicit_length);
258  if (has_explicit_length)
259  return StringPrinter::ReadStringAndDumpToStream<
260  StringPrinter::StringElementType::UTF8>(options);
261  else
262  return StringPrinter::ReadStringAndDumpToStream<
263  StringPrinter::StringElementType::ASCII>(options);
264  } else {
265  uint64_t location = valobj_addr + 2 * ptr_size;
266  location = process_sp->ReadPointerFromMemory(location, error);
267  if (error.Fail())
268  return false;
269  if (has_explicit_length && !has_null)
270  explicit_length++; // account for the fact that there is no NULL and we
271  // need to have one added
272  options.SetLocation(location);
273  options.SetTargetSP(valobj.GetTargetSP());
274  options.SetStream(&stream);
275  options.SetSourceSize(explicit_length);
276  options.SetHasSourceSize(has_explicit_length);
277  options.SetIgnoreMaxLength(summary_options.GetCapping() ==
279  return StringPrinter::ReadStringAndDumpToStream<
280  StringPrinter::StringElementType::ASCII>(options);
281  }
282 }
283 
285  ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
286  TargetSP target_sp(valobj.GetTargetSP());
287  if (!target_sp)
288  return false;
289  uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize();
290  uint64_t pointer_value = valobj.GetValueAsUnsigned(0);
291  if (!pointer_value)
292  return false;
293  pointer_value += addr_size;
294  CompilerType type(valobj.GetCompilerType());
295  ExecutionContext exe_ctx(target_sp, false);
296  ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress(
297  "string_ptr", pointer_value, exe_ctx, type));
298  if (!child_ptr_sp)
299  return false;
300  DataExtractor data;
301  Status error;
302  child_ptr_sp->GetData(data, error);
303  if (error.Fail())
304  return false;
305  ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData(
306  "string_data", data, exe_ctx, type));
307  child_sp->GetValueAsUnsigned(0);
308  if (child_sp)
309  return NSStringSummaryProvider(*child_sp, stream, options);
310  return false;
311 }
312 
314  ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) {
315  return NSAttributedStringSummaryProvider(valobj, stream, options);
316 }
317 
320  Stream &stream, const TypeSummaryOptions &summary_options) {
321  static ConstString g_TypeHint("NSString");
322 
323  if (!descriptor)
324  return false;
325  uint64_t len_bits = 0, data_bits = 0;
326  if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr))
327  return false;
328 
329  static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN
330  static const int g_SixbitMaxLen = 9;
331  static const int g_fiveBitMaxLen = 11;
332 
333  static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013"
334  "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX";
335 
336  if (len_bits > g_fiveBitMaxLen)
337  return false;
338 
339  std::string prefix, suffix;
340  if (Language *language =
341  Language::FindPlugin(summary_options.GetLanguage())) {
342  if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix,
343  suffix)) {
344  prefix.clear();
345  suffix.clear();
346  }
347  }
348 
349  // this is a fairly ugly trick - pretend that the numeric value is actually a
350  // char* this works under a few assumptions: little endian architecture
351  // sizeof(uint64_t) > g_MaxNonBitmaskedLen
352  if (len_bits <= g_MaxNonBitmaskedLen) {
353  stream.Printf("%s", prefix.c_str());
354  stream.Printf("\"%s\"", (const char *)&data_bits);
355  stream.Printf("%s", suffix.c_str());
356  return true;
357  }
358 
359  // if the data is bitmasked, we need to actually process the bytes
360  uint8_t bitmask = 0;
361  uint8_t shift_offset = 0;
362 
363  if (len_bits <= g_SixbitMaxLen) {
364  bitmask = 0x03f;
365  shift_offset = 6;
366  } else {
367  bitmask = 0x01f;
368  shift_offset = 5;
369  }
370 
371  std::vector<uint8_t> bytes;
372  bytes.resize(len_bits);
373  for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) {
374  uint8_t packed = data_bits & bitmask;
375  bytes.insert(bytes.begin(), sixBitToCharLookup[packed]);
376  }
377 
378  stream.Printf("%s", prefix.c_str());
379  stream.Printf("\"%s\"", &bytes[0]);
380  stream.Printf("%s", suffix.c_str());
381  return true;
382 }
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetPrefixToken
void SetPrefixToken(const std::string &p)
Definition: StringPrinter.h:37
lldb_private::ValueObject::GetValueAsUnsigned
virtual uint64_t GetValueAsUnsigned(uint64_t fail_value, bool *success=nullptr)
Definition: ValueObject.cpp:1090
lldb_private::ExecutionContext
Definition: ExecutionContext.h:292
lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::SetLocation
void SetLocation(Address l)
Definition: StringPrinter.h:108
lldb_private::ObjCLanguageRuntime
Definition: ObjCLanguageRuntime.h:35
lldb_private::ObjCLanguageRuntime::ClassDescriptorSP
std::shared_ptr< ClassDescriptor > ClassDescriptorSP
Definition: ObjCLanguageRuntime.h:45
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetNeedsZeroTermination
void SetNeedsZeroTermination(bool z)
Definition: StringPrinter.h:57
lldb_private::Stream
Definition: Stream.h:28
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetSourceSize
void SetSourceSize(uint32_t s)
Definition: StringPrinter.h:53
lldb::addr_t
uint64_t addr_t
Definition: lldb-types.h:83
Language.h
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetSuffixToken
void SetSuffixToken(const std::string &p)
Definition: StringPrinter.h:43
lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::SetTargetSP
void SetTargetSP(lldb::TargetSP t)
Definition: StringPrinter.h:112
lldb_private::formatters::NSStringSummaryProvider
bool NSStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition: NSString.cpp:33
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetBinaryZeroIsTerminator
void SetBinaryZeroIsTerminator(bool e)
Definition: StringPrinter.h:61
Target.h
lldb_private::ValueObject::GetProcessSP
lldb::ProcessSP GetProcessSP() const
Definition: ValueObject.h:338
lldb_private::TypeSummaryOptions::GetLanguage
lldb::LanguageType GetLanguage() const
Definition: TypeSummary.cpp:31
error
static llvm::raw_ostream & error(Stream &strm)
Definition: CommandReturnObject.cpp:17
lldb_private::DataExtractor
Definition: DataExtractor.h:48
lldb_private::ConstString::GetStringRef
llvm::StringRef GetStringRef() const
Get the string value as a llvm::StringRef.
Definition: ConstString.h:202
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetQuote
void SetQuote(char q)
Definition: StringPrinter.h:49
lldb::eTypeSummaryUncapped
@ eTypeSummaryUncapped
Definition: lldb-enumerations.h:1137
lldb_private::ObjCLanguageRuntime::GetClassDescriptor
virtual ClassDescriptorSP GetClassDescriptor(ValueObject &in_value)
Definition: ObjCLanguageRuntime.cpp:258
lldb_private::ValueObject::CreateValueObjectFromAddress
static lldb::ValueObjectSP CreateValueObjectFromAddress(llvm::StringRef name, uint64_t address, const ExecutionContext &exe_ctx, CompilerType type)
Definition: ValueObject.cpp:2985
lldb_private::ConstString
Definition: ConstString.h:40
lldb_private::ValueObject::GetSyntheticChildAtOffset
virtual lldb::ValueObjectSP GetSyntheticChildAtOffset(uint32_t offset, const CompilerType &type, bool can_create, ConstString name_const_str=ConstString())
Definition: ValueObject.cpp:1679
string
string(SUBSTRING ${p} 10 -1 pStripped) if($
Definition: Plugins/CMakeLists.txt:40
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetIgnoreMaxLength
void SetIgnoreMaxLength(bool e)
Definition: StringPrinter.h:69
ValueObject.h
lldb_private::formatters::NSMutableAttributedStringSummaryProvider
bool NSMutableAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition: NSString.cpp:313
lldb::eBasicTypeUnsignedInt
@ eBasicTypeUnsignedInt
Definition: lldb-enumerations.h:764
ValueObjectConstResult.h
lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions::SetHasSourceSize
void SetHasSourceSize(bool e)
Definition: StringPrinter.h:116
lldb_private::Status
Definition: Status.h:44
lldb_private::ValueObject
ValueObject:
Definition: ValueObject.h:105
uint32_t
lldb_private::formatters::NSTaggedString_SummaryProvider
bool NSTaggedString_SummaryProvider(ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, Stream &stream, const TypeSummaryOptions &summary_options)
Definition: NSString.cpp:318
lldb_private::Language
Definition: Language.h:29
lldb_private::ValueObject::GetCompilerType
CompilerType GetCompilerType()
Definition: ValueObject.h:352
lldb_private::TypeSummaryOptions
Definition: TypeSummary.h:26
lldb_private::formatters::StringPrinter::ReadStringAndDumpToStreamOptions
Definition: StringPrinter.h:102
lldb_private::ConstString::GetCString
const char * GetCString() const
Get the string value as a C string.
Definition: ConstString.h:216
lldb_private::CompilerType::GetBasicTypeFromAST
CompilerType GetBasicTypeFromAST(lldb::BasicType basic_type) const
Create related types using the current type's AST.
Definition: CompilerType.cpp:474
lldb_private::CompilerType
Generic representation of a type in a programming language.
Definition: CompilerType.h:33
lldb_private::Stream::Printf
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition: Stream.cpp:107
Status.h
lldb_private::formatters
Definition: CXXFunctionPointer.h:15
NSString.h
lldb_private
A class that represents a running process on the host machine.
Definition: SBCommandInterpreterRunOptions.h:16
lldb_private::TypeSummaryOptions::GetCapping
lldb::TypeSummaryCapping GetCapping() const
Definition: TypeSummary.cpp:33
ConstString.h
lldb_private::ValueObject::GetTargetSP
lldb::TargetSP GetTargetSP() const
Definition: ValueObject.h:334
StringPrinter.h
Stream.h
lldb_private::formatters::StringPrinter::DumpToStreamOptions::SetStream
void SetStream(Stream *s)
Definition: StringPrinter.h:33
lldb_private::formatters::NSAttributedStringSummaryProvider
bool NSAttributedStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options)
Definition: NSString.cpp:284
arm64_dwarf::x10
@ x10
Definition: ARM64_DWARF_Registers.h:27
FormattersHelpers.h
lldb::eByteOrderLittle
@ eByteOrderLittle
Definition: lldb-enumerations.h:142
lldb
Definition: SBAddress.h:15
Endian.h
DataBufferHeap.h