PLT 와 GOT

declspec의 이미지

리눅스에서 공유라이브러리상의 함수를 호출할때
PLT 와 GOT 에 대한 얘기가 나옵니다.

설명을 봐도 좀 어려워서 이해가 쉽지않은데
명쾌하게 설명해주실 분 안계신가요? ㅠ.ㅠ

그리고 요즘 32비트 OS 에서 모든 프로세스는 4G 의 가상주소공간을 사용하는데요
공유 라이브러리(libc 같은) 상의 함수에 대한 실제적인 실행 코드들이 4G 의 가상주소공간
어딘가에 매핑이 되는 것으로 알고 있는데요(잘못된게 있으면 알려주세요)
매핑되는 위치가 언제나 고정되는게 아닐텐데
그렇다면 컴파일 타임때 printf 를 호출하는 소스코드가 있는경우
컴파일러는 이 printf 함수의 시작주소(가상주소)를 어떻게 결정하나요?
여기서 PLT, GOT 에 대한 얘기가 답이 되는거같긴한데 이게 어떻게 되는건지 확실히 모르겠네요

그리고 커널메모리영역과 유저메모리영역은 여기서는 상관없는 이야기인가요?
즉 printf 함수는 c 라이브러리 함수이고 그 내부에서 시스템콜을 호출하는 것이니까
printf 함수의 진입점에 갔을때까지는 계속 유저권한으로 실행이 되는 것이겠죠?

프로세스의 흐름이 커널 메모리 영역에 진입해서 뭔가를 수행하는것은
시스템콜이 호출될 때만 그렇다고 봐도 될까요?

정리하자면
1. 컴파일러는 printf 의 진입점주소를 어떻게 결정하나
2. PLT, GOT 는 프로세스별로 있는가? 시스템 전체가 공유하는가?, 메모리상에 어디있는 테이블이고 어떻게 사용되는가?
3. 4G 가상주소 내에서 커널메모리영역, 유저메모리영역 이 어떻게 나뉘어지는가?
4. 시스템콜의 실행코드들은 커널메모리영역에 위치하는것인가?
5. 커널메모리영역의 실행코드를 수행하는것이 곳 커널모드(특권)로 CPU 가 작동하는것인가?
아니면 커널메모리영역 이라는 말과 커널모드는 별개의 의미인가?

좋은 답변 기대해봅니다

grassman의 이미지

1. 컴파일러는 printf 의 진입점주소를 어떻게 결정하나

정적 링크에서는 특별히 어려운 문제가 아니므로 Linux ELF의 동적 링크에 대해서만 설명합니다. (동적 링크 방법은 운영체제마다 각각 다르게 구현할 수 있는 문제입니다. 따라서 본문의 주 관심사인 ELF에 대해서만 설명합니다. Linux로 제한한 이유는 제가 Linux 이외의 ELF 포맷을 쓰는 운영체제의 dynamic loader 이름을 모르기 때문입니다.)
컴파일러는 printf라는 함수를 호출하기 위한 GOT(Global Offset Table)을 생성하고 printf 함수의 호출이 필요한 부분에서 printf의 GOT entry 주소로 분기하도록 합니다. GOT entry가 symbol의 주소로 초기화되지 않았을 때에는 ld-linux.so(.?)라는 dynamic loader를 호출하도록 되어 있습니다. dynamic loader의 symbol resolver 함수가 해당 라이브러리를 메모리에 적재한 뒤 printf 함수의 주소를 찾아서 GOT entry에 printf 함수 주소로의 분기 명령을 기록합니다. 이후로는 printf의 GOT entry로 분기하면 그 다음부터 printf 함수의 주소로 다시 분기하게 됩니다.

2. PLT, GOT 는 프로세스별로 있는가? 시스템 전체가 공유하는가?, 메모리상에 어디있는 테이블이고 어떻게 사용되는가?

프로세스 별이 아니라 object마다 있습니다. 실행 파일 및 so library를 구성하는 object 파일마다 있습니다. 컴파일러가 각각의 실행 파일을 생성할 때 .got 및 .plt라는 별도의 section으로 작성해 줍니다. PLT와 GOT는 개별 프로세스마다 다르게 나타납니다. 하지만 로딩된 라이브러리의 code section은 공유됩니다. (주소 매핑은 다르겠지만 실행되는 code는 메모리의 단일 공간에 위치합니다) 메모리 어디에 위치하는지는 dynamic loader와 kernel만이 알겠지요.

3. 4G 가상주소 내에서 커널메모리영역, 유저메모리영역 이 어떻게 나뉘어지는가?

32-bit Linux의 경우 user 3G, kernel 1G 또는 user 2G, kernel 2G로 나눌 수 있는 것으로 알고 있습니다.

4. 시스템콜의 실행코드들은 커널메모리영역에 위치하는것인가?

시스템 콜로 커널 모드를 진입한 이후라면 커널 메모리 영역에 위치한 코드가 동작합니다. (물론 유저 메모리 영역의 코드도 수행할 수 있습니다만 특별히 필요로 하지 않는 이상 안합니다) 시스템 콜 수행 전이나 수행 후의 코드는 모두 유저 메모리 영역에서 동작합니다.

5. 커널메모리영역의 실행코드를 수행하는것이 곳 커널모드(특권)로 CPU 가 작동하는것인가? 아니면 커널메모리영역 이라는 말과 커널모드는 별개의 의미인가?

일반적으로 특권 모드가 존재하는 CPU라면 MMU를 사용하는 Linux에서 커널 메모리 영역으로 user process가 접근할 수 있는 방법은 없습니다. 즉, 커널모드로 동작해야 커널 메모리 영역을 접근할 수 있습니다.

oosap의 이미지

4번과 5번 답변관련 내용입니다.

프로세스는 힙, 스택, 데이타, 코드, .bss 섹션들로 크게 구분되잖아요. 이것들은 가상주소의 '유저영역'에 자리잡게 될 것으로 생각됩니다. 32비트 머신에서는 3GB 공간을 차지하겠지요. '커널영역' 1GB 에는 다음과 같은 것들이 자리잡히게 된다고 읽었습니다.

BIOS comm area
Kernel code image
DMA region
mem_map
Direct Mapping
vmalloc
kmap
Fixed mapping

그런데 위에서 보면 커널영역에는 커널코드의 이미지는 있는데 힙이나 스택 .bss 라는 이름은 안보여요. 그래서 짐작컨데 유저모드에서는 3GB 유저 영역에서 코드가 수행되고 코드 수행 도중에 커널 콜이 발생되면 트랩이 발생되면서 커널모드로 전환되어 1GB 영역의 커널 코드가 수행되는데

커널모드에서는 힙과 스택이라는 영역이없고 커널만의 독특한 메모리 레이아웃을 운용해서 CPU를
사용한다.

라고 보면 맞는 것일까요?
그리고 또 하나,
커널 영역 1GB 는 모든 프로세스가 공유하는 것이다. 그러니까 헬로월드라는 프로세스가 로더에
의해 메모리에 로드될 때는 커널 영역은 이미 존재(사용중)하므로 헬로월드의 4GB 가상메모리가
만들어질 때 커널영역은 기존의 것이 그냥 매핑되고 유저공간만 추가로 더해져서 4GB 가상
메모리를 형성하고 프로세스가 수행되고 프로세스가 종료할 때는 반대로 4GB 가상메모리가
제거될 때에 커널공간은 그대로 남아있고 단지 언매핑만 이루어지고 유저공간만 제거되는 것

이라고 생각하면 맞는 것일까요?
그러니까
커널공간은 부팅시 부트로더가 로딩해줄 때 한번 로드되고 모든 다른 프로세스들로부터
공유되며 시스템이 종료할 때 마지막으로 언로드 된다.

태그로 표시한 부분이 제가 '짐작'하는 바이고 그걸 '확인'받고 싶습니다. ^^;
제 '짐작'이 맞을까요?

이거 예전부터 궁금했는데 커널 공부를 중단해서 아쉽게도 아직 모릅니다 TT 다시 커널 공부를 해보고 싶네요..

http://kldp.org/node/122682

제가 예전에 만든 쓰레드입니다.
그때 궁금했던 내용중에 마이크로 커널의 이야기도 아직도 궁금하네요.
혹시 마이크로 커널의 경우 제 생각대로 커널 공간이라는 것이 정말 없거나 최소화 되어있을까요? 그러니까 인터럽트 핸들링을 하는 정도의 최소한의 부분만 커널영역 비슷한 공간을 차지하고 나머지 커널의 기능들은 별도의 가상주소를 가지고 실행되는게 아닐까... 그래서 마이크로 커널이겠지요..

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

grassman의 이미지

커널 모드에서도 동일하게 스택과 힙이 존재합니다. (당연한 얘기지만 stack과 data 영역이 없으면 변수 처리 및 함수 호출이 불가능합니다) 그리고 커널 모드에서 실행할 코드 역시 정상적인 주소 공간 내에 있어야 하므로 4G 영역의 어느 지점에 존재할 수 밖에 없고요.

태그로 표시한 내용 중 첫번째를 제외한 나머지 2개의 내용은 특별히 반론의 여지가 없는 듯 합니다. 단, 커널 메모리 영역은 유저 모드에서 접근이 일체 불가능합니다. 따라서 매핑을 할 이유도 없습니다.

oosap의 이미지

답글 감사드립니다.
커널을 언젠가 공부해봐야 하겠다.. 생각만 하고 있네요..

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

oosap의 이미지


3. 4G 가상주소 내에서 커널메모리영역, 유저메모리영역 이 어떻게 나뉘어지는가?

리눅스에서 32비트 머신에서 한 프로세스는 가상 주소 공간 4GB 를 가집니다. 그 중
0 ~ 3 GB 는 유저 공간
3 ~ 4 GB 는 커널 공간 입니다.
유저 공간에는 그 프로세스의 코드, 데이타(.bss 포함), 스택, 힙이 자리합니다.
커널 공간에는 책(리눅스 커널 내부구조 - 교학사)에서 설명하기로는
32 비트 머신은 BIOS comm area/Kernel code image/DMA region/mem_map/Direct Mapping/vmalloc/kmap/Fixed mapping
64 비트 머신은 module mapping space/unused hole/kenel text mapping/ioremap space/direct mapping space/guard hole
이 그 공간에 존재하게된다고 하는데요,(64비트머신에서는 가상메모리의 크기도 더 크고 레이아웃도 다르겠죠)

책에 있는 내용입니다.
저도 공부를 많이 못해서 책 내용 수준에서만 답변해봅니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

익명 사용자의 이미지

답변 모두 도움이 되었습니다
감사합니다~

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.