Skip to content

[lld-macho] Fix invalid DWARF with --icf=safe_thunks #111097

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
merged 5 commits into from
Oct 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lld/MachO/SyntheticSections.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,17 @@ void SymtabSection::emitStabs() {
if (defined->isAbsolute())
continue;

// Never generate a STABS entry for a symbol that has been ICF'ed using a
// thunk - just like we do for fully ICF'ed functions. Otherwise we end up
// generating invalid DWARF as dsymutil will think the entire function
// body is at that location, when in actuality only the thunk will be
// present. This will end up causing overlapping DWARF entries.
// TODO: Find an implementation that works in combination with
// `--keep-icf-stabs`.
if (defined->identicalCodeFoldingKind == Symbol::ICFFoldKind::Thunk) {
continue;
}

// Constant-folded symbols go in the executable's symbol table, but don't
// get a stabs entry unless --keep-icf-stabs flag is specified
if (!config->keepICFStabs &&
Expand Down
233 changes: 149 additions & 84 deletions lld/test/MachO/icf-safe-thunks.ll
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@
; RUN: llvm-objdump %t/icf-safe.dylib -d --macho | FileCheck %s --check-prefixes=CHECK-ARM64
; RUN: cat %t/icf-safe.map | FileCheck %s --check-prefixes=CHECK-ARM64-MAP

;;; Check that we generate valid dSYM and that stabs entries are not created for the ICF'ed functions (thunks)
; RUN: dsymutil %t/icf-safe.dylib -o %t/icf-safe.dSYM
; RUN: llvm-dwarfdump --verify %t/icf-safe.dSYM | FileCheck %s --check-prefix=VERIFY-DSYM
; VERIFY-DSYM: No errors.

;;; Check that we don't generate STABS entries (N_FUN) for ICF'ed functions
; RUN: dsymutil -s %t/icf-safe.dylib | FileCheck %s --check-prefix=VERIFY-STABS
; VERIFY-STABS-NOT: N_FUN {{.*}} _func_2identical_v2
; VERIFY-STABS-NOT: N_FUN {{.*}} _func_3identical_v2
; VERIFY-STABS-NOT: N_FUN {{.*}} _func_3identical_v3
; VERIFY-STABS-NOT: N_FUN {{.*}} _func_3identical_v2_canmerge
; VERIFY-STABS-NOT: N_FUN {{.*}} _func_3identical_v3_canmerge

; CHECK-ARM64: (__TEXT,__text) section
; CHECK-ARM64-NEXT: _func_unique_1:
; CHECK-ARM64-NEXT: mov {{.*}}, #0x1
Expand Down Expand Up @@ -49,127 +62,179 @@
; CHECK-ARM64-MAP-NEXT: 0x00000004 [ 2] _func_3identical_v2
; CHECK-ARM64-MAP-NEXT: 0x00000004 [ 2] _func_3identical_v3

; ModuleID = 'icf-safe-thunks.cpp'
source_filename = "icf-safe-thunks.cpp"
target datalayout = "e-m:o-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "arm64-apple-macosx11.0.0"

@g_val = global i8 0, align 1
@g_ptr = global ptr null, align 8

@g_val = global i8 0, align 1, !dbg !0
@g_ptr = global ptr null, align 8, !dbg !7
; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_unique_1() #0 {
define void @func_unique_1() #0 !dbg !19 {
entry:
store volatile i8 1, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 1, ptr @g_val, align 1, !dbg !22, !tbaa !23
ret void, !dbg !26
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_unique_2_canmerge() local_unnamed_addr #0 {
define void @func_unique_2_canmerge() local_unnamed_addr #0 !dbg !27 {
entry:
store volatile i8 2, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 2, ptr @g_val, align 1, !dbg !28, !tbaa !23
ret void, !dbg !29
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_2identical_v1() #0 {
define void @func_2identical_v1() #0 !dbg !30 {
entry:
store volatile i8 2, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 2, ptr @g_val, align 1, !dbg !31, !tbaa !23
ret void, !dbg !32
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_2identical_v2() #0 {
define void @func_2identical_v2() #0 !dbg !33 {
entry:
store volatile i8 2, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 2, ptr @g_val, align 1, !dbg !34, !tbaa !23
ret void, !dbg !35
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v1() #0 {
define void @func_3identical_v1() #0 !dbg !36 {
entry:
store volatile i8 3, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 3, ptr @g_val, align 1, !dbg !37, !tbaa !23
ret void, !dbg !38
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v2() #0 {
define void @func_3identical_v2() #0 !dbg !39 {
entry:
store volatile i8 3, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 3, ptr @g_val, align 1, !dbg !40, !tbaa !23
ret void, !dbg !41
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v3() #0 {
define void @func_3identical_v3() #0 !dbg !42 {
entry:
store volatile i8 3, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 3, ptr @g_val, align 1, !dbg !43, !tbaa !23
ret void, !dbg !44
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v1_canmerge() local_unnamed_addr #0 {
define void @func_3identical_v1_canmerge() local_unnamed_addr #0 !dbg !45 {
entry:
store volatile i8 33, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 33, ptr @g_val, align 1, !dbg !46, !tbaa !23
ret void, !dbg !47
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v2_canmerge() local_unnamed_addr #0 {
define void @func_3identical_v2_canmerge() local_unnamed_addr #0 !dbg !48 {
entry:
store volatile i8 33, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 33, ptr @g_val, align 1, !dbg !49, !tbaa !23
ret void, !dbg !50
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @func_3identical_v3_canmerge() local_unnamed_addr #0 {
define void @func_3identical_v3_canmerge() local_unnamed_addr #0 !dbg !51 {
entry:
store volatile i8 33, ptr @g_val, align 1, !tbaa !5
ret void
store volatile i8 33, ptr @g_val, align 1, !dbg !52, !tbaa !23
ret void, !dbg !53
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp uwtable(sync)
define void @call_all_funcs() local_unnamed_addr #1 {
define void @call_all_funcs() local_unnamed_addr #1 !dbg !54 {
entry:
tail call void @func_unique_1()
tail call void @func_unique_2_canmerge()
tail call void @func_2identical_v1()
tail call void @func_2identical_v2()
tail call void @func_3identical_v1()
tail call void @func_3identical_v2()
tail call void @func_3identical_v3()
tail call void @func_3identical_v1_canmerge()
tail call void @func_3identical_v2_canmerge()
tail call void @func_3identical_v3_canmerge()
ret void
tail call void @func_unique_1(), !dbg !55
tail call void @func_unique_2_canmerge(), !dbg !56
tail call void @func_2identical_v1(), !dbg !57
tail call void @func_2identical_v2(), !dbg !58
tail call void @func_3identical_v1(), !dbg !59
tail call void @func_3identical_v2(), !dbg !60
tail call void @func_3identical_v3(), !dbg !61
tail call void @func_3identical_v1_canmerge(), !dbg !62
tail call void @func_3identical_v2_canmerge(), !dbg !63
tail call void @func_3identical_v3_canmerge(), !dbg !64
ret void, !dbg !65
}

; Function Attrs: mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync)
define void @take_func_addr() local_unnamed_addr #0 {
define void @take_func_addr() local_unnamed_addr #0 !dbg !66 {
entry:
store volatile ptr @func_unique_1, ptr @g_ptr, align 8, !tbaa !8
store volatile ptr @func_2identical_v1, ptr @g_ptr, align 8, !tbaa !8
store volatile ptr @func_2identical_v2, ptr @g_ptr, align 8, !tbaa !8
store volatile ptr @func_3identical_v1, ptr @g_ptr, align 8, !tbaa !8
store volatile ptr @func_3identical_v2, ptr @g_ptr, align 8, !tbaa !8
store volatile ptr @func_3identical_v3, ptr @g_ptr, align 8, !tbaa !8
ret void
store volatile ptr @func_unique_1, ptr @g_ptr, align 8, !dbg !67, !tbaa !68
store volatile ptr @func_2identical_v1, ptr @g_ptr, align 8, !dbg !70, !tbaa !68
store volatile ptr @func_2identical_v2, ptr @g_ptr, align 8, !dbg !71, !tbaa !68
store volatile ptr @func_3identical_v1, ptr @g_ptr, align 8, !dbg !72, !tbaa !68
store volatile ptr @func_3identical_v2, ptr @g_ptr, align 8, !dbg !73, !tbaa !68
store volatile ptr @func_3identical_v3, ptr @g_ptr, align 8, !dbg !74, !tbaa !68
ret void, !dbg !75
}

attributes #0 = { mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+altnzcv,+ccdp,+complxnum,+crc,+dotprod,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a,+zcm,+zcz" }
attributes #1 = { mustprogress nofree noinline norecurse nounwind ssp uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+altnzcv,+ccdp,+complxnum,+crc,+dotprod,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a,+zcm,+zcz" }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"uwtable", i32 1}
!3 = !{i32 7, !"frame-pointer", i32 1}
!4 = !{!"clang"}
!5 = !{!6, !6, i64 0}
!6 = !{!"omnipotent char", !7, i64 0}
!7 = !{!"Simple C++ TBAA"}
!8 = !{!9, !9, i64 0}
!9 = !{!"any pointer", !6, i64 0}

attributes #0 = { mustprogress nofree noinline norecurse nounwind ssp memory(readwrite, argmem: none) uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+altnzcv,+ccdp,+ccidx,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a,+zcm,+zcz" }
attributes #1 = { mustprogress nofree noinline norecurse nounwind ssp uwtable(sync) "frame-pointer"="non-leaf" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="apple-m1" "target-features"="+aes,+altnzcv,+ccdp,+ccidx,+complxnum,+crc,+dit,+dotprod,+flagm,+fp-armv8,+fp16fml,+fptoint,+fullfp16,+jsconv,+lse,+neon,+pauth,+perfmon,+predres,+ras,+rcpc,+rdm,+sb,+sha2,+sha3,+specrestrict,+ssbs,+v8.1a,+v8.2a,+v8.3a,+v8.4a,+v8a,+zcm,+zcz" }
!llvm.dbg.cu = !{!2}
!llvm.module.flags = !{!12, !13, !14, !15, !16, !17}
!llvm.ident = !{!18}
!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression())
!1 = distinct !DIGlobalVariable(name: "g_val", scope: !2, file: !3, line: 5, type: !10, isLocal: false, isDefinition: true)
!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "clang version 20.0.0git (https://github.com/alx32/llvm-project.git 02d6aad5cc940f17904c1288dfabc3fd2d439279)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, retainedTypes: !4, globals: !6, splitDebugInlining: false, nameTableKind: Apple, sysroot: "/")
!3 = !DIFile(filename: "icf-safe-thunks.cpp", directory: "/tmp/safe_thunks")
!4 = !{!5}
!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
!6 = !{!0, !7}
!7 = !DIGlobalVariableExpression(var: !8, expr: !DIExpression())
!8 = distinct !DIGlobalVariable(name: "g_ptr", scope: !2, file: !3, line: 6, type: !9, isLocal: false, isDefinition: true)
!9 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !5)
!10 = !DIDerivedType(tag: DW_TAG_volatile_type, baseType: !11)
!11 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
!12 = !{i32 7, !"Dwarf Version", i32 4}
!13 = !{i32 2, !"Debug Info Version", i32 3}
!14 = !{i32 1, !"wchar_size", i32 4}
!15 = !{i32 8, !"PIC Level", i32 2}
!16 = !{i32 7, !"uwtable", i32 1}
!17 = !{i32 7, !"frame-pointer", i32 1}
!18 = !{!"clang version 20.0.0git (https://github.com/alx32/llvm-project.git 02d6aad5cc940f17904c1288dfabc3fd2d439279)"}
!19 = distinct !DISubprogram(name: "func_unique_1", scope: !3, file: !3, line: 8, type: !20, scopeLine: 8, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!20 = !DISubroutineType(types: !21)
!21 = !{null}
!22 = !DILocation(line: 9, column: 11, scope: !19)
!23 = !{!24, !24, i64 0}
!24 = !{!"omnipotent char", !25, i64 0}
!25 = !{!"Simple C++ TBAA"}
!26 = !DILocation(line: 10, column: 1, scope: !19)
!27 = distinct !DISubprogram(name: "func_unique_2_canmerge", scope: !3, file: !3, line: 12, type: !20, scopeLine: 12, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!28 = !DILocation(line: 13, column: 11, scope: !27)
!29 = !DILocation(line: 14, column: 1, scope: !27)
!30 = distinct !DISubprogram(name: "func_2identical_v1", scope: !3, file: !3, line: 16, type: !20, scopeLine: 16, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!31 = !DILocation(line: 17, column: 11, scope: !30)
!32 = !DILocation(line: 18, column: 1, scope: !30)
!33 = distinct !DISubprogram(name: "func_2identical_v2", scope: !3, file: !3, line: 20, type: !20, scopeLine: 20, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!34 = !DILocation(line: 21, column: 11, scope: !33)
!35 = !DILocation(line: 22, column: 1, scope: !33)
!36 = distinct !DISubprogram(name: "func_3identical_v1", scope: !3, file: !3, line: 24, type: !20, scopeLine: 24, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!37 = !DILocation(line: 25, column: 11, scope: !36)
!38 = !DILocation(line: 26, column: 1, scope: !36)
!39 = distinct !DISubprogram(name: "func_3identical_v2", scope: !3, file: !3, line: 28, type: !20, scopeLine: 28, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!40 = !DILocation(line: 29, column: 11, scope: !39)
!41 = !DILocation(line: 30, column: 1, scope: !39)
!42 = distinct !DISubprogram(name: "func_3identical_v3", scope: !3, file: !3, line: 32, type: !20, scopeLine: 32, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!43 = !DILocation(line: 33, column: 11, scope: !42)
!44 = !DILocation(line: 34, column: 1, scope: !42)
!45 = distinct !DISubprogram(name: "func_3identical_v1_canmerge", scope: !3, file: !3, line: 36, type: !20, scopeLine: 36, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!46 = !DILocation(line: 37, column: 11, scope: !45)
!47 = !DILocation(line: 38, column: 1, scope: !45)
!48 = distinct !DISubprogram(name: "func_3identical_v2_canmerge", scope: !3, file: !3, line: 40, type: !20, scopeLine: 40, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!49 = !DILocation(line: 41, column: 11, scope: !48)
!50 = !DILocation(line: 42, column: 1, scope: !48)
!51 = distinct !DISubprogram(name: "func_3identical_v3_canmerge", scope: !3, file: !3, line: 44, type: !20, scopeLine: 44, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!52 = !DILocation(line: 45, column: 11, scope: !51)
!53 = !DILocation(line: 46, column: 1, scope: !51)
!54 = distinct !DISubprogram(name: "call_all_funcs", scope: !3, file: !3, line: 48, type: !20, scopeLine: 48, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!55 = !DILocation(line: 49, column: 5, scope: !54)
!56 = !DILocation(line: 50, column: 5, scope: !54)
!57 = !DILocation(line: 51, column: 5, scope: !54)
!58 = !DILocation(line: 52, column: 5, scope: !54)
!59 = !DILocation(line: 53, column: 5, scope: !54)
!60 = !DILocation(line: 54, column: 5, scope: !54)
!61 = !DILocation(line: 55, column: 5, scope: !54)
!62 = !DILocation(line: 56, column: 5, scope: !54)
!63 = !DILocation(line: 57, column: 5, scope: !54)
!64 = !DILocation(line: 58, column: 5, scope: !54)
!65 = !DILocation(line: 59, column: 1, scope: !54)
!66 = distinct !DISubprogram(name: "take_func_addr", scope: !3, file: !3, line: 61, type: !20, scopeLine: 61, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !2)
!67 = !DILocation(line: 62, column: 11, scope: !66)
!68 = !{!69, !69, i64 0}
!69 = !{!"any pointer", !24, i64 0}
!70 = !DILocation(line: 63, column: 11, scope: !66)
!71 = !DILocation(line: 64, column: 11, scope: !66)
!72 = !DILocation(line: 65, column: 11, scope: !66)
!73 = !DILocation(line: 66, column: 11, scope: !66)
!74 = !DILocation(line: 67, column: 11, scope: !66)
!75 = !DILocation(line: 68, column: 1, scope: !66)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;; Generate the above LLVM IR with the below script ;;;;;;;;;;;;;;;
Expand Down Expand Up @@ -250,5 +315,5 @@ attributes #1 = { mustprogress nofree noinline norecurse nounwind ssp uwtable(sy
; }
; EOF
;
; $TOOLCHAIN_BIN/clang -target arm64-apple-macos11.0 -S -emit-llvm \
; $TOOLCHAIN_BIN/clang -target arm64-apple-macos11.0 -S -emit-llvm -g \
; icf-safe-thunks.cpp -O3 -o icf-safe-thunks.ll
Loading