int *q
이의 .bss
에하지 .data
섹션을 갈 것이다 (그래서이없는 C에, C++에서만 합법적이다). 실행 파일의 데이터 세그먼트에 8 바이트를 가질 필요가 없습니다.
컴파일러는 main
을 호출하기 전에 CRT (C 런타임) 시작 코드가 호출하는 이니셜 라이저 배열에 해당 주소를 넣어서 이니셜 라이저 함수가 실행되도록 정렬합니다.
Godbolt 컴파일러 탐색기에서 지시문의 모든 잡음없이 init 함수의 asm을 볼 수 있습니다. 주소 지정 모드는 q
에 대한 단순한 RIP 관련 액세스 일뿐입니다. 링커는 .text
및섹션이 개별 세그먼트로 끝나더라도 링크 시점 상수이므로 RIP에서 오른쪽 오프셋을 채 웁니다.
Godbolt의 compiler-noise filtering은 우리에게 적합하지 않습니다. 지시어 중 일부는 적합하지만 대부분은 관련이 없습니다. 아래는 손으로 선택한 gcc6.2 -O3
asm output with Godbolt's "filter directives" option unchecked의 조합입니다 (int* q = new int(13);
진술에만 해당). (main
을 동시에 컴파일 할 필요가 없으며 실행 파일을 링크하지 않습니다.)
# gcc6.2 -O3 output
_GLOBAL__sub_I_q: # presumably stands for subroutine
sub rsp, 8 # align the stack for calling another function
mov edi, 4 # 4 bytes
call operator new(unsigned long) # this is the demangled name, like from objdump -dC
mov DWORD PTR [rax], 13
mov QWORD PTR q[rip], rax # clang uses the equivalent `[rip + q]`
add rsp, 8
ret
.globl q
.bss
q:
.zero 8 # reserve 8 bytes in the BSS
ELF 데이터 (또는 기타) 세그먼트의 기준에 대한 언급이 없습니다.
세그먼트 레지스터 무시가 없습니다. ELF 세그먼트는 x86 세그먼트와 아무 관련이 없습니다. (이 경우 기본 세그먼트 레지스터는 DS
이므로 컴파일러에서 [ds:rip+q]
등을 내 보내지 않아도됩니다. 지시어에 세그먼트 무시 접두사가 없더라도 일부 디스어셈블러는 명시 적이며 DS를 표시 할 수 있습니다.)
컴파일러는 호출 할 주선 방법
이다 main()
전 :
브라운관 시작 코드는
.init_array
섹션의 크기를 알고 메모리 간접
call
명령을 사용하는 루프를 가지고
# the "aw" sets options/flags for this section to tell the linker about it.
.section .init_array,"aw"
.align 8
.quad _GLOBAL__sub_I_q # this assembles to the absolute address of the function.
각 기능에 대해 r 차례로.
.init_array
섹션은 쓰기 가능으로 표시되어 있으므로 데이터 세그먼트로 이동합니다. 나는 그것을 쓰는 것이 확실하지 않다. 어쩌면 CRT 코드는 포인터를 호출 한 후에 포인터를 0으로 설정하여 이미 완료했다고 표시 할 수 있습니까?
동적 링크를 수행하는 동안 ELF 인터프리터에 의해 수행되는 동적 라이브러리에서 초기화를 실행하기위한 리눅스에서 유사한 메커니즘이있다. 따라서 printf()
또는 기타 glibc stdio 함수를 직접 작성한 asm으로 만든 동적 연결 바이너리에서 _start
으로 호출 할 수 있으며, 올바른 init 함수를 호출하지 않으면 왜 정적으로 링크 된 바이너리에서 실패하는지 이유가 여기 있습니다. 자신의 _start
또는 단지 main()
을 정의하는 정적 또는 동적 바이너리를 빌드하는 방법에 대한 자세한 내용은 this Q&A을 참조하십시오 (libc 포함 또는 제외).
'gcc '로 링크 할 때, 디폴트 시작점은'_start'입니다. 이 초기화가 코드를 가지고있는 곳이며,'main'을 호출합니다. 따라서 clib없이 어셈블리 프로그래밍을하고 있지만 gcc와 연결하는 사람들은 자신의 코드 시작 부분에'_start :'라벨을 붙여야 만합니다. 반면에 기본 clib와 연결된 사람들은'main :'에서 시작합니다 (소스에서 바이너리가 시작됩니다 lib에서'_start :'에). :) – Ped7g
그것에 대해 자세히 설명하기 위해'_start'는 함수가 아니므로 C 또는 C++에서'_start'를 쓸 수 없습니다. 어셈블리에서는 함수를 작성할 필요가 없으며 임의 코드를 작성할 수 있으므로'_start'를 직접 작성할 수 있습니다. –
감사합니다. Ped7g & Dietrich Epp. –