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