From dc2d0ac21a3dc68584e1b35303d1229888f16989 Mon Sep 17 00:00:00 2001 From: prabhukr Date: Fri, 18 Apr 2025 23:04:18 +0000 Subject: [PATCH 1/3] All objdump in one patch --- llvm/docs/CommandGuide/llvm-objdump.rst | 5 + ...graph-info-callgraph-section-warnings.test | 35 + .../call-graph-info-callgraph-section.test | 73 ++ .../ELF/call-graph-info-callsites.test | 40 ++ ...graph-info-err-invalid-format-version.test | 18 + ...all-graph-info-err-invalid-func-entry.test | 19 + ...call-graph-info-err-invalid-func-kind.test | 20 + ...-info-err-malformed-callgraph-section.test | 22 + ...info-err-malformed-callgraph-section2.test | 22 + .../ELF/call-graph-info-functions.test | 28 + ...-graph-info-warn-no-callgraph-section.test | 15 + .../llvm-objdump/ELF/call-graph-info.test | 647 ++++++++++++++++++ llvm/tools/llvm-objdump/ObjdumpOpts.td | 4 + llvm/tools/llvm-objdump/llvm-objdump.cpp | 301 +++++++- 14 files changed, 1248 insertions(+), 1 deletion(-) create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section-warnings.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-callsites.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-format-version.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-entry.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-kind.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section2.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-functions.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info-warn-no-callgraph-section.test create mode 100644 llvm/test/tools/llvm-objdump/ELF/call-graph-info.test diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst index aaf38f84b92e5..09ff59f9e489f 100644 --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -25,6 +25,11 @@ combined with other commands: Display the information contained within an archive's headers. +.. option:: --call-graph-info + + Dump call graph information including indirect call and target IDs from call + graph section, if available. + .. option:: -d, --disassemble Disassemble all executable sections found in the input files. On some diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section-warnings.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section-warnings.test new file mode 100644 index 0000000000000..c4831c8b889f6 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section-warnings.test @@ -0,0 +1,35 @@ +## Tests --call-graph-info warnings for missing/invalid .callgraph section +## contents. + +# 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-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section has type ids for 1 instructions which are not indirect calls +# CHECK-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section does not have type ids for 1 indirect calls +# CHECK-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section does not have information for 1 functions +# CHECK-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section has unknown type id for 1 indirect targets + +.text +.globl foo +.type foo,@function +foo: +.Lfoo_begin: +.Lnot_indirect_call: + retq + +.globl bar +.type bar,@function +bar: + callq *%rcx + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad .Lfoo_begin #< foo()'s entry address. +.quad 1 #< A warning: function kind is 1: the type id for indirect target foo is unknown. +.quad 1 #< Count of indirect call sites that follow: 1. +.quad 0 #< Indirect call type id. +.quad .Lnot_indirect_call #< A warning: type id for non-indirect call instruction. +# A warning: .callgraph section does not have information for 1 function (bar). +# A warning: .callgraph section does not have type id for 1 indirect call (one in bar). +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section.test new file mode 100644 index 0000000000000..beefc452797f7 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-callgraph-section.test @@ -0,0 +1,73 @@ +## Tests --call-graph-info prints information from call graph section. + +# 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 TARGET TYPES (TYPEID [FUNC_ADDR,]) +# CHECK-NEXT: UNKNOWN 6 b +# CHECK-NEXT: 20 a +# CHECK-EMPTY: +# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,]) +# CHECK-NEXT: 10 9 +# CHECK-EMPTY: +# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,]) +# CHECK-NEXT: 6 9 +# CHECK-EMPTY: +# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),]) +# CHECK-NEXT: 0 5 5 +# CHECK-EMPTY: +# CHECK-NEXT: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME) +# CHECK-NEXT: 0 foo +# CHECK-NEXT: 6 bar +# CHECK-NEXT: a baz +# CHECK-NEXT: b qux + +.text + +.globl foo +.type foo,@function +foo: #< foo is at 0. +.Lfoo_begin: + callq foo #< direct call is at 5. target is foo (5). + retq + +.globl bar +.type bar,@function +bar: #< bar is at 6. + callq *-40(%rbp) #< indirect call is at 9. + retq + +.globl baz +.type baz,@function +baz: #< baz is at 10 (a). + retq + +.globl qux +.type qux,@function +qux: #< qux is at 11 (b). + retq + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad 0 #< foo()'s entry address. +.quad 0 #< Function kind: not an indirect target. +.quad 0 #< Count of indirect call sites that follow: 0. + +.quad 0 #< Format version number. +.quad 6 #< bar()'s entry address. +.quad 1 #< Function kind: indirect target with unknown type id. +.quad 1 #< Count of indirect call sites that follow: 1. +.quad 16 #< Indirect call type id. +.quad 9 #< Indirect call site. + +.quad 0 #< Format version number. +.quad 10 #< baz()'s entry address. +.quad 2 #< Function kind: indirect target with known type id. +.quad 32 #< Indirect target type id. +.quad 0 #< Count of indirect call sites that follow: 1. + +# No call graph section entry for qux: will be printed as unknown target. + +.text 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 index 0000000000000..0a12505aaae8d --- /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: 1 8 +# CHECK-NEXT: 12 14 +# CHECK-EMPTY: +# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),]) +# CHECK-NEXT: 1 6 6 d d 12 12 +# CHECK-NEXT: 12 19 19 +# CHECK-EMPTY: +# CHECK-NEXT: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME) +# CHECK-NEXT: 0 foo +# CHECK-NEXT: 1 bar +# CHECK-NEXT: 12 baz + +.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-err-invalid-format-version.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-format-version.test new file mode 100644 index 0000000000000..bbab998681be6 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-format-version.test @@ -0,0 +1,18 @@ +## Tests that --call-graph-info fails if .callgraph section has unknown format +## version number. + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR + +# CHECK: [[FILE]]: file format elf64-x86-64 +# ERR: llvm-objdump: error: '[[FILE]]': Unknown format version in .callgraph section. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT + +.section .callgraph,"o",@progbits,.text +.quad 1 #< Invalid format version number: the only supported version is 0. +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-entry.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-entry.test new file mode 100644 index 0000000000000..43f88d5a2b5ff --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-entry.test @@ -0,0 +1,19 @@ +## Tests that --call-graph-info fails if .callgraph section has invalid +## function entry address. + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR + +# CHECK: [[FILE]]: file format elf64-x86-64 +# ERR: llvm-objdump: error: '[[FILE]]': Invalid function entry pc in .callgraph section. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad 12345 #< Invalid function entry address. +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-kind.test new file mode 100644 index 0000000000000..67d3ebc34e7c7 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-invalid-func-kind.test @@ -0,0 +1,20 @@ +## Tests that --call-graph-info fails if .callgraph section has invalid +## function kind value. + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR + +# CHECK: [[FILE]]: file format elf64-x86-64 +# ERR: llvm-objdump: error: '[[FILE]]': Unknown function kind in .callgraph section. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad 0 #< Function entry address. +.quad 3 #< Invalid function kind: must be one of 0, 1, 2. +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section.test new file mode 100644 index 0000000000000..c539a34874d44 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section.test @@ -0,0 +1,22 @@ +## Tests that --call-graph-info fails if .callgraph section has invalid size. + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR + +# CHECK: [[FILE]]: file format elf64-x86-64 +# ERR: llvm-objdump: error: '[[FILE]]': Malformed .callgraph section. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT + +.section .callgraph,"o",@progbits,.text +# Each unit in .callgraph section must have 64bit size. Therefore, byte size +# must be divisible by 64. +.quad 0 +.quad 0 +.quad 0 +.byte 0 +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section2.test new file mode 100644 index 0000000000000..959d54799081d --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-err-malformed-callgraph-section2.test @@ -0,0 +1,22 @@ +## Tests that --call-graph-info fails if .callgraph section does not have +## an expected value, e.g., not as much call sites as the given count. + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=ERR + +# CHECK: [[FILE]]: file format elf64-x86-64 +# ERR: llvm-objdump: error: '[[FILE]]': Malformed .callgraph section. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad 0 #< Function entry address. +.quad 0 #< Function kind. +.quad 2 #< Indirect call site count that follows. + #< Missing indirect calls? +.text diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-functions.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-functions.test new file mode 100644 index 0000000000000..fb1f2d4d78b23 --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-functions.test @@ -0,0 +1,28 @@ +## Tests --call-graph-info prints functions. + +# 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: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME) +# CHECK-NEXT: 0 foo +# CHECK-NEXT: 1 bar +# CHECK-NEXT: 2 baz + +.text + +.globl foo +.type foo,@function +foo: + retq + +.globl bar +.type bar,@function +bar: + retq + +.globl baz +.type baz,@function +baz: + retq diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info-warn-no-callgraph-section.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-warn-no-callgraph-section.test new file mode 100644 index 0000000000000..fcee2da238d6c --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info-warn-no-callgraph-section.test @@ -0,0 +1,15 @@ +## Tests that --call-graph-info warns if there is no .callgraph section. + +# 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-NEXT: llvm-objdump: warning: '[[FILE]]': there is no .callgraph section +# CHECK-NOT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,]) +# CHECK-NOT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,]) + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT diff --git a/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test b/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test new file mode 100644 index 0000000000000..d928c2272f2fd --- /dev/null +++ b/llvm/test/tools/llvm-objdump/ELF/call-graph-info.test @@ -0,0 +1,647 @@ +## Tests how --call-graph-info prints the call graph information. +# RUN: yaml2obj --docnum=1 %s -o %t +# RUN: llvm-objdump --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t + +# Yaml input is obtained by compiling the below source to object with: +# clang -fcall-graph-section test.c -o test.o +# then to yaml with: +# obj2yaml test.o > test.yaml + +# The content of the .callgraph section is fixed with this yaml in raw format. + +# Source: +# void foo() {} +# +# void bar() {} +# +# int baz(char a) { +# return 0; +# } +# +# int main() { +# // Indirect calls. +# void (*fp_foo)() = foo; +# fp_foo(); +# +# void (*fp_bar)() = bar; +# fp_bar(); +# +# char a; +# int (*fp_baz)(char) = baz; +# fp_baz(a); +# +# // Direct calls. +# foo(); +# bar(); +# baz(a); +# +# return 0; +# } + +# CHECK: [[FILE]]: file format elf64-x86-64 +# CHECK-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section does not have type ids for 3 indirect calls +# CHECK-NEXT: llvm-objdump: warning: '[[FILE]]': callgraph section does not have information for 10 functions +# CHECK-EMPTY: +# CHECK-NEXT: INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,]) +# CHECK-NEXT: UNKNOWN 401000 401020 401050 401060 401090 4010d0 401100 4011c0 401220 401224 +# CHECK-NEXT: 3ecbeef531f74424 401110 401120 +# CHECK-NEXT: 308e4b8159bc8654 401130 +# CHECK-NEXT: fa6809609a76afca 401140 +# CHECK-EMPTY: +# CHECK-NEXT: INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,]) +# CHECK-NEXT: 3ecbeef531f74424 401165 40117b +# CHECK-NEXT: 308e4b8159bc8654 401195 +# CHECK-EMPTY: +# CHECK-NEXT: INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,]) +# CHECK-NEXT: 401000 401012 +# CHECK-NEXT: 401020 40104a +# CHECK-NEXT: 401140 401165 40117b 401195 +# CHECK-NEXT: 4011c0 401205 +# CHECK-EMPTY: +# CHECK-NEXT: DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),]) +# CHECK-NEXT: 4010d0 4010e2 401060 +# CHECK-NEXT: 401140 40119a 401110 40119f 401120 4011aa 401130 +# CHECK-NEXT: 4011c0 4011ed 401000 +# CHECK-EMPTY: +# CHECK-NEXT: FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME) +# CHECK-NEXT: 401000 _init +# CHECK-NEXT: 401020 _start +# CHECK-NEXT: 401050 _dl_relocate_static_pie +# CHECK-NEXT: 401060 deregister_tm_clones +# CHECK-NEXT: 401090 register_tm_clones +# CHECK-NEXT: 4010d0 __do_global_dtors_aux +# CHECK-NEXT: 401100 frame_dummy +# CHECK-NEXT: 401110 foo +# CHECK-NEXT: 401120 bar +# CHECK-NEXT: 401130 baz +# CHECK-NEXT: 401140 main +# CHECK-NEXT: 4011c0 __libc_csu_init +# CHECK-NEXT: 401220 __libc_csu_fini +# CHECK-NEXT: 401224 _fini + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 + Entry: 0x401020 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x400040 + Align: 0x8 + - Type: PT_INTERP + Flags: [ PF_R ] + FirstSec: .interp + LastSec: .interp + VAddr: 0x4002A8 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .interp + LastSec: .rela.dyn + VAddr: 0x400000 + Align: 0x1000 + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + FirstSec: .init + LastSec: .fini + VAddr: 0x401000 + Align: 0x1000 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .rodata + LastSec: .eh_frame + VAddr: 0x402000 + Align: 0x1000 + - Type: PT_LOAD + Flags: [ PF_W, PF_R ] + FirstSec: .init_array + LastSec: .bss + VAddr: 0x403E40 + Align: 0x1000 + - Type: PT_DYNAMIC + Flags: [ PF_W, PF_R ] + FirstSec: .dynamic + LastSec: .dynamic + VAddr: 0x403E50 + Align: 0x8 + - Type: PT_NOTE + Flags: [ PF_R ] + FirstSec: .note.ABI-tag + LastSec: .note.ABI-tag + VAddr: 0x4002C4 + Align: 0x4 + - Type: PT_GNU_EH_FRAME + Flags: [ PF_R ] + FirstSec: .eh_frame_hdr + LastSec: .eh_frame_hdr + VAddr: 0x402004 + Align: 0x4 + - Type: PT_GNU_STACK + Flags: [ PF_W, PF_R ] + Align: 0x10 + - Type: PT_GNU_RELRO + Flags: [ PF_R ] + FirstSec: .init_array + LastSec: .got + VAddr: 0x403E40 +Sections: + - Name: .interp + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x4002A8 + AddressAlign: 0x1 + Content: 2F6C696236342F6C642D6C696E75782D7838362D36342E736F2E3200 + - Name: .note.ABI-tag + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x4002C4 + AddressAlign: 0x4 + Notes: + - Name: GNU + Desc: '00000000030000000200000000000000' + Type: NT_VERSION + - Name: .hash + Type: SHT_HASH + Flags: [ SHF_ALLOC ] + Address: 0x4002E8 + Link: .dynsym + AddressAlign: 0x8 + Bucket: [ 2 ] + Chain: [ 0, 0, 1 ] + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Address: 0x400300 + Link: .dynsym + AddressAlign: 0x8 + Header: + SymNdx: 0x1 + Shift2: 0x0 + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0 ] + HashValues: [ ] + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x400320 + Link: .dynstr + AddressAlign: 0x8 + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x400368 + AddressAlign: 0x1 + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Address: 0x4003A0 + Link: .dynsym + AddressAlign: 0x2 + Entries: [ 0, 2, 0 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Address: 0x4003A8 + Link: .dynstr + AddressAlign: 0x8 + Dependencies: + - Version: 1 + File: libc.so.6 + Entries: + - Name: GLIBC_2.2.5 + Hash: 157882997 + Flags: 0 + Other: 2 + - Name: .rela.dyn + Type: SHT_RELA + Flags: [ SHF_ALLOC ] + Address: 0x4003C8 + Link: .dynsym + AddressAlign: 0x8 + Relocations: + - Offset: 0x403FF0 + Symbol: __libc_start_main + Type: R_X86_64_GLOB_DAT + - Offset: 0x403FF8 + Symbol: __gmon_start__ + Type: R_X86_64_GLOB_DAT + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401000 + AddressAlign: 0x4 + Offset: 0x1000 + Content: 4883EC08488B05ED2F00004885C07402FFD04883C408C3 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401020 + AddressAlign: 0x10 + Content: 31ED4989D15E4889E24883E4F0505449C7C02012400048C7C1C011400048C7C740114000FF15A62F0000F40F1F440000C3662E0F1F8400000000000F1F440000B828404000483D284040007413B8000000004885C07409BF28404000FFE06690C366662E0F1F8400000000000F1F4000BE284040004881EE284040004889F048C1EE3F48C1F8034801C648D1FE7411B8000000004885C07407BF28404000FFE0C366662E0F1F8400000000000F1F4000803D512F0000007517554889E5E87EFFFFFFC6053F2F0000015DC30F1F440000C366662E0F1F8400000000000F1F4000EB8E662E0F1F8400000000000F1F4000554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC30C745FC0000000048B81011400000000000488945F0488B4DF031C0FFD148B82011400000000000488945E8488B4DE831C0FFD148B83011400000000000488945D8488B45D88A4DE70FBEF9FFD0E876FFFFFFE881FFFFFF8A45E70FBEF8E886FFFFFF31C04883C4305DC3662E0F1F8400000000000F1F400041574C8D3D772C000041564989D641554989F541544189FC55488D2D682C0000534C29FD4883EC08E813FEFFFF48C1FD03741B31DB0F1F004C89F24C89EE4489E741FF14DF4883C3014839DD75EA4883C4085B5D415C415D415E415FC30F1F00C3 + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x401224 + AddressAlign: 0x4 + Content: 4883EC084883C408C3 + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE ] + Address: 0x402000 + AddressAlign: 0x4 + EntSize: 0x4 + Offset: 0x2000 + Content: '01000200' + - Name: .eh_frame_hdr + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x402004 + AddressAlign: 0x4 + Content: 011B033B48000000080000001CF0FFFF640000004CF0FFFF900000000CF1FFFFA40000001CF1FFFFC40000002CF1FFFFE40000003CF1FFFF04010000BCF1FFFF240100001CF2FFFF6C010000 + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x402050 + AddressAlign: 0x8 + Content: 1400000000000000017A5200017810011B0C070890010710100000001C000000B0EFFFFF2B000000000000001400000000000000017A5200017810011B0C070890010000100000001C000000B4EFFFFF01000000000000001C0000003000000060F0FFFF0600000000410E108602430D06410C07080000001C0000005000000050F0FFFF0600000000410E108602430D06410C07080000001C0000007000000040F0FFFF0E00000000410E108602430D06490C07080000001C0000009000000030F0FFFF7200000000410E108602430D06026D0C0708000044000000B000000090F0FFFF5D00000000420E108F02490E188E03450E208D04450E288C05440E308606480E388307470E406A0E38410E30410E28420E20420E18420E10420E080010000000F8000000A8F0FFFF010000000000000000000000 + - Name: .init_array + Type: SHT_INIT_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403E40 + AddressAlign: 0x8 + EntSize: 0x8 + Offset: 0x2E40 + Content: '0011400000000000' + - Name: .fini_array + Type: SHT_FINI_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403E48 + AddressAlign: 0x8 + EntSize: 0x8 + Content: D010400000000000 + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403E50 + Link: .dynstr + AddressAlign: 0x8 + Entries: + - Tag: DT_NEEDED + Value: 0x13 + - Tag: DT_INIT + Value: 0x401000 + - Tag: DT_FINI + Value: 0x401224 + - Tag: DT_INIT_ARRAY + Value: 0x403E40 + - Tag: DT_INIT_ARRAYSZ + Value: 0x8 + - Tag: DT_FINI_ARRAY + Value: 0x403E48 + - Tag: DT_FINI_ARRAYSZ + Value: 0x8 + - Tag: DT_HASH + Value: 0x4002E8 + - Tag: DT_GNU_HASH + Value: 0x400300 + - Tag: DT_STRTAB + Value: 0x400368 + - Tag: DT_SYMTAB + Value: 0x400320 + - Tag: DT_STRSZ + Value: 0x38 + - Tag: DT_SYMENT + Value: 0x18 + - Tag: DT_DEBUG + Value: 0x0 + - Tag: DT_RELA + Value: 0x4003C8 + - Tag: DT_RELASZ + Value: 0x30 + - Tag: DT_RELAENT + Value: 0x18 + - Tag: DT_VERNEED + Value: 0x4003A8 + - Tag: DT_VERNEEDNUM + Value: 0x1 + - Tag: DT_VERSYM + Value: 0x4003A0 + - Tag: DT_NULL + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Tag: DT_NULL + Value: 0x0 + - Name: .got + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x403FF0 + AddressAlign: 0x8 + EntSize: 0x8 + Content: '00000000000000000000000000000000' + - Name: .got.plt + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404000 + AddressAlign: 0x8 + EntSize: 0x8 + Content: '503E40000000000000000000000000000000000000000000' + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404018 + AddressAlign: 0x8 + Content: '00000000000000000000000000000000' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x404028 + AddressAlign: 0x1 + Size: 0x8 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 4743433A202844656269616E2031302E322E312D362B6275696C6432292031302E322E3120323032313031313000636C616E672076657273696F6E2031332E302E302028676974406769746875622E636F6D3A6C6C766D2F6C6C766D2D70726F6A6563742E67697420663862303066393466366230393538373233363337363565353838313133356532356462646465392900 + - Name: .callgraph + Type: SHT_PROGBITS + Flags: [ SHF_LINK_ORDER ] + Link: .text + AddressAlign: 0x1 + Content: 0000000000000000101140000000000002000000000000002444F731F5EECB3E00000000000000000000000000000000201140000000000002000000000000002444F731F5EECB3E00000000000000000000000000000000301140000000000002000000000000005486BC59814B8E300000000000000000000000000000000040114000000000000200000000000000CAAF769A600968FA03000000000000002444F731F5EECB3E65114000000000002444F731F5EECB3E7B114000000000005486BC59814B8E309511400000000000 +Symbols: + - Name: .interp + Type: STT_SECTION + Section: .interp + Value: 0x4002A8 + - Name: .note.ABI-tag + Type: STT_SECTION + Section: .note.ABI-tag + Value: 0x4002C4 + - Name: .hash + Type: STT_SECTION + Section: .hash + Value: 0x4002E8 + - Name: .gnu.hash + Type: STT_SECTION + Section: .gnu.hash + Value: 0x400300 + - Name: .dynsym + Type: STT_SECTION + Section: .dynsym + Value: 0x400320 + - Name: .dynstr + Type: STT_SECTION + Section: .dynstr + Value: 0x400368 + - Name: .gnu.version + Type: STT_SECTION + Section: .gnu.version + Value: 0x4003A0 + - Name: .gnu.version_r + Type: STT_SECTION + Section: .gnu.version_r + Value: 0x4003A8 + - Name: .rela.dyn + Type: STT_SECTION + Section: .rela.dyn + Value: 0x4003C8 + - Name: .init + Type: STT_SECTION + Section: .init + Value: 0x401000 + - Name: .text + Type: STT_SECTION + Section: .text + Value: 0x401020 + - Name: .fini + Type: STT_SECTION + Section: .fini + Value: 0x401224 + - Name: .rodata + Type: STT_SECTION + Section: .rodata + Value: 0x402000 + - Name: .eh_frame_hdr + Type: STT_SECTION + Section: .eh_frame_hdr + Value: 0x402004 + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Value: 0x402050 + - Name: .init_array + Type: STT_SECTION + Section: .init_array + Value: 0x403E40 + - Name: .fini_array + Type: STT_SECTION + Section: .fini_array + Value: 0x403E48 + - Name: .dynamic + Type: STT_SECTION + Section: .dynamic + Value: 0x403E50 + - Name: .got + Type: STT_SECTION + Section: .got + Value: 0x403FF0 + - Name: .got.plt + Type: STT_SECTION + Section: .got.plt + Value: 0x404000 + - Name: .data + Type: STT_SECTION + Section: .data + Value: 0x404018 + - Name: .bss + Type: STT_SECTION + Section: .bss + Value: 0x404028 + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .callgraph + Type: STT_SECTION + Section: .callgraph + - Name: crtstuff.c + Type: STT_FILE + Index: SHN_ABS + - Name: deregister_tm_clones + Type: STT_FUNC + Section: .text + Value: 0x401060 + - Name: register_tm_clones + Type: STT_FUNC + Section: .text + Value: 0x401090 + - Name: __do_global_dtors_aux + Type: STT_FUNC + Section: .text + Value: 0x4010D0 + - Name: completed.0 + Type: STT_OBJECT + Section: .bss + Value: 0x404028 + Size: 0x1 + - Name: __do_global_dtors_aux_fini_array_entry + Type: STT_OBJECT + Section: .fini_array + Value: 0x403E48 + - Name: frame_dummy + Type: STT_FUNC + Section: .text + Value: 0x401100 + - Name: __frame_dummy_init_array_entry + Type: STT_OBJECT + Section: .init_array + Value: 0x403E40 + - Name: all.c + Type: STT_FILE + Index: SHN_ABS + - Name: 'crtstuff.c (1)' + Type: STT_FILE + Index: SHN_ABS + - Name: __FRAME_END__ + Type: STT_OBJECT + Section: .eh_frame + Value: 0x402184 + - Type: STT_FILE + Index: SHN_ABS + - Name: __init_array_end + Section: .init_array + Value: 0x403E48 + - Name: _DYNAMIC + Type: STT_OBJECT + Section: .dynamic + Value: 0x403E50 + - Name: __init_array_start + Section: .init_array + Value: 0x403E40 + - Name: __GNU_EH_FRAME_HDR + Section: .eh_frame_hdr + Value: 0x402004 + - Name: _GLOBAL_OFFSET_TABLE_ + Type: STT_OBJECT + Section: .got.plt + Value: 0x404000 + - Name: __libc_csu_fini + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401220 + Size: 0x1 + - Name: data_start + Section: .data + Binding: STB_WEAK + Value: 0x404018 + - Name: baz + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401130 + Size: 0xE + - Name: _edata + Section: .data + Binding: STB_GLOBAL + Value: 0x404028 + - Name: bar + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401120 + Size: 0x6 + - Name: _fini + Type: STT_FUNC + Section: .fini + Binding: STB_GLOBAL + Value: 0x401224 + Other: [ STV_HIDDEN ] + - Name: '__libc_start_main@GLIBC_2.2.5' + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: __data_start + Section: .data + Binding: STB_GLOBAL + Value: 0x404018 + - Name: __gmon_start__ + Binding: STB_WEAK + - Name: __dso_handle + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x404020 + Other: [ STV_HIDDEN ] + - Name: _IO_stdin_used + Type: STT_OBJECT + Section: .rodata + Binding: STB_GLOBAL + Value: 0x402000 + Size: 0x4 + - Name: __libc_csu_init + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x4011C0 + Size: 0x5D + - Name: foo + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401110 + Size: 0x6 + - Name: _end + Section: .bss + Binding: STB_GLOBAL + Value: 0x404030 + - Name: _dl_relocate_static_pie + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401050 + Size: 0x1 + Other: [ STV_HIDDEN ] + - Name: _start + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401020 + Size: 0x2B + - Name: __bss_start + Section: .bss + Binding: STB_GLOBAL + Value: 0x404028 + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x401140 + Size: 0x72 + - Name: __TMC_END__ + Type: STT_OBJECT + Section: .data + Binding: STB_GLOBAL + Value: 0x404028 + Other: [ STV_HIDDEN ] + - Name: _init + Type: STT_FUNC + Section: .init + Binding: STB_GLOBAL + Value: 0x401000 + Other: [ STV_HIDDEN ] +DynamicSymbols: + - Name: __libc_start_main + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: __gmon_start__ + Binding: STB_WEAK +... diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index c97e06f3ed173..231205c48b47d 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -45,6 +45,10 @@ defm build_id : def : Flag<["-"], "a">, Alias, HelpText<"Alias for --archive-headers">; +def call_graph_info : Flag<["--"], "call-graph-info">, + HelpText<"Dump call graph information including indirect call and target IDs " + "from call graph section, if available.">; + def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; def : Flag<["-"], "C">, Alias, HelpText<"Alias for --demangle">; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index 0316c4ba51da6..c42d71030273f 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -301,6 +301,7 @@ static uint64_t AdjustVMA; static bool AllHeaders; static std::string ArchName; bool objdump::ArchiveHeaders; +static bool CallGraphInfo; bool objdump::Demangle; bool objdump::Disassemble; bool objdump::DisassembleAll; @@ -348,6 +349,38 @@ static bool Wide; std::string objdump::Prefix; uint32_t objdump::PrefixStrip; +// Enumeration of function kinds, and their mapping to function kind values +// from call graph section (.callgraph). +// Must stay in sync with enum from llvm/include/llvm/CodeGen/AsmPrinter.h. +enum FunctionKind { + // Function cannot be target to indirect calls. + NOT_INDIRECT_TARGET = 0, + // Function may be target to indirect calls but its type id is unknown. + INDIRECT_TARGET_UNKNOWN_TID = 1, + // Function may be target to indirect calls and its type id is known. + INDIRECT_TARGET_KNOWN_TID = 2, + + // Available in the binary but not listed in the call graph section. + NOT_LISTED = -1, +}; + +struct FunctionInfo { + std::string Name; + FunctionKind Kind; + struct DirectCallSite { + uint64_t CallSite; + uint64_t Callee; + DirectCallSite(uint64_t CallSite, uint64_t Callee) + : CallSite(CallSite), Callee(Callee) {} + }; + SmallVector DirectCallSites; + SmallVector IndirectCallSites; +}; +// Map function entry pc to function info. +MapVector FuncInfo; +// Set of all indirect call sites. +DenseSet IndirectCallSites; + DebugFormat objdump::DbgVariables = DFDisabled; DebugFormat objdump::DbgInlinedFunctions = DFDisabled; @@ -1939,7 +1972,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, } for (const SectionRef &Section : ToolSectionFilter(Obj)) { - if (FilterSections.empty() && !DisassembleAll && + if (((FilterSections.empty() && !DisassembleAll) || CallGraphInfo) && (!Section.isText() || Section.isVirtual())) continue; @@ -2253,6 +2286,14 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, BBAddrMapLabels); } + if (CallGraphInfo && Symbols[SI].Type == ELF::STT_FUNC) { + auto FuncPc = Symbols[SI].Addr; + auto FuncName = Symbols[SI].Name.str(); + FuncInfo[FuncPc].Name = FuncName; + // Initalize to be later updated while parsing the call graph section. + FuncInfo[FuncPc].Kind = NOT_LISTED; + } + if (DT->InstrAnalysis) DT->InstrAnalysis->resetState(); @@ -2374,6 +2415,59 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, {ThisAddr + Size, Section.getIndex()}, Index + Size != End); + if (CallGraphInfo) { + auto MIA = DT->InstrAnalysis.get(); + 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 = SectionAddr + Start + VMAAdjustment; + // 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; + 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++; + } + } + // 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. + IndirectCallSites.insert(CallSitePc); + 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); + } + + if(FuncInfo[CallerPc].Name.empty()) { + for (size_t Index = 0; Index < SymbolsHere.size(); ++Index) { + if (SymbolsHere[Index].Addr == CallerPc) { + FuncInfo[CallerPc].Name = SymNamesHere[Index]; + } + } + } + } + } + DT->InstPrinter->setCommentStream(CommentStream); DT->Printer->printInst( @@ -3131,6 +3225,207 @@ void Dumper::printSymbol(const SymbolRef &Symbol, outs() << ' ' << SymName << '\n'; } +static void printCallGraphInfo(ObjectFile *Obj) { + // Get function info through disassembly. + disassembleObject(Obj, /*InlineRelocs=*/false, outs()); + + // Get the .callgraph section. + StringRef CallGraphSectionName(".callgraph"); + std::optional CallGraphSection; + for (auto Sec : ToolSectionFilter(*Obj)) { + StringRef Name; + if (Expected NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == CallGraphSectionName) { + CallGraphSection = Sec; + break; + } + } + if (!CallGraphSection) + reportWarning("there is no .callgraph section", Obj->getFileName()); + + // Map type id to indirect call sites. + MapVector> TypeIdToIndirCallSites; + // Map type id to indirect targets. + MapVector> TypeIdToIndirTargets; + // Instructions that are not indirect calls but have a type id are ignored. + uint64_t IgnoredICallIdCount = 0; + // Number of valid indirect calls with type ids. + uint64_t ICallWithTypeIdCount = 0; + if (CallGraphSection) { + StringRef CGSecContents = unwrapOrError( + CallGraphSection.value().getContents(), Obj->getFileName()); + // TODO: some entries are written in pointer size. are they always 64-bit? + if (CGSecContents.size() % sizeof(uint64_t)) + reportError(Obj->getFileName(), "Malformed .callgraph section."); + + size_t Size = CGSecContents.size() / sizeof(uint64_t); + auto *It = reinterpret_cast(CGSecContents.data()); + const auto *const End = It + Size; + + auto CGHasNext = [&]() { return It < End; }; + auto CGNext = [&]() -> uint64_t { + if (!CGHasNext()) + reportError(Obj->getFileName(), "Malformed .callgraph section."); + return *It++; + }; + + // Parse the content + while (CGHasNext()) { + // Format version number. + uint64_t FormatVersionNumber = CGNext(); + if (FormatVersionNumber != 0) + reportError(Obj->getFileName(), + "Unknown format version in .callgraph section."); + + // Function entry pc. + uint64_t FuncEntryPc = CGNext(); + if (!FuncInfo.count(FuncEntryPc)) + reportError(Obj->getFileName(), + "Invalid function entry pc in .callgraph section."); + + // Function kind. + uint64_t Kind = CGNext(); + switch (Kind) { + case 0: // not an indirect target + FuncInfo[FuncEntryPc].Kind = NOT_INDIRECT_TARGET; + break; + case 1: // indirect target with unknown type id + FuncInfo[FuncEntryPc].Kind = INDIRECT_TARGET_UNKNOWN_TID; + break; + case 2: // indirect target with known type id + FuncInfo[FuncEntryPc].Kind = INDIRECT_TARGET_KNOWN_TID; + TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc); + break; + default: + reportError(Obj->getFileName(), + "Unknown function kind in .callgraph section."); + } + + // Read call sites. + uint64_t CallSiteCount = CGNext(); + for (unsigned long I = 0; I < CallSiteCount; I++) { + uint64_t TypeId = CGNext(); + uint64_t CallSitePc = CGNext(); + if (IndirectCallSites.count(CallSitePc)) { + TypeIdToIndirCallSites[TypeId].push_back(CallSitePc); + ICallWithTypeIdCount++; + } else { + // FIXME: .callgraph may have type ids for calls that are lowered to + // jump, which is why ignoring indirect call type ids are expected. + // However, actual indirect call site references stored as relocation + // entries here may also be discarded. + IgnoredICallIdCount++; + } + } + } + + // Print any required warnings regarding the callgraph section. + if (IgnoredICallIdCount) + reportWarning("callgraph section has type ids for " + + std::to_string(IgnoredICallIdCount) + " instructions " + + "which are not indirect calls", + Obj->getFileName()); + + if (auto ICallWithoutTypeIdCount = + IndirectCallSites.size() - ICallWithTypeIdCount) + reportWarning("callgraph section does not have type ids for " + + std::to_string(ICallWithoutTypeIdCount) + + " indirect calls", + Obj->getFileName()); + + uint64_t NotListedCount = 0; + uint64_t UnknownCount = 0; + for (const auto &El : FuncInfo) { + NotListedCount += El.second.Kind == NOT_LISTED; + UnknownCount += El.second.Kind == INDIRECT_TARGET_UNKNOWN_TID; + } + if (NotListedCount) + reportWarning("callgraph section does not have information for " + + std::to_string(NotListedCount) + " functions", + Obj->getFileName()); + if (UnknownCount) + reportWarning("callgraph section has unknown type id for " + + std::to_string(UnknownCount) + " indirect targets", + Obj->getFileName()); + + // Print indirect targets + outs() << "\nINDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,])"; + + // Print indirect targets with unknown type. + // For completeness, functions for which the call graph section does not + // provide information are included. + if (NotListedCount || UnknownCount) { + outs() << "\nUNKNOWN"; + for (const auto &El : FuncInfo) { + uint64_t FuncEntryPc = El.first; + FunctionKind FuncKind = El.second.Kind; + if (FuncKind == NOT_LISTED || FuncKind == INDIRECT_TARGET_UNKNOWN_TID) + outs() << " " << format("%lx", FuncEntryPc); + } + } + + // Print indirect targets to type id mapping. + for (const auto &El : TypeIdToIndirTargets) { + uint64_t TypeId = El.first; + outs() << "\n" << format("%lx", TypeId); + for (uint64_t IndirTargetPc : El.second) + outs() << " " << format("%lx", IndirTargetPc); + } + + // Print indirect calls to type id mapping. Any indirect call without a + // type id can be deduced by comparing this list to indirect call sites + // list. + outs() << "\n\nINDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,])"; + for (const auto &El : TypeIdToIndirCallSites) { + uint64_t TypeId = El.first; + outs() << "\n" << format("%lx", TypeId); + for (uint64_t IndirCallSitePc : El.second) + outs() << " " << format("%lx", IndirCallSitePc); + } + } + + // 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 (const FunctionInfo::DirectCallSite &DCS : FuncDirCallSites) { + outs() << " " << format("%lx", DCS.CallSite) << " " + << format("%lx", DCS.Callee); + } + } + } + + // Print function entry pc to function name mapping. + outs() << "\n\nFUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME)"; + for (const auto &El : FuncInfo) { + uint64_t EntryPc = El.first; + const auto &Name = El.second.Name; + outs() << "\n" << format("%lx", EntryPc) << " " << Name; + } + outs() << "\n"; +} + static void printUnwindInfo(const ObjectFile *O) { outs() << "Unwind info:\n\n"; @@ -3405,6 +3700,8 @@ static void dumpObject(ObjectFile *O, const Archive *A = nullptr, printRawClangAST(O); if (FaultMapSection) printFaultMaps(O); + if (CallGraphInfo) + printCallGraphInfo(O); if (Offloading) dumpOffloadBinary(*O, StringRef(ArchName)); } @@ -3571,6 +3868,7 @@ static void parseObjdumpOptions(const llvm::opt::InputArgList &InputArgs) { AllHeaders = InputArgs.hasArg(OBJDUMP_all_headers); ArchName = InputArgs.getLastArgValue(OBJDUMP_arch_name_EQ).str(); ArchiveHeaders = InputArgs.hasArg(OBJDUMP_archive_headers); + CallGraphInfo = InputArgs.hasArg(OBJDUMP_call_graph_info); Demangle = InputArgs.hasArg(OBJDUMP_demangle); Disassemble = InputArgs.hasArg(OBJDUMP_disassemble); DisassembleAll = InputArgs.hasArg(OBJDUMP_disassemble_all); @@ -3812,6 +4110,7 @@ int llvm_objdump_main(int argc, char **argv, const llvm::ToolContext &) { !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST && !Relocations && !SectionHeaders && !SectionContents && !SymbolTable && !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading && + !CallGraphInfo && !(MachOOpt && (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId || DylibsUsed || ExportsTrie || FirstPrivateHeader || From f5613af85f9d4ad3da4b46b8683cbe88276ab158 Mon Sep 17 00:00:00 2001 From: prabhukr Date: Tue, 29 Jul 2025 02:04:16 +0000 Subject: [PATCH 2/3] Objdump callgraph section disassembly bug fixes and formatting. --- llvm/tools/llvm-objdump/llvm-objdump.cpp | 65 ++++++++++++++---------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp index c42d71030273f..7a67815a7d3b8 100644 --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -352,7 +352,7 @@ uint32_t objdump::PrefixStrip; // Enumeration of function kinds, and their mapping to function kind values // from call graph section (.callgraph). // Must stay in sync with enum from llvm/include/llvm/CodeGen/AsmPrinter.h. -enum FunctionKind { +enum class FunctionKind : uint64_t { // Function cannot be target to indirect calls. NOT_INDIRECT_TARGET = 0, // Function may be target to indirect calls but its type id is unknown. @@ -361,7 +361,7 @@ enum FunctionKind { INDIRECT_TARGET_KNOWN_TID = 2, // Available in the binary but not listed in the call graph section. - NOT_LISTED = -1, + NOT_LISTED = 3, }; struct FunctionInfo { @@ -2162,6 +2162,8 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, if (!PrintedSection) { PrintedSection = true; OS << "\nDisassembly of section "; + // llvm::report_fatal_error("NO PRINTING BUSINESS FOR + // MEeeeeeeeeeeeeeeeeee"); if (!SegmentName.empty()) OS << SegmentName << ","; OS << SectionName << ":\n"; @@ -2208,6 +2210,14 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, for (size_t SHI = 0; SHI < SymbolsHere.size(); ++SHI) { SymbolInfoTy Symbol = SymbolsHere[SHI]; + if (CallGraphInfo && Symbol.Type == ELF::STT_FUNC) { + auto FuncPc = Symbol.Addr; + auto FuncName = Symbol.Name.str(); + FuncInfo[FuncPc].Name = FuncName; + // Initalize to be later updated while parsing the call graph section. + FuncInfo[FuncPc].Kind = FunctionKind::NOT_LISTED; + } + Expected RespondedOrErr = DT->DisAsm->onSymbolStart( Symbol, Size, Bytes.slice(Start, End - Start), SectionAddr + Start); @@ -2286,14 +2296,6 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, BBAddrMapLabels); } - if (CallGraphInfo && Symbols[SI].Type == ELF::STT_FUNC) { - auto FuncPc = Symbols[SI].Addr; - auto FuncName = Symbols[SI].Name.str(); - FuncInfo[FuncPc].Name = FuncName; - // Initalize to be later updated while parsing the call graph section. - FuncInfo[FuncPc].Kind = NOT_LISTED; - } - if (DT->InstrAnalysis) DT->InstrAnalysis->resetState(); @@ -2420,7 +2422,7 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, 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. + // as it appears on the stack trace. uint64_t CallSitePc = SectionAddr + Index + Size; uint64_t CallerPc = SectionAddr + Start + VMAAdjustment; // Check the operands to decide whether this is an direct or @@ -2440,10 +2442,10 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, } // 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)."); + (!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. IndirectCallSites.insert(CallSitePc); @@ -2452,21 +2454,21 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj, // Direct call. uint64_t CalleePc; bool Res = MIA->evaluateBranch(Inst, SectionAddr + Index, Size, - CalleePc); + CalleePc); assert(Res && "Failed to evaluate direct call target address."); FuncInfo[CallerPc].DirectCallSites.emplace_back(CallSitePc, CalleePc); } - if(FuncInfo[CallerPc].Name.empty()) { + if (FuncInfo[CallerPc].Name.empty()) { for (size_t Index = 0; Index < SymbolsHere.size(); ++Index) { if (SymbolsHere[Index].Addr == CallerPc) { FuncInfo[CallerPc].Name = SymNamesHere[Index]; } - } - } + } + } } - } + } DT->InstPrinter->setCommentStream(CommentStream); @@ -3226,8 +3228,9 @@ void Dumper::printSymbol(const SymbolRef &Symbol, } static void printCallGraphInfo(ObjectFile *Obj) { - // Get function info through disassembly. - disassembleObject(Obj, /*InlineRelocs=*/false, outs()); + // Get function info through disassembly. Suppress disassembler outputs to + // console. + disassembleObject(Obj, /*InlineRelocs=*/false, nulls()); // Get the .callgraph section. StringRef CallGraphSectionName(".callgraph"); @@ -3291,13 +3294,13 @@ static void printCallGraphInfo(ObjectFile *Obj) { uint64_t Kind = CGNext(); switch (Kind) { case 0: // not an indirect target - FuncInfo[FuncEntryPc].Kind = NOT_INDIRECT_TARGET; + FuncInfo[FuncEntryPc].Kind = FunctionKind::NOT_INDIRECT_TARGET; break; case 1: // indirect target with unknown type id - FuncInfo[FuncEntryPc].Kind = INDIRECT_TARGET_UNKNOWN_TID; + FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_UNKNOWN_TID; break; case 2: // indirect target with known type id - FuncInfo[FuncEntryPc].Kind = INDIRECT_TARGET_KNOWN_TID; + FuncInfo[FuncEntryPc].Kind = FunctionKind::INDIRECT_TARGET_KNOWN_TID; TypeIdToIndirTargets[CGNext()].push_back(FuncEntryPc); break; default: @@ -3339,9 +3342,14 @@ static void printCallGraphInfo(ObjectFile *Obj) { uint64_t NotListedCount = 0; uint64_t UnknownCount = 0; + + llvm::sort(FuncInfo, + [](const auto &A, const auto &B) { return A.first < B.first; }); + for (const auto &El : FuncInfo) { - NotListedCount += El.second.Kind == NOT_LISTED; - UnknownCount += El.second.Kind == INDIRECT_TARGET_UNKNOWN_TID; + NotListedCount += El.second.Kind == FunctionKind::NOT_LISTED; + UnknownCount += + El.second.Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID; } if (NotListedCount) reportWarning("callgraph section does not have information for " + @@ -3363,7 +3371,8 @@ static void printCallGraphInfo(ObjectFile *Obj) { for (const auto &El : FuncInfo) { uint64_t FuncEntryPc = El.first; FunctionKind FuncKind = El.second.Kind; - if (FuncKind == NOT_LISTED || FuncKind == INDIRECT_TARGET_UNKNOWN_TID) + if (FuncKind == FunctionKind::NOT_LISTED || + FuncKind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) outs() << " " << format("%lx", FuncEntryPc); } } From 8a60a270dc8ef934caf139a16c24f15aa62d7cc7 Mon Sep 17 00:00:00 2001 From: prabhukr Date: Tue, 29 Jul 2025 02:04:36 +0000 Subject: [PATCH 3/3] Formatting fixes in objdumpopts file. --- llvm/tools/llvm-objdump/ObjdumpOpts.td | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td index 231205c48b47d..cb9e0d15fca65 100644 --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -45,9 +45,11 @@ defm build_id : def : Flag<["-"], "a">, Alias, HelpText<"Alias for --archive-headers">; -def call_graph_info : Flag<["--"], "call-graph-info">, - HelpText<"Dump call graph information including indirect call and target IDs " - "from call graph section, if available.">; +def call_graph_info + : Flag<["--"], "call-graph-info">, + HelpText< + "Dump call graph information including indirect call and target IDs " + "from call graph section, if available.">; def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; def : Flag<["-"], "C">, Alias, HelpText<"Alias for --demangle">;