Hacker News 의견
  • ELF 파일의 동적 링크 과정에 대해 설명함
    커널은 ELF의 PT_LOAD 세그먼트를 매핑하고, PT_INTERP로 지정된 동적 링커(ld.so) 를 로드한 뒤 제어를 넘김
    이후 동적 링커가 스스로 재배치(relocation)하고 필요한 공유 객체를 mmap/mprotect로 로드함
    이 구조는 스크립트의 shebang(#!) 메커니즘과 유사하다고 비유함

    • 커널은 섹션 정보에는 전혀 관심이 없고, 오직 PT_LOAD 세그먼트만 처리함
      예전에 objcopy로 ELF에 임의 파일을 삽입하려다 커널이 로드하지 않아 혼란스러웠던 경험을 공유함
      결국 직접 프로그램 헤더 테이블 패치 도구를 만들었고, mold 링커에도 이 기능이 추가되었다고 함
      관련 글: Self-contained Lone Lisp Applications
    • 작성자가 이전에 내용을 잘못 수정해 올렸음을 인정하고 수정하겠다고 함
    • 리눅스에서는 로더가 사용자 공간에서 동작하는데, 왜 더 다양한 로더가 없는지 항상 궁금했다고 함
  • 전체 코드를 main() 이전 혹은 main() 없이 패킹하는 실험을 했다고 함
    관련 글: Packing a codebase into a single function

    • 읽어보니 의외로 단순하고 취약하지 않아서 흥미로웠다고 함
      모든 함수를 main(100+n, ...) 형태로 바꾸면 된다고 농담함
  • 이 주제에 흥미가 있다면 자신이 만든 cpu.land를 참고하라고 함
    메모리 레이아웃보다는 멀티태스킹과 코드 로딩 과정을 다룸

    • cpu.land를 정말 좋아한다고 감사 인사를 전함
  • C 프로젝트 중 표준 라이브러리를 피하고 Linux syscall만 직접 호출하는 경우가 얼마나 될지 궁금하다고 함
    이렇게 코드를 짜는 게 훨씬 재미있다고 느낌

    • 직접 syscall을 쓰는 건 오히려 비효율적이라고 주장함
      ALSA, DRM 같은 기능은 커널 syscall 대신 시스템 라이브러리를 통해 접근하는 게 이점이 많음
      이 방식이 이식성과 유지보수성 면에서 Windows 스타일 접근보다 낫다고 설명함
    • Windows에서는 Win32 API만 사용하면 C 런타임을 링크하지 않아도 된다고 덧붙임
    • 자신도 예전에 liblinux 프로젝트를 만들어 syscall만으로 프로그램을 작성했었다고 함
      지금은 Linux의 nolibc 헤더가 잘 되어 있어 중단했지만,
      현재는 syscall 기반의 Lisp 인터프리터 언어를 개발 중이라고 함
      시스템 호출로 직접 Linux 유저 스페이스를 구성하는 실험이라 매우 흥미로운 여정이었다고 함
    • 이식성을 유지하려 하지만, 파일 디스크립터는 너무 편리해서 포기하기 어렵다고 함
    • 많은 드라이버 코드가 실제로 syscall만 사용한다고 덧붙임
  • ELF 인터프리터(ld.so)가 초기 ELF 세그먼트를 매핑한 후 모든 로딩을 담당한다고 설명함
    execve는 PT_LOAD 세그먼트를 매핑하고 aux vector를 스택에 채운 뒤
    ELF 인터프리터의 엔트리 포인트로 점프함
    커널은 PLT/GOT에 대해 아무것도 모름

  • 대학에서 이 주제를 가르치는 사람으로서, 학생들이 메모리 다이어그램 때문에 혼란스러워한다고 함
    교재는 주소가 높을수록 위쪽에 그려지지만, 실제 Linux 프로세스는
    낮은 주소가 위, 높은 주소가 아래로 출력됨
    /proc/<pid>/maps를 보면 스크롤을 아래로 내릴수록 주소가 커짐
    즉, “heap은 위로 자라고(stack은 아래로 자란다)”는 표현은 숫자상의 방향일 뿐,
    시각적으로는 오히려 반대임
    IDE처럼 아래로 갈수록 주소가 커지는 식으로 그리면 훨씬 직관적이라고 제안함

    • 스택은 어쨌든 스택 포인터가 감소하면서 자라므로 “아래로 자란다”는 표현이 여전히 맞다고 함
      다만 시각화는 가로 방향으로 하는 게 더 자연스럽다고 제안함
    • 자신도 예전에 같은 혼란을 겪었고, 리틀엔디언 주소 표기가 헷갈렸다고 회상함
    • 실제 사물의 쌓이는 방향을 생각하면 “스택이 아래로 자란다”는 표현이 직관적이지 않다고 반박함
  • 오래된 PIC16 마이크로컨트롤러로 이런 실험을 하는 걸 좋아한다고 함
    스택 포인터, 타이머, 변수 설정 등을 직접 다루는 게 재미있다고 느낌

  • shebang(#!) 관련 경험을 공유함
    Java 애플리케이션이 실행 스크립트를 찾지 못한다는 오류를 냈는데,
    실제 문제는 스크립트의 shebang 경로가 잘못된 것이었음
    로컬에서는 잘 실행됐지만, 원격 서버의 인터프리터 경로가 달라서 생긴 문제였음

    • 이건 Java만의 문제가 아니라, ENOENT 오류가 발생하는 모든 프로그램에서 생길 수 있다고 함
      strace로 실행하면 어떤 syscall에서 오류가 났는지 바로 확인 가능하다고 조언함
    • shebang의 구조를 분석한 글을 공유함: What the #! means
    • 커널에서 shebang을 지원하려면 CONFIG_BINFMT_SCRIPT=y 설정이 필요하다고 덧붙임
  • 디버깅 중에 메인 바이너리의 재배치 순서가 언제 적용되는지 항상 헷갈린다고 함
    링커가 자신의 심볼을 해결하기 전인지 후인지가 마치 블랙매직 같다고 표현함

  • 마크다운 내 “lang_start function (defined here)” 부분의 링크가 깨져 있음을 지적함