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 return StringPrinter::ReadStringAndDumpToStream< 179 StringPrinter::StringElementType::UTF16>(options); 180 } else { 181 options.SetLocation(location + 1); 182 options.SetProcessSP(process_sp); 183 options.SetStream(&stream); 184 options.SetSourceSize(explicit_length); 185 options.SetHasSourceSize(has_explicit_length); 186 options.SetNeedsZeroTermination(false); 187 options.SetIgnoreMaxLength(summary_options.GetCapping() == 188 TypeSummaryCapping::eTypeSummaryUncapped); 189 options.SetBinaryZeroIsTerminator(false); 190 return StringPrinter::ReadStringAndDumpToStream< 191 StringPrinter::StringElementType::ASCII>(options); 192 } 193 } else if (is_inline && has_explicit_length && !is_unicode && 194 !is_path_store && !is_mutable) { 195 uint64_t location = 3 * ptr_size + valobj_addr; 196 197 options.SetLocation(location); 198 options.SetProcessSP(process_sp); 199 options.SetStream(&stream); 200 options.SetQuote('"'); 201 options.SetSourceSize(explicit_length); 202 options.SetHasSourceSize(has_explicit_length); 203 options.SetIgnoreMaxLength(summary_options.GetCapping() == 204 TypeSummaryCapping::eTypeSummaryUncapped); 205 return StringPrinter::ReadStringAndDumpToStream< 206 StringPrinter::StringElementType::ASCII>(options); 207 } else if (is_unicode) { 208 uint64_t location = valobj_addr + 2 * ptr_size; 209 if (is_inline) { 210 if (!has_explicit_length) { 211 return false; 212 } else 213 location += ptr_size; 214 } else { 215 location = process_sp->ReadPointerFromMemory(location, error); 216 if (error.Fail()) 217 return false; 218 } 219 options.SetLocation(location); 220 options.SetProcessSP(process_sp); 221 options.SetStream(&stream); 222 options.SetQuote('"'); 223 options.SetSourceSize(explicit_length); 224 options.SetHasSourceSize(has_explicit_length); 225 options.SetNeedsZeroTermination(!has_explicit_length); 226 options.SetIgnoreMaxLength(summary_options.GetCapping() == 227 TypeSummaryCapping::eTypeSummaryUncapped); 228 options.SetBinaryZeroIsTerminator(!has_explicit_length); 229 return StringPrinter::ReadStringAndDumpToStream< 230 StringPrinter::StringElementType::UTF16>(options); 231 } else if (is_path_store) { 232 ProcessStructReader reader(valobj.GetProcessSP().get(), 233 valobj.GetValueAsUnsigned(0), 234 GetNSPathStore2Type(*valobj.GetTargetSP())); 235 explicit_length = 236 reader.GetField<uint32_t>(ConstString("lengthAndRef")) >> 20; 237 lldb::addr_t location = valobj.GetValueAsUnsigned(0) + ptr_size + 4; 238 239 options.SetLocation(location); 240 options.SetProcessSP(process_sp); 241 options.SetStream(&stream); 242 options.SetQuote('"'); 243 options.SetSourceSize(explicit_length); 244 options.SetHasSourceSize(has_explicit_length); 245 options.SetNeedsZeroTermination(!has_explicit_length); 246 options.SetIgnoreMaxLength(summary_options.GetCapping() == 247 TypeSummaryCapping::eTypeSummaryUncapped); 248 options.SetBinaryZeroIsTerminator(!has_explicit_length); 249 return StringPrinter::ReadStringAndDumpToStream< 250 StringPrinter::StringElementType::UTF16>(options); 251 } else if (is_inline) { 252 uint64_t location = valobj_addr + 2 * ptr_size; 253 if (!has_explicit_length) { 254 // in this kind of string, the byte before the string content is a length 255 // byte so let's try and use it to handle the embedded NUL case 256 Status error; 257 explicit_length = 258 process_sp->ReadUnsignedIntegerFromMemory(location, 1, 0, error); 259 has_explicit_length = !(error.Fail() || explicit_length == 0); 260 location++; 261 } 262 options.SetLocation(location); 263 options.SetProcessSP(process_sp); 264 options.SetStream(&stream); 265 options.SetSourceSize(explicit_length); 266 options.SetHasSourceSize(has_explicit_length); 267 options.SetNeedsZeroTermination(!has_explicit_length); 268 options.SetIgnoreMaxLength(summary_options.GetCapping() == 269 TypeSummaryCapping::eTypeSummaryUncapped); 270 options.SetBinaryZeroIsTerminator(!has_explicit_length); 271 if (has_explicit_length) 272 return StringPrinter::ReadStringAndDumpToStream< 273 StringPrinter::StringElementType::UTF8>(options); 274 else 275 return StringPrinter::ReadStringAndDumpToStream< 276 StringPrinter::StringElementType::ASCII>(options); 277 } else { 278 uint64_t location = valobj_addr + 2 * ptr_size; 279 location = process_sp->ReadPointerFromMemory(location, error); 280 if (error.Fail()) 281 return false; 282 if (has_explicit_length && !has_null) 283 explicit_length++; // account for the fact that there is no NULL and we 284 // need to have one added 285 options.SetLocation(location); 286 options.SetProcessSP(process_sp); 287 options.SetStream(&stream); 288 options.SetSourceSize(explicit_length); 289 options.SetHasSourceSize(has_explicit_length); 290 options.SetIgnoreMaxLength(summary_options.GetCapping() == 291 TypeSummaryCapping::eTypeSummaryUncapped); 292 return StringPrinter::ReadStringAndDumpToStream< 293 StringPrinter::StringElementType::ASCII>(options); 294 } 295 } 296 297 bool lldb_private::formatters::NSAttributedStringSummaryProvider( 298 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 299 TargetSP target_sp(valobj.GetTargetSP()); 300 if (!target_sp) 301 return false; 302 uint32_t addr_size = target_sp->GetArchitecture().GetAddressByteSize(); 303 uint64_t pointer_value = valobj.GetValueAsUnsigned(0); 304 if (!pointer_value) 305 return false; 306 pointer_value += addr_size; 307 CompilerType type(valobj.GetCompilerType()); 308 ExecutionContext exe_ctx(target_sp, false); 309 ValueObjectSP child_ptr_sp(valobj.CreateValueObjectFromAddress( 310 "string_ptr", pointer_value, exe_ctx, type)); 311 if (!child_ptr_sp) 312 return false; 313 DataExtractor data; 314 Status error; 315 child_ptr_sp->GetData(data, error); 316 if (error.Fail()) 317 return false; 318 ValueObjectSP child_sp(child_ptr_sp->CreateValueObjectFromData( 319 "string_data", data, exe_ctx, type)); 320 child_sp->GetValueAsUnsigned(0); 321 if (child_sp) 322 return NSStringSummaryProvider(*child_sp, stream, options); 323 return false; 324 } 325 326 bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider( 327 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 328 return NSAttributedStringSummaryProvider(valobj, stream, options); 329 } 330 331 bool lldb_private::formatters::NSTaggedString_SummaryProvider( 332 ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, 333 Stream &stream, const TypeSummaryOptions &summary_options) { 334 static ConstString g_TypeHint("NSString"); 335 336 if (!descriptor) 337 return false; 338 uint64_t len_bits = 0, data_bits = 0; 339 if (!descriptor->GetTaggedPointerInfo(&len_bits, &data_bits, nullptr)) 340 return false; 341 342 static const int g_MaxNonBitmaskedLen = 7; // TAGGED_STRING_UNPACKED_MAXLEN 343 static const int g_SixbitMaxLen = 9; 344 static const int g_fiveBitMaxLen = 11; 345 346 static const char *sixBitToCharLookup = "eilotrm.apdnsIc ufkMShjTRxgC4013" 347 "bDNvwyUL2O856P-B79AFKEWV_zGJ/HYX"; 348 349 if (len_bits > g_fiveBitMaxLen) 350 return false; 351 352 std::string prefix, suffix; 353 if (Language *language = 354 Language::FindPlugin(summary_options.GetLanguage())) { 355 if (!language->GetFormatterPrefixSuffix(valobj, g_TypeHint, prefix, 356 suffix)) { 357 prefix.clear(); 358 suffix.clear(); 359 } 360 } 361 362 // this is a fairly ugly trick - pretend that the numeric value is actually a 363 // char* this works under a few assumptions: little endian architecture 364 // sizeof(uint64_t) > g_MaxNonBitmaskedLen 365 if (len_bits <= g_MaxNonBitmaskedLen) { 366 stream.Printf("%s", prefix.c_str()); 367 stream.Printf("\"%s\"", (const char *)&data_bits); 368 stream.Printf("%s", suffix.c_str()); 369 return true; 370 } 371 372 // if the data is bitmasked, we need to actually process the bytes 373 uint8_t bitmask = 0; 374 uint8_t shift_offset = 0; 375 376 if (len_bits <= g_SixbitMaxLen) { 377 bitmask = 0x03f; 378 shift_offset = 6; 379 } else { 380 bitmask = 0x01f; 381 shift_offset = 5; 382 } 383 384 std::vector<uint8_t> bytes; 385 bytes.resize(len_bits); 386 for (; len_bits > 0; data_bits >>= shift_offset, --len_bits) { 387 uint8_t packed = data_bits & bitmask; 388 bytes.insert(bytes.begin(), sixBitToCharLookup[packed]); 389 } 390 391 stream.Printf("%s", prefix.c_str()); 392 stream.Printf("\"%s\"", &bytes[0]); 393 stream.Printf("%s", suffix.c_str()); 394 return true; 395 } 396