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" 12 #include "lldb/Core/ValueObjectConstResult.h" 13 #include "lldb/DataFormatters/FormattersHelpers.h" 14 #include "lldb/DataFormatters/StringPrinter.h" 15 #include "lldb/Target/Language.h" 16 #include "lldb/Target/Target.h" 17 #include "lldb/Utility/ConstString.h" 18 #include "lldb/Utility/DataBufferHeap.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 33 bool lldb_private::formatters::NSStringSummaryProvider( 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 47 ObjCLanguageRuntime::ClassDescriptorSP descriptor( 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 139 StringPrinter::ReadStringAndDumpToStreamOptions options(valobj); 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() == 157 TypeSummaryCapping::eTypeSummaryUncapped); 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() == 169 TypeSummaryCapping::eTypeSummaryUncapped); 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() == 185 TypeSummaryCapping::eTypeSummaryUncapped); 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() == 208 TypeSummaryCapping::eTypeSummaryUncapped); 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( 216 lldb::eBasicTypeUnsignedInt); 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() == 234 TypeSummaryCapping::eTypeSummaryUncapped); 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() == 256 TypeSummaryCapping::eTypeSummaryUncapped); 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() == 278 TypeSummaryCapping::eTypeSummaryUncapped); 279 return StringPrinter::ReadStringAndDumpToStream< 280 StringPrinter::StringElementType::ASCII>(options); 281 } 282 } 283 284 bool lldb_private::formatters::NSAttributedStringSummaryProvider( 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 313 bool lldb_private::formatters::NSMutableAttributedStringSummaryProvider( 314 ValueObject &valobj, Stream &stream, const TypeSummaryOptions &options) { 315 return NSAttributedStringSummaryProvider(valobj, stream, options); 316 } 317 318 bool lldb_private::formatters::NSTaggedString_SummaryProvider( 319 ValueObject &valobj, ObjCLanguageRuntime::ClassDescriptorSP descriptor, 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 } 383