Skip to content

Conversation

@cabbaken
Copy link
Contributor

@cabbaken cabbaken commented Dec 17, 2025

This patch fix memory leak introduced in #165661.

The call to DummyTarget->createMCSubtargetInfo within mcpuHelp() returns a pointer that is not subsequently freed, leading to a memory leak.
Use std::unique_ptr to ensure the memory is released automatically.

@llvmbot
Copy link
Member

llvmbot commented Dec 17, 2025

@llvm/pr-subscribers-llvm-binary-utilities

Author: Ruoyu Qiu (cabbaken)

Changes

This patch fix memory leak introduced in #165661


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

4 Files Affected:

  • (modified) llvm/docs/ReleaseNotes.md (+3)
  • (modified) llvm/test/tools/llvm-objdump/mattr-mcpu-help.test (+97-4)
  • (added) llvm/test/tools/llvm-objdump/mattr-mcpu-triple-help.test (+21)
  • (modified) llvm/tools/llvm-objdump/llvm-objdump.cpp (+73-10)
diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index 4406a9d45c97e..503cf641a221f 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -246,6 +246,9 @@ Changes to the LLVM tools
   emitted on stdout, to account for spaces or other special characters in path.
   (`#97305 <https://github.com/llvm/llvm-project/pull/97305>`_).
 
+* `llvm-objdump` now supports using `--mcpu=help` and `--mattr=help` with the `--triple` option
+  without requiring an input file or the `-d` (disassemble) flag.
+
 Changes to LLDB
 ---------------------------------
 
diff --git a/llvm/test/tools/llvm-objdump/mattr-mcpu-help.test b/llvm/test/tools/llvm-objdump/mattr-mcpu-help.test
index 65c426008fd6a..7aeaf66bab1ee 100644
--- a/llvm/test/tools/llvm-objdump/mattr-mcpu-help.test
+++ b/llvm/test/tools/llvm-objdump/mattr-mcpu-help.test
@@ -1,12 +1,40 @@
-# RUN: yaml2obj %s -o %t
-# RUN: llvm-objdump -d %t --mattr=help 2>&1 | FileCheck %s
-# RUN: llvm-objdump -d %t --mcpu=help 2>&1 | FileCheck %s
 # REQUIRES: x86-registered-target
 
+# RUN: yaml2obj --docnum=1 %s -o %t
+# RUN: llvm-objdump -d %t --mattr=help 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-DISASSEMBLE
+# RUN: llvm-objdump -d %t --mcpu=help 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-DISASSEMBLE
+
 # CHECK: Available CPUs for this target:
 # CHECK: Available features for this target:
 ## To check we still disassemble the file:
-# CHECK: file format elf64-x86-64
+# CHECK-DISASSEMBLE: file format elf64-x86-64
+
+## The help message can be printed without -d.
+# RUN: llvm-objdump --mattr=help %t 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-NO-DISASSEMBLE
+# RUN: llvm-objdump --mcpu=help %t 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-NO-DISASSEMBLE
+
+# CHECK-NO-DISASSEMBLE-NOT: file format elf64-x86-64
+
+## We still handle other options.
+# RUN: llvm-objdump --mattr=help --section-headers %t 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-SECTION-HEADERS
+# RUN: llvm-objdump --mcpu=help --section-headers %t 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK,CHECK-SECTION-HEADERS
+
+# CHECK-SECTION-HEADERS: Sections:
+# CHECK-SECTION-HEADERS: Idx Name               Size     VMA              Type
+
+## We report an error when we can't infer the triple because we don't have --triple or an input object (including a.out).
+# RUN: not llvm-objdump --mattr=help 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=CHECK-MISSING-ERROR --implicit-check-not=error: -DMSG=%errc_ENOENT
+# RUN: not llvm-objdump --mcpu=help 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=CHECK-MISSING-ERROR --implicit-check-not=error: -DMSG=%errc_ENOENT
+
+# CHECK-MISSING-ERROR: llvm-objdump{{.*}}: error: 'a.out': triple was not specified and could not be inferred from the input file: [[MSG]]
 
 --- !ELF
 FileHeader:
@@ -14,3 +42,68 @@ FileHeader:
   Data:            ELFDATA2LSB
   Type:            ET_EXEC
   Machine:         EM_X86_64
+
+# RUN: yaml2obj --docnum=2 %s -o %t.minidump
+## We report an error when the binary file isn't an object file format.
+# RUN: not llvm-objdump --mattr=help %t.minidump 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=CHECK-UNSUPPORTED-ERROR --implicit-check-not=error: -DFILE=%t.minidump
+# RUN: not llvm-objdump --mcpu=help %t.minidump 2>&1 \
+# RUN:   | FileCheck %s --check-prefix=CHECK-UNSUPPORTED-ERROR --implicit-check-not=error: -DFILE=%t.minidump
+
+# CHECK-UNSUPPORTED-ERROR: llvm-objdump{{.*}}: error: '[[FILE]]': target triple could not be derived from input file
+
+--- !minidump
+Streams:
+  - Type:            SystemInfo
+    Processor Arch:  PPC
+    Platform ID:     Linux
+
+# RUN: llvm-ar rc %t.a %t
+# RUN: llvm-objdump --mcpu=help %t.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK
+# RUN: llvm-objdump --mattr=help %t.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK
+
+## We report an error when the triple cannot be inferred from any archive member.
+# RUN: llvm-ar rc %t.empty.a
+# RUN: not llvm-objdump --mcpu=help %t.empty.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-UNSUPPORTED-ERROR --implicit-check-not=error: -DFILE=%t.empty.a
+# RUN: not llvm-objdump --mattr=help %t.empty.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-UNSUPPORTED-ERROR --implicit-check-not=error: -DFILE=%t.empty.a
+
+## We are able to handle an archive with unsupported binary files. The target is
+## derived from the first recognised object file.
+# RUN: llvm-ar rc %t.a %t.minidump %t
+# RUN: llvm-objdump --mcpu=help %t.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK
+# RUN: llvm-objdump --mattr=help %t.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK
+
+## We report an error when the archive is malformed.
+# RUN: yaml2obj --docnum=3 %s -o %t.malformed.archive.a
+# RUN: not llvm-objdump --mcpu=help %t.malformed.archive.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-MALFORMED-ARCHIVE-ERROR --implicit-check-not=error: -DFILE=%t.malformed.archive.a
+# RUN: not llvm-objdump --mattr=help %t.malformed.archive.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-MALFORMED-ARCHIVE-ERROR --implicit-check-not=error: -DFILE=%t.malformed.archive.a
+
+# CHECK-MALFORMED-ARCHIVE-ERROR: llvm-objdump{{.*}}: error: '[[FILE]]': truncated or malformed archive
+
+--- !Arch
+Members:
+  - Name: 'foo.c'
+    Size: '1'
+
+## We report an error when we encounter an unexpected error while iterating files in an archive.
+# RUN: yaml2obj --docnum=4 %s -o %t.invalid.error.code.archive.a
+# RUN: not llvm-objdump --mcpu=help %t.invalid.error.code.archive.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-INVALID-ERROR-ARCHIVE-ERROR --implicit-check-not=error: -DFILE=%t.invalid.error.code.archive.a
+# RUN: not llvm-objdump --mattr=help %t.invalid.error.code.archive.a 2>&1 \
+# RUN:   | FileCheck %s --check-prefixes=CHECK-INVALID-ERROR-ARCHIVE-ERROR --implicit-check-not=error: -DFILE=%t.invalid.error.code.archive.a
+
+# CHECK-INVALID-ERROR-ARCHIVE-ERROR: llvm-objdump{{.*}}: error: [[FILE]](<file index: 1>): truncated or malformed archive (long name offset characters after the '/' are not all decimal numbers: '&a25*' for archive member header at offset 68)
+
+--- !Arch
+Members:
+## We need the first member to be a valid member to trigger the right error.
+  - Name: 'hello.c/'
+  - Name: "/&a25*"
diff --git a/llvm/test/tools/llvm-objdump/mattr-mcpu-triple-help.test b/llvm/test/tools/llvm-objdump/mattr-mcpu-triple-help.test
new file mode 100644
index 0000000000000..1212773afc35a
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/mattr-mcpu-triple-help.test
@@ -0,0 +1,21 @@
+# RUN: yaml2obj %s -o %t
+# REQUIRES: x86-registered-target
+
+## When specifying --triple, the help message should be printed for the specified target.
+# RUN: llvm-objdump --mattr=help --triple=x86_64 2>&1 | FileCheck %s
+# RUN: llvm-objdump --mcpu=help --triple=x86_64 2>&1 | FileCheck %s
+
+## The help message will depend on the target specified by --triple even if the input is for a different target.
+# RUN: llvm-objdump --mattr=help --triple=x86_64 %t 2>&1 | FileCheck %s
+# RUN: llvm-objdump --mcpu=help --triple=x86_64 %t 2>&1 | FileCheck %s
+
+# CHECK: Available CPUs for this target:
+# CHECK: x86-64
+# CHECK: Available features for this target:
+
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS32
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_MIPS
diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 38c3f31441c06..5e816f054e74b 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -3533,6 +3533,58 @@ commaSeparatedValues(const llvm::opt::InputArgList &InputArgs, int ID) {
   return Values;
 }
 
+static void mcpuHelp() {
+  Triple TheTriple;
+
+  if (!TripleName.empty()) {
+    TheTriple.setTriple(TripleName);
+  } else {
+    assert(!InputFilenames.empty());
+    Expected<OwningBinary<Binary>> OBinary = createBinary(InputFilenames[0]);
+    if (Error E = OBinary.takeError()) {
+      reportError(InputFilenames[0], "triple was not specified and could not "
+                                     "be inferred from the input file: " +
+                                         toString(std::move(E)));
+    }
+
+    Binary *Bin = OBinary->getBinary();
+    if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin)) {
+      TheTriple = Obj->makeTriple();
+    } else if (Archive *A = dyn_cast<Archive>(Bin)) {
+      Error Err = Error::success();
+      unsigned I = -1;
+      for (auto &C : A->children(Err)) {
+        ++I;
+        Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
+        if (!ChildOrErr) {
+          if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
+            reportError(std::move(E), getFileNameForError(C, I),
+                        A->getFileName());
+          continue;
+        }
+        if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
+          TheTriple = Obj->makeTriple();
+          break;
+        }
+      }
+      if (Err)
+        reportError(std::move(Err), A->getFileName());
+    }
+    if (TheTriple.empty())
+      reportError(InputFilenames[0],
+                  "target triple could not be derived from input file");
+  }
+
+  std::string ErrMessage;
+  const Target *DummyTarget =
+      TargetRegistry::lookupTarget(TheTriple, ErrMessage);
+  if (!DummyTarget)
+    reportCmdLineError(ErrMessage);
+  // We need to access the Help() through the corresponding MCSubtargetInfo.
+  std::unique_ptr<MCSubtargetInfo> MSI(
+      DummyTarget->createMCSubtargetInfo(TheTriple, "help", ""));
+}
+
 static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) {
   MachOOpt = true;
   FullLeadingAddr = true;
@@ -3826,20 +3878,31 @@ int llvm_objdump_main(int argc, char **argv, const llvm::ToolContext &) {
       !DisassembleSymbols.empty())
     Disassemble = true;
 
-  if (!ArchiveHeaders && !Disassemble && DwarfDumpType == DIDT_Null &&
-      !DynamicRelocations && !FileHeaders && !PrivateHeaders && !RawClangAST &&
-      !Relocations && !SectionHeaders && !SectionContents && !SymbolTable &&
-      !DynamicSymbolTable && !UnwindInfo && !FaultMapSection && !Offloading &&
-      !(MachOOpt &&
-        (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId ||
-         DylibsUsed || ExportsTrie || FirstPrivateHeader ||
-         FunctionStartsType != FunctionStartsMode::None || IndirectSymbols ||
-         InfoPlist || LazyBind || LinkOptHints || ObjcMetaData || Rebase ||
-         Rpaths || UniversalHeaders || WeakBind || !FilterSections.empty()))) {
+  const bool PrintCpuHelp = (MCPU == "help" || is_contained(MAttrs, "help"));
+
+  const bool ShouldDump =
+      ArchiveHeaders || Disassemble || DwarfDumpType != DIDT_Null ||
+      DynamicRelocations || FileHeaders || PrivateHeaders || RawClangAST ||
+      Relocations || SectionHeaders || SectionContents || SymbolTable ||
+      DynamicSymbolTable || UnwindInfo || FaultMapSection || Offloading ||
+      (MachOOpt &&
+       (Bind || DataInCode || ChainedFixups || DyldInfo || DylibId ||
+        DylibsUsed || ExportsTrie || FirstPrivateHeader ||
+        FunctionStartsType != FunctionStartsMode::None || IndirectSymbols ||
+        InfoPlist || LazyBind || LinkOptHints || ObjcMetaData || Rebase ||
+        Rpaths || UniversalHeaders || WeakBind || !FilterSections.empty()));
+
+  if (!ShouldDump && !PrintCpuHelp) {
     T->printHelp(ToolName);
     return 2;
   }
 
+  if (PrintCpuHelp) {
+    mcpuHelp();
+    if (!ShouldDump)
+      return EXIT_SUCCESS;
+  }
+
   DisasmSymbolSet.insert_range(DisassembleSymbols);
 
   llvm::for_each(InputFilenames, dumpInput);

@cabbaken
Copy link
Contributor Author

@jh7370
Apologies for the oversight in my previous PR. I have addressed the memory leak in this update. Could you please review the fix?
To be brief, the only change is:

diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 97032ec..5e816f0 100644
--- a/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -3581,7 +3581,8 @@ static void mcpuHelp() {
   if (!DummyTarget)
     reportCmdLineError(ErrMessage);
   // We need to access the Help() through the corresponding MCSubtargetInfo.
-  DummyTarget->createMCSubtargetInfo(TheTriple, "help", "");
+  std::unique_ptr<MCSubtargetInfo> MSI(
+      DummyTarget->createMCSubtargetInfo(TheTriple, "help", ""));
 }
 
 static void parseOtoolOptions(const llvm::opt::InputArgList &InputArgs) {

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

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

Looks fine to me, with one addition.

TargetRegistry::lookupTarget(TheTriple, ErrMessage);
if (!DummyTarget)
reportCmdLineError(ErrMessage);
// We need to access the Help() through the corresponding MCSubtargetInfo.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd expand this comment to mention why you are wrapping the result in a unique_ptr, e.g. "To avoid a memory leak, we wrap the createMcSubtargetInfo result in a unique_ptr."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for your reply! I added comment in the latest commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants