2P by GN⁺ 4시간전 | ★ favorite | 댓글 1개
  • initrd를 커널이 직접 해석하고 실행하는 프로그램 단위로 정의하며, Linux를 일종의 인터프리터로 재해석함
  • kexec, base64, cpio를 이용해 자기 자신을 재부팅하는 재귀형 Linux 배포판을 구성, initrd가 스스로를 다시 실행함
  • /init 스크립트가 자신의 cpio 이미지를 출력하도록 하면 Quine 형태의 자기 복제 initrd가 형성됨
  • ELF 실행 구조와 ld.so, binfmt_misc를 통해 인터프리터 계층이 커널까지 이어지는 구조를 설명함
  • kexec나 QEMU를 활용하면 Linux 위에서 또 다른 Linux를 꼬리 재귀적으로 실행할 수 있어, 커널·가상화·인터프리터의 경계를 실험적으로 확장함

rkx.gz 역공학과 자기 재귀형 initrd 구조

  • curl https://astrid.tech/rkx.gz | gunzip | sudo sh 명령은 20MB 크기의 base64 인코딩된 셸 스크립트를 다운로드하고 실행함
    • 스크립트는 root 권한을 확인하고 kexec, base64, cpio의 존재를 검사
    • base64 데이터를 디코딩해 r이라는 cpio 아카이브를 만들고, 그 안에서 k라는 커널 이미지를 추출
    • kexec를 이용해 k를 커널로, r을 램디스크로 로드 후 실행
  • r.cpio 내부에는 /bin, /init, k 파일이 포함되며, kLinux 6.18.18 커널 이미지, /init은 셸 스크립트 형태
    • /init/proc을 마운트하고, /r에 현재 파일 시스템을 cpio로 묶은 뒤 kexec/k/r을 다시 실행
    • 결과적으로 자기 자신을 계속 재부팅하는 재귀형 Linux 배포판이 됨

Linux 커널을 인터프리터로 보는 관점

  • initrd는 단순한 부팅용 램디스크가 아니라, Linux 커널이 해석하고 실행하는 프로그램으로 볼 수 있음
    • curl | shpython3 script.py처럼 initrd도 커널에 의해 실행되는 입력 프로그램 형태
    • 따라서 Linux 커널은 initrd를 해석하는 인터프리터로 기능
  • 이 구조는 꼬리 재귀 최적화(tail-call optimization) 와 유사함
    • kexec는 이전 커널을 덮어쓰지 않고 새로운 메모리 공간에 로드해 실행
    • 각 커널은 이전 상태를 유지하지 않으며, 새로운 “스택 프레임”으로 교체됨

Quine과 initrd 자기 복제

  • Quine은 자기 자신을 출력하는 프로그램을 의미
    • /init 스크립트가 마지막에 cat /r을 수행하도록 하면 자신과 동일한 cpio를 출력
    • 이 경우 Linux initrd 인터프리터의 Quine이 형성됨
    • 모든 파일이 RAM 상의 tmpfs에 존재하므로 실제 디스크 I/O는 발생하지 않음

ELF, ld.so, 그리고 인터프리터의 계층

  • ELF 실행 파일은 헤더에 인터프리터(ld-linux-x86-64.so.2) 경로를 포함함
    • 실행 시 커널은 ld.so를 먼저 실행하고, ld.so가 ELF의 동적 라이브러리를 로드 후 프로그램을 실행
    • 따라서 ELF도 일종의 인터프리터 언어로 볼 수 있음
  • /bin/shld.so에 의해 해석되고, ld.so는 커널이 직접 해석
    • ld.so는 정적 링크된 ELF이므로 커널이 직접 실행 가능
    • 이로써 인터프리터 계층의 기저(base case) 가 형성됨

binfmt_misc를 통한 CPIO 실행

  • binfmt_misc를 이용하면 특정 매직 바이트를 가진 파일을 지정된 인터프리터로 실행 가능
    • 예시 등록 명령:
      echo ':cpio:M::\x30\x37\x30\x37\x30\x31::/path/to/my/script.sh:' > /proc/sys/fs/binfmt_misc/register
      
    • 이 설정으로 chmod +x된 CPIO 파일을 직접 실행 가능
  • QEMU를 이용해 CPIO를 initrd로 실행하는 스크립트를 인터프리터로 등록할 수 있음
    • QEMU는 지정된 커널과 initrd를 사용해 가상 머신을 부팅
    • 결과적으로 CPIO 파일의 인터프리터는 QEMU가 구동하는 Linux 커널이 됨

재귀적 인터프리터와 “가장 이상한 루프”

  • QEMU 기반 인터프리터는 새로운 Linux 환경 스택 프레임을 생성
    • Linux 위에서 또 다른 Linux를 실행하는 구조로, 메모리 한계까지 중첩 가능
    • kexec 기반 인터프리터로 대체하면 꼬리 호출 최적화된 재귀형 Linux 실행이 가능
  • /init에서 binfmt_misc를 등록하고 /r을 실행하도록 구성하면 자기 자신을 실행하는 initrd가 완성됨
    • /r은 CPIO 포맷의 다음 init 프로세스이며, 실행 시 다시 자신을 해석

결론

  • initrd는 단순한 부팅 도구가 아니라, Linux 커널이 해석하는 프로그램 단위
  • kexecbinfmt_misc를 이용하면 Linux 자체를 인터프리터처럼 재귀적으로 실행 가능
  • 이 구조는 커널, 가상화, 인터프리터, 자기 복제 프로그램의 경계를 허무는 실험적 개념
  • 관련 소스코드는 GitHub 저장소 ifd3f/rekexec에 공개됨
Hacker News 의견들
  • 이 글을 읽으면서 너무 많은 오해 때문에 고통스러웠음
    cpio 아카이브는 파일시스템이 아님. 글쓴이는 initramfs를 사용하는데, 이는 tmpfs 기반임. Linux는 cpio를 tmpfs로 추출할 수 있음. 파일과 디렉토리의 아카이브는 그 자체로 프로그램이 아님
    어떤 것이 비슷하게 보인다고 해서 동일한 것은 아님. 바이너리 프로그램은 CPU에서 실행되며, 인터프리터가 있다면 그것은 하드웨어 환경에 숨어 있음. 이는 커널의 범위를 벗어남
    쉘 스크립트를 실행하려면 그 스크립트를 해석할 쉘이 필요함. 글쓴이는 이 부분을 생략하고 커널과 쉘 프로그램을 혼동함
    Linux는 initramfs나 ramdisk 없이도 컴파일 가능하며, 여전히 파일시스템의 유저랜드를 실행할 수 있음
    “Linux initrd interpreter”라는 표현은 정말 잘못된 설명임

    • ELF 파일도 그 자체로는 프로그램이 아닐 수 있음. 일부 ELF는 동적 라이브러리로 엔트리포인트가 없기 때문임. ELF 중 일부가 실행 가능하듯, CPIO 중 일부도 실행 가능하다고 볼 수 있음. 결국 ld.so가 ELF를 메모리에 풀고 엔트리포인트를 실행하는 것과, 커널이 initramfs를 풀고 엔트리포인트를 실행하는 것은 유사한 개념임
    • cpio 안의 init 파일이 실제로 해석되는 프로그램이며, 나머지 파일들은 그 프로그램이 사용할 메모리 역할을 함
    • 바이너리 프로그램은 CPU에서 실행되지만, 프로그램 파일 자체는 여러 섹션으로 구성된 아카이브 구조임. CPU는 프로그램 파일을 직접 이해하지 못함. Linux는 프로그램이 실행될 주소 공간을 설정하고, 그 후 프로그램 카운터가 가리키는 주소로 점프함. ELF의 메타데이터 섹션이 이 과정을 정의함
    • 적어도 AI가 쓴 글은 아니라는 점이 위안임
  • 모든 OS는 커널 권한으로 머신 코드 인터프리터 역할을 하는 것 아님?

    • 아니라고 생각함. OS는 각 명령어를 직접 해석하지 않고, CPU에 넘겨서 실행시킴
    • OS는 시스템 자원을 사용할 수 있게 하는 인터페이스임. CPU가 머신 코드를 해석하고, OS는 CPU가 무엇을 실행할지 지시할 수 있음
    • 이 경우에는 CPIO 파일을 위한 인터프리터라고 볼 수 있음
  • 이 글은 “Linux는 인터프리터다”라는 정신적 모델로 보면 괜찮지만, 문자 그대로 받아들이면 틀림
    CPU 명령어 수준에서의 해석이 아니라, 커널이 ELF, shebang 스크립트, initramfs 같은 실행 형식을 조율하는 역할로 보면 더 타당함. 혼란은 ‘인터프리터’의 두 가지 의미가 섞인 데서 비롯된 듯함

  • 핵심은 비유가 맞느냐가 아니라, ‘실행’이라는 개념이 얼마나 환경에 의존적인가를 보여준다는 점임

  • “모든 것은 인터프리터다?”

    • 그렇지만 컴파일러는 예외임
  • Turing의 Theta Combinator

    • 그게 이 글과 어떤 관련이 있는지 잘 모르겠음. 함수형 프로그래밍 개념에 익숙하지 않음
  • 시리즈의 이전 글에서, 글쓴이는 Contabo의 오브젝트 스토리지를 쓰기 싫어서 직접 VPS 이미지를 만들었다고 함
    월 1.50달러를 아끼려 50시간을 쓰는 것과, 25만 달러를 토큰에 쓰는 극단 사이에 균형점이 있다고 생각함.
    인프라 비용을 감당 못 한다면, 기술력보다 사회적 요인이 문제일 수도 있음. Doom을 curl로 돌리는 데 집착하는 건 생산적이지 않다고 느낌

    • 나도 예전에 그랬음. VPS 월 5유로가 너무 비싸서, 돈이 모일 때까지 인스턴스를 종료하고 루트 파일시스템을 엄마 노트북에 백업하곤 했음. 나중엔 Kindle에 Terminal IDE를 깔아 busybox와 gcc로 놀았음. Spartacus Rex에게 커리어의 시작을 만들어줘서 감사함
    • 글쓴이의 말은 농담이었음. 실제 이유는 바로 다음 문단에 있음 — “재미있는 트릭이라 생각했고, 블로그에 올리면 나도 배우고 독자도 배우고 인터넷 포인트도 얻는 win-win이라 생각했음”
    • 어떤 사람에게는 비생산적으로 보여도, 특별한 관심사를 즐기는 건 정신 건강에 중요함. ADHD인 나에게는 오히려 꼭 필요한 활동임
    • “1.50달러를 못 내면 프로가 아니다”라는 말은 이상함. 프로페셔널은 돈을 받느냐로 정의되는 것이지, 지출로 정의되는 게 아님
  • man ld.so를 보면, ELF의 .interp 섹션에 저장된 동적 링커가 실행된다고 명시되어 있음. 섹션 이름 자체가 흥미로움

  • Linux는 프로그래머블 인터페이스로서 매우 유용함. Windows도 가능하지만, Linux가 더 적합하다고 느낌
    GUI는 Windows가 낫다고 생각하지만, GNOME이나 KDE도 불편함. 그래서 fluxbox, icewm, 때로는 xfce나 mate-desktop을 씀. 요즘은 단순하고 빠른 환경을 선호함. 대부분의 작업은 커맨드라인과 코드 편집으로 처리함

    • 빠르고 단순한 환경을 원한다면 Sway + foot 조합이 좋음. 키바인드로 워크스페이스를 구성하면 데스크탑 없이도 쾌적하게 쓸 수 있음
    • Windows GUI가 낫다는 건 동의 못함. GNOME, KDE도 별로지만 Windows는 Microsoft의 복잡한 WM에서 벗어날 수 없음. 개인적으로는 Xerox 계열보다 mpx/mux 계열 인터페이스(예: 9wm, cwm, dwm)가 훨씬 낫다고 생각함. Engelbart의 철학에 더 가깝고 전반적으로 더 깔끔함