programing

문자 배열/문자열은 바이너리 파일에 어떻게 저장됩니까?

yellowcard 2023. 6. 9. 21:56
반응형

문자 배열/문자열은 바이너리 파일에 어떻게 저장됩니까?

다른 컴파일러를 사용하여 이 코드를 컴파일하고 16진수 편집기에서 출력을 검사할 때 "낸시" 문자열을 어딘가에서 찾을 것으로 예상됩니다.

#include <stdio.h>

int main()
{
    char temp[6] = "Nancy";
    printf("%s", temp);

    return 0;
}
  1. 다음에 대한 출력 파일gcc -o main main.c다음과 같이 표시됩니다.

    sdf

  2. 에 대한 출력g++ -o main main.c"낸시"를 찾을 수가 없어요.

  3. 비주얼 스튜디오(MSVC 1929)에서 동일한 코드를 컴파일하면 16진수 편집기에 전체 문자열이 표시됩니다.

(1)의 문자열 중간에 랜덤 바이트가 있는 이유는 무엇입니까?

컴파일러가 생성하는 출력 파일에 데이터를 저장하는 방법에 대한 단일 규칙은 없습니다.

데이터는 "일정한" 섹션에 저장할 수 있습니다.

데이터는 명령어의 "즉시" 피연산자에 내장될 수 있으며, 여기서 데이터는 명령어를 인코딩하는 비트의 다양한 필드에 인코딩됩니다.

컴파일러에서 생성된 명령어를 사용하여 다른 데이터에서 데이터를 계산할 수 있습니다.

한 곳에서 "Nanc"를 보고 다른 곳에서 "y"를 보는 경우는 "Nanc"를 형성하는 바이트를 즉시 피연산자로 로드하는 로드 명령("mov"로 작성될 수 있음)과 뒤에 오는 널 문자로 "y"를 형성하는 바이트를 로드하는 다른 로드 명령을 사용하는 컴파일러일 것입니다.로드된 데이터를 스택에 저장하고 주소를 전달하는 다른 지침과 함께printf.

진단하기에 충분한 정보를 제공하지 않았습니다.g++case: 컴파일러 또는 해당 버전 번호의 이름을 지정하지 않았거나 생성된 출력의 일부를 제공하지 않았습니다.

저는 gcc 9.3.0(리눅스 민트 20.2)을 사용하여 x86-64 시스템(인텔)에서 복제했습니다.

의 결과hexdump -C:

여기에 이미지 설명 입력

바이트 시퀀스는 동일합니다.

그래서 사용합니다.gcc -S -c:

    .file   "teststr.c"
    .text
    .section    .rodata
.LC0:
    .string "%s"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    endbr64
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movq    %fs:40, %rax
    movq    %rax, -8(%rbp)
    xorl    %eax, %eax
    movl    $1668178254, -14(%rbp) # NOTE THIS PART HERE
    movw    $121, -10(%rbp)        # AND HERE
    leaq    -14(%rbp), %rax
    movq    %rax, %rsi
    leaq    .LC0(%rip), %rdi
    movl    $0, %eax
    call    printf@PLT
    movl    $0, %eax
    movq    -8(%rbp), %rdx
    xorq    %fs:40, %rdx
    je  .L3
    call    __stack_chk_fail@PLT
.L3:
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
    .section    .note.GNU-stack,"",@progbits
    .section    .note.gnu.property,"a"
    .align 8
    .long    1f - 0f
    .long    4f - 1f
    .long    5
0:
    .string  "GNU"
1:
    .align 8
    .long    0xc0000002
    .long    3f - 2f
2:
    .long    0x3
3:
    .align 8
4:

강조 표시된 값166817825416진수636E614E또는 "cnaN"(x86이 리틀 엔디언 시스템이기 때문에 "Nanc"가 됨),12116진수79또는 "y".

따라서 짧은 문자열인 경우 파일의 바이트 문자열 섹션에서 루프 복사 대신 두 개의 이동 명령을 사용하며, 중간에 있는 "가비지"는 다음과 같습니다.movw설명.최적화 플래그가 컴파일러에게 "공식적으로" 제공되지 않았음에도 불구하고 메모리를 통해 바이트 단위로 루프하는 것에 비해 초기화를 최적화하는 방법일 가능성이 높습니다. 즉, 컴파일러는 이와 관련하여 원하는 작업을 수행할 수 있습니다.따라서 마이크로소프트의 컴파일러는 컴파일 방식에서 더 "시학적"인 것처럼 보입니다. 왜냐하면 실제로는 문자열을 연속적으로 결합하는 것을 선호하기 때문에 최적화를 포기하기 때문입니다.

일반적으로 컴파일된 프로그램은 다른 유형의 "섹션"으로 나뉩니다.어셈블리 파일은 명령어를 사용하여 명령어 간에 전환합니다.

  • 코드(.text")
  • 정적 읽기 전용 데이터(.section .rodata")
  • 초기화된 전역 또는 정적 변수(.data")
  • 초기화되지 않은(또는 초기화되지 않은) 전역 또는 정적 변수(.bss")

C의 문자열 리터럴은 두 가지 방법으로 사용할 수 있습니다.

  • 상수 데이터에 대한 포인터로 사용됩니다.
  • 배열의 이니셜라이저로.

문자열 리터럴이 포인터로 사용되는 경우 컴파일러는 문자열 데이터를 읽기 전용 데이터 섹션에 배치할 가능성이 높습니다.

문자열 리터럴을 사용하여 글로벌/정적 배열을 초기화하는 경우 컴파일러는 초기화된 데이터 섹션(또는 배열이 const로 선언된 경우 읽기 전용 데이터 섹션)에 배열을 배치할 가능성이 높습니다.

그러나 이 경우 초기화하는 배열은 자동 로컬 변수입니다.따라서 프로그램 시작 전에는 사전 초기화할 수 없습니다.컴파일러는 함수가 실행될 때마다 코드를 초기화하기 위해 코드를 포함해야 합니다.

컴파일러는 문자열을 읽기 전용 데이터 위치에 저장한 다음 복사 루틴(인라인 또는 호출)을 사용하여 로컬 배열에 복사하도록 선택할 수 있습니다. (이 경우 전체의 연속된 복사본이 있을 것이고 그렇지 않으면 없습니다.)단순히 배열의 요소를 하나씩 설정하는 명령을 생성하도록 선택할 수 있습니다.동시에 여러 배열 요소를 설정하는 명령을 생성하도록 선택할 수 있습니다.(예: 4바이트 및 종료를 포함한 2바이트)'\0')

추신: 이 질문에 대한 다른 답변에 https//godbolt.org/ 링크를 올리는 사람들이 있습니다.컴파일러 탐색기는 유용한 도구이지만 기본적으로 어셈블리 출력에서 섹션 전환 지시사항을 숨깁니다.

언급URL : https://stackoverflow.com/questions/71932148/how-are-char-arrays-strings-stored-in-binary-files

반응형