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 šŸ« : lldb13 In comparison, this is GDB 11.1: gdb Let’s see how an older version of LLDB is doing. Here’s LLDB version 12.0.1 šŸ«„: lldb12 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: lldb-patched 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