[llvm] Improve llvm-gsymutil to set the end address correctly. (PR #225593)
Greg Clayton via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 21:58:39 PDT 2026
https://github.com/clayborg created https://github.com/llvm/llvm-project/pull/225593
When converting DWARF in llvm-gsymutil, if the last function info had no size, it would set it to the end of the valid text ranges. This meant a symbol could get a larger than necessary size in mach-o files. This patch will set the address to the minimum of the last TextRanges or the end of the section that contains the symbol.
>From 909077f69de1875a521a0fea82254b05555df2e4 Mon Sep 17 00:00:00 2001
From: Greg Clayton <clayborg at gmail.com>
Date: Tue, 22 Sep 2026 21:52:56 -0700
Subject: [PATCH] Improve llvm-gsymutil to set the end address correctly.
When converting DWARF in llvm-gsymutil, if the last function info had no size, it would set it to the end of the valid text ranges. This meant a symbol could get a larger than necessary size in mach-o files. This patch will set the address to the minimum of the last TextRanges or the end of the section that contains the symbol.
---
.../include/llvm/DebugInfo/GSYM/GsymCreator.h | 14 +-
llvm/lib/DebugInfo/GSYM/GsymCreator.cpp | 37 ++++-
.../X86/macho-last-symbol-section-end.yaml | 127 ++++++++++++++++++
llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp | 6 +-
4 files changed, 177 insertions(+), 7 deletions(-)
create mode 100644 llvm/test/tools/llvm-gsymutil/X86/macho-last-symbol-section-end.yaml
diff --git a/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h b/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h
index 22d9096f1107ef..87df07b62c022d 100644
--- a/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h
+++ b/llvm/include/llvm/DebugInfo/GSYM/GsymCreator.h
@@ -27,6 +27,10 @@
namespace llvm {
+namespace object {
+class ObjectFile;
+}
+
namespace gsym {
class FileWriter;
class OutputAggregator;
@@ -415,9 +419,17 @@ class GsymCreator {
///
/// \param OS Output stream to report duplicate function infos, overlapping
/// function infos, and function infos that were merged or removed.
+ /// \param Obj An optional object file that the function infos were created
+ /// from. The last function info often has no size, and its size gets
+ /// filled in from the valid text ranges. A valid text range can span
+ /// more than one section, so the object file is used to find the
+ /// section that contains the function and keep the size from
+ /// extending past the end of that section. If no object file is
+ /// supplied the size is filled in from the valid text ranges alone.
/// \returns An error object that indicates success or failure of the
/// finalize.
- LLVM_ABI llvm::Error finalize(OutputAggregator &OS);
+ LLVM_ABI llvm::Error finalize(OutputAggregator &OS,
+ const object::ObjectFile *Obj = nullptr);
/// Set the UUID value.
///
diff --git a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp
index c7d1d0e91ac016..af0dbb3337098c 100644
--- a/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp
+++ b/llvm/lib/DebugInfo/GSYM/GsymCreator.cpp
@@ -12,6 +12,7 @@
#include "llvm/DebugInfo/GSYM/LineTable.h"
#include "llvm/DebugInfo/GSYM/OutputAggregator.h"
#include "llvm/MC/StringTableBuilder.h"
+#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
@@ -155,7 +156,25 @@ void GsymCreator::prepareMergedFunctions(OutputAggregator &Out) {
std::swap(Funcs, TopLevelFuncs);
}
-llvm::Error GsymCreator::finalize(OutputAggregator &Out) {
+/// Find the end address of the section that contains \a Addr.
+///
+/// \returns The address of the first byte past the end of the section that
+/// contains \a Addr, or std::nullopt if no section contains \a Addr.
+static std::optional<uint64_t>
+getSectionEndAddress(const object::ObjectFile &Obj, uint64_t Addr) {
+ for (const object::SectionRef &Sect : Obj.sections()) {
+ const uint64_t SectSize = Sect.getSize();
+ if (SectSize == 0)
+ continue;
+ const uint64_t SectAddr = Sect.getAddress();
+ if (Addr >= SectAddr && Addr < SectAddr + SectSize)
+ return SectAddr + SectSize;
+ }
+ return std::nullopt;
+}
+
+llvm::Error GsymCreator::finalize(OutputAggregator &Out,
+ const object::ObjectFile *Obj) {
std::lock_guard<std::mutex> Guard(Mutex);
if (Finalized)
return createStringError(std::errc::invalid_argument, "already finalized");
@@ -265,9 +284,19 @@ llvm::Error GsymCreator::finalize(OutputAggregator &Out) {
// help ensure we don't cause lookups to always return the last symbol that
// has no size when doing lookups.
if (!Funcs.empty() && Funcs.back().Range.size() == 0 && ValidTextRanges) {
- if (auto Range =
- ValidTextRanges->getRangeThatContains(Funcs.back().Range.start())) {
- Funcs.back().Range = {Funcs.back().Range.start(), Range->end()};
+ const uint64_t StartAddr = Funcs.back().Range.start();
+ if (auto Range = ValidTextRanges->getRangeThatContains(StartAddr)) {
+ uint64_t EndAddr = Range->end();
+ // A valid text range can be made up of more than one section, so
+ // stopping at the end of the range can make the function extend past
+ // the end of the section that it actually lives in. Limit the size to
+ // the end of the containing section when we have an object file to
+ // look the section up in.
+ if (Obj) {
+ if (auto SectEndAddr = getSectionEndAddress(*Obj, StartAddr))
+ EndAddr = std::min(EndAddr, *SectEndAddr);
+ }
+ Funcs.back().Range = {StartAddr, EndAddr};
}
}
Out << "Pruned " << NumBefore - Funcs.size() << " functions, ended with "
diff --git a/llvm/test/tools/llvm-gsymutil/X86/macho-last-symbol-section-end.yaml b/llvm/test/tools/llvm-gsymutil/X86/macho-last-symbol-section-end.yaml
new file mode 100644
index 00000000000000..2b9571aa1ba2e2
--- /dev/null
+++ b/llvm/test/tools/llvm-gsymutil/X86/macho-last-symbol-section-end.yaml
@@ -0,0 +1,127 @@
+## Test that the last function info, which has no size, does not get a size
+## that extends past the end of the section that contains it.
+##
+## Mach-O symbols have no size, so the size of the last function info gets
+## filled in during GsymCreator::finalize() using the valid text ranges. This
+## file has two adjacent code sections, __text [0x100000f90 - 0x100000fb0) and
+## __text_cold [0x100000fb0 - 0x100000fd0), which get combined into the single
+## valid text range [0x100000f90 - 0x100000fd0). The only symbol is "_main" at
+## the start of __text and __text_cold deliberately has no symbols in it, so
+## "main" is the last function info and it has no size. Its size must be
+## limited to the end of __text and must not cover any of __text_cold.
+
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-gsymutil --convert %t -o %t.gsym 2>&1 | FileCheck %s --check-prefix=CONVERT
+# RUN: llvm-gsymutil --address=0x100000f90 --address=0x100000faf --address=0x100000fb0 --address=0x100000fcf %t.gsym 2>&1 | FileCheck %s --check-prefix=ADDR
+# RUN: llvm-gsymutil %t.gsym 2>&1 | FileCheck %s --check-prefix=DUMP
+
+# CONVERT: Input file: {{.*\.yaml\.tmp}}
+# CONVERT: Output file (x86_64): {{.*\.yaml\.tmp\.gsym}}
+# CONVERT: Loaded 1 functions from symbol table.
+# CONVERT: Pruned 0 functions, ended with 1 total
+
+## The last byte of __text resolves to "main", but the first byte of
+## __text_cold must not. If the size was taken from the end of the valid text
+## range instead of the end of the section, the last two lookups would both
+## incorrectly resolve to "main".
+# ADDR: Looking up addresses in "{{.*\.yaml\.tmp\.gsym}}":
+# ADDR-NEXT: 0x0000000100000f90: main
+# ADDR-NEXT: 0x0000000100000faf: main + 31
+# ADDR-NEXT: 0x0000000100000fb0: error: address 0x100000fb0 is not in GSYM
+# ADDR-NEXT: 0x0000000100000fcf: error: address 0x100000fcf is not in GSYM
+
+## The range must end at 0x100000fb0, the end of __text, not at 0x100000fd0,
+## the end of the valid text range.
+# DUMP: FunctionInfo @ 0x{{[0-9a-f]+}}: [0x0000000100000f90 - 0x0000000100000fb0) "main"
+
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ cputype: 0x1000007
+ cpusubtype: 0x3
+ filetype: 0x2
+ ncmds: 5
+ sizeofcmds: 424
+ flags: 0x200085
+ reserved: 0x0
+LoadCommands:
+ - cmd: LC_SEGMENT_64
+ cmdsize: 72
+ segname: __PAGEZERO
+ vmaddr: 0
+ vmsize: 4294967296
+ fileoff: 0
+ filesize: 0
+ maxprot: 0
+ initprot: 0
+ nsects: 0
+ flags: 0
+ - cmd: LC_SEGMENT_64
+ cmdsize: 232
+ segname: __TEXT
+ vmaddr: 4294967296
+ vmsize: 4096
+ fileoff: 0
+ filesize: 4096
+ maxprot: 5
+ initprot: 5
+ nsects: 2
+ flags: 0
+ Sections:
+ - sectname: __text
+ segname: __TEXT
+ addr: 0x100000F90
+ size: 32
+ offset: 0xF90
+ align: 4
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 'C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3C3'
+ - sectname: __text_cold
+ segname: __TEXT
+ addr: 0x100000FB0
+ size: 32
+ offset: 0xFB0
+ align: 4
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: '9090909090909090909090909090909090909090909090909090909090909090'
+ - cmd: LC_SEGMENT_64
+ cmdsize: 72
+ segname: __LINKEDIT
+ vmaddr: 4294971392
+ vmsize: 4096
+ fileoff: 4096
+ filesize: 24
+ maxprot: 1
+ initprot: 1
+ nsects: 0
+ flags: 0
+ - cmd: LC_SYMTAB
+ cmdsize: 24
+ symoff: 4096
+ nsyms: 1
+ stroff: 4112
+ strsize: 8
+ - cmd: LC_UUID
+ cmdsize: 24
+ uuid: 11111111-2222-3333-4444-555555555555
+LinkEditData:
+ NameList:
+ - n_strx: 2
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 0
+ n_value: 4294971280
+ StringTable:
+ - ' '
+ - _main
+...
diff --git a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
index e168c8141def98..cb93054a89273d 100644
--- a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
+++ b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -545,8 +545,10 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, ObjectFile *SymtabObj,
// Finalize the GSYM to make it ready to save to disk. This will remove
// duplicate FunctionInfo entries where we might have found an entry from
- // debug info and also a symbol table entry from the object file.
- if (auto Err = Gsym.finalize(Out))
+ // debug info and also a symbol table entry from the object file. Pass along
+ // the object file that the symbols came from so the size of a trailing
+ // symbol that has no size doesn't extend past the end of its section.
+ if (auto Err = Gsym.finalize(Out, SymtabObj ? SymtabObj : &Obj))
return Err;
// Save the GSYM file to disk.
More information about the llvm-commits
mailing list