수동 조립 대 GCC
면책 사항:이제 막 x86 조립을 시작합니다.대학에서 SPIM을 조금 배웠지만, 그것은 거의 언급할 가치가 없습니다.
libc에서 가장 간단한 기능인 abs()부터 시작하려고 생각했습니다.C에서 꽤 간단합니다.
long myAbs(long j) {
return j < 0 ? -j : j;
}
조립 중인 내 버전:
.global myAbs
.type myAbs, @function
.text
myAbs:
test %rdi, %rdi
jns end
negq %rdi
end:
movq %rdi, %rax
ret
(이것은 32비트 정수에서는 작동하지 않습니다. 아마도 RAX가 64비트 레지스터이고 기호가 잘못된 위치에 있기 때문일 것입니다. - 조사해야 합니다.)
이제 gcc가 하는 일은 다음과 같습니다(gcc-O2-SmyAbs.c).
.file "myAbs.c"
.section .text.unlikely,"ax",@progbits
.LCOLDB0:
.text
.LHOTB0:
.p2align 4,,15
.globl myAbs
.type myAbs, @function
myAbs:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $4144, %rsp
orq $0, (%rsp)
addq $4128, %rsp
movq %rdi, %rdx
sarq $63, %rdx
movq %fs:40, %rax
movq %rax, -8(%rbp)
xorl %eax, %eax
movq %rdi, %rax
xorq %rdx, %rax
subq %rdx, %rax
movq -8(%rbp), %rcx
xorq %fs:40, %rcx
jne .L5
leave
.cfi_remember_state
.cfi_def_cfa 7, 8
ret
.L5:
.cfi_restore_state
call __stack_chk_fail@PLT
.cfi_endproc
.LFE0:
.size myAbs, .-myAbs
.section .text.unlikely
.LCOLDE0:
.text
.LHOTE0:
.ident "GCC: (Gentoo Hardened 5.1.0 p1.2, pie-0.6.3) 5.1.0"
.section .note.GNU-stack,"",@progbits
왜 이렇게 큰 차이가 나는 거지?GCC는 훨씬 더 많은 명령어를 생산합니다.이것이 내 코드보다 느리리라고는 상상할 수 없습니다.내가 뭘 빼놓았나요?아니면 제가 여기서 뭔가 심각한 잘못을 하고 있는 건가요?
생성된 코드가 무엇인지 궁금해하는 사람들을 위해, 먼저 GCC가 컴파일 할 때 주목하십시오.myAbs스택 보호 기능을 통해 이러한 형태로 변환
long myAbs(long j) {
uintptr_t canary = __stack_chk_guard;
register long result = j < 0 ? -j : j;
if ( (canary = canary ^ __stack_chk_guard) != 0 )
__stack_chk_fail();
}
간단히 수행할 코드j < 0 ? -j : j;가
movq %rdi, %rdx ;RDX = j
movq %rdi, %rax ;RAX = j
sarq $63, %rdx ;RDX = 0 if j >=0, 0fff...ffh if j < 0
xorq %rdx, %rax ;Note: x xor 0ff...ffh = Not X, x xor 0 = x
;RAX = j if j >=0, ~j if j < 0
subq %rdx, %rax ;Note: 0fff...ffh = -1
;RAX = j+0 = j if j >= 0, ~j+1 = -j if j < 0
;~j+1 = -j in two complement
우리가 얻은 코드를 분석하는 것은
pushq %rbp
movq %rsp, %rbp ;Standard prologue
subq $4144, %rsp ;Allocate slight more than 4 KiB
orq $0, (%rsp) ;Perform a useless RW operation to test if there is enough stack space for __stack_chk_fail
addq $4128, %rsp ;This leave 16 byte allocated for local vars
movq %rdi, %rdx ;See above
sarq $63, %rdx ;See above
movq %fs:40, %rax ;Get the canary
movq %rax, -8(%rbp) ;Save it as a local var
xorl %eax, %eax ;Clear it
movq %rdi, %rax ;See above
xorq %rdx, %rax ;See above
subq %rdx, %rax ;See above
movq -8(%rbp), %rcx ;RCX = Canary
xorq %fs:40, %rcx ;Check if equal to the original value
jne .L5 ;If not fail
leave
ret
.L5:
call __stack_chk_fail@PLT ;__stack_chk_fail is noreturn
따라서 스택 스매싱 프로텍터를 구현하기 위한 추가 지침이 모두 제공됩니다.
프롤로그 후 첫번째 지시사항 사용을 지적해주신 FUZxxl님께 감사드립니다.
대부분의 초기 호출은 스택을 설정하고 반환 주소를 저장하는 것입니다.스택 보호 작업이 진행되고 있는 것 같습니다.오버헤드를 제거하기 위해 컴파일러 설정을 조정할 수도 있습니다.
컴파일러에 다음과 같은 플래그를 추가할 수 있습니다.-fno-stack-protector이 차이를 최소화할 수 있습니다.
예, 이것은 아마도 손으로 쓴 조립품보다 느리겠지만, 훨씬 더 많은 보호 기능을 제공하며, 약간의 오버헤드에 비해 가치가 있을 것입니다.
스택 보호 기능이 리프 함수임에도 불구하고 여전히 존재하는 이유는 여기를 참조하십시오.
언급URL : https://stackoverflow.com/questions/31539725/manual-assembly-vs-gcc
'programing' 카테고리의 다른 글
| 일부 값을 반환하는 저장 루틴을 만들 수 없습니다. (0) | 2023.09.27 |
|---|---|
| JSON을 사용하여 Spring MVC 컨트롤러에 중첩 개체 게시 (0) | 2023.09.27 |
| Python docstring의 클래스 메서드에 대한 링크 (0) | 2023.09.27 |
| build.gradle 파일에 주석을 작성하는 구문은 무엇입니까? (0) | 2023.09.27 |
| C 매크로에서 출력 사용 및 반환 (0) | 2023.09.27 |