Tag Archives: Steam

How to Fix Broken Steam Linux Client With Radeon Graphics Driver (Workaround)

Short Version
If Steam gives out the error: “OpenGL GLX context is not using direct rendering, which may cause performance problems.” and you’re sure the graphics driver is loaded properly try the following script to run Steam:

#!/bin/bash
export LD_PRELOAD='/usr/$LIB/libstdc++.so.6' #Export so all child processes are affected as well
export DISPLAY=:0
#export LIBGL_DEBUG=verbose
steam

Long Version
In the past 6 months my livingroom PC is a Linux box with AMD graphics card. I could never get AMD’s binary driver (FGLRX) to work with Steam reliably but the open source Radeon driver is surprisingly good and keeps improving. The downside is that every so often Steam client updates break for weeks until another update fix it. After going through this several times I decided to actually figure out the root cause and fix it. Here’s a quick walkthrough on how to debug your Steam client setup and how to fix a common problem that may pop up with open source graphics driver.

Running steam from the command line gives out the venerable ‘GLX direct rendering’ error but there’s a hint in the lines before it:

Running Steam on ubuntu 14.10 64-bit
STEAM_RUNTIME is enabled automatically
Installing breakpad exception handler for appid(steam)/version(1420770381)
libGL error: unable to load driver: radeonsi_dri.so
libGL error: driver pointer missing
libGL error: failed to load driver: radeonsi
libGL error: unable to load driver: swrast_dri.so
libGL error: failed to load driver: swrast
Installing breakpad exception handler for appid(steam)/version(1420770381)

Steam can’t open radeonsi_dri.so, the shared library responsible for communicating with the graphics driver. To rule out a problem with the driver itself, let’s verify an OpenGL enabled graphics driver is loaded by running:
DISPLAY=:0 glxinfo | grep -i direct
The output should be:

direct rendering: Yes

Next, to debug Steam load run the Steam client from commandline in verbose mode:
DISPLAY=:0 LIBGL_DEBUG=verbose steam

And now the output is:
Running Steam on ubuntu 14.10 64-bit
STEAM_RUNTIME is enabled automatically
Installing breakpad exception handler for appid(steam)/version(1420770381)
libGL: screen 0 does not appear to be DRI3 capable
libGL: pci id for fd 7: 1002:6798, driver radeonsi
libGL: OpenDriver: trying /usr/lib/i386-linux-gnu/dri/tls/radeonsi_dri.so
libGL: OpenDriver: trying /usr/lib/i386-linux-gnu/dri/radeonsi_dri.so
libGL: dlopen /usr/lib/i386-linux-gnu/dri/radeonsi_dri.so failed (/home/user/.local/share/Steam/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /usr/lib/i386-linux-gnu/dri/radeonsi_dri.so))
libGL: OpenDriver: trying ${ORIGIN}/dri/tls/radeonsi_dri.so
libGL: OpenDriver: trying ${ORIGIN}/dri/radeonsi_dri.so

The dlopen error tells us the file exist but there was an error with the LibC6 version the driver requires. Let’s check which LibC6 version is actually behind the libstdc++.so.6 that’s loaded:

~$ ls -l /home/user/.local/share/Steam/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu/libstdc++.so.6

lrwxrwxrwx 1 user user 19 Jul 19 00:52 /home/user/.local/share/Steam/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.18

Let’s check what version is installed on the global /usr/lib path:
~$ ls -l /usr/lib/i386-linux-gnu/libstdc++.so.6

lrwxrwxrwx 1 root root 19 Oct 11 14:58 /usr/lib/i386-linux-gnu/libstdc++.so.6 -> libstdc++.so.6.0.20

So Steam loaded LibC6 with ABI version 18 where Radeonsi expects version 20.

Hold on for a second, why does this happen in the first place?

The answer is STEAM_RUNTIME. Steam for Linux packs a set of (standard) shared libraries called STEAM_RUNTIME. This allows the guys at Valve to optimize the client for specific dependencies without fearing the target machine will have different version. This is somewhat similar to static compilation where you compile the program’s dependencies in the main binary.

The problem is that Steam can’t come preloaded with ALL the libraries it needs and has to rely on the OS to supply some. This is exactly the case with radeonsi_dri.so. This library is in charge of the client’s OpenGL interface with the actual driver. Radeonsi also needs libstdc++ from LibC6 and gets updated independently every once in a while. Whenever an update changes the ABI, the global LibC6 and Steam’s gets out of sync.

So the only thing we need is to get Steam to load the newer version of the library.

Some forums advise you to remove Steam’s version of LibC6 by running:

rm /home/user/.local/share/Steam/ubuntu12_32/steam-runtime/i386/usr/lib/i386-linux-gnu/libstdc++.so.6

etc. This won’t work since Steam checks the integrity of its runtime and will fix the missing files on load.

A more elegant solution would be to have the proper LibC6 version loaded by the OS, by running:
LD_PRELOAD=/usr/lib/i386-linux-gnu/libstdc++.so.6 DISPLAY=:0 steam

This seems to work but puts out a lot of errors in the form of:
ERROR: ld.so: object '/usr/lib/i386-linux-gnu/libstdc++.so.6' from LD_PRELOAD cannot be preloaded (wrong ELF class: ELFCLASS32): ignored.

This error is an artifact of the fact Steam is a 32-bit binary running on a 64-bit machine. LibC6 has two versions (32-bit and 64-bit). The Steam binary needs the 32-bit version but other parts of the client run as native 64-bit and when they start they will get the LD_PRELOAD environment specifying a library with the wrong format. So how do we tell LD_PRELOAD to take the right one?

LD_PRELOAD='/usr/$LIB/libstdc++.so.6' DISPLAY=:0 steam

The somewhat non-intuitive $LIB parameter gets expanded in ld.so to the right path based on the platform of the process being started (man 8 ld.so for details).
The quotes around the environment are mandatory. It tells the shell not to try and expand the $LIB but rather pass it as is.

So finally Steam will load cleanly. A more permanent solution can be implemented in a loading script that checks if the global LibC6 version is newer than the one in STEAM_RUNTIME and only then LD_PRELOAD’s. I’ll leave this as an exercise to the reader.

Happy Linux beta gaming!

Advertisements

11 Comments

Filed under Uncategorized