실행 중 바이너리 수정

실행 중 바이너리 수정

a.out개발할 때 바이너리 가 긴 작업을 수행하기 때문에 백그라운드에서 바이너리를 실행하는 상황에 자주 직면합니다 . 그 과정에서 a.out생성되어 다시 컴파일된 C 코드를 변경했습니다 a.out. 지금까지 아무런 문제가 없었습니다. 실행 중인 프로세스는 a.out충돌 없이 정상적으로 계속 실행되며 원래 시작된 이전 코드를 항상 실행합니다.

그러나 a.out이것이 아마도 RAM 크기 정도의 거대한 파일이라고 가정하십시오. 이 상황에서는 어떻게 되나요? 공유 객체 파일에 연결되어 있다고 가정하면 런타임에 수정하면 어떻게 libblas.so되나요 ? libblas.so무슨 일이야?

내 주요 질문은 - OS가 a.out원래 코드를 실행할 때 원본 코드가 항상 올바르게 실행되도록 보장하는가입니다.원래바이너리(링크된 바이너리나 파일의 크기에 관계없이 해당 파일 과 파일이 런타임에 수정 .so되더라도 )?.o.so

나는 다음 질문이 비슷한 문제를 다룬다는 것을 알고 있습니다. https://stackoverflow.com/questions/8506865/when-a-binary-file-runs-does-it-copy-its-entire-binary-data-into-memory-at-once 실행 중에 스크립트를 편집하면 어떻게 되나요? 프로그램이 실행되는 동안 어떻게 실시간 업데이트를 할 수 있나요?

이것은 내가 이것을 더 잘 이해하는 데 도움이 되었지만, 그들이 나에게 정확히 내가 원하는 것이 무엇인지 묻고 있었던 것 같지는 않습니다. 이것이 결과의 일반적인 규칙입니다.실행 중 바이너리 수정

답변1

처음에는 스택 오버플로 질문이 적절해 보였지만 귀하의 의견을 통해 이에 대해 여전히 질문이 있는 이유를 이해합니다. 나에게는 이것이 정확히 보이는 것입니다.긴급 순간두 개의 UNIX 하위 시스템(프로세스 및 파일)이 통신할 때 관련됩니다.

아시다시피 UNIX 시스템은 일반적으로 파일 하위 시스템과 프로세스 하위 시스템이라는 두 개의 하위 시스템으로 나뉩니다. 이제 커널은 시스템 호출을 통해 달리 지시하지 않는 한 이 두 하위 시스템이 서로 상호 작용하도록 허용해서는 안 됩니다. 그러나 한 가지 예외가 있습니다. 실행 파일을 프로세스에 로드하는 것입니다.텍스트 영역. 물론 이 작업도 execve시스템 호출( )에 의해 발생한다고 말할 수도 있지만 일반적으로 이는하나프로세스 하위 시스템이 파일 하위 시스템에 암시적으로 요청하는 상황입니다.

프로세스 하위 시스템은 기본적으로 파일을 처리할 수 없기 때문에(그렇지 않으면 전체를 두 부분으로 분할하는 것이 의미가 없으므로) 파일에 액세스하기 위해 파일 하위 시스템이 제공하는 모든 것을 사용해야 합니다. 이는 또한 프로세스 하위 시스템이 파일 편집/삭제와 관련하여 파일 하위 시스템에서 수행한 모든 작업을 따른다는 것을 의미합니다. 이 시점에서 나는 읽기를 권장합니다자일스의 대답도착하다이 U&L 질문. 내 답변의 나머지 부분은 Giles의 보다 일반적인 답변을 기반으로 합니다.

가장 먼저 주목해야 할 점은 내부적으로 파일을 통해서만 전달할 수 있다는 것입니다.인덱스 노드. 커널에 경로가 제공되면 첫 번째 단계는 이를 다른 모든 작업을 위한 inode로 변환하는 것입니다. 프로세스가 실행 파일을 메모리에 로드할 때 경로 변환 후 파일 하위 시스템에서 제공하는 inode를 통해 로드합니다. 인덱스 노드는 여러 경로(링크)와 연관될 수 있으며 프로그램은 링크만 삭제할 수 있습니다. 파일과 해당 inode를 삭제하려면 사용자 공간은 inode에 대한 기존 링크를 모두 삭제하고 완전히 사용되지 않도록 해야 합니다. 이러한 조건이 충족되면 커널은 자동으로 디스크에서 파일을 삭제합니다.

당신이 보면실행 파일 교체Gilles의 답변 중 일부에 따라 다르다는 것을 알 수 있습니다.수정/삭제하는 방법파일의 경우 커널은 항상 파일 하위 시스템 내에 구현된 메커니즘을 통해 다양한 방식으로 반응/적응합니다.

  • 첫 번째 전략을 시도하면(열기/0으로 자르기/쓰기또는새 크기로 열기/쓰기/자르기), 커널이 요청 처리를 방해하지 않는다는 것을 알게 될 것입니다. 오류 26이 발생합니다.텍스트 파일이 사용 중입니다.( ETXTBSY). 결과는 없습니다.
  • 전략 2를 시도하는 경우 첫 번째 단계는 실행 파일을 삭제하는 것입니다. 그러나 프로세스에서 사용 중이기 때문에 파일 하위 시스템이 시작되어 파일(및 해당 inode)이정말디스크에서 삭제합니다. 이 시점부터 이전 파일의 내용에 액세스하는 유일한 방법은 inode를 통하는 것입니다. 이는 프로세스 하위 시스템이 새 데이터를 로드해야 할 때마다 수행하는 작업입니다.텍스트 부분(내부적으로 경로를 inode로 변환하지 않는 한 경로를 사용하는 것은 의미가 없습니다.) 당신이 가지고 있더라도연결 해제됨파일(모든 경로 제거)을 삭제하더라도 프로세스는 아무 작업도 하지 않은 것처럼 해당 파일을 계속 사용할 수 있습니다. 이전 경로를 사용하여 새 파일을 생성해도 아무 것도 변경되지 않습니다. 새 파일에는 완전히 새로운 inode가 제공되며 실행 중인 프로세스는 이에 대해 아무것도 알지 못합니다.

전략 2와 3은 실행 파일에도 안전합니다. 실행 중인 실행 파일(및 동적으로 로드된 라이브러리)은 파일 설명자가 있다는 점에서 열린 파일은 아니지만 매우 유사한 방식으로 동작합니다. 프로그램이 코드를 실행하는 동안에는 디렉토리 항목이 없더라도 파일은 디스크에 남아 있습니다.

  • mv전략 3은 작업이 원자적이라는 점에서 매우 유사합니다 . 이를 위해서는 시스템 호출을 사용해야 할 수 rename있으며 프로세스는 커널 모드에서 중단될 수 없으므로 완료될 때까지(성공 여부) 이 작업을 방해할 것이 없습니다. 마찬가지로 이전 파일의 inode는 변경되지 않습니다. 새 파일이 생성되고 이미 실행 중인 프로세스는 이전 inode에 대한 링크 중 하나와 이미 연결되어 있더라도 이에 대해 알지 못합니다.

전략 3에서는 새 파일을 기존 이름으로 이동하는 단계에서 기존 콘텐츠로 연결되는 디렉터리 항목을 삭제하고 새 콘텐츠로 연결되는 디렉터리 항목을 생성합니다. 이는 하나의 원자 작업으로 수행되므로 이 전략에는 한 가지 주요 이점이 있습니다. 프로세스가 언제든지 파일을 열면 이전 콘텐츠나 새 콘텐츠가 표시됩니다. 혼합된 콘텐츠를 얻거나 파일이 존재하지 않을 위험이 없습니다. 기존의.

파일 재컴파일: 사용하면 gcc(다른 많은 컴파일러에서도 유사하게 동작할 수 있음) 전략 2를 사용하는 것입니다. strace컴파일러 프로세스를 실행하면 이를 확인할 수 있습니다 .

stat("a.out", {st_mode=S_IFREG|0750, st_size=8511, ...}) = 0
unlink("a.out") = 0
open("a.out", O_RDWR|O_CREAT|O_TRUNC, 0666) = 3
chmod("a.out", 0750) = 0
  • 컴파일러는 stat및 시스템 호출을 통해 lstat파일이 이미 존재하는지 감지합니다 .
  • 파일은연결 해제됨. 여기서는 더 이상 name을 통해 액세스할 수 없지만 a.out이미 실행 중인 프로세스에서 해당 inode와 내용을 사용하고 있는 한 디스크에 남아 있습니다.
  • 새 파일을 만들고 이름별로 실행 권한을 부여합니다 a.out. 이는 이미 실행 중인 프로세스에서 신경 쓸 필요가 없는 새로운 inode이자 새로운 콘텐츠입니다.

이제 공유 라이브러리에도 동일한 동작이 적용됩니다. 프로세스에서 라이브러리 개체를 사용하는 한 해당 연결을 어떻게 변경하더라도 디스크에서 제거되지 않습니다. 무언가를 메모리에 로드해야 할 때마다 커널은 파일의 inode를 통해 이를 수행하므로 링크에 대한 변경 사항(예: 새 파일과 연결)을 무시합니다.

답변2

내가 이해한 바는 실행 중인 프로세스의 메모리 매핑으로 인해 커널이 매핑된 파일의 예약된 부분을 업데이트하는 것이 허용되지 않는다는 것입니다. 프로세스가 실행 중이라면 모든 파일이 보존되고 따라서 실제로 새로운 inode 세트가 생성되는 소스 코드의 새 버전을 컴파일한 이후 업데이트됩니다. 즉, 이전 버전의 실행 파일은 페이지 오류 이벤트를 통해 디스크에서 계속 액세스할 수 있습니다. 따라서 대용량 파일을 업데이트하더라도~해야 한다액세스 가능한 상태로 유지되고 커널~해야 한다프로세스가 실행되는 동안에는 변경되지 않은 버전이 계속 표시됩니다. 원본 파일 아이노드해서는 안 된다프로세스가 실행되는 동안 재사용할 수 있습니다.

물론 이것은 아직 확인되지 않았습니다.

답변3

.jar 파일을 바꿀 때 항상 그런 것은 아닙니다. Jar 리소스와 일부 런타임 반사 클래스 로더는 프로그램이 명시적으로 정보를 요청할 때까지 디스크에서 읽혀지지 않습니다.

jar는 메모리에 매핑된 단일 실행 파일이 아니라 단순한 아카이브이기 때문에 이는 문제일 뿐입니다. 이것은 주제에서 약간 벗어났지만 여전히 귀하의 질문의 일부이고 제가 발에 총을 맞은 것입니다.

실행 파일의 경우 그렇습니다. jar 파일의 경우: 아마도(구현에 따라 다름).

관련 정보