diff --git a/llvm/test/tools/llvm-objdump/X86/disassemble-same-section-addr.test b/llvm/test/tools/llvm-objdump/X86/disassemble-same-section-addr.test --- a/llvm/test/tools/llvm-objdump/X86/disassemble-same-section-addr.test +++ b/llvm/test/tools/llvm-objdump/X86/disassemble-same-section-addr.test @@ -3,15 +3,33 @@ ## picked was non-deterministic, resulting in different symbols being found for ## the section. The test uses YAML for the input, as we need a fully linked ELF ## to reproduce the original failure. +## Note: SHN_LOPROC is used as the section index for most instances of "other", +## as only symbols in sections and SHN_ABS symbols have any impact on +## disassembly. -# RUN: yaml2obj %s -o %t1 -D SECTION=.second -# RUN: llvm-objdump -d %t1 | FileCheck %s -# RUN: yaml2obj %s -o %t2 -D SECTION=.first -## FIXME: this case should print "" too. -# RUN: llvm-objdump -d %t2 | FileCheck %s --check-prefix=FAIL +## Two empty sections, one with symbol in, one without. +# RUN: yaml2obj %s --docnum=1 -o %t1 -D SIZE1=0 -D SIZE2=0 -D SECTION=.second -D INDEX=SHN_LOPROC +# RUN: llvm-objdump -d %t1 | FileCheck %s --check-prefix=TARGET +# RUN: yaml2obj %s --docnum=1 -o %t2 -D SIZE1=0 -D SIZE2=0 -D SECTION=.first -D INDEX=SHN_LOPROC +# RUN: llvm-objdump -d %t2 | FileCheck %s --check-prefix=TARGET -# CHECK: callq 0x5 -# FAIL: callq 0x5{{$}} +## Two sections, one empty with symbol, other non-empty, without symbol. +# RUN: yaml2obj %s --docnum=1 -o %t3 -D SIZE1=1 -D SIZE2=0 -D SECTION=.second -D INDEX=SHN_LOPROC +# RUN: llvm-objdump -d %t3 | FileCheck %s --check-prefix=TARGET +# RUN: yaml2obj %s --docnum=1 -o %t4 -D SIZE1=0 -D SIZE2=1 -D SECTION=.first -D INDEX=SHN_LOPROC +# RUN: llvm-objdump -d %t4 | FileCheck %s --check-prefix=TARGET + +## Fall back to absolute symbol if no symbol found in candidate sections. +# RUN: yaml2obj %s --docnum=1 -o %t5 -D SIZE1=1 -D SIZE2=0 -D SECTION=.caller -D INDEX=SHN_ABS +# RUN: llvm-objdump -d %t5 | FileCheck %s --check-prefix=ABSOLUTE + +## Print no target if no symbol in section/absolute symbol found. +# RUN: yaml2obj %s --docnum=1 -o %t5 -D SIZE1=1 -D SIZE2=0 -D SECTION=.caller -D INDEX=SHN_LOPROC +# RUN: llvm-objdump -d %t6 | FileCheck %s --check-prefix=FAIL + +# TARGET: callq 0x5 +# ABSOLUTE: callq 0x5 +# FAIL: callq 0x5{{$}} --- !ELF FileHeader: @@ -24,16 +42,71 @@ Type: SHT_PROGBITS Flags: [SHF_ALLOC, SHF_EXECINSTR] Address: 0x0 - Content: e800000000 # Call instruction to next address. + Content: e800000000 ## Call instruction to next address. - Name: .first Type: SHT_PROGBITS Flags: [SHF_ALLOC, SHF_EXECINSTR] Address: 0x5 + Size: [[SIZE1]] - Name: .second Type: SHT_PROGBITS Flags: [SHF_ALLOC, SHF_EXECINSTR] Address: 0x5 + Size: [[SIZE2]] Symbols: - Name: target Section: [[SECTION]] Value: 0x5 + - Name: other + Index: [[INDEX]] + Value: 0x0 + +## Two empty sections, both with symbols. +# RUN: yaml2obj %s --docnum=2 -o %t7 -D SIZE1=0 -D SIZE2=0 -D SYMVAL1=0x5 -D SYMVAL2=0x5 +# RUN: llvm-objdump -d %t7 | FileCheck %s --check-prefix=SECOND + +## Two sections, both with symbols, one empty, the other not. +# RUN: yaml2obj %s --docnum=2 -o %t8 -D SIZE1=1 -D SIZE2=0 -D SYMVAL1=0x5 -D SYMVAL2=0x5 +# RUN: llvm-objdump -d %t8 | FileCheck %s --check-prefix=FIRST +# RUN: yaml2obj %s --docnum=2 -o %t9 -D SIZE1=0 -D SIZE2=1 -D SYMVAL1=0x5 -D SYMVAL2=0x5 +# RUN: llvm-objdump -d %t9 | FileCheck %s --check-prefix=SECOND + +## Two sections, both with symbols, one empty, other not, symbol in non-empty +## section has value higher than target address. +# RUN: yaml2obj %s --docnum=2 -o %t10 -D SIZE1=1 -D SIZE2=0 -D SYMVAL1=0x6 -D SYMVAL2=0x5 +# RUN: llvm-objdump -d %t10 | FileCheck %s --check-prefix=SECOND +# RUN: yaml2obj %s --docnum=2 -o %t11 -D SIZE1=0 -D SIZE2=1 -D SYMVAL1=0x5 -D SYMVAL2=0x6 +# RUN: llvm-objdump -d %t11 | FileCheck %s --check-prefix=FIRST + +# FIRST: callq 0x5 +# SECOND: callq 0x5 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .caller + Type: SHT_PROGBITS + Flags: [SHF_ALLOC, SHF_EXECINSTR] + Address: 0x0 + Content: e800000000 ## Call instruction to next address. + - Name: .first + Type: SHT_PROGBITS + Flags: [SHF_ALLOC, SHF_EXECINSTR] + Address: 0x5 + Size: [[SIZE1]] + - Name: .second + Type: SHT_PROGBITS + Flags: [SHF_ALLOC, SHF_EXECINSTR] + Address: 0x5 + Size: [[SIZE2]] +Symbols: + - Name: first + Section: .first + Value: [[SYMVAL1]] + - Name: second + Section: .second + Value: [[SYMVAL2]] diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -1244,12 +1244,17 @@ addPltEntries(Obj, AllSymbols, Saver); // Create a mapping from virtual address to section. An empty section can - // cause more than one section at the same address. Use a stable sort to - // stabilize the output. + // cause more than one section at the same address. Sort such sections to be + // before same-addressed non-empty sections so that symbol lookups prefer the + // non-empty section. std::vector> SectionAddresses; for (SectionRef Sec : Obj->sections()) SectionAddresses.emplace_back(Sec.getAddress(), Sec); - llvm::stable_sort(SectionAddresses, llvm::less_first()); + llvm::stable_sort(SectionAddresses, [](const auto &LHS, const auto &RHS) { + if (LHS.first != RHS.first) + return LHS.first < RHS.first; + return LHS.second.getSize() < RHS.second.getSize(); + }); // Linked executables (.exe and .dll files) typically don't include a real // symbol table but they might contain an export table. @@ -1525,41 +1530,48 @@ // through a relocation. // // In a non-relocatable object, the target may be in any section. + // In that case, locate the section(s) containing the target address + // and find the symbol in one of those, if possible. // // N.B. We don't walk the relocations in the relocatable case yet. - auto *TargetSectionSymbols = &Symbols; + std::vector TargetSectionSymbols; if (!Obj->isRelocatableObject()) { - auto It = partition_point( + auto It = llvm::partition_point( SectionAddresses, [=](const std::pair &O) { return O.first <= Target; }); - if (It != SectionAddresses.begin()) { + uint64_t TargetSecAddr = 0; + while (It != SectionAddresses.begin()) { --It; - TargetSectionSymbols = &AllSymbols[It->second]; - } else { - TargetSectionSymbols = &AbsoluteSymbols; + if (TargetSecAddr == 0) + TargetSecAddr = It->first; + if (It->first != TargetSecAddr) + break; + TargetSectionSymbols.push_back(&AllSymbols[It->second]); } + } else { + TargetSectionSymbols.push_back(&Symbols); } - - // Find the last symbol in the section whose offset is less than - // or equal to the target. If there isn't a section that contains - // the target, find the nearest preceding absolute symbol. - auto TargetSym = partition_point( - *TargetSectionSymbols, - [=](const SymbolInfoTy &O) { - return O.Addr <= Target; - }); - if (TargetSym == TargetSectionSymbols->begin()) { - TargetSectionSymbols = &AbsoluteSymbols; - TargetSym = partition_point( - AbsoluteSymbols, - [=](const SymbolInfoTy &O) { - return O.Addr <= Target; - }); + TargetSectionSymbols.push_back(&AbsoluteSymbols); + + // Find the last symbol in the first candidate section whose offset + // is less than or equal to the target. If there are no such + // symbols, try in the next section and so on, before finally using + // the nearest preceding absolute symbol (if any), if there are no + // other valid symbols. + const SymbolInfoTy *TargetSym = nullptr; + for (const SectionSymbolsTy *TargetSymbols : TargetSectionSymbols) { + auto It = llvm::partition_point( + *TargetSymbols, + [=](const SymbolInfoTy &O) { return O.Addr <= Target; }); + if (It != TargetSymbols->begin()) { + TargetSym = &*(It - 1); + break; + } } - if (TargetSym != TargetSectionSymbols->begin()) { - --TargetSym; + + if (TargetSym != nullptr) { uint64_t TargetAddress = TargetSym->Addr; StringRef TargetName = TargetSym->Name; outs() << " <" << TargetName;