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