diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callsites.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callsites.test
new file mode 100644
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callsites.test
@@ -0,0 +1,40 @@
+## Tests --call-graph-info prints call sites.
+
+# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t
+# RUN: llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t
+
+# CHECK: [[FILE]]: file format elf64-x86-64
+
+# CHECK: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
+# CHECK-NEXT: 12 14
+# CHECK-NEXT: 1 8
+# CHECK-EMPTY:
+# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
+# CHECK-NEXT: 12 19 19
+# CHECK-NEXT: 1 6 6 d d 12 12
+# CHECK-EMPTY:
+# CHECK-NEXT: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)
+# CHECK-NEXT: 0 foo
+# CHECK-NEXT: 12 baz
+# CHECK-NEXT: 1 bar
+
+.text
+
+.globl foo
+.type foo,@function
+foo:
+ retq
+
+.globl bar
+.type bar,@function
+bar:
+ callq foo
+ callq *%rcx
+ callq bar
+ callq foo
+
+.globl baz
+.type baz,@function
+baz:
+ callq *%rcx
+ callq foo
diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test
--- a/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test
+++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test
@@ -39,7 +39,18 @@
# }
# CHECK: [[FILE]]: file format elf64-x86-64
-# CHECK: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)
+# CHECK: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])
+# CHECK-NEXT: 401000 401012
+# CHECK-NEXT: 401140 401165 40117b 401195
+# CHECK-NEXT: 4011c0 401205
+# CHECK-NEXT: 401020 40104a
+# CHECK-EMPTY:
+# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])
+# CHECK-NEXT: 401140 40119a 401110 40119f 401120 4011aa 401130
+# CHECK-NEXT: 4011c0 4011ed 401000
+# CHECK-NEXT: 4010d0 4010e2 401060
+# CHECK-EMPTY:
+# CHECK-NEXT: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)
# CHECK-NEXT: 401000 _init
# CHECK-NEXT: 401100 frame_dummy
# CHECK-NEXT: 401140 main
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
@@ -224,6 +224,10 @@
struct FunctionInfo {
std::string Name;
+
+ using DirectCallSite = std::pair;
+ SmallVector DirectCallSites;
+ SmallVector IndirectCallSites;
};
// Map function entry pc to function info.
@@ -1437,6 +1441,51 @@
LVP.update({Index, Section.getIndex()},
{Index + Size, Section.getIndex()}, Index + Size != End);
+ if (CallGraphInfo) {
+ if (Disassembled && MIA->isCall(Inst)) {
+ // Call site address is the address of the instruction just
+ // next to the call instruction. This is the return address
+ // as appears on the stack trace.
+ uint64_t CallSitePc = SectionAddr + Index + Size;
+ uint64_t CallerPc = Symbols[SI].Addr;
+ // Check the operands to decide whether this is an direct or
+ // indirect call.
+ // Assumption: a call instruction with at least one register
+ // operand is an indirect call. Otherwise, it is a direct call
+ // with exactly one immediate operand.
+ bool HasRegOperand = false;
+ unsigned int ImmOperandCount = 0;
+ const MCOperand *ImmOperand = NULL;
+ for (unsigned int I = 0; I < Inst.getNumOperands(); I++) {
+ const auto &Operand = Inst.getOperand(I);
+ if (Operand.isReg()) {
+ HasRegOperand = true;
+ } else if (Operand.isImm()) {
+ ImmOperandCount++;
+ ImmOperand = &Operand;
+ }
+ }
+ // Check if the assumption holds true.
+ assert(HasRegOperand ||
+ (!HasRegOperand && ImmOperandCount == 1) &&
+ "Call instruction is expected to have at least one "
+ "register operand (i.e., indirect call) or exactly "
+ "one immediate operand (i.e., direct call).");
+ if (HasRegOperand) {
+ // Indirect call.
+ FuncInfo[CallerPc].IndirectCallSites.push_back(CallSitePc);
+ } else {
+ // Direct call.
+ uint64_t CalleePc;
+ bool Res = MIA->evaluateBranch(Inst, SectionAddr + Index, Size,
+ CalleePc);
+ assert(Res && "Failed to evaluate direct call target address.");
+ FuncInfo[CallerPc].DirectCallSites.emplace_back(CallSitePc,
+ CalleePc);
+ }
+ }
+ }
+
IP->setCommentStream(CommentStream);
PIP.printInst(
@@ -2054,6 +2103,36 @@
// Get function info through disassembly.
disassembleObject(Obj, /*InlineRelocs=*/false);
+ // Print function entry to indirect call site addresses mapping from disasm.
+ outs() << "\n\nINDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,])";
+ for (const auto &El : FuncInfo) {
+ auto CallerPc = El.first;
+ auto FuncIndirCallSites = El.second.IndirectCallSites;
+ if (!FuncIndirCallSites.empty()) {
+ outs() << "\n" << format("%lx", CallerPc);
+ for (auto IndirCallSitePc : FuncIndirCallSites)
+ outs() << " " << format("%lx", IndirCallSitePc);
+ }
+ }
+
+ // Print function entry to direct call site and target function entry
+ // addresses mapping from disasm.
+ outs()
+ << "\n\nDIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),])";
+ for (const auto &El : FuncInfo) {
+ auto CallerPc = El.first;
+ auto FuncDirCallSites = El.second.DirectCallSites;
+ if (!FuncDirCallSites.empty()) {
+ outs() << "\n" << format("%lx", CallerPc);
+ for (auto DirCallSite : FuncDirCallSites) {
+ auto DirCallSitePc = DirCallSite.first;
+ auto CalleePc = DirCallSite.second;
+ outs() << " " << format("%lx", DirCallSitePc) << " "
+ << format("%lx", CalleePc);
+ }
+ }
+ }
+
// Print function entry pc to function name mapping.
outs() << "\n\nFUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)";
for (const auto &El : FuncInfo) {