1 //===-- Address.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 "lldb/Core/Address.h" 11 #include "lldb/Core/Module.h" 12 #include "lldb/Core/Section.h" 13 #include "lldb/Symbol/ObjectFile.h" 14 #include "lldb/Target/ExecutionContext.h" 15 #include "lldb/Target/Process.h" 16 #include "lldb/Target/Target.h" 17 18 using namespace lldb; 19 using namespace lldb_private; 20 21 static size_t 22 ReadBytes (ExecutionContextScope *exe_scope, const Address &address, void *dst, size_t dst_len) 23 { 24 if (exe_scope == NULL) 25 return 0; 26 27 Target *target = exe_scope->CalculateTarget(); 28 if (target) 29 { 30 Error error; 31 bool prefer_file_cache = false; 32 return target->ReadMemory (address, prefer_file_cache, dst, dst_len, error); 33 } 34 return 0; 35 } 36 37 static bool 38 GetByteOrderAndAddressSize (ExecutionContextScope *exe_scope, const Address &address, ByteOrder& byte_order, uint32_t& addr_size) 39 { 40 byte_order = eByteOrderInvalid; 41 addr_size = 0; 42 if (exe_scope == NULL) 43 return false; 44 45 Process *process = exe_scope->CalculateProcess(); 46 if (process) 47 { 48 byte_order = process->GetByteOrder(); 49 addr_size = process->GetAddressByteSize(); 50 } 51 52 if (byte_order == eByteOrderInvalid || addr_size == 0) 53 { 54 Module *module = address.GetModule(); 55 if (module) 56 { 57 byte_order = module->GetArchitecture().GetDefaultEndian(); 58 addr_size = module->GetArchitecture().GetAddressByteSize(); 59 } 60 } 61 return byte_order != eByteOrderInvalid && addr_size != 0; 62 } 63 64 static uint64_t 65 ReadUIntMax64 (ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, bool &success) 66 { 67 uint64_t uval64 = 0; 68 if (exe_scope == NULL || byte_size > sizeof(uint64_t)) 69 { 70 success = false; 71 return 0; 72 } 73 uint64_t buf; 74 75 success = ReadBytes (exe_scope, address, &buf, byte_size) == byte_size; 76 if (success) 77 { 78 ByteOrder byte_order = eByteOrderInvalid; 79 uint32_t addr_size = 0; 80 if (GetByteOrderAndAddressSize (exe_scope, address, byte_order, addr_size)) 81 { 82 DataExtractor data (&buf, sizeof(buf), byte_order, addr_size); 83 uint32_t offset = 0; 84 uval64 = data.GetU64(&offset); 85 } 86 else 87 success = false; 88 } 89 return uval64; 90 } 91 92 static bool 93 ReadAddress (ExecutionContextScope *exe_scope, const Address &address, uint32_t pointer_size, Address &deref_so_addr) 94 { 95 if (exe_scope == NULL) 96 return false; 97 98 99 bool success = false; 100 addr_t deref_addr = ReadUIntMax64 (exe_scope, address, pointer_size, success); 101 if (success) 102 { 103 ExecutionContext exe_ctx; 104 exe_scope->CalculateExecutionContext(exe_ctx); 105 // If we have any sections that are loaded, try and resolve using the 106 // section load list 107 if (exe_ctx.target && !exe_ctx.target->GetSectionLoadList().IsEmpty()) 108 { 109 if (exe_ctx.target->GetSectionLoadList().ResolveLoadAddress (deref_addr, deref_so_addr)) 110 return true; 111 } 112 else 113 { 114 // If we were not running, yet able to read an integer, we must 115 // have a module 116 Module *module = address.GetModule(); 117 assert (module); 118 if (module->ResolveFileAddress(deref_addr, deref_so_addr)) 119 return true; 120 } 121 122 // We couldn't make "deref_addr" into a section offset value, but we were 123 // able to read the address, so we return a section offset address with 124 // no section and "deref_addr" as the offset (address). 125 deref_so_addr.SetSection(NULL); 126 deref_so_addr.SetOffset(deref_addr); 127 return true; 128 } 129 return false; 130 } 131 132 static bool 133 DumpUInt (ExecutionContextScope *exe_scope, const Address &address, uint32_t byte_size, Stream* strm) 134 { 135 if (exe_scope == NULL || byte_size == 0) 136 return 0; 137 std::vector<uint8_t> buf(byte_size, 0); 138 139 if (ReadBytes (exe_scope, address, &buf[0], buf.size()) == buf.size()) 140 { 141 ByteOrder byte_order = eByteOrderInvalid; 142 uint32_t addr_size = 0; 143 if (GetByteOrderAndAddressSize (exe_scope, address, byte_order, addr_size)) 144 { 145 DataExtractor data (&buf.front(), buf.size(), byte_order, addr_size); 146 147 data.Dump (strm, 148 0, // Start offset in "data" 149 eFormatHex, // Print as characters 150 buf.size(), // Size of item 151 1, // Items count 152 UINT32_MAX, // num per line 153 LLDB_INVALID_ADDRESS,// base address 154 0, // bitfield bit size 155 0); // bitfield bit offset 156 157 return true; 158 } 159 } 160 return false; 161 } 162 163 164 static size_t 165 ReadCStringFromMemory (ExecutionContextScope *exe_scope, const Address &address, Stream *strm) 166 { 167 if (exe_scope == NULL) 168 return 0; 169 const size_t k_buf_len = 256; 170 char buf[k_buf_len+1]; 171 buf[k_buf_len] = '\0'; // NULL terminate 172 173 // Byte order and address size don't matter for C string dumping.. 174 DataExtractor data (buf, sizeof(buf), eByteOrderHost, 4); 175 size_t total_len = 0; 176 size_t bytes_read; 177 Address curr_address(address); 178 strm->PutChar ('"'); 179 while ((bytes_read = ReadBytes (exe_scope, curr_address, buf, k_buf_len)) > 0) 180 { 181 size_t len = strlen(buf); 182 if (len == 0) 183 break; 184 if (len > bytes_read) 185 len = bytes_read; 186 187 data.Dump (strm, 188 0, // Start offset in "data" 189 eFormatChar, // Print as characters 190 1, // Size of item (1 byte for a char!) 191 len, // How many bytes to print? 192 UINT32_MAX, // num per line 193 LLDB_INVALID_ADDRESS,// base address 194 0, // bitfield bit size 195 196 0); // bitfield bit offset 197 198 total_len += bytes_read; 199 200 if (len < k_buf_len) 201 break; 202 curr_address.SetOffset (curr_address.GetOffset() + bytes_read); 203 } 204 strm->PutChar ('"'); 205 return total_len; 206 } 207 208 Address::Address (addr_t address, const SectionList * sections) : 209 m_section (NULL), 210 m_offset (LLDB_INVALID_ADDRESS) 211 { 212 ResolveAddressUsingFileSections(address, sections); 213 } 214 215 const Address& 216 Address::operator= (const Address& rhs) 217 { 218 if (this != &rhs) 219 { 220 m_section = rhs.m_section; 221 m_offset = rhs.m_offset; 222 } 223 return *this; 224 } 225 226 bool 227 Address::ResolveAddressUsingFileSections (addr_t addr, const SectionList *sections) 228 { 229 if (sections) 230 m_section = sections->FindSectionContainingFileAddress(addr).get(); 231 else 232 m_section = NULL; 233 234 if (m_section != NULL) 235 { 236 assert( m_section->ContainsFileAddress(addr) ); 237 m_offset = addr - m_section->GetFileAddress(); 238 return true; // Successfully transformed addr into a section offset address 239 } 240 241 m_offset = addr; 242 return false; // Failed to resolve this address to a section offset value 243 } 244 245 Module * 246 Address::GetModule () const 247 { 248 if (m_section) 249 return m_section->GetModule(); 250 return NULL; 251 } 252 253 addr_t 254 Address::GetFileAddress () const 255 { 256 if (m_section != NULL) 257 { 258 addr_t sect_file_addr = m_section->GetFileAddress(); 259 if (sect_file_addr == LLDB_INVALID_ADDRESS) 260 { 261 // Section isn't resolved, we can't return a valid file address 262 return LLDB_INVALID_ADDRESS; 263 } 264 // We have a valid file range, so we can return the file based 265 // address by adding the file base address to our offset 266 return sect_file_addr + m_offset; 267 } 268 // No section, we just return the offset since it is the value in this case 269 return m_offset; 270 } 271 272 addr_t 273 Address::GetLoadAddress (Target *target) const 274 { 275 if (m_section == NULL) 276 { 277 // No section, we just return the offset since it is the value in this case 278 return m_offset; 279 } 280 281 if (target) 282 { 283 addr_t sect_load_addr = m_section->GetLoadBaseAddress (target); 284 285 if (sect_load_addr != LLDB_INVALID_ADDRESS) 286 { 287 // We have a valid file range, so we can return the file based 288 // address by adding the file base address to our offset 289 return sect_load_addr + m_offset; 290 } 291 } 292 // The section isn't resolved or no process was supplied so we can't 293 // return a valid file address. 294 return LLDB_INVALID_ADDRESS; 295 } 296 297 bool 298 Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style, uint32_t addr_size) const 299 { 300 // If the section was NULL, only load address is going to work. 301 if (m_section == NULL) 302 style = DumpStyleLoadAddress; 303 304 Target *target = NULL; 305 Process *process = NULL; 306 if (exe_scope) 307 { 308 target = exe_scope->CalculateTarget(); 309 process = exe_scope->CalculateProcess(); 310 } 311 // If addr_byte_size is UINT32_MAX, then determine the correct address 312 // byte size for the process or default to the size of addr_t 313 if (addr_size == UINT32_MAX) 314 { 315 if (process) 316 addr_size = process->GetAddressByteSize (); 317 else 318 addr_size = sizeof(addr_t); 319 } 320 321 Address so_addr; 322 switch (style) 323 { 324 case DumpStyleInvalid: 325 return false; 326 327 case DumpStyleSectionNameOffset: 328 if (m_section != NULL) 329 { 330 m_section->DumpName(s); 331 s->Printf (" + %llu", m_offset); 332 } 333 else 334 { 335 s->Address(m_offset, addr_size); 336 } 337 break; 338 339 case DumpStyleSectionPointerOffset: 340 s->Printf("(Section *)%.*p + ", (int)sizeof(void*) * 2, m_section); 341 s->Address(m_offset, addr_size); 342 break; 343 344 case DumpStyleModuleWithFileAddress: 345 if (m_section) 346 s->Printf("%s[", m_section->GetModule()->GetFileSpec().GetFilename().AsCString()); 347 // Fall through 348 case DumpStyleFileAddress: 349 { 350 addr_t file_addr = GetFileAddress(); 351 if (file_addr == LLDB_INVALID_ADDRESS) 352 { 353 if (fallback_style != DumpStyleInvalid) 354 return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); 355 return false; 356 } 357 s->Address (file_addr, addr_size); 358 if (style == DumpStyleModuleWithFileAddress && m_section) 359 s->PutChar(']'); 360 } 361 break; 362 363 case DumpStyleLoadAddress: 364 { 365 addr_t load_addr = GetLoadAddress (target); 366 if (load_addr == LLDB_INVALID_ADDRESS) 367 { 368 if (fallback_style != DumpStyleInvalid) 369 return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); 370 return false; 371 } 372 s->Address (load_addr, addr_size); 373 } 374 break; 375 376 case DumpStyleResolvedDescription: 377 case DumpStyleResolvedDescriptionNoModule: 378 if (IsSectionOffset()) 379 { 380 lldb::AddressType addr_type = eAddressTypeLoad; 381 addr_t addr = GetLoadAddress (target); 382 if (addr == LLDB_INVALID_ADDRESS) 383 { 384 addr = GetFileAddress(); 385 addr_type = eAddressTypeFile; 386 } 387 388 uint32_t pointer_size = 4; 389 Module *module = GetModule(); 390 if (process) 391 pointer_size = process->GetAddressByteSize(); 392 else if (module) 393 pointer_size = module->GetArchitecture().GetAddressByteSize(); 394 395 bool showed_info = false; 396 const Section *section = GetSection(); 397 if (section) 398 { 399 SectionType sect_type = section->GetType(); 400 switch (sect_type) 401 { 402 case eSectionTypeData: 403 if (module) 404 { 405 ObjectFile *objfile = module->GetObjectFile(); 406 if (objfile) 407 { 408 Symtab *symtab = objfile->GetSymtab(); 409 if (symtab) 410 { 411 const addr_t file_Addr = GetFileAddress(); 412 Symbol *symbol = symtab->FindSymbolContainingFileAddress (file_Addr); 413 if (symbol) 414 { 415 const char *symbol_name = symbol->GetName().AsCString(); 416 if (symbol_name) 417 { 418 s->PutCString(symbol_name); 419 addr_t delta = file_Addr - symbol->GetAddressRangePtr()->GetBaseAddress().GetFileAddress(); 420 if (delta) 421 s->Printf(" + %llu", delta); 422 showed_info = true; 423 } 424 } 425 } 426 } 427 } 428 break; 429 430 case eSectionTypeDataCString: 431 // Read the C string from memory and display it 432 showed_info = true; 433 ReadCStringFromMemory (exe_scope, *this, s); 434 break; 435 436 case eSectionTypeDataCStringPointers: 437 { 438 if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) 439 { 440 #if VERBOSE_OUTPUT 441 s->PutCString("(char *)"); 442 so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); 443 s->PutCString(": "); 444 #endif 445 showed_info = true; 446 ReadCStringFromMemory (exe_scope, so_addr, s); 447 } 448 } 449 break; 450 451 case eSectionTypeDataObjCMessageRefs: 452 { 453 if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) 454 { 455 if (target && so_addr.IsSectionOffset()) 456 { 457 SymbolContext func_sc; 458 target->GetImages().ResolveSymbolContextForAddress (so_addr, 459 eSymbolContextEverything, 460 func_sc); 461 if (func_sc.function || func_sc.symbol) 462 { 463 showed_info = true; 464 #if VERBOSE_OUTPUT 465 s->PutCString ("(objc_msgref *) -> { (func*)"); 466 so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); 467 #else 468 s->PutCString ("{ "); 469 #endif 470 Address cstr_addr(*this); 471 cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); 472 func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false); 473 if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) 474 { 475 #if VERBOSE_OUTPUT 476 s->PutCString("), (char *)"); 477 so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); 478 s->PutCString(" ("); 479 #else 480 s->PutCString(", "); 481 #endif 482 ReadCStringFromMemory (exe_scope, so_addr, s); 483 } 484 #if VERBOSE_OUTPUT 485 s->PutCString(") }"); 486 #else 487 s->PutCString(" }"); 488 #endif 489 } 490 } 491 } 492 } 493 break; 494 495 case eSectionTypeDataObjCCFStrings: 496 { 497 Address cfstring_data_addr(*this); 498 cfstring_data_addr.SetOffset(cfstring_data_addr.GetOffset() + (2 * pointer_size)); 499 if (ReadAddress (exe_scope, cfstring_data_addr, pointer_size, so_addr)) 500 { 501 #if VERBOSE_OUTPUT 502 s->PutCString("(CFString *) "); 503 cfstring_data_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); 504 s->PutCString(" -> @"); 505 #else 506 s->PutChar('@'); 507 #endif 508 if (so_addr.Dump(s, exe_scope, DumpStyleResolvedDescription)) 509 showed_info = true; 510 } 511 } 512 break; 513 514 case eSectionTypeData4: 515 // Read the 4 byte data and display it 516 showed_info = true; 517 s->PutCString("(uint32_t) "); 518 DumpUInt (exe_scope, *this, 4, s); 519 break; 520 521 case eSectionTypeData8: 522 // Read the 8 byte data and display it 523 showed_info = true; 524 s->PutCString("(uint64_t) "); 525 DumpUInt (exe_scope, *this, 8, s); 526 break; 527 528 case eSectionTypeData16: 529 // Read the 16 byte data and display it 530 showed_info = true; 531 s->PutCString("(uint128_t) "); 532 DumpUInt (exe_scope, *this, 16, s); 533 break; 534 535 case eSectionTypeDataPointers: 536 // Read the pointer data and display it 537 { 538 if (ReadAddress (exe_scope, *this, pointer_size, so_addr)) 539 { 540 s->PutCString ("(void *)"); 541 so_addr.Dump(s, exe_scope, DumpStyleLoadAddress, DumpStyleFileAddress); 542 543 showed_info = true; 544 if (so_addr.IsSectionOffset()) 545 { 546 SymbolContext pointer_sc; 547 if (target) 548 { 549 target->GetImages().ResolveSymbolContextForAddress (so_addr, 550 eSymbolContextEverything, 551 pointer_sc); 552 if (pointer_sc.function || pointer_sc.symbol) 553 { 554 s->PutCString(": "); 555 pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); 556 } 557 } 558 } 559 } 560 } 561 break; 562 563 default: 564 break; 565 } 566 } 567 568 if (!showed_info) 569 { 570 if (module) 571 { 572 SymbolContext sc; 573 module->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); 574 if (sc.function || sc.symbol) 575 { 576 bool show_stop_context = true; 577 const bool show_module = (style == DumpStyleResolvedDescription); 578 const bool show_fullpaths = false; 579 const bool show_inlined_frames = false; 580 if (sc.function == NULL && sc.symbol != NULL) 581 { 582 // If we have just a symbol make sure it is in the right section 583 if (sc.symbol->GetAddressRangePtr()) 584 { 585 if (sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetSection() != GetSection()) 586 { 587 // don't show the module if the symbol is a trampoline symbol 588 show_stop_context = false; 589 } 590 } 591 } 592 if (show_stop_context) 593 { 594 // We have a function or a symbol from the same 595 // sections as this address. 596 sc.DumpStopContext (s, 597 exe_scope, 598 *this, 599 show_fullpaths, 600 show_module, 601 show_inlined_frames); 602 } 603 else 604 { 605 // We found a symbol but it was in a different 606 // section so it isn't the symbol we should be 607 // showing, just show the section name + offset 608 Dump (s, exe_scope, DumpStyleSectionNameOffset); 609 } 610 } 611 } 612 } 613 } 614 else 615 { 616 if (fallback_style != DumpStyleInvalid) 617 return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); 618 return false; 619 } 620 break; 621 622 case DumpStyleDetailedSymbolContext: 623 if (IsSectionOffset()) 624 { 625 Module *module = GetModule(); 626 if (module) 627 { 628 SymbolContext sc; 629 module->ResolveSymbolContextForAddress(*this, eSymbolContextEverything, sc); 630 if (sc.symbol) 631 { 632 // If we have just a symbol make sure it is in the same section 633 // as our address. If it isn't, then we might have just found 634 // the last symbol that came before the address that we are 635 // looking up that has nothing to do with our address lookup. 636 if (sc.symbol->GetAddressRangePtr() && sc.symbol->GetAddressRangePtr()->GetBaseAddress().GetSection() != GetSection()) 637 sc.symbol = NULL; 638 } 639 sc.GetDescription(s, eDescriptionLevelBrief, target); 640 } 641 } 642 if (fallback_style != DumpStyleInvalid) 643 return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); 644 return false; 645 break; 646 } 647 648 return true; 649 } 650 651 void 652 Address::CalculateSymbolContext (SymbolContext *sc) 653 { 654 sc->Clear(); 655 // Absolute addresses don't have enough information to reconstruct even their target. 656 if (m_section == NULL) 657 return; 658 659 if (m_section->GetModule()) 660 { 661 sc->module_sp = m_section->GetModule()->GetSP(); 662 if (sc->module_sp) 663 sc->module_sp->ResolveSymbolContextForAddress (*this, eSymbolContextEverything, *sc); 664 } 665 } 666 667 int 668 Address::CompareFileAddress (const Address& a, const Address& b) 669 { 670 addr_t a_file_addr = a.GetFileAddress(); 671 addr_t b_file_addr = b.GetFileAddress(); 672 if (a_file_addr < b_file_addr) 673 return -1; 674 if (a_file_addr > b_file_addr) 675 return +1; 676 return 0; 677 } 678 679 680 int 681 Address::CompareLoadAddress (const Address& a, const Address& b, Target *target) 682 { 683 assert (target != NULL); 684 addr_t a_load_addr = a.GetLoadAddress (target); 685 addr_t b_load_addr = b.GetLoadAddress (target); 686 if (a_load_addr < b_load_addr) 687 return -1; 688 if (a_load_addr > b_load_addr) 689 return +1; 690 return 0; 691 } 692 693 int 694 Address::CompareModulePointerAndOffset (const Address& a, const Address& b) 695 { 696 Module *a_module = a.GetModule (); 697 Module *b_module = b.GetModule (); 698 if (a_module < b_module) 699 return -1; 700 if (a_module > b_module) 701 return +1; 702 // Modules are the same, just compare the file address since they should 703 // be unique 704 addr_t a_file_addr = a.GetFileAddress(); 705 addr_t b_file_addr = b.GetFileAddress(); 706 if (a_file_addr < b_file_addr) 707 return -1; 708 if (a_file_addr > b_file_addr) 709 return +1; 710 return 0; 711 } 712 713 714 size_t 715 Address::MemorySize () const 716 { 717 // Noting special for the memory size of a single Address object, 718 // it is just the size of itself. 719 return sizeof(Address); 720 } 721 722 723 //---------------------------------------------------------------------- 724 // NOTE: Be careful using this operator. It can correctly compare two 725 // addresses from the same Module correctly. It can't compare two 726 // addresses from different modules in any meaningful way, but it will 727 // compare the module pointers. 728 // 729 // To sum things up: 730 // - works great for addresses within the same module 731 // - it works for addresses across multiple modules, but don't expect the 732 // address results to make much sense 733 // 734 // This basically lets Address objects be used in ordered collection 735 // classes. 736 //---------------------------------------------------------------------- 737 738 bool 739 lldb_private::operator< (const Address& lhs, const Address& rhs) 740 { 741 Module *lhs_module = lhs.GetModule(); 742 Module *rhs_module = rhs.GetModule(); 743 if (lhs_module == rhs_module) 744 { 745 // Addresses are in the same module, just compare the file addresses 746 return lhs.GetFileAddress() < rhs.GetFileAddress(); 747 } 748 else 749 { 750 // The addresses are from different modules, just use the module 751 // pointer value to get consistent ordering 752 return lhs_module < rhs_module; 753 } 754 } 755 756 bool 757 lldb_private::operator> (const Address& lhs, const Address& rhs) 758 { 759 Module *lhs_module = lhs.GetModule(); 760 Module *rhs_module = rhs.GetModule(); 761 if (lhs_module == rhs_module) 762 { 763 // Addresses are in the same module, just compare the file addresses 764 return lhs.GetFileAddress() > rhs.GetFileAddress(); 765 } 766 else 767 { 768 // The addresses are from different modules, just use the module 769 // pointer value to get consistent ordering 770 return lhs_module > rhs_module; 771 } 772 } 773 774 775 // The operator == checks for exact equality only (same section, same offset) 776 bool 777 lldb_private::operator== (const Address& a, const Address& rhs) 778 { 779 return a.GetSection() == rhs.GetSection() && 780 a.GetOffset() == rhs.GetOffset(); 781 } 782 // The operator != checks for exact inequality only (differing section, or 783 // different offset) 784 bool 785 lldb_private::operator!= (const Address& a, const Address& rhs) 786 { 787 return a.GetSection() != rhs.GetSection() || 788 a.GetOffset() != rhs.GetOffset(); 789 } 790 791 bool 792 Address::IsLinkedAddress () const 793 { 794 return m_section && m_section->GetLinkedSection(); 795 } 796 797 798 void 799 Address::ResolveLinkedAddress () 800 { 801 if (m_section) 802 { 803 const Section *linked_section = m_section->GetLinkedSection(); 804 if (linked_section) 805 { 806 m_offset += m_section->GetLinkedOffset(); 807 m_section = linked_section; 808 } 809 } 810 } 811