Skip to content

[llvm-debuginfo-analyzer] Fix crash with thread local storage. #113904

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

CarlosAlbertoEnciso
Copy link
Member

The DW_OP_GNU_push_tls_address, DW_OP_form_tls_address DWARF
location forms generated for thread local storage variables, caused a
crash in the DWARFReader, due to incorrect number of operands.

The DW_OP_GNU_push_tls_address, DW_OP_form_tls_address DWARF
location forms generated for thread local storage variables,
caused a crash in the DWARFReader, due to incorrect number of
operands.
@llvmbot
Copy link
Member

llvmbot commented Oct 28, 2024

@llvm/pr-subscribers-debuginfo

Author: Carlos Alberto Enciso (CarlosAlbertoEnciso)

Changes

The DW_OP_GNU_push_tls_address, DW_OP_form_tls_address DWARF
location forms generated for thread local storage variables, caused a
crash in the DWARFReader, due to incorrect number of operands.


Full diff: https://github.com/llvm/llvm-project/pull/113904.diff

3 Files Affected:

  • (modified) llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp (+2-2)
  • (added) llvm/test/tools/llvm-debuginfo-analyzer/DWARF/Inputs/ThreadLocalStorage.s (+309)
  • (added) llvm/test/tools/llvm-debuginfo-analyzer/DWARF/crash-thread-local-storage.test (+47)
diff --git a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
index 17b32a5f67b49b..3c078d8ee74b80 100644
--- a/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
+++ b/llvm/lib/DebugInfo/LogicalView/Core/LVLocation.cpp
@@ -156,7 +156,7 @@ std::string LVOperation::getOperandsDWARFInfo() {
     Stream << "push_object_address";
     break;
   case dwarf::DW_OP_form_tls_address:
-    Stream << "form_tls_address " << hexString(Operands[0]);
+    Stream << "form_tls_address";
     break;
   case dwarf::DW_OP_call_frame_cfa:
     Stream << "call_frame_cfa";
@@ -308,7 +308,7 @@ std::string LVOperation::getOperandsDWARFInfo() {
     PrintRegisterInfo(dwarf::DW_OP_reg0);
     break;
   case dwarf::DW_OP_GNU_push_tls_address:
-    Stream << "gnu_push_tls_address " << hexString(Operands[0]);
+    Stream << "gnu_push_tls_address";
     break;
   case dwarf::DW_OP_GNU_addr_index:
     Stream << "gnu_addr_index " << unsigned(Operands[0]);
diff --git a/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/Inputs/ThreadLocalStorage.s b/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/Inputs/ThreadLocalStorage.s
new file mode 100644
index 00000000000000..703c6c826967f4
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/Inputs/ThreadLocalStorage.s
@@ -0,0 +1,309 @@
+	.text
+	.file	"ThreadLocalStorage.cpp"
+	.file	0 "/data/projects/sandbox/debuginfo-analyzer" "ThreadLocalStorage.cpp" md5 0x8fc7af759be43d1ae3bc8c835ed18e5b
+	.globl	_Z4testv                        # -- Begin function _Z4testv
+	.p2align	4
+	.type	_Z4testv,@function
+_Z4testv:                               # @_Z4testv
+.Lfunc_begin0:
+	.loc	0 3 0                           # ThreadLocalStorage.cpp:3:0
+	.cfi_startproc
+# %bb.0:                                # %entry
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+.Ltmp0:
+	.loc	0 5 3 prologue_end              # ThreadLocalStorage.cpp:5:3
+	movq	%fs:0, %rax
+	leaq	TGlobal@TPOFF(%rax), %rax
+	.loc	0 5 11 is_stmt 0                # ThreadLocalStorage.cpp:5:11
+	movl	$1, (%rax)
+	.loc	0 7 7 is_stmt 1                 # ThreadLocalStorage.cpp:7:7
+	movl	$0, -4(%rbp)
+	.loc	0 8 11                          # ThreadLocalStorage.cpp:8:11
+	movl	$2, NGlobal(%rip)
+	.loc	0 9 1 epilogue_begin            # ThreadLocalStorage.cpp:9:1
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Ltmp1:
+.Lfunc_end0:
+	.size	_Z4testv, .Lfunc_end0-_Z4testv
+	.cfi_endproc
+                                        # -- End function
+	.section	.text._ZTW7TGlobal,"axG",@progbits,_ZTW7TGlobal,comdat
+	.hidden	_ZTW7TGlobal                    # -- Begin function _ZTW7TGlobal
+	.weak	_ZTW7TGlobal
+	.p2align	4
+	.type	_ZTW7TGlobal,@function
+_ZTW7TGlobal:                           # @_ZTW7TGlobal
+.Lfunc_begin1:
+	.cfi_startproc
+# %bb.0:
+	pushq	%rbp
+	.cfi_def_cfa_offset 16
+	.cfi_offset %rbp, -16
+	movq	%rsp, %rbp
+	.cfi_def_cfa_register %rbp
+	movq	%fs:0, %rax
+	leaq	TGlobal@TPOFF(%rax), %rax
+	popq	%rbp
+	.cfi_def_cfa %rsp, 8
+	retq
+.Lfunc_end1:
+	.size	_ZTW7TGlobal, .Lfunc_end1-_ZTW7TGlobal
+	.cfi_endproc
+                                        # -- End function
+	.type	TGlobal,@object                 # @TGlobal
+	.section	.tbss,"awT",@nobits
+	.globl	TGlobal
+	.p2align	2, 0x0
+TGlobal:
+	.long	0                               # 0x0
+	.size	TGlobal, 4
+
+	.type	NGlobal,@object                 # @NGlobal
+	.data
+	.globl	NGlobal
+	.p2align	2, 0x0
+NGlobal:
+	.long	1                               # 0x1
+	.size	NGlobal, 4
+
+	.type	_ZZ4testvE6TLocal,@object       # @_ZZ4testvE6TLocal
+	.section	.tbss,"awT",@nobits
+	.p2align	2, 0x0
+_ZZ4testvE6TLocal:
+	.long	0                               # 0x0
+	.size	_ZZ4testvE6TLocal, 4
+
+	.section	.debug_abbrev,"",@progbits
+	.byte	1                               # Abbreviation Code
+	.byte	17                              # DW_TAG_compile_unit
+	.byte	1                               # DW_CHILDREN_yes
+	.byte	37                              # DW_AT_producer
+	.byte	37                              # DW_FORM_strx1
+	.byte	19                              # DW_AT_language
+	.byte	5                               # DW_FORM_data2
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	114                             # DW_AT_str_offsets_base
+	.byte	23                              # DW_FORM_sec_offset
+	.byte	16                              # DW_AT_stmt_list
+	.byte	23                              # DW_FORM_sec_offset
+	.byte	27                              # DW_AT_comp_dir
+	.byte	37                              # DW_FORM_strx1
+	.byte	17                              # DW_AT_low_pc
+	.byte	27                              # DW_FORM_addrx
+	.byte	18                              # DW_AT_high_pc
+	.byte	6                               # DW_FORM_data4
+	.byte	115                             # DW_AT_addr_base
+	.byte	23                              # DW_FORM_sec_offset
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	2                               # Abbreviation Code
+	.byte	52                              # DW_TAG_variable
+	.byte	0                               # DW_CHILDREN_no
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	73                              # DW_AT_type
+	.byte	19                              # DW_FORM_ref4
+	.byte	63                              # DW_AT_external
+	.byte	25                              # DW_FORM_flag_present
+	.byte	58                              # DW_AT_decl_file
+	.byte	11                              # DW_FORM_data1
+	.byte	59                              # DW_AT_decl_line
+	.byte	11                              # DW_FORM_data1
+	.byte	2                               # DW_AT_location
+	.byte	24                              # DW_FORM_exprloc
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	3                               # Abbreviation Code
+	.byte	36                              # DW_TAG_base_type
+	.byte	0                               # DW_CHILDREN_no
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	62                              # DW_AT_encoding
+	.byte	11                              # DW_FORM_data1
+	.byte	11                              # DW_AT_byte_size
+	.byte	11                              # DW_FORM_data1
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	4                               # Abbreviation Code
+	.byte	46                              # DW_TAG_subprogram
+	.byte	1                               # DW_CHILDREN_yes
+	.byte	17                              # DW_AT_low_pc
+	.byte	27                              # DW_FORM_addrx
+	.byte	18                              # DW_AT_high_pc
+	.byte	6                               # DW_FORM_data4
+	.byte	64                              # DW_AT_frame_base
+	.byte	24                              # DW_FORM_exprloc
+	.byte	110                             # DW_AT_linkage_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	58                              # DW_AT_decl_file
+	.byte	11                              # DW_FORM_data1
+	.byte	59                              # DW_AT_decl_line
+	.byte	11                              # DW_FORM_data1
+	.byte	63                              # DW_AT_external
+	.byte	25                              # DW_FORM_flag_present
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	5                               # Abbreviation Code
+	.byte	52                              # DW_TAG_variable
+	.byte	0                               # DW_CHILDREN_no
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	73                              # DW_AT_type
+	.byte	19                              # DW_FORM_ref4
+	.byte	58                              # DW_AT_decl_file
+	.byte	11                              # DW_FORM_data1
+	.byte	59                              # DW_AT_decl_line
+	.byte	11                              # DW_FORM_data1
+	.byte	2                               # DW_AT_location
+	.byte	24                              # DW_FORM_exprloc
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	6                               # Abbreviation Code
+	.byte	52                              # DW_TAG_variable
+	.byte	0                               # DW_CHILDREN_no
+	.byte	2                               # DW_AT_location
+	.byte	24                              # DW_FORM_exprloc
+	.byte	3                               # DW_AT_name
+	.byte	37                              # DW_FORM_strx1
+	.byte	58                              # DW_AT_decl_file
+	.byte	11                              # DW_FORM_data1
+	.byte	59                              # DW_AT_decl_line
+	.byte	11                              # DW_FORM_data1
+	.byte	73                              # DW_AT_type
+	.byte	19                              # DW_FORM_ref4
+	.byte	0                               # EOM(1)
+	.byte	0                               # EOM(2)
+	.byte	0                               # EOM(3)
+	.section	.debug_info,"",@progbits
+.Lcu_begin0:
+	.long	.Ldebug_info_end0-.Ldebug_info_start0 # Length of Unit
+.Ldebug_info_start0:
+	.short	5                               # DWARF version number
+	.byte	1                               # DWARF Unit Type
+	.byte	8                               # Address Size (in bytes)
+	.long	.debug_abbrev                   # Offset Into Abbrev. Section
+	.byte	1                               # Abbrev [1] 0xc:0x65 DW_TAG_compile_unit
+	.byte	0                               # DW_AT_producer
+	.short	33                              # DW_AT_language
+	.byte	1                               # DW_AT_name
+	.long	.Lstr_offsets_base0             # DW_AT_str_offsets_base
+	.long	.Lline_table_start0             # DW_AT_stmt_list
+	.byte	2                               # DW_AT_comp_dir
+	.byte	1                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.long	.Laddr_table_base0              # DW_AT_addr_base
+	.byte	2                               # Abbrev [2] 0x23:0x13 DW_TAG_variable
+	.byte	3                               # DW_AT_name
+	.long	54                              # DW_AT_type
+                                        # DW_AT_external
+	.byte	0                               # DW_AT_decl_file
+	.byte	1                               # DW_AT_decl_line
+	.byte	10                              # DW_AT_location
+	.byte	14
+	.quad	TGlobal@DTPOFF
+	.byte	224
+	.byte	3                               # Abbrev [3] 0x36:0x4 DW_TAG_base_type
+	.byte	4                               # DW_AT_name
+	.byte	5                               # DW_AT_encoding
+	.byte	4                               # DW_AT_byte_size
+	.byte	2                               # Abbrev [2] 0x3a:0xb DW_TAG_variable
+	.byte	5                               # DW_AT_name
+	.long	54                              # DW_AT_type
+                                        # DW_AT_external
+	.byte	0                               # DW_AT_decl_file
+	.byte	2                               # DW_AT_decl_line
+	.byte	2                               # DW_AT_location
+	.byte	161
+	.byte	0
+	.byte	4                               # Abbrev [4] 0x45:0x2b DW_TAG_subprogram
+	.byte	1                               # DW_AT_low_pc
+	.long	.Lfunc_end0-.Lfunc_begin0       # DW_AT_high_pc
+	.byte	1                               # DW_AT_frame_base
+	.byte	86
+	.byte	7                               # DW_AT_linkage_name
+	.byte	8                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	3                               # DW_AT_decl_line
+                                        # DW_AT_external
+	.byte	5                               # Abbrev [5] 0x51:0x13 DW_TAG_variable
+	.byte	6                               # DW_AT_name
+	.long	54                              # DW_AT_type
+	.byte	0                               # DW_AT_decl_file
+	.byte	4                               # DW_AT_decl_line
+	.byte	10                              # DW_AT_location
+	.byte	14
+	.quad	_ZZ4testvE6TLocal@DTPOFF
+	.byte	224
+	.byte	6                               # Abbrev [6] 0x64:0xb DW_TAG_variable
+	.byte	2                               # DW_AT_location
+	.byte	145
+	.byte	124
+	.byte	9                               # DW_AT_name
+	.byte	0                               # DW_AT_decl_file
+	.byte	7                               # DW_AT_decl_line
+	.long	54                              # DW_AT_type
+	.byte	0                               # End Of Children Mark
+	.byte	0                               # End Of Children Mark
+.Ldebug_info_end0:
+	.section	.debug_str_offsets,"",@progbits
+	.long	44                              # Length of String Offsets Set
+	.short	5
+	.short	0
+.Lstr_offsets_base0:
+	.section	.debug_str,"MS",@progbits,1
+.Linfo_string0:
+	.asciz	"clang version 20.0.0git (https://github.com/CarlosAlbertoEnciso/llvm-project.git 38fca7b7db2ba1647c87679d6750fc4a4bfe72e1)" # string offset=0
+.Linfo_string1:
+	.asciz	"ThreadLocalStorage.cpp"        # string offset=123
+.Linfo_string2:
+	.asciz	"/data/projects/sandbox/debuginfo-analyzer" # string offset=146
+.Linfo_string3:
+	.asciz	"TGlobal"                       # string offset=188
+.Linfo_string4:
+	.asciz	"int"                           # string offset=196
+.Linfo_string5:
+	.asciz	"NGlobal"                       # string offset=200
+.Linfo_string6:
+	.asciz	"TLocal"                        # string offset=208
+.Linfo_string7:
+	.asciz	"_Z4testv"                      # string offset=215
+.Linfo_string8:
+	.asciz	"test"                          # string offset=224
+.Linfo_string9:
+	.asciz	"NLocal"                        # string offset=229
+	.section	.debug_str_offsets,"",@progbits
+	.long	.Linfo_string0
+	.long	.Linfo_string1
+	.long	.Linfo_string2
+	.long	.Linfo_string3
+	.long	.Linfo_string4
+	.long	.Linfo_string5
+	.long	.Linfo_string6
+	.long	.Linfo_string7
+	.long	.Linfo_string8
+	.long	.Linfo_string9
+	.section	.debug_addr,"",@progbits
+	.long	.Ldebug_addr_end0-.Ldebug_addr_start0 # Length of contribution
+.Ldebug_addr_start0:
+	.short	5                               # DWARF version number
+	.byte	8                               # Address size
+	.byte	0                               # Segment selector size
+.Laddr_table_base0:
+	.quad	NGlobal
+	.quad	.Lfunc_begin0
+.Ldebug_addr_end0:
+	.ident	"clang version 20.0.0git (https://github.com/CarlosAlbertoEnciso/llvm-project.git 38fca7b7db2ba1647c87679d6750fc4a4bfe72e1)"
+	.section	".note.GNU-stack","",@progbits
+	.addrsig
+	.addrsig_sym NGlobal
+	.section	.debug_line,"",@progbits
+.Lline_table_start0:
diff --git a/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/crash-thread-local-storage.test b/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/crash-thread-local-storage.test
new file mode 100644
index 00000000000000..3cc100b0941aa6
--- /dev/null
+++ b/llvm/test/tools/llvm-debuginfo-analyzer/DWARF/crash-thread-local-storage.test
@@ -0,0 +1,47 @@
+; REQUIRES: x86-registered-target
+
+; For the given test case:
+
+; // ThreadLocalStorage.cpp
+; 1 thread_local int TGlobal = 0;
+; 2 int NGlobal = 1;
+; 3 void test() {
+; 4   thread_local int TLocal = 0;
+; 5   TGlobal = 1;
+; 6
+; 7   int NLocal = 0;
+; 8   NGlobal = 2;
+; 9 }
+
+; The llvm-debuginfo-analyzer crashes when producing a logical view for
+; the object file generated using the following commands:
+;
+; llvm-mc --triple=x86_64-pc-linux-gnu --filetype=obj ThreadLocalStorage.s -o ThreadLocalStorage.s.o
+; llvm-debuginfo-analyzer --attribute=location --print=symbols
+;
+
+; RUN: llvm-mc --triple=x86_64-pc-linux-gnu --filetype=obj \
+; RUN:         %p/Inputs/ThreadLocalStorage.s -o %t.ThreadLocalStorage.o
+
+; RUN: llvm-debuginfo-analyzer --attribute=location \
+; RUN:                         --print=symbols \
+; RUN:                         %t.ThreadLocalStorage.o 2>&1 | \
+; RUN: FileCheck --strict-whitespace %s
+
+; CHECK: Logical View:
+; CHECK:            {File} '{{.*}}threadlocalstorage.o'
+; CHECK-EMPTY:
+; CHECK:              {CompileUnit} 'threadlocalstorage.cpp'
+; CHECK:      1         {Variable} extern 'TGlobal' -> 'int'
+; CHECK:                  {Location}
+; CHECK:                    {Entry} const_u 0, gnu_push_tls_address
+; CHECK:      2         {Variable} extern 'NGlobal' -> 'int'
+; CHECK:                  {Location}
+; CHECK:                    {Entry} addrx 0
+; CHECK:      3         {Function} extern not_inlined 'test' -> 'void'
+; CHECK:      4           {Variable} 'TLocal' -> 'int'
+; CHECK:                    {Location}
+; CHECK:                      {Entry} const_u 0, gnu_push_tls_address
+; CHECK:      7           {Variable} 'NLocal' -> 'int'
+; CHECK:                    {Location}
+; CHECK:                      {Entry} fbreg -4

Copy link
Member

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall, just wondering a bit about the test.

@@ -0,0 +1,309 @@
.text
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this test could be minimised further...? It would be nice to reduce this down to just what's needed for the issue.

It may also be worth considering the yaml2obj format that some debug info tests use elsewhere.

Copy link
Member Author

@CarlosAlbertoEnciso CarlosAlbertoEnciso Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your comments.
In the initial patches for llvm-debuginfo-analyzer I used yaml2obj.
But for the latest WebAssembly patches, I start using llvm-mca.
llvm/test/tools/llvm-debuginfo-analyzer/WebAssembly

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jryans Changed from using a full .s to a reduced IR, with just the minimum code to reproduce the issue. Thanks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the IR version looks much more minimal and maintainable.

The DW_OP_GNU_push_tls_address, DW_OP_form_tls_address DWARF
location forms generated for thread local storage variables,
caused a crash in the DWARFReader, due to incorrect number of
operands.

Change the test case from being a full .s to a reduced IR.
Copy link
Member

@jryans jryans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, thanks for the fix! 😄

@CarlosAlbertoEnciso
Copy link
Member Author

@jryans Many thanks for your feedback.

@CarlosAlbertoEnciso CarlosAlbertoEnciso merged commit a912c81 into llvm:main Nov 11, 2024
8 checks passed
@CarlosAlbertoEnciso CarlosAlbertoEnciso deleted the crash-thread-local-storage branch November 11, 2024 07:40
Groverkss pushed a commit to iree-org/llvm-project that referenced this pull request Nov 15, 2024
…113904)

The DW_OP_GNU_push_tls_address, DW_OP_form_tls_address DWARF
location forms generated for thread local storage variables, caused a
crash in the DWARFReader, due to incorrect number of operands.
ElvisWang123 added a commit to ElvisWang123/llvm-project that referenced this pull request Apr 29, 2025
…on. NFC

This patch add two new recipes for extended-reduction and the
mul-accumulate-reductions.

Split from llvm#113904.
ElvisWang123 added a commit to ElvisWang123/llvm-project that referenced this pull request Apr 29, 2025
…on. NFC

This patch add two new recipes for extended-reduction and the
mul-accumulate-reductions.

Split from llvm#113904.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants