diff --git a/bolt/test/X86/encoding-validation.s b/bolt/test/X86/encoding-validation.s
index 0c716abc4cc0cf4..c01361296489502 100644
--- a/bolt/test/X86/encoding-validation.s
+++ b/bolt/test/X86/encoding-validation.s
@@ -2,7 +2,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o -o %t.exe -q
-# RUN: llvm-bolt %t.exe --relocs -o %t.out --check-encoding |& FileCheck %s
+# RUN: llvm-bolt %t.exe --relocs -o %t.out --check-encoding 2>&1 | FileCheck %s
.text
.globl _start
diff --git a/bolt/test/X86/gotpcrelx.s b/bolt/test/X86/gotpcrelx.s
index 6dec125c6a72bdf..c5bc7ac7d0d4723 100644
--- a/bolt/test/X86/gotpcrelx.s
+++ b/bolt/test/X86/gotpcrelx.s
@@ -10,11 +10,11 @@
# RUN: ld.lld %t.o -o %t.pie.exe -q -pie
# RUN: ld.lld %t.o -o %t.no-relax.exe -q --no-relax
# RUN: llvm-bolt %t.exe --relocs -o %t.out --print-cfg --print-only=_start \
-# RUN: |& FileCheck --check-prefix=BOLT %s
+# RUN: 2>&1 | FileCheck --check-prefix=BOLT %s
# RUN: llvm-bolt %t.pie.exe -o %t.null --print-cfg --print-only=_start \
-# RUN: |& FileCheck --check-prefix=PIE-BOLT %s
+# RUN: 2>&1 | FileCheck --check-prefix=PIE-BOLT %s
# RUN: llvm-bolt %t.no-relax.exe -o %t.null --print-cfg --print-only=_start \
-# RUN: |& FileCheck --check-prefix=NO-RELAX-BOLT %s
+# RUN: 2>&1 | FileCheck --check-prefix=NO-RELAX-BOLT %s
# RUN: llvm-objdump -d --no-show-raw-insn --print-imm-hex \
# RUN: %t.out | FileCheck --check-prefix=DISASM %s
diff --git a/bolt/test/X86/icf-jump-tables.test b/bolt/test/X86/icf-jump-tables.test
index 41820008b01338b..57137b79bc317e1 100644
--- a/bolt/test/X86/icf-jump-tables.test
+++ b/bolt/test/X86/icf-jump-tables.test
@@ -4,7 +4,7 @@
# REQUIRES: system-linux
# RUN: %clang %cflags -O1 -g %p/../Inputs/icf-jump-tables.c -o %t.exe -Wl,-q
-# RUN: llvm-bolt %t.exe --icf -o %t.bolt |& FileCheck %s
+# RUN: llvm-bolt %t.exe --icf -o %t.bolt 2>&1 | FileCheck %s
## Check that BOLT successfully folded a function with jump table:
# CHECK: ICF folded {{.*}}. {{[^0]}} functions had jump tables.
diff --git a/bolt/test/X86/indirect-goto-pie.test b/bolt/test/X86/indirect-goto-pie.test
index 81cff9a32fbbddc..3311c1aec061c5e 100644
--- a/bolt/test/X86/indirect-goto-pie.test
+++ b/bolt/test/X86/indirect-goto-pie.test
@@ -6,7 +6,7 @@ REQUIRES: x86_64-linux
RUN: %clang %S/Inputs/indirect_goto.c -o %t -fpic -pie -Wl,-q
RUN: not llvm-bolt %t -o %t.bolt --relocs=1 --print-cfg --print-only=main \
-RUN: |& FileCheck %s
+RUN: 2>&1 | FileCheck %s
## Check that processing works if main() is skipped.
RUN: llvm-bolt %t -o %t.bolt --relocs=1 --skip-funcs=main
diff --git a/bolt/test/X86/jump-table-func-entry.s b/bolt/test/X86/jump-table-func-entry.s
index 77b444d520a1f10..b34e7142785d4a0 100644
--- a/bolt/test/X86/jump-table-func-entry.s
+++ b/bolt/test/X86/jump-table-func-entry.s
@@ -7,7 +7,7 @@
# RUN: %clang %cflags %t.o -o %t.exe -no-pie -Wl,-q
# RUN: llvm-bolt %t.exe --print-normalized --print-only=foo -o %t.out \
-# RUN: |& FileCheck %s
+# RUN: 2>&1 | FileCheck %s
diff --git a/bolt/test/X86/keep-nops.s b/bolt/test/X86/keep-nops.s
index 37da2ff07b9b798..ddd34ce61d620de 100644
--- a/bolt/test/X86/keep-nops.s
+++ b/bolt/test/X86/keep-nops.s
@@ -5,7 +5,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-linux %s -o %t.o
# RUN: ld.lld %t.o -o %t.exe -q
# RUN: llvm-bolt %t.exe -o %t.bolt.exe --keep-nops --relocs --print-finalized \
-# RUN: |& FileCheck --check-prefix=CHECK-BOLT %s
+# RUN: 2>&1 | FileCheck --check-prefix=CHECK-BOLT %s
# RUN: llvm-objdump -d %t.bolt.exe | FileCheck %s
.text
diff --git a/bolt/test/X86/linux-bug-table.s b/bolt/test/X86/linux-bug-table.s
index 63f70a0b35d9fe5..07a4729ade73747 100644
--- a/bolt/test/X86/linux-bug-table.s
+++ b/bolt/test/X86/linux-bug-table.s
@@ -15,7 +15,7 @@
## Verify bug entry bindings again after unreachable code elimination.
# RUN: llvm-bolt %t.out -o %t.out.1 --print-only=_start --print-normalized \
-# RUN: |& FileCheck --check-prefix=CHECK-REOPT %s
+# RUN: 2>&1 | FileCheck --check-prefix=CHECK-REOPT %s
# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 bug table entries
diff --git a/bolt/test/X86/linux-orc.s b/bolt/test/X86/linux-orc.s
index 5f2096278e92d6a..1b0e681b1dbf96e 100644
--- a/bolt/test/X86/linux-orc.s
+++ b/bolt/test/X86/linux-orc.s
@@ -9,7 +9,7 @@
## Verify reading contents of ORC sections.
-# RUN: llvm-bolt %t.exe --dump-orc -o /dev/null |& FileCheck %s \
+# RUN: llvm-bolt %t.exe --dump-orc -o /dev/null 2>&1 | FileCheck %s \
# RUN: --check-prefix=CHECK-ORC
# CHECK-ORC: BOLT-INFO: ORC unwind information:
@@ -27,19 +27,19 @@
## Verify ORC bindings to instructions.
# RUN: llvm-bolt %t.exe --print-normalized --dump-orc --print-orc -o %t.out \
-# RUN: --keep-nops=0 --bolt-info=0 |& FileCheck %s
+# RUN: --keep-nops=0 --bolt-info=0 2>&1 | FileCheck %s
## Verify ORC bindings after rewrite.
# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized --print-orc \
-# RUN: |& FileCheck %s
+# RUN: 2>&1 | FileCheck %s
## Verify ORC binding after rewrite when some of the functions are skipped.
# RUN: llvm-bolt %t.exe -o %t.out --skip-funcs=bar --bolt-info=0 --keep-nops=0
# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized --print-orc \
-# RUN: |& FileCheck %s
+# RUN: 2>&1 | FileCheck %s
# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 9 ORC entries
diff --git a/bolt/test/X86/linux-pci-fixup.s b/bolt/test/X86/linux-pci-fixup.s
index a574ba84c4df11e..42504c108d339ce 100644
--- a/bolt/test/X86/linux-pci-fixup.s
+++ b/bolt/test/X86/linux-pci-fixup.s
@@ -3,7 +3,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
-# RUN: llvm-bolt %t.exe --print-normalized -o %t.out |& FileCheck %s
+# RUN: llvm-bolt %t.exe --print-normalized -o %t.out 2>&1 | FileCheck %s
## Check that BOLT correctly parses the Linux kernel .pci_fixup section and
## verify that PCI fixup hook in the middle of a function is detected.
diff --git a/bolt/test/X86/linux-smp-locks.s b/bolt/test/X86/linux-smp-locks.s
index 5f4410d14fc6b08..50d9e632b117205 100644
--- a/bolt/test/X86/linux-smp-locks.s
+++ b/bolt/test/X86/linux-smp-locks.s
@@ -7,11 +7,11 @@
# RUN: %clang %cflags -nostdlib %t.o -o %t.exe \
# RUN: -Wl,--image-base=0xffffffff80000000,--no-dynamic-linker,--no-eh-frame-hdr,--no-pie
# RUN: llvm-bolt %t.exe --print-normalized --keep-nops=0 --bolt-info=0 -o %t.out \
-# RUN: |& FileCheck %s
+# RUN: 2>&1 | FileCheck %s
## Check the output of BOLT with NOPs removed.
-# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s
+# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized 2>&1 | FileCheck %s
# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 SMP lock entries
diff --git a/bolt/test/X86/linux-static-calls.s b/bolt/test/X86/linux-static-calls.s
index caf95e1c03227d2..ce90f4bb79c094e 100644
--- a/bolt/test/X86/linux-static-calls.s
+++ b/bolt/test/X86/linux-static-calls.s
@@ -9,11 +9,11 @@
## Verify static calls bindings to instructions.
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
-# RUN: --bolt-info=0 |& FileCheck %s
+# RUN: --bolt-info=0 2>&1 | FileCheck %s
## Verify the bindings again on the rewritten binary with nops removed.
-# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s
+# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized 2>&1 | FileCheck %s
# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 static call entries
diff --git a/bolt/test/X86/linux-static-keys.s b/bolt/test/X86/linux-static-keys.s
index fb419e0f7627559..0bd17a375d88248 100644
--- a/bolt/test/X86/linux-static-keys.s
+++ b/bolt/test/X86/linux-static-keys.s
@@ -11,17 +11,17 @@
## Verify static keys jump bindings to instructions.
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
-# RUN: --bolt-info=0 |& FileCheck %s
+# RUN: --bolt-info=0 2>&1 | FileCheck %s
## Verify that profile is matched correctly.
# RUN: llvm-bolt %t.exe --print-normalized -o %t.out --keep-nops=0 \
-# RUN: --bolt-info=0 --data %t.fdata |& \
-# RUN: FileCheck --check-prefix=CHECK-FDATA %s
+# RUN: --bolt-info=0 --data %t.fdata 2>&1 \
+# RUN: | FileCheck --check-prefix=CHECK-FDATA %s
## Verify the bindings again on the rewritten binary with nops removed.
-# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized |& FileCheck %s
+# RUN: llvm-bolt %t.out -o %t.out.1 --print-normalized 2>&1 | FileCheck %s
# CHECK: BOLT-INFO: Linux kernel binary detected
# CHECK: BOLT-INFO: parsed 2 static keys jump entries
diff --git a/bolt/test/X86/pt_gnu_relro.s b/bolt/test/X86/pt_gnu_relro.s
index d7cfad5f954be5e..ff897b96e090970 100644
--- a/bolt/test/X86/pt_gnu_relro.s
+++ b/bolt/test/X86/pt_gnu_relro.s
@@ -22,7 +22,7 @@
# READELF: 04 .got
# RUN: llvm-bolt %t.exe --relocs -o %t.null -v=1 \
-# RUN: |& FileCheck --check-prefix=BOLT %s
+# RUN: 2>&1 | FileCheck --check-prefix=BOLT %s
# BOLT: BOLT-INFO: marking .got as GNU_RELRO
.globl _start
diff --git a/bolt/test/X86/unclaimed-jt-entries.s b/bolt/test/X86/unclaimed-jt-entries.s
index 2d56167286c36bb..1102e4ae413e277 100644
--- a/bolt/test/X86/unclaimed-jt-entries.s
+++ b/bolt/test/X86/unclaimed-jt-entries.s
@@ -18,7 +18,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags -no-pie %t.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt %t.exe -v=1 -o %t.out |& FileCheck %s
+# RUN: llvm-bolt %t.exe -v=1 -o %t.out 2>&1 | FileCheck %s
# CHECK: BOLT-WARNING: unclaimed data to code reference (possibly an unrecognized jump table entry) to .Ltmp[[#]] in main
# CHECK: BOLT-WARNING: unclaimed data to code reference (possibly an unrecognized jump table entry) to .Ltmp[[#]] in main
diff --git a/bolt/test/X86/vararg.test b/bolt/test/X86/vararg.test
index 0b8668a842ed4d5..296c739c6e11383 100644
--- a/bolt/test/X86/vararg.test
+++ b/bolt/test/X86/vararg.test
@@ -5,7 +5,7 @@
REQUIRES: x86_64-linux
RUN: %clangxx %cxxflags -no-pie %p/../Inputs/vararg.s -o %t -Wl,-q
-RUN: llvm-bolt %t -o %t.null --print-cfg --print-only=.*printf.* |& FileCheck %s
+RUN: llvm-bolt %t -o %t.null --print-cfg --print-only=.*printf.* 2>&1 | FileCheck %s
CHECK: IsSimple : 0
CHECK: Entry Point
diff --git a/bolt/test/runtime/X86/unclaimed-jt-entries.s b/bolt/test/runtime/X86/unclaimed-jt-entries.s
index d0691d256ba0458..1725fb808efbff6 100644
--- a/bolt/test/runtime/X86/unclaimed-jt-entries.s
+++ b/bolt/test/runtime/X86/unclaimed-jt-entries.s
@@ -18,7 +18,7 @@
# RUN: llvm-mc -filetype=obj -triple x86_64-unknown-unknown %s -o %t.o
# RUN: %clang %cflags %S/Inputs/unclaimed-jt-entries.c -no-pie %t.o -o %t.exe -Wl,-q
-# RUN: llvm-bolt %t.exe -v=1 -o %t.out --sequential-disassembly |& FileCheck %s
+# RUN: llvm-bolt %t.exe -v=1 -o %t.out --sequential-disassembly 2>&1 | FileCheck %s
# CHECK: BOLT-WARNING: unclaimed data to code reference (possibly an unrecognized jump table entry) to .Ltmp[[#]] in func
# CHECK: BOLT-WARNING: unclaimed data to code reference (possibly an unrecognized jump table entry) to .Ltmp[[#]] in func
diff --git a/clang-tools-extra/clang-doc/HTMLGenerator.cpp b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
index aef22453035c30a..21ee417da6028a8 100644
--- a/clang-tools-extra/clang-doc/HTMLGenerator.cpp
+++ b/clang-tools-extra/clang-doc/HTMLGenerator.cpp
@@ -352,6 +352,7 @@ genHTML(const EnumInfo &I, const ClangDocContext &CDCtx);
static std::vector>
genHTML(const FunctionInfo &I, const ClangDocContext &CDCtx,
StringRef ParentInfoDir);
+static std::unique_ptr genHTML(const std::vector &C);
static std::vector>
genEnumsBlock(const std::vector &Enums,
@@ -418,9 +419,13 @@ genRecordMembersBlock(const llvm::SmallVector &Members,
if (Access != "")
Access = Access + " ";
auto LIBody = std::make_unique(HTMLTag::TAG_LI);
- LIBody->Children.emplace_back(std::make_unique(Access));
- LIBody->Children.emplace_back(genReference(M.Type, ParentInfoDir));
- LIBody->Children.emplace_back(std::make_unique(" " + M.Name));
+ auto MemberDecl = std::make_unique(HTMLTag::TAG_DIV);
+ MemberDecl->Children.emplace_back(std::make_unique(Access));
+ MemberDecl->Children.emplace_back(genReference(M.Type, ParentInfoDir));
+ MemberDecl->Children.emplace_back(std::make_unique(" " + M.Name));
+ if (!M.Description.empty())
+ LIBody->Children.emplace_back(genHTML(M.Description));
+ LIBody->Children.emplace_back(std::move(MemberDecl));
ULBody->Children.emplace_back(std::move(LIBody));
}
return Out;
diff --git a/clang-tools-extra/test/clang-doc/basic-project.test b/clang-tools-extra/test/clang-doc/basic-project.test
index c2c7548f5a9690c..2865ed4446e7ef8 100644
--- a/clang-tools-extra/test/clang-doc/basic-project.test
+++ b/clang-tools-extra/test/clang-doc/basic-project.test
@@ -91,8 +91,10 @@
// HTML-RECTANGLE: Shape
// HTML-RECTANGLE:
// HTML-RECTANGLE: Members
-// HTML-RECTANGLE: private double width_
-// HTML-RECTANGLE: private double height_
+// HTML-RECTANGLE: Width of the rectangle.
+// HTML-RECTANGLE: private double width_
+// HTML-RECTANGLE: Height of the rectangle.
+// HTML-RECTANGLE: private double height_
// HTML-RECTANGLE: Functions
// HTML-RECTANGLE: Rectangle
// HTML-RECTANGLE: public void Rectangle(double width, double height)
@@ -112,7 +114,8 @@
// HTML-CIRCLE: Shape
// HTML-CIRCLE:
// HTML-CIRCLE: Members
-// HTML-CIRCLE: private double radius_
+// HTML-CIRCLE: Radius of the circle.
+// HTML-CIRCLE: private double radius_
// HTML-CIRCLE: Functions
// HTML-CIRCLE: Circle
// HTML-CIRCLE: public void Circle(double radius)
diff --git a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
index e4a7340318b9341..bd031282b042abb 100644
--- a/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
+++ b/clang-tools-extra/unittests/clang-doc/HTMLGeneratorTest.cpp
@@ -197,7 +197,9 @@ TEST(HTMLGeneratorTest, emitRecordHTML) {
Members
- - private int X
+ -
+
private int X
+
Records
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6796a619ba97f8e..39e1b0fcb09bbd5 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -217,6 +217,8 @@ Bug Fixes to C++ Support
- Clang now preserves the unexpanded flag in a lambda transform used for pack expansion. (#GH56852), (#GH85667),
(#GH99877).
- Fixed a bug when diagnosing ambiguous explicit specializations of constrained member functions.
+- Fixed an assertion failure when selecting a function from an overload set that includes a
+ specialization of a conversion function template.
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 94cf9113cd43a47..58a820508da42b6 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -34,6 +34,7 @@
#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
@@ -1194,8 +1195,8 @@ class ASTContext : public RefCountedBase {
llvm::DenseSet CUDADeviceVarODRUsedByHost;
/// Keep track of CUDA/HIP external kernels or device variables ODR-used by
- /// host code.
- llvm::DenseSet CUDAExternalDeviceDeclODRUsedByHost;
+ /// host code. SetVector is used to maintain the order.
+ llvm::SetVector CUDAExternalDeviceDeclODRUsedByHost;
/// Keep track of CUDA/HIP implicit host device functions used on device side
/// in device compilation.
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index a024f9b2a9f8c0d..eb82e0159b56ede 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -166,15 +166,22 @@ def note_constexpr_heap_alloc_limit_exceeded : Note<
def note_constexpr_this : Note<
"%select{|implicit }0use of 'this' pointer is only allowed within the "
"evaluation of a call to a 'constexpr' member function">;
-def note_constexpr_lifetime_ended : Note<
+def access_kind : TextSubstitution<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 %select{temporary|variable}1 whose "
- "%plural{8:storage duration|:lifetime}0 has ended">;
-def note_constexpr_access_uninit : Note<
+ "destruction of}0">;
+def access_kind_subobject : TextSubstitution<
"%select{read of|read of|assignment to|increment of|decrement of|"
"member call on|dynamic_cast of|typeid applied to|"
- "construction of subobject of|destruction of}0 "
+ "construction of subobject of|destruction of}0">;
+def access_kind_volatile : TextSubstitution<
+ "%select{read of|read of|assignment to|increment of|decrement of|"
+ "||||}0">;
+def note_constexpr_lifetime_ended : Note<
+ "%sub{access_kind}0 %select{temporary|variable}1 whose "
+ "%plural{8:storage duration|:lifetime}0 has ended">;
+def note_constexpr_access_uninit : Note<
+ "%sub{access_kind_subobject}0 "
"%select{object outside its lifetime|uninitialized object}1 "
"is not allowed in a constant expression">;
def note_constexpr_use_uninit_reference : Note<
@@ -184,20 +191,16 @@ def note_constexpr_modify_const_type : Note<
"modification of object of const-qualified type %0 is not allowed "
"in a constant expression">;
def note_constexpr_access_volatile_type : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "|||}0 "
+ "%sub{access_kind_volatile}0 "
"volatile-qualified type %1 is not allowed in a constant expression">;
def note_constexpr_access_volatile_obj : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "|||}0 "
+ "%sub{access_kind_volatile}0 "
"volatile %select{temporary|object %2|member %2}1 is not allowed in "
"a constant expression">;
def note_constexpr_volatile_here : Note<
"volatile %select{temporary created|object declared|member declared}0 here">;
def note_constexpr_access_mutable : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
+ "%sub{access_kind}0 "
"mutable member %1 is not allowed in a constant expression">;
def note_constexpr_ltor_non_const_int : Note<
"read of non-const variable %0 is not allowed in a constant expression">;
@@ -209,47 +212,28 @@ def note_constexpr_ltor_non_constexpr : Note<
def note_constexpr_ltor_incomplete_type : Note<
"read of incomplete type %0 is not allowed in a constant expression">;
def note_constexpr_access_null : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
+ "%sub{access_kind}0 "
"dereferenced null pointer is not allowed in a constant expression">;
def note_constexpr_access_past_end : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
- "dereferenced one-past-the-end pointer is not allowed "
- "in a constant expression">;
+ "%sub{access_kind}0 dereferenced one-past-the-end pointer "
+ "is not allowed in a constant expression">;
def note_constexpr_access_unsized_array : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
- "element of array without known bound "
+ "%sub{access_kind}0 element of array without known bound "
"is not allowed in a constant expression">;
def note_constexpr_access_inactive_union_member : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|"
- "construction of subobject of|destruction of}0 "
+ "%sub{access_kind_subobject}0 "
"member %1 of union with %select{active member %3|no active member}2 "
"is not allowed in a constant expression">;
def note_constexpr_union_member_change_during_init : Note<
"assignment would change active union member during the initialization of "
"a different member of the same union">;
def note_constexpr_access_static_temporary : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|reconstruction of|"
- "destruction of}0 temporary "
- "is not allowed in a constant expression outside the expression that "
- "created the temporary">;
+ "%sub{access_kind}0 temporary is not allowed in a constant expression "
+ "outside the expression that created the temporary">;
def note_constexpr_access_unreadable_object : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
- "object '%1' whose value is not known">;
+ "%sub{access_kind}0 object '%1' whose value is not known">;
def note_constexpr_access_deleted_object : Note<
- "%select{read of|read of|assignment to|increment of|decrement of|"
- "member call on|dynamic_cast of|typeid applied to|construction of|"
- "destruction of}0 "
- "heap allocated object that has been deleted">;
+ "%sub{access_kind}0 heap allocated object that has been deleted">;
def note_constexpr_modify_global : Note<
"a constant expression cannot modify an object that is visible outside "
"that expression">;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index f8d50d12bb9351d..12aab09f285567f 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1260,9 +1260,6 @@ def warn_pragma_intrinsic_builtin : Warning<
def warn_pragma_unused_expected_var : Warning<
"expected '#pragma unused' argument to be a variable name">,
InGroup;
-// - #pragma mc_func
-def err_pragma_mc_func_not_supported :
- Error<"#pragma mc_func is not supported">;
// - #pragma init_seg
def warn_pragma_init_seg_unsupported_target : Warning<
"'#pragma init_seg' is only supported when targeting a "
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e196c3dc5cb3be8..0b38139bd279725 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -8114,13 +8114,6 @@ def source_date_epoch : Separate<["-"], "source-date-epoch">,
} // let Visibility = [CC1Option]
-defm err_pragma_mc_func_aix : BoolFOption<"err-pragma-mc-func-aix",
- PreprocessorOpts<"ErrorOnPragmaMcfuncOnAIX">, DefaultFalse,
- PosFlag,
- NegFlag>;
-
//===----------------------------------------------------------------------===//
// CUDA Options
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/Lex/PreprocessorOptions.h b/clang/include/clang/Lex/PreprocessorOptions.h
index 3f7dd9db18ba7d1..c2e3d68333024a5 100644
--- a/clang/include/clang/Lex/PreprocessorOptions.h
+++ b/clang/include/clang/Lex/PreprocessorOptions.h
@@ -211,10 +211,6 @@ class PreprocessorOptions {
/// If set, the UNIX timestamp specified by SOURCE_DATE_EPOCH.
std::optional SourceDateEpoch;
- /// If set, the preprocessor reports an error when processing #pragma mc_func
- /// on AIX.
- bool ErrorOnPragmaMcfuncOnAIX = false;
-
public:
PreprocessorOptions() : PrecompiledPreambleBytes(0, false) {}
@@ -252,7 +248,6 @@ class PreprocessorOptions {
PrecompiledPreambleBytes.first = 0;
PrecompiledPreambleBytes.second = false;
RetainExcludedConditionalBlocks = false;
- ErrorOnPragmaMcfuncOnAIX = false;
}
};
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 39c5f588167edeb..99a0b0200fa06fe 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -221,7 +221,6 @@ class Parser : public CodeCompletionHandler {
std::unique_ptr MaxTokensHerePragmaHandler;
std::unique_ptr MaxTokensTotalPragmaHandler;
std::unique_ptr RISCVPragmaHandler;
- std::unique_ptr MCFuncPragmaHandler;
std::unique_ptr CommentSemaHandler;
diff --git a/clang/lib/AST/Interp/Interp.cpp b/clang/lib/AST/Interp/Interp.cpp
index 13390007fde33c8..4a50b4487b66544 100644
--- a/clang/lib/AST/Interp/Interp.cpp
+++ b/clang/lib/AST/Interp/Interp.cpp
@@ -126,13 +126,17 @@ static bool CheckActive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
return true;
assert(Ptr.inUnion());
+ assert(Ptr.isField() && Ptr.getField());
Pointer U = Ptr.getBase();
Pointer C = Ptr;
while (!U.isRoot() && U.inUnion() && !U.isActive()) {
- C = U;
+ if (U.getField())
+ C = U;
U = U.getBase();
}
+ assert(C.isField());
+
// Get the inactive field descriptor.
const FieldDecl *InactiveField = C.getField();
assert(InactiveField);
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index 1841a2a4714d89d..c3370e2e5286e07 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -1635,7 +1635,58 @@ bool SetThreeWayComparisonField(InterpState &S, CodePtr OpPC,
return true;
}
-bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
+static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
+ Pointer &Dest, bool Activate);
+static bool copyRecord(InterpState &S, CodePtr OpPC, const Pointer &Src,
+ Pointer &Dest, bool Activate = false) {
+ [[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
+ const Descriptor *DestDesc = Dest.getFieldDesc();
+
+ auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
+ Pointer DestField = Dest.atField(F.Offset);
+ if (std::optional FT = S.Ctx.classify(F.Decl->getType())) {
+ TYPE_SWITCH(*FT, {
+ DestField.deref() = Src.atField(F.Offset).deref();
+ if (Src.atField(F.Offset).isInitialized())
+ DestField.initialize();
+ if (Activate)
+ DestField.activate();
+ });
+ return true;
+ }
+ // Composite field.
+ return copyComposite(S, OpPC, Src.atField(F.Offset), DestField, Activate);
+ };
+
+ assert(SrcDesc->isRecord());
+ assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
+ const Record *R = DestDesc->ElemRecord;
+ for (const Record::Field &F : R->fields()) {
+ if (R->isUnion()) {
+ // For unions, only copy the active field.
+ const Pointer &SrcField = Src.atField(F.Offset);
+ if (SrcField.isActive()) {
+ if (!copyField(F, /*Activate=*/true))
+ return false;
+ }
+ } else {
+ if (!copyField(F, Activate))
+ return false;
+ }
+ }
+
+ for (const Record::Base &B : R->bases()) {
+ Pointer DestBase = Dest.atField(B.Offset);
+ if (!copyRecord(S, OpPC, Src.atField(B.Offset), DestBase, Activate))
+ return false;
+ }
+
+ Dest.initialize();
+ return true;
+}
+
+static bool copyComposite(InterpState &S, CodePtr OpPC, const Pointer &Src,
+ Pointer &Dest, bool Activate = false) {
assert(Src.isLive() && Dest.isLive());
[[maybe_unused]] const Descriptor *SrcDesc = Src.getFieldDesc();
@@ -1657,44 +1708,14 @@ bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
return true;
}
- if (DestDesc->isRecord()) {
- auto copyField = [&](const Record::Field &F, bool Activate) -> bool {
- Pointer DestField = Dest.atField(F.Offset);
- if (std::optional FT = S.Ctx.classify(F.Decl->getType())) {
- TYPE_SWITCH(*FT, {
- DestField.deref() = Src.atField(F.Offset).deref();
- DestField.initialize();
- if (Activate)
- DestField.activate();
- });
- return true;
- }
- return Invalid(S, OpPC);
- };
-
- assert(SrcDesc->isRecord());
- assert(SrcDesc->ElemRecord == DestDesc->ElemRecord);
- const Record *R = DestDesc->ElemRecord;
- for (const Record::Field &F : R->fields()) {
- if (R->isUnion()) {
- // For unions, only copy the active field.
- const Pointer &SrcField = Src.atField(F.Offset);
- if (SrcField.isActive()) {
- if (!copyField(F, /*Activate=*/true))
- return false;
- }
- } else {
- if (!copyField(F, /*Activate=*/false))
- return false;
- }
- }
- return true;
- }
-
- // FIXME: Composite types.
-
+ if (DestDesc->isRecord())
+ return copyRecord(S, OpPC, Src, Dest, Activate);
return Invalid(S, OpPC);
}
+bool DoMemcpy(InterpState &S, CodePtr OpPC, const Pointer &Src, Pointer &Dest) {
+ return copyComposite(S, OpPC, Src, Dest);
+}
+
} // namespace interp
} // namespace clang
diff --git a/clang/lib/Driver/ToolChains/AIX.cpp b/clang/lib/Driver/ToolChains/AIX.cpp
index b2885b7776d1321..c2de7328c25c5d9 100644
--- a/clang/lib/Driver/ToolChains/AIX.cpp
+++ b/clang/lib/Driver/ToolChains/AIX.cpp
@@ -560,12 +560,6 @@ void AIX::addClangTargetOptions(
if (!Args.getLastArgNoClaim(options::OPT_fsized_deallocation,
options::OPT_fno_sized_deallocation))
CC1Args.push_back("-fno-sized-deallocation");
-
- if (Args.hasFlag(options::OPT_ferr_pragma_mc_func_aix,
- options::OPT_fno_err_pragma_mc_func_aix, false))
- CC1Args.push_back("-ferr-pragma-mc-func-aix");
- else
- CC1Args.push_back("-fno-err-pragma-mc-func-aix");
}
void AIX::addProfileRTLibs(const llvm::opt::ArgList &Args,
diff --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index aef4ddb75881644..cc6f18b5b319f95 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -14,7 +14,6 @@
#include "clang/Basic/PragmaKinds.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/Preprocessor.h"
-#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Lex/Token.h"
#include "clang/Parse/LoopHint.h"
#include "clang/Parse/ParseDiagnostic.h"
@@ -412,19 +411,6 @@ struct PragmaRISCVHandler : public PragmaHandler {
Sema &Actions;
};
-struct PragmaMCFuncHandler : public PragmaHandler {
- PragmaMCFuncHandler(bool ReportError)
- : PragmaHandler("mc_func"), ReportError(ReportError) {}
- void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
- Token &Tok) override {
- if (ReportError)
- PP.Diag(Tok, diag::err_pragma_mc_func_not_supported);
- }
-
-private:
- bool ReportError = false;
-};
-
void markAsReinjectedForRelexing(llvm::MutableArrayRef Toks) {
for (auto &T : Toks)
T.setFlag(clang::Token::IsReinjected);
@@ -582,12 +568,6 @@ void Parser::initializePragmaHandlers() {
RISCVPragmaHandler = std::make_unique(Actions);
PP.AddPragmaHandler("clang", RISCVPragmaHandler.get());
}
-
- if (getTargetInfo().getTriple().isOSAIX()) {
- MCFuncPragmaHandler = std::make_unique(
- PP.getPreprocessorOpts().ErrorOnPragmaMcfuncOnAIX);
- PP.AddPragmaHandler(MCFuncPragmaHandler.get());
- }
}
void Parser::resetPragmaHandlers() {
@@ -722,11 +702,6 @@ void Parser::resetPragmaHandlers() {
PP.RemovePragmaHandler("clang", RISCVPragmaHandler.get());
RISCVPragmaHandler.reset();
}
-
- if (getTargetInfo().getTriple().isOSAIX()) {
- PP.RemovePragmaHandler(MCFuncPragmaHandler.get());
- MCFuncPragmaHandler.reset();
- }
}
/// Handle the annotation token produced for #pragma unused(...)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index e9705ec43d86cc6..ec951d5ac06dbc6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5805,12 +5805,19 @@ FunctionDecl *Sema::getMoreConstrainedFunction(FunctionDecl *FD1,
FunctionDecl *FD2) {
assert(!FD1->getDescribedTemplate() && !FD2->getDescribedTemplate() &&
"not for function templates");
+ assert(!FD1->isFunctionTemplateSpecialization() ||
+ isa(FD1));
+ assert(!FD2->isFunctionTemplateSpecialization() ||
+ isa(FD2));
+
FunctionDecl *F1 = FD1;
- if (FunctionDecl *MF = FD1->getInstantiatedFromMemberFunction())
- F1 = MF;
+ if (FunctionDecl *P = FD1->getTemplateInstantiationPattern(false))
+ F1 = P;
+
FunctionDecl *F2 = FD2;
- if (FunctionDecl *MF = FD2->getInstantiatedFromMemberFunction())
- F2 = MF;
+ if (FunctionDecl *P = FD2->getTemplateInstantiationPattern(false))
+ F2 = P;
+
llvm::SmallVector AC1, AC2;
F1->getAssociatedConstraints(AC1);
F2->getAssociatedConstraints(AC2);
diff --git a/clang/test/AST/Interp/unions.cpp b/clang/test/AST/Interp/unions.cpp
index 996d29e143fe2c8..35b4a520baa2695 100644
--- a/clang/test/AST/Interp/unions.cpp
+++ b/clang/test/AST/Interp/unions.cpp
@@ -361,7 +361,7 @@ namespace CopyCtor {
namespace UnionInBase {
struct Base {
- int y;
+ int y; // both-note {{subobject declared here}}
};
struct A : Base {
int x;
@@ -380,5 +380,29 @@ namespace UnionInBase {
}
static_assert(read_wrong_member_indirect() == 1); // both-error {{not an integral constant expression}} \
// both-note {{in call to}}
+ constexpr int read_uninitialized() {
+ B b = {.b = 1};
+ int *p = &b.a.y;
+ b.a.x = 1;
+ return *p; // both-note {{read of uninitialized object}}
+ }
+ static_assert(read_uninitialized() == 0); // both-error {{constant}} \
+ // both-note {{in call}}
+ constexpr int write_uninitialized() {
+ B b = {.b = 1};
+ int *p = &b.a.y;
+ b.a.x = 1;
+ *p = 1;
+ return *p;
+ }
+
+ constexpr B return_uninit() {
+ B b = {.b = 1};
+ b.a.x = 2;
+ return b;
+ }
+ constexpr B uninit = return_uninit(); // both-error {{constant expression}} \
+ // both-note {{subobject 'y' is not initialized}}
+ static_assert(return_uninit().a.x == 2);
}
#endif
diff --git a/clang/test/CodeGenCUDA/host-used-extern-determinism.cu b/clang/test/CodeGenCUDA/host-used-extern-determinism.cu
new file mode 100644
index 000000000000000..1e52887b894b17a
--- /dev/null
+++ b/clang/test/CodeGenCUDA/host-used-extern-determinism.cu
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -triple amdgcn-amd-amdhsa -fcuda-is-device -x hip %s \
+// RUN: -fgpu-rdc -std=c++11 -emit-llvm -o - -target-cpu gfx906 | FileCheck %s
+
+#include "Inputs/cuda.h"
+
+// CHECK-LABEL: @__clang_gpu_used_external = internal {{.*}}global
+// References to the kernels must be in order of appearance.
+// CHECK-SAME: [ptr @_Z6kernelILi3EEvPi, ptr @_Z6kernelILi1EEvPi, ptr @_Z6kernelILi2EEvPi, ptr @_Z6kernelILi0EEvPi]
+
+template
+__global__ void kernel(int* out) { *out = N; }
+
+void host(int n) {
+ void * k;
+ switch (n) {
+ case 3: k = (void*)&kernel<3>; break;
+ case 1: k = (void*)&kernel<1>; break;
+ case 2: k = (void*)&kernel<2>; break;
+ case 0: k = (void*)&kernel<0>; break;
+ }
+}
diff --git a/clang/test/Preprocessor/pragma_mc_func.c b/clang/test/Preprocessor/pragma_mc_func.c
deleted file mode 100644
index f0d3e49e5dddcaf..000000000000000
--- a/clang/test/Preprocessor/pragma_mc_func.c
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: not %clang --target=powerpc64-ibm-aix -ferr-pragma-mc-func-aix -fsyntax-only \
-// RUN: %s 2>&1 | FileCheck %s
-#pragma mc_func asm_barrier {"60000000"}
-
-// CHECK: error: #pragma mc_func is not supported
-
-// Cases where no errors occur.
-// RUN: %clang --target=powerpc64-ibm-aix -fno-err-pragma-mc-func-aix -fsyntax-only %s
-// RUN: %clang --target=powerpc64-ibm-aix -ferr-pragma-mc-func-aix -fsyntax-only \
-// RUN: -fno-err-pragma-mc-func-aix %s
-// RUN: %clang --target=powerpc64-ibm-aix -fsyntax-only %s
-// RUN: %clang --target=powerpc64-ibm-aix -Werror=unknown-pragmas \
-// RUN: -fno-err-pragma-mc-func-aix -fsyntax-only %s
-
-// Cases where we have errors or warnings.
-// RUN: not %clang --target=powerpc64le-unknown-linux-gnu \
-// RUN: -Werror=unknown-pragmas -fno-err-pragma-mc-func-aix -fsyntax-only %s 2>&1 | \
-// RUN: FileCheck --check-prefix=UNUSED %s
-// RUN: %clang --target=powerpc64le-unknown-linux-gnu \
-// RUN: -fno-err-pragma-mc-func-aix -fsyntax-only %s 2>&1 | \
-// RUN: FileCheck --check-prefix=UNUSED %s
-
-// UNUSED: clang: warning: argument unused during compilation: '-fno-err-pragma-mc-func-aix' [-Wunused-command-line-argument]
diff --git a/clang/test/SemaCXX/PR98671.cpp b/clang/test/SemaCXX/PR98671.cpp
new file mode 100644
index 000000000000000..f50518673588520
--- /dev/null
+++ b/clang/test/SemaCXX/PR98671.cpp
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only %s -verify
+
+struct S1 {
+ operator int();
+
+ template
+ operator T();
+};
+
+
+// Ensure that no assertion is raised when overload resolution fails while
+// choosing between an operator function template and an operator function.
+constexpr auto r = &S1::operator int;
+// expected-error@-1 {{initializer of type ''}}
+
+
+template
+struct S2 {
+ template
+ S2(U={}) requires (sizeof(T) > 0) {}
+ // expected-note@-1 {{candidate constructor}}
+
+ template
+ S2(U={}) requires (true) {}
+ // expected-note@-1 {{candidate constructor}}
+};
+
+S2 s; // expected-error {{call to constructor of 'S2' is ambiguous}}
diff --git a/compiler-rt/lib/nsan/CMakeLists.txt b/compiler-rt/lib/nsan/CMakeLists.txt
index fa9f02abdf0801c..2846f0292307b3a 100644
--- a/compiler-rt/lib/nsan/CMakeLists.txt
+++ b/compiler-rt/lib/nsan/CMakeLists.txt
@@ -4,9 +4,11 @@ include_directories(..)
set(NSAN_SOURCES
nsan.cpp
+ nsan_allocator.cpp
nsan_flags.cpp
nsan_interceptors.cpp
nsan_malloc_linux.cpp
+ nsan_new_delete.cpp
nsan_stats.cpp
nsan_suppressions.cpp
nsan_thread.cpp
diff --git a/compiler-rt/lib/nsan/nsan.cpp b/compiler-rt/lib/nsan/nsan.cpp
index 7d10681a1bc9178..bfa55c317cfe79e 100644
--- a/compiler-rt/lib/nsan/nsan.cpp
+++ b/compiler-rt/lib/nsan/nsan.cpp
@@ -807,6 +807,7 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
if (nsan_initialized)
return;
nsan_init_is_running = true;
+ SanitizerToolName = "NumericalStabilitySanitizer";
InitializeFlags();
InitializeSuppressions();
@@ -814,11 +815,12 @@ extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __nsan_init() {
DisableCoreDumperIfNecessary();
- if (!MmapFixedNoReserve(TypesAddr(), UnusedAddr() - TypesAddr()))
+ if (!MmapFixedNoReserve(TypesAddr(), AllocatorAddr() - TypesAddr()))
Die();
InitializeInterceptors();
NsanTSDInit(NsanTSDDtor);
+ NsanAllocatorInit();
NsanThread *main_thread = NsanThread::Create(nullptr, nullptr);
SetCurrentThread(main_thread);
diff --git a/compiler-rt/lib/nsan/nsan.h b/compiler-rt/lib/nsan/nsan.h
index 4e88ef4c00974d5..08dd02746be65a0 100644
--- a/compiler-rt/lib/nsan/nsan.h
+++ b/compiler-rt/lib/nsan/nsan.h
@@ -51,6 +51,14 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *
__nsan_default_options();
}
+// Unwind the stack for fatal error, as the parameter `stack` is
+// empty without origins.
+#define GET_FATAL_STACK_TRACE_IF_EMPTY(STACK) \
+ if (nsan_initialized && (STACK)->size == 0) { \
+ (STACK)->Unwind(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), nullptr, \
+ common_flags()->fast_unwind_on_fatal); \
+ }
+
namespace __nsan {
extern bool nsan_initialized;
diff --git a/compiler-rt/lib/nsan/nsan_allocator.cpp b/compiler-rt/lib/nsan/nsan_allocator.cpp
new file mode 100644
index 000000000000000..19004ad7dc8dbe7
--- /dev/null
+++ b/compiler-rt/lib/nsan/nsan_allocator.cpp
@@ -0,0 +1,340 @@
+//===- nsan_allocator.cpp -------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// NumericalStabilitySanitizer allocator.
+//
+//===----------------------------------------------------------------------===//
+
+#include "nsan_allocator.h"
+#include "interception/interception.h"
+#include "nsan.h"
+#include "nsan_flags.h"
+#include "nsan_platform.h"
+#include "nsan_thread.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_checks.h"
+#include "sanitizer_common/sanitizer_allocator_interface.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+#include "sanitizer_common/sanitizer_common.h"
+#include "sanitizer_common/sanitizer_errno.h"
+
+using namespace __nsan;
+
+DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n)
+DECLARE_REAL(void *, memset, void *dest, int c, uptr n)
+
+namespace {
+struct Metadata {
+ uptr requested_size;
+};
+
+struct NsanMapUnmapCallback {
+ void OnMap(uptr p, uptr size) const {}
+ void OnMapSecondary(uptr p, uptr size, uptr user_begin,
+ uptr user_size) const {}
+ void OnUnmap(uptr p, uptr size) const {}
+};
+
+const uptr kMaxAllowedMallocSize = 1ULL << 40;
+
+// Allocator64 parameters. Deliberately using a short name.
+struct AP64 {
+ static const uptr kSpaceBeg = Mapping::kHeapMemBeg;
+ static const uptr kSpaceSize = 0x40000000000; // 4T.
+ static const uptr kMetadataSize = sizeof(Metadata);
+ using SizeClassMap = DefaultSizeClassMap;
+ using MapUnmapCallback = NsanMapUnmapCallback;
+ static const uptr kFlags = 0;
+ using AddressSpaceView = LocalAddressSpaceView;
+};
+} // namespace
+
+using PrimaryAllocator = SizeClassAllocator64;
+using Allocator = CombinedAllocator;
+using AllocatorCache = Allocator::AllocatorCache;
+
+static Allocator allocator;
+static AllocatorCache fallback_allocator_cache;
+static StaticSpinMutex fallback_mutex;
+
+static uptr max_malloc_size;
+
+void __nsan::NsanAllocatorInit() {
+ SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
+ allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
+ if (common_flags()->max_allocation_size_mb)
+ max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,
+ kMaxAllowedMallocSize);
+ else
+ max_malloc_size = kMaxAllowedMallocSize;
+}
+
+static AllocatorCache *GetAllocatorCache(NsanThreadLocalMallocStorage *ms) {
+ CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
+ return reinterpret_cast(ms->allocator_cache);
+}
+
+void NsanThreadLocalMallocStorage::Init() {
+ allocator.InitCache(GetAllocatorCache(this));
+}
+
+void NsanThreadLocalMallocStorage::CommitBack() {
+ allocator.SwallowCache(GetAllocatorCache(this));
+ allocator.DestroyCache(GetAllocatorCache(this));
+}
+
+static void *NsanAllocate(uptr size, uptr alignment, bool zero) {
+ if (UNLIKELY(size > max_malloc_size)) {
+ if (AllocatorMayReturnNull()) {
+ Report("WARNING: NumericalStabilitySanitizer failed to allocate 0x%zx "
+ "bytes\n",
+ size);
+ return nullptr;
+ }
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportAllocationSizeTooBig(size, max_malloc_size, &stack);
+ }
+ if (UNLIKELY(IsRssLimitExceeded())) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportRssLimitExceeded(&stack);
+ }
+
+ void *allocated;
+ if (NsanThread *t = GetCurrentThread()) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocated = allocator.Allocate(cache, size, alignment);
+ } else {
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocated = allocator.Allocate(cache, size, alignment);
+ }
+ if (UNLIKELY(!allocated)) {
+ SetAllocatorOutOfMemory();
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportOutOfMemory(size, &stack);
+ }
+ auto *meta = reinterpret_cast(allocator.GetMetaData(allocated));
+ meta->requested_size = size;
+ if (zero && allocator.FromPrimary(allocated))
+ REAL(memset)(allocated, 0, size);
+ __nsan_set_value_unknown(allocated, size);
+ RunMallocHooks(allocated, size);
+ return allocated;
+}
+
+void __nsan::NsanDeallocate(void *p) {
+ DCHECK(p);
+ RunFreeHooks(p);
+ auto *meta = reinterpret_cast(allocator.GetMetaData(p));
+ uptr size = meta->requested_size;
+ meta->requested_size = 0;
+ if (flags().poison_in_free)
+ __nsan_set_value_unknown(p, size);
+ if (NsanThread *t = GetCurrentThread()) {
+ AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
+ allocator.Deallocate(cache, p);
+ } else {
+ // In a just created thread, glibc's _dl_deallocate_tls might reach here
+ // before nsan_current_thread is set.
+ SpinMutexLock l(&fallback_mutex);
+ AllocatorCache *cache = &fallback_allocator_cache;
+ allocator.Deallocate(cache, p);
+ }
+}
+
+static void *NsanReallocate(void *ptr, uptr new_size, uptr alignment) {
+ Metadata *meta = reinterpret_cast(allocator.GetMetaData(ptr));
+ uptr old_size = meta->requested_size;
+ uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(ptr);
+ if (new_size <= actually_allocated_size) {
+ // We are not reallocating here.
+ meta->requested_size = new_size;
+ if (new_size > old_size)
+ __nsan_set_value_unknown((u8 *)ptr + old_size, new_size - old_size);
+ return ptr;
+ }
+ void *new_p = NsanAllocate(new_size, alignment, false);
+ if (new_p) {
+ uptr memcpy_size = Min(new_size, old_size);
+ REAL(memcpy)(new_p, ptr, memcpy_size);
+ __nsan_copy_values(new_p, ptr, memcpy_size);
+ NsanDeallocate(ptr);
+ }
+ return new_p;
+}
+
+static void *NsanCalloc(uptr nmemb, uptr size) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportCallocOverflow(nmemb, size, &stack);
+ }
+ return NsanAllocate(nmemb * size, sizeof(u64), true);
+}
+
+static const void *AllocationBegin(const void *p) {
+ if (!p)
+ return nullptr;
+ void *beg = allocator.GetBlockBegin(p);
+ if (!beg)
+ return nullptr;
+ auto *b = reinterpret_cast(allocator.GetMetaData(beg));
+ if (!b)
+ return nullptr;
+ if (b->requested_size == 0)
+ return nullptr;
+
+ return beg;
+}
+
+static uptr AllocationSizeFast(const void *p) {
+ return reinterpret_cast(allocator.GetMetaData(p))->requested_size;
+}
+
+static uptr AllocationSize(const void *p) {
+ if (!p)
+ return 0;
+ if (allocator.GetBlockBegin(p) != p)
+ return 0;
+ return AllocationSizeFast(p);
+}
+
+void *__nsan::nsan_malloc(uptr size) {
+ return SetErrnoOnNull(NsanAllocate(size, sizeof(u64), false));
+}
+
+void *__nsan::nsan_calloc(uptr nmemb, uptr size) {
+ return SetErrnoOnNull(NsanCalloc(nmemb, size));
+}
+
+void *__nsan::nsan_realloc(void *ptr, uptr size) {
+ if (!ptr)
+ return SetErrnoOnNull(NsanAllocate(size, sizeof(u64), false));
+ if (size == 0) {
+ NsanDeallocate(ptr);
+ return nullptr;
+ }
+ return SetErrnoOnNull(NsanReallocate(ptr, size, sizeof(u64)));
+}
+
+void *__nsan::nsan_reallocarray(void *ptr, uptr nmemb, uptr size) {
+ if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportReallocArrayOverflow(nmemb, size, &stack);
+ }
+ return nsan_realloc(ptr, nmemb * size);
+}
+
+void *__nsan::nsan_valloc(uptr size) {
+ return SetErrnoOnNull(NsanAllocate(size, GetPageSizeCached(), false));
+}
+
+void *__nsan::nsan_pvalloc(uptr size) {
+ uptr PageSize = GetPageSizeCached();
+ if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
+ errno = errno_ENOMEM;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportPvallocOverflow(size, &stack);
+ }
+ // pvalloc(0) should allocate one page.
+ size = size ? RoundUpTo(size, PageSize) : PageSize;
+ return SetErrnoOnNull(NsanAllocate(size, PageSize, false));
+}
+
+void *__nsan::nsan_aligned_alloc(uptr alignment, uptr size) {
+ if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
+ }
+ return SetErrnoOnNull(NsanAllocate(size, alignment, false));
+}
+
+void *__nsan::nsan_memalign(uptr alignment, uptr size) {
+ if (UNLIKELY(!IsPowerOfTwo(alignment))) {
+ errno = errno_EINVAL;
+ if (AllocatorMayReturnNull())
+ return nullptr;
+ BufferedStackTrace stack;
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);
+ ReportInvalidAllocationAlignment(alignment, &stack);
+ }
+ return SetErrnoOnNull(NsanAllocate(size, alignment, false));
+}
+
+int __nsan::nsan_posix_memalign(void **memptr, uptr alignment, uptr size) {
+ if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
+ if (AllocatorMayReturnNull())
+ return errno_EINVAL;
+ BufferedStackTrace stack;
+ ReportInvalidPosixMemalignAlignment(alignment, &stack);
+ }
+ void *ptr = NsanAllocate(size, alignment, false);
+ if (UNLIKELY(!ptr))
+ // OOM error is already taken care of by NsanAllocate.
+ return errno_ENOMEM;
+ DCHECK(IsAligned((uptr)ptr, alignment));
+ *memptr = ptr;
+ return 0;
+}
+
+extern "C" {
+uptr __sanitizer_get_current_allocated_bytes() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatAllocated];
+}
+
+uptr __sanitizer_get_heap_size() {
+ uptr stats[AllocatorStatCount];
+ allocator.GetStats(stats);
+ return stats[AllocatorStatMapped];
+}
+
+uptr __sanitizer_get_free_bytes() { return 1; }
+
+uptr __sanitizer_get_unmapped_bytes() { return 1; }
+
+uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
+
+int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
+
+const void *__sanitizer_get_allocated_begin(const void *p) {
+ return AllocationBegin(p);
+}
+
+uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
+
+uptr __sanitizer_get_allocated_size_fast(const void *p) {
+ DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));
+ uptr ret = AllocationSizeFast(p);
+ DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));
+ return ret;
+}
+
+void __sanitizer_purge_allocator() { allocator.ForceReleaseToOS(); }
+}
diff --git a/compiler-rt/lib/nsan/nsan_allocator.h b/compiler-rt/lib/nsan/nsan_allocator.h
new file mode 100644
index 000000000000000..d41560493c1a74c
--- /dev/null
+++ b/compiler-rt/lib/nsan/nsan_allocator.h
@@ -0,0 +1,41 @@
+//===-- nsan_allocator.h ----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef NSAN_ALLOCATOR_H
+#define NSAN_ALLOCATOR_H
+
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __nsan {
+
+struct NsanThreadLocalMallocStorage {
+ // Allocator cache contains atomic_uint64_t which must be 8-byte aligned.
+ alignas(8) uptr allocator_cache[96 * (512 * 8 + 16)]; // Opaque.
+ void Init();
+ void CommitBack();
+
+private:
+ // These objects are allocated via mmap() and are zero-initialized.
+ NsanThreadLocalMallocStorage() {}
+};
+
+void NsanAllocatorInit();
+void NsanDeallocate(void *ptr);
+
+void *nsan_malloc(uptr size);
+void *nsan_calloc(uptr nmemb, uptr size);
+void *nsan_realloc(void *ptr, uptr size);
+void *nsan_reallocarray(void *ptr, uptr nmemb, uptr size);
+void *nsan_valloc(uptr size);
+void *nsan_pvalloc(uptr size);
+void *nsan_aligned_alloc(uptr alignment, uptr size);
+void *nsan_memalign(uptr alignment, uptr size);
+int nsan_posix_memalign(void **memptr, uptr alignment, uptr size);
+
+} // namespace __nsan
+#endif // NSAN_ALLOCATOR_H
diff --git a/compiler-rt/lib/nsan/nsan_flags.inc b/compiler-rt/lib/nsan/nsan_flags.inc
index 63c15475f6754bb..658cd5b3b01bf4d 100644
--- a/compiler-rt/lib/nsan/nsan_flags.inc
+++ b/compiler-rt/lib/nsan/nsan_flags.inc
@@ -46,4 +46,5 @@ NSAN_FLAG(bool, enable_loadtracking_stats, false,
"If true, compute load tracking stats, i.e. for each load from "
"memory, the number of times nsan resumed from the original value "
"due to invalid or unknown types.")
+NSAN_FLAG(bool, poison_in_free, true, "")
NSAN_FLAG(bool, print_stats_on_exit, false, "If true, print stats on exit.")
diff --git a/compiler-rt/lib/nsan/nsan_malloc_linux.cpp b/compiler-rt/lib/nsan/nsan_malloc_linux.cpp
index 02f52e7be07facb..c97591e4ac1593e 100644
--- a/compiler-rt/lib/nsan/nsan_malloc_linux.cpp
+++ b/compiler-rt/lib/nsan/nsan_malloc_linux.cpp
@@ -12,14 +12,16 @@
#include "interception/interception.h"
#include "nsan.h"
+#include "nsan_allocator.h"
#include "sanitizer_common/sanitizer_allocator_dlsym.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_platform.h"
#include "sanitizer_common/sanitizer_platform_interceptors.h"
+#include "sanitizer_common/sanitizer_stacktrace.h"
#if !SANITIZER_APPLE && !SANITIZER_WINDOWS
using namespace __sanitizer;
-using __nsan::nsan_initialized;
+using namespace __nsan;
namespace {
struct DlsymAlloc : public DlSymAllocator {
@@ -28,78 +30,53 @@ struct DlsymAlloc : public DlSymAllocator {
} // namespace
INTERCEPTOR(void *, aligned_alloc, uptr align, uptr size) {
- void *res = REAL(aligned_alloc)(align, size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), size);
- return res;
+ return nsan_aligned_alloc(align, size);
}
INTERCEPTOR(void *, calloc, uptr nmemb, uptr size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Callocate(nmemb, size);
-
- void *res = REAL(calloc)(nmemb, size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), nmemb * size);
- return res;
+ return nsan_calloc(nmemb, size);
}
INTERCEPTOR(void, free, void *ptr) {
+ if (UNLIKELY(!ptr))
+ return;
if (DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Free(ptr);
- REAL(free)(ptr);
+ NsanDeallocate(ptr);
}
INTERCEPTOR(void *, malloc, uptr size) {
if (DlsymAlloc::Use())
return DlsymAlloc::Allocate(size);
- void *res = REAL(malloc)(size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), size);
- return res;
+ return nsan_malloc(size);
}
INTERCEPTOR(void *, realloc, void *ptr, uptr size) {
if (DlsymAlloc::Use() || DlsymAlloc::PointerIsMine(ptr))
return DlsymAlloc::Realloc(ptr, size);
- void *res = REAL(realloc)(ptr, size);
- // TODO: We might want to copy the types from the original allocation
- // (although that would require that we know its size).
- if (res)
- __nsan_set_value_unknown(static_cast(res), size);
- return res;
+ return nsan_realloc(ptr, size);
}
#if SANITIZER_INTERCEPT_REALLOCARRAY
INTERCEPTOR(void *, reallocarray, void *ptr, uptr nmemb, uptr size) {
- void *res = REAL(reallocarray)(ptr, nmemb, size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), nmemb * size);
- return res;
+ return nsan_reallocarray(ptr, nmemb, size);
}
#endif // SANITIZER_INTERCEPT_REALLOCARRAY
INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr size) {
- int res = REAL(posix_memalign)(memptr, align, size);
- if (res == 0 && *memptr)
- __nsan_set_value_unknown(static_cast(*memptr), size);
- return res;
+ return nsan_posix_memalign(memptr, align, size);
}
// Deprecated allocation functions (memalign, etc).
#if SANITIZER_INTERCEPT_MEMALIGN
INTERCEPTOR(void *, memalign, uptr align, uptr size) {
- void *const res = REAL(memalign)(align, size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), size);
- return res;
+ return nsan_memalign(align, size);
}
INTERCEPTOR(void *, __libc_memalign, uptr align, uptr size) {
- void *const res = REAL(__libc_memalign)(align, size);
- if (res)
- __nsan_set_value_unknown(static_cast(res), size);
- return res;
+ return nsan_memalign(align, size);
}
#endif
diff --git a/compiler-rt/lib/nsan/nsan_new_delete.cpp b/compiler-rt/lib/nsan/nsan_new_delete.cpp
new file mode 100644
index 000000000000000..f203a583f2c4480
--- /dev/null
+++ b/compiler-rt/lib/nsan/nsan_new_delete.cpp
@@ -0,0 +1,126 @@
+//===-- nsan_new_delete.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// Interceptors for operators new and delete.
+//===----------------------------------------------------------------------===//
+
+#include "interception/interception.h"
+#include "nsan.h"
+#include "nsan_allocator.h"
+#include "sanitizer_common/sanitizer_allocator.h"
+#include "sanitizer_common/sanitizer_allocator_report.h"
+
+#include
+
+using namespace __nsan;
+
+// Fake std::nothrow_t and std::align_val_t to avoid including .
+namespace std {
+struct nothrow_t {};
+enum class align_val_t : size_t {};
+} // namespace std
+
+#define OPERATOR_NEW_BODY(nothrow) \
+ void *res = nsan_malloc(size); \
+ if (!nothrow && UNLIKELY(!res)) { \
+ BufferedStackTrace stack; \
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack); \
+ ReportOutOfMemory(size, &stack); \
+ } \
+ return res
+#define OPERATOR_NEW_BODY_ALIGN(nothrow) \
+ void *res = nsan_memalign((uptr)align, size); \
+ if (!nothrow && UNLIKELY(!res)) { \
+ BufferedStackTrace stack; \
+ GET_FATAL_STACK_TRACE_IF_EMPTY(&stack); \
+ ReportOutOfMemory(size, &stack); \
+ } \
+ return res;
+
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size) { OPERATOR_NEW_BODY(/*nothrow=*/false); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size) { OPERATOR_NEW_BODY(/*nothrow=*/false); }
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::nothrow_t const &) {
+ OPERATOR_NEW_BODY(/*nothrow=*/true);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::nothrow_t const &) {
+ OPERATOR_NEW_BODY(/*nothrow=*/true);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align) {
+ OPERATOR_NEW_BODY_ALIGN(/*nothrow=*/false);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align) {
+ OPERATOR_NEW_BODY_ALIGN(/*nothrow=*/false);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new(size_t size, std::align_val_t align,
+ std::nothrow_t const &) {
+ OPERATOR_NEW_BODY_ALIGN(/*nothrow=*/true);
+}
+INTERCEPTOR_ATTRIBUTE
+void *operator new[](size_t size, std::align_val_t align,
+ std::nothrow_t const &) {
+ OPERATOR_NEW_BODY_ALIGN(/*nothrow=*/true);
+}
+
+#define OPERATOR_DELETE_BODY \
+ if (ptr) \
+ NsanDeallocate(ptr)
+
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::nothrow_t const &) {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::nothrow_t const &) {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size) NOEXCEPT { OPERATOR_DELETE_BODY; }
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size) NOEXCEPT {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, std::align_val_t align,
+ std::nothrow_t const &) {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, std::align_val_t align,
+ std::nothrow_t const &) {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete(void *ptr, size_t size, std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY;
+}
+INTERCEPTOR_ATTRIBUTE
+void operator delete[](void *ptr, size_t size,
+ std::align_val_t align) NOEXCEPT {
+ OPERATOR_DELETE_BODY;
+}
diff --git a/compiler-rt/lib/nsan/nsan_platform.h b/compiler-rt/lib/nsan/nsan_platform.h
index c9d4cacd8c8898b..cc9d63ecb4a9baf 100644
--- a/compiler-rt/lib/nsan/nsan_platform.h
+++ b/compiler-rt/lib/nsan/nsan_platform.h
@@ -40,7 +40,9 @@ namespace __nsan {
// | |
// | unused |
// | |
-// +--------------------+ 0x400000000000 (kUnusedAddr)
+// +--------------------+ 0x440000008000
+// | allocator |
+// +--------------------+ 0x400000000000 (kHeapMemBeg)
// | shadow memory |
// +--------------------+ 0x200000000000 (kShadowAddr)
// | shadow types |
@@ -79,7 +81,7 @@ enum {
struct Mapping {
// FIXME: kAppAddr == 0x700000000000 ?
static const uptr kAppAddr = 0x700000008000;
- static const uptr kUnusedAddr = 0x400000000000;
+ static const uptr kHeapMemBeg = 0x400000000000;
static const uptr kShadowAddr = 0x200000000000;
static const uptr kTypesAddr = 0x100000000000;
static const uptr kShadowMask = ~0x700000000000;
@@ -90,7 +92,7 @@ struct Mapping {
enum MappingType {
MAPPING_APP_ADDR,
- MAPPING_UNUSED_ADDR,
+ MAPPING_ALLOCATOR_ADDR,
MAPPING_SHADOW_ADDR,
MAPPING_TYPES_ADDR,
MAPPING_SHADOW_MASK
@@ -100,8 +102,8 @@ template uptr MappingImpl() {
switch (Type) {
case MAPPING_APP_ADDR:
return Mapping::kAppAddr;
- case MAPPING_UNUSED_ADDR:
- return Mapping::kUnusedAddr;
+ case MAPPING_ALLOCATOR_ADDR:
+ return Mapping::kHeapMemBeg;
case MAPPING_SHADOW_ADDR:
return Mapping::kShadowAddr;
case MAPPING_TYPES_ADDR:
@@ -119,7 +121,7 @@ ALWAYS_INLINE
uptr AppAddr() { return MappingArchImpl(); }
ALWAYS_INLINE
-uptr UnusedAddr() { return MappingArchImpl(); }
+uptr AllocatorAddr() { return MappingArchImpl(); }
ALWAYS_INLINE
uptr ShadowAddr() { return MappingArchImpl(); }
diff --git a/compiler-rt/lib/nsan/nsan_thread.cpp b/compiler-rt/lib/nsan/nsan_thread.cpp
index 273c46831cf3810..85706aea80ebd1a 100644
--- a/compiler-rt/lib/nsan/nsan_thread.cpp
+++ b/compiler-rt/lib/nsan/nsan_thread.cpp
@@ -55,6 +55,7 @@ void NsanThread::ClearShadowForThreadStackAndTLS() {
void NsanThread::Init() {
SetThreadStackAndTls();
ClearShadowForThreadStackAndTLS();
+ malloc_storage().Init();
}
void NsanThread::TSDDtor(void *tsd) {
@@ -63,6 +64,7 @@ void NsanThread::TSDDtor(void *tsd) {
}
void NsanThread::Destroy() {
+ malloc_storage().CommitBack();
// We also clear the shadow on thread destruction because
// some code may still be executing in later TSD destructors
// and we don't want it to have any poisoned stack.
diff --git a/compiler-rt/lib/nsan/nsan_thread.h b/compiler-rt/lib/nsan/nsan_thread.h
index 18f24fd6f1d78a9..143e61f37db96bb 100644
--- a/compiler-rt/lib/nsan/nsan_thread.h
+++ b/compiler-rt/lib/nsan/nsan_thread.h
@@ -9,6 +9,7 @@
#ifndef NSAN_THREAD_H
#define NSAN_THREAD_H
+#include "nsan_allocator.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_posix.h"
@@ -34,6 +35,8 @@ class NsanThread {
void StartSwitchFiber(uptr bottom, uptr size);
void FinishSwitchFiber(uptr *bottom_old, uptr *size_old);
+ NsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
+
int destructor_iterations_;
__sanitizer_sigset_t starting_sigset_;
@@ -56,6 +59,8 @@ class NsanThread {
uptr tls_begin_;
uptr tls_end_;
+
+ NsanThreadLocalMallocStorage malloc_storage_;
};
NsanThread *GetCurrentThread();
diff --git a/compiler-rt/lib/scudo/standalone/list.h b/compiler-rt/lib/scudo/standalone/list.h
index 0137667d1dcf3ef..6b952a610e30557 100644
--- a/compiler-rt/lib/scudo/standalone/list.h
+++ b/compiler-rt/lib/scudo/standalone/list.h
@@ -11,17 +11,113 @@
#include "internal_defs.h"
+// TODO: Move the helpers to a header.
+namespace {
+template struct isPointer {
+ static constexpr bool value = false;
+};
+
+template struct isPointer {
+ static constexpr bool value = true;
+};
+} // namespace
+
namespace scudo {
// Intrusive POD singly and doubly linked list.
// An object with all zero fields should represent a valid empty list. clear()
// should be called on all non-zero-initialized objects before using.
+//
+// The intrusive list requires the member `Next` (and `Prev` if doubly linked
+// list)` defined in the node type. The type of `Next`/`Prev` can be a pointer
+// or an index to an array. For example, if the storage of the nodes is an
+// array, instead of using a pointer type, linking with an index type can save
+// some space.
+//
+// There are two things to be noticed while using an index type,
+// 1. Call init() to set up the base address of the array.
+// 2. Define `EndOfListVal` as the nil of the list.
+
+template ::value>
+class LinkOp {
+public:
+ LinkOp() = default;
+ LinkOp(UNUSED T *BaseT, UNUSED uptr BaseSize) {}
+ void init(UNUSED T *LinkBase, UNUSED uptr Size) {}
+ T *getBase() const { return nullptr; }
+ uptr getSize() const { return 0; }
+
+ T *getNext(T *X) const { return X->Next; }
+ void setNext(T *X, T *Next) const { X->Next = Next; }
+
+ T *getPrev(T *X) const { return X->Prev; }
+ void setPrev(T *X, T *Prev) const { X->Prev = Prev; }
+
+ T *getEndOfListVal() const { return nullptr; }
+};
+
+template class LinkOp {
+public:
+ using LinkTy = decltype(T::Next);
+
+ LinkOp() = default;
+ LinkOp(T *BaseT, uptr BaseSize) : Base(BaseT), Size(BaseSize) {}
+ void init(T *LinkBase, uptr BaseSize) {
+ Base = LinkBase;
+ // TODO: Check if the `BaseSize` can fit in `Size`.
+ Size = static_cast(BaseSize);
+ }
+ T *getBase() const { return Base; }
+ LinkTy getSize() const { return Size; }
+
+ T *getNext(T *X) const {
+ DCHECK_NE(getBase(), nullptr);
+ if (X->Next == getEndOfListVal())
+ return nullptr;
+ DCHECK_LT(X->Next, Size);
+ return &Base[X->Next];
+ }
+ // Set `X->Next` to `Next`.
+ void setNext(T *X, T *Next) const {
+ // TODO: Check if the offset fits in the size of `LinkTy`.
+ if (Next == nullptr)
+ X->Next = getEndOfListVal();
+ else
+ X->Next = static_cast(Next - Base);
+ }
-template class IteratorBase {
+ T *getPrev(T *X) const {
+ DCHECK_NE(getBase(), nullptr);
+ if (X->Prev == getEndOfListVal())
+ return nullptr;
+ DCHECK_LT(X->Prev, Size);
+ return &Base[X->Prev];
+ }
+ // Set `X->Prev` to `Prev`.
+ void setPrev(T *X, T *Prev) const {
+ DCHECK_LT(reinterpret_cast(Prev),
+ reinterpret_cast(Base + Size));
+ if (Prev == nullptr)
+ X->Prev = getEndOfListVal();
+ else
+ X->Prev = static_cast(Prev - Base);
+ }
+
+ // TODO: `LinkTy` should be the same as decltype(T::EndOfListVal).
+ LinkTy getEndOfListVal() const { return T::EndOfListVal; }
+
+protected:
+ T *Base = nullptr;
+ LinkTy Size = 0;
+};
+
+template class IteratorBase : public LinkOp {
public:
- explicit IteratorBase(T *CurrentT) : Current(CurrentT) {}
+ IteratorBase(const LinkOp &Link, T *CurrentT)
+ : LinkOp(Link), Current(CurrentT) {}
+
IteratorBase &operator++() {
- Current = Current->Next;
+ Current = this->getNext(Current);
return *this;
}
bool operator!=(IteratorBase Other) const { return Current != Other.Current; }
@@ -31,7 +127,10 @@ template class IteratorBase {
T *Current;
};
-template struct IntrusiveList {
+template struct IntrusiveList : public LinkOp {
+ IntrusiveList() = default;
+ void init(T *Base, uptr BaseSize) { LinkOp::init(Base, BaseSize); }
+
bool empty() const { return Size == 0; }
uptr size() const { return Size; }
@@ -48,11 +147,21 @@ template struct IntrusiveList {
typedef IteratorBase Iterator;
typedef IteratorBase ConstIterator;
- Iterator begin() { return Iterator(First); }
- Iterator end() { return Iterator(nullptr); }
+ Iterator begin() {
+ return Iterator(LinkOp(this->getBase(), this->getSize()), First);
+ }
+ Iterator end() {
+ return Iterator(LinkOp(this->getBase(), this->getSize()), nullptr);
+ }
- ConstIterator begin() const { return ConstIterator(First); }
- ConstIterator end() const { return ConstIterator(nullptr); }
+ ConstIterator begin() const {
+ return ConstIterator(LinkOp(this->getBase(), this->getSize()),
+ First);
+ }
+ ConstIterator end() const {
+ return ConstIterator(LinkOp(this->getBase(), this->getSize()),
+ nullptr);
+ }
void checkConsistency() const;
@@ -68,13 +177,13 @@ template void IntrusiveList::checkConsistency() const {
CHECK_EQ(Last, nullptr);
} else {
uptr Count = 0;
- for (T *I = First;; I = I->Next) {
+ for (T *I = First;; I = this->getNext(I)) {
Count++;
if (I == Last)
break;
}
CHECK_EQ(this->size(), Count);
- CHECK_EQ(Last->Next, nullptr);
+ CHECK_EQ(this->getNext(Last), nullptr);
}
}
@@ -83,13 +192,16 @@ template struct SinglyLinkedList : public IntrusiveList {
using IntrusiveList::Last;
using IntrusiveList::Size;
using IntrusiveList::empty;
+ using IntrusiveList::setNext;
+ using IntrusiveList::getNext;
+ using IntrusiveList::getEndOfListVal;
void push_back(T *X) {
- X->Next = nullptr;
+ setNext(X, nullptr);
if (empty())
First = X;
else
- Last->Next = X;
+ setNext(Last, X);
Last = X;
Size++;
}
@@ -97,14 +209,14 @@ template struct SinglyLinkedList : public IntrusiveList {
void push_front(T *X) {
if (empty())
Last = X;
- X->Next = First;
+ setNext(X, First);
First = X;
Size++;
}
void pop_front() {
DCHECK(!empty());
- First = First->Next;
+ First = getNext(First);
if (!First)
Last = nullptr;
Size--;
@@ -115,8 +227,8 @@ template struct SinglyLinkedList : public IntrusiveList {
DCHECK(!empty());
DCHECK_NE(Prev, nullptr);
DCHECK_NE(X, nullptr);
- X->Next = Prev->Next;
- Prev->Next = X;
+ setNext(X, getNext(Prev));
+ setNext(Prev, X);
if (Last == Prev)
Last = X;
++Size;
@@ -126,8 +238,8 @@ template struct SinglyLinkedList : public IntrusiveList {
DCHECK(!empty());
DCHECK_NE(Prev, nullptr);
DCHECK_NE(X, nullptr);
- DCHECK_EQ(Prev->Next, X);
- Prev->Next = X->Next;
+ DCHECK_EQ(getNext(Prev), X);
+ setNext(Prev, getNext(X));
if (Last == X)
Last = Prev;
Size--;
@@ -140,7 +252,7 @@ template struct SinglyLinkedList : public IntrusiveList {
if (empty()) {
*this = *L;
} else {
- Last->Next = L->First;
+ setNext(Last, L->First);
Last = L->Last;
Size += L->size();
}
@@ -153,16 +265,21 @@ template struct DoublyLinkedList : IntrusiveList {
using IntrusiveList::Last;
using IntrusiveList::Size;
using IntrusiveList::empty;
+ using IntrusiveList::setNext;
+ using IntrusiveList::getNext;
+ using IntrusiveList::setPrev;
+ using IntrusiveList::getPrev;
+ using IntrusiveList::getEndOfListVal;
void push_front(T *X) {
- X->Prev = nullptr;
+ setPrev(X, nullptr);
if (empty()) {
Last = X;
} else {
- DCHECK_EQ(First->Prev, nullptr);
- First->Prev = X;
+ DCHECK_EQ(getPrev(First), nullptr);
+ setPrev(First, X);
}
- X->Next = First;
+ setNext(X, First);
First = X;
Size++;
}
@@ -171,37 +288,37 @@ template struct DoublyLinkedList : IntrusiveList {
void insert(T *X, T *Y) {
if (Y == First)
return push_front(X);
- T *Prev = Y->Prev;
+ T *Prev = getPrev(Y);
// This is a hard CHECK to ensure consistency in the event of an intentional
// corruption of Y->Prev, to prevent a potential write-{4,8}.
- CHECK_EQ(Prev->Next, Y);
- Prev->Next = X;
- X->Prev = Prev;
- X->Next = Y;
- Y->Prev = X;
+ CHECK_EQ(getNext(Prev), Y);
+ setNext(Prev, X);
+ setPrev(X, Prev);
+ setNext(X, Y);
+ setPrev(Y, X);
Size++;
}
void push_back(T *X) {
- X->Next = nullptr;
+ setNext(X, nullptr);
if (empty()) {
First = X;
} else {
- DCHECK_EQ(Last->Next, nullptr);
- Last->Next = X;
+ DCHECK_EQ(getNext(Last), nullptr);
+ setNext(Last, X);
}
- X->Prev = Last;
+ setPrev(X, Last);
Last = X;
Size++;
}
void pop_front() {
DCHECK(!empty());
- First = First->Next;
+ First = getNext(First);
if (!First)
Last = nullptr;
else
- First->Prev = nullptr;
+ setPrev(First, nullptr);
Size--;
}
@@ -209,15 +326,15 @@ template struct DoublyLinkedList : IntrusiveList {
// catch potential corruption attempts, that could yield a mirrored
// write-{4,8} primitive. nullptr checks are deemed less vital.
void remove(T *X) {
- T *Prev = X->Prev;
- T *Next = X->Next;
+ T *Prev = getPrev(X);
+ T *Next = getNext(X);
if (Prev) {
- CHECK_EQ(Prev->Next, X);
- Prev->Next = Next;
+ CHECK_EQ(getNext(Prev), X);
+ setNext(Prev, Next);
}
if (Next) {
- CHECK_EQ(Next->Prev, X);
- Next->Prev = Prev;
+ CHECK_EQ(getPrev(Next), X);
+ setPrev(Next, Prev);
}
if (First == X) {
DCHECK_EQ(Prev, nullptr);
diff --git a/compiler-rt/lib/scudo/standalone/tests/list_test.cpp b/compiler-rt/lib/scudo/standalone/tests/list_test.cpp
index 140ca027ae92836..688cbbef6a032f2 100644
--- a/compiler-rt/lib/scudo/standalone/tests/list_test.cpp
+++ b/compiler-rt/lib/scudo/standalone/tests/list_test.cpp
@@ -10,25 +10,22 @@
#include "list.h"
-struct ListItem {
- ListItem *Next;
- ListItem *Prev;
-};
+#include
-static ListItem Items[6];
-static ListItem *X = &Items[0];
-static ListItem *Y = &Items[1];
-static ListItem *Z = &Items[2];
-static ListItem *A = &Items[3];
-static ListItem *B = &Items[4];
-static ListItem *C = &Items[5];
+struct ListItemLinkedWithPtr {
+ ListItemLinkedWithPtr *Next;
+ ListItemLinkedWithPtr *Prev;
+};
-typedef scudo::SinglyLinkedList SLList;
-typedef scudo::DoublyLinkedList DLList;
+struct ListItemLinkedWithIndex {
+ scudo::uptr Next;
+ scudo::uptr Prev;
+ static constexpr scudo::uptr EndOfListVal = 1ULL << 30;
+};
-template
-static void setList(ListT *L, ListItem *I1 = nullptr, ListItem *I2 = nullptr,
- ListItem *I3 = nullptr) {
+template
+static void setList(ListT *L, ListItemTy *I1 = nullptr,
+ ListItemTy *I2 = nullptr, ListItemTy *I3 = nullptr) {
L->clear();
if (I1)
L->push_back(I1);
@@ -38,10 +35,10 @@ static void setList(ListT *L, ListItem *I1 = nullptr, ListItem *I2 = nullptr,
L->push_back(I3);
}
-template
-static void checkList(ListT *L, ListItem *I1, ListItem *I2 = nullptr,
- ListItem *I3 = nullptr, ListItem *I4 = nullptr,
- ListItem *I5 = nullptr, ListItem *I6 = nullptr) {
+template
+static void checkList(ListT *L, ListItemTy *I1, ListItemTy *I2 = nullptr,
+ ListItemTy *I3 = nullptr, ListItemTy *I4 = nullptr,
+ ListItemTy *I5 = nullptr, ListItemTy *I6 = nullptr) {
if (I1) {
EXPECT_EQ(L->front(), I1);
L->pop_front();
@@ -69,9 +66,16 @@ static void checkList(ListT *L, ListItem *I1, ListItem *I2 = nullptr,
EXPECT_TRUE(L->empty());
}
-template static void testListCommon(void) {
- ListT L;
+template class ListTy, typename ListItemTy>
+static void testListCommon(void) {
+ ListItemTy Items[3];
+ ListItemTy *X = &Items[0];
+ ListItemTy *Y = &Items[1];
+ ListItemTy *Z = &Items[2];
+
+ ListTy L;
L.clear();
+ L.init(Items, sizeof(Items));
EXPECT_EQ(L.size(), 0U);
L.push_back(X);
@@ -123,16 +127,40 @@ template static void testListCommon(void) {
L.pop_front();
EXPECT_TRUE(L.empty());
L.checkConsistency();
+
+ L.push_back(X);
+ L.push_back(Y);
+ L.push_back(Z);
+
+ // Verify the iterator
+ std::array visitOrder{X, Y, Z};
+ auto Iter = visitOrder.begin();
+ for (const auto &Item : L) {
+ EXPECT_EQ(&Item, *Iter);
+ ++Iter;
+ }
}
TEST(ScudoListTest, LinkedListCommon) {
- testListCommon();
- testListCommon();
+ testListCommon();
+ testListCommon();
+ testListCommon();
+ testListCommon();
}
-TEST(ScudoListTest, SinglyLinkedList) {
- SLList L;
+template class ListTy, typename ListItemTy>
+static void testSinglyLinkedList() {
+ ListItemTy Items[6];
+ ListItemTy *X = &Items[0];
+ ListItemTy *Y = &Items[1];
+ ListItemTy *Z = &Items[2];
+ ListItemTy *A = &Items[3];
+ ListItemTy *B = &Items[4];
+ ListItemTy *C = &Items[5];
+
+ ListTy L;
L.clear();
+ L.init(Items, sizeof(Items));
L.push_back(X);
L.push_back(Y);
@@ -150,9 +178,11 @@ TEST(ScudoListTest, SinglyLinkedList) {
L.pop_front();
EXPECT_TRUE(L.empty());
- SLList L1, L2;
+ ListTy L1, L2;
L1.clear();
L2.clear();
+ L1.init(Items, sizeof(Items));
+ L2.init(Items, sizeof(Items));
L1.append_back(&L2);
EXPECT_TRUE(L1.empty());
@@ -180,9 +210,21 @@ TEST(ScudoListTest, SinglyLinkedList) {
EXPECT_EQ(L1.size(), 1U);
}
-TEST(ScudoListTest, DoublyLinkedList) {
- DLList L;
+TEST(ScudoListTest, SinglyLinkedList) {
+ testSinglyLinkedList();
+ testSinglyLinkedList();
+}
+
+template class ListTy, typename ListItemTy>
+static void testDoublyLinkedList() {
+ ListItemTy Items[3];
+ ListItemTy *X = &Items[0];
+ ListItemTy *Y = &Items[1];
+ ListItemTy *Z = &Items[2];
+
+ ListTy L;
L.clear();
+ L.init(Items, sizeof(Items));
L.push_back(X);
L.push_back(Y);
@@ -214,3 +256,8 @@ TEST(ScudoListTest, DoublyLinkedList) {
L.pop_front();
EXPECT_TRUE(L.empty());
}
+
+TEST(ScudoListTest, DoublyLinkedList) {
+ testDoublyLinkedList();
+ testDoublyLinkedList();
+}
diff --git a/compiler-rt/test/nsan/Posix/allocator_mapping.cpp b/compiler-rt/test/nsan/Posix/allocator_mapping.cpp
new file mode 100644
index 000000000000000..3a3e655e259d0b2
--- /dev/null
+++ b/compiler-rt/test/nsan/Posix/allocator_mapping.cpp
@@ -0,0 +1,30 @@
+/// From msan/allocator_mapping.cpp
+/// Test that a module constructor can not map memory over the NSan heap
+/// (without MAP_FIXED, of course).
+// RUN: %clangxx_nsan -O0 %s -o %t_1
+// RUN: %clangxx_nsan -O0 -DHEAP_ADDRESS=$(%run %t_1) %s -o %t_2 && %run %t_2
+
+#include
+#include
+#include
+#include
+
+#ifdef HEAP_ADDRESS
+struct A {
+ A() {
+ void *const hint = reinterpret_cast(HEAP_ADDRESS);
+ void *p = mmap(hint, 4096, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ // This address must be already mapped. Check that mmap() succeeds, but at a
+ // different address.
+ assert(p != reinterpret_cast(-1));
+ assert(p != hint);
+ }
+} a;
+#endif
+
+int main() {
+ void *p = malloc(10);
+ printf("0x%zx\n", reinterpret_cast(p) & (~0xfff));
+ free(p);
+}
diff --git a/compiler-rt/test/nsan/allocator_interface.cpp b/compiler-rt/test/nsan/allocator_interface.cpp
new file mode 100644
index 000000000000000..e658bd07291ad74
--- /dev/null
+++ b/compiler-rt/test/nsan/allocator_interface.cpp
@@ -0,0 +1,45 @@
+/// From sanitizer_common/TestCases/allocator_interface.cpp
+// RUN: %clangxx_nsan %s -o %t && %run %t 1234
+// RUN: %clangxx_nsan %s -o %t && %run %t 5678910
+
+#include
+#include
+#include
+#include
+#include
+
+void Test(int size) {
+ auto allocated_bytes_before = __sanitizer_get_current_allocated_bytes();
+ int *p = (int *)malloc(size);
+ assert(__sanitizer_get_estimated_allocated_size(size) >= size);
+ assert(__sanitizer_get_ownership(p));
+ assert(!__sanitizer_get_ownership(&p));
+ assert(__sanitizer_get_allocated_size(p) == size);
+ assert(__sanitizer_get_allocated_size_fast(p) == size);
+ assert(__sanitizer_get_allocated_begin(p) == p);
+ assert(__sanitizer_get_allocated_begin(p + 1) == p);
+ assert(__sanitizer_get_current_allocated_bytes() >=
+ size + allocated_bytes_before);
+ assert(__sanitizer_get_current_allocated_bytes() <=
+ 2 * size + allocated_bytes_before);
+ assert(__sanitizer_get_heap_size() >= size);
+ free(p);
+
+ // These are not implemented.
+ assert(__sanitizer_get_unmapped_bytes() <= 1);
+ assert(__sanitizer_get_free_bytes() > 0);
+
+ __sanitizer_purge_allocator();
+}
+
+int main(int argc, char **argv) {
+ int size = atoi(argv[1]);
+
+ Test(size);
+
+ // Check the thread local caches work as well.
+ std::thread t(Test, size);
+ t.join();
+
+ return 0;
+}
diff --git a/compiler-rt/test/nsan/malloc_hook.cpp b/compiler-rt/test/nsan/malloc_hook.cpp
new file mode 100644
index 000000000000000..3dcbfd845e75884
--- /dev/null
+++ b/compiler-rt/test/nsan/malloc_hook.cpp
@@ -0,0 +1,57 @@
+// RUN: %clangxx_nsan %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include
+#include
+#include
+
+extern "C" {
+const volatile void *global_ptr;
+
+#define WRITE(s) write(1, s, sizeof(s))
+
+// Note: avoid calling functions that allocate memory in malloc/free
+// to avoid infinite recursion.
+void __sanitizer_malloc_hook(const volatile void *ptr, size_t sz) {
+ if (__sanitizer_get_ownership(ptr) && sz == 4) {
+ WRITE("MallocHook\n");
+ global_ptr = ptr;
+ }
+}
+void __sanitizer_free_hook(const volatile void *ptr) {
+ if (__sanitizer_get_ownership(ptr) && ptr == global_ptr)
+ WRITE("FreeHook\n");
+}
+} // extern "C"
+
+volatile int *x;
+
+void MallocHook1(const volatile void *ptr, size_t sz) { WRITE("MH1\n"); }
+void MallocHook2(const volatile void *ptr, size_t sz) { WRITE("MH2\n"); }
+void FreeHook1(const volatile void *ptr) { WRITE("FH1\n"); }
+void FreeHook2(const volatile void *ptr) { WRITE("FH2\n"); }
+// Call this function with uninitialized arguments to poison
+// TLS shadow for function parameters before calling operator
+// new and, eventually, user-provided hook.
+__attribute__((noinline)) void allocate(int *unused1, int *unused2) {
+ x = new int;
+}
+
+int main() {
+ __sanitizer_install_malloc_and_free_hooks(MallocHook1, FreeHook1);
+ __sanitizer_install_malloc_and_free_hooks(MallocHook2, FreeHook2);
+ int *undef1, *undef2;
+ allocate(undef1, undef2);
+ // CHECK: MallocHook
+ // CHECK: MH1
+ // CHECK: MH2
+ // Check that malloc hook was called with correct argument.
+ if (global_ptr != (void *)x) {
+ _exit(1);
+ }
+ *x = 0;
+ delete x;
+ // CHECK: FreeHook
+ // CHECK: FH1
+ // CHECK: FH2
+ return 0;
+}
diff --git a/compiler-rt/test/nsan/new_delete_test.cpp b/compiler-rt/test/nsan/new_delete_test.cpp
new file mode 100644
index 000000000000000..356145e9bf21f02
--- /dev/null
+++ b/compiler-rt/test/nsan/new_delete_test.cpp
@@ -0,0 +1,77 @@
+/// From sanitizer_common/TestCases/Linux/new_delete_test.cpp
+// RUN: %clangxx_nsan -fno-sized-deallocation -O0 %s -o %t && %run %t
+// RUN: %clangxx_nsan -fsized-deallocation -O0 %s -o %t && %run %t
+
+#include
+
+namespace std {
+struct nothrow_t {};
+static const nothrow_t nothrow;
+enum class align_val_t : size_t {};
+} // namespace std
+
+void *operator new(size_t);
+void *operator new[](size_t);
+void *operator new(size_t, std::nothrow_t const &);
+void *operator new[](size_t, std::nothrow_t const &);
+void *operator new(size_t, std::align_val_t);
+void *operator new[](size_t, std::align_val_t);
+void *operator new(size_t, std::align_val_t, std::nothrow_t const &);
+void *operator new[](size_t, std::align_val_t, std::nothrow_t const &);
+
+void operator delete(void *) throw();
+void operator delete[](void *) throw();
+void operator delete(void *, std::nothrow_t const &);
+void operator delete[](void *, std::nothrow_t const &);
+void operator delete(void *, size_t) throw();
+void operator delete[](void *, size_t) throw();
+void operator delete(void *, std::align_val_t) throw();
+void operator delete[](void *, std::align_val_t) throw();
+void operator delete(void *, std::align_val_t, std::nothrow_t const &);
+void operator delete[](void *, std::align_val_t, std::nothrow_t const &);
+void operator delete(void *, size_t, std::align_val_t) throw();
+void operator delete[](void *, size_t, std::align_val_t) throw();
+
+template inline T *break_optimization(T *arg) {
+ __asm__ __volatile__("" : : "r"(arg) : "memory");
+ return arg;
+}
+
+struct S12 {
+ int a, b, c;
+};
+struct alignas(128) S12_128 {
+ int a, b, c;
+};
+struct alignas(256) S12_256 {
+ int a, b, c;
+};
+struct alignas(512) S1024_512 {
+ char a[1024];
+};
+struct alignas(1024) S1024_1024 {
+ char a[1024];
+};
+
+int main(int argc, char **argv) {
+ delete break_optimization(new S12);
+ operator delete(break_optimization(new S12), std::nothrow);
+ delete[] break_optimization(new S12[100]);
+ operator delete[](break_optimization(new S12[100]), std::nothrow);
+
+ delete break_optimization(new S12_128);
+ operator delete(break_optimization(new S12_128),
+ std::align_val_t(alignof(S12_128)));
+ operator delete(break_optimization(new S12_128),
+ std::align_val_t(alignof(S12_128)), std::nothrow);
+ operator delete(break_optimization(new S12_128), sizeof(S12_128),
+ std::align_val_t(alignof(S12_128)));
+
+ delete[] break_optimization(new S12_128[100]);
+ operator delete[](break_optimization(new S12_128[100]),
+ std::align_val_t(alignof(S12_128)));
+ operator delete[](break_optimization(new S12_128[100]),
+ std::align_val_t(alignof(S12_128)), std::nothrow);
+ operator delete[](break_optimization(new S12_128[100]), sizeof(S12_128[100]),
+ std::align_val_t(alignof(S12_128)));
+}
diff --git a/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h b/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h
index bd31aa0782493c4..e45011c8e02a33b 100644
--- a/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h
+++ b/flang/include/flang/Optimizer/Dialect/Support/FIRContext.h
@@ -71,6 +71,12 @@ void setTargetFeatures(mlir::ModuleOp mod, llvm::StringRef features);
/// Get the target features from the Module.
mlir::LLVM::TargetFeaturesAttr getTargetFeatures(mlir::ModuleOp mod);
+/// Set the compiler identifier for the module.
+void setIdent(mlir::ModuleOp mod, llvm::StringRef ident);
+
+/// Get the compiler identifier from the Module.
+llvm::StringRef getIdent(mlir::ModuleOp mod);
+
/// Helper for determining the target from the host, etc. Tools may use this
/// function to provide a consistent interpretation of the `--target=`
/// command-line option.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 3d5b2e2a2fe028a..ccbb481f472d819 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -11,6 +11,7 @@
//===----------------------------------------------------------------------===//
#include "flang/Lower/Bridge.h"
+#include "flang/Common/Version.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/CallInterface.h"
#include "flang/Lower/Coarray.h"
@@ -6125,6 +6126,7 @@ Fortran::lower::LoweringBridge::LoweringBridge(
fir::setTargetFeatures(*module.get(), targetMachine.getTargetFeatureString());
fir::support::setMLIRDataLayout(*module.get(),
targetMachine.createDataLayout());
+ fir::setIdent(*module.get(), Fortran::common::getFlangFullVersion());
}
void Fortran::lower::genCleanUpInRegionIfAny(
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 14723360f0ee1ce..1c0e541e4a36a79 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2069,10 +2069,12 @@ static void genCompositeDistributeSimd(
// TODO: Populate entry block arguments with private variables.
auto distributeOp = genWrapperOp(
converter, loc, distributeClauseOps, /*blockArgTypes=*/{});
+ distributeOp.setComposite(/*val=*/true);
// TODO: Populate entry block arguments with reduction and private variables.
auto simdOp = genWrapperOp(converter, loc, simdClauseOps,
/*blockArgTypes=*/{});
+ simdOp.setComposite(/*val=*/true);
// Construct wrapper entry block list and associated symbols. It is important
// that the symbol order and the block argument order match, so that the
@@ -2117,10 +2119,12 @@ static void genCompositeDoSimd(lower::AbstractConverter &converter,
// TODO: Add private variables to entry block arguments.
auto wsloopOp = genWrapperOp(
converter, loc, wsloopClauseOps, wsloopReductionTypes);
+ wsloopOp.setComposite(/*val=*/true);
// TODO: Populate entry block arguments with reduction and private variables.
auto simdOp = genWrapperOp(converter, loc, simdClauseOps,
/*blockArgTypes=*/{});
+ simdOp.setComposite(/*val=*/true);
// Construct wrapper entry block list and associated symbols. It is important
// that the symbol and block argument order match, so that the symbol-value
diff --git a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp
index 1aa631cb3912690..5bd8e2d43363616 100644
--- a/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp
+++ b/flang/lib/Optimizer/Dialect/Support/FIRContext.cpp
@@ -114,6 +114,22 @@ mlir::LLVM::TargetFeaturesAttr fir::getTargetFeatures(mlir::ModuleOp mod) {
return {};
}
+void fir::setIdent(mlir::ModuleOp mod, llvm::StringRef ident) {
+ if (ident.empty())
+ return;
+
+ mlir::MLIRContext *ctx = mod.getContext();
+ mod->setAttr(mlir::LLVM::LLVMDialect::getIdentAttrName(),
+ mlir::StringAttr::get(ctx, ident));
+}
+
+llvm::StringRef fir::getIdent(mlir::ModuleOp mod) {
+ if (auto attr = mod->getAttrOfType(
+ mlir::LLVM::LLVMDialect::getIdentAttrName()))
+ return attr;
+ return {};
+}
+
std::string fir::determineTargetTriple(llvm::StringRef triple) {
// Treat "" or "default" as stand-ins for the default machine.
if (triple.empty() || triple == "default")
diff --git a/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp b/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
index 7d0b8b3d7bc53d5..1e44288c784c2a9 100644
--- a/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
+++ b/flang/lib/Optimizer/Transforms/ConstantArgumentGlobalisation.cpp
@@ -98,9 +98,8 @@ class CallOpRewriter : public mlir::OpRewritePattern {
assert(!builder.getNamedGlobal(globalName) &&
"We should have a unique name here");
- if (std::find_if(allocas.begin(), allocas.end(), [alloca](auto x) {
- return x.first == alloca;
- }) == allocas.end()) {
+ if (llvm::none_of(allocas,
+ [alloca](auto x) { return x.first == alloca; })) {
allocas.push_back(std::make_pair(alloca, store));
}
diff --git a/flang/lib/Semantics/data-to-inits.cpp b/flang/lib/Semantics/data-to-inits.cpp
index b6dbbbf63138ec0..0f8b36de460865a 100644
--- a/flang/lib/Semantics/data-to-inits.cpp
+++ b/flang/lib/Semantics/data-to-inits.cpp
@@ -522,11 +522,10 @@ static const DerivedTypeSpec *HasDefaultInitialization(const Symbol &symbol) {
} else if (!object->isDummy() && object->type()) {
if (const DerivedTypeSpec * derived{object->type()->AsDerived()}) {
DirectComponentIterator directs{*derived};
- if (std::find_if(
- directs.begin(), directs.end(), [](const Symbol &component) {
- return !IsAllocatable(component) &&
- HasDeclarationInitializer(component);
- }) != directs.end()) {
+ if (llvm::any_of(directs, [](const Symbol &component) {
+ return !IsAllocatable(component) &&
+ HasDeclarationInitializer(component);
+ })) {
return derived;
}
}
diff --git a/flang/lib/Semantics/rewrite-directives.cpp b/flang/lib/Semantics/rewrite-directives.cpp
index 2c3c87f2546a356..c94d0f3855bee31 100644
--- a/flang/lib/Semantics/rewrite-directives.cpp
+++ b/flang/lib/Semantics/rewrite-directives.cpp
@@ -88,11 +88,9 @@ bool OmpRewriteMutator::Pre(parser::OpenMPAtomicConstruct &x) {
auto findMemOrderClause =
[](const std::list &clauses) {
- return std::find_if(
- clauses.begin(), clauses.end(), [](const auto &clause) {
- return std::get_if(
- &clause.u);
- }) != clauses.end();
+ return llvm::any_of(clauses, [](const auto &clause) {
+ return std::get_if(&clause.u);
+ });
};
// Get the clause list to which the new memory order clause must be added,
diff --git a/flang/lib/Semantics/symbol.cpp b/flang/lib/Semantics/symbol.cpp
index 31e91ee7355e332..b593bf89b18bc94 100644
--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -230,11 +230,10 @@ void GenericDetails::CopyFrom(const GenericDetails &from) {
derivedType_ = from.derivedType_;
}
for (std::size_t i{0}; i < from.specificProcs_.size(); ++i) {
- if (std::find_if(specificProcs_.begin(), specificProcs_.end(),
- [&](const Symbol &mySymbol) {
- return &mySymbol.GetUltimate() ==
- &from.specificProcs_[i]->GetUltimate();
- }) == specificProcs_.end()) {
+ if (llvm::none_of(specificProcs_, [&](const Symbol &mySymbol) {
+ return &mySymbol.GetUltimate() ==
+ &from.specificProcs_[i]->GetUltimate();
+ })) {
specificProcs_.push_back(from.specificProcs_[i]);
bindingNames_.push_back(from.bindingNames_[i]);
}
diff --git a/flang/test/Lower/ident.f90 b/flang/test/Lower/ident.f90
new file mode 100644
index 000000000000000..c5201edbcf83f7b
--- /dev/null
+++ b/flang/test/Lower/ident.f90
@@ -0,0 +1,5 @@
+! RUN: bbc -emit-fir %s -o - | FileCheck %s
+! RUN: %flang_fc1 -emit-fir %s -o - | FileCheck %s
+
+! CHECK: module attributes {
+! CHECK-SAME: llvm.ident = "flang version {{.+}}"
diff --git a/libc/config/gpu/entrypoints.txt b/libc/config/gpu/entrypoints.txt
index 5e05c1617a3be01..b2644d2ebf386cf 100644
--- a/libc/config/gpu/entrypoints.txt
+++ b/libc/config/gpu/entrypoints.txt
@@ -251,16 +251,26 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.atanf
libc.src.math.atanh
libc.src.math.atanhf
+ libc.src.math.canonicalize
+ libc.src.math.canonicalizef
+ libc.src.math.canonicalizel
libc.src.math.cbrt
libc.src.math.cbrtf
libc.src.math.ceil
libc.src.math.ceilf
+ libc.src.math.ceill
libc.src.math.copysign
libc.src.math.copysignf
+ libc.src.math.copysignl
libc.src.math.cos
libc.src.math.cosf
libc.src.math.cosh
libc.src.math.coshf
+ libc.src.math.cospif
+ libc.src.math.ddivl
+ libc.src.math.dfmal
+ libc.src.math.dmull
+ libc.src.math.dsqrtl
libc.src.math.erf
libc.src.math.erff
libc.src.math.exp
@@ -268,40 +278,101 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.exp10f
libc.src.math.exp2
libc.src.math.exp2f
+ libc.src.math.exp2m1f
libc.src.math.expf
libc.src.math.expm1
libc.src.math.expm1f
libc.src.math.fabs
libc.src.math.fabsf
+ libc.src.math.fabsl
libc.src.math.fadd
+ libc.src.math.faddl
libc.src.math.fdim
libc.src.math.fdimf
+ libc.src.math.fdiml
+ libc.src.math.fdiv
+ libc.src.math.fdivl
+ libc.src.math.ffma
+ libc.src.math.ffmal
libc.src.math.floor
libc.src.math.floorf
+ libc.src.math.floorl
libc.src.math.fma
libc.src.math.fmaf
libc.src.math.fmax
libc.src.math.fmaxf
+ libc.src.math.fmaximum
+ libc.src.math.fmaximumf
+ libc.src.math.fmaximuml
+ libc.src.math.fmaximum_mag
+ libc.src.math.fmaximum_magf
+ libc.src.math.fmaximum_magl
+ libc.src.math.fmaximum_mag_num
+ libc.src.math.fmaximum_mag_numf
+ libc.src.math.fmaximum_mag_numl
+ libc.src.math.fmaximum_num
+ libc.src.math.fmaximum_numf
+ libc.src.math.fmaximum_numl
+ libc.src.math.fmaxl
libc.src.math.fmin
libc.src.math.fminf
+ libc.src.math.fminimum
+ libc.src.math.fminimumf
+ libc.src.math.fminimuml
+ libc.src.math.fminimum_mag
+ libc.src.math.fminimum_magf
+ libc.src.math.fminimum_magl
+ libc.src.math.fminimum_mag_num
+ libc.src.math.fminimum_mag_numf
+ libc.src.math.fminimum_mag_numl
+ libc.src.math.fminimum_num
+ libc.src.math.fminimum_numf
+ libc.src.math.fminimum_numl
+ libc.src.math.fminl
libc.src.math.fmod
libc.src.math.fmodf
+ libc.src.math.fmodl
+ libc.src.math.fmul
+ libc.src.math.fmull
libc.src.math.frexp
libc.src.math.frexpf
+ libc.src.math.frexpl
+ # FIXME: Broken on NVPTX.
+ # libc.src.math.fromfp
+ # libc.src.math.fromfpf
+ # libc.src.math.fromfpl
+ # libc.src.math.fromfpx
+ # libc.src.math.fromfpxf
+ # libc.src.math.fromfpxl
+ libc.src.math.fsqrt
+ libc.src.math.fsqrtl
+ libc.src.math.fsub
+ libc.src.math.fsubl
libc.src.math.getpayload
libc.src.math.getpayloadf
+ libc.src.math.getpayloadl
libc.src.math.hypot
libc.src.math.hypotf
libc.src.math.ilogb
libc.src.math.ilogbf
+ libc.src.math.ilogbl
+ libc.src.math.isnan
+ libc.src.math.isnanf
+ libc.src.math.isnanl
libc.src.math.ldexp
libc.src.math.ldexpf
+ libc.src.math.ldexpl
+ libc.src.math.lgamma
+ libc.src.math.lgamma_r
libc.src.math.llogb
libc.src.math.llogbf
+ libc.src.math.llogbl
libc.src.math.llrint
libc.src.math.llrintf
+ libc.src.math.llrintl
libc.src.math.llround
libc.src.math.llroundf
+ libc.src.math.llroundl
libc.src.math.log
libc.src.math.log10
libc.src.math.log10f
@@ -309,55 +380,97 @@ set(TARGET_LIBM_ENTRYPOINTS
libc.src.math.log1pf
libc.src.math.log2
libc.src.math.log2f
+ libc.src.math.logb
+ libc.src.math.logbf
+ libc.src.math.logbl
libc.src.math.logf
libc.src.math.lrint
libc.src.math.lrintf
+ libc.src.math.lrintl
libc.src.math.lround
libc.src.math.lroundf
+ libc.src.math.lroundl
libc.src.math.modf
libc.src.math.modff
+ libc.src.math.modfl
libc.src.math.nan
libc.src.math.nanf
+ libc.src.math.nanl
libc.src.math.nearbyint
libc.src.math.nearbyintf
+ libc.src.math.nearbyintl
libc.src.math.nextafter
libc.src.math.nextafterf
+ libc.src.math.nextafterl
+ libc.src.math.nextdown
+ libc.src.math.nextdownf
+ libc.src.math.nextdownl
libc.src.math.nexttoward
libc.src.math.nexttowardf
+ libc.src.math.nexttowardl
+ libc.src.math.nextup
+ libc.src.math.nextupf
+ libc.src.math.nextupl
libc.src.math.pow
libc.src.math.powf
libc.src.math.powi
libc.src.math.powif
libc.src.math.remainder
libc.src.math.remainderf
+ libc.src.math.remainderl
libc.src.math.remquo
libc.src.math.remquof
+ libc.src.math.remquol
libc.src.math.rint
libc.src.math.rintf
+ libc.src.math.rintl
libc.src.math.round
+ libc.src.math.roundeven
+ libc.src.math.roundevenf
+ libc.src.math.roundevenl
libc.src.math.roundf
libc.src.math.scalbln
libc.src.math.scalblnf
libc.src.math.scalbn
libc.src.math.scalbnf
+ libc.src.math.scalbnl
+ libc.src.math.setpayload
+ libc.src.math.setpayloadf
+ libc.src.math.setpayloadl
+ libc.src.math.setpayloadsig
+ libc.src.math.setpayloadsigf
+ libc.src.math.setpayloadsigl
libc.src.math.sin
libc.src.math.sincos
libc.src.math.sincosf
libc.src.math.sinf
libc.src.math.sinh
libc.src.math.sinhf
+ libc.src.math.sinpif
libc.src.math.sqrt
libc.src.math.sqrtf
+ libc.src.math.sqrtl
libc.src.math.tan
libc.src.math.tanf
libc.src.math.tanh
libc.src.math.tanhf
libc.src.math.tgamma
libc.src.math.tgammaf
- libc.src.math.lgamma
- libc.src.math.lgamma_r
+ libc.src.math.totalorder
+ libc.src.math.totalorderf
+ libc.src.math.totalordermag
+ libc.src.math.totalordermagf
+ libc.src.math.totalordermagl
libc.src.math.trunc
libc.src.math.truncf
+ libc.src.math.truncl
+ # FIXME: Broken on NVPTX.
+ # libc.src.math.ufromfp
+ # libc.src.math.ufromfpf
+ # libc.src.math.ufromfpl
+ # libc.src.math.ufromfpx
+ # libc.src.math.ufromfpxf
+ # libc.src.math.ufromfpxl
)
if(LIBC_TYPES_HAS_FLOAT16)
@@ -366,18 +479,27 @@ if(LIBC_TYPES_HAS_FLOAT16)
libc.src.math.canonicalizef16
libc.src.math.ceilf16
libc.src.math.copysignf16
+ libc.src.math.exp10f16
+ libc.src.math.exp2f16
+ libc.src.math.expf16
libc.src.math.f16add
libc.src.math.f16addf
+ libc.src.math.f16addl
libc.src.math.f16div
libc.src.math.f16divf
+ libc.src.math.f16divl
libc.src.math.f16fma
libc.src.math.f16fmaf
+ libc.src.math.f16fmal
libc.src.math.f16mul
libc.src.math.f16mulf
+ libc.src.math.f16mull
libc.src.math.f16sqrt
libc.src.math.f16sqrtf
+ libc.src.math.f16sqrtl
libc.src.math.f16sub
libc.src.math.f16subf
+ libc.src.math.f16subl
libc.src.math.fabsf16
libc.src.math.fdimf16
libc.src.math.floorf16
diff --git a/libc/src/__support/str_to_float.h b/libc/src/__support/str_to_float.h
index 3bcfc190026257d..ffd6ebf27c77264 100644
--- a/libc/src/__support/str_to_float.h
+++ b/libc/src/__support/str_to_float.h
@@ -1159,13 +1159,11 @@ LIBC_INLINE StrToNumResult strtofloatingpoint(const char *__restrict src) {
index += 3;
StorageType nan_mantissa = 0;
// this handles the case of `NaN(n-character-sequence)`, where the
- // n-character-sequence is made of 0 or more letters and numbers in any
- // order.
+ // n-character-sequence is made of 0 or more letters, numbers, or
+ // underscore characters in any order.
if (src[index] == '(') {
size_t left_paren = index;
++index;
- // Apparently it's common for underscores to also be accepted. No idea
- // why, but it's causing fuzz failures.
while (isalnum(src[index]) || src[index] == '_')
++index;
if (src[index] == ')') {
diff --git a/libc/test/UnitTest/LibcTestMain.cpp b/libc/test/UnitTest/LibcTestMain.cpp
index 94536e971646865..c348d5ef1aa1b12 100644
--- a/libc/test/UnitTest/LibcTestMain.cpp
+++ b/libc/test/UnitTest/LibcTestMain.cpp
@@ -43,7 +43,15 @@ TestOptions parseOptions(int argc, char **argv) {
} // anonymous namespace
-extern "C" int main(int argc, char **argv, char **envp) {
+// The C++ standard forbids declaring the main function with a linkage specifier
+// outisde of 'freestanding' mode, only define the linkage for hermetic tests.
+#if __STDC_HOSTED__
+#define TEST_MAIN int main
+#else
+#define TEST_MAIN extern "C" int main
+#endif
+
+TEST_MAIN(int argc, char **argv, char **envp) {
LIBC_NAMESPACE::testing::argc = argc;
LIBC_NAMESPACE::testing::argv = argv;
LIBC_NAMESPACE::testing::envp = envp;
diff --git a/libc/test/src/stdlib/strtof_test.cpp b/libc/test/src/stdlib/strtof_test.cpp
index d7991745b69e6cd..6a716c956291cc2 100644
--- a/libc/test/src/stdlib/strtof_test.cpp
+++ b/libc/test/src/stdlib/strtof_test.cpp
@@ -200,7 +200,7 @@ TEST_F(LlvmLibcStrToFTest, NaNWithParenthesesValidSequenceInvalidNumberTests) {
run_test("NaN(1a)", 7, 0x7fc00000);
run_test("NaN(asdf)", 9, 0x7fc00000);
run_test("NaN(1A1)", 8, 0x7fc00000);
- run_test("NaN(why_does_this_work)", 23, 0x7fc00000);
+ run_test("NaN(underscores_are_ok)", 23, 0x7fc00000);
run_test(
"NaN(1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_)",
68, 0x7fc00000);
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index c4c6a855cc517be..b6381f86323dbd4 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -159,7 +159,7 @@
"`LWG3660 `__","``iterator_traits::pointer`` should conform to §[iterator.traits]","February 2022","|Complete|","14.0","|ranges|"
"`LWG3661 `__","``constinit atomic> a(nullptr);`` should work","February 2022","","",""
"","","","","",""
-"`LWG3564 `__","``transform_view::iterator::value_type`` and ``iterator_category`` should use ``const F&``","July 2022","","","|ranges|"
+"`LWG3564 `__","``transform_view::iterator::value_type`` and ``iterator_category`` should use ``const F&``","July 2022","|Complete|","20.0","|ranges|"
"`LWG3617 `__","``function``/``packaged_task`` deduction guides and deducing ``this``","July 2022","","",""
"`LWG3656 `__","Inconsistent bit operations returning a count","July 2022","|Complete|","15.0",""
"`LWG3659 `__","Consider ``ATOMIC_FLAG_INIT`` undeprecation","July 2022","|Complete|","15.0",""
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index bcce389c0e6809f..686a2a8fe8c21e3 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -173,7 +173,8 @@ template
# endif
requires __transform_view_constraints<_View, _Fn>
template
-class transform_view<_View, _Fn>::__iterator : public __transform_view_iterator_category_base<_View, _Fn> {
+class transform_view<_View, _Fn>::__iterator
+ : public __transform_view_iterator_category_base<_View, __maybe_const<_Const, _Fn>> {
using _Parent = __maybe_const<_Const, transform_view>;
using _Base = __maybe_const<_Const, _View>;
@@ -190,7 +191,7 @@ class transform_view<_View, _Fn>::__iterator : public __transform_view_iterator_
iterator_t<_Base> __current_ = iterator_t<_Base>();
using iterator_concept = typename __transform_view_iterator_concept<_View>::type;
- using value_type = remove_cvref_t>>;
+ using value_type = remove_cvref_t&, range_reference_t<_Base>>>;
using difference_type = range_difference_t<_Base>;
_LIBCPP_HIDE_FROM_ABI __iterator()
diff --git a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
index 4c2b483914f47e0..2c8534977febc7e 100644
--- a/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
+++ b/libcxx/test/libcxx/gdb/gdb_pretty_printer_test.sh.cpp
@@ -20,6 +20,9 @@
// support gdb anymore, favoring lldb instead.
// UNSUPPORTED: android
+// This test doesn't work as such on Windows.
+// UNSUPPORTED: windows
+
// RUN: %{cxx} %{flags} %s -o %t.exe %{compile_flags} -g %{link_flags}
// Ensure locale-independence for unicode tests.
// RUN: env LANG=en_US.UTF-8 %{gdb} -nx -batch -iex "set autoload off" -ex "source %S/../../../utils/gdb/libcxx/printers.py" -ex "python register_libcxx_printer_loader()" -ex "source %S/gdb_pretty_printer_test.py" %t.exe
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/types.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/types.pass.cpp
index 25d8936f4e4ebd1..607b748ac3e4bd8 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/types.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/iterator/types.pass.cpp
@@ -61,6 +61,22 @@ constexpr bool test() {
static_assert(std::same_as);
static_assert(std::same_as);
}
+ {
+ // Member typedefs for random access iterator, LWG3564 mutable overload
+ using TView = std::ranges::transform_view;
+ using TIter = std::ranges::iterator_t;
+ static_assert(std::same_as);
+ static_assert(std::same_as);
+ static_assert(std::same_as);
+ static_assert(std::same_as);
+
+ using CTIter = std::ranges::iterator_t;
+ static_assert(std::same_as);
+ static_assert(std::same_as); // Note: this is now input_iterator_tag.
+ static_assert(std::same_as);
+ static_assert(std::same_as);
+ }
{
// Member typedefs for bidirectional iterator.
using TView = std::ranges::transform_view;
diff --git a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
index cc5679f229de250..e94e7dfcdaeb7e9 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.transform/types.h
@@ -131,6 +131,11 @@ struct PlusOne {
constexpr int operator()(int x) const { return x + 1; }
};
+struct PlusOneMutableOverload {
+ constexpr int operator()(int x) const { return x + 1; }
+ constexpr int& operator()(int& x) { return ++x; }
+};
+
struct Increment {
constexpr int& operator()(int& x) { return ++x; }
};
diff --git a/libcxx/test/support/count_new.h b/libcxx/test/support/count_new.h
index 0a95e05b72421bc..2298c4fd63e845b 100644
--- a/libcxx/test/support/count_new.h
+++ b/libcxx/test/support/count_new.h
@@ -455,7 +455,7 @@ void operator delete[](void* p, std::nothrow_t const&) TEST_NOEXCEPT {
# define USE_ALIGNED_ALLOC
# endif
-inline void* alocate_aligned_impl(std::size_t size, std::align_val_t align) {
+inline void* allocate_aligned_impl(std::size_t size, std::align_val_t align) {
const std::size_t alignment = static_cast(align);
void* ret = nullptr;
# ifdef USE_ALIGNED_ALLOC
@@ -479,7 +479,7 @@ inline void free_aligned_impl(void* ptr, std::align_val_t) {
// operator new(size_t, align_val_t[, nothrow_t]) and operator delete(size_t, align_val_t[, nothrow_t])
void* operator new(std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
getGlobalMemCounter()->alignedNewCalled(s, static_cast(av));
- void* p = alocate_aligned_impl(s, av);
+ void* p = allocate_aligned_impl(s, av);
if (p == nullptr)
detail::throw_bad_alloc_helper();
return p;
@@ -495,7 +495,7 @@ void* operator new(std::size_t s, std::align_val_t av, std::nothrow_t const&) TE
return nullptr;
}
# endif
- return alocate_aligned_impl(s, av);
+ return allocate_aligned_impl(s, av);
}
void operator delete(void* p, std::align_val_t av) TEST_NOEXCEPT {
@@ -511,7 +511,7 @@ void operator delete(void* p, std::align_val_t av, std::nothrow_t const&) TEST_N
// operator new[](size_t, align_val_t[, nothrow_t]) and operator delete[](size_t, align_val_t[, nothrow_t])
void* operator new[](std::size_t s, std::align_val_t av) TEST_THROW_SPEC(std::bad_alloc) {
getGlobalMemCounter()->alignedNewArrayCalled(s, static_cast(av));
- void* p = alocate_aligned_impl(s, av);
+ void* p = allocate_aligned_impl(s, av);
if (p == nullptr)
detail::throw_bad_alloc_helper();
return p;
@@ -527,7 +527,7 @@ void* operator new[](std::size_t s, std::align_val_t av, std::nothrow_t const&)
return nullptr;
}
# endif
- return alocate_aligned_impl(s, av);
+ return allocate_aligned_impl(s, av);
}
void operator delete[](void* p, std::align_val_t av) TEST_NOEXCEPT {
diff --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index cf34a814fed4154..a7bb9bd47299e25 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -682,6 +682,8 @@ static int64_t getTlsTpOffset(const Symbol &s) {
// before TP. The alignment padding is added so that (TP - padding -
// p_memsz) is congruent to p_vaddr modulo p_align.
PhdrEntry *tls = ctx.tlsPhdr;
+ if (!tls) // Reported an error in getSymVA
+ return 0;
switch (config->emachine) {
// Variant 1.
case EM_ARM:
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 4e1ae819b86fad3..13fc6dc0dd572a2 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -119,9 +119,11 @@ static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
// after sections are finalized. (e.g. Measuring the size of .rela.dyn
// for Android relocation packing requires knowing TLS symbol addresses
// during section finalization.)
- if (!ctx.tlsPhdr || !ctx.tlsPhdr->firstSec)
- fatal(toString(d.file) +
- " has an STT_TLS symbol but doesn't have an SHF_TLS section");
+ if (!ctx.tlsPhdr || !ctx.tlsPhdr->firstSec) {
+ errorOrWarn(toString(d.file) +
+ " has an STT_TLS symbol but doesn't have a PT_TLS segment");
+ return 0;
+ }
return va - ctx.tlsPhdr->firstSec->addr;
}
return va;
diff --git a/lld/test/ELF/invalid/tls-symbol.s b/lld/test/ELF/invalid/tls-symbol.s
index 3f371d406e1f2f6..1af1e783d1f5b4e 100644
--- a/lld/test/ELF/invalid/tls-symbol.s
+++ b/lld/test/ELF/invalid/tls-symbol.s
@@ -4,7 +4,7 @@
# RUN: yaml2obj %s -o %t.o
# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
-# CHECK: has an STT_TLS symbol but doesn't have an SHF_TLS section
+# CHECK: has an STT_TLS symbol but doesn't have a PT_TLS segment
--- !ELF
FileHeader:
diff --git a/lld/test/ELF/linkerscript/phdrs-no-tls.test b/lld/test/ELF/linkerscript/phdrs-no-tls.test
new file mode 100644
index 000000000000000..0d374beaf8eee86
--- /dev/null
+++ b/lld/test/ELF/linkerscript/phdrs-no-tls.test
@@ -0,0 +1,32 @@
+## Test STT_TLS and relocations without PT_TLS. See also invalid/tls-symbol.s.
+# REQUIRES: x86
+# RUN: rm -rf %t && split-file %s %t && cd %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64 a.s -o a.o
+# RUN: not ld.lld -T a.lds a.o
+# RUN: ld.lld -T a.lds a.o --noinhibit-exec 2>&1 | FileCheck %s --implicit-check-not=warning:
+
+# CHECK: warning: a.o has an STT_TLS symbol but doesn't have a PT_TLS segment
+# CHECK-NEXT: warning: a.o has an STT_TLS symbol but doesn't have a PT_TLS segment
+
+#--- a.lds
+PHDRS {
+ text PT_LOAD FLAGS(5);
+ data PT_LOAD FLAGS(6);
+}
+
+SECTIONS {
+ . = SIZEOF_HEADERS;
+ .text : { *(.text .text.*) } :text
+ .data : { *(.data .data.*) } :data
+}
+
+#--- a.s
+.globl _start
+_start:
+ movl %fs:a@TPOFF, %eax
+ movl %fs:b@TPOFF, %eax
+
+.section .tbss,"awT"
+a:
+b:
+ .long 0
diff --git a/lldb/include/lldb/Symbol/ObjectFile.h b/lldb/include/lldb/Symbol/ObjectFile.h
index 8592323322e383e..d89314d44bf6719 100644
--- a/lldb/include/lldb/Symbol/ObjectFile.h
+++ b/lldb/include/lldb/Symbol/ObjectFile.h
@@ -656,8 +656,9 @@ class ObjectFile : public std::enable_shared_from_this,
// When an object file is in memory, subclasses should try and lock the
// process weak pointer. If the process weak pointer produces a valid
// ProcessSP, then subclasses can call this function to read memory.
- static lldb::DataBufferSP ReadMemory(const lldb::ProcessSP &process_sp,
- lldb::addr_t addr, size_t byte_size);
+ static lldb::WritableDataBufferSP
+ ReadMemory(const lldb::ProcessSP &process_sp, lldb::addr_t addr,
+ size_t byte_size);
// This function returns raw file contents. Do not use it if you want
// transparent decompression of section contents.
diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp
index 5b670067b5c4337..030b30f070ee352 100644
--- a/lldb/source/Expression/IRInterpreter.cpp
+++ b/lldb/source/Expression/IRInterpreter.cpp
@@ -96,7 +96,7 @@ class InterpreterStackFrame {
typedef std::map ValueMap;
ValueMap m_values;
- DataLayout &m_target_data;
+ const DataLayout &m_target_data;
lldb_private::IRExecutionUnit &m_execution_unit;
const BasicBlock *m_bb = nullptr;
const BasicBlock *m_prev_bb = nullptr;
@@ -110,7 +110,7 @@ class InterpreterStackFrame {
lldb::ByteOrder m_byte_order;
size_t m_addr_byte_size;
- InterpreterStackFrame(DataLayout &target_data,
+ InterpreterStackFrame(const DataLayout &target_data,
lldb_private::IRExecutionUnit &execution_unit,
lldb::addr_t stack_frame_bottom,
lldb::addr_t stack_frame_top)
@@ -703,7 +703,7 @@ bool IRInterpreter::Interpret(llvm::Module &module, llvm::Function &function,
s.c_str());
}
- DataLayout data_layout(&module);
+ const DataLayout &data_layout = module.getDataLayout();
InterpreterStackFrame frame(data_layout, execution_unit, stack_frame_bottom,
stack_frame_top);
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp b/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
index bc0f5993aad0d6a..defd72bbd931062 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/IRDynamicChecks.cpp
@@ -280,10 +280,9 @@ class Instrumenter {
IntegerType *GetIntptrTy() {
if (!m_intptr_ty) {
- llvm::DataLayout data_layout(&m_module);
-
- m_intptr_ty = llvm::Type::getIntNTy(m_module.getContext(),
- data_layout.getPointerSizeInBits());
+ m_intptr_ty = llvm::Type::getIntNTy(
+ m_module.getContext(),
+ m_module.getDataLayout().getPointerSizeInBits());
}
return m_intptr_ty;
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
index cc9bd14c6194e4f..34461da46dfc7be 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
+++ b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.cpp
@@ -1606,7 +1606,7 @@ bool IRForTarget::runOnModule(Module &llvm_module) {
lldb_private::Log *log(GetLog(LLDBLog::Expressions));
m_module = &llvm_module;
- m_target_data = std::make_unique(m_module);
+ m_target_data = &m_module->getDataLayout();
m_intptr_ty = llvm::Type::getIntNTy(m_module->getContext(),
m_target_data->getPointerSizeInBits());
diff --git a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
index a924187ba04c061..45027fcd6fa4921 100644
--- a/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
+++ b/lldb/source/Plugins/ExpressionParser/Clang/IRForTarget.h
@@ -331,9 +331,9 @@ class IRForTarget {
lldb_private::TypeFromParser m_result_type;
/// The module being processed, or NULL if that has not been determined yet.
llvm::Module *m_module = nullptr;
- /// The target data for the module being processed, or NULL if there is no
+ /// The target data for the module being processed, or nullptr if there is no
/// module.
- std::unique_ptr m_target_data;
+ const llvm::DataLayout *m_target_data = nullptr;
/// The DeclMap containing the Decls
lldb_private::ClangExpressionDeclMap *m_decl_map;
/// The address of the function CFStringCreateWithBytes, cast to the
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
index 890db5c27481462..f348b94baad806c 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp
@@ -419,19 +419,35 @@ ObjectFile *ObjectFileELF::CreateInstance(const lldb::ModuleSP &module_sp,
ObjectFile *ObjectFileELF::CreateMemoryInstance(
const lldb::ModuleSP &module_sp, WritableDataBufferSP data_sp,
const lldb::ProcessSP &process_sp, lldb::addr_t header_addr) {
- if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT)) {
- const uint8_t *magic = data_sp->GetBytes();
- if (ELFHeader::MagicBytesMatch(magic)) {
- unsigned address_size = ELFHeader::AddressSizeInBytes(magic);
- if (address_size == 4 || address_size == 8) {
- std::unique_ptr objfile_up(
- new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
- ArchSpec spec = objfile_up->GetArchitecture();
- if (spec && objfile_up->SetModulesArchitecture(spec))
- return objfile_up.release();
- }
- }
- }
+ if (!data_sp || data_sp->GetByteSize() < (llvm::ELF::EI_NIDENT))
+ return nullptr;
+ const uint8_t *magic = data_sp->GetBytes();
+ if (!ELFHeader::MagicBytesMatch(magic))
+ return nullptr;
+ // Read the ELF header first so we can figure out how many bytes we need
+ // to read to get as least the ELF header + program headers.
+ DataExtractor data;
+ data.SetData(data_sp);
+ elf::ELFHeader hdr;
+ lldb::offset_t offset = 0;
+ if (!hdr.Parse(data, &offset))
+ return nullptr;
+
+ // Make sure the address size is set correctly in the ELF header.
+ if (!hdr.Is32Bit() && !hdr.Is64Bit())
+ return nullptr;
+ // Figure out where the program headers end and read enough bytes to get the
+ // program headers in their entirety.
+ lldb::offset_t end_phdrs = hdr.e_phoff + (hdr.e_phentsize * hdr.e_phnum);
+ if (end_phdrs > data_sp->GetByteSize())
+ data_sp = ReadMemory(process_sp, header_addr, end_phdrs);
+
+ std::unique_ptr objfile_up(
+ new ObjectFileELF(module_sp, data_sp, process_sp, header_addr));
+ ArchSpec spec = objfile_up->GetArchitecture();
+ if (spec && objfile_up->SetModulesArchitecture(spec))
+ return objfile_up.release();
+
return nullptr;
}
@@ -873,42 +889,37 @@ Address ObjectFileELF::GetImageInfoAddress(Target *target) {
if (!section_list)
return Address();
- // Find the SHT_DYNAMIC (.dynamic) section.
- SectionSP dynsym_section_sp(
- section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true));
- if (!dynsym_section_sp)
- return Address();
- assert(dynsym_section_sp->GetObjectFile() == this);
+ for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
+ const ELFDynamic &symbol = m_dynamic_symbols[i].symbol;
- user_id_t dynsym_id = dynsym_section_sp->GetID();
- const ELFSectionHeaderInfo *dynsym_hdr = GetSectionHeaderByIndex(dynsym_id);
- if (!dynsym_hdr)
- return Address();
+ if (symbol.d_tag != DT_DEBUG && symbol.d_tag != DT_MIPS_RLD_MAP &&
+ symbol.d_tag != DT_MIPS_RLD_MAP_REL)
+ continue;
- for (size_t i = 0; i < m_dynamic_symbols.size(); ++i) {
- ELFDynamic &symbol = m_dynamic_symbols[i];
+ // Compute the offset as the number of previous entries plus the size of
+ // d_tag.
+ const addr_t offset = (i * 2 + 1) * GetAddressByteSize();
+ const addr_t d_file_addr = m_dynamic_base_addr + offset;
+ Address d_addr;
+ if (!d_addr.ResolveAddressUsingFileSections(d_file_addr, GetSectionList()))
+ return Address();
+ if (symbol.d_tag == DT_DEBUG)
+ return d_addr;
- if (symbol.d_tag == DT_DEBUG) {
- // Compute the offset as the number of previous entries plus the size of
- // d_tag.
- addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
- return Address(dynsym_section_sp, offset);
- }
// MIPS executables uses DT_MIPS_RLD_MAP_REL to support PIE. DT_MIPS_RLD_MAP
// exists in non-PIE.
- else if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
- symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
- target) {
- addr_t offset = i * dynsym_hdr->sh_entsize + GetAddressByteSize();
- addr_t dyn_base = dynsym_section_sp->GetLoadBaseAddress(target);
- if (dyn_base == LLDB_INVALID_ADDRESS)
+ if ((symbol.d_tag == DT_MIPS_RLD_MAP ||
+ symbol.d_tag == DT_MIPS_RLD_MAP_REL) &&
+ target) {
+ const addr_t d_load_addr = d_addr.GetLoadAddress(target);
+ if (d_load_addr == LLDB_INVALID_ADDRESS)
return Address();
Status error;
if (symbol.d_tag == DT_MIPS_RLD_MAP) {
// DT_MIPS_RLD_MAP tag stores an absolute address of the debug pointer.
Address addr;
- if (target->ReadPointerFromMemory(dyn_base + offset, error, addr, true))
+ if (target->ReadPointerFromMemory(d_load_addr, error, addr, true))
return addr;
}
if (symbol.d_tag == DT_MIPS_RLD_MAP_REL) {
@@ -916,18 +927,17 @@ Address ObjectFileELF::GetImageInfoAddress(Target *target) {
// relative to the address of the tag.
uint64_t rel_offset;
rel_offset = target->ReadUnsignedIntegerFromMemory(
- dyn_base + offset, GetAddressByteSize(), UINT64_MAX, error, true);
+ d_load_addr, GetAddressByteSize(), UINT64_MAX, error, true);
if (error.Success() && rel_offset != UINT64_MAX) {
Address addr;
addr_t debug_ptr_address =
- dyn_base + (offset - GetAddressByteSize()) + rel_offset;
+ d_load_addr - GetAddressByteSize() + rel_offset;
addr.SetOffset(debug_ptr_address);
return addr;
}
}
}
}
-
return Address();
}
@@ -970,62 +980,23 @@ Address ObjectFileELF::GetBaseAddress() {
return LLDB_INVALID_ADDRESS;
}
-// ParseDependentModules
size_t ObjectFileELF::ParseDependentModules() {
if (m_filespec_up)
return m_filespec_up->GetSize();
m_filespec_up = std::make_unique();
- if (!ParseSectionHeaders())
- return 0;
-
- SectionList *section_list = GetSectionList();
- if (!section_list)
- return 0;
-
- // Find the SHT_DYNAMIC section.
- Section *dynsym =
- section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
- .get();
- if (!dynsym)
- return 0;
- assert(dynsym->GetObjectFile() == this);
-
- const ELFSectionHeaderInfo *header = GetSectionHeaderByIndex(dynsym->GetID());
- if (!header)
- return 0;
- // sh_link: section header index of string table used by entries in the
- // section.
- Section *dynstr = section_list->FindSectionByID(header->sh_link).get();
- if (!dynstr)
- return 0;
-
- DataExtractor dynsym_data;
- DataExtractor dynstr_data;
- if (ReadSectionData(dynsym, dynsym_data) &&
- ReadSectionData(dynstr, dynstr_data)) {
- ELFDynamic symbol;
- const lldb::offset_t section_size = dynsym_data.GetByteSize();
- lldb::offset_t offset = 0;
-
- // The only type of entries we are concerned with are tagged DT_NEEDED,
- // yielding the name of a required library.
- while (offset < section_size) {
- if (!symbol.Parse(dynsym_data, &offset))
- break;
-
- if (symbol.d_tag != DT_NEEDED)
+ if (ParseDynamicSymbols()) {
+ for (const auto &entry : m_dynamic_symbols) {
+ if (entry.symbol.d_tag != DT_NEEDED)
continue;
-
- uint32_t str_index = static_cast(symbol.d_val);
- const char *lib_name = dynstr_data.PeekCStr(str_index);
- FileSpec file_spec(lib_name);
- FileSystem::Instance().Resolve(file_spec);
- m_filespec_up->Append(file_spec);
+ if (!entry.name.empty()) {
+ FileSpec file_spec(entry.name);
+ FileSystem::Instance().Resolve(file_spec);
+ m_filespec_up->Append(file_spec);
+ }
}
}
-
return m_filespec_up->GetSize();
}
@@ -2472,48 +2443,47 @@ size_t ObjectFileELF::ParseDynamicSymbols() {
if (m_dynamic_symbols.size())
return m_dynamic_symbols.size();
- SectionList *section_list = GetSectionList();
- if (!section_list)
+ std::optional dynamic_data = GetDynamicData();
+ if (!dynamic_data)
return 0;
- // Find the SHT_DYNAMIC section.
- Section *dynsym =
- section_list->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
- .get();
- if (!dynsym)
- return 0;
- assert(dynsym->GetObjectFile() == this);
-
- ELFDynamic symbol;
- DataExtractor dynsym_data;
- if (ReadSectionData(dynsym, dynsym_data)) {
- const lldb::offset_t section_size = dynsym_data.GetByteSize();
- lldb::offset_t cursor = 0;
-
- while (cursor < section_size) {
- if (!symbol.Parse(dynsym_data, &cursor))
+ ELFDynamicWithName e;
+ lldb::offset_t cursor = 0;
+ while (e.symbol.Parse(*dynamic_data, &cursor)) {
+ m_dynamic_symbols.push_back(e);
+ if (e.symbol.d_tag == DT_NULL)
+ break;
+ }
+ if (std::optional dynstr_data = GetDynstrData()) {
+ for (ELFDynamicWithName &entry : m_dynamic_symbols) {
+ switch (entry.symbol.d_tag) {
+ case DT_NEEDED:
+ case DT_SONAME:
+ case DT_RPATH:
+ case DT_RUNPATH:
+ case DT_AUXILIARY:
+ case DT_FILTER: {
+ lldb::offset_t cursor = entry.symbol.d_val;
+ const char *name = dynstr_data->GetCStr(&cursor);
+ if (name)
+ entry.name = std::string(name);
break;
-
- m_dynamic_symbols.push_back(symbol);
+ }
+ default:
+ break;
+ }
}
}
-
return m_dynamic_symbols.size();
}
const ELFDynamic *ObjectFileELF::FindDynamicSymbol(unsigned tag) {
if (!ParseDynamicSymbols())
return nullptr;
-
- DynamicSymbolCollIter I = m_dynamic_symbols.begin();
- DynamicSymbolCollIter E = m_dynamic_symbols.end();
- for (; I != E; ++I) {
- ELFDynamic *symbol = &*I;
-
- if (symbol->d_tag == tag)
- return symbol;
+ for (const auto &entry : m_dynamic_symbols) {
+ if (entry.symbol.d_tag == tag)
+ return &entry.symbol;
}
-
return nullptr;
}
@@ -3230,7 +3200,10 @@ void ObjectFileELF::Dump(Stream *s) {
ArchSpec header_arch = GetArchitecture();
*s << ", file = '" << m_file
- << "', arch = " << header_arch.GetArchitectureName() << "\n";
+ << "', arch = " << header_arch.GetArchitectureName();
+ if (m_memory_addr != LLDB_INVALID_ADDRESS)
+ s->Printf(", addr = %#16.16" PRIx64, m_memory_addr);
+ s->EOL();
DumpELFHeader(s, m_header);
s->EOL();
@@ -3248,6 +3221,12 @@ void ObjectFileELF::Dump(Stream *s) {
s->EOL();
DumpDependentModules(s);
s->EOL();
+ DumpELFDynamic(s);
+ s->EOL();
+ Address image_info_addr = GetImageInfoAddress(nullptr);
+ if (image_info_addr.IsValid())
+ s->Printf("image_info_address = %#16.16" PRIx64 "\n",
+ image_info_addr.GetFileAddress());
}
// DumpELFHeader
@@ -3492,6 +3471,111 @@ void ObjectFileELF::DumpDependentModules(lldb_private::Stream *s) {
}
}
+std::string static getDynamicTagAsString(uint16_t Arch, uint64_t Type) {
+#define DYNAMIC_STRINGIFY_ENUM(tag, value) \
+ case value: \
+ return #tag;
+
+#define DYNAMIC_TAG(n, v)
+ switch (Arch) {
+ case llvm::ELF::EM_AARCH64:
+ switch (Type) {
+#define AARCH64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef AARCH64_DYNAMIC_TAG
+ }
+ break;
+
+ case llvm::ELF::EM_HEXAGON:
+ switch (Type) {
+#define HEXAGON_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef HEXAGON_DYNAMIC_TAG
+ }
+ break;
+
+ case llvm::ELF::EM_MIPS:
+ switch (Type) {
+#define MIPS_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef MIPS_DYNAMIC_TAG
+ }
+ break;
+
+ case llvm::ELF::EM_PPC:
+ switch (Type) {
+#define PPC_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef PPC_DYNAMIC_TAG
+ }
+ break;
+
+ case llvm::ELF::EM_PPC64:
+ switch (Type) {
+#define PPC64_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef PPC64_DYNAMIC_TAG
+ }
+ break;
+
+ case llvm::ELF::EM_RISCV:
+ switch (Type) {
+#define RISCV_DYNAMIC_TAG(name, value) DYNAMIC_STRINGIFY_ENUM(name, value)
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef RISCV_DYNAMIC_TAG
+ }
+ break;
+ }
+#undef DYNAMIC_TAG
+ switch (Type) {
+// Now handle all dynamic tags except the architecture specific ones
+#define AARCH64_DYNAMIC_TAG(name, value)
+#define MIPS_DYNAMIC_TAG(name, value)
+#define HEXAGON_DYNAMIC_TAG(name, value)
+#define PPC_DYNAMIC_TAG(name, value)
+#define PPC64_DYNAMIC_TAG(name, value)
+#define RISCV_DYNAMIC_TAG(name, value)
+// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc.
+#define DYNAMIC_TAG_MARKER(name, value)
+#define DYNAMIC_TAG(name, value) \
+ case value: \
+ return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef DYNAMIC_TAG
+#undef AARCH64_DYNAMIC_TAG
+#undef MIPS_DYNAMIC_TAG
+#undef HEXAGON_DYNAMIC_TAG
+#undef PPC_DYNAMIC_TAG
+#undef PPC64_DYNAMIC_TAG
+#undef RISCV_DYNAMIC_TAG
+#undef DYNAMIC_TAG_MARKER
+#undef DYNAMIC_STRINGIFY_ENUM
+ default:
+ return "0x" + llvm::utohexstr(Type, true);
+ }
+}
+
+void ObjectFileELF::DumpELFDynamic(lldb_private::Stream *s) {
+ ParseDynamicSymbols();
+ if (m_dynamic_symbols.empty())
+ return;
+
+ s->PutCString(".dynamic:\n");
+ s->PutCString("IDX d_tag d_val/d_ptr\n");
+ s->PutCString("==== ---------------- ------------------\n");
+ uint32_t idx = 0;
+ for (const auto &entry : m_dynamic_symbols) {
+ s->Printf("[%2u] ", idx++);
+ s->Printf(
+ "%-16s 0x%16.16" PRIx64,
+ getDynamicTagAsString(m_header.e_machine, entry.symbol.d_tag).c_str(),
+ entry.symbol.d_ptr);
+ if (!entry.name.empty())
+ s->Printf(" \"%s\"", entry.name.c_str());
+ s->EOL();
+ }
+}
+
ArchSpec ObjectFileELF::GetArchitecture() {
if (!ParseHeader())
return ArchSpec();
@@ -3664,7 +3748,24 @@ llvm::ArrayRef ObjectFileELF::ProgramHeaders() {
}
DataExtractor ObjectFileELF::GetSegmentData(const ELFProgramHeader &H) {
- return DataExtractor(m_data, H.p_offset, H.p_filesz);
+ // Try and read the program header from our cached m_data which can come from
+ // the file on disk being mmap'ed or from the initial part of the ELF file we
+ // read from memory and cached.
+ DataExtractor data = DataExtractor(m_data, H.p_offset, H.p_filesz);
+ if (data.GetByteSize() == H.p_filesz)
+ return data;
+ if (IsInMemory()) {
+ // We have a ELF file in process memory, read the program header data from
+ // the process.
+ if (ProcessSP process_sp = m_process_wp.lock()) {
+ const lldb::offset_t base_file_addr = GetBaseAddress().GetFileAddress();
+ const addr_t load_bias = m_memory_addr - base_file_addr;
+ const addr_t data_addr = H.p_vaddr + load_bias;
+ if (DataBufferSP data_sp = ReadMemory(process_sp, data_addr, H.p_memsz))
+ return DataExtractor(data_sp, GetByteOrder(), GetAddressByteSize());
+ }
+ }
+ return DataExtractor();
}
bool ObjectFileELF::AnySegmentHasPhysicalAddress() {
@@ -3704,3 +3805,88 @@ ObjectFileELF::MapFileDataWritable(const FileSpec &file, uint64_t Size,
return FileSystem::Instance().CreateWritableDataBuffer(file.GetPath(), Size,
Offset);
}
+
+std::optional ObjectFileELF::GetDynstrData() {
+ if (SectionList *section_list = GetSectionList()) {
+ // Find the SHT_DYNAMIC section.
+ if (Section *dynamic =
+ section_list
+ ->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get()) {
+ assert(dynamic->GetObjectFile() == this);
+ if (const ELFSectionHeaderInfo *header =
+ GetSectionHeaderByIndex(dynamic->GetID())) {
+ // sh_link: section header index of string table used by entries in
+ // the section.
+ if (Section *dynstr =
+ section_list->FindSectionByID(header->sh_link).get()) {
+ DataExtractor data;
+ if (ReadSectionData(dynstr, data))
+ return data;
+ }
+ }
+ }
+ }
+
+ // Every ELF file which represents an executable or shared library has
+ // mandatory .dynamic entries. Two of these values are DT_STRTAB and DT_STRSZ
+ // and represent the dynamic symbol tables's string table. These are needed
+ // by the dynamic loader and we can read them from a process' address space.
+ //
+ // When loading and ELF file from memory, only the program headers end up
+ // being mapped into memory, and we can find these values in the PT_DYNAMIC
+ // segment.
+ const ELFDynamic *strtab = FindDynamicSymbol(DT_STRTAB);
+ const ELFDynamic *strsz = FindDynamicSymbol(DT_STRSZ);
+ if (strtab == nullptr || strsz == nullptr)
+ return std::nullopt;
+
+ if (ProcessSP process_sp = m_process_wp.lock()) {
+ if (DataBufferSP data_sp =
+ ReadMemory(process_sp, strtab->d_ptr, strsz->d_val))
+ return DataExtractor(data_sp, GetByteOrder(), GetAddressByteSize());
+ } else {
+ // We have an ELF file with no section headers or we didn't find the
+ // .dynamic section. Try and find the .dynstr section.
+ Address addr;
+ if (addr.ResolveAddressUsingFileSections(strtab->d_ptr, GetSectionList())) {
+ DataExtractor data;
+ addr.GetSection()->GetSectionData(data);
+ return DataExtractor(data,
+ strtab->d_ptr - addr.GetSection()->GetFileAddress(),
+ strsz->d_val);
+ }
+ }
+ return std::nullopt;
+}
+
+std::optional ObjectFileELF::GetDynamicData() {
+ DataExtractor data;
+ // The PT_DYNAMIC program header describes where the .dynamic section is and
+ // doesn't require parsing section headers. The PT_DYNAMIC is required by
+ // executables and shared libraries so it will always be available.
+ for (const ELFProgramHeader &H : ProgramHeaders()) {
+ if (H.p_type == llvm::ELF::PT_DYNAMIC) {
+ data = GetSegmentData(H);
+ if (data.GetByteSize() > 0) {
+ m_dynamic_base_addr = H.p_vaddr;
+ return data;
+ }
+ }
+ }
+ // Fall back to using section headers.
+ if (SectionList *section_list = GetSectionList()) {
+ // Find the SHT_DYNAMIC section.
+ if (Section *dynamic =
+ section_list
+ ->FindSectionByType(eSectionTypeELFDynamicLinkInfo, true)
+ .get()) {
+ assert(dynamic->GetObjectFile() == this);
+ if (ReadSectionData(dynamic, data)) {
+ m_dynamic_base_addr = dynamic->GetFileAddress();
+ return data;
+ }
+ }
+ }
+ return std::nullopt;
+}
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
index 844e981b1d890a3..aba3a5bfcbf5b6c 100644
--- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
+++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h
@@ -183,7 +183,11 @@ class ObjectFileELF : public lldb_private::ObjectFile {
typedef SectionHeaderColl::iterator SectionHeaderCollIter;
typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter;
- typedef std::vector DynamicSymbolColl;
+ struct ELFDynamicWithName {
+ elf::ELFDynamic symbol;
+ std::string name;
+ };
+ typedef std::vector DynamicSymbolColl;
typedef DynamicSymbolColl::iterator DynamicSymbolCollIter;
typedef DynamicSymbolColl::const_iterator DynamicSymbolCollConstIter;
@@ -213,6 +217,10 @@ class ObjectFileELF : public lldb_private::ObjectFile {
/// Collection of section headers.
SectionHeaderColl m_section_headers;
+ /// The file address of the .dynamic section. This can be found in the p_vaddr
+ /// of the PT_DYNAMIC program header.
+ lldb::addr_t m_dynamic_base_addr = LLDB_INVALID_ADDRESS;
+
/// Collection of symbols from the dynamic table.
DynamicSymbolColl m_dynamic_symbols;
@@ -384,6 +392,9 @@ class ObjectFileELF : public lldb_private::ObjectFile {
/// ELF dependent module dump routine.
void DumpDependentModules(lldb_private::Stream *s);
+ /// ELF dump the .dynamic section
+ void DumpELFDynamic(lldb_private::Stream *s);
+
const elf::ELFDynamic *FindDynamicSymbol(unsigned tag);
unsigned PLTRelocationType();
@@ -402,6 +413,28 @@ class ObjectFileELF : public lldb_private::ObjectFile {
/// .gnu_debugdata section or \c nullptr if an error occured or if there's no
/// section with that name.
std::shared_ptr GetGnuDebugDataObjectFile();
+
+ /// Get the bytes that represent the .dynamic section.
+ ///
+ /// This function will fetch the data for the .dynamic section in an ELF file.
+ /// The PT_DYNAMIC program header will be used to extract the data and this
+ /// function will fall back to using the section headers if PT_DYNAMIC isn't
+ /// found.
+ ///
+ /// \return The bytes that represent the string table data or \c std::nullopt
+ /// if an error occured.
+ std::optional GetDynamicData();
+
+ /// Get the bytes that represent the dynamic string table data.
+ ///
+ /// This function will fetch the data for the string table in an ELF file. If
+ /// the ELF file is loaded from a file on disk, it will use the section
+ /// headers to extract the data and fall back to using the DT_STRTAB and
+ /// DT_STRSZ .dynamic entries.
+ ///
+ /// \return The bytes that represent the string table data or \c std::nullopt
+ /// if an error occured.
+ std::optional GetDynstrData();
};
#endif // LLDB_SOURCE_PLUGINS_OBJECTFILE_ELF_OBJECTFILEELF_H
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
index 0a52159d055bb4c..66a762bf9b68542 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.cpp
@@ -97,14 +97,12 @@ void DWARFUnit::ExtractUnitDIEIfNeeded() {
*m_dwo_id, m_first_die.GetOffset()));
return; // Can't fetch the compile unit from the dwo file.
}
-
- // Link the DWO unit to this object, if it hasn't been linked already (this
- // can happen when we have an index, and the DWO unit is parsed first).
- if (!dwo_cu->LinkToSkeletonUnit(*this)) {
- SetDwoError(Status::createWithFormat(
- "multiple compile units with Dwo ID {0:x16}", *m_dwo_id));
- return;
- }
+ // If the skeleton compile unit gets its unit DIE parsed first, then this
+ // will fill in the DWO file's back pointer to this skeleton compile unit.
+ // If the DWO files get parsed on their own first the skeleton back link
+ // can be done manually in DWARFUnit::GetSkeletonCompileUnit() which will
+ // do a reverse lookup and cache the result.
+ dwo_cu->SetSkeletonUnit(this);
DWARFBaseDIE dwo_cu_die = dwo_cu->GetUnitDIEOnly();
if (!dwo_cu_die.IsValid()) {
@@ -720,11 +718,13 @@ DWARFCompileUnit *DWARFUnit::GetSkeletonUnit() {
return llvm::dyn_cast_or_null(m_skeleton_unit);
}
-bool DWARFUnit::LinkToSkeletonUnit(DWARFUnit &skeleton_unit) {
- if (m_skeleton_unit && m_skeleton_unit != &skeleton_unit)
- return false;
- m_skeleton_unit = &skeleton_unit;
- return true;
+void DWARFUnit::SetSkeletonUnit(DWARFUnit *skeleton_unit) {
+ // If someone is re-setting the skeleton compile unit backlink, make sure
+ // it is setting it to a valid value when it wasn't valid, or if the
+ // value in m_skeleton_unit was valid, it should be the same value.
+ assert(skeleton_unit);
+ assert(m_skeleton_unit == nullptr || m_skeleton_unit == skeleton_unit);
+ m_skeleton_unit = skeleton_unit;
}
bool DWARFUnit::Supports_DW_AT_APPLE_objc_complete_type() {
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
index 209104fe3a054e7..85c37971ced8e0f 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFUnit.h
@@ -170,7 +170,7 @@ class DWARFUnit : public UserID {
/// both cases correctly and avoids crashes.
DWARFCompileUnit *GetSkeletonUnit();
- bool LinkToSkeletonUnit(DWARFUnit &skeleton_unit);
+ void SetSkeletonUnit(DWARFUnit *skeleton_unit);
bool Supports_DW_AT_APPLE_objc_complete_type();
diff --git a/lldb/source/Symbol/ObjectFile.cpp b/lldb/source/Symbol/ObjectFile.cpp
index 2608a9c5fb79a2f..35317d209de1f9a 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -454,9 +454,10 @@ AddressClass ObjectFile::GetAddressClass(addr_t file_addr) {
return AddressClass::eUnknown;
}
-DataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp,
- lldb::addr_t addr, size_t byte_size) {
- DataBufferSP data_sp;
+WritableDataBufferSP ObjectFile::ReadMemory(const ProcessSP &process_sp,
+ lldb::addr_t addr,
+ size_t byte_size) {
+ WritableDataBufferSP data_sp;
if (process_sp) {
std::unique_ptr data_up(new DataBufferHeap(byte_size, 0));
Status error;
diff --git a/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp b/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp
new file mode 100644
index 000000000000000..9cae6c99c9f7616
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/Inputs/memory-elf.cpp
@@ -0,0 +1,5 @@
+#include
+int main() {
+ printf("Hello World\n"); // Use something from libc.so
+ return 0;
+}
diff --git a/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml b/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml
new file mode 100644
index 000000000000000..e7b2f8d971c90b6
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/elf-dynamic-no-shdrs.yaml
@@ -0,0 +1,93 @@
+## This test verifies that loading an ELF file that has no section headers can
+## find the contents on the .dynamic section and the strings associated with
+## the .dynamic seciton.
+## - Loading the .dynamic section from the PT_DYNAMIC
+## This test will make a simple executable that links against libc.so and we
+## verify that we can find the DT_NEEDED entry with the shared library found
+## in the .dynamic dump from "image dump objfile"
+
+# RUN: yaml2obj %s -o %t
+
+# RUN: %lldb -b \
+# RUN: -o "target create -d '%t'" \
+# RUN: -o "image dump objfile" \
+# RUN: | FileCheck %s --dump-input=always
+# CHECK: (lldb) image dump objfile
+# CHECK: Dumping headers for 1 module(s).
+# CHECK: ObjectFileELF, file =
+# CHECK: ELF Header
+# Make sure there are no section headers
+# CHECK: e_shnum = 0x00000000
+
+# Make sure we find the program headers and see a PT_DYNAMIC entry.
+# CHECK: Program Headers
+# CHECK: IDX p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align
+# CHECK: ==== --------------- -------- -------- -------- -------- -------- ------------------------- --------
+# CHECK: [ 0] PT_LOAD 000000b0 00000000 00000000 00000170 00000170 00000000 ( ) 00000001
+# CHECK: [ 1] PT_DYNAMIC 000001b0 00000100 00000100 00000070 00000070 00000000 ( ) 00000008
+
+# CHECK: Dependent Modules:
+# CHECK: ccc
+# CHECK: aaa
+# CHECK: bbb
+
+# Make sure we see some sections created from the program headers
+# MAIN: SectID
+# MAIN: PT_LOAD[0]
+
+# CHECK: .dynamic:
+# CHECK: IDX d_tag d_val/d_ptr
+# CHECK: ==== ---------------- ------------------
+# CHECK: [ 0] STRTAB 0x0000000000000000
+# CHECK: [ 1] NEEDED 0x0000000000000009 "ccc"
+# CHECK: [ 2] NEEDED 0x0000000000000001 "aaa"
+# CHECK: [ 3] NEEDED 0x0000000000000005 "bbb"
+# CHECK: [ 4] STRSZ 0x0000000000000100
+# CHECK: [ 5] DEBUG 0x00000000deadbeef
+# CHECK: [ 6] NULL 0x0000000000000000
+
+# Make sure the ObjectFileELF::GetImageInfoAddress() works.
+# CHECK: image_info_address = 0x0000000000000158
+
+--- !ELF
+FileHeader:
+ Class: ELFCLASS64
+ Data: ELFDATA2LSB
+ Type: ET_EXEC
+ Machine: EM_X86_64
+Sections:
+ - Type: SectionHeaderTable
+ NoHeaders: true
+ - Name: .dynstr
+ Type: SHT_STRTAB
+ Flags: [ SHF_ALLOC ]
+ Content: '00616161006262620063636300' ## 0,a,a,a,0,b,b,b,0,c,c,c,0
+ Size: 0x100
+ - Name: .dynamic
+ Type: SHT_DYNAMIC
+ Address: 0x100
+ Entries:
+ - Tag: DT_STRTAB
+ Value: 0x0000000000000000
+ - Tag: DT_NEEDED
+ Value: 9
+ - Tag: DT_NEEDED
+ Value: 1
+ - Tag: DT_NEEDED
+ Value: 5
+ - Tag: DT_STRSZ
+ Value: 0x100
+ - Tag: DT_DEBUG
+ Value: 0xdeadbeef
+ - Tag: DT_NULL
+ Value: 0x0
+ProgramHeaders:
+ - Type: PT_LOAD
+ VAddr: 0x0
+ FirstSec: .dynstr
+ LastSec: .dynamic
+ - Type: PT_DYNAMIC
+ FirstSec: .dynamic
+ LastSec: .dynamic
+ VAddr: 0x100
+ Align: 0x8
diff --git a/lldb/test/Shell/ObjectFile/ELF/elf-memory.test b/lldb/test/Shell/ObjectFile/ELF/elf-memory.test
new file mode 100644
index 000000000000000..0b1c01486a4b431
--- /dev/null
+++ b/lldb/test/Shell/ObjectFile/ELF/elf-memory.test
@@ -0,0 +1,55 @@
+// REQUIRES: system-linux, native
+
+// This test verifies that loading an ELF file from memory works and the new
+// features that were added when loading from memory work like:
+// - Loading the .dynamic section from the PT_DYNAMIC since ELF files loaded
+// from memory don't have the section headers available.
+// This test will make a simple executable that:
+// - links against libc
+// - runs and stops at a breakpoint
+// - create a memory ELF file
+// - verify that "image dump objfile" will dump the dynamic section of the
+// memory elf file and find the .dynamic string table.
+
+// RUN: %clang_host %p/Inputs/memory-elf.cpp -g -O0 -o %t
+
+// RUN: %lldb %t -b \
+// RUN: -o "b main" \
+// RUN: -o "run" \
+// RUN: -o "script real_module = lldb.target.module[0]" \
+// RUN: -o "script base_addr = real_module.GetObjectFileHeaderAddress().GetLoadAddress(lldb.target)" \
+// RUN: -o "script mem_module = lldb.SBModule(lldb.process, base_addr)" \
+// RUN: -o "script target2 = lldb.debugger.CreateTarget('')" \
+// RUN: -o "script target2.AddModule(mem_module)" \
+// RUN: -o "target select 1" \
+// RUN: -o "image dump objfile" \
+// RUN: | FileCheck %s --check-prefix=MAIN --dump-input=always
+// MAIN: (lldb) image dump objfile
+// MAIN: Dumping headers for 1 module(s).
+// MAIN: ObjectFileELF, file = '', arch = {{.*, addr = 0x[0-9a-f]+}}
+// MAIN: ELF Header
+
+// Make sure we find the program headers and see a PT_DYNAMIC entry.
+// MAIN: Program Headers
+// MAIN: ] PT_DYNAMIC
+
+// Make sure we see some sections created from the program headers
+// MAIN: SectID
+// MAIN: PT_LOAD[0]
+
+// Ensure we find some dependent modules as won't find these if we aren't able
+// to load the .dynamic section from the PT_DYNAMIC program header.
+// MAIN: Dependent Modules:
+
+// Check for the .dynamic dump and ensure we find all dynamic entries that are
+// required to be there and needed to get the .dynstr section and the symbol
+// table, and the DT_DEBUG entry to find the list of shared libraries.
+// MAIN: .dynamic:
+// Make sure we found the .dynstr section by checking for valid strings after NEEDED
+// MAIN-DAG: NEEDED {{0x[0-9a-f]+ ".*libc.*"}}
+// MAIN-DAG: STRTAB {{0x[0-9a-f]+}}
+// MAIN-DAG: SYMTAB {{0x[0-9a-f]+}}
+// MAIN-DAG: STRSZ {{0x[0-9a-f]+}}
+// MAIN-DAG: SYMENT {{0x[0-9a-f]+}}
+// MAIN-DAG: DEBUG {{0x[0-9a-f]+}}
+// MAIN: NULL {{0x[0-9a-f]+}}
diff --git a/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-hash-collision.s b/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-hash-collision.s
deleted file mode 100644
index d626b4602ad58f4..000000000000000
--- a/lldb/test/Shell/SymbolFile/DWARF/x86/dwp-hash-collision.s
+++ /dev/null
@@ -1,142 +0,0 @@
-## Test that lldb handles (mainly, that it doesn't crash) the situation where
-## two skeleton compile units have the same DWO ID (and try to claim the same
-## split unit from the DWP file. This can sometimes happen when the compile unit
-## is nearly empty (e.g. because LTO has optimized all of it away).
-
-# RUN: llvm-mc -triple=x86_64-pc-linux -filetype=obj %s --defsym MAIN=0 > %t
-# RUN: llvm-mc -triple=x86_64-pc-linux -filetype=obj %s > %t.dwp
-# RUN: %lldb %t -o "image lookup -t my_enum_type" \
-# RUN: -o "image dump separate-debug-info" -o exit | FileCheck %s
-
-## Check that we're able to access the type within the split unit (no matter
-## which skeleton unit it ends up associated with). Completely ignoring the unit
-## might also be reasonable.
-# CHECK: image lookup -t my_enum_type
-# CHECK: 1 match found
-# CHECK: name = "my_enum_type", byte-size = 4, compiler_type = "enum my_enum_type {
-# CHECK-NEXT: }"
-#
-## Check that we get some indication of the error.
-# CHECK: image dump separate-debug-info
-# CHECK: Dwo ID Err Dwo Path
-# CHECK: 0xdeadbeefbaadf00d E multiple compile units with Dwo ID 0xdeadbeefbaadf00d
-
-.set DWO_ID, 0xdeadbeefbaadf00d
-
-## The main file.
-.ifdef MAIN
- .section .debug_abbrev,"",@progbits
- .byte 1 # Abbreviation Code
- .byte 74 # DW_TAG_compile_unit
- .byte 0 # DW_CHILDREN_no
- .byte 0x76 # DW_AT_dwo_name
- .byte 8 # DW_FORM_string
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 0 # EOM(3)
-
-
- .section .debug_info,"",@progbits
-.irpc I,01
-.Lcu_begin\I:
- .long .Ldebug_info_end\I-.Ldebug_info_start\I # Length of Unit
-.Ldebug_info_start\I:
- .short 5 # DWARF version number
- .byte 4 # DWARF Unit Type
- .byte 8 # Address Size (in bytes)
- .long .debug_abbrev # Offset Into Abbrev. Section
- .quad DWO_ID # DWO id
- .byte 1 # Abbrev [1] DW_TAG_compile_unit
- .ascii "foo"
- .byte '0' + \I
- .asciz ".dwo\0" # DW_AT_dwo_name
-.Ldebug_info_end\I:
-.endr
-
-.else
-## DWP file starts here.
-
- .section .debug_abbrev.dwo,"e",@progbits
-.LAbbrevBegin:
- .byte 1 # Abbreviation Code
- .byte 17 # DW_TAG_compile_unit
- .byte 1 # DW_CHILDREN_yes
- .byte 37 # DW_AT_producer
- .byte 8 # DW_FORM_string
- .byte 19 # DW_AT_language
- .byte 5 # DW_FORM_data2
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 2 # Abbreviation Code
- .byte 4 # DW_TAG_enumeration_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 8 # DW_FORM_string
- .byte 73 # DW_AT_type
- .byte 19 # DW_FORM_ref4
- .byte 11 # DW_AT_byte_size
- .byte 11 # DW_FORM_data1
- .byte 0 # EOM(1)
- .byte 0 # EOM(2)
- .byte 4 # Abbreviation Code
- .byte 36 # DW_TAG_base_type
- .byte 0 # DW_CHILDREN_no
- .byte 3 # DW_AT_name
- .byte 8 # DW_FORM_string
- .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 0 # EOM(3)
-.LAbbrevEnd:
- .section .debug_info.dwo,"e",@progbits
-.LCUBegin:
-.Lcu_begin1:
- .long .Ldebug_info_end1-.Ldebug_info_start1 # Length of Unit
-.Ldebug_info_start1:
- .short 5 # DWARF version number
- .byte 5 # DWARF Unit Type
- .byte 8 # Address Size (in bytes)
- .long 0 # Offset Into Abbrev. Section
- .quad DWO_ID # DWO id
- .byte 1 # Abbrev [1] DW_TAG_compile_unit
- .asciz "Hand-written DWARF" # DW_AT_producer
- .short 12 # DW_AT_language
- .byte 2 # Abbrev [2] DW_TAG_enumeration_type
- .asciz "my_enum_type" # DW_AT_name
- .long .Lint-.Lcu_begin1 # DW_AT_type
- .byte 4 # DW_AT_byte_size
-.Lint:
- .byte 4 # Abbrev [4] DW_TAG_base_type
- .asciz "int" # DW_AT_name
- .byte 5 # DW_AT_encoding
- .byte 4 # DW_AT_byte_size
- .byte 0 # End Of Children Mark
-.Ldebug_info_end1:
-.LCUEnd:
- .section .debug_cu_index, "", @progbits
-## Header:
- .short 5 # Version
- .short 0 # Padding
- .long 2 # Section count
- .long 1 # Unit count
- .long 2 # Slot count
-## Hash Table of Signatures:
- .quad 0
- .quad DWO_ID
-## Parallel Table of Indexes:
- .long 0
- .long 1
-## Table of Section Offsets:
-## Row 0:
- .long 1 # DW_SECT_INFO
- .long 3 # DW_SECT_ABBREV
-## Row 1:
- .long 0 # Offset in .debug_info.dwo
- .long 0 # Offset in .debug_abbrev.dwo
-## Table of Section Sizes:
- .long .LCUEnd-.LCUBegin # Size in .debug_info.dwo
- .long .LAbbrevEnd-.LAbbrevBegin # Size in .debug_abbrev.dwo
-.endif
diff --git a/lldb/tools/lldb-server/lldb-platform.cpp b/lldb/tools/lldb-server/lldb-platform.cpp
index 7148a1d2a30941f..82a3a0d6b4e51c0 100644
--- a/lldb/tools/lldb-server/lldb-platform.cpp
+++ b/lldb/tools/lldb-server/lldb-platform.cpp
@@ -22,6 +22,7 @@
#include
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
@@ -32,8 +33,10 @@
#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/HostGetOpt.h"
#include "lldb/Host/OptionParser.h"
+#include "lldb/Host/Socket.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Utility/FileSpec.h"
+#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Status.h"
using namespace lldb;
@@ -44,6 +47,108 @@ using namespace llvm;
// option descriptors for getopt_long_only()
+#ifdef _WIN32
+typedef pipe_t shared_fd_t;
+const shared_fd_t kInvalidSharedFD = LLDB_INVALID_PIPE;
+#else
+typedef NativeSocket shared_fd_t;
+const shared_fd_t kInvalidSharedFD = Socket::kInvalidSocketValue;
+#endif
+
+class SharedSocket {
+public:
+ SharedSocket(Connection *conn, Status &error) {
+ m_fd = kInvalidSharedFD;
+
+ const Socket *socket =
+ static_cast(conn->GetReadObject().get());
+ if (socket == nullptr) {
+ error = Status("invalid conn socket");
+ return;
+ }
+
+#ifdef _WIN32
+ m_socket = socket->GetNativeSocket();
+
+ // Create a pipe to transfer WSAPROTOCOL_INFO to the child process.
+ error = m_socket_pipe.CreateNew(true);
+ if (error.Fail())
+ return;
+
+ m_fd = m_socket_pipe.GetReadPipe();
+#else
+ m_fd = socket->GetNativeSocket();
+ error = Status();
+#endif
+ }
+
+ shared_fd_t GetSendableFD() { return m_fd; }
+
+ Status CompleteSending(lldb::pid_t child_pid) {
+#ifdef _WIN32
+ // Transfer WSAPROTOCOL_INFO to the child process.
+ m_socket_pipe.CloseReadFileDescriptor();
+
+ WSAPROTOCOL_INFO protocol_info;
+ if (::WSADuplicateSocket(m_socket, child_pid, &protocol_info) ==
+ SOCKET_ERROR) {
+ int last_error = ::WSAGetLastError();
+ return Status("WSADuplicateSocket() failed, error: %d", last_error);
+ }
+
+ size_t num_bytes;
+ Status error =
+ m_socket_pipe.WriteWithTimeout(&protocol_info, sizeof(protocol_info),
+ std::chrono::seconds(10), num_bytes);
+ if (error.Fail())
+ return error;
+ if (num_bytes != sizeof(protocol_info))
+ return Status("WriteWithTimeout(WSAPROTOCOL_INFO) failed: %d bytes",
+ num_bytes);
+#endif
+ return Status();
+ }
+
+ static Status GetNativeSocket(shared_fd_t fd, NativeSocket &socket) {
+#ifdef _WIN32
+ socket = Socket::kInvalidSocketValue;
+ // Read WSAPROTOCOL_INFO from the parent process and create NativeSocket.
+ WSAPROTOCOL_INFO protocol_info;
+ {
+ Pipe socket_pipe(fd, LLDB_INVALID_PIPE);
+ size_t num_bytes;
+ Status error =
+ socket_pipe.ReadWithTimeout(&protocol_info, sizeof(protocol_info),
+ std::chrono::seconds(10), num_bytes);
+ if (error.Fail())
+ return error;
+ if (num_bytes != sizeof(protocol_info)) {
+ return Status(
+ "socket_pipe.ReadWithTimeout(WSAPROTOCOL_INFO) failed: % d bytes",
+ num_bytes);
+ }
+ }
+ socket = ::WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO, &protocol_info, 0, 0);
+ if (socket == INVALID_SOCKET) {
+ return Status("WSASocket(FROM_PROTOCOL_INFO) failed: error %d",
+ ::WSAGetLastError());
+ }
+ return Status();
+#else
+ socket = fd;
+ return Status();
+#endif
+ }
+
+private:
+#ifdef _WIN32
+ Pipe m_socket_pipe;
+ NativeSocket m_socket;
+#endif
+ shared_fd_t m_fd;
+};
+
static int g_debug = 0;
static int g_verbose = 0;
static int g_server = 0;
@@ -60,6 +165,7 @@ static struct option g_long_options[] = {
{"max-gdbserver-port", required_argument, nullptr, 'M'},
{"socket-file", required_argument, nullptr, 'f'},
{"server", no_argument, &g_server, 1},
+ {"child-platform-fd", required_argument, nullptr, 2},
{nullptr, 0, nullptr, 0}};
#if defined(__APPLE__)
@@ -114,6 +220,130 @@ static Status save_socket_id_to_file(const std::string &socket_id,
return status;
}
+static void client_handle(GDBRemoteCommunicationServerPlatform &platform,
+ const lldb_private::Args &args) {
+ if (!platform.IsConnected())
+ return;
+
+ if (args.GetArgumentCount() > 0) {
+ lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
+ std::optional port;
+ std::string socket_name;
+ Status error = platform.LaunchGDBServer(args,
+ "", // hostname
+ pid, port, socket_name);
+ if (error.Success())
+ platform.SetPendingGdbServer(pid, *port, socket_name);
+ else
+ fprintf(stderr, "failed to start gdbserver: %s\n", error.AsCString());
+ }
+
+ bool interrupt = false;
+ bool done = false;
+ Status error;
+ while (!interrupt && !done) {
+ if (platform.GetPacketAndSendResponse(std::nullopt, error, interrupt,
+ done) !=
+ GDBRemoteCommunication::PacketResult::Success)
+ break;
+ }
+
+ printf("Disconnected.\n");
+}
+
+static GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
+static std::mutex gdbserver_portmap_mutex;
+
+static void spawn_process_reaped(lldb::pid_t pid, int signal, int status) {
+ std::lock_guard guard(gdbserver_portmap_mutex);
+ gdbserver_portmap.FreePortForProcess(pid);
+}
+
+static Status spawn_process(const char *progname, Connection *conn,
+ uint16_t gdb_port, uint16_t port_offset,
+ const lldb_private::Args &args,
+ const std::string &log_file,
+ const StringRef log_channels) {
+ Status error;
+ SharedSocket shared_socket(conn, error);
+ if (error.Fail())
+ return error;
+
+ ProcessLaunchInfo launch_info;
+
+ FileSpec self_spec(progname, FileSpec::Style::native);
+ launch_info.SetExecutableFile(self_spec, true);
+ Args &self_args = launch_info.GetArguments();
+ self_args.AppendArgument(llvm::StringRef("platform"));
+ self_args.AppendArgument(llvm::StringRef("--child-platform-fd"));
+ self_args.AppendArgument(llvm::to_string(shared_socket.GetSendableFD()));
+ if (gdb_port) {
+ self_args.AppendArgument(llvm::StringRef("--gdbserver-port"));
+ self_args.AppendArgument(llvm::to_string(gdb_port));
+ }
+ if (port_offset > 0) {
+ self_args.AppendArgument(llvm::StringRef("--port-offset"));
+ self_args.AppendArgument(llvm::to_string(port_offset));
+ }
+ if (!log_file.empty()) {
+ self_args.AppendArgument(llvm::StringRef("--log-file"));
+ self_args.AppendArgument(log_file);
+ }
+ if (!log_channels.empty()) {
+ self_args.AppendArgument(llvm::StringRef("--log-channels"));
+ self_args.AppendArgument(log_channels);
+ }
+ if (args.GetArgumentCount() > 0) {
+ self_args.AppendArgument("--");
+ self_args.AppendArguments(args);
+ }
+
+ launch_info.SetLaunchInSeparateProcessGroup(false);
+ launch_info.SetMonitorProcessCallback(&spawn_process_reaped);
+
+ // Copy the current environment.
+ launch_info.GetEnvironment() = Host::GetEnvironment();
+
+ launch_info.GetFlags().Set(eLaunchFlagDisableSTDIO);
+
+ // Close STDIN, STDOUT and STDERR.
+ launch_info.AppendCloseFileAction(STDIN_FILENO);
+ launch_info.AppendCloseFileAction(STDOUT_FILENO);
+ launch_info.AppendCloseFileAction(STDERR_FILENO);
+
+ // Redirect STDIN, STDOUT and STDERR to "/dev/null".
+ launch_info.AppendSuppressFileAction(STDIN_FILENO, true, false);
+ launch_info.AppendSuppressFileAction(STDOUT_FILENO, false, true);
+ launch_info.AppendSuppressFileAction(STDERR_FILENO, false, true);
+
+ std::string cmd;
+ self_args.GetCommandString(cmd);
+
+ error = Host::LaunchProcess(launch_info);
+ if (error.Fail())
+ return error;
+
+ lldb::pid_t child_pid = launch_info.GetProcessID();
+ if (child_pid == LLDB_INVALID_PROCESS_ID)
+ return Status("invalid pid");
+
+ LLDB_LOG(GetLog(LLDBLog::Platform), "lldb-platform launched '{0}', pid={1}",
+ cmd, child_pid);
+
+ {
+ std::lock_guard guard(gdbserver_portmap_mutex);
+ gdbserver_portmap.AssociatePortWithProcess(gdb_port, child_pid);
+ }
+
+ error = shared_socket.CompleteSending(child_pid);
+ if (error.Fail()) {
+ Host::Kill(child_pid, SIGTERM);
+ return error;
+ }
+
+ return Status();
+}
+
// main
int main_platform(int argc, char *argv[]) {
const char *progname = argv[0];
@@ -133,7 +363,8 @@ int main_platform(int argc, char *argv[]) {
StringRef
log_channels; // e.g. "lldb process threads:gdb-remote default:linux all"
- GDBRemoteCommunicationServerPlatform::PortMap gdbserver_portmap;
+ shared_fd_t fd = kInvalidSharedFD;
+
int min_gdbserver_port = 0;
int max_gdbserver_port = 0;
uint16_t port_offset = 0;
@@ -217,6 +448,15 @@ int main_platform(int argc, char *argv[]) {
max_gdbserver_port = portnum;
} break;
+ case 2: {
+ uint64_t _fd;
+ if (!llvm::to_integer(optarg, _fd)) {
+ WithColor::error() << "invalid fd " << optarg << "\n";
+ option_error = 6;
+ } else
+ fd = (shared_fd_t)_fd;
+ } break;
+
case 'h': /* fall-through is intentional */
case '?':
show_usage = true;
@@ -240,7 +480,7 @@ int main_platform(int argc, char *argv[]) {
}
// Print usage and exit if no listening port is specified.
- if (listen_host_port.empty())
+ if (listen_host_port.empty() && fd == kInvalidSharedFD)
show_usage = true;
if (show_usage || option_error) {
@@ -254,6 +494,33 @@ int main_platform(int argc, char *argv[]) {
lldb_private::Args inferior_arguments;
inferior_arguments.SetArguments(argc, const_cast(argv));
+ if (fd != kInvalidSharedFD) {
+ // Child process will handle the connection and exit.
+ Log *log = GetLog(LLDBLog::Platform);
+ if (!listen_host_port.empty()) {
+ LLDB_LOGF(log, "lldb-platform child: "
+ "ambiguous parameters --listen and --child-platform-fd");
+ return socket_error;
+ }
+
+ NativeSocket socket;
+ error = SharedSocket::GetNativeSocket(fd, socket);
+ if (error.Fail()) {
+ LLDB_LOGF(log, "lldb-platform child: %s", error.AsCString());
+ return socket_error;
+ }
+
+ Connection *conn =
+ new ConnectionFileDescriptor(new TCPSocket(socket, true, false));
+ GDBRemoteCommunicationServerPlatform platform(Socket::ProtocolTcp, "tcp");
+ if (port_offset > 0)
+ platform.SetPortOffset(port_offset);
+ platform.SetPortMap(std::move(gdbserver_portmap));
+ platform.SetConnection(std::unique_ptr(conn));
+ client_handle(platform, inferior_arguments);
+ return 0;
+ }
+
const bool children_inherit_listen_socket = false;
// the test suite makes many connections in parallel, let's not miss any.
// The highest this should get reasonably is a function of the number
@@ -298,48 +565,38 @@ int main_platform(int argc, char *argv[]) {
printf("Connection established.\n");
if (g_server) {
- // Collect child zombie processes.
-#if !defined(_WIN32)
- ::pid_t waitResult;
- while ((waitResult = waitpid(-1, nullptr, WNOHANG)) > 0) {
- // waitResult is the child pid
- gdbserver_portmap.FreePortForProcess(waitResult);
+ std::optional available_port;
+ {
+ std::lock_guard guard(gdbserver_portmap_mutex);
+ auto port = gdbserver_portmap.GetNextAvailablePort();
+ if (port)
+ available_port = *port;
+ else
+ llvm::consumeError(port.takeError());
}
-#endif
- // TODO: Clean up portmap for Windows when children die
- // See https://github.com/llvm/llvm-project/issues/90923
-
- // After collecting zombie ports, get the next available
- GDBRemoteCommunicationServerPlatform::PortMap portmap_for_child;
- llvm::Expected available_port =
- gdbserver_portmap.GetNextAvailablePort();
- if (available_port) {
- // GetNextAvailablePort() may return 0 if gdbserver_portmap is empty.
- if (*available_port)
- portmap_for_child.AllowPort(*available_port);
- } else {
- llvm::consumeError(available_port.takeError());
+ if (!available_port) {
fprintf(stderr,
"no available gdbserver port for connection - dropping...\n");
- delete conn;
- continue;
- }
- platform.SetPortMap(std::move(portmap_for_child));
-
- auto childPid = fork();
- if (childPid) {
- gdbserver_portmap.AssociatePortWithProcess(*available_port, childPid);
- // Parent doesn't need a connection to the lldb client
- delete conn;
-
- // Parent will continue to listen for new connections.
- continue;
} else {
- // Child process will handle the connection and exit.
- g_server = 0;
- // Listening socket is owned by parent process.
- acceptor_up.release();
+ error = spawn_process(progname, conn, *available_port, port_offset,
+ inferior_arguments, log_file, log_channels);
+ if (error.Fail()) {
+ {
+
+ std::lock_guard guard(gdbserver_portmap_mutex);
+ gdbserver_portmap.FreePort(*available_port);
+ }
+ LLDB_LOGF(GetLog(LLDBLog::Platform), "spawn_process failed: %s",
+ error.AsCString());
+ WithColor::error()
+ << "spawn_process failed: " << error.AsCString() << "\n";
+ }
}
+ // Parent doesn't need a connection to the lldb client
+ delete conn;
+
+ // Parent will continue to listen for new connections.
+ continue;
} else {
// If not running as a server, this process will not accept
// connections while a connection is active.
@@ -350,33 +607,7 @@ int main_platform(int argc, char *argv[]) {
}
platform.SetConnection(std::unique_ptr(conn));
-
- if (platform.IsConnected()) {
- if (inferior_arguments.GetArgumentCount() > 0) {
- lldb::pid_t pid = LLDB_INVALID_PROCESS_ID;
- std::optional