- 0x0000007b
- 2차세계대전
- 3.20해킹
- 3d프린터
- 4대강
- 502 error
- 53빌딩
- 7840hs
- 88체육관 수영장
- ABI
- abortive close
- abortive shutdown
- AHCI
- aic8800d80 chipset
- akmods
- ALC1220
- alma linux
- alternatives
- AM4 메인보드
- AMD
- amd 7840hs
- amd 그래픽 게임용
- amd 내장 그래픽 최적화
- anonymous file
- API
- apple.com
- APT
- apt-get
- Armagnac
- Asrock
- Today / Yesterday
- /
- Total
Linux Programmer
RockyLinux9의 X윈도우 GDM 오류 : 아, 뭔가 문제가 생겼습니다 본문
Rocky Linux 9 리눅스에서 X.org 에러로 인해 "아, 뭔가 문제가 생겼습니다. 문제가 발생했지만 복구하지 못했습니다. 시스템 관리자에게 문의하십시오" 메시지가 나온 경우를 에러 처리 하는 방법이다.
일전에 강의를 하다가 실습 이미지를 가지고 이것저것 해보는 수강자 분이 갑자기 X윈도가 안된다고 했다. 그래서 고쳐드렸는데, 에러를 찾아내면서 해결하는 과정을 보시더니, 다른 프로그램의 오류나 충돌도 이런 해결 방식을 사용할 수 있냐고 질문하시길래, 100% 같은 방법은 아니지만 얼추 비슷하게 할 수 있다고 말씀드렸다. 이렇게 문제를 해결하는 방법에 대해 더 자세히 알기를 원하셨지만 부족한 강의 시간 때문에 따로 과외를 해드릴 수는 없었고, 여기에 블로그 글로 남겨서 읽을 수 있게 해드리겠다고 하였다. 이 글은 당시 과정을 재현하기 위해 Virtualbox에 RockyLinux9을 설치하고 같은 오류를 만든 후 그림 및 로그를 캡쳐하였다.
1. 에러 상태
에러 상태는 어느 날 갑자기 X윈도우 로그인을 담당하는 GDM(GNOME Display Manager)가 화면을 띄우지 못하고 아래처럼 에러를 뱉어내는 상황이다. 어제 수업까지는 잘 되었다고 하는데, 당일 아침에 와서 실습용 컴퓨터를 켜니까 저렇게 에러가 발생했다고 하였다.

그런데 이와 별개로 "아, 뭔가 문제가 생겼습니다"라는 메시지가 너무 귀엽지 않은가? 시스템 언어가 English인 경우에는 "Oh no! Something has gone wrong. A problem has occurred and the system can't recover. Please log out and try again."이라고 표시된다. 해석이 좀 엉망이긴 한데, 이건 기계 번역이라서 그럴 것이다.
2. 해결 방법을 모색
모든 프로그램의 문제를 해결하기 위해서는 최우선적으로 log 메시지를 살펴봐야 한다. 일반적으로 로그 메시지는 journalctl로 보거나 아니면 개별적으로 해당 프로세스가 남기는 log 파일이 있다면 그것을 살펴본다. 로그가 하나도 나오지 않는 시스템이라면 strace 같은 것으로 추적하는 방법을 실행하면서 함수가 error로 리턴하는 경우를 살펴보면 된다.
이 작업은 root의 권한을 필요로 하기 때문에 su - 로 변경하던가, 아니면 sudoers를 잡아놨다면 sudo로 명령한다. 여기서는 su - 로 root 유저로 변경한 것을 간주하여 명령하도록 한다.
2.1. X window의 로그 살펴보기
Rocky Linux 9에서는 startx 명령어를 사용해서 수동으로 X window를 실행할 수 있다. 기존에 X 윈도우가 띄워져 있으면 에러가 발생할 수 있기 때문에 default target을 multi-user.target으로 바꾸고 수동으로 startx를 해야만 한다. default target을 변경하는 명령은 아래와 같다.
systemctl isolate multi-user.target
위 명령을 성공한 뒤에, startx를 실행했더니 아래처럼 실패가 발생한다. 아래 그림은 관련 화면을 캡쳐한 것이다.

로그 파일의 위치는 /var/log/Xorg.0.log로 보인다. X server가 종료되는 것을 확인 후 해당 위치를 가서 확인해본다. (종료되지 않는다면 강제로 Ctrl-C를 눌러 종료한다)

X 서버 로그는 information은 (II)로 표시하고, 에러는 (EE)로 표시한다. 따라서 (EE)문자열을 검색하면 빠르게 에러 위치를 찾을 수 있다. 위 그림에서 에러 위치에 AIGLX error: dlopen of /usr/lib64/dri/swrast_dri.so failed (/usr/lib64/dri/swrast_dri.so: undefined symbol: amdgpu_cs_ctx_stable_pstate) 으로 표시된 것을 볼 수 있다.
여기서 undefined symbol은 ABI가 깨진 것이다. 아마도 개발자가 함수명을 변경했을 것으로 보인다. 그러나 swrast_dri.so가 문제는 아니다. 반대로 swrast_dri.so에서 amdgpu_cs_ctx_stable_pstate를 호출하는 대상이 되는 shared object를 찾아야 한다. 그리고 해당 so를 업데이트해주는 것이 맞는데, 이걸 찾기 위해 역으로 호출하는 관계를 찾아야 한다.
2.2. rpm으로 오류의 원인 파일 찾기
패키지 관리툴인 rpm 명령의 query 옵션으로 /usr/lib64/dri/swrast_dri.so가 어느 패키지에 속한 파일인지 역추적해본다.

패키지 이름은 mesa-dri-drivers 이다. 이 패키지가 언제 변경되었는지 추적하기 위해 dnf history를 통해 살펴본다. 해당 패키지의 이력은 history id 1은 설치될 때이고, history id 26번은 bcc-tools를 설치할 때 업데이트 되었다.

자세한 트랜잭션을 보기 위해 26번 ID를 살펴보자. mesa-dri-drivers-21.3.4-2.el9.x86_64에서 mesa-dri-drivers-22.3.0-2.el9.x86_64로 업데이트 된 것을 알 수 있다.

dnf history info 26은 dnf install bcc-tools 명령을 의미하고, dependency에 의해mesa-dri-drivers가 upgrade 된 것을 볼 수 있다. 보기 쉽게 위 그림에는 노란색 화살표로 표시해두었다.
아래는 rpm -qR로 디펜던시도 확인해 보았다. 명령어가 길어서 아래에 history를 적어 두었다. 출력되는 명령어 순서는 괘념치 말자. 사실 이미 여기서 직감적으로 libdrm_amdgpu.so.1 가 범인인 것을 알 수 있었다. 왜냐하면 shared object가 호출하는 함수이름이 amdgpu_cs_ctx_stable_pstate이므로 amdgpu라는 단어가 들어갈 가능성이 높았기 때문이다. 설상가상으로 libdrm_amdgpu.so.1 파일이 등장하니까 금방 확인이 가능했다. 따라서 rpm -qf <library PATH>/libdrm_amdgpu.so.1으로 바로 찾을 수 있지만, 여기서는 직감보다는 좀 더 제너럴한 방법으로 해결 방법을 찾아가는 것을 살펴볼 것이다.

ldd swrast_dri.so 명령어로 링킹된 shared object 파일을 조사해보면 다음과 같이 볼 수 있다. 사실 안해봐도 되지만 뭐든지 2중, 3중으로 확인하는 것이 좋다.

2.3. ltrace로 추적하기
리눅스에는 프로그램의 실행을 추적하기 위해서 strace, ltrace를 제공한다. 이 2가지 툴은 개발자나 관리자에게 거의 필수적으로 배워야 하는 툴이다. strace는 system call tracer이고, ltrace는 library call tracer이다. 여기서는 사용자 라이브러리 함수인 amdgpu_cs_ctx_stable_pstate가 문제이므로 ltrace를 사용한다.
ltrace로 startx의 실제 실행 파일인 xinit가 실행될 때를 추적해보자. shared object는 메모리를 할당하는 과정이 포함되므로 malloc-free를 추적하면 쉽다. 사용된 옵션은 아래와 같다.
| -s <string length> | string length 기본값이 32라서 argument가 긴 경우 짤릴 수 있다. 보통 128~256을 많이 사용한다. |
| -o <file> | 화면 출력이 아닌 지정된 file 출력으로 변경한다. |
| -S | library call뿐만 아니라 system call도 같이 출력한다. |
| -f | fork를 follow한다. |

ltrace로 xinit를 추적한 뒤에 출력 파일인 ltrace_xinit.txt를 열어서 추적하다보면 dlopen하는 부분에서 swrast_dir.so를 여는 부분을 찾을 수 있다. 그리고 swrast_dir.so를 open한 뒤에 어느 순간에 amdgpu_cs_ctx_stable_pstate를 호출하다가 undefined symbol 오류를 내는 부분이 나올 것으로 예측할 수 있을 것이다.
파일을 오픈하는 함수는 openat이므로 openat@SYS를 찾으면 된다. 아래 그림에 표시해 두었다.

위 그림에서 openat@SYS을 살펴보면 각각 오픈하는 so(shared object) 파일들이 libglapi.so libdrm_radeon.so, libdrm_amdgpu.so, libelf.so등으로 보인다. 여기서 libelf.so는 시스템에서 기본으로 사용하는 shared object이므로 범인이 아니다. 참고로 2171 openat@SYS(AT_FDCWD, "/lib64/libdrm_amdgpu.so.1"을 오픈하는 것을 볼 수 있는데, 직후 _dl_catch_exception 익셉션은 그냥 발생하는 것이므로 에러가 아니다. 그러면 이제 원인이 된 라이브러리 목록을 알았으니 하나하나 찾아가면 된다.
찾는 방법은 심볼 이름들로 찾으면 되는데, static library는 nm으로 찾지만 so는 readelf -s (-s옵션은 --syms로 심볼을 본다는 뜻)으로 찾는다. 따라서 여기서는 readelf를 사용한다.
그런데 symbol name이 길면 [...]으로 축약되어 출력된다. 그냥 볼 때는 별 상관이 없지만 자세한 심볼 이름이 필요한 경우는 민폐다. 따라서 -W (--wide) 옵션으로 symbol의 fullname을 보는 경우가 많다.

위와 같은 방법을 쓰다보면 amdgpu_cs_... 어쩌구가 있는 라이브러리인 libdrm_amdgpu.so.1을 찾을 수 있다. 그리고 rpm -qf로 해당 파일이 어느 패키지에 속해있는지 추적해보면 libdrm 인 것을 알 수 있다.

이제 dnf update libdrm 으로 문제를 일으킨 패키지를 업데이트 해주자.(간접 의존성이 있어서 자동 업데이트가 안된 것이므로 이렇게 수동으로 업데이트하면 된다.)
dnf update libdrm
업데이트가 완료된 뒤에는 다시 graphical.target으로 돌아가야 한다. systemctl 명령어를 아래와 같이 사용한다. (재부팅해도 되지만 사실 재부팅을 안하고 해결하는 것이 제일 좋은 방식이다. 실제 서버나 워크스테이션은 정말 최후의 상황에만 재부팅을 한다. 시스템 관리자나 개발자는 재부팅 없이 문제를 해결하는 방법을 찾아야만 한다.)
systemctl isolate graphical.target
이제 제대로 작동하게 될 것이다. 위와 같은 경우는 흔하지는 않지만 간혹 시스템에서 발생되는 문제이다. 모든 패키지가 의존성이 완벽하지 않아서 A패키지가 의존성에 의해 B를 추가 업그레이드 했는데, 다른 패키지인 X가 Y를 의존하고, 하필이면 Y가 B의 기능을 사용하는 관계라면 어떻게 될까? 이런 경우 B의 ABI가 변경된 경우, 즉 위와 같이 symbol name이나 argument가 변경되었다면 이를 인지하지 못한 X, Y를 호출하면서 오류가 발생한다. 이런 경우 Y를 업그레이드 해주면 되지만, 그걸 찾아내려면 위와 같이 하나하나 찾아내야 한다. 이렇게 indirect한 하위 의존성은 복잡한 패키지 구조에서는 찾기 힘들 때가 있다.
물론 모든 패키지를 다 업데이트하면 해결되는 경우도 있지만, 안정성 때문에 security에 관련된 최소한의 업데이트만 하는 경우가 많기 때문에 핀셋처럼 딱 찾아내는 방법도 알아둬야만 한다. 위 예시는 그런 경우를 찾아내는 방법이다.
이제 libdrm를 업데이트 한 뒤에 /lib64 디렉터리로 이동해서 readelf로 해당 심볼을 제대로 찾아 낼 수 있다.

2.4. 원인과 결과
나중에 알게되었는데, 이 강의말고 이전 강의에 bpf를 이용한 시스템 문제 해결, 튜닝 수업이 있었는데, 해당 강의 자료 PDF가 들어있는 디렉토리를 수강자분께서 보고 관심이 생겨서 쉬는 시간에 실습하셨다고 했다. 그래서 bcc-tools를 설치했다가 의존성에 의해 mesa-dri-drivers가 업데이트 되어 발생한 문제였다. (원래 수강자분이 듣던 강의는 bash shell programming이라서 bcc-tools를 쓸 일이 없으므로 설치할 일도 없었다.) 궁금증이 원인이었는데, 수강자분은 딴짓해서 죄송하다고 했지만, 사실 이렇게 궁금증이 많아서 이것저것 해보는 분들이 나중에 대성하는 경향이 있어서 오히려 기운을 복돋아 드렸다. 나중에 높은 사람이거나 유명한 사람이 되면 나를 좀 이끌어 달라고... -_-;;
3. LD_DEBUG를 사용하는 방법(꼼수)
이건 팁이면서 다른 방식의 해결법이다. 사실 이 방법이 더 쉽다. startx 실행시 아래처럼 LD_DEBUG=libs를 주고 실행하면 아래와 같은 출력을 볼 수 있다. 이 기능은 모든 CLI 명령어에 쓸 수 있다. 예를 들어 ls에 붙이면 ls가 호출하는 라이브러리를 모두 볼 수 있다.
LD_DEBUG=libs startx
LD_DEBUG=libs startx의 실행 결과는 아래 그림과 같다. 여기서 find library부분에 어떤 라이브러리를 참조하는지 나오는데, swrast_dri.so가 참조되기 전에 가져온 라이브러리 파일 경로를 보면 된다. 여기서 보이는 파일은 libglapi.so, libdrm_radeon.so libdrm_amdgpu.so, libelf.so, libxcd-dri3.so뿐이다. 이들만 readelf, rpm -qf, dnf history 로 조사해보면 금방 오류를 확인할 수 있다. 이 방법은 약간 꼼수에 가깝지만 실력자들이 많이 사용하는 기능이다.

LD_DEBUG에는 libs 말고도 reloc, files, bindings, unused 등등... 다양한 기능이 있으므로 높은 수준의 개발자나 admin 기법을 배우고자 한다면 꼭 살펴보도록 하자. 이런 고급 꼼수 기법은 아무나 가르쳐주는 기법이 아니므로, 알아두면 정말 큰 도움이 될 것이다.
히스토리
2025.11.29 첫 릴리즈.
'컴퓨터 관련 > 리눅스 데스크탑' 카테고리의 다른 글
| Zeuslab 터치 모니터의 터치 범위 설정 (0) | 2025.10.12 |
|---|---|
| 삼성 갤럭시북3 갤럭시북4 스피커 사운드 및 Fn키 설정 (0) | 2025.10.10 |
| 리눅스에서 제우스랩 P16KT 터치 기능 끄기 (0) | 2025.06.23 |
| gnome-tweaks로 리눅스 데스크탑 환경의 미세 조정 (0) | 2025.01.19 |
| 페도라 리눅스의 한글 입력기를 fcitx5로 교체하기 (2) | 2024.12.13 |
| 리눅스용 멀티미디어 프로그램 - 유튜브 플레이어, 다운로더 (7) | 2024.07.23 |
| Audacious 리눅스 & 윈도 MP3 플레이어 (0) | 2024.05.31 |
| 터치패드 켜기/끄기 토글 기능 - Fedora Linux (4) | 2024.01.08 |