The Issue
One day, I was wondering how dynamic function call works.
I thought I’d start with a simple puts call like in this C snippet.
#include <stdio.h>
int main(void) { puts("Hello\n"); }
To see it going through the Procedure Linkage Table (PLT)
in the context of lazy binding on x86_64 Linux LIVE, naturally, LLDB came to my mind.
To my dismay, instead of something relevant to puts
there were only these ugly and unidentifiable ___lldb_unnamed_symbol36 in the disaseembly on LLDB 13.0.0 š« :
 In comparison, this is GDB 11.1:
In comparison, this is GDB 11.1:
 Let’s see how an older version of LLDB is doing.
Here’s LLDB version 12.0.1 š«„:
Let’s see how an older version of LLDB is doing.
Here’s LLDB version 12.0.1 š«„:
 Apparently, something went wrong between the creation of LLVM version 12 and 13 release branch.
It turned out that it was
a patch landing in July 2021 that changed the behavior.
Someone also filed a GitHub issue just a few days before I found this.
I then commented my findings on the code review page of the seemingly problematic patch.
Apparently, something went wrong between the creation of LLVM version 12 and 13 release branch.
It turned out that it was
a patch landing in July 2021 that changed the behavior.
Someone also filed a GitHub issue just a few days before I found this.
I then commented my findings on the code review page of the seemingly problematic patch.
Finding the culprit
Thankfully, LLDB has a nice “batch mode”, so it’s pretty easy to lauch it in a debugger as shown below. Otherwise I have no idea how to debug an REPL program.
lldb -- ~/llvm-project/build-lldb/bin/lldb -b -o 'image lookup -a 0x401030' a.out
After some debugging of the debugger, I finally knew where the problem was. The aforementioned patch was innocent!
Here’s the whole function added by the patch (comments removed for brevity) that’s seemingly causing the problem:
void Symbol::SynthesizeNameIfNeeded() const {
  if (m_is_synthetic && !m_mangled) {
    llvm::SmallString<256> name;
    llvm::raw_svector_ostream os(name);
    os << GetSyntheticSymbolPrefix() << GetID();
    m_mangled.SetDemangledName(ConstString(os.str()));
  }
}
Let’s focus on the condition of the if statement, m_mangled is of type Mangled, so !m_mangled calls the broken Mangled::operator!,
which according to git blame, dates back to Apple’s initial open-sourcing LLDB in 2010, untouched, unused, and untested for over a decade.
To my surprise, the implementation of Mangled::operator! and Mangled::operator void*
differed from the intended behavior documented by comments in the header file.
I then replaced them with one single explicit operator bool
which implements Mangled’s documented bool semantics.
Now, the disassmbly correctly shows:
 bringing balance to the Force.
bringing balance to the Force.
Finally, as per a reviewer’s request, I added unit tests for my new bool conversion operator, which was also my first time using GoogleTest. What a fun ride! The GitHub commit can be found at: https://github.com/llvm/llvm-project/commit/633b002944b9