SoC 벤더 커널을 Clang으로 옮길 때 깨지는 패턴 정리

메인라인 커널은 이미 Clang / LLVM 빌드를 공식 지원한다.
하지만 SoC 벤더 커널(특히 4.x/5.4 계열)이나 오래된 BSP 트리는 여전히 GCC 전용 코드가 많다.

리눅스 커널 Clang 빌드 호환성은 아래 글에서 대략의 타임라인을 확인할 수 있다.

이 글에서는 GCC에서는 되는데 Clang에서 깨지는 대표 패턴을 유형별로 정리한다.
실전에서 가장 자주 마주치는 것들만 모았다.

Clang 빌드 적용시 깨지는 지점은 대체로 4군데다.

  1. inline asm
  2. GCC 전용 확장 문법
  3. section / attribute 처리
  4. 링커(ld.lld) 차이

1. Inline Assembly (가장 많이 깨짐)

1-1. 잘못된 constraint 사용

GCC 전용 패턴

asm volatile("..." : "=r"(val) : "0"(val));
  • GCC는 느슨하게 허용
  • Clang은 constraint mismatch에 엄격

해결 방향

  • constraint 명확히 지정
  • "=r" / "r" 분리
  • early-clobber ("=&r") 필요한지 확인

1-2. clobber 누락

asm volatile("mov %0, %%cr3" : : "r"(val));

Clang은 memory clobber 누락을 더 잘 잡는다.

수정

asm volatile("mov %0, %%cr3" : : "r"(val) : "memory");

1-3. Integrated Assembler 문제

Clang 기본은 IAS(통합 어셈블러)다.

GCC 빌드에서 GNU as에 의존하던 코드가 깨질 수 있다.

임시 해결

make LLVM=1 LLVM_IAS=0

그러나 근본 해결은

  • .S 문법을 GNU as 의존에서 벗어나게 수정
  • .arch, .syntax directive 점검

2. GCC 전용 확장 문법

2-1. Statement Expression 남용

#define foo(x) ({ int y = x; y + 1; })

Clang도 지원하지만 복잡한 nested 매크로에서 깨지는 경우 있음.

2-2. typeof + 비표준 패턴

typeof(a) b;

Clang은 __typeof__ 형태를 선호.

안전한 형태

__typeof__(a) b;

2-3. asm goto 미지원 구버전

오래된 Clang에서는 asm goto 지원이 미흡했다.

벤더 커널이 4.x 계열이면 문제 발생 가능.

3. section / attribute 관련 문제

3-1. section attribute 정렬 문제

__attribute__((section(".my_section")))

Clang은 alignment 요구가 엄격하다.

해결

__attribute__((section(".my_section"), aligned(8)))

3-2. weak symbol 처리 차이

GCC는 관대
Clang은 duplicate weak symbol에서 에러 발생 가능

특히:

  • 드라이버
  • vendor HAL

에서 자주 발생

4. 링커(ld.lld) 차이

4-1. undefined symbol 처리

ld.bfd는 느슨하게 넘어가는 경우가 있다.
ld.lld는 바로 실패한다.

ld.lld: error: undefined symbol: foo

이 경우는 실제로 코드가 잘못된 경우가 많다.

4-2. orphan section 처리 차이

벤더 링크 스크립트가 오래된 경우

warning: orphan section `.init.something`

ld.lld는 섹션 정렬/배치 규칙이 더 엄격하다.

5. volatile / memory barrier 관련 차이

Clang은 최적화가 더 공격적이다.

GCC에서 “우연히” 동작하던 코드가

  • barrier 누락
  • READ_ONCE/WRITE_ONCE 누락

때문에 동작이 바뀌는 경우 있다.

➡️ Clang으로 옮기면서 숨어 있던 메모리 버그가 드러나는 경우가 많다.

6. LTO 활성화 시 추가 문제

벤더 커널은 LTO 전제를 두고 작성되지 않은 경우가 많다.

깨지는 대표 패턴:

  • static 함수가 최적화로 제거됨
  • inline 함수 visibility 문제
  • asm symbol name mismatch

7. BTF / pahole 연계 문제

Clang + BTF에서 깨지는 경우:

  • 오래된 pahole
  • struct layout 가정 코드
  • packed struct alignment 문제

8. 실제 포팅 전략 (현실적인 접근)

벤더 커널을 Clang으로 옮길 때는 이렇게 간다.

1단계

  • LTO 끄기
  • IAS 끄기
  • BTF 끄기
make LLVM=1 LLVM_IAS=0

2단계

  • inline asm 수정
  • attribute 정리
  • section 정렬 맞추기

3단계

  • IAS 다시 켜기
  • BTF 켜기
  • sanitizer 적용

9. 가장 자주 깨지는 TOP 5 요약

  1. inline asm constraint 문제
  2. GNU as 전용 문법
  3. section alignment 누락
  4. ld.lld의 엄격한 undefined symbol 처리
  5. memory barrier 누락 코드

10. 결론

벤더 커널이 Clang에서 깨진다는 건

Clang이 이상해서가 아니라
GCC가 너무 관대했기 때문인 경우가 많다.

Clang 전환은 단순한 컴파일러 교체가 아니라

  • 코드 품질 개선
  • 숨은 버그 노출
  • 장기 유지보수성 확보

의 과정에 가깝다.

참고

답글 남기기