2016-09-09 4 views
2

x86_64 아키텍처에서는 동일한 효과를 얻기 위해 더 짧은 실행 파일을 사용하는 일부 명령어 - 피연산자 조합을 변경할 수 있지만 실행 파일은 더 작습니다.어셈블 된 실행 파일 크기가 같은 이유

xor eax, eax 

대신 :

xor rax, rax 

나는 그것을 테스트하고 싶었 어셈블리에 간단한 프로그램을 작성 : 내장

segment .text 
    global main 
main: 
    push rbp 
    mov rbp, rsp 
    xor rax, rax ; line in question 
    leave 
    ret 

: 예를 들어 , 쓰는 것이 일반적이다

yasm -f elf64 -m amd64 -g dwarf2 main.asm; clang -o main main.o 

크기 :

.... 
Size: 9184 
... 

확인에 문제의 라인을 변경 :

stat main 

이있어 작은 실행 파일을 얻을 수 있었으면

xor eax, eax 

하지만, 크기가 같은 9184 바이트를 얻었다. 짧은 명령어 형식을 사용하면 왜 크기가 줄어들지 않았습니까?

+2

다음 기능 전에 16B 경계로 채우기. 디스어셈블러 출력을보십시오. –

+0

내가 GDB에서'disassemble'했다'0x400re0 <+0> 가지고 : 푸시 %의 rbp' 을'=> 0x4004e4 <+4> %의 RAX %의 rax', 그것은'의 XOR EAX 16 바이트 경계에서도 경우에없는 XOR, EAX '. –

+3

_ "16 바이트 경계가 아닙니다"_ 그 명령이 중요하게 시작되는 곳이 아닙니다. 명령이있는 섹션은 X 바이트의 배수로 패딩 될 수 있습니다. 그러나 @PeterCordes와 마찬가지로 이미 디스어셈블러를 사용하여 원하는 명령어의 생성 된 기계 코드 바이트를 살펴보십시오. – Michael

답변

5

size 명령을 사용하여 이진 부분의 크기를 확인합니다. ls 또는 stat을 사용하면 이진 부분이 으로 채워져이 2의 제곱수 (예 : 16의 다음 배수로)로 채워지므로 정확하지 않습니다.

main.o의 텍스트 세그먼트가 시작 코드 crt0.o이 연결된 후 16 바이트의 배수로 채워지기 때문에 귀하의 경우에는 차이가 없습니다. 따라서 코드 크기에는 차이가 없습니다.

+0

이제 저는 이해하기 쉽고 간결하고 간결합니다. –

3

분해 : 다음 코드를 추가 nop에 의해 정렬 될 수로 (다른 사람의 의견에 설명 된대로)

31 c0   xor eax,eax ; 2 bytes opcode 
48 31 c0  xor rax,rax ; 3 bytes opcode 

실행 파일이 많은 다른 것들을 포함하고 코드는 총, 같은 남아있을 수 있습니다. 파일 크기가 삭감되는 opcode의 모든 바이트에 반응 할 것이라고 기대하지 마십시오.

6

개체 파일을 함께 연결하면 링커는 .text 섹션의 끝에 패킹을 삽입하여 의 텍스트 섹션 시작 부분을 crt0.o부터 시작하여 16B 맞춤 경계에서 시작합니다. 내가 제안처럼 당신이 당신의 바이너리를 분해했다면

, 당신이 본 것 :

$ objdump -Mintel -drw main 
... 
0000000000400500 <main>: 
    400500:  55      push rbp 
    400501:  48 89 e5    mov rbp,rsp 
    400504:  48 31 c0    xor rax,rax 
    400507:  c9      leave 
    400508:  c3      ret  
    400509:  0f 1f 80 00 00 00 00 nop DWORD PTR [rax+0x0] <--- padding inserted by linker 

0000000000400510 <__libc_csu_init>: 
    400510:  41 57     push r15 
    ... 

그냥 NOP 패딩의 크기를 변경합니다 main()의 크기를 변경, 당신은 16B 통과 할 때까지 경계. 당신이 main.o를 분해하면


흥미롭게도, 거기에 ret 후 패딩 없다, 그래서 NOP가 링커에 의해 삽입 된 생각합니다.

사용 readelf -aW main.o 쇼 : -W없이

Section Headers: 
    [Nr] Name  Type   Address   Off Size ES Flg Lk Inf Al 
... 
    [ 4] .text  PROGBITS  0000000000000000 000040 000009 00 AX 0 0 16 
.... 

, 당신은 그들을 대신 한 줄에 포장 필요없이 전체 열 이름을 볼 수 있습니다. 마지막 열은 "정렬"입니다. 이것은 yasm이 링커에게 그 객체의 .text 섹션이 32B를 필요로하는지 또는 링커 출력의 텍스트 세그먼트 안에 다른 정렬이 필요하다는 것을 알려주는 방법입니다.

main: 앞에 ALIGN 4096을 추가하면 .o의 맞춤 열에 4096이 표시됩니다. NOF 패딩을 연결된 바이너리에서 의 끝에main 앞에 추가하므로 main은 0x00402000입니다. 이것은 바이너리의 크기를 변경합니다.

+0

'align 4096'을 추가했는데 이제 바이너리 크기가 크게 늘어났습니다. 설명해 주시겠습니까? 왜 사용자가 기본 정렬 대신 어셈블러에서 설정 한 맞춤 정렬을 사용해야 할 수 있습니까? –

+1

@BulatM : 페이지 경계가 어디 있는지 알 수 있도록 페이지 정렬을 원할 수도 있습니다. 따라서 페이지 오류를 일으키지 않고 개체 외부에서 읽을 수 있습니다. IDK, 당신이 이것을 원할지도 모르는 많은 특별한 이유가있다. 보통 성능 튜닝에 사용된다. 코드 정렬은 명백한 I- 캐시 라인 경계에서 덜 분명한 글로벌 분기 예측 테이블 앨리어싱에 이르기까지 CPU 성능에 많은 직접 및 간접적 인 영향을 미칩니다. 그리고, 영원히 걸릴 것이기 때문에 나는 당신을 위해 그것을 설명하지 않을 것입니다. 그것까지 구글. –