Bug Summary

File:build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
Warning:line 650, column 3
Value stored to 'do_dlopen_function' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name PlatformPOSIX.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -ffunction-sections -fdata-sections -fcoverage-compilation-dir=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins -resource-dir /usr/lib/llvm-16/lib/clang/16.0.0 -isystem /usr/include/libxml2 -D HAVE_ROUND -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I tools/lldb/source/Plugins/Platform/POSIX -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/lldb/source/Plugins/Platform/POSIX -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/lldb/include -I tools/lldb/include -I include -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/llvm/include -I /usr/include/python3.9 -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/clang/include -I tools/lldb/../clang/include -I /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/lldb/source -I tools/lldb/source -D _FORTIFY_SOURCE=2 -D NDEBUG -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/lib/llvm-16/lib/clang/16.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/10/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fmacro-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fcoverage-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -O2 -Wno-unused-command-line-argument -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-class-memaccess -Wno-redundant-move -Wno-pessimizing-move -Wno-noexcept-type -Wno-comment -Wno-misleading-indentation -Wno-deprecated-declarations -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-stringop-truncation -std=c++17 -fdeprecated-macro -fdebug-compilation-dir=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/build-llvm/tools/clang/stage2-bins=build-llvm/tools/clang/stage2-bins -fdebug-prefix-map=/build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/= -ferror-limit 19 -fvisibility-inlines-hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-10-03-140002-15933-1 -x c++ /build/llvm-toolchain-snapshot-16~++20221003111214+1fa2019828ca/lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
1//===-- PlatformPOSIX.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 "PlatformPOSIX.h"
10
11#include "Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h"
12#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
13#include "lldb/Core/Debugger.h"
14#include "lldb/Core/Module.h"
15#include "lldb/Core/ValueObject.h"
16#include "lldb/Expression/DiagnosticManager.h"
17#include "lldb/Expression/FunctionCaller.h"
18#include "lldb/Expression/UserExpression.h"
19#include "lldb/Expression/UtilityFunction.h"
20#include "lldb/Host/File.h"
21#include "lldb/Host/FileCache.h"
22#include "lldb/Host/FileSystem.h"
23#include "lldb/Host/Host.h"
24#include "lldb/Host/HostInfo.h"
25#include "lldb/Host/ProcessLaunchInfo.h"
26#include "lldb/Target/DynamicLoader.h"
27#include "lldb/Target/ExecutionContext.h"
28#include "lldb/Target/Process.h"
29#include "lldb/Target/Thread.h"
30#include "lldb/Utility/DataBufferHeap.h"
31#include "lldb/Utility/FileSpec.h"
32#include "lldb/Utility/LLDBLog.h"
33#include "lldb/Utility/Log.h"
34#include "lldb/Utility/StreamString.h"
35#include "llvm/ADT/ScopeExit.h"
36
37using namespace lldb;
38using namespace lldb_private;
39
40/// Default Constructor
41PlatformPOSIX::PlatformPOSIX(bool is_host)
42 : RemoteAwarePlatform(is_host), // This is the local host platform
43 m_option_group_platform_rsync(new OptionGroupPlatformRSync()),
44 m_option_group_platform_ssh(new OptionGroupPlatformSSH()),
45 m_option_group_platform_caching(new OptionGroupPlatformCaching()) {}
46
47/// Destructor.
48///
49/// The destructor is virtual since this class is designed to be
50/// inherited from by the plug-in instance.
51PlatformPOSIX::~PlatformPOSIX() = default;
52
53lldb_private::OptionGroupOptions *PlatformPOSIX::GetConnectionOptions(
54 lldb_private::CommandInterpreter &interpreter) {
55 auto iter = m_options.find(&interpreter), end = m_options.end();
56 if (iter == end) {
57 std::unique_ptr<lldb_private::OptionGroupOptions> options(
58 new OptionGroupOptions());
59 options->Append(m_option_group_platform_rsync.get());
60 options->Append(m_option_group_platform_ssh.get());
61 options->Append(m_option_group_platform_caching.get());
62 m_options[&interpreter] = std::move(options);
63 }
64
65 return m_options.at(&interpreter).get();
66}
67
68static uint32_t chown_file(Platform *platform, const char *path,
69 uint32_t uid = UINT32_MAX(4294967295U),
70 uint32_t gid = UINT32_MAX(4294967295U)) {
71 if (!platform || !path || *path == 0)
72 return UINT32_MAX(4294967295U);
73
74 if (uid == UINT32_MAX(4294967295U) && gid == UINT32_MAX(4294967295U))
75 return 0; // pretend I did chown correctly - actually I just didn't care
76
77 StreamString command;
78 command.PutCString("chown ");
79 if (uid != UINT32_MAX(4294967295U))
80 command.Printf("%d", uid);
81 if (gid != UINT32_MAX(4294967295U))
82 command.Printf(":%d", gid);
83 command.Printf("%s", path);
84 int status;
85 platform->RunShellCommand(command.GetData(), FileSpec(), &status, nullptr,
86 nullptr, std::chrono::seconds(10));
87 return status;
88}
89
90lldb_private::Status
91PlatformPOSIX::PutFile(const lldb_private::FileSpec &source,
92 const lldb_private::FileSpec &destination, uint32_t uid,
93 uint32_t gid) {
94 Log *log = GetLog(LLDBLog::Platform);
95
96 if (IsHost()) {
97 if (source == destination)
98 return Status();
99 // cp src dst
100 // chown uid:gid dst
101 std::string src_path(source.GetPath());
102 if (src_path.empty())
103 return Status("unable to get file path for source");
104 std::string dst_path(destination.GetPath());
105 if (dst_path.empty())
106 return Status("unable to get file path for destination");
107 StreamString command;
108 command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
109 int status;
110 RunShellCommand(command.GetData(), FileSpec(), &status, nullptr, nullptr,
111 std::chrono::seconds(10));
112 if (status != 0)
113 return Status("unable to perform copy");
114 if (uid == UINT32_MAX(4294967295U) && gid == UINT32_MAX(4294967295U))
115 return Status();
116 if (chown_file(this, dst_path.c_str(), uid, gid) != 0)
117 return Status("unable to perform chown");
118 return Status();
119 } else if (m_remote_platform_sp) {
120 if (GetSupportsRSync()) {
121 std::string src_path(source.GetPath());
122 if (src_path.empty())
123 return Status("unable to get file path for source");
124 std::string dst_path(destination.GetPath());
125 if (dst_path.empty())
126 return Status("unable to get file path for destination");
127 StreamString command;
128 if (GetIgnoresRemoteHostname()) {
129 if (!GetRSyncPrefix())
130 command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
131 dst_path.c_str());
132 else
133 command.Printf("rsync %s %s %s%s", GetRSyncOpts(), src_path.c_str(),
134 GetRSyncPrefix(), dst_path.c_str());
135 } else
136 command.Printf("rsync %s %s %s:%s", GetRSyncOpts(), src_path.c_str(),
137 GetHostname(), dst_path.c_str());
138 LLDB_LOGF(log, "[PutFile] Running command: %s\n", command.GetData())do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("[PutFile] Running command: %s\n", command
.GetData()); } while (0)
;
139 int retcode;
140 Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
141 nullptr, std::chrono::minutes(1));
142 if (retcode == 0) {
143 // Don't chown a local file for a remote system
144 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
145 // return Status("unable to perform chown");
146 return Status();
147 }
148 // if we are still here rsync has failed - let's try the slow way before
149 // giving up
150 }
151 }
152 return Platform::PutFile(source, destination, uid, gid);
153}
154
155lldb_private::Status PlatformPOSIX::GetFile(
156 const lldb_private::FileSpec &source, // remote file path
157 const lldb_private::FileSpec &destination) // local file path
158{
159 Log *log = GetLog(LLDBLog::Platform);
160
161 // Check the args, first.
162 std::string src_path(source.GetPath());
163 if (src_path.empty())
164 return Status("unable to get file path for source");
165 std::string dst_path(destination.GetPath());
166 if (dst_path.empty())
167 return Status("unable to get file path for destination");
168 if (IsHost()) {
169 if (source == destination)
170 return Status("local scenario->source and destination are the same file "
171 "path: no operation performed");
172 // cp src dst
173 StreamString cp_command;
174 cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
175 int status;
176 RunShellCommand(cp_command.GetData(), FileSpec(), &status, nullptr, nullptr,
177 std::chrono::seconds(10));
178 if (status != 0)
179 return Status("unable to perform copy");
180 return Status();
181 } else if (m_remote_platform_sp) {
182 if (GetSupportsRSync()) {
183 StreamString command;
184 if (GetIgnoresRemoteHostname()) {
185 if (!GetRSyncPrefix())
186 command.Printf("rsync %s %s %s", GetRSyncOpts(), src_path.c_str(),
187 dst_path.c_str());
188 else
189 command.Printf("rsync %s %s%s %s", GetRSyncOpts(), GetRSyncPrefix(),
190 src_path.c_str(), dst_path.c_str());
191 } else
192 command.Printf("rsync %s %s:%s %s", GetRSyncOpts(),
193 m_remote_platform_sp->GetHostname(), src_path.c_str(),
194 dst_path.c_str());
195 LLDB_LOGF(log, "[GetFile] Running command: %s\n", command.GetData())do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("[GetFile] Running command: %s\n", command
.GetData()); } while (0)
;
196 int retcode;
197 Host::RunShellCommand(command.GetData(), FileSpec(), &retcode, nullptr,
198 nullptr, std::chrono::minutes(1));
199 if (retcode == 0)
200 return Status();
201 // If we are here, rsync has failed - let's try the slow way before
202 // giving up
203 }
204 // open src and dst
205 // read/write, read/write, read/write, ...
206 // close src
207 // close dst
208 LLDB_LOGF(log, "[GetFile] Using block by block transfer....\n")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("[GetFile] Using block by block transfer....\n"
); } while (0)
;
209 Status error;
210 user_id_t fd_src = OpenFile(source, File::eOpenOptionReadOnly,
211 lldb::eFilePermissionsFileDefault, error);
212
213 if (fd_src == UINT64_MAX(18446744073709551615UL))
214 return Status("unable to open source file");
215
216 uint32_t permissions = 0;
217 error = GetFilePermissions(source, permissions);
218
219 if (permissions == 0)
220 permissions = lldb::eFilePermissionsFileDefault;
221
222 user_id_t fd_dst = FileCache::GetInstance().OpenFile(
223 destination, File::eOpenOptionCanCreate | File::eOpenOptionWriteOnly |
224 File::eOpenOptionTruncate,
225 permissions, error);
226
227 if (fd_dst == UINT64_MAX(18446744073709551615UL)) {
228 if (error.Success())
229 error.SetErrorString("unable to open destination file");
230 }
231
232 if (error.Success()) {
233 lldb::WritableDataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
234 uint64_t offset = 0;
235 error.Clear();
236 while (error.Success()) {
237 const uint64_t n_read = ReadFile(fd_src, offset, buffer_sp->GetBytes(),
238 buffer_sp->GetByteSize(), error);
239 if (error.Fail())
240 break;
241 if (n_read == 0)
242 break;
243 if (FileCache::GetInstance().WriteFile(fd_dst, offset,
244 buffer_sp->GetBytes(), n_read,
245 error) != n_read) {
246 if (!error.Fail())
247 error.SetErrorString("unable to write to destination file");
248 break;
249 }
250 offset += n_read;
251 }
252 }
253 // Ignore the close error of src.
254 if (fd_src != UINT64_MAX(18446744073709551615UL))
255 CloseFile(fd_src, error);
256 // And close the dst file descriptot.
257 if (fd_dst != UINT64_MAX(18446744073709551615UL) &&
258 !FileCache::GetInstance().CloseFile(fd_dst, error)) {
259 if (!error.Fail())
260 error.SetErrorString("unable to close destination file");
261 }
262 return error;
263 }
264 return Platform::GetFile(source, destination);
265}
266
267std::string PlatformPOSIX::GetPlatformSpecificConnectionInformation() {
268 StreamString stream;
269 if (GetSupportsRSync()) {
270 stream.PutCString("rsync");
271 if ((GetRSyncOpts() && *GetRSyncOpts()) ||
272 (GetRSyncPrefix() && *GetRSyncPrefix()) || GetIgnoresRemoteHostname()) {
273 stream.Printf(", options: ");
274 if (GetRSyncOpts() && *GetRSyncOpts())
275 stream.Printf("'%s' ", GetRSyncOpts());
276 stream.Printf(", prefix: ");
277 if (GetRSyncPrefix() && *GetRSyncPrefix())
278 stream.Printf("'%s' ", GetRSyncPrefix());
279 if (GetIgnoresRemoteHostname())
280 stream.Printf("ignore remote-hostname ");
281 }
282 }
283 if (GetSupportsSSH()) {
284 stream.PutCString("ssh");
285 if (GetSSHOpts() && *GetSSHOpts())
286 stream.Printf(", options: '%s' ", GetSSHOpts());
287 }
288 if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
289 stream.Printf("cache dir: %s", GetLocalCacheDirectory());
290 if (stream.GetSize())
291 return std::string(stream.GetString());
292 else
293 return "";
294}
295
296const lldb::UnixSignalsSP &PlatformPOSIX::GetRemoteUnixSignals() {
297 if (IsRemote() && m_remote_platform_sp)
298 return m_remote_platform_sp->GetRemoteUnixSignals();
299 return Platform::GetRemoteUnixSignals();
300}
301
302Status PlatformPOSIX::ConnectRemote(Args &args) {
303 Status error;
304 if (IsHost()) {
305 error.SetErrorStringWithFormatv(
306 "can't connect to the host platform '{0}', always connected",
307 GetPluginName());
308 } else {
309 if (!m_remote_platform_sp)
310 m_remote_platform_sp =
311 platform_gdb_server::PlatformRemoteGDBServer::CreateInstance(
312 /*force=*/true, nullptr);
313
314 if (m_remote_platform_sp && error.Success())
315 error = m_remote_platform_sp->ConnectRemote(args);
316 else
317 error.SetErrorString("failed to create a 'remote-gdb-server' platform");
318
319 if (error.Fail())
320 m_remote_platform_sp.reset();
321 }
322
323 if (error.Success() && m_remote_platform_sp) {
324 if (m_option_group_platform_rsync.get() &&
325 m_option_group_platform_ssh.get() &&
326 m_option_group_platform_caching.get()) {
327 if (m_option_group_platform_rsync->m_rsync) {
328 SetSupportsRSync(true);
329 SetRSyncOpts(m_option_group_platform_rsync->m_rsync_opts.c_str());
330 SetRSyncPrefix(m_option_group_platform_rsync->m_rsync_prefix.c_str());
331 SetIgnoresRemoteHostname(
332 m_option_group_platform_rsync->m_ignores_remote_hostname);
333 }
334 if (m_option_group_platform_ssh->m_ssh) {
335 SetSupportsSSH(true);
336 SetSSHOpts(m_option_group_platform_ssh->m_ssh_opts.c_str());
337 }
338 SetLocalCacheDirectory(
339 m_option_group_platform_caching->m_cache_dir.c_str());
340 }
341 }
342
343 return error;
344}
345
346Status PlatformPOSIX::DisconnectRemote() {
347 Status error;
348
349 if (IsHost()) {
350 error.SetErrorStringWithFormatv(
351 "can't disconnect from the host platform '{0}', always connected",
352 GetPluginName());
353 } else {
354 if (m_remote_platform_sp)
355 error = m_remote_platform_sp->DisconnectRemote();
356 else
357 error.SetErrorString("the platform is not currently connected");
358 }
359 return error;
360}
361
362lldb::ProcessSP PlatformPOSIX::Attach(ProcessAttachInfo &attach_info,
363 Debugger &debugger, Target *target,
364 Status &error) {
365 lldb::ProcessSP process_sp;
366 Log *log = GetLog(LLDBLog::Platform);
367
368 if (IsHost()) {
369 if (target == nullptr) {
370 TargetSP new_target_sp;
371
372 error = debugger.GetTargetList().CreateTarget(
373 debugger, "", "", eLoadDependentsNo, nullptr, new_target_sp);
374 target = new_target_sp.get();
375 LLDB_LOGF(log, "PlatformPOSIX::%s created new target", __FUNCTION__)do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s created new target"
, __FUNCTION__); } while (0)
;
376 } else {
377 error.Clear();
378 LLDB_LOGF(log, "PlatformPOSIX::%s target already existed, setting target",do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s target already existed, setting target"
, __FUNCTION__); } while (0)
379 __FUNCTION__)do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s target already existed, setting target"
, __FUNCTION__); } while (0)
;
380 }
381
382 if (target && error.Success()) {
383 if (log) {
384 ModuleSP exe_module_sp = target->GetExecutableModule();
385 LLDB_LOGF(log, "PlatformPOSIX::%s set selected target to %p %s",do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s set selected target to %p %s"
, __FUNCTION__, (void *)target, exe_module_sp ? exe_module_sp
->GetFileSpec().GetPath().c_str() : "<null>"); } while
(0)
386 __FUNCTION__, (void *)target,do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s set selected target to %p %s"
, __FUNCTION__, (void *)target, exe_module_sp ? exe_module_sp
->GetFileSpec().GetPath().c_str() : "<null>"); } while
(0)
387 exe_module_sp ? exe_module_sp->GetFileSpec().GetPath().c_str()do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s set selected target to %p %s"
, __FUNCTION__, (void *)target, exe_module_sp ? exe_module_sp
->GetFileSpec().GetPath().c_str() : "<null>"); } while
(0)
388 : "<null>")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Printf("PlatformPOSIX::%s set selected target to %p %s"
, __FUNCTION__, (void *)target, exe_module_sp ? exe_module_sp
->GetFileSpec().GetPath().c_str() : "<null>"); } while
(0)
;
389 }
390
391 process_sp =
392 target->CreateProcess(attach_info.GetListenerForProcess(debugger),
393 "gdb-remote", nullptr, true);
394
395 if (process_sp) {
396 ListenerSP listener_sp = attach_info.GetHijackListener();
397 if (listener_sp == nullptr) {
398 listener_sp =
399 Listener::MakeListener("lldb.PlatformPOSIX.attach.hijack");
400 attach_info.SetHijackListener(listener_sp);
401 }
402 process_sp->HijackProcessEvents(listener_sp);
403 error = process_sp->Attach(attach_info);
404 }
405 }
406 } else {
407 if (m_remote_platform_sp)
408 process_sp =
409 m_remote_platform_sp->Attach(attach_info, debugger, target, error);
410 else
411 error.SetErrorString("the platform is not currently connected");
412 }
413 return process_sp;
414}
415
416lldb::ProcessSP PlatformPOSIX::DebugProcess(ProcessLaunchInfo &launch_info,
417 Debugger &debugger, Target &target,
418 Status &error) {
419 Log *log = GetLog(LLDBLog::Platform);
420 LLDB_LOG(log, "target {0}", &target)do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "target {0}", &target); } while (0)
;
421
422 ProcessSP process_sp;
423
424 if (!IsHost()) {
425 if (m_remote_platform_sp)
426 process_sp = m_remote_platform_sp->DebugProcess(launch_info, debugger,
427 target, error);
428 else
429 error.SetErrorString("the platform is not currently connected");
430 return process_sp;
431 }
432
433 //
434 // For local debugging, we'll insist on having ProcessGDBRemote create the
435 // process.
436 //
437
438 // Make sure we stop at the entry point
439 launch_info.GetFlags().Set(eLaunchFlagDebug);
440
441 // We always launch the process we are going to debug in a separate process
442 // group, since then we can handle ^C interrupts ourselves w/o having to
443 // worry about the target getting them as well.
444 launch_info.SetLaunchInSeparateProcessGroup(true);
445
446 // Now create the gdb-remote process.
447 LLDB_LOG(log, "having target create process with gdb-remote plugin")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "having target create process with gdb-remote plugin"
); } while (0)
;
448 process_sp = target.CreateProcess(launch_info.GetListener(), "gdb-remote",
449 nullptr, true);
450
451 if (!process_sp) {
452 error.SetErrorString("CreateProcess() failed for gdb-remote process");
453 LLDB_LOG(log, "error: {0}", error)do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "error: {0}", error); } while (0)
;
454 return process_sp;
455 }
456
457 LLDB_LOG(log, "successfully created process")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "successfully created process"); } while (0)
;
458
459 process_sp->HijackProcessEvents(launch_info.GetHijackListener());
460
461 // Log file actions.
462 if (log) {
463 LLDB_LOG(log, "launching process with the following file actions:")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "launching process with the following file actions:"
); } while (0)
;
464 StreamString stream;
465 size_t i = 0;
466 const FileAction *file_action;
467 while ((file_action = launch_info.GetFileActionAtIndex(i++)) != nullptr) {
468 file_action->Dump(stream);
469 LLDB_LOG(log, "{0}", stream.GetData())do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "{0}", stream.GetData()); } while (0)
;
470 stream.Clear();
471 }
472 }
473
474 // Do the launch.
475 error = process_sp->Launch(launch_info);
476 if (error.Success()) {
477 // Hook up process PTY if we have one (which we should for local debugging
478 // with llgs).
479 int pty_fd = launch_info.GetPTY().ReleasePrimaryFileDescriptor();
480 if (pty_fd != PseudoTerminal::invalid_fd) {
481 process_sp->SetSTDIOFileDescriptor(pty_fd);
482 LLDB_LOG(log, "hooked up STDIO pty to process")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "hooked up STDIO pty to process"); } while (0)
;
483 } else
484 LLDB_LOG(log, "not using process STDIO pty")do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "not using process STDIO pty"); } while (0)
;
485 } else {
486 LLDB_LOG(log, "{0}", error)do { ::lldb_private::Log *log_private = (log); if (log_private
) log_private->Format("lldb/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp"
, __func__, "{0}", error); } while (0)
;
487 // FIXME figure out appropriate cleanup here. Do we delete the process?
488 // Does our caller do that?
489 }
490
491 return process_sp;
492}
493
494void PlatformPOSIX::CalculateTrapHandlerSymbolNames() {
495 m_trap_handlers.push_back(ConstString("_sigtramp"));
496}
497
498Status PlatformPOSIX::EvaluateLibdlExpression(
499 lldb_private::Process *process, const char *expr_cstr,
500 llvm::StringRef expr_prefix, lldb::ValueObjectSP &result_valobj_sp) {
501 DynamicLoader *loader = process->GetDynamicLoader();
502 if (loader) {
503 Status error = loader->CanLoadImage();
504 if (error.Fail())
505 return error;
506 }
507
508 ThreadSP thread_sp(process->GetThreadList().GetExpressionExecutionThread());
509 if (!thread_sp)
510 return Status("Selected thread isn't valid");
511
512 StackFrameSP frame_sp(thread_sp->GetStackFrameAtIndex(0));
513 if (!frame_sp)
514 return Status("Frame 0 isn't valid");
515
516 ExecutionContext exe_ctx;
517 frame_sp->CalculateExecutionContext(exe_ctx);
518 EvaluateExpressionOptions expr_options;
519 expr_options.SetUnwindOnError(true);
520 expr_options.SetIgnoreBreakpoints(true);
521 expr_options.SetExecutionPolicy(eExecutionPolicyAlways);
522 expr_options.SetLanguage(eLanguageTypeC_plus_plus);
523 expr_options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
524 // don't do the work to trap them.
525 expr_options.SetTimeout(process->GetUtilityExpressionTimeout());
526
527 Status expr_error;
528 ExpressionResults result =
529 UserExpression::Evaluate(exe_ctx, expr_options, expr_cstr, expr_prefix,
530 result_valobj_sp, expr_error);
531 if (result != eExpressionCompleted)
532 return expr_error;
533
534 if (result_valobj_sp->GetError().Fail())
535 return result_valobj_sp->GetError();
536 return Status();
537}
538
539std::unique_ptr<UtilityFunction>
540PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx,
541 Status &error) {
542 // Remember to prepend this with the prefix from
543 // GetLibdlFunctionDeclarations. The returned values are all in
544 // __lldb_dlopen_result for consistency. The wrapper returns a void * but
545 // doesn't use it because UtilityFunctions don't work with void returns at
546 // present.
547 //
548 // Use lazy binding so as to not make dlopen()'s success conditional on
549 // forcing every symbol in the library.
550 //
551 // In general, the debugger should allow programs to load & run with
552 // libraries as far as they can, instead of defaulting to being super-picky
553 // about unavailable symbols.
554 //
555 // The value "1" appears to imply lazy binding (RTLD_LAZY) on both Darwin
556 // and other POSIX OSes.
557 static const char *dlopen_wrapper_code = R"(
558 const int RTLD_LAZY = 1;
559
560 struct __lldb_dlopen_result {
561 void *image_ptr;
562 const char *error_str;
563 };
564
565 extern "C" void *memcpy(void *, const void *, size_t size);
566 extern "C" size_t strlen(const char *);
567
568
569 void * __lldb_dlopen_wrapper (const char *name,
570 const char *path_strings,
571 char *buffer,
572 __lldb_dlopen_result *result_ptr)
573 {
574 // This is the case where the name is the full path:
575 if (!path_strings) {
576 result_ptr->image_ptr = dlopen(name, RTLD_LAZY);
577 if (result_ptr->image_ptr)
578 result_ptr->error_str = nullptr;
579 else
580 result_ptr->error_str = dlerror();
581 return nullptr;
582 }
583
584 // This is the case where we have a list of paths:
585 size_t name_len = strlen(name);
586 while (path_strings && path_strings[0] != '\0') {
587 size_t path_len = strlen(path_strings);
588 memcpy((void *) buffer, (void *) path_strings, path_len);
589 buffer[path_len] = '/';
590 char *target_ptr = buffer+path_len+1;
591 memcpy((void *) target_ptr, (void *) name, name_len + 1);
592 result_ptr->image_ptr = dlopen(buffer, RTLD_LAZY);
593 if (result_ptr->image_ptr) {
594 result_ptr->error_str = nullptr;
595 break;
596 }
597 result_ptr->error_str = dlerror();
598 path_strings = path_strings + path_len + 1;
599 }
600 return nullptr;
601 }
602 )";
603
604 static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
605 Process *process = exe_ctx.GetProcessSP().get();
606 // Insert the dlopen shim defines into our generic expression:
607 std::string expr(std::string(GetLibdlFunctionDeclarations(process)));
608 expr.append(dlopen_wrapper_code);
609 Status utility_error;
610 DiagnosticManager diagnostics;
611
612 auto utility_fn_or_error = process->GetTarget().CreateUtilityFunction(
613 std::move(expr), dlopen_wrapper_name, eLanguageTypeC_plus_plus, exe_ctx);
614 if (!utility_fn_or_error) {
615 std::string error_str = llvm::toString(utility_fn_or_error.takeError());
616 error.SetErrorStringWithFormat(
617 "dlopen error: could not create utility function: %s",
618 error_str.c_str());
619 return nullptr;
620 }
621 std::unique_ptr<UtilityFunction> dlopen_utility_func_up =
622 std::move(*utility_fn_or_error);
623
624 Value value;
625 ValueList arguments;
626 FunctionCaller *do_dlopen_function = nullptr;
627
628 // Fetch the clang types we will need:
629 TypeSystemClang *ast =
630 ScratchTypeSystemClang::GetForTarget(process->GetTarget());
631 if (!ast)
632 return nullptr;
633
634 CompilerType clang_void_pointer_type
635 = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
636 CompilerType clang_char_pointer_type
637 = ast->GetBasicType(eBasicTypeChar).GetPointerType();
638
639 // We are passing four arguments, the basename, the list of places to look,
640 // a buffer big enough for all the path + name combos, and
641 // a pointer to the storage we've made for the result:
642 value.SetValueType(Value::ValueType::Scalar);
643 value.SetCompilerType(clang_void_pointer_type);
644 arguments.PushValue(value);
645 value.SetCompilerType(clang_char_pointer_type);
646 arguments.PushValue(value);
647 arguments.PushValue(value);
648 arguments.PushValue(value);
649
650 do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
Value stored to 'do_dlopen_function' is never read
651 clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
652 if (utility_error.Fail()) {
653 error.SetErrorStringWithFormat(
654 "dlopen error: could not make function caller: %s",
655 utility_error.AsCString());
656 return nullptr;
657 }
658
659 do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
660 if (!do_dlopen_function) {
661 error.SetErrorString("dlopen error: could not get function caller.");
662 return nullptr;
663 }
664
665 // We made a good utility function, so cache it in the process:
666 return dlopen_utility_func_up;
667}
668
669uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
670 const lldb_private::FileSpec &remote_file,
671 const std::vector<std::string> *paths,
672 lldb_private::Status &error,
673 lldb_private::FileSpec *loaded_image) {
674 if (loaded_image)
675 loaded_image->Clear();
676
677 std::string path;
678 path = remote_file.GetPath();
679
680 ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
681 if (!thread_sp) {
682 error.SetErrorString("dlopen error: no thread available to call dlopen.");
683 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
684 }
685
686 DiagnosticManager diagnostics;
687
688 ExecutionContext exe_ctx;
689 thread_sp->CalculateExecutionContext(exe_ctx);
690
691 Status utility_error;
692 UtilityFunction *dlopen_utility_func;
693 ValueList arguments;
694 FunctionCaller *do_dlopen_function = nullptr;
695
696 // The UtilityFunction is held in the Process. Platforms don't track the
697 // lifespan of the Targets that use them, we can't put this in the Platform.
698 dlopen_utility_func = process->GetLoadImageUtilityFunction(
699 this, [&]() -> std::unique_ptr<UtilityFunction> {
700 return MakeLoadImageUtilityFunction(exe_ctx, error);
701 });
702 // If we couldn't make it, the error will be in error, so we can exit here.
703 if (!dlopen_utility_func)
704 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
705
706 do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
707 if (!do_dlopen_function) {
708 error.SetErrorString("dlopen error: could not get function caller.");
709 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
710 }
711 arguments = do_dlopen_function->GetArgumentValues();
712
713 // Now insert the path we are searching for and the result structure into the
714 // target.
715 uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
716 size_t path_len = path.size() + 1;
717 lldb::addr_t path_addr = process->AllocateMemory(path_len,
718 permissions,
719 utility_error);
720 if (path_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) {
721 error.SetErrorStringWithFormat(
722 "dlopen error: could not allocate memory for path: %s",
723 utility_error.AsCString());
724 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
725 }
726
727 // Make sure we deallocate the input string memory:
728 auto path_cleanup = llvm::make_scope_exit([process, path_addr] {
729 // Deallocate the buffer.
730 process->DeallocateMemory(path_addr);
731 });
732
733 process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
734 if (utility_error.Fail()) {
735 error.SetErrorStringWithFormat(
736 "dlopen error: could not write path string: %s",
737 utility_error.AsCString());
738 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
739 }
740
741 // Make space for our return structure. It is two pointers big: the token
742 // and the error string.
743 const uint32_t addr_size = process->GetAddressByteSize();
744 lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
745 permissions,
746 utility_error);
747 if (utility_error.Fail()) {
748 error.SetErrorStringWithFormat(
749 "dlopen error: could not allocate memory for path: %s",
750 utility_error.AsCString());
751 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
752 }
753
754 // Make sure we deallocate the result structure memory
755 auto return_cleanup = llvm::make_scope_exit([process, return_addr] {
756 // Deallocate the buffer
757 process->DeallocateMemory(return_addr);
758 });
759
760 // This will be the address of the storage for paths, if we are using them,
761 // or nullptr to signal we aren't.
762 lldb::addr_t path_array_addr = 0x0;
763 llvm::Optional<llvm::detail::scope_exit<std::function<void()>>>
764 path_array_cleanup;
765
766 // This is the address to a buffer large enough to hold the largest path
767 // conjoined with the library name we're passing in. This is a convenience
768 // to avoid having to call malloc in the dlopen function.
769 lldb::addr_t buffer_addr = 0x0;
770 llvm::Optional<llvm::detail::scope_exit<std::function<void()>>>
771 buffer_cleanup;
772
773 // Set the values into our args and write them to the target:
774 if (paths != nullptr) {
775 // First insert the paths into the target. This is expected to be a
776 // continuous buffer with the strings laid out null terminated and
777 // end to end with an empty string terminating the buffer.
778 // We also compute the buffer's required size as we go.
779 size_t buffer_size = 0;
780 std::string path_array;
781 for (auto path : *paths) {
782 // Don't insert empty paths, they will make us abort the path
783 // search prematurely.
784 if (path.empty())
785 continue;
786 size_t path_size = path.size();
787 path_array.append(path);
788 path_array.push_back('\0');
789 if (path_size > buffer_size)
790 buffer_size = path_size;
791 }
792 path_array.push_back('\0');
793
794 path_array_addr = process->AllocateMemory(path_array.size(),
795 permissions,
796 utility_error);
797 if (path_array_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) {
798 error.SetErrorStringWithFormat(
799 "dlopen error: could not allocate memory for path array: %s",
800 utility_error.AsCString());
801 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
802 }
803
804 // Make sure we deallocate the paths array.
805 path_array_cleanup.emplace([process, path_array_addr]() {
806 // Deallocate the path array.
807 process->DeallocateMemory(path_array_addr);
808 });
809
810 process->WriteMemory(path_array_addr, path_array.data(),
811 path_array.size(), utility_error);
812
813 if (utility_error.Fail()) {
814 error.SetErrorStringWithFormat(
815 "dlopen error: could not write path array: %s",
816 utility_error.AsCString());
817 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
818 }
819 // Now make spaces in the target for the buffer. We need to add one for
820 // the '/' that the utility function will insert and one for the '\0':
821 buffer_size += path.size() + 2;
822
823 buffer_addr = process->AllocateMemory(buffer_size,
824 permissions,
825 utility_error);
826 if (buffer_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL)) {
827 error.SetErrorStringWithFormat(
828 "dlopen error: could not allocate memory for buffer: %s",
829 utility_error.AsCString());
830 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
831 }
832
833 // Make sure we deallocate the buffer memory:
834 buffer_cleanup.emplace([process, buffer_addr]() {
835 // Deallocate the buffer.
836 process->DeallocateMemory(buffer_addr);
837 });
838 }
839
840 arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
841 arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
842 arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
843 arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
844
845 lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS(18446744073709551615UL);
846
847 diagnostics.Clear();
848 if (!do_dlopen_function->WriteFunctionArguments(exe_ctx,
849 func_args_addr,
850 arguments,
851 diagnostics)) {
852 error.SetErrorStringWithFormat(
853 "dlopen error: could not write function arguments: %s",
854 diagnostics.GetString().c_str());
855 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
856 }
857
858 // Make sure we clean up the args structure. We can't reuse it because the
859 // Platform lives longer than the process and the Platforms don't get a
860 // signal to clean up cached data when a process goes away.
861 auto args_cleanup =
862 llvm::make_scope_exit([do_dlopen_function, &exe_ctx, func_args_addr] {
863 do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
864 });
865
866 // Now run the caller:
867 EvaluateExpressionOptions options;
868 options.SetExecutionPolicy(eExecutionPolicyAlways);
869 options.SetLanguage(eLanguageTypeC_plus_plus);
870 options.SetIgnoreBreakpoints(true);
871 options.SetUnwindOnError(true);
872 options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
873 // don't do the work to trap them.
874 options.SetTimeout(process->GetUtilityExpressionTimeout());
875 options.SetIsForUtilityExpr(true);
876
877 Value return_value;
878 // Fetch the clang types we will need:
879 TypeSystemClang *ast =
880 ScratchTypeSystemClang::GetForTarget(process->GetTarget());
881 if (!ast) {
882 error.SetErrorString("dlopen error: Unable to get TypeSystemClang");
883 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
884 }
885
886 CompilerType clang_void_pointer_type
887 = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
888
889 return_value.SetCompilerType(clang_void_pointer_type);
890
891 ExpressionResults results = do_dlopen_function->ExecuteFunction(
892 exe_ctx, &func_args_addr, options, diagnostics, return_value);
893 if (results != eExpressionCompleted) {
894 error.SetErrorStringWithFormat(
895 "dlopen error: failed executing dlopen wrapper function: %s",
896 diagnostics.GetString().c_str());
897 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
898 }
899
900 // Read the dlopen token from the return area:
901 lldb::addr_t token = process->ReadPointerFromMemory(return_addr,
902 utility_error);
903 if (utility_error.Fail()) {
904 error.SetErrorStringWithFormat(
905 "dlopen error: could not read the return struct: %s",
906 utility_error.AsCString());
907 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
908 }
909
910 // The dlopen succeeded!
911 if (token != 0x0) {
912 if (loaded_image && buffer_addr != 0x0)
913 {
914 // Capture the image which was loaded. We leave it in the buffer on
915 // exit from the dlopen function, so we can just read it from there:
916 std::string name_string;
917 process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
918 if (utility_error.Success())
919 loaded_image->SetFile(name_string, llvm::sys::path::Style::posix);
920 }
921 return process->AddImageToken(token);
922 }
923
924 // We got an error, lets read in the error string:
925 std::string dlopen_error_str;
926 lldb::addr_t error_addr
927 = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
928 if (utility_error.Fail()) {
929 error.SetErrorStringWithFormat(
930 "dlopen error: could not read error string: %s",
931 utility_error.AsCString());
932 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
933 }
934
935 size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size,
936 dlopen_error_str,
937 utility_error);
938 if (utility_error.Success() && num_chars > 0)
939 error.SetErrorStringWithFormat("dlopen error: %s",
940 dlopen_error_str.c_str());
941 else
942 error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
943
944 return LLDB_INVALID_IMAGE_TOKEN(4294967295U);
945}
946
947Status PlatformPOSIX::UnloadImage(lldb_private::Process *process,
948 uint32_t image_token) {
949 const addr_t image_addr = process->GetImagePtrFromToken(image_token);
950 if (image_addr == LLDB_INVALID_ADDRESS(18446744073709551615UL))
951 return Status("Invalid image token");
952
953 StreamString expr;
954 expr.Printf("dlclose((void *)0x%" PRIx64"l" "x" ")", image_addr);
955 llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
956 lldb::ValueObjectSP result_valobj_sp;
957 Status error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
958 result_valobj_sp);
959 if (error.Fail())
960 return error;
961
962 if (result_valobj_sp->GetError().Fail())
963 return result_valobj_sp->GetError();
964
965 Scalar scalar;
966 if (result_valobj_sp->ResolveValue(scalar)) {
967 if (scalar.UInt(1))
968 return Status("expression failed: \"%s\"", expr.GetData());
969 process->ResetImageToken(image_token);
970 }
971 return Status();
972}
973
974llvm::StringRef
975PlatformPOSIX::GetLibdlFunctionDeclarations(lldb_private::Process *process) {
976 return R"(
977 extern "C" void* dlopen(const char*, int);
978 extern "C" void* dlsym(void*, const char*);
979 extern "C" int dlclose(void*);
980 extern "C" char* dlerror(void);
981 )";
982}
983
984ConstString PlatformPOSIX::GetFullNameForDylib(ConstString basename) {
985 if (basename.IsEmpty())
986 return basename;
987
988 StreamString stream;
989 stream.Printf("lib%s.so", basename.GetCString());
990 return ConstString(stream.GetString());
991}