eax에 대한`testl` eax?


118

나는 어떤 어셈블리를 이해하려고 노력하고 있습니다.

다음과 같이 어셈블리, 나는 testl라인에 관심이 있습니다.

000319df  8b4508        movl   0x08(%ebp), %eax  
000319e2  8b4004        movl   0x04(%eax), %eax  
000319e5  85c0          testl  %eax, %eax  
000319e7  7407          je     0x000319f0  

나는 testl사이의 포인트를 이해하려고 노력 %eax하고 %eax있습니까? 이 코드의 세부 사항이 중요하지 않다고 생각합니다. 테스트 자체를 이해하려고합니다. 값이 항상 사실이 아닐까요?

답변:


91

eax0 인지 , 위 또는 아래 인지 테스트합니다 . 이 경우 eax0 이면 점프가 수행됩니다 .


2
나는이 대중적인 대답을 "이 TEST가 무엇에 관한 것이며 CMP와 어떻게 다른지"에 대한 더 나은 표준 대답으로 바꾸도록 편집했습니다. 이것은 일종의 암시입니다. 동의어 JE와 JZ의 의미 론적 의미에 대한 의견은 내 대답을 더 참조하십시오. 내 편집 내용이 꽤 중요하므로 검토하십시오. 여전히 귀하의 대답입니다.
Peter Cordes

@PeterCordes 의도에 감사하지만 편집 내용을 되돌릴 것입니다. 1. 당신의 "목소리"는 저와는 매우 다릅니다. 그리고 지금은 저보다 당신의 대답과 훨씬 더 비슷합니다. 2. 더 문제가되는 것은 플래그가 test와 사이에서 정확히 같은 방식으로 나온다는 대담한 주장입니다 cmp. 예, Cody에 대한 귀하의 의견에 근거한 귀하의 믿음임을 이해합니다. 그러나 내 게시물에 올리는 것은 다른 문제입니다. 모든 경우에 동일한 지 알 수 없기 때문에 기꺼이 기꺼이 기꺼이 기꺼이지지 할 주장 이 아닙니다 .
크리스 광대 - 젊은

1
@PeterCordes 여가 시간을 찾으면이 답변을 더 정식으로 구체화하고 싶습니다. 그래도 글을 쓰는 것처럼 글을 쓰고 싶고 글을 쓰는 방법에 대해 매우 특별합니다. :-) 예를 들어, 내가 쓰는 것 je, jz, cmp,과 test, 그리고 JE, JZ, CMP, 또는 TEST. 난 그렇게 까다 롭다.
Chris Jester-Young

1
나는 내 자신의 대답을 높이려고하지 않았습니다. 나는 편집을 할 때 내가이 질문에 직접 대답했다는 것을 잊었고 나중에 야 알아 차렸다. 나는 누군가가 그것을 부딪친 후에 이것을 보았고 작은 편집으로 시작된 것이 너무 많이 눈덩이처럼 뭉쳤다. 롤백하기를 원했던 위반 사항은 없습니다. 그것은 단지 제안 일 뿐이며 분명히 당신의 것이 아니라 내 작품처럼 읽혀집니다. 내가 쓴 내용 중 일부를 가져 와서 내 대답에 넣을 것입니다.
Peter Cordes dec.

2
와,이 질문에 내가 추가 한 내용을 포함하도록이 질문에 대한 답변을 편집 한 후 6 월에 작성한 대부분의 내용을 거의 정확하게 복제했음을 깨달았습니다. 이런! 나는 나의 주장 백업에 더 많은 추론으로 업데이트 test a,a하고 cmp $0,a동일 플래그 설정을; 사소한 주장이 아니라는 점을 지적 해 주셔서 감사합니다. re : TEST vs. test: 최근 Intel의 매뉴얼과 같은 전체 대문자를 사용하기 시작했습니다. 그러나 AT & T 니모닉과 인텔 니모닉에 대해 이야기 할 때는 AT & T에 testb스타일을 사용 합니다. 가독성에 도움이되는 경우 IDK.
Peter Cordes dec.

90

의 의미는 test인수를 함께 AND하고 결과가 0인지 확인하는 것입니다. 따라서이 코드는 EAX가 0인지 여부를 테스트합니다. je0이면 점프합니다.

BTW, 이것은 cmp eax, 0컴파일러가 일반적으로 이런 방식으로 수행하는 이유 보다 작은 명령을 생성합니다 .


34

테스트 명령어는 피연산자간에 논리 AND 연산을 수행하지만 결과를 레지스터에 다시 쓰지 않습니다. 플래그 만 업데이트됩니다.

귀하의 예제에서 eax, eax는 eax가 0이면 0 플래그를 설정하고 가장 높은 비트가 설정되면 부호 플래그를 설정하고 다른 플래그도 설정합니다.

같으면 점프 (je) 명령은 제로 플래그가 설정된 경우 점프합니다.

다음과 같이 코드를 더 읽기 쉬운 코드로 변환 할 수 있습니다.

cmp eax, 0
je  somewhere

기능은 동일하지만 약간의 코드 공간이 필요합니다. 이것이 컴파일러가 비교 대신 테스트를 내 보낸 이유입니다.


3
실제로 cmp가 작동하지 않을 수 있습니다. 즉, 제시된 특정 경우에 대해 작동하지만 cmp는 and 대신 내부 하위이기 때문에 test와는 다르게 플래그에 영향을 미칩니다. 명심해야 할 것.
Cody Brocious

4
0에 대한 테스트의 경우 완벽하게 유효합니다.
Nils Pipenbrinck

3
하지만 나중에 플래그를 어떻게 보는지 알 수 없습니다. 플래그에 미치는 영향은 매우 다르므로 문제가 될 수 있으며 매우 자주 발생합니다.
Cody Brocious

2
아니요, 다른 / method /에 의해 설정된 유일한 플래그는 carry 및 overflow이며 둘 다 0으로 설정됩니다. cmp가 sub 및 test 사용을 사용하기 때문에 다른 플래그의 / values ​​/가 다릅니다.
Cody Brocious

2
@CodyBrocious : test eax, eaxcmp eax, 0두 세트의 모든 플래그를하고, 동일한 값으로 설정합니다. 두 명령어 모두 "결과에 따라"모든 플래그를 설정합니다. 빼기 0는 캐리 또는 오버플로를 생성 할 수 없습니다. 귀하의 인수는 0 이외의 즉각적인에 대한 올바른 있지만 0입니다
피터 코르

22

testandFLAGS 만 작성하고 두 입력을 수정하지 않은 상태로 두는 것을 제외하면 과 같습니다 . 두 개의 다른 입력을 사용 하면 일부 비트가 모두 0인지 또는 적어도 하나가 설정되었는지 테스트하는 데 유용합니다. (예 : test al, 3EAX가 4의 배수 인 경우 ZF를 설정합니다 (따라서 하위 2 비트가 모두 0이 됨).


test eax,eax모든 플래그를 정확히 같은 방식으로 설정하는 cmp eax, 0것을 :

더 이상 사용되지 않는 AF (ASCII / BCD 명령어에서 사용되는 보조 운반 플래그)는 예외입니다. TEST는 정의되지 않은 상태 로 두지 만 CMP는 "결과에 따라"설정합니다 . 0을 빼면 4 번째에서 5 번째 비트까지 캐리가 생성되지 않으므로 CMP는 항상 AF를 지워야합니다.


TEST는 더 작고 (즉시 없음) 때로는 더 빠릅니다 (CMP보다 더 많은 경우에 더 많은 CPU에서 비교 및 ​​분기 uop에 매크로 융합 가능). 이것은 test레지스터를 0과 비교할 때 선호하는 관용구가됩니다 . 틈새 최적화입니다.cmp reg,0의미 론적 의미에 관계없이 사용할 수 .

즉치 0과 함께 CMP를 사용하는 유일한 일반적인 이유는 메모리 피연산자와 비교하려는 경우입니다. 예를 들어, cmpb $0, (%esi)암시 적 길이 C 스타일 문자열의 끝에서 끝나는 0 바이트를 확인합니다.


AVX512F는 추가kortestw k1, k2 하고 AVX512DQ / BW (KNL이 아닌 Skylake-X) add ktestb/w/d/q k1, k2는 AVX512 마스크 레지스터 (k0..k7)에서 작동하지만 test정수 OR또는 AND명령어 와 동일한 방식으로 일반 FLAGS를 설정 합니다. (SSE4 ptest또는 SSE 와 비슷한 종류 ucomiss: SIMD 도메인에 입력하고 정수 FLAGS가됩니다.)

kortestw k1,k1AVX512 비교 결과를 기반으로 / cmovcc / setcc를 분기하는 관용적 방법으로 SSE / AVX2 (v)pmovmskb/ps/pd+ test또는 cmp.


jzvs. 사용은 je혼란 스러울 수 있습니다.

jz그리고 je말 그대로 동일한 명령이다 , 기계 코드에서 동일한 연산 코드를 즉. 그들은 같은 일을하지만 인간에게는 다른 의미 론적 의미를 가지고 있습니다 . 디스어셈블러 (및 일반적으로 컴파일러의 asm 출력)는 하나만 사용하므로 의미 구분이 손실됩니다.

cmpsub두 입력이 같을 때 ZF 를 설정합니다 (즉, 뺄셈 결과가 0). je(같으면 점프)는 의미 상 관련있는 동의어입니다.

test %eax,%eax/ and %eax,%eax결과가 0 일 때 다시 ZF를 설정하지만 "동등"테스트가 없습니다. 테스트 후 ZF는 두 피연산자가 같은지 여부를 알려주지 않습니다. 따라서 jz(0이면 점프)는 의미 상 관련있는 동의어입니다.


나는 test비트 and연산 에 대한 기본 정보를 추가하는 것을 고려할 것이며 어셈블리를 배우는 사람들에게는 분명하지 않을 수 있습니다 (그리고 60 초마다 지침 참조 가이드를 확인하는 게 게 으르거나 알지 못하는 경우;) :)).
Ped7g

1
@ Ped7g : 충분히 공평합니다.이 부분을 다른 답변에 남겨 두는 대신이 답변에 모든 것을 넣는 것이 아프지 않습니다. 추가 AVX512 kortest*ktest*나는 그것을 동안.
Peter Cordes

BTW, 이것은 기본적으로 동일한 질문의 다른 버전에 대한 내 대답과 동일 하지만 성능에 대한 자세한 내용을 언급했습니다. 예를 들어 동일한 값으로 레지스터를 다시 작성하여 Nehalem과 같은 구형 P6 제품군 CPU에서 레지스터 읽기 중단을 피할 수 있습니다.
Peter Cordes

@PeterCordes 이것이 허용되는 대답이어야합니다. 철저하고 기술적입니다. 수락 된 게시물과 달리 이것은 지식에 대한 호기심과 갈증을 해소합니다. 계속해주십시오.
programmersn

PF는 하위 8 비트의 패리티로 설정되며이 경우에는 AL입니다.
ecm

5

이 코드 스 니펫은 무언가, 아마도 어떤 구조체 나 객체에 대한 포인터가 주어진 서브 루틴에서 온 것입니다. 두 번째 줄은 그 포인터를 역 참조하여 그 것에서 값을 가져옵니다. 아마도 그 자체가 포인터이거나 아니면 그냥 정수일 수 있으며, 두 번째 멤버 (offset +4)로 저장됩니다. 세 번째와 네 번째 줄은이 값이 0인지 테스트하고 (포인터 인 경우 NULL) 0 인 경우 다음 몇 가지 작업 (표시되지 않음)을 건너 뜁니다.

0에 대한 테스트는 때때로 즉각적인 리터럴 0 값과 비교하여 코딩되지만 이것을 작성한 컴파일러 (또는 인간?)는 파이프 라이닝 및 레지스터와 같은 모든 최신 CPU 항목을 고려하여 testl 작업이 더 빠르게 실행될 것이라고 생각했을 수 있습니다. 이름 바꾸기. 분명하지만 느린 MOV EAX, # 0 (이전 표기법 사용 ).

asm에서는 perl과 마찬가지로 TMTOWTDI입니다.


3

eax가 0이면 조건부 점프를 수행하고 그렇지 않으면 319e9에서 실행을 계속합니다.


0

일부 프로그램에서는 버퍼 오버플로를 확인하는 데 사용할 수 있습니다. 할당 된 공간의 맨 위에 0이 배치됩니다. 스택에 데이터를 입력 한 후 할당 된 공간이 오버플로되지 않았는지 확인하기 위해 할당 된 공간의 맨 처음에있는 0을 찾습니다.

스택 0 익스플로잇-연습에서 오버플로가 발생했는지 확인하고 거기에 0이있는 경우 "다시 시도"를 표시하는 데 사용되었습니다.

0x080483f4 <main+0>:    push   ebp
0x080483f5 <main+1>:    mov    ebp,esp
0x080483f7 <main+3>:    and    esp,0xfffffff0
0x080483fa <main+6>:    sub    esp,0x60                     
0x080483fd <main+9>:    mov    DWORD PTR [esp+0x5c],0x0 ;puts a zero on stack
0x08048405 <main+17>:   lea    eax,[esp+0x1c]
0x08048409 <main+21>:   mov    DWORD PTR [esp],eax
0x0804840c <main+24>:   call   0x804830c <gets@plt>
0x08048411 <main+29>:   mov    eax,DWORD PTR [esp+0x5c] 
0x08048415 <main+33>:   test   eax,eax                  ; checks if its zero
0x08048417 <main+35>:   je     0x8048427 <main+51>
0x08048419 <main+37>:   mov    DWORD PTR [esp],0x8048500 
0x08048420 <main+44>:   call   0x804832c <puts@plt>
0x08048425 <main+49>:   jmp    0x8048433 <main+63>
0x08048427 <main+51>:   mov    DWORD PTR [esp],0x8048529
0x0804842e <main+58>:   call   0x804832c <puts@plt>
0x08048433 <main+63>:   leave
0x08048434 <main+64>:   ret

0이 아닌지 레지스터를 확인하는이 특정 사례가이 Q & A에 추가되는 내용을 알 수 없습니다. 특히 때 cmp DWORD PTR [esp+0x5c], 0/은 jz 0x8048427 <main+51>별도의 MOV로드 한 후 TEST보다 더 효율적이었을 것입니다. 이것은 0을 확인하는 일반적인 사용 사례가 아닙니다.
Peter Cordes

-4

우리가 볼 수 JG를 , JLE을 하면 testl %edx,%edx. jle .L3우리가 쉽게 찾을 수 JLE은 벌이다 (SF^OF)|ZF%의 EDX가 0이 아닌 및 -1의 testl 후 경우 %의 EDX는, ZF = 1 0 인 경우,하지만,의 = 0 및 SF = 1, 그래서 점프를 구현하는 플래그 = true, 죄송합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.