summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s206
-rw-r--r--llvm/test/tools/llvm-cfi-verify/X86/lit.local.cfg2
-rw-r--r--llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s203
-rw-r--r--llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s167
-rw-r--r--llvm/test/tools/llvm-cfi-verify/X86/unprotected-nolineinfo.s92
-rw-r--r--llvm/tools/llvm-cfi-verify/CMakeLists.txt1
-rw-r--r--llvm/tools/llvm-cfi-verify/LLVMBuild.txt2
-rw-r--r--llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt1
-rw-r--r--llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp54
-rw-r--r--llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h19
-rw-r--r--llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp7
-rw-r--r--llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt2
-rw-r--r--llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp55
13 files changed, 797 insertions, 14 deletions
diff --git a/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s b/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s
new file mode 100644
index 00000000000..bf1d87a2eb8
--- /dev/null
+++ b/llvm/test/tools/llvm-cfi-verify/X86/indirect-cf-elimination.s
@@ -0,0 +1,206 @@
+# RUN: llvm-mc %s -filetype obj -triple x86_64-linux-elf -o %t.o
+# RUN: llvm-cfi-verify %t.o 2>&1 | FileCheck %s
+
+# This is the same file as protected-lineinfo.s, however contains a hand-
+# assembled function (fake_function) that has no line table information
+# associated with it. Because there is no LT info, the indirect call made in the
+# function should not be reported at all by llvm-cfi-verify.
+
+# We can test that this indirect call is ignored from the final statistics
+# reporting of the cfi-verify program. It should only find a single indirect CF
+# instruction at `tiny.cc:11` (see protected-lineinfo.s for the source).
+
+# CHECK: Unprotected: 0 (0.00%), Protected: 1 (100.00%)
+
+ .text
+ .file "ld-temp.o"
+ .p2align 4, 0x90
+ .type fake_function,@function
+fake_function:
+ nop
+ nop
+ nop
+ nop
+ callq *%rax
+ nop
+ nop
+ nop
+ nop
+ .type _Z1av.cfi,@function
+_Z1av.cfi:
+.Lfunc_begin0:
+ .file 1 "tiny.cc"
+ .loc 1 3 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 1 3 11 prologue_end
+ popq %rbp
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size _Z1av.cfi, .Lfunc_end0-_Z1av.cfi
+ .cfi_endproc
+
+ .p2align 4, 0x90
+ .type _Z1bv.cfi,@function
+_Z1bv.cfi:
+.Lfunc_begin1:
+ .loc 1 4 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp2:
+ .loc 1 4 11 prologue_end
+ popq %rbp
+ retq
+.Ltmp3:
+.Lfunc_end1:
+ .size _Z1bv.cfi, .Lfunc_end1-_Z1bv.cfi
+ .cfi_endproc
+
+ .hidden main
+ .globl main
+ .p2align 4, 0x90
+ .type main,@function
+main:
+.Lfunc_begin2:
+ .loc 1 6 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movb $32, -1(%rbp)
+ movl $0, -12(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -32(%rbp)
+.Ltmp4:
+ .loc 1 8 12 prologue_end
+ cmpl $1, -8(%rbp)
+ .loc 1 8 7 is_stmt 0
+ jne .LBB2_2
+ .loc 1 0 7
+ leaq _Z1av(%rip), %rax
+ .loc 1 9 9 is_stmt 1
+ movq %rax, -24(%rbp)
+ .loc 1 9 5 is_stmt 0
+ jmp .LBB2_3
+.LBB2_2:
+ .loc 1 0 5
+ leaq _Z1bv(%rip), %rax
+ .loc 1 11 9 is_stmt 1
+ movq %rax, -24(%rbp)
+.LBB2_3:
+ .loc 1 0 9 is_stmt 0
+ leaq .L.cfi.jumptable(%rip), %rcx
+ .loc 1 13 3 is_stmt 1
+ movq -24(%rbp), %rax
+ movq %rax, %rdx
+ subq %rcx, %rdx
+ movq %rdx, %rcx
+ shrq $3, %rcx
+ shlq $61, %rdx
+ orq %rcx, %rdx
+ cmpq $1, %rdx
+ jbe .LBB2_5
+ ud2
+.LBB2_5:
+ callq *%rax
+ .loc 1 14 11
+ movb $-1, -1(%rbp)
+ .loc 1 15 1
+ movl -12(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ retq
+.Ltmp5:
+.Lfunc_end2:
+ .size main, .Lfunc_end2-main
+ .cfi_endproc
+
+ .p2align 3, 0x90
+ .type .L.cfi.jumptable,@function
+.L.cfi.jumptable:
+.Lfunc_begin3:
+ .cfi_startproc
+ #APP
+ jmp _Z1av.cfi@PLT
+ int3
+ int3
+ int3
+ jmp _Z1bv.cfi@PLT
+ int3
+ int3
+ int3
+
+ #NO_APP
+.Lfunc_end3:
+ .size .L.cfi.jumptable, .Lfunc_end3-.L.cfi.jumptable
+ .cfi_endproc
+
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 6.0.0 (trunk 316774)"
+.Linfo_string1:
+ .asciz "tiny.cc"
+.Linfo_string2:
+ .asciz "/tmp/a/b"
+ .section .debug_abbrev,"",@progbits
+ .byte 1
+ .byte 17
+ .byte 0
+ .byte 37
+ .byte 14
+ .byte 19
+ .byte 5
+ .byte 3
+ .byte 14
+ .byte 16
+ .byte 23
+ .byte 27
+ .byte 14
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long 38
+ .short 4
+ .long .debug_abbrev
+ .byte 8
+ .byte 1
+ .long .Linfo_string0
+ .short 4
+ .long .Linfo_string1
+ .long .Lline_table_start0
+ .long .Linfo_string2
+ .quad .Lfunc_begin0
+ .long .Lfunc_end2-.Lfunc_begin0
+ .section .debug_ranges,"",@progbits
+ .section .debug_macinfo,"",@progbits
+.Lcu_macro_begin0:
+ .byte 0
+
+ .type _Z1av,@function
+_Z1av = .L.cfi.jumptable
+ .type _Z1bv,@function
+_Z1bv = .L.cfi.jumptable+8
+ .ident "clang version 6.0.0 (trunk 316774)"
+ .section ".note.GNU-stack","",@progbits
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
+
diff --git a/llvm/test/tools/llvm-cfi-verify/X86/lit.local.cfg b/llvm/test/tools/llvm-cfi-verify/X86/lit.local.cfg
new file mode 100644
index 00000000000..c8625f4d9d2
--- /dev/null
+++ b/llvm/test/tools/llvm-cfi-verify/X86/lit.local.cfg
@@ -0,0 +1,2 @@
+if not 'X86' in config.root.targets:
+ config.unsupported = True
diff --git a/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s b/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s
new file mode 100644
index 00000000000..e3bb0f7af46
--- /dev/null
+++ b/llvm/test/tools/llvm-cfi-verify/X86/protected-lineinfo.s
@@ -0,0 +1,203 @@
+# RUN: llvm-mc %s -filetype obj -triple x86_64-linux-elf -o %t.o
+# RUN: llvm-cfi-verify %t.o | FileCheck %s
+
+# CHECK-LABEL: P
+# CHECK-NEXT: tiny.cc:11
+
+# CHECK: Unprotected: 0 (0.00%), Protected: 1 (100.00%)
+
+# Source (tiny.cc):
+# void a() {}
+# void b() {}
+# int main(int argc, char** argv) {
+# void(*ptr)();
+# if (argc == 1)
+# ptr = &a;
+# else
+# ptr = &b;
+# ptr();
+# }
+# Compile with (output is in tiny.s.0):
+# clang++ -flto -fsanitize=cfi -fvisibility=hidden -c tiny.cc -o tiny.o -gmlt
+# clang++ tiny.o -o tiny -flto -fuse-ld=gold -Wl,-plugin-opt,save-temps
+# clang++ -fsanitize=cfi -flto -fvisibility=hidden -c tiny.cc -o tiny.o -gmlt
+# llvm-lto2 run @tiny.resolution.txt -o tiny.s -filetype=asm
+
+ .text
+ .file "ld-temp.o"
+ .p2align 4, 0x90
+ .type _Z1av.cfi,@function
+_Z1av.cfi:
+.Lfunc_begin0:
+ .file 1 "tiny.cc"
+ .loc 1 1 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 1 1 11 prologue_end
+ popq %rbp
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size _Z1av.cfi, .Lfunc_end0-_Z1av.cfi
+ .cfi_endproc
+
+ .p2align 4, 0x90
+ .type _Z1bv.cfi,@function
+_Z1bv.cfi:
+.Lfunc_begin1:
+ .loc 1 2 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp2:
+ .loc 1 2 11 prologue_end
+ popq %rbp
+ retq
+.Ltmp3:
+.Lfunc_end1:
+ .size _Z1bv.cfi, .Lfunc_end1-_Z1bv.cfi
+ .cfi_endproc
+
+ .hidden main
+ .globl main
+ .p2align 4, 0x90
+ .type main,@function
+main:
+.Lfunc_begin2:
+ .loc 1 4 0
+ .cfi_startproc
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movl $0, -8(%rbp)
+ movl %edi, -4(%rbp)
+ movq %rsi, -24(%rbp)
+.Ltmp4:
+ .loc 1 6 12 prologue_end
+ cmpl $1, -4(%rbp)
+ .loc 1 6 7 is_stmt 0
+ jne .LBB2_2
+ .loc 1 0 7
+ leaq _Z1av(%rip), %rax
+ .loc 1 7 9 is_stmt 1
+ movq %rax, -16(%rbp)
+ .loc 1 7 5 is_stmt 0
+ jmp .LBB2_3
+.LBB2_2:
+ .loc 1 0 5
+ leaq _Z1bv(%rip), %rax
+ .loc 1 9 9 is_stmt 1
+ movq %rax, -16(%rbp)
+.LBB2_3:
+ .loc 1 0 9 is_stmt 0
+ leaq .L.cfi.jumptable(%rip), %rcx
+ .loc 1 11 3 is_stmt 1
+ movq -16(%rbp), %rax
+ movq %rax, %rdx
+ subq %rcx, %rdx
+ movq %rdx, %rcx
+ shrq $3, %rcx
+ shlq $61, %rdx
+ orq %rcx, %rdx
+ cmpq $1, %rdx
+ jbe .LBB2_5
+ ud2
+.LBB2_5:
+ callq *%rax
+ .loc 1 12 1
+ movl -8(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ retq
+.Ltmp5:
+.Lfunc_end2:
+ .size main, .Lfunc_end2-main
+ .cfi_endproc
+
+ .p2align 3, 0x90
+ .type .L.cfi.jumptable,@function
+.L.cfi.jumptable:
+.Lfunc_begin3:
+ .cfi_startproc
+ #APP
+ jmp _Z1av.cfi@PLT
+ int3
+ int3
+ int3
+ jmp _Z1bv.cfi@PLT
+ int3
+ int3
+ int3
+
+ #NO_APP
+.Lfunc_end3:
+ .size .L.cfi.jumptable, .Lfunc_end3-.L.cfi.jumptable
+ .cfi_endproc
+
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 6.0.0 (trunk 316774)"
+.Linfo_string1:
+ .asciz "tiny.cc"
+.Linfo_string2:
+ .asciz ""
+ .section .debug_abbrev,"",@progbits
+ .byte 1
+ .byte 17
+ .byte 0
+ .byte 37
+ .byte 14
+ .byte 19
+ .byte 5
+ .byte 3
+ .byte 14
+ .byte 16
+ .byte 23
+ .byte 27
+ .byte 14
+ .byte 17
+ .byte 1
+ .byte 18
+ .byte 6
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long 38
+ .short 4
+ .long .debug_abbrev
+ .byte 8
+ .byte 1
+ .long .Linfo_string0
+ .short 4
+ .long .Linfo_string1
+ .long .Lline_table_start0
+ .long .Linfo_string2
+ .quad .Lfunc_begin0
+ .long .Lfunc_end2-.Lfunc_begin0
+ .section .debug_ranges,"",@progbits
+ .section .debug_macinfo,"",@progbits
+.Lcu_macro_begin0:
+ .byte 0
+
+ .type _Z1av,@function
+_Z1av = .L.cfi.jumptable
+ .type _Z1bv,@function
+_Z1bv = .L.cfi.jumptable+8
+ .ident "clang version 6.0.0 (trunk 316774)"
+ .section ".note.GNU-stack","",@progbits
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
+
diff --git a/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s
new file mode 100644
index 00000000000..d8819e16e37
--- /dev/null
+++ b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-lineinfo.s
@@ -0,0 +1,167 @@
+# RUN: llvm-mc %s -filetype obj -triple x86_64-linux-elf -o %t.o
+# RUN: llvm-cfi-verify %t.o | FileCheck %s
+
+# CHECK-LABEL: U
+# CHECK-NEXT: tiny.cc:11
+
+# CHECK: Unprotected: 1 (100.00%), Protected: 0 (0.00%)
+
+# Source (tiny.cc):
+# void a() {}
+# void b() {}
+# int main(int argc, char** argv) {
+# void(*ptr)();
+# if (argc == 1)
+# ptr = &a;
+# else
+# ptr = &b;
+# ptr();
+# }
+# Compile with:
+# clang++ -gmlt tiny.cc -S -o tiny.s
+
+ .text
+ .file "tiny.cc"
+ .globl _Z1av # -- Begin function _Z1av
+ .p2align 4, 0x90
+ .type _Z1av,@function
+_Z1av: # @_Z1av
+.Lfunc_begin0:
+ .file 1 "tiny.cc"
+ .loc 1 1 0 # tiny.cc:1:0
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp0:
+ .loc 1 1 11 prologue_end # tiny.cc:1:11
+ popq %rbp
+ retq
+.Ltmp1:
+.Lfunc_end0:
+ .size _Z1av, .Lfunc_end0-_Z1av
+ .cfi_endproc
+ # -- End function
+ .globl _Z1bv # -- Begin function _Z1bv
+ .p2align 4, 0x90
+ .type _Z1bv,@function
+_Z1bv: # @_Z1bv
+.Lfunc_begin1:
+ .loc 1 2 0 # tiny.cc:2:0
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+.Ltmp2:
+ .loc 1 2 11 prologue_end # tiny.cc:2:11
+ popq %rbp
+ retq
+.Ltmp3:
+.Lfunc_end1:
+ .size _Z1bv, .Lfunc_end1-_Z1bv
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main,@function
+main: # @main
+.Lfunc_begin2:
+ .loc 1 4 0 # tiny.cc:4:0
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+.Ltmp4:
+ .loc 1 6 12 prologue_end # tiny.cc:6:12
+ cmpl $1, -8(%rbp)
+ .loc 1 6 7 is_stmt 0 # tiny.cc:6:7
+ jne .LBB2_2
+# BB#1:
+ .loc 1 0 7 # tiny.cc:0:7
+ movabsq $_Z1av, %rax
+ .loc 1 7 9 is_stmt 1 # tiny.cc:7:9
+ movq %rax, -24(%rbp)
+ .loc 1 7 5 is_stmt 0 # tiny.cc:7:5
+ jmp .LBB2_3
+.LBB2_2:
+ .loc 1 0 5 # tiny.cc:0:5
+ movabsq $_Z1bv, %rax
+ .loc 1 9 9 is_stmt 1 # tiny.cc:9:9
+ movq %rax, -24(%rbp)
+.LBB2_3:
+ .loc 1 11 3 # tiny.cc:11:3
+ callq *-24(%rbp)
+ .loc 1 12 1 # tiny.cc:12:1
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ retq
+.Ltmp5:
+.Lfunc_end2:
+ .size main, .Lfunc_end2-main
+ .cfi_endproc
+ # -- End function
+ .section .debug_str,"MS",@progbits,1
+.Linfo_string0:
+ .asciz "clang version 6.0.0 (trunk 316774)" # string offset=0
+.Linfo_string1:
+ .asciz "tiny.cc" # string offset=35
+.Linfo_string2:
+ .asciz "/tmp/a/b" # string offset=43
+ .section .debug_abbrev,"",@progbits
+ .byte 1 # Abbreviation Code
+ .byte 17 # DW_TAG_compile_unit
+ .byte 0 # DW_CHILDREN_no
+ .byte 37 # DW_AT_producer
+ .byte 14 # DW_FORM_strp
+ .byte 19 # DW_AT_language
+ .byte 5 # DW_FORM_data2
+ .byte 3 # DW_AT_name
+ .byte 14 # DW_FORM_strp
+ .byte 16 # DW_AT_stmt_list
+ .byte 23 # DW_FORM_sec_offset
+ .byte 27 # DW_AT_comp_dir
+ .byte 14 # DW_FORM_strp
+ .byte 17 # DW_AT_low_pc
+ .byte 1 # DW_FORM_addr
+ .byte 18 # DW_AT_high_pc
+ .byte 6 # DW_FORM_data4
+ .byte 0 # EOM(1)
+ .byte 0 # EOM(2)
+ .byte 0 # EOM(3)
+ .section .debug_info,"",@progbits
+.Lcu_begin0:
+ .long 38 # Length of Unit
+ .short 4 # DWARF version number
+ .long .debug_abbrev # Offset Into Abbrev. Section
+ .byte 8 # Address Size (in bytes)
+ .byte 1 # Abbrev [1] 0xb:0x1f DW_TAG_compile_unit
+ .long .Linfo_string0 # DW_AT_producer
+ .short 4 # DW_AT_language
+ .long .Linfo_string1 # DW_AT_name
+ .long .Lline_table_start0 # DW_AT_stmt_list
+ .long .Linfo_string2 # DW_AT_comp_dir
+ .quad .Lfunc_begin0 # DW_AT_low_pc
+ .long .Lfunc_end2-.Lfunc_begin0 # DW_AT_high_pc
+ .section .debug_ranges,"",@progbits
+ .section .debug_macinfo,"",@progbits
+.Lcu_macro_begin0:
+ .byte 0 # End Of Macro List Mark
+
+ .ident "clang version 6.0.0 (trunk 316774)"
+ .section ".note.GNU-stack","",@progbits
+ .section .debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-cfi-verify/X86/unprotected-nolineinfo.s b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-nolineinfo.s
new file mode 100644
index 00000000000..c023a4a84ab
--- /dev/null
+++ b/llvm/test/tools/llvm-cfi-verify/X86/unprotected-nolineinfo.s
@@ -0,0 +1,92 @@
+# RUN: llvm-mc %s -filetype obj -triple x86_64-linux-elf -o %t.o
+# RUN: not llvm-cfi-verify %t.o 2>&1 | FileCheck %s
+
+# CHECK: DWARF line information missing. Did you compile with '-g'?
+
+# Source (tiny.cc):
+# void a() {}
+# void b() {}
+# int main(int argc, char** argv) {
+# void(*ptr)();
+# if (argc == 1)
+# ptr = &a;
+# else
+# ptr = &b;
+# ptr();
+# }
+# Compile with:
+# clang++ tiny.cc -S -o tiny.s
+
+ .text
+ .file "tiny.cc"
+ .globl _Z1av # -- Begin function _Z1av
+ .p2align 4, 0x90
+ .type _Z1av,@function
+_Z1av: # @_Z1av
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ retq
+.Lfunc_end0:
+ .size _Z1av, .Lfunc_end0-_Z1av
+ .cfi_endproc
+ # -- End function
+ .globl _Z1bv # -- Begin function _Z1bv
+ .p2align 4, 0x90
+ .type _Z1bv,@function
+_Z1bv: # @_Z1bv
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ popq %rbp
+ retq
+.Lfunc_end1:
+ .size _Z1bv, .Lfunc_end1-_Z1bv
+ .cfi_endproc
+ # -- End function
+ .globl main # -- Begin function main
+ .p2align 4, 0x90
+ .type main,@function
+main: # @main
+ .cfi_startproc
+# BB#0:
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset %rbp, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register %rbp
+ subq $32, %rsp
+ movl $0, -4(%rbp)
+ movl %edi, -8(%rbp)
+ movq %rsi, -16(%rbp)
+ cmpl $1, -8(%rbp)
+ jne .LBB2_2
+# BB#1:
+ movabsq $_Z1av, %rax
+ movq %rax, -24(%rbp)
+ jmp .LBB2_3
+.LBB2_2:
+ movabsq $_Z1bv, %rax
+ movq %rax, -24(%rbp)
+.LBB2_3:
+ callq *-24(%rbp)
+ movl -4(%rbp), %eax
+ addq $32, %rsp
+ popq %rbp
+ retq
+.Lfunc_end2:
+ .size main, .Lfunc_end2-main
+ .cfi_endproc
+ # -- End function
+
+ .ident "clang version 6.0.0 (trunk 316774)"
+ .section ".note.GNU-stack","",@progbits
diff --git a/llvm/tools/llvm-cfi-verify/CMakeLists.txt b/llvm/tools/llvm-cfi-verify/CMakeLists.txt
index 8dc8cb96b6f..07c6504bf48 100644
--- a/llvm/tools/llvm-cfi-verify/CMakeLists.txt
+++ b/llvm/tools/llvm-cfi-verify/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
+ DebugInfoDWARF
MC
MCParser
Object
diff --git a/llvm/tools/llvm-cfi-verify/LLVMBuild.txt b/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
index 9bff3a876cf..5c4ce263090 100644
--- a/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
+++ b/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-cfi-verify
parent = Tools
-required_libraries = all-targets MC MCDisassembler MCParser Support
+required_libraries = all-targets DebugInfoDWARF MC MCDisassembler MCParser Support
diff --git a/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt b/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
index 8cbbc79ceca..c90e4ed485e 100644
--- a/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
+++ b/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
@@ -7,6 +7,7 @@ add_library(LLVMCFIVerify
llvm_update_compile_flags(LLVMCFIVerify)
llvm_map_components_to_libnames(libs
+ DebugInfoDWARF
MC
MCParser
Object
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 928571bfd0a..278e861dfd3 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -11,6 +11,7 @@
#include "GraphBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
@@ -42,6 +43,19 @@ using Instr = llvm::cfi_verify::FileAnalysis::Instr;
namespace llvm {
namespace cfi_verify {
+static cl::opt<bool> IgnoreDWARF(
+ "ignore-dwarf",
+ cl::desc(
+ "Ignore all DWARF data. This relaxes the requirements for all "
+ "statically linked libraries to have been compiled with '-g', but "
+ "will result in false positives for 'CFI unprotected' instructions."),
+ cl::init(false));
+
+cl::opt<unsigned long long> DWARFSearchRange(
+ "dwarf-search-range",
+ cl::desc("Address search range used to determine if instruction is valid."),
+ cl::init(0x10));
+
Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
// Open the filename provided.
Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
@@ -294,6 +308,28 @@ Error FileAnalysis::initialiseDisassemblyMembers() {
}
Error FileAnalysis::parseCodeSections() {
+ if (!IgnoreDWARF) {
+ DWARF.reset(DWARFContext::create(*Object).release());
+ if (!DWARF)
+ return make_error<StringError>("Could not create DWARF information.",
+ inconvertibleErrorCode());
+
+ bool LineInfoValid = false;
+
+ for (auto &Unit : DWARF->compile_units()) {
+ const auto &LineTable = DWARF->getLineTableForUnit(Unit.get());
+ if (LineTable && !LineTable->Rows.empty()) {
+ LineInfoValid = true;
+ break;
+ }
+ }
+
+ if (!LineInfoValid)
+ return make_error<StringError>(
+ "DWARF line information missing. Did you compile with '-g'?",
+ inconvertibleErrorCode());
+ }
+
for (const object::SectionRef &Section : Object->sections()) {
// Ensure only executable sections get analysed.
if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
@@ -311,6 +347,19 @@ Error FileAnalysis::parseCodeSections() {
return Error::success();
}
+DILineInfoTable FileAnalysis::getLineInfoForAddressRange(uint64_t Address) {
+ if (!hasLineTableInfo())
+ return DILineInfoTable();
+
+ return DWARF->getLineInfoForAddressRange(Address, DWARFSearchRange);
+}
+
+bool FileAnalysis::hasValidLineInfoForAddressRange(uint64_t Address) {
+ return !getLineInfoForAddressRange(Address).empty();
+}
+
+bool FileAnalysis::hasLineTableInfo() const { return DWARF != nullptr; }
+
void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
uint64_t SectionAddress) {
MCInst Instruction;
@@ -330,6 +379,11 @@ void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
InstrMeta.VMAddress = VMAddress;
InstrMeta.InstructionSize = InstructionSize;
InstrMeta.Valid = ValidInstruction;
+
+ // Check if this instruction exists in the range of the DWARF metadata.
+ if (hasLineTableInfo() && !hasValidLineInfoForAddressRange(VMAddress))
+ continue;
+
addInstruction(InstrMeta);
if (!ValidInstruction)
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
index 1ed575bb9e4..9945a2110a2 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
@@ -120,6 +121,18 @@ public:
const MCInstrInfo *getMCInstrInfo() const;
const MCInstrAnalysis *getMCInstrAnalysis() const;
+ // Returns true if this class is using DWARF line tables for elimination.
+ bool hasLineTableInfo() const;
+
+ // Returns the line table information for the range {Address +-
+ // DWARFSearchRange}. Returns an empty table if the address has no valid line
+ // table information, or this analysis object has DWARF handling disabled.
+ DILineInfoTable getLineInfoForAddressRange(uint64_t Address);
+
+ // Returns whether the provided address has valid line information for
+ // instructions in the range of Address +- DWARFSearchRange.
+ bool hasValidLineInfoForAddressRange(uint64_t Address);
+
protected:
// Construct a blank object with the provided triple and features. Used in
// testing, where a sub class will dependency inject protected methods to
@@ -162,8 +175,12 @@ private:
std::unique_ptr<const MCInstrAnalysis> MIA;
std::unique_ptr<MCInstPrinter> Printer;
+ // DWARF debug information.
+ std::unique_ptr<DWARFContext> DWARF;
+
// A mapping between the virtual memory address to the instruction metadata
- // struct.
+ // struct. TODO(hctim): Reimplement this as a sorted vector to avoid per-
+ // insertion allocation.
std::map<uint64_t, Instr> Instructions;
// Contains a mapping between a specific address, and a list of instructions
diff --git a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
index 1121045780f..808b9927fcc 100644
--- a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -221,6 +221,13 @@ void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis,
continue;
}
+ // Call instructions are not valid in the upwards traversal.
+ if (ParentDesc.isCall()) {
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
// Evaluate the branch target to ascertain whether this XRef is the result
// of a fallthrough or the target of a branch.
uint64_t BranchTarget;
diff --git a/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt b/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
index 39537f561f6..99b678fc88a 100644
--- a/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
+++ b/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Library
name = CFIVerify
parent = Libraries
-required_libraries = MC MCDisassembler MCParser Support
+required_libraries = DebugInfoDWARF MC MCDisassembler MCParser Support
diff --git a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
index 00324ed0eb4..d4a46fcc226 100644
--- a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
+++ b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
@@ -22,6 +22,7 @@
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
#include <cstdlib>
@@ -34,30 +35,62 @@ cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
ExitOnError ExitOnErr;
-void printIndirectCFInstructions(const FileAnalysis &Verifier) {
- for (uint64_t Address : Verifier.getIndirectInstructions()) {
- const auto &InstrMeta = Verifier.getInstructionOrDie(Address);
- outs() << format_hex(Address, 2) << " |"
- << Verifier.getMCInstrInfo()->getName(
+void printIndirectCFInstructions(FileAnalysis &Analysis) {
+ uint64_t ProtectedCount = 0;
+ uint64_t UnprotectedCount = 0;
+
+ for (uint64_t Address : Analysis.getIndirectInstructions()) {
+ const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
+
+ if (Analysis.isIndirectInstructionCFIProtected(Address)) {
+ outs() << "P ";
+ ProtectedCount++;
+ } else {
+ outs() << "U ";
+ UnprotectedCount++;
+ }
+
+ outs() << format_hex(Address, 2) << " | "
+ << Analysis.getMCInstrInfo()->getName(
InstrMeta.Instruction.getOpcode())
<< " ";
- InstrMeta.Instruction.print(outs());
outs() << "\n";
- outs() << " Protected? "
- << Verifier.isIndirectInstructionCFIProtected(Address) << "\n";
+
+ if (Analysis.hasLineTableInfo()) {
+ for (const auto &LineKV : Analysis.getLineInfoForAddressRange(Address)) {
+ outs() << " " << format_hex(LineKV.first, 2) << " = "
+ << LineKV.second.FileName << ":" << LineKV.second.Line << ":"
+ << LineKV.second.Column << " (" << LineKV.second.FunctionName
+ << ")\n";
+ }
+ }
}
+
+ if (ProtectedCount || UnprotectedCount)
+ outs() << formatv(
+ "Unprotected: {0} ({1:P}), Protected: {2} ({3:P})\n", UnprotectedCount,
+ (((double)UnprotectedCount) / (UnprotectedCount + ProtectedCount)),
+ ProtectedCount,
+ (((double)ProtectedCount) / (UnprotectedCount + ProtectedCount)));
+ else
+ outs() << "No indirect CF instructions found.\n";
}
int main(int argc, char **argv) {
- cl::ParseCommandLineOptions(argc, argv);
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "Identifies whether Control Flow Integrity protects all indirect control "
+ "flow instructions in the provided object file, DSO or binary.\nNote: "
+ "Anything statically linked into the provided file *must* be compiled "
+ "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllAsmParsers();
InitializeAllDisassemblers();
- FileAnalysis Verifier = ExitOnErr(FileAnalysis::Create(InputFilename));
- printIndirectCFInstructions(Verifier);
+ FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
+ printIndirectCFInstructions(Analysis);
return EXIT_SUCCESS;
}
OpenPOWER on IntegriCloud