Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Core/Value.cpp
39587 views
1
//===-- Value.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 "lldb/Core/Value.h"
10
11
#include "lldb/Core/Address.h"
12
#include "lldb/Core/Module.h"
13
#include "lldb/Symbol/CompilerType.h"
14
#include "lldb/Symbol/ObjectFile.h"
15
#include "lldb/Symbol/SymbolContext.h"
16
#include "lldb/Symbol/Type.h"
17
#include "lldb/Symbol/Variable.h"
18
#include "lldb/Target/ExecutionContext.h"
19
#include "lldb/Target/Process.h"
20
#include "lldb/Target/SectionLoadList.h"
21
#include "lldb/Target/Target.h"
22
#include "lldb/Utility/ConstString.h"
23
#include "lldb/Utility/DataBufferHeap.h"
24
#include "lldb/Utility/DataExtractor.h"
25
#include "lldb/Utility/Endian.h"
26
#include "lldb/Utility/FileSpec.h"
27
#include "lldb/Utility/State.h"
28
#include "lldb/Utility/Stream.h"
29
#include "lldb/lldb-defines.h"
30
#include "lldb/lldb-forward.h"
31
#include "lldb/lldb-types.h"
32
33
#include <memory>
34
#include <optional>
35
#include <string>
36
37
#include <cinttypes>
38
39
using namespace lldb;
40
using namespace lldb_private;
41
42
Value::Value() : m_value(), m_compiler_type(), m_data_buffer() {}
43
44
Value::Value(const Scalar &scalar)
45
: m_value(scalar), m_compiler_type(), m_data_buffer() {}
46
47
Value::Value(const void *bytes, int len)
48
: m_value(), m_compiler_type(), m_value_type(ValueType::HostAddress),
49
m_data_buffer() {
50
SetBytes(bytes, len);
51
}
52
53
Value::Value(const Value &v)
54
: m_value(v.m_value), m_compiler_type(v.m_compiler_type),
55
m_context(v.m_context), m_value_type(v.m_value_type),
56
m_context_type(v.m_context_type), m_data_buffer() {
57
const uintptr_t rhs_value =
58
(uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS);
59
if ((rhs_value != 0) &&
60
(rhs_value == (uintptr_t)v.m_data_buffer.GetBytes())) {
61
m_data_buffer.CopyData(v.m_data_buffer.GetBytes(),
62
v.m_data_buffer.GetByteSize());
63
64
m_value = (uintptr_t)m_data_buffer.GetBytes();
65
}
66
}
67
68
Value &Value::operator=(const Value &rhs) {
69
if (this != &rhs) {
70
m_value = rhs.m_value;
71
m_compiler_type = rhs.m_compiler_type;
72
m_context = rhs.m_context;
73
m_value_type = rhs.m_value_type;
74
m_context_type = rhs.m_context_type;
75
const uintptr_t rhs_value =
76
(uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS);
77
if ((rhs_value != 0) &&
78
(rhs_value == (uintptr_t)rhs.m_data_buffer.GetBytes())) {
79
m_data_buffer.CopyData(rhs.m_data_buffer.GetBytes(),
80
rhs.m_data_buffer.GetByteSize());
81
82
m_value = (uintptr_t)m_data_buffer.GetBytes();
83
}
84
}
85
return *this;
86
}
87
88
void Value::SetBytes(const void *bytes, int len) {
89
m_value_type = ValueType::HostAddress;
90
m_data_buffer.CopyData(bytes, len);
91
m_value = (uintptr_t)m_data_buffer.GetBytes();
92
}
93
94
void Value::AppendBytes(const void *bytes, int len) {
95
m_value_type = ValueType::HostAddress;
96
m_data_buffer.AppendData(bytes, len);
97
m_value = (uintptr_t)m_data_buffer.GetBytes();
98
}
99
100
void Value::Dump(Stream *strm) {
101
if (!strm)
102
return;
103
m_value.GetValue(*strm, true);
104
strm->Printf(", value_type = %s, context = %p, context_type = %s",
105
Value::GetValueTypeAsCString(m_value_type), m_context,
106
Value::GetContextTypeAsCString(m_context_type));
107
}
108
109
Value::ValueType Value::GetValueType() const { return m_value_type; }
110
111
AddressType Value::GetValueAddressType() const {
112
switch (m_value_type) {
113
case ValueType::Invalid:
114
case ValueType::Scalar:
115
break;
116
case ValueType::LoadAddress:
117
return eAddressTypeLoad;
118
case ValueType::FileAddress:
119
return eAddressTypeFile;
120
case ValueType::HostAddress:
121
return eAddressTypeHost;
122
}
123
return eAddressTypeInvalid;
124
}
125
126
Value::ValueType Value::GetValueTypeFromAddressType(AddressType address_type) {
127
switch (address_type) {
128
case eAddressTypeFile:
129
return Value::ValueType::FileAddress;
130
case eAddressTypeLoad:
131
return Value::ValueType::LoadAddress;
132
case eAddressTypeHost:
133
return Value::ValueType::HostAddress;
134
case eAddressTypeInvalid:
135
return Value::ValueType::Invalid;
136
}
137
llvm_unreachable("Unexpected address type!");
138
}
139
140
RegisterInfo *Value::GetRegisterInfo() const {
141
if (m_context_type == ContextType::RegisterInfo)
142
return static_cast<RegisterInfo *>(m_context);
143
return nullptr;
144
}
145
146
Type *Value::GetType() {
147
if (m_context_type == ContextType::LLDBType)
148
return static_cast<Type *>(m_context);
149
return nullptr;
150
}
151
152
size_t Value::AppendDataToHostBuffer(const Value &rhs) {
153
if (this == &rhs)
154
return 0;
155
156
size_t curr_size = m_data_buffer.GetByteSize();
157
Status error;
158
switch (rhs.GetValueType()) {
159
case ValueType::Invalid:
160
return 0;
161
case ValueType::Scalar: {
162
const size_t scalar_size = rhs.m_value.GetByteSize();
163
if (scalar_size > 0) {
164
const size_t new_size = curr_size + scalar_size;
165
if (ResizeData(new_size) == new_size) {
166
rhs.m_value.GetAsMemoryData(m_data_buffer.GetBytes() + curr_size,
167
scalar_size, endian::InlHostByteOrder(),
168
error);
169
return scalar_size;
170
}
171
}
172
} break;
173
case ValueType::FileAddress:
174
case ValueType::LoadAddress:
175
case ValueType::HostAddress: {
176
const uint8_t *src = rhs.GetBuffer().GetBytes();
177
const size_t src_len = rhs.GetBuffer().GetByteSize();
178
if (src && src_len > 0) {
179
const size_t new_size = curr_size + src_len;
180
if (ResizeData(new_size) == new_size) {
181
::memcpy(m_data_buffer.GetBytes() + curr_size, src, src_len);
182
return src_len;
183
}
184
}
185
} break;
186
}
187
return 0;
188
}
189
190
size_t Value::ResizeData(size_t len) {
191
m_value_type = ValueType::HostAddress;
192
m_data_buffer.SetByteSize(len);
193
m_value = (uintptr_t)m_data_buffer.GetBytes();
194
return m_data_buffer.GetByteSize();
195
}
196
197
bool Value::ValueOf(ExecutionContext *exe_ctx) {
198
switch (m_context_type) {
199
case ContextType::Invalid:
200
case ContextType::RegisterInfo: // RegisterInfo *
201
case ContextType::LLDBType: // Type *
202
break;
203
204
case ContextType::Variable: // Variable *
205
ResolveValue(exe_ctx);
206
return true;
207
}
208
return false;
209
}
210
211
uint64_t Value::GetValueByteSize(Status *error_ptr, ExecutionContext *exe_ctx) {
212
switch (m_context_type) {
213
case ContextType::RegisterInfo: // RegisterInfo *
214
if (GetRegisterInfo()) {
215
if (error_ptr)
216
error_ptr->Clear();
217
return GetRegisterInfo()->byte_size;
218
}
219
break;
220
221
case ContextType::Invalid:
222
case ContextType::LLDBType: // Type *
223
case ContextType::Variable: // Variable *
224
{
225
auto *scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr;
226
if (std::optional<uint64_t> size = GetCompilerType().GetByteSize(scope)) {
227
if (error_ptr)
228
error_ptr->Clear();
229
return *size;
230
}
231
break;
232
}
233
}
234
if (error_ptr && error_ptr->Success())
235
error_ptr->SetErrorString("Unable to determine byte size.");
236
return 0;
237
}
238
239
const CompilerType &Value::GetCompilerType() {
240
if (!m_compiler_type.IsValid()) {
241
switch (m_context_type) {
242
case ContextType::Invalid:
243
break;
244
245
case ContextType::RegisterInfo:
246
break; // TODO: Eventually convert into a compiler type?
247
248
case ContextType::LLDBType: {
249
Type *lldb_type = GetType();
250
if (lldb_type)
251
m_compiler_type = lldb_type->GetForwardCompilerType();
252
} break;
253
254
case ContextType::Variable: {
255
Variable *variable = GetVariable();
256
if (variable) {
257
Type *variable_type = variable->GetType();
258
if (variable_type)
259
m_compiler_type = variable_type->GetForwardCompilerType();
260
}
261
} break;
262
}
263
}
264
265
return m_compiler_type;
266
}
267
268
void Value::SetCompilerType(const CompilerType &compiler_type) {
269
m_compiler_type = compiler_type;
270
}
271
272
lldb::Format Value::GetValueDefaultFormat() {
273
switch (m_context_type) {
274
case ContextType::RegisterInfo:
275
if (GetRegisterInfo())
276
return GetRegisterInfo()->format;
277
break;
278
279
case ContextType::Invalid:
280
case ContextType::LLDBType:
281
case ContextType::Variable: {
282
const CompilerType &ast_type = GetCompilerType();
283
if (ast_type.IsValid())
284
return ast_type.GetFormat();
285
} break;
286
}
287
288
// Return a good default in case we can't figure anything out
289
return eFormatHex;
290
}
291
292
bool Value::GetData(DataExtractor &data) {
293
switch (m_value_type) {
294
case ValueType::Invalid:
295
return false;
296
case ValueType::Scalar:
297
if (m_value.GetData(data))
298
return true;
299
break;
300
301
case ValueType::LoadAddress:
302
case ValueType::FileAddress:
303
case ValueType::HostAddress:
304
if (m_data_buffer.GetByteSize()) {
305
data.SetData(m_data_buffer.GetBytes(), m_data_buffer.GetByteSize(),
306
data.GetByteOrder());
307
return true;
308
}
309
break;
310
}
311
312
return false;
313
}
314
315
Status Value::GetValueAsData(ExecutionContext *exe_ctx, DataExtractor &data,
316
Module *module) {
317
data.Clear();
318
319
Status error;
320
lldb::addr_t address = LLDB_INVALID_ADDRESS;
321
AddressType address_type = eAddressTypeFile;
322
Address file_so_addr;
323
const CompilerType &ast_type = GetCompilerType();
324
std::optional<uint64_t> type_size = ast_type.GetByteSize(
325
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr);
326
// Nothing to be done for a zero-sized type.
327
if (type_size && *type_size == 0)
328
return error;
329
330
switch (m_value_type) {
331
case ValueType::Invalid:
332
error.SetErrorString("invalid value");
333
break;
334
case ValueType::Scalar: {
335
data.SetByteOrder(endian::InlHostByteOrder());
336
if (ast_type.IsValid())
337
data.SetAddressByteSize(ast_type.GetPointerByteSize());
338
else
339
data.SetAddressByteSize(sizeof(void *));
340
341
uint32_t limit_byte_size = UINT32_MAX;
342
343
if (type_size)
344
limit_byte_size = *type_size;
345
346
if (limit_byte_size <= m_value.GetByteSize()) {
347
if (m_value.GetData(data, limit_byte_size))
348
return error; // Success;
349
}
350
351
error.SetErrorString("extracting data from value failed");
352
break;
353
}
354
case ValueType::LoadAddress:
355
if (exe_ctx == nullptr) {
356
error.SetErrorString("can't read load address (no execution context)");
357
} else {
358
Process *process = exe_ctx->GetProcessPtr();
359
if (process == nullptr || !process->IsAlive()) {
360
Target *target = exe_ctx->GetTargetPtr();
361
if (target) {
362
// Allow expressions to run and evaluate things when the target has
363
// memory sections loaded. This allows you to use "target modules
364
// load" to load your executable and any shared libraries, then
365
// execute commands where you can look at types in data sections.
366
const SectionLoadList &target_sections = target->GetSectionLoadList();
367
if (!target_sections.IsEmpty()) {
368
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
369
if (target_sections.ResolveLoadAddress(address, file_so_addr)) {
370
address_type = eAddressTypeLoad;
371
data.SetByteOrder(target->GetArchitecture().GetByteOrder());
372
data.SetAddressByteSize(
373
target->GetArchitecture().GetAddressByteSize());
374
} else
375
address = LLDB_INVALID_ADDRESS;
376
}
377
} else {
378
error.SetErrorString("can't read load address (invalid process)");
379
}
380
} else {
381
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
382
address_type = eAddressTypeLoad;
383
data.SetByteOrder(
384
process->GetTarget().GetArchitecture().GetByteOrder());
385
data.SetAddressByteSize(
386
process->GetTarget().GetArchitecture().GetAddressByteSize());
387
}
388
}
389
break;
390
391
case ValueType::FileAddress:
392
if (exe_ctx == nullptr) {
393
error.SetErrorString("can't read file address (no execution context)");
394
} else if (exe_ctx->GetTargetPtr() == nullptr) {
395
error.SetErrorString("can't read file address (invalid target)");
396
} else {
397
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
398
if (address == LLDB_INVALID_ADDRESS) {
399
error.SetErrorString("invalid file address");
400
} else {
401
if (module == nullptr) {
402
// The only thing we can currently lock down to a module so that we
403
// can resolve a file address, is a variable.
404
Variable *variable = GetVariable();
405
if (variable) {
406
SymbolContext var_sc;
407
variable->CalculateSymbolContext(&var_sc);
408
module = var_sc.module_sp.get();
409
}
410
}
411
412
if (module) {
413
bool resolved = false;
414
ObjectFile *objfile = module->GetObjectFile();
415
if (objfile) {
416
Address so_addr(address, objfile->GetSectionList());
417
addr_t load_address =
418
so_addr.GetLoadAddress(exe_ctx->GetTargetPtr());
419
bool process_launched_and_stopped =
420
exe_ctx->GetProcessPtr()
421
? StateIsStoppedState(exe_ctx->GetProcessPtr()->GetState(),
422
true /* must_exist */)
423
: false;
424
// Don't use the load address if the process has exited.
425
if (load_address != LLDB_INVALID_ADDRESS &&
426
process_launched_and_stopped) {
427
resolved = true;
428
address = load_address;
429
address_type = eAddressTypeLoad;
430
data.SetByteOrder(
431
exe_ctx->GetTargetRef().GetArchitecture().GetByteOrder());
432
data.SetAddressByteSize(exe_ctx->GetTargetRef()
433
.GetArchitecture()
434
.GetAddressByteSize());
435
} else {
436
if (so_addr.IsSectionOffset()) {
437
resolved = true;
438
file_so_addr = so_addr;
439
data.SetByteOrder(objfile->GetByteOrder());
440
data.SetAddressByteSize(objfile->GetAddressByteSize());
441
}
442
}
443
}
444
if (!resolved) {
445
Variable *variable = GetVariable();
446
447
if (module) {
448
if (variable)
449
error.SetErrorStringWithFormat(
450
"unable to resolve the module for file address 0x%" PRIx64
451
" for variable '%s' in %s",
452
address, variable->GetName().AsCString(""),
453
module->GetFileSpec().GetPath().c_str());
454
else
455
error.SetErrorStringWithFormat(
456
"unable to resolve the module for file address 0x%" PRIx64
457
" in %s",
458
address, module->GetFileSpec().GetPath().c_str());
459
} else {
460
if (variable)
461
error.SetErrorStringWithFormat(
462
"unable to resolve the module for file address 0x%" PRIx64
463
" for variable '%s'",
464
address, variable->GetName().AsCString(""));
465
else
466
error.SetErrorStringWithFormat(
467
"unable to resolve the module for file address 0x%" PRIx64,
468
address);
469
}
470
}
471
} else {
472
// Can't convert a file address to anything valid without more
473
// context (which Module it came from)
474
error.SetErrorString(
475
"can't read memory from file address without more context");
476
}
477
}
478
}
479
break;
480
481
case ValueType::HostAddress:
482
address = m_value.ULongLong(LLDB_INVALID_ADDRESS);
483
address_type = eAddressTypeHost;
484
if (exe_ctx) {
485
Target *target = exe_ctx->GetTargetPtr();
486
if (target) {
487
data.SetByteOrder(target->GetArchitecture().GetByteOrder());
488
data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize());
489
break;
490
}
491
}
492
// fallback to host settings
493
data.SetByteOrder(endian::InlHostByteOrder());
494
data.SetAddressByteSize(sizeof(void *));
495
break;
496
}
497
498
// Bail if we encountered any errors
499
if (error.Fail())
500
return error;
501
502
if (address == LLDB_INVALID_ADDRESS) {
503
error.SetErrorStringWithFormat("invalid %s address",
504
address_type == eAddressTypeHost ? "host"
505
: "load");
506
return error;
507
}
508
509
// If we got here, we need to read the value from memory.
510
size_t byte_size = GetValueByteSize(&error, exe_ctx);
511
512
// Bail if we encountered any errors getting the byte size.
513
if (error.Fail())
514
return error;
515
516
// No memory to read for zero-sized types.
517
if (byte_size == 0)
518
return error;
519
520
// Make sure we have enough room within "data", and if we don't make
521
// something large enough that does
522
if (!data.ValidOffsetForDataOfSize(0, byte_size)) {
523
auto data_sp = std::make_shared<DataBufferHeap>(byte_size, '\0');
524
data.SetData(data_sp);
525
}
526
527
uint8_t *dst = const_cast<uint8_t *>(data.PeekData(0, byte_size));
528
if (dst != nullptr) {
529
if (address_type == eAddressTypeHost) {
530
// The address is an address in this process, so just copy it.
531
if (address == 0) {
532
error.SetErrorString("trying to read from host address of 0.");
533
return error;
534
}
535
memcpy(dst, reinterpret_cast<uint8_t *>(address), byte_size);
536
} else if ((address_type == eAddressTypeLoad) ||
537
(address_type == eAddressTypeFile)) {
538
if (file_so_addr.IsValid()) {
539
const bool force_live_memory = true;
540
if (exe_ctx->GetTargetRef().ReadMemory(file_so_addr, dst, byte_size,
541
error, force_live_memory) !=
542
byte_size) {
543
error.SetErrorStringWithFormat(
544
"read memory from 0x%" PRIx64 " failed", (uint64_t)address);
545
}
546
} else {
547
// The execution context might have a NULL process, but it might have a
548
// valid process in the exe_ctx->target, so use the
549
// ExecutionContext::GetProcess accessor to ensure we get the process
550
// if there is one.
551
Process *process = exe_ctx->GetProcessPtr();
552
553
if (process) {
554
const size_t bytes_read =
555
process->ReadMemory(address, dst, byte_size, error);
556
if (bytes_read != byte_size)
557
error.SetErrorStringWithFormat(
558
"read memory from 0x%" PRIx64 " failed (%u of %u bytes read)",
559
(uint64_t)address, (uint32_t)bytes_read, (uint32_t)byte_size);
560
} else {
561
error.SetErrorStringWithFormat("read memory from 0x%" PRIx64
562
" failed (invalid process)",
563
(uint64_t)address);
564
}
565
}
566
} else {
567
error.SetErrorStringWithFormat("unsupported AddressType value (%i)",
568
address_type);
569
}
570
} else {
571
error.SetErrorString("out of memory");
572
}
573
574
return error;
575
}
576
577
Scalar &Value::ResolveValue(ExecutionContext *exe_ctx, Module *module) {
578
const CompilerType &compiler_type = GetCompilerType();
579
if (compiler_type.IsValid()) {
580
switch (m_value_type) {
581
case ValueType::Invalid:
582
case ValueType::Scalar: // raw scalar value
583
break;
584
585
case ValueType::FileAddress:
586
case ValueType::LoadAddress: // load address value
587
case ValueType::HostAddress: // host address value (for memory in the process
588
// that is using liblldb)
589
{
590
DataExtractor data;
591
lldb::addr_t addr = m_value.ULongLong(LLDB_INVALID_ADDRESS);
592
Status error(GetValueAsData(exe_ctx, data, module));
593
if (error.Success()) {
594
Scalar scalar;
595
if (compiler_type.GetValueAsScalar(
596
data, 0, data.GetByteSize(), scalar,
597
exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr)) {
598
m_value = scalar;
599
m_value_type = ValueType::Scalar;
600
} else {
601
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
602
m_value.Clear();
603
m_value_type = ValueType::Scalar;
604
}
605
}
606
} else {
607
if ((uintptr_t)addr != (uintptr_t)m_data_buffer.GetBytes()) {
608
m_value.Clear();
609
m_value_type = ValueType::Scalar;
610
}
611
}
612
} break;
613
}
614
}
615
return m_value;
616
}
617
618
Variable *Value::GetVariable() {
619
if (m_context_type == ContextType::Variable)
620
return static_cast<Variable *>(m_context);
621
return nullptr;
622
}
623
624
void Value::Clear() {
625
m_value.Clear();
626
m_compiler_type.Clear();
627
m_value_type = ValueType::Scalar;
628
m_context = nullptr;
629
m_context_type = ContextType::Invalid;
630
m_data_buffer.Clear();
631
}
632
633
const char *Value::GetValueTypeAsCString(ValueType value_type) {
634
switch (value_type) {
635
case ValueType::Invalid:
636
return "invalid";
637
case ValueType::Scalar:
638
return "scalar";
639
case ValueType::FileAddress:
640
return "file address";
641
case ValueType::LoadAddress:
642
return "load address";
643
case ValueType::HostAddress:
644
return "host address";
645
};
646
llvm_unreachable("enum cases exhausted.");
647
}
648
649
const char *Value::GetContextTypeAsCString(ContextType context_type) {
650
switch (context_type) {
651
case ContextType::Invalid:
652
return "invalid";
653
case ContextType::RegisterInfo:
654
return "RegisterInfo *";
655
case ContextType::LLDBType:
656
return "Type *";
657
case ContextType::Variable:
658
return "Variable *";
659
};
660
llvm_unreachable("enum cases exhausted.");
661
}
662
663
void Value::ConvertToLoadAddress(Module *module, Target *target) {
664
if (!module || !target || (GetValueType() != ValueType::FileAddress))
665
return;
666
667
lldb::addr_t file_addr = GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
668
if (file_addr == LLDB_INVALID_ADDRESS)
669
return;
670
671
Address so_addr;
672
if (!module->ResolveFileAddress(file_addr, so_addr))
673
return;
674
lldb::addr_t load_addr = so_addr.GetLoadAddress(target);
675
if (load_addr == LLDB_INVALID_ADDRESS)
676
return;
677
678
SetValueType(Value::ValueType::LoadAddress);
679
GetScalar() = load_addr;
680
}
681
682
void ValueList::PushValue(const Value &value) { m_values.push_back(value); }
683
684
size_t ValueList::GetSize() { return m_values.size(); }
685
686
Value *ValueList::GetValueAtIndex(size_t idx) {
687
if (idx < GetSize()) {
688
return &(m_values[idx]);
689
} else
690
return nullptr;
691
}
692
693
void ValueList::Clear() { m_values.clear(); }
694
695