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