Repository navigation
Description
Discovery and investigation
After updating Xcode on macOS, I rebuilt KLayout and ran the QA tests.
dbLibrariesTests:7_monsterlib crashed in db::Layout::cleanup()—a failure I had not encountered with the previous toolchain.
I investigated a debug build with LLDB, with assistance from ChatGPT. The investigation showed that a forward scan over equivalent std::multimap keys started at find(), assuming that it returned the first matching entry.
With the updated libc++, it returned an entry partway through the group.
As a result, unregistering a library proxy could miss its entry, leaving a stale registration after the cell was deleted.
The official libc++ 22 release notes describe this implementation change:
“This was never guaranteed by the Standard, but libc++ previously happened to always return the first equal element, as other implementations do.”
The release notes recommend lower_bound() or equal_range() when the first equivalent entry is required.
This implementation change exposes an existing assumption, not a change to the C++ standard.
Targeted fix
I changed three calls from find() to lower_bound(), retaining the existing loop boundary checks:
- auto l = m_lib_proxy_map.find (key);
+ auto l = m_lib_proxy_map.lower_bound (key);| File | Function | Locations |
|---|---|---|
dbLayout.cc |
Layout::unregister_lib_proxy() |
1 |
dbLibraryManager.cc |
LibraryManager::lib_by_name() |
2 |
The first change resolved the crash. The other two addressed library lookup failures in dbLibrariesTests:4. After all three changes, the selected library tests passed in both debug and release builds on macOS.
This was a targeted investigation; I have not audited the entire source tree for similar assumptions.
Verification on Ubuntu
To investigate outside macOS, I installed Clang 22 and libc++ 22.1.8 alongside the existing libstdc++ environment on Ubuntu 22.04.
A small program test_multimap.cpp inserted twelve entries with the same key into a multimap, with values from 1 through 12:
#include <iostream>
#include <map>
int main()
{
std::multimap<int, int> m;
for (int i = 1; i <= 12; ++i)
m.emplace(0, i);
std::cout << "find: " << m.find(0)->second << '\n';
std::cout << "lower_bound: " << m.lower_bound(0)->second << '\n';
}| Compiler / standard library | Value returned by find() |
Value returned by lower_bound() |
|---|---|---|
| GCC / libstdc++ | 1 | 1 |
| Clang 22 / libstdc++ | 1 | 1 |
| Clang 22 / libc++ 22.1.8 | 4 | 1 |
These results demonstrate the library-dependent behavior for this example; the particular value returned by find() should not be relied upon.
I then built KLayout with Clang 22 and -stdlib=libc++, testing both the original and patched commits.
CC=clang-22 \
CXX=clang++-22 \
AR=ar \
OBJCOPY=objcopy \
CFLAGS="-O2 -DNDEBUG" \
CXXFLAGS="-O2 -DNDEBUG -stdlib=libc++" \
LDFLAGS="-stdlib=libc++" \
LIBS="" \
./build.sh -expert -option -j6Before/after results
| Environment | Before: e71272c3b |
After: 19a9a645e |
|---|---|---|
| macOS arm64 / updated Xcode / libc++ 22.1.6 | 7_monsterlib: SIGSEGV in Layout::cleanup(), address 0x0 |
Library tests: all 16 executions passed in each of the debug and release builds |
| Ubuntu 22.04 x86_64 / Clang 22 / libc++ 22.1.8 / release | 7_monsterlib: SIGSEGV in Layout::cleanup(), address 0x0 |
Library tests: all 16 executions passed |
| Ubuntu 22.04 x86_64 / GCC / libstdc++ / release | Not tested in this comparison | Library tests: all 16 executions passed |
The 16 executions cover eight tests in both editable and non-editable modes.
The Ubuntu reproduction matches the macOS failure in test name, failing function, and fault address.
Although the exact source line and variables were not inspected in the Ubuntu release build, the comparison provides a Linux reproduction and supports the targeted fix.
A separate macOS crash in gsiExpressionTests:16 remains under investigation; the patched Ubuntu/libc++ build passed all 36 executions of gsiExpressionTests.
Activity
Kazzz-S commentedon Oct 22, 2026
Xcode 27 Release Notes from Apple
https://developer.apple.com/documentation/xcode-release-notes/xcode-27-release-notes?utm_source=chatgpt.com
Kazzz-S commentedon Oct 22, 2026
Follow-up: verification with the previous Xcode toolchain
I reinstalled Xcode 26.6 alongside Xcode 27.1 and rebuilt the unmodified baseline commit
e71272c3b, before the threefind()→lower_bound()changes.The libc++ header versions were confirmed using
_LIBCPP_VERSION:_LIBCPP_VERSIONWith Xcode 26.6, the release build passed both tests below in non-editable and editable modes:
dbLibrariesTests:7_monsterlibgsiExpressionTests:16The
7_monsterlibresult complements the earlier Ubuntu reproduction: the unmodified baseline passes with libc++ 21.1.6 on macOS, but crashes with libc++ 22.1.6 on macOS and libc++ 22.1.8 on Ubuntu. This supports the diagnosis that the newerfind()behavior exposed an existing assumption in KLayout.The separate
gsiExpressionTests:16failure also did not reproduce with Xcode 26.6. However, the root cause remains unresolved: the earlier macOS debug build reported a heap use-after-free, and passing with the older toolchain doesn't prove the underlying issue is fixed or related to the multimap changes.For now, I will use Xcode 26.6 for regular builds while keeping Xcode 27.1 available for further investigation.
Kazzz-S commentedon Oct 22, 2026
Switching Between Xcode Versions (for self)
Both versions are installed under
/Applications:/Applications/Xcode-26.6.app/Applications/Xcode.appAdjust the paths below if the applications are renamed.
Select Xcode 26.6
Select Xcode 27.x
These commands change the system-wide selection used by command-line developer tools.
Check the Active Toolchain
The compiler's
InstalledDirshould point to the selected Xcode installation.Check the libc++ Header Version
Confirmed on this Mac:
_LIBCPP_VERSIONTemporarily Select Xcode for One Command
Use
DEVELOPER_DIRto override the system-wide selection without changing it. Nosudois required.For example, check the older libc++ headers:
If
DEVELOPER_DIRhas previously been exported in the current shell, it overridesxcode-select. Remove that override to use the system-wide selection:Rebuilding KLayout After Switching
Use a clean build when comparing toolchains. Move aside the previous build output to avoid mixing object files compiled with different Xcode versions.
Switching Xcode does not rebuild existing binaries.
Kazzz-S commentedon Oct 22, 2026
Important: Xcode 26.6 Cannot Be Used on macOS GoldenGate
Xcode 26.6 cannot run on macOS GoldenGate in my environment. Therefore, keeping an older Xcode installation does not provide a workaround on that OS.
Impact on the KLayout Investigation
On macOS Tahoe, I confirmed that the unmodified baseline commit
e71272c3b, built with Xcode 26.6 and libc++ 21.1.6, passes both of these tests in editable and non-editable modes:dbLibrariesTests:7_monsterlibgsiExpressionTests:16However, this fallback depends on retaining a compatible macOS environment. It won't carry over to GoldenGate with Xcode 26.6.
Revised Plan
find()→lower_bound()fixes for compatibility with libc++ 22.Downgrading Xcode is a temporary workaround on Tahoe, not a long-term solution for newer macOS environments.
Scope of This Observation
This incompatibility concerns Xcode 26.6 in my GoldenGate environment. I have not individually verified other Xcode 26.x releases.
klayoutmatthias commentedon Oct 22, 2026
Hi @Kazzz-S,
thanks for bringing this up.
This applies to many places. I don't know why C++ needs the find implementation then anyway if there are no warranties. So far maybe the behavior maybe was not guaranteed but C++, but it was always the first element of the sequence of elements with identical keys. There are many non-warranties that are useful and C++ should not change. I hope these C++ purists know that.
I had to change many more places. I assume that help, although I can't find ways to reproduce problems for these other cases.
Best regards,
Matthias
klayoutmatthias commentedon Oct 22, 2026
Turns out that
lower_boundis not exactly a replacement forfind. The exact replacement is:While
findreturnedend()if it could not find an object with the given key,lower_boundwill return the iterator pointing to a element past the given key.Matthias
Kazzz-S commentedon Oct 23, 2026
Hi @klayoutmatthias ,
Thank you for looking into this issue.
I've tested the issue-2518 branch (06be194) on Golden Gate and attached a QAT log file.
==> QATest_06be19450_2026_1023_0944__qt5MP.build.macos-GoldenGate-C32-release-RsysPsys.macQAT.zip
It still appears there are some potential issues.
Best regards,
Kazzz-S
klayoutmatthias commentedon Oct 25, 2026
Hi @Kazzz-S,
thanks for this additional information. I don't think there is a relationship to the multimap problem. Maybe there is something else. I will try to reproduce the problem with my tools at hand.
Matthias
klayoutmatthias commentedon Oct 25, 2026
I can see the problem. It does not crash for me, but I can see it in valgrind.
I used clang 21.1.8 with libc++ 22.1.2.
However, I also see a crazy number of other fails - like assertions in the OASIS writer and such. That combination is definitely not stable. That needs a lot more debugging :(
Matthias
klayoutmatthias commentedon Oct 27, 2026
I have added two patches to the branch: the first one fixes the
gsiExpressionTests:16failure. The reason was improper invalidation of references to objects stored intl::Variantand most likely a change in the destruction order ofstd::maptriggered the problem.The second patch fixes the OASIS writer assertion I got with clang 21.1.8. However, that is not an obvious bug in KLayout code. Instead a
dynamic_castfails while it shouldn't. I had similar trouble in the past when compilers had problems keeping the RTTI tables in sync between different shared objects.Maybe the second problem is specific to Linux. In that particular case, the fix is to replace
dynamic_castbystatic_cast. This is possible here, but not in general and I think that triggers other issues I have not debugged yet. So my advise is not to use clang 21 at least on Linux.Matthias
Kazzz-S commentedon Oct 28, 2026
Hi @klayoutmatthias ,
Thank you for the fix 24896c9.
The QA tests are now complete.
I have attached the logs from macOS 27 GoldenGate.
==> QATest_24896c9ab_2026_1028_1920__qt5MP.build.macos-GoldenGate-C32-release-RsysPsys.macQAT.zip
Best regards,
Kazzz-S
Kazzz-S commentedon Oct 29, 2026
😄 Improved with 4a17a36.
==> QATest_4a17a3675_2026_1029_1054__qt5MP.build.macos-GoldenGate-C32-release-RsysPsys.macQAT.zip
Golden Gage Env. (arm64)
Best regards,
Kazzz-S
klayoutmatthias commentedon Oct 29, 2026
Hi @Kazzz-S, thanks a lot for your test reports!
The
dbRecursiveShapeIteratorTests:10fail scares me a bit. Is that unique to this branch and/or clang 21/libc++ 22?Thanks,
Matthias
Kazzz-S commentedon Oct 29, 2026
Again with 4a17a36 ...
Tahoe Env. (arm64)
==> QATest_4a17a3675_2026_1030_0813__qt5MP.build.macos-Tahoe-C32-release-RsysPsys.macQAT.zip
Sequoia Env. (x86_64)
==> QATest_4a17a3675_2026_1030_1017__qt5MP.build.macos-Sequoia-C32-release-RsysPsys.macQAT.zip
Tahoe Env. (arm64) with the latest Xcode 27.2
==> QATest_4a17a3675_2026_1031_0852__qt5MP.build.macos-Tahoe-C32-release-RsysPsys.macQAT.zip
klayoutmatthias commentedon Nov 1, 2026
I could not see the RecursiveShapeIterator problem on Linux with libc++ 22 and clang 21, so I think it is related to the ARM platform. It needs further investigation, but it can't be a fundamental problem as otherwise many more tests should fail. I'm closing this ticket and create a new one specifically for this problem as the original PR contains some important patches also for Linux.
Thanks for the feedback.
Matthias