diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test new file mode 100644 index 0000000000000..0130c4f35791e --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-callgraph-section.test @@ -0,0 +1,191 @@ +## Tests --call-graph-info prints information from call graph section. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --match-full-lines +# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=LLVM +# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=JSON + +# CHECK: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets. +# CHECK-NEXT: Per-function call graph information:: +# CHECK-EMPTY: +# CHECK-NEXT: Function:: foo +# CHECK-NEXT: Function PC:: 0x0 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: NOT_INDIRECT +# CHECK-NEXT: Indirect callee count:: 0 +# CHECK-NEXT: Direct callee count:: 1 +# CHECK-NEXT: { +# CHECK-NEXT: CalleePC:: 0x5 +# CHECK-NEXT: } +# CHECK-EMPTY: +# CHECK-NEXT: Function:: bar +# CHECK-NEXT: Function PC:: 0x6 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: UNKNOWN_TID +# CHECK-NEXT: Indirect callee count:: 1 +# CHECK-NEXT: { +# CHECK-NEXT: callsite: 0x9 +# CHECK-NEXT: calleeTypeId: 0x10 +# CHECK-NEXT: } +# CHECK-NEXT: Direct callee count:: 0 +# CHECK-EMPTY: +# CHECK-NEXT: Function:: baz +# CHECK-NEXT: Function PC:: 0xa +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: KNOWN_TID +# CHECK-NEXT: Function Type ID:: 0x20 +# CHECK-NEXT: Indirect callee count:: 0 +# CHECK-NEXT: Direct callee count:: 0 + + +# LLVM: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets. +# LLVM: callgraph_info [ +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: foo +# LLVM-NEXT: Address: 0x0 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: NOT_INDIRECT +# LLVM-NEXT: Kind: 0 +# LLVM-NEXT: NumIndirectCallSites: 0 +# LLVM-NEXT: NumDirectCallSites: 1 +# LLVM-NEXT: DirectCallees [ +# LLVM-NEXT: Entry { +# LLVM-NEXT: Address: 0x5 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: bar +# LLVM-NEXT: Address: 0x6 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: UNKNOWN_TID +# LLVM-NEXT: Kind: 1 +# LLVM-NEXT: NumIndirectCallSites: 1 +# LLVM-NEXT: IndirectCallsites [ +# LLVM-NEXT: IndirectCallsite { +# LLVM-NEXT: Address: 0x9 +# LLVM-NEXT: TypeId: 0x10 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: NumDirectCallSites: 0 +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: baz +# LLVM-NEXT: Address: 0xA +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: KNOWN_TID +# LLVM-NEXT: Kind: 2 +# LLVM-NEXT: TypeId: 0x20 +# LLVM-NEXT: NumIndirectCallSites: 0 +# LLVM-NEXT: NumDirectCallSites: 0 +# LLVM-NEXT: } +# LLVM-NEXT: ] + +# JSON: {{.*}}llvm-readelf{{.*}}: warning: '[[FILE]]': .callgraph section has unknown type id for 1 indirect targets. +# JSON: "callgraph_info": [ +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "foo", +# JSON-NEXT: "Address": 0, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "NOT_INDIRECT", +# JSON-NEXT: "Kind": 0, +# JSON-NEXT: "NumIndirectCallSites": 0, +# JSON-NEXT: "NumDirectCallSites": 1, +# JSON-NEXT: "DirectCallees": [ +# JSON-NEXT: { +# JSON-NEXT: "Entry": { +# JSON-NEXT: "Address": 5 +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ] +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "bar", +# JSON-NEXT: "Address": 6, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "UNKNOWN_TID", +# JSON-NEXT: "Kind": 1, +# JSON-NEXT: "NumIndirectCallSites": 1, +# JSON-NEXT: "IndirectCallsites": [ +# JSON-NEXT: { +# JSON-NEXT: "IndirectCallsite": { +# JSON-NEXT: "Address": 9, +# JSON-NEXT: "TypeId": 16 +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ], +# JSON-NEXT: "NumDirectCallSites": 0 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "baz", +# JSON-NEXT: "Address": 10, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "KNOWN_TID", +# JSON-NEXT: "Kind": 2, +# JSON-NEXT: "TypeId": 32, +# JSON-NEXT: "NumIndirectCallSites": 0, +# JSON-NEXT: "NumDirectCallSites": 0 +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ] + + +.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 1 #< Count of direct callees that follow: 1> +.quad 5 #< Direct callee foo's address> + +.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 #< Count of direct call sites that follow: 0> + +.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: 0>. +.quad 0 #< Count of direct call sites that follow: 0> + +# No call graph section entry for qux. +# Technically its "UNKNOWN" type id but will not be printed as such by llvm-readelf. + +.text diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test new file mode 100644 index 0000000000000..2956f5317d22b --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-format-version.test @@ -0,0 +1,21 @@ +## Tests that --call-graph-info fails if .callgraph section has unknown format +## version number. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR + +# ERR: {{.*}}llvm-readelf{{.*}}: error: 'Unknown value': Unknown format version value [1] 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-readobj/ELF/call-graph-info-err-invalid-func-kind.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test new file mode 100644 index 0000000000000..985e00f2aac60 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-invalid-func-kind.test @@ -0,0 +1,23 @@ +## Tests that --call-graph-info fails if .callgraph section has invalid +## function kind value. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR + +# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [FunctionKind] for function at [0x0]': Unknown value [4]. + +.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 4 #< Invalid function kind: must be one of 0, 1, 2, 3. +.text diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test new file mode 100644 index 0000000000000..0f2528da803e1 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section.test @@ -0,0 +1,25 @@ +## Tests that --call-graph-info fails if .callgraph section has invalid size. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR + +# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [number of indirect callsites] for function at [0x0]': unexpected end of data at offset 0x19 while reading [0x18, 0x20) + +.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-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test new file mode 100644 index 0000000000000..105831ec9dd65 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section2.test @@ -0,0 +1,25 @@ +## 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. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR + +# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [indirect target type id] for function at [0x0]': unexpected end of data at offset 0x20 while reading [0x20, 0x28) + +.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 1 #< Indirect call site count that follows. + #< Missing indirect call entries here. +.text diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test new file mode 100644 index 0000000000000..f2d682d23217a --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-malformed-callgraph-section3.test @@ -0,0 +1,27 @@ +## 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. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t --check-prefix=ERR + +# ERR: {{.*}}llvm-readelf{{.*}}: error: 'While reading call graph info's [direct callee PC] for function at [0x0]': unexpected end of data at offset 0x28 while reading [0x28, 0x30) + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov + retq + +.section .callgraph,"o",@progbits,.text +.quad 0 #< Format version number. +.quad 0 #< Function entry address. +.quad 0 #< Function kind. +.quad 0 #< Indirect call site count. +.quad 1 #< Direct call site count that follows. + #< Missing direct call entries here. +.text diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test new file mode 100644 index 0000000000000..28e23e8db6f6d --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info-err-no-callgraph-section.test @@ -0,0 +1,16 @@ +## Tests that --call-graph-info warns if there is no .callgraph section. + +# REQUIRES: x86-registered-target + +# RUN: llvm-mc %s -filetype=obj -triple=x86_64-pc-linux -o %t +# RUN: not llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t +# RUN: not llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t +# RUN: not llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t + +# CHECK: {{.*}}llvm-readelf{{.*}}: error: 'Missing section': No .callgraph section found. + +.text +.globl _Z3foov +.type _Z3foov,@function +_Z3foov: + callq _Z3foov@PLT diff --git a/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test new file mode 100644 index 0000000000000..594a3b9650dfe --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/call-graph-info.test @@ -0,0 +1,765 @@ +## Tests how --call-graph-info prints the call graph information. +# RUN: yaml2obj --docnum=1 %s -o %t +# RUN: llvm-readelf --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines -DFILE=%t +# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=LLVM -DFILE=%t +# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s --match-full-lines --check-prefix=JSON -DFILE=%t + +# Yaml input is obtained by compiling the below source to object with: +# clang -fexperimental-call-graph-section test.c -o test.o +# then to yaml with: +# obj2yaml test.o > test.yaml +# Remove ProgramHeaders if obj2yaml fails. + +# 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: Per-function call graph information:: +# CHECK-EMPTY: +# CHECK-NEXT: Function:: foo +# CHECK-NEXT: Function PC:: 0x1790 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: KNOWN_TID +# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424 +# CHECK-NEXT: Indirect callee count:: 0 +# CHECK-NEXT: Direct callee count:: 0 +# CHECK-EMPTY: +# CHECK-NEXT: Function:: bar +# CHECK-NEXT: Function PC:: 0x17a0 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: KNOWN_TID +# CHECK-NEXT: Function Type ID:: 0x3ecbeef531f74424 +# CHECK-NEXT: Indirect callee count:: 0 +# CHECK-NEXT: Direct callee count:: 0 +# CHECK-EMPTY: +# CHECK-NEXT: Function:: baz +# CHECK-NEXT: Function PC:: 0x17b0 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: KNOWN_TID +# CHECK-NEXT: Function Type ID:: 0x308e4b8159bc8654 +# CHECK-NEXT: Indirect callee count:: 0 +# CHECK-NEXT: Direct callee count:: 0 +# CHECK-EMPTY: +# CHECK-NEXT: Function:: main +# CHECK-NEXT: Function PC:: 0x17c0 +# CHECK-NEXT: FormatVersionNumber:: 0 +# CHECK-NEXT: Function Kind:: KNOWN_TID +# CHECK-NEXT: Function Type ID:: 0xfa6809609a76afca +# CHECK-NEXT: Indirect callee count:: 3 +# CHECK-NEXT: { +# CHECK-NEXT: callsite: 0x17ef +# CHECK-NEXT: calleeTypeId: 0x3ecbeef531f74424 +# CHECK-NEXT: callsite: 0x17df +# CHECK-NEXT: calleeTypeId: 0x3ecbeef531f74424 +# CHECK-NEXT: callsite: 0x1804 +# CHECK-NEXT: calleeTypeId: 0x308e4b8159bc8654 +# CHECK-NEXT: } +# CHECK-NEXT: Direct callee count:: 3 +# CHECK-NEXT: { +# CHECK-NEXT: Callee:: foo +# CHECK-NEXT: CalleePC:: 0x1790 +# CHECK-NEXT: Callee:: bar +# CHECK-NEXT: CalleePC:: 0x17a0 +# CHECK-NEXT: Callee:: baz +# CHECK-NEXT: CalleePC:: 0x17b0 +# CHECK-NEXT: } + + +# LLVM: callgraph_info [ +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: foo +# LLVM-NEXT: Address: 0x1790 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: KNOWN_TID +# LLVM-NEXT: Kind: 2 +# LLVM-NEXT: TypeId: 0x3ECBEEF531F74424 +# LLVM-NEXT: NumIndirectCallSites: 0 +# LLVM-NEXT: NumDirectCallSites: 0 +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: bar +# LLVM-NEXT: Address: 0x17A0 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: KNOWN_TID +# LLVM-NEXT: Kind: 2 +# LLVM-NEXT: TypeId: 0x3ECBEEF531F74424 +# LLVM-NEXT: NumIndirectCallSites: 0 +# LLVM-NEXT: NumDirectCallSites: 0 +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: baz +# LLVM-NEXT: Address: 0x17B0 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: KNOWN_TID +# LLVM-NEXT: Kind: 2 +# LLVM-NEXT: TypeId: 0x308E4B8159BC8654 +# LLVM-NEXT: NumIndirectCallSites: 0 +# LLVM-NEXT: NumDirectCallSites: 0 +# LLVM-NEXT: } +# LLVM-NEXT: Function { +# LLVM-NEXT: Name: main +# LLVM-NEXT: Address: 0x17C0 +# LLVM-NEXT: Version: 0 +# LLVM-NEXT: KindStr: KNOWN_TID +# LLVM-NEXT: Kind: 2 +# LLVM-NEXT: TypeId: 0xFA6809609A76AFCA +# LLVM-NEXT: NumIndirectCallSites: 3 +# LLVM-NEXT: IndirectCallsites [ +# LLVM-NEXT: IndirectCallsite { +# LLVM-NEXT: Address: 0x17EF +# LLVM-NEXT: TypeId: 0x3ECBEEF531F74424 +# LLVM-NEXT: } +# LLVM-NEXT: IndirectCallsite { +# LLVM-NEXT: Address: 0x17DF +# LLVM-NEXT: TypeId: 0x3ECBEEF531F74424 +# LLVM-NEXT: } +# LLVM-NEXT: IndirectCallsite { +# LLVM-NEXT: Address: 0x1804 +# LLVM-NEXT: TypeId: 0x308E4B8159BC8654 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: NumDirectCallSites: 3 +# LLVM-NEXT: DirectCallees [ +# LLVM-NEXT: Entry { +# LLVM-NEXT: Name: foo +# LLVM-NEXT: Address: 0x1790 +# LLVM-NEXT: } +# LLVM-NEXT: Entry { +# LLVM-NEXT: Name: bar +# LLVM-NEXT: Address: 0x17A0 +# LLVM-NEXT: } +# LLVM-NEXT: Entry { +# LLVM-NEXT: Name: baz +# LLVM-NEXT: Address: 0x17B0 +# LLVM-NEXT: } +# LLVM-NEXT: ] +# LLVM-NEXT: } +# LLVM-NEXT: ] + + +# JSON: "callgraph_info": [ +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "foo", +# JSON-NEXT: "Address": 6032, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "KNOWN_TID", +# JSON-NEXT: "Kind": 2, +# JSON-NEXT: "TypeId": 4524972987496481828, +# JSON-NEXT: "NumIndirectCallSites": 0, +# JSON-NEXT: "NumDirectCallSites": 0 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "bar", +# JSON-NEXT: "Address": 6048, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "KNOWN_TID", +# JSON-NEXT: "Kind": 2, +# JSON-NEXT: "TypeId": 4524972987496481828, +# JSON-NEXT: "NumIndirectCallSites": 0, +# JSON-NEXT: "NumDirectCallSites": 0 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "baz", +# JSON-NEXT: "Address": 6064, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "KNOWN_TID", +# JSON-NEXT: "Kind": 2, +# JSON-NEXT: "TypeId": 3498816979441845844, +# JSON-NEXT: "NumIndirectCallSites": 0, +# JSON-NEXT: "NumDirectCallSites": 0 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Function": { +# JSON-NEXT: "Name": "main", +# JSON-NEXT: "Address": 6080, +# JSON-NEXT: "Version": 0, +# JSON-NEXT: "KindStr": "KNOWN_TID", +# JSON-NEXT: "Kind": 2, +# JSON-NEXT: "TypeId": 18043682217572872138, +# JSON-NEXT: "NumIndirectCallSites": 3, +# JSON-NEXT: "IndirectCallsites": [ +# JSON-NEXT: { +# JSON-NEXT: "IndirectCallsite": { +# JSON-NEXT: "Address": 6127, +# JSON-NEXT: "TypeId": 4524972987496481828 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "IndirectCallsite": { +# JSON-NEXT: "Address": 6111, +# JSON-NEXT: "TypeId": 4524972987496481828 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "IndirectCallsite": { +# JSON-NEXT: "Address": 6148, +# JSON-NEXT: "TypeId": 3498816979441845844 +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ], +# JSON-NEXT: "NumDirectCallSites": 3, +# JSON-NEXT: "DirectCallees": [ +# JSON-NEXT: { +# JSON-NEXT: "Entry": { +# JSON-NEXT: "Name": "foo", +# JSON-NEXT: "Address": 6032 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Entry": { +# JSON-NEXT: "Name": "bar", +# JSON-NEXT: "Address": 6048 +# JSON-NEXT: } +# JSON-NEXT: }, +# JSON-NEXT: { +# JSON-NEXT: "Entry": { +# JSON-NEXT: "Name": "baz", +# JSON-NEXT: "Address": 6064 +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ] +# JSON-NEXT: } +# JSON-NEXT: } +# JSON-NEXT: ] + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_DYN + Machine: EM_X86_64 + Entry: 0x16D0 +ProgramHeaders: + - Type: PT_PHDR + Flags: [ PF_R ] + VAddr: 0x40 + Align: 0x8 + Offset: 0x40 + - Type: PT_INTERP + Flags: [ PF_R ] + FirstSec: .interp + LastSec: .interp + VAddr: 0x2A8 + Offset: 0x2A8 + - Type: PT_LOAD + Flags: [ PF_R ] + FirstSec: .interp + LastSec: .eh_frame + Align: 0x1000 + Offset: 0x0 + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + FirstSec: .text + LastSec: .plt + VAddr: 0x16D0 + Align: 0x1000 + Offset: 0x6D0 + - Type: PT_LOAD + Flags: [ PF_W, PF_R ] + FirstSec: .init_array + LastSec: .relro_padding + VAddr: 0x2890 + Align: 0x1000 + Offset: 0x890 + - Type: PT_LOAD + Flags: [ PF_W, PF_R ] + FirstSec: .data + LastSec: .bss + VAddr: 0x3A68 + Align: 0x1000 + Offset: 0xA68 + - Type: PT_DYNAMIC + Flags: [ PF_W, PF_R ] + FirstSec: .dynamic + LastSec: .dynamic + VAddr: 0x28A0 + Align: 0x8 + Offset: 0x8A0 + - Type: PT_GNU_RELRO + Flags: [ PF_R ] + FirstSec: .init_array + LastSec: .relro_padding + VAddr: 0x2890 + Offset: 0x890 + - Type: PT_GNU_EH_FRAME + Flags: [ PF_R ] + FirstSec: .eh_frame_hdr + LastSec: .eh_frame_hdr + VAddr: 0x584 + Align: 0x4 + Offset: 0x584 + - Type: PT_GNU_STACK + Flags: [ PF_W, PF_R ] + Align: 0x0 + Offset: 0x0 + - Type: PT_NOTE + Flags: [ PF_R ] + FirstSec: .note.ABI-tag + LastSec: .note.gnu.build-id + VAddr: 0x2C4 + Align: 0x4 + Offset: 0x2C4 +Sections: + - Name: .interp + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x2A8 + AddressAlign: 0x1 + Content: 2F6C696236342F6C642D6C696E75782D7838362D36342E736F2E3200 + - Name: .note.ABI-tag + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x2C4 + AddressAlign: 0x4 + Notes: + - Name: GNU + Desc: '00000000030000000200000000000000' + Type: NT_VERSION + - Name: .note.gnu.build-id + Type: SHT_NOTE + Flags: [ SHF_ALLOC ] + Address: 0x2E4 + AddressAlign: 0x4 + Notes: + - Name: GNU + Desc: EB389D8FA0BAAB684147C8107EEA8143CBF335AC + Type: NT_PRPSINFO + - Name: .dynsym + Type: SHT_DYNSYM + Flags: [ SHF_ALLOC ] + Address: 0x308 + Link: .dynstr + AddressAlign: 0x8 + - Name: .gnu.version + Type: SHT_GNU_versym + Flags: [ SHF_ALLOC ] + Address: 0x398 + Link: .dynsym + AddressAlign: 0x2 + Entries: [ 0, 2, 1, 1, 3, 1 ] + - Name: .gnu.version_r + Type: SHT_GNU_verneed + Flags: [ SHF_ALLOC ] + Address: 0x3A4 + Link: .dynstr + AddressAlign: 0x4 + Dependencies: + - Version: 1 + File: libc.so.6 + Entries: + - Name: GLIBC_2.2.5 + Hash: 157882997 + Flags: 0 + Other: 3 + - Name: GLIBC_2.34 + Hash: 110530996 + Flags: 0 + Other: 2 + - Name: .gnu.hash + Type: SHT_GNU_HASH + Flags: [ SHF_ALLOC ] + Address: 0x3D8 + Link: .dynsym + AddressAlign: 0x8 + Header: + SymNdx: 0x6 + Shift2: 0x1A + BloomFilter: [ 0x0 ] + HashBuckets: [ 0x0 ] + HashValues: [ ] + - Name: .dynstr + Type: SHT_STRTAB + Flags: [ SHF_ALLOC ] + Address: 0x3F4 + AddressAlign: 0x1 + - Name: .rela.dyn + Type: SHT_RELA + Flags: [ SHF_ALLOC ] + Address: 0x478 + Link: .dynsym + AddressAlign: 0x8 + Relocations: + - Offset: 0x2890 + Type: R_X86_64_RELATIVE + Addend: 5888 + - Offset: 0x2898 + Type: R_X86_64_RELATIVE + Addend: 5952 + - Offset: 0x3A70 + Type: R_X86_64_RELATIVE + Addend: 14960 + - Offset: 0x2A40 + Symbol: __libc_start_main + Type: R_X86_64_GLOB_DAT + - Offset: 0x2A48 + Symbol: __gmon_start__ + Type: R_X86_64_GLOB_DAT + - Offset: 0x2A50 + Symbol: __register_frame_info + Type: R_X86_64_GLOB_DAT + - Offset: 0x2A58 + Symbol: __cxa_finalize + Type: R_X86_64_GLOB_DAT + - Offset: 0x2A60 + Symbol: __deregister_frame_info + Type: R_X86_64_GLOB_DAT + - Name: .rela.plt + Type: SHT_RELA + Flags: [ SHF_ALLOC, SHF_INFO_LINK ] + Address: 0x538 + Link: .dynsym + AddressAlign: 0x8 + Info: .got.plt + Relocations: + - Offset: 0x3A90 + Symbol: __register_frame_info + Type: R_X86_64_JUMP_SLOT + - Offset: 0x3A98 + Symbol: __cxa_finalize + Type: R_X86_64_JUMP_SLOT + - Offset: 0x3AA0 + Symbol: __deregister_frame_info + Type: R_X86_64_JUMP_SLOT + - Name: .rodata + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE ] + Address: 0x580 + AddressAlign: 0x4 + EntSize: 0x4 + Content: '01000200' + - Name: .eh_frame_hdr + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x584 + AddressAlign: 0x4 + Content: 011B033B40000000070000004C1100005C0000007C11000088000000BC110000A80000000C120000C80000001C120000E80000002C120000080100003C12000028010000 + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x5C8 + AddressAlign: 0x8 + Content: 1400000000000000017A5200017810011B0C070890010710100000001C000000E810000022000000000000001400000000000000017A5200017810011B0C0708900100001C0000001C000000EC1000003800000000410E108602430D06730C07080000001C0000003C0000000C1100004A00000000410E108602430D0602450C070800001C0000005C0000003C1100000600000000410E108602430D06410C07080000001C0000007C0000002C1100000600000000410E108602430D06410C07080000001C0000009C0000001C1100000E00000000410E108602430D06490C07080000001C000000BC0000000C1100005F00000000410E108602430D06025A0C0708000000000000 + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x16D0 + AddressAlign: 0x10 + Content: 31ED4989D15E4889E24883E4F050544531C031C9488D3DD5000000FF154F130000F4CCCCCCCCCCCCCCCCCCCCCCCCCCCC554889E5F6059D230000017402EB27C6059223000001488B05331300004883F8007413488D3DCAEEFFFF488D357F230000E82A0100005DC30F1F840000000000554889E5F605A5230000017402EB39C6059A23000001488B05FB1200004883F800740C488B3D06230000E801010000488B05EA1200004883F800740C488D3D71EEFFFFE8F80000005DC3CCCCCCCCCCCC554889E55DC3662E0F1F840000000000554889E55DC3662E0F1F840000000000554889E54088F88845FF31C05DC36690554889E54883EC30C745FC00000000488D05BAFFFFFF488945F0B000FF55F0488D05BAFFFFFF488945E8B000FF55E8488D05BAFFFFFF488945D8488B45D80FBE7DE7FFD0E887FFFFFFE892FFFFFF0FBE7DE7E899FFFFFF31C04883C4305DC3CC + - Name: .init + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1820 + AddressAlign: 0x4 + Content: 4883EC08488B051D1200004885C07402FFD04883C408C3 + - Name: .fini + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1838 + AddressAlign: 0x4 + Content: 4883EC084883C408C3 + - Name: .plt + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1850 + AddressAlign: 0x10 + Content: FF352A220000FF252C2200000F1F4000FF252A2200006800000000E9E0FFFFFFFF25222200006801000000E9D0FFFFFFFF251A2200006802000000E9C0FFFFFF + - Name: .init_array + Type: SHT_INIT_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2890 + AddressAlign: 0x8 + Content: '0000000000000000' + - Name: .fini_array + Type: SHT_FINI_ARRAY + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2898 + AddressAlign: 0x8 + Content: '0000000000000000' + - Name: .dynamic + Type: SHT_DYNAMIC + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x28A0 + Link: .dynstr + AddressAlign: 0x8 + Entries: + - Tag: DT_NEEDED + Value: 0x5F + - Tag: DT_FLAGS_1 + Value: 0x8000000 + - Tag: DT_DEBUG + Value: 0x0 + - Tag: DT_RELA + Value: 0x478 + - Tag: DT_RELASZ + Value: 0xC0 + - Tag: DT_RELAENT + Value: 0x18 + - Tag: DT_RELACOUNT + Value: 0x3 + - Tag: DT_JMPREL + Value: 0x538 + - Tag: DT_PLTRELSZ + Value: 0x48 + - Tag: DT_PLTGOT + Value: 0x3A78 + - Tag: DT_PLTREL + Value: 0x7 + - Tag: DT_SYMTAB + Value: 0x308 + - Tag: DT_SYMENT + Value: 0x18 + - Tag: DT_STRTAB + Value: 0x3F4 + - Tag: DT_STRSZ + Value: 0x80 + - Tag: DT_GNU_HASH + Value: 0x3D8 + - Tag: DT_INIT_ARRAY + Value: 0x2890 + - Tag: DT_INIT_ARRAYSZ + Value: 0x8 + - Tag: DT_FINI_ARRAY + Value: 0x2898 + - Tag: DT_FINI_ARRAYSZ + Value: 0x8 + - Tag: DT_INIT + Value: 0x1820 + - Tag: DT_FINI + Value: 0x1838 + - Tag: DT_VERSYM + Value: 0x398 + - Tag: DT_VERNEED + Value: 0x3A4 + - Tag: DT_VERNEEDNUM + Value: 0x1 + - Tag: DT_NULL + Value: 0x0 + - Name: .got + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2A40 + AddressAlign: 0x8 + Content: '00000000000000000000000000000000000000000000000000000000000000000000000000000000' + - Name: .relro_padding + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x2A68 + AddressAlign: 0x1 + Size: 0x598 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x3A68 + AddressAlign: 0x8 + Content: '00000000000000000000000000000000' + - Name: .got.plt + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x3A78 + AddressAlign: 0x8 + Content: A02800000000000000000000000000000000000000000000661800000000000076180000000000008618000000000000 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x3AA8 + AddressAlign: 0x8 + Size: 0x49 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x1 + EntSize: 0x1 + Content: 004C696E6B65723A2046756368736961204C4C442032322E302E302028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E676974206530383561356338333634363166323964373366373339333433343437656439643533386265633129004675636873696120636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A5072616268756B2F6C6C766D2D70726F6A6563742E67697420653038356135633833363436316632396437336637333933343334343765643964353338626563312900 + - Name: .callgraph + Type: SHT_PROGBITS + Flags: [ SHF_LINK_ORDER ] + Link: .text + AddressAlign: 0x1 + Content: 0000000000000000901700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000A01700000000000002000000000000002444F731F5EECB3E000000000000000000000000000000000000000000000000B01700000000000002000000000000005486BC59814B8E30000000000000000000000000000000000000000000000000C0170000000000000200000000000000CAAF769A600968FA03000000000000002444F731F5EECB3EDF170000000000002444F731F5EECB3EEF170000000000005486BC59814B8E30041800000000000003000000000000009017000000000000A017000000000000B017000000000000 +Symbols: + - Name: __abi_tag + Type: STT_OBJECT + Section: .note.ABI-tag + Value: 0x2C4 + Size: 0x20 + - Name: crtbegin.c + Type: STT_FILE + Index: SHN_ABS + - Name: __do_init + Type: STT_FUNC + Section: .text + Value: 0x1700 + Size: 0x38 + - Name: __do_init.__initialized + Type: STT_OBJECT + Section: .bss + Value: 0x3AA8 + Size: 0x1 + - Name: __EH_FRAME_LIST__ + Type: STT_OBJECT + Section: .eh_frame + Value: 0x5F4 + - Name: __do_init.__object + Type: STT_OBJECT + Section: .bss + Value: 0x3AB0 + Size: 0x40 + - Name: __do_fini + Type: STT_FUNC + Section: .text + Value: 0x1740 + Size: 0x4A + - Name: __do_fini.__finalized + Type: STT_OBJECT + Section: .bss + Value: 0x3AF0 + Size: 0x1 + - Name: __init + Type: STT_OBJECT + Section: .init_array + Value: 0x2890 + Size: 0x8 + - Name: __fini + Type: STT_OBJECT + Section: .fini_array + Value: 0x2898 + Size: 0x8 + - Name: __dso_handle + Type: STT_OBJECT + Section: .data + Value: 0x3A70 + Size: 0x8 + Other: [ STV_HIDDEN ] + - Name: test.c + Type: STT_FILE + Index: SHN_ABS + - Name: crtend.c + Type: STT_FILE + Index: SHN_ABS + - Name: __EH_FRAME_LIST_END__ + Type: STT_OBJECT + Section: .eh_frame + Value: 0x5C8 + Size: 0x4 + Other: [ STV_HIDDEN ] + - Name: _GLOBAL_OFFSET_TABLE_ + Section: .got.plt + Value: 0x3A78 + Other: [ STV_HIDDEN ] + - Name: _DYNAMIC + Section: .dynamic + Value: 0x28A0 + Other: [ STV_HIDDEN ] + - Name: _init + Type: STT_FUNC + Section: .init + Value: 0x1820 + Other: [ STV_HIDDEN ] + - Name: _fini + Type: STT_FUNC + Section: .fini + Value: 0x1838 + Other: [ STV_HIDDEN ] + - Name: _start + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x16D0 + Size: 0x22 + - Name: main + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x17C0 + Size: 0x5F + - Name: data_start + Section: .data + Binding: STB_WEAK + Value: 0x3A68 + - Name: _IO_stdin_used + Type: STT_OBJECT + Section: .rodata + Binding: STB_GLOBAL + Value: 0x580 + Size: 0x4 + - Name: __libc_start_main + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: __data_start + Section: .data + Binding: STB_GLOBAL + Value: 0x3A68 + - Name: __gmon_start__ + Binding: STB_WEAK + - Name: __register_frame_info + Binding: STB_WEAK + - Name: __cxa_finalize + Type: STT_FUNC + Binding: STB_WEAK + - Name: __deregister_frame_info + Binding: STB_WEAK + - Name: foo + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x1790 + Size: 0x6 + - Name: bar + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x17A0 + Size: 0x6 + - Name: baz + Type: STT_FUNC + Section: .text + Binding: STB_GLOBAL + Value: 0x17B0 + Size: 0xE +DynamicSymbols: + - Name: __libc_start_main + Type: STT_FUNC + Binding: STB_GLOBAL + - Name: __gmon_start__ + Binding: STB_WEAK + - Name: __register_frame_info + Binding: STB_WEAK + - Name: __cxa_finalize + Type: STT_FUNC + Binding: STB_WEAK + - Name: __deregister_frame_info + Binding: STB_WEAK +... diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index 30102abdc5c5b..13e750ae34070 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -22,6 +22,7 @@ #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" @@ -47,7 +48,10 @@ #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Debug.h" #include "llvm/Support/Endian.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" @@ -74,6 +78,7 @@ #include #include #include +#include #include using namespace llvm; @@ -183,6 +188,29 @@ struct GroupSection { std::vector Members; }; +// Call graph function kind. +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. + 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 = 3, +}; + +// Per-function call graph information. +template struct FunctionCallgraphInfoImpl { + uint64_t FormatVersionNumber; + FunctionKind Kind; + uint64_t + FunctionTypeId; // Only if Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID + DenseMap IndirectCallsites; + SmallSet DirectCallees; +}; + namespace { struct NoteType { @@ -433,6 +461,23 @@ template class ELFDumper : public ObjDumper { const typename SFrameParser::FDERange::iterator FDE, ArrayRef> Relocations, const Elf_Shdr *RelocSymTab); + using FunctionCallgraphInfo = + ::FunctionCallgraphInfoImpl; + + // Callgraph - Main data structure to maintain per function callgraph + // information. + MapVector FuncCGInfos; + + // // Callgraph - 64 bit type id mapped to entry PC addresses of functions + // which + // // are of the given type id. + // MapVector> TypeIdToIndirTargets; + + // Callgraph - Read callgraph section and process its contents to populate + // Callgraph related data structures which will be used to dump callgraph + // info. Returns false if there is no .callgraph section in the input file. + bool processCallGraphSection(); + private: mutable SmallVector, 0> VersionMap; }; @@ -619,6 +664,7 @@ template class GNUELFDumper : public ELFDumper { void printVersionDefinitionSection(const Elf_Shdr *Sec) override; void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; + void printCallGraphInfo() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; void printAddrsig() override; void printNotes() override; @@ -730,6 +776,7 @@ template class LLVMELFDumper : public ELFDumper { void printVersionDefinitionSection(const Elf_Shdr *Sec) override; void printVersionDependencySection(const Elf_Shdr *Sec) override; void printCGProfile() override; + void printCallGraphInfo() override; void printBBAddrMaps(bool PrettyPGOAnalysis) override; void printAddrsig() override; void printNotes() override; @@ -5260,6 +5307,247 @@ template void GNUELFDumper::printCGProfile() { OS << "GNUStyle::printCGProfile not implemented\n"; } +template +static std::optional +getCallGraphSection(const object::ELFObjectFile &ObjF) { + // Get the .callgraph section. + StringRef CallGraphSectionName(".callgraph"); + std::optional CallGraphSection; + for (auto Sec : ObjF.sections()) { + StringRef Name; + if (Expected NameOrErr = Sec.getName()) + Name = *NameOrErr; + else + consumeError(NameOrErr.takeError()); + + if (Name == CallGraphSectionName) + return Sec; + } + return CallGraphSection; +} + +template bool ELFDumper::processCallGraphSection() { + const Elf_Shdr *CGSection = findSectionByName(".callgraph"); + if (!CGSection) { + Error NoSectionErr = createError("No .callgraph section found."); + reportError(std::move(NoSectionErr), "Missing section"); + } + + Expected> SectionBytesOrErr = + Obj.getSectionContents(*CGSection); + if (!SectionBytesOrErr) { + Error SectionReadErr = SectionBytesOrErr.takeError(); + reportError(std::move(SectionReadErr), + "Unable to read the .callgraph section"); + } + + auto PrintMalformedError = [&](Error &E, Twine FuncPC, StringRef Component) { + // auto Msg = llvm::Twine("Malformed callgraph section while reading [") + + // Component + llvm::Twine("] .\n"); + std::string Msg = + (StringRef("While reading call graph info's [") + Component + + StringRef("] for function at [0x") + StringRef(FuncPC.str()) + "]") + .str(); + reportError(std::move(E), StringRef(Msg)); + }; + + DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(), + ObjF.getBytesInAddress()); + + uint64_t NotListedCount = 0; + uint64_t UnknownCount = 0; + + uint64_t Offset = 0; + while (Offset < CGSection->sh_size) { + Error CGSectionErr = Error::success(); + // Format version number. + uint64_t FormatVersionNumber = Data.getU64(&Offset, &CGSectionErr); + + if (CGSectionErr) + reportError(std::move(CGSectionErr), + "While reading call graph info FormatVersionNumber"); + + if (FormatVersionNumber != 0) { + Error FormatErr = createError("Unknown format version value [" + + std::to_string(FormatVersionNumber) + + "] in .callgraph section."); + reportError(std::move(FormatErr), "Unknown value"); + } + + // Read function address. + typename ELFT::uint FuncAddr = + Data.getUnsigned(&Offset, sizeof(FuncAddr), &CGSectionErr); + if (CGSectionErr) + reportError(std::move(CGSectionErr), + "While reading call graph info function entry PC"); + + if (FuncCGInfos.find(FuncAddr) != FuncCGInfos.end()) { + Error DuplicatePcErr = + createError("for function PC: 0x" + Twine::utohexstr(FuncAddr)); + reportError(std::move(DuplicatePcErr), "Duplicate call graph entry"); + } + + using FunctionCallgraphInfo = + ::FunctionCallgraphInfoImpl; + + // Create a new entry for this function. + FunctionCallgraphInfo CGInfo; + CGInfo.FormatVersionNumber = FormatVersionNumber; + + // Read function kind. + uint64_t KindVal = Data.getU64(&Offset, &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), "Kind"); + + if (KindVal > 3) { + Error KindErr = + createError("Unknown value [" + std::to_string(KindVal) + "]."); + PrintMalformedError(KindErr, Twine::utohexstr(FuncAddr), "FunctionKind"); + } + + FunctionKind Kind = static_cast(KindVal); + CGInfo.Kind = Kind; + if (Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) { + // Read type id if this function is an indirect call target. + uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "indirect type id"); + CGInfo.FunctionTypeId = TypeId; + } + if (Kind == FunctionKind::INDIRECT_TARGET_UNKNOWN_TID) + UnknownCount++; + if (Kind == FunctionKind::NOT_LISTED) + NotListedCount++; + + // Read number of indirect call sites for this function. + uint64_t NumIndirectCallsites = Data.getU64(&Offset, &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "number of indirect callsites"); + + for (unsigned long I = 0; I < NumIndirectCallsites; I++) { + uint64_t TypeId = Data.getU64(&Offset, &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "indirect target type id"); + typename ELFT::uint CallSitePc = + Data.getUnsigned(&Offset, sizeof(CallSitePc), &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "indirect callsite PC"); + CGInfo.IndirectCallsites.try_emplace(CallSitePc, TypeId); + } + + // Read number of direct call sites for this function. + uint64_t NumDirectCallees = Data.getU64(&Offset, &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "number of direct callsites"); + // Read direct call sites and populate FuncCGInfos. + for (uint64_t I = 0; I < NumDirectCallees; ++I) { + typename ELFT::uint Callee = + Data.getUnsigned(&Offset, sizeof(Callee), &CGSectionErr); + if (CGSectionErr) + PrintMalformedError(CGSectionErr, Twine::utohexstr(FuncAddr), + "direct callee PC"); + CGInfo.DirectCallees.insert(Callee); + } + FuncCGInfos[FuncAddr] = CGInfo; + } + + if (NotListedCount) + reportUniqueWarning(".callgraph section does not have information for " + + std::to_string(NotListedCount) + " functions."); + if (UnknownCount) + reportUniqueWarning(".callgraph section has unknown type id for " + + std::to_string(UnknownCount) + " indirect targets."); + + // Sort function info by function PC. + llvm::sort(FuncCGInfos, + [](const auto &A, const auto &B) { return A.first < B.first; }); + return true; +} + +static StringRef GetFuntionKindString(FunctionKind Kind) { + switch (Kind) { + case FunctionKind::NOT_INDIRECT_TARGET: + return "NOT_INDIRECT"; + case FunctionKind::INDIRECT_TARGET_UNKNOWN_TID: + return "UNKNOWN_TID"; + case FunctionKind::INDIRECT_TARGET_KNOWN_TID: + return "KNOWN_TID"; + case FunctionKind::NOT_LISTED: + return "NO_INFO"; + } + llvm_unreachable("Unknown FunctionKind."); +} + +template void GNUELFDumper::printCallGraphInfo() { + if (!this->processCallGraphSection()) + return; + if (this->FuncCGInfos.size() == 0) + return; + using FunctionCallgraphInfo = + ::FunctionCallgraphInfoImpl; + + auto GetFunctionName = [&](typename ELFT::uint EntryPc) { + SmallVector FuncSymIndexes = + this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt); + if (FuncSymIndexes.empty()) + return std::string(""); + + SmallVector FuncSymNames; + for (uint32_t Index : FuncSymIndexes) + FuncSymNames.push_back(this->getStaticSymbolName(Index)); + return join(FuncSymNames, ", "); + }; + + OS << "Per-function call graph information:: \n"; + for (const auto &El : this->FuncCGInfos) { + typename ELFT::uint FuncEntryPc = El.first; + FunctionCallgraphInfo CGInfo = El.second; + std::string FuncSymNames = GetFunctionName(FuncEntryPc); + if (!FuncSymNames.empty()) + OS << "\nFunction:: " << FuncSymNames; + OS << "\nFunction PC:: " << format("0x%lx", FuncEntryPc); + OS << "\nFormatVersionNumber:: " << CGInfo.FormatVersionNumber; + OS << "\nFunction Kind:: " << GetFuntionKindString(CGInfo.Kind); + if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) + OS << "\nFunction Type ID:: 0x" << format("%lx", CGInfo.FunctionTypeId); + OS << "\nIndirect callee count:: " << CGInfo.IndirectCallsites.size(); + if (CGInfo.IndirectCallsites.size() > 0) { + OS << "\n{"; + for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) { + OS << "\n"; + OS.PadToColumn(2); + OS << "callsite: 0x" << format("%lx", IndirCallSitePc); + OS << "\n"; + OS.PadToColumn(2); + OS << "calleeTypeId: 0x" << format("%lx", TypeId); + } + OS << "\n}"; + } + OS << "\nDirect callee count:: " << CGInfo.DirectCallees.size(); + if (CGInfo.DirectCallees.size() > 0) { + OS << "\n{"; + for (auto CalleePC : CGInfo.DirectCallees) { + std::string FuncSymNames = GetFunctionName(CalleePC); + if (!FuncSymNames.empty()) { + OS << "\n"; + OS.PadToColumn(2); + OS << "Callee:: " << FuncSymNames; + } + OS << "\n"; + OS.PadToColumn(2); + OS << "CalleePC:: 0x" << format("%lx", CalleePC); + } + OS << "\n}"; + } + OS << "\n"; + } +} + template void GNUELFDumper::printBBAddrMaps(bool /*PrettyPGOAnalysis*/) { OS << "GNUStyle::printBBAddrMaps not implemented\n"; @@ -8091,6 +8379,65 @@ template void LLVMELFDumper::printCGProfile() { } } +template void LLVMELFDumper::printCallGraphInfo() { + if (!this->processCallGraphSection()) + return; + if (this->FuncCGInfos.size() == 0) + return; + using FunctionCallgraphInfo = + ::FunctionCallgraphInfoImpl; + + auto GetFunctionName = [&](typename ELFT::uint EntryPc) { + SmallVector FuncSymIndexes = + this->getSymbolIndexesForFunctionAddress(EntryPc, std::nullopt); + if (FuncSymIndexes.empty()) + return std::string(""); + + SmallVector FuncSymNames; + for (uint32_t Index : FuncSymIndexes) + FuncSymNames.push_back(this->getStaticSymbolName(Index)); + return join(FuncSymNames, ", "); + }; + + ListScope CGI(W, "callgraph_info"); + + for (const auto &El : this->FuncCGInfos) { + DictScope D(W, "Function"); + typename ELFT::uint FuncEntryPc = El.first; + FunctionCallgraphInfo CGInfo = El.second; + std::string FuncSymName = GetFunctionName(FuncEntryPc); + if (!FuncSymName.empty()) + W.printString("Name", FuncSymName); + + W.printHex("Address", FuncEntryPc); + W.printNumber("Version", CGInfo.FormatVersionNumber); + W.printString("KindStr", GetFuntionKindString(CGInfo.Kind)); + W.printNumber("Kind", (uint64_t)CGInfo.Kind); + if (CGInfo.Kind == FunctionKind::INDIRECT_TARGET_KNOWN_TID) + W.printHex("TypeId", CGInfo.FunctionTypeId); + W.printNumber("NumIndirectCallSites", CGInfo.IndirectCallsites.size()); + if (CGInfo.IndirectCallsites.size() > 0) { + ListScope ICSs(W, "IndirectCallsites"); + for (auto &[IndirCallSitePc, TypeId] : CGInfo.IndirectCallsites) { + DictScope ICS(W, "IndirectCallsite"); + W.printHex("Address", IndirCallSitePc); + W.printHex("TypeId", TypeId); + } + } + W.printNumber("NumDirectCallSites", CGInfo.DirectCallees.size()); + if (CGInfo.DirectCallees.size() > 0) { + ListScope DCs(W, "DirectCallees"); + for (auto CalleePC : CGInfo.DirectCallees) { + DictScope DCs(W, "Entry"); + std::string CalleeSymName = GetFunctionName(CalleePC); + if (!CalleeSymName.empty()) + W.printString("Name", CalleeSymName); + W.printHex("Address", CalleePC); + } + } + } +} + template void LLVMELFDumper::printBBAddrMaps(bool PrettyPGOAnalysis) { bool IsRelocatable = this->Obj.getHeader().e_type == ELF::ET_REL; diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h index a654078a770ff..c704a24290b30 100644 --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -129,6 +129,7 @@ class ObjDumper { virtual void printGroupSections() {} virtual void printHashHistograms() {} virtual void printCGProfile() {} + virtual void printCallGraphInfo() {} // If PrettyPGOAnalysis is true, prints BFI as relative frequency and BPI as // percentage. Otherwise raw values are displayed. virtual void printBBAddrMaps(bool PrettyPGOAnalysis) {} diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td index 711522c4acb14..209881d67eb0c 100644 --- a/llvm/tools/llvm-readobj/Opts.td +++ b/llvm/tools/llvm-readobj/Opts.td @@ -20,6 +20,7 @@ def all : FF<"all", "Equivalent to setting: --file-header, --program-headers, -- def arch_specific : FF<"arch-specific", "Display architecture-specific information">; def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">; def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">; +def call_graph_info : FF<"call-graph-info", "Display call graph information">; def cg_profile : FF<"cg-profile", "Display call graph profile section">; def decompress : FF<"decompress", "Dump decompressed section content when used with -x or -p">; defm demangle : BB<"demangle", "Demangle symbol names", "Do not demangle symbol names (default)">; diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp index 2b34761b2cc6c..cb22f95eedbac 100644 --- a/llvm/tools/llvm-readobj/llvm-readobj.cpp +++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -97,6 +97,7 @@ static bool Addrsig; static bool All; static bool ArchSpecificInfo; static bool BBAddrMap; +static bool CallGraphInfo; static bool PrettyPGOAnalysisMap; bool ExpandRelocs; static bool CGProfile; @@ -216,6 +217,7 @@ static void parseOptions(const opt::InputArgList &Args) { opts::All = Args.hasArg(OPT_all); opts::ArchSpecificInfo = Args.hasArg(OPT_arch_specific); opts::BBAddrMap = Args.hasArg(OPT_bb_addr_map); + opts::CallGraphInfo = Args.hasArg(OPT_call_graph_info); opts::PrettyPGOAnalysisMap = Args.hasArg(OPT_pretty_pgo_analysis_map); if (opts::PrettyPGOAnalysisMap && !opts::BBAddrMap) WithColor::warning(errs(), ToolName) @@ -474,6 +476,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer, Dumper->printHashHistograms(); if (opts::CGProfile) Dumper->printCGProfile(); + if (opts::CallGraphInfo) + Dumper->printCallGraphInfo(); if (opts::BBAddrMap) Dumper->printBBAddrMaps(opts::PrettyPGOAnalysisMap); if (opts::Addrsig)