Home Blog

MATLAB

"The glibc problem" on arch + MATLAB isn't one bug — it's a category. Two different releases, two different root causes, found by actually reading the crash instead of guessing.

glibc 2.41 executable-stack bug

glibc 2.41 stopped honoring a library's request for an executable stack via dlopen/dlmopen. Some of MATLAB's bundled .so files ask for exactly that, so they segfault on load. Clear the flag with patchelf:

sudo patchelf --clear-execstack ~/.MathWorks/ServiceHost/-mw_shared_installs/v2025.1.1.2/bin/glnxa64/libmwfoundation_crash_handling.so
sudo patchelf --clear-execstack ~/.MathWorks/ServiceHost/-mw_shared_installs/v2025.1.1.2/bin/glnxa64/mathworksservicehost/rcf/matlabconnector/serviceprocess/rcf/service/libmwmshrcfservice.so

Check whether a given install even has the problem before chasing this fix — later releases (R2026a) ship with zero executable-stack libraries, MathWorks fixed it upstream:

find "$MATLAB_ROOT" -name '*.so' -print0 \
  | xargs -0 -I{} sh -c 'readelf -lW "{}" | grep -q "GNU_STACK.*RWE" && echo {}'

gnutls ABI break (R2026a activation)

Fresh R2026a install: the activation window never appears, no error, process just dies. Not the exec-stack bug above — confirmed with a real backtrace instead of assuming:

gdb -q -batch -ex run -ex bt --args \
  "$MATLAB_ROOT/bin/glnxa64/MathWorksProductAuthorizer"

Crash is inside libgnutls.so.30, called from lc_init() in libmwinstall_activationwsclientimpl.so. MATLAB's activation/licensing plugin framework dlopen()s the system gnutls at runtime, and Arch's current gnutls is ABI-incompatible with what the client expects.

Fix: stage a pinned, private gnutls + nettle build and point LD_LIBRARY_PATH at it for MATLAB's own processes — no need to touch the system gnutls or the MATLAB install itself. Version pairing matters: gnutls 3.8.9-1 needs a libhogweed exporting nettle_rsa_oaep_sha384_decrypt under symbol version HOGWEED_6; nettle 3.9.1 is missing that symbol, nettle 3.10-1 has it under the same soname.

mkdir -p ~/.local/lib/matlab-gnutls-compat
cd /tmp && mkdir gnutls-old nettle-310

curl -sL -o gnutls-old/g.pkg.tar.zst \
  https://archive.archlinux.org/packages/g/gnutls/gnutls-3.8.9-1-x86_64.pkg.tar.zst
tar --zstd -xf gnutls-old/g.pkg.tar.zst -C gnutls-old

curl -sL -o nettle-310/n.pkg.tar.zst \
  https://archive.archlinux.org/packages/n/nettle/nettle-3.10-1-x86_64.pkg.tar.zst
tar --zstd -xf nettle-310/n.pkg.tar.zst -C nettle-310

cp -a gnutls-old/usr/lib/libgnutls* ~/.local/lib/matlab-gnutls-compat/
cp -a nettle-310/usr/lib/libnettle* nettle-310/usr/lib/libhogweed* \
  ~/.local/lib/matlab-gnutls-compat/

Then launch MATLAB with that on LD_LIBRARY_PATH:

env LD_LIBRARY_PATH=~/.local/lib/matlab-gnutls-compat \
  "$MATLAB_ROOT/bin/matlab" -nodesktop -nosplash -nosoftwareopengl

To pop the activation window on its own — e.g. to (re)activate without launching the full app — run the authorizer client directly with the same LD_LIBRARY_PATH:

env LD_LIBRARY_PATH=~/.local/lib/matlab-gnutls-compat \
  "$MATLAB_ROOT/bin/glnxa64/MathWorksProductAuthorizer.sh"

Watch out for stale LD_PRELOADs from older fixes lying around in shell config (LD_PRELOAD=/usr/lib/libstdc++.so forcing the system C++ runtime over MATLAB's bundled one is its own separate, documented crash cause) — they're easy to leave behind across MATLAB version upgrades and mask what's actually going on.

"Graphics acceleration hardware is unavailable" (R2026a)

Same bundled-runtime theme as above, different victim. R2026a no longer draws figures with desktop OpenGL; it renders WebGL inside an embedded Chromium (MATLABWindow + libcef.so) through ANGLE. So the fallback is no longer MATLAB's own software OpenGL, it's SwiftShader, and on arch you land there by default.

opengl('data') is gone in R2026a. The replacement is rendererinfo:

r = rendererinfo;
disp(r.RendererDevice)
disp(r.Details.HardwareSupportLevel)

Broken looks like this, on a Radeon 780M that has a perfectly good amdgpu + mesa stack:

ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)

Root cause

ANGLE picks its Vulkan backend, asks the loader to enumerate devices, and the only ICD that answers is the SwiftShader one MATLAB ships (bin/glnxa64/vk_swiftshader_icd.json). The real driver never loads. Reproduce the failure directly, outside MATLAB, with a five-line dlopen harness:

#include <dlfcn.h>
#include <stdio.h>
int main(int c, char **v) {
  void *h = dlopen(v[1], RTLD_NOW);
  printf("%s\n", h ? "OK" : dlerror());
  return !h;
}
gcc -o dlopentest dlopentest.c
./dlopentest /usr/lib/libvulkan_radeon.so                      # OK
LD_LIBRARY_PATH=$MATLAB_ROOT/sys/os/glnxa64 \
  ./dlopentest /usr/lib/libvulkan_radeon.so
# .../sys/os/glnxa64/libstdc++.so.6: version `GLIBCXX_3.4.32' not found
#   (required by /usr/lib/libSPIRV-Tools.so)

There it is. radv links libSPIRV-Tools, which is built against current mesa's toolchain and needs GLIBCXX_3.4.32. MATLAB puts its bundled libstdc++.so.6.0.30 (GCC 12.3) ahead of the system 6.0.36, so radv fails to load, Vulkan enumeration comes back with SwiftShader only, and MATLAB dutifully warns that hardware acceleration is unavailable. The GPU is fine; the C++ runtime under it is too old.

Why LD_LIBRARY_PATH cannot fix this

The obvious move is to prepend a directory holding a symlink to the system libstdc++. The matlab launcher even documents a variable for it, LDPATH_PREFIX, which lands at the very front of LD_LIBRARY_PATH. It changes nothing, and the reason is worth knowing:

readelf -d "$MATLAB_ROOT/bin/glnxa64/MATLABWindow" | grep -E 'RPATH|RUNPATH'
# 0x000000000000000f (RPATH) Library rpath: [$ORIGIN:$ORIGIN/../../sys/os/glnxa64]

DT_RPATH, not DT_RUNPATH. The dynamic linker searches DT_RPATH before LD_LIBRARY_PATH, so no amount of path ordering wins. Confirm what a live process actually mapped rather than what you hoped it would:

grep -oE '/[^ ]*libstdc\+\+[^ ]*' /proc/$(pgrep -f MATLABWindow | head -1)/maps
# /usr/local/MATLAB/R2026a/sys/os/glnxa64/libstdc++.so.6.0.30

LD_PRELOAD is loaded before anything the RPATH can reach, so that is the lever that works.

Fix

env LD_PRELOAD=/usr/lib/libstdc++.so.6 "$MATLAB_ROOT/bin/matlab"

Newer libstdc++ keeps the old symbol versions, so MATLAB's own GCC-12-era libraries are satisfied by 6.0.36 too. The launcher appends to LD_PRELOAD (LD_PRELOAD="${LD_PRELOAD:+${LD_PRELOAD}:}...") instead of clobbering it, so an inherited value survives alongside MATLAB's own shims. Result:

ANGLE (AMD, AMD Radeon 780M Graphics (radeonsi phoenix ACO), OpenGL 4.6)
HardwareSupportLevel: Full

Note this is the narrow, versioned libstdc++.so.6, not the libstdc++.so linker script blamed for crashes at the end of the previous section. Same family of hack, and it's still the first thing to rip out when MATLAB later misbehaves for unrelated reasons.

If you'd rather not carry the variable, MATLAB's bundled libstdc++.so.6 is already just a symlink and the pristine copies live in sys/os/glnxa64/orig/, so repointing it at /usr/lib/libstdc++.so.6 does the same thing install-wide, at the cost of a change a MATLAB update can quietly revert.

Two dead ends worth naming

strings over the CEF glue libraries turns up MW_ENABLE_GPU_RENDERING sitting next to disable-gpu and enable-unsafe-swiftshader in libmwcef_common.so, which looks exactly like the switch you want. It isn't; setting it made no difference, because the GPU was never being blocklisted, just left driverless. bin/glnxa64/gpu_info is likewise a red herring that reports the right device (0x1002:0x1900) with driver version 0.0.0.0.

Also, -nosoftwareopengl is now inert: R2026a answers with Warning: OpenGL Startup options have been removed. Drop it from launcher aliases.

Verify

rendererinfo alone can lie by omission, so render something that leans on the GPU (lighting plus transparency) and confirm it survives:

f = figure('Visible','off');
[X,Y,Z] = peaks(120);
surf(X,Y,Z,'EdgeColor','none'); camlight; lighting gouraud; alpha 0.7
exportgraphics(f, 'hwtest.png', 'Resolution', 150)