[llvm] Add support for mach-o symbol stubs. (PR #225155)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 21 13:50:43 PDT 2026
================
@@ -66,12 +68,115 @@ static std::vector<uint8_t> getUUID(const object::ObjectFile &Obj) {
return UUID;
}
+/// Create function information entries for Mach-O symbol stubs.
+///
+/// Symbol stubs are small chunks of code, all with the same fixed size, that
+/// resolves a function pointer on first call and then jumps to the resolved
+/// function on subsequent calls for functions that live in another shared
+/// library. The stubs are stored in sections whose type is S_SYMBOL_STUBS and
+/// the size of a single stub is stored in the "reserved2" field of the section
+/// header. These stubs have no entries in the symbol table of their own, so
+/// they end up being attributed to whatever function precedes them unless they
+/// are synthesized here.
+///
+/// The name of the function a stub jumps to is found using the indirect symbol
+/// table from the LC_DYSYMTAB load command. The "reserved1" field of the
+/// section header contains the index of the indirect symbol table entry that
+/// describes the first stub in the section, and each subsequent stub is
+/// described by the entry that follows. Each indirect symbol table entry is an
+/// index into the symbol table where the matching undefined (N_UNDF) symbol
+/// supplies the name to use for the stub. Each name gets a "symbol stub for: "
+/// prefix prepended to it so that symbolication makes it clear that the address
+/// is the stub for a function and not the function itself.
+///
+/// \returns The number of function infos that were added to \a Gsym.
+static uint64_t addMachOSymbolStubs(const object::MachOObjectFile &MachO,
+ OutputAggregator &Out, GsymCreator &Gsym) {
+ const MachO::dysymtab_command Dysymtab = MachO.getDysymtabLoadCommand();
+ if (Dysymtab.nindirectsyms == 0)
+ return 0;
+ const uint32_t NumSyms = MachO.getSymtabLoadCommand().nsyms;
+ const bool Is64Bit = MachO.is64Bit();
+
+ size_t NumBefore = Gsym.getNumFunctionInfos();
+ for (const object::SectionRef &Sect : MachO.sections()) {
+ const object::DataRefImpl SectDRI = Sect.getRawDataRefImpl();
+ uint32_t SectFlags, IndirectSymIdxStart, StubByteSize;
+ if (Is64Bit) {
+ const MachO::section_64 S = MachO.getSection64(SectDRI);
+ SectFlags = S.flags;
+ IndirectSymIdxStart = S.reserved1;
+ StubByteSize = S.reserved2;
+ } else {
+ const MachO::section S = MachO.getSection(SectDRI);
+ SectFlags = S.flags;
+ IndirectSymIdxStart = S.reserved1;
+ StubByteSize = S.reserved2;
+ }
+ if ((SectFlags & MachO::SECTION_TYPE) != MachO::S_SYMBOL_STUBS)
+ continue;
+ if (StubByteSize == 0)
+ continue;
+
+ const uint64_t SectAddr = Sect.getAddress();
+ const uint64_t NumStubs = Sect.getSize() / StubByteSize;
+ for (uint64_t StubIdx = 0; StubIdx < NumStubs; ++StubIdx) {
+ const uint64_t StubAddr = SectAddr + StubIdx * StubByteSize;
+ if (!Gsym.IsValidTextAddress(StubAddr))
+ continue;
+ const uint64_t IndirectSymIdx = IndirectSymIdxStart + StubIdx;
+ if (IndirectSymIdx >= Dysymtab.nindirectsyms)
+ continue;
+ const uint32_t SymIdx =
+ MachO.getIndirectSymbolTableEntry(Dysymtab, IndirectSymIdx);
+ // Entries that are absolute or local don't refer to a symbol table entry.
+ if (SymIdx & (MachO::INDIRECT_SYMBOL_ABS | MachO::INDIRECT_SYMBOL_LOCAL))
+ continue;
+ if (SymIdx >= NumSyms)
+ continue;
+ const object::symbol_iterator SymIt = MachO.getSymbolByIndex(SymIdx);
+ const object::DataRefImpl SymDRI = SymIt->getRawDataRefImpl();
+ const uint8_t NType = Is64Bit ? MachO.getSymbol64TableEntry(SymDRI).n_type
+ : MachO.getSymbolTableEntry(SymDRI).n_type;
+ // Only undefined symbols name a stub, any other symbol type means the
+ // function itself is in this file and already has a symbol table entry.
+ if ((NType & MachO::N_TYPE) != MachO::N_UNDF)
+ continue;
+ Expected<StringRef> Name = SymIt->getName();
+ if (!Name) {
+ if (Out.GetOS())
+ logAllUnhandledErrors(Name.takeError(), *Out.GetOS(),
+ "ObjectFileTransformer: ");
+ else
+ consumeError(Name.takeError());
+ continue;
+ }
+ // Remove the leading '_' character in any symbol names if there is one
+ // for mach-o files.
+ Name->consume_front("_");
+ if (Name->empty())
+ continue;
+ // Append a "symbol stub for: " prefix so it is clear when symbolicating
+ // that the address is the stub for the function and not the function
+ // itself. The string must be copied into the string table since it is
+ // created here and, unlike the symbol names, has no backing storage in
+ // the object file.
+ constexpr bool Copy = true;
+ const std::string StubName = "symbol stub for: " + Name->str();
----------------
alx32 wrote:
For a C++ target this stores `symbol stub for: _ZN3foo3barEv`. Will llvm-symbolizer still demangle that, given it hands the whole string to the demangler ? If not - do we want to demangle before emitting ?
https://github.com/llvm/llvm-project/pull/225155
More information about the llvm-commits
mailing list