llvm / llvm-project

The LLVM Project is a collection of modular and reusable compiler and toolchain technologies.
http://llvm.org
Other
28.34k stars 11.7k forks source link

Exceptions constructed from std::string bypass catch handlers on Clang 18 #94292

Open alexanderchuranov opened 4 months ago

alexanderchuranov commented 4 months ago

Exceptions constructed from an std::string bypass catch handlers and terminate the program. The same exception constructed from a string literal behaves normally. The following program compiled with Clang 14 catches both exceptions. However, on Clang 18 the "dynamic" version terminates the program with the following message:

libc++abi: terminating due to uncaught exception of type std::runtime_error: dynamic

// clang++ -std=c++20
#include <iostream>
#include <stdexcept>
#include <string>

void literal() {
  throw std::runtime_error("literal");
}

void dynamic() {
  throw std::runtime_error(std::string("dynamic"));
}

int main() {
  for (auto f : {literal, dynamic}) {
    try {
      f();
    } catch (std::runtime_error e) {
      std::cerr << "caught: " << e.what() << std::endl;
    }
  }
}

Working compiler:

Compiler that fails:

DimitryAndric commented 4 months ago

Don't see it here, on macOS 13.6.7 (M1):

% uname -a
Darwin macbookair 22.6.0 Darwin Kernel Version 22.6.0: Mon Apr 22 20:50:39 PDT 2024; root:xnu-8796.141.3.705.2~1/RELEASE_ARM64_T8103 arm64

% /usr/bin/clang++ -v
Apple clang version 15.0.0 (clang-1500.1.0.2.5)
Target: arm64-apple-darwin22.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

% /usr/bin/clang++ -std=c++20 pr94292.cpp -o pr94292

% ./pr94292
caught: literal
caught: dynamic

% /opt/homebrew/opt/llvm/bin/clang++ -v
Homebrew clang version 18.1.6
Target: arm64-apple-darwin22.6.0
Thread model: posix
InstalledDir: /opt/homebrew/opt/llvm/bin
% /opt/homebrew/opt/llvm/bin/clang++ -std=c++20 pr94292.cpp -o pr94292

% ./pr94292
caught: literal
caught: dynamic
alexanderchuranov commented 4 months ago

Thank you for looking into this!

I see now that the problem depends on the compiler flags.

> /opt/homebrew/opt/llvm/bin/clang++ -v
Homebrew clang version 18.1.6
Target: arm64-apple-darwin22.5.0
Thread model: posix
InstalledDir: /opt/homebrew/opt/llvm/bin

> /opt/homebrew/opt/llvm/bin/clang++ \ 
  -std=c++20 \
  -I/opt/homebrew/opt/llvm/include \
  -L/opt/homebrew/opt/llvm/lib/c++ \
  -o pr94292 pr94292.cpp
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/c++/libc++.dylib) was built for newer macOS version (13.6) than being linked (13.0)

> ./pr94292 
caught: literal
libc++abi: terminating due to uncaught exception of type std::runtime_error: dynamic

The problem does not reproduce when I remove the -L/opt/homebrew/opt/llvm/lib/c++:

> /opt/homebrew/opt/llvm/bin/clang++ \
  -std=c++20 \
  -I/opt/homebrew/opt/llvm/include \
  -o pr94292 pr94292.cpp

> ./pr94292 
caught: literal
caught: dynamic

This is still surprising, because brew llvm info suggests these environment variables:

export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++"

The compilation works when I'm running it with all those variables set.

> echo ${LDFLAGS}

> echo ${CPPFLAGS}

> export LDFLAGS="-L/opt/homebrew/opt/llvm/lib"
> export CPPFLAGS="-I/opt/homebrew/opt/llvm/include"
> LDFLAGS="-L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++"
> /opt/homebrew/opt/llvm/bin/clang++ \
  -std=c++20 \
  -o pr94292 pr94292.cpp

> ./pr94292 
caught: literal
caught: dynamic

But it doesn't work if all the same values are specified directly on the command-line:

> /opt/homebrew/opt/llvm/bin/clang++ \
  -std=c++20 \
  -I/opt/homebrew/opt/llvm/include \  
  -L/opt/homebrew/opt/llvm/lib \
  -L/opt/homebrew/opt/llvm/lib/c++ -Wl,-rpath,/opt/homebrew/opt/llvm/lib/c++ \
  -o pr94292 pr94292.cpp
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/c++/libc++.dylib) was built for newer macOS version (13.6) than being linked (13.0)
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)

> ./pr94292 
caught: literal
libc++abi: terminating due to uncaught exception of type std::runtime_error: dynamic
alexanderchuranov commented 4 months ago

BTW the culprit is the flag -L/opt/homebrew/opt/llvm/lib/c++.

> /opt/homebrew/opt/llvm/bin/clang++ \
  -std=c++20 \
  -I/opt/homebrew/opt/llvm/include \
  -L/opt/homebrew/opt/llvm/lib \
  -L/opt/homebrew/opt/llvm/lib/c++ \
  -o pr94292 pr94292.cpp
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/c++/libc++.dylib) was built for newer macOS version (13.6) than being linked (13.0)
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)

> ./pr94292 
caught: literal
libc++abi: terminating due to uncaught exception of type std::runtime_error: dynamic

Removing that particular search path fixes the problem:

> /opt/homebrew/opt/llvm/bin/clang++ \
  -std=c++20 \
  -I/opt/homebrew/opt/llvm/include \
  -L/opt/homebrew/opt/llvm/lib \
  -o pr94292 pr94292.cpp
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)
ld: warning: dylib (/opt/homebrew/opt/llvm/lib/libunwind.dylib) was built for newer macOS version (13.6) than being linked (13.0)

> ./pr94292 
caught: literal
caught: dynamic
alexanderchuranov commented 4 months ago

I don't know now if this is still a bug in LLVM or it is my ignorance about the proper ways to pass flag values from the compiler front-end to the linker.

It would still be nice to understand why this happens, but it's not a critical problem.

Thanks!

det commented 3 months ago

I see this problem, too. It only happens when using libc++.1.dylib from LLVM 18.1.X. Removing the -L flag makes it use the system libc++abi.1.dylib.