2016-06-21 2 views
1

오늘은 함수에 대체 진입 점을 만들 어셈블리 코드에서 함수 포인터를 증가와 함께 놀았 :이 어셈블리 코드와 비슷한 C 언어의 연산이 있습니까?

.386 
.MODEL FLAT, C 
.DATA 
    INCLUDELIB MSVCRT 
    EXTRN puts:PROC 
    HLO DB "Hello!", 0 
    WLD DB "World!", 0 
.CODE 
    dentry PROC 
     push offset HLO 
     call puts   
     add esp, 4 
     push offset WLD 
     call puts 
     add esp, 4 
     ret 
    dentry ENDP 
    main PROC 
     lea edx, offset dentry 
     call edx 
     lea edx, offset dentry 
     add edx, 13 
     call edx 
     ret 
    main ENDP 
END 

(I, 그것은 초기화되는 CRT없이 puts를 호출하기 때문에 기술적으로이 코드가 잘못 알고 있지만, 적어도 MSVC 2010 SP1에서는 어셈블리 나 런타임 오류없이 작동합니다.)

dentry에 대한 두 번째 호출에서 이전처럼 edx 레지스터의 함수 주소를 가져 왔지만 이번에는이를 증가 시켰습니다 함수를 호출하기 전에 13 바이트 씩

이 프로그램의 출력은, 그러므로 :

C:\Temp>dblentry 
Hello! 
World! 
World! 

C:\Temp> 

제 2 출력에서 ​​시작하여 호출에서 반면 "Hello!\nWorld!"의 제 1 출력은 상기 기능의 시작에 대한 호출로부터 인 " push offset WLD "명령.

이런 종류의 것들이 C, Pascal 또는 FORTRAN과 같은 어셈블러에서 한 단계 올라 가기위한 언어로 존재하는지 궁금합니다. 나는 C가 함수 포인터를 증가시키지 않는다는 것을 알고 있지만, 이런 종류의 일을 성취 할 다른 방법이 있습니까?

+4

참조하십시오. 더 높은 수준의 언어 (심지어 asm에서도)를 두 개의 분리 된 함수로 나눠서 하나를 다른 함수로 호출하고 컴파일러가 최선의 구현을 선택하도록해야합니다. – Jester

+1

이전 FORTRAN이 함수에 대한 여러 진입 점을 지원한다고 생각하고 빠른 google 검색을 통해 https://gcc.gnu.org/onlinedocs/gcc-3.4.4/g77/Alternate-Entry-Points.html 및 https :// /docs.oracle.com/cd/E19957-01/805-4939/6j4m0vn99/index.html – ninjalj

+0

실제 질문은 ** 이유 **입니다. 이것은 유지 보수의 악몽입니다. 최적화까지 : 프로그램의 99 % 이상이 이것을 필요로하지 않습니다. 나머지는 종종 컴파일러와 70/80 년대 베이직 해킹보다 훨씬 좋은 구조로 최적화 될 수 있습니다. – Olaf

답변

1

당신은 longjmp를 기능을 사용할 수 있습니다 http://www.cplusplus.com/reference/csetjmp/longjmp/

그것은 매우 끔찍한 기능입니다,하지만 당신이 무엇을 추구 할 것입니다.

+0

longjmp를 사용하면 상위 함수의 한 지점으로 돌아갈 수 있습니다. 즉, 현재 실행중인 기능의 부모가 아닌 임의의 기능으로가 아니라 콜 트리를 백업하십시오. 그것은 교차 기능 점프를 만드는 또 다른 방법이지만 사용 요구 사항이 매우 다르기 때문에 OP가 요구했던 것과 같은 것이 아닙니다. –

+0

예, longjmp에는 점프 항목의 사전 실행이 필요합니다. 그러나 OP 샘플 코드도 마찬가지입니다. – Illishar

+1

아니야! OP의'main()'은'(dentry + 13)()'을 호출합니다. 이것은'dentry()'에 대한 초기 호출에 상관없이 같은 방식으로 발생합니다 (두 번째 호출 전에 반환되었습니다). 그것은 유사하지도 않습니다. –

1

AFAIK를 사용하면 asm에 여러 진입 점이있는 함수 만 쓸 수 있습니다.

모든 진입 점에 레이블을 넣을 수 있으므로 첫 번째 function-name의 오프셋을 하드 코딩하는 대신 일반 직접 호출을 사용할 수 있습니다.

이렇게하면 일반적으로 C 또는 다른 언어로 쉽게 호출 할 수 있습니다.

이전 엔트리 포인트는 기능 단위가 겹치지 않는 혼란스러운 도구 (또는 인간)에 대해 걱정할 경우 다른 기능의 본문으로 넘어가는 기능처럼 작동합니다.


당신은 초기 엔트리 포인트가 추가로 물건의 작은 비트를 할 경우이 작업을 수행 한 다음 주요 기능으로 통해 떨어질 수 있습니다. 주로 코드 크기 절약 기술이 될 것입니다 (I-cache/uop-cache hit rate을 향상시킬 수 있습니다).


컴파일러는 약간 다른 기능 사이에서 공통적으로 구현되는 큰 덩어리를 공유하는 대신 기능간에 코드를 복제하는 경향이 있습니다. 것보다 약간 더있는 Godbolt compiler explorer

foobar tailcall bigfunc에 실제 예를 참조

int foo(int a) { return bigfunc(a + 1); } 
int bar(int a) { return bigfunc(a + 2); } 

int bigfunc(int x) { /* a lot of code */ } 

:

그러나, 당신은 아마 같은 뭔가 하나 추가 jmp으로 수행 할 수 있습니다 bar 불 투과율이 bigfunc입니다.(bigfunc으로 bar 이상 foo 점프를 갖는 ESP, 여전히 좋다. bar 그 사소한되지 않습니다.)


함수의 중간에 점프하지 일반적으로 안전에 일반적이지 않은 기능 때문에 일부 reg를 저장/복원해야합니다. 그래서 프롤로그가 그들을 밀고, 에필로그가 그들을 터뜨립니다. 가운데로 점프하면 프롤로그의 pop이 스택의 불균형을 일으 킵니다. (즉, 반환 주소를 레지스터로 띄우고 쓰레기 주소로 돌아갑니다).

그것은 일반적으로 모든 종류의 문제로 이어질 것입니다 기능 프롤로그를 건너 뛰는 때문에 작동하지 않습니다 또한 Does a function with instructions before the entry-point label cause problems for anything (linking)?