2016-11-05 2 views
0

서브 루틴 내에서 서브 루틴을 호출 :ARM 어셈블리 적절한 서브 루틴에서 POP 링크 레지스터와 PC를 밀어 방법 내가 내 서브 루틴에서 다음을 수행 한</p> <p>는 ARM 어셈블리를 들어

SubRoutine: 
    PUSH {r1,r2,lr} 
    //code that changes r1 and r2 
    POP {r1,r2,lr} 
    bx lr 

이인가 서브 루틴에서 돌아가서 주 함수에서 코드를 계속 사용하는 올바른 방법? 나는 사람들이 다음을 수행하는 것을 주위 보았다 :

SubRoutine: 
    PUSH {r1,r2,lr} 
    //code that changes r1 and r2 
    POP {r1,r2,pc} 
    bx lr 

그러나 당신이 LR 밀어 때 PC를 POP 왜 모르겠어요. 올바른 방법과 이유는 무엇입니까? 당신이 서브 루틴 내에서 서브 루틴을 호출하는 경우

또한, 다음을 수행하십시오

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    PUSH {lr} 
    bl AnotherRoutine (where bx lr will be used to return from it) 
    POP {lr} 

    POP {r1,r2,pc} 
    bx lr 

또는 대신 다음과 같이 그것을 수행

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    PUSH {lr} 
    bl AnotherRoutine(where bx lr will be used to return from it) 
    POP {pc} 

    POP {r1,r2,pc} 
    bx lr 
+0

설명서를 읽는 데 어려움이 있다면 ARMv5T 이후 목록에'pc'가있는'pop'이 인터 워킹 브랜치라는 것을 알 것입니다. 그 후에 결코 도달하지 않는'bxlr'. – EOF

+0

"LR을 푸시 할 때 PC를 왜 POP하는지 모르겠다."---- 여기에 근본적인 것이 빠져있는 것처럼 들리 네요. LR__ 값을 푸시 한 다음 해당 값을 PC__에 힙니다 (따라서 해당 주소로 분기를 수행합니다). 당신은 asm 프로그래밍에 대한 몇 가지 기본을 읽을 필요가 있습니다. 인터넷에 많은 것들이 있습니다. –

+0

잘 설명해 드리면 – SeesSound

답변

2

세 가지 경우가 있습니다 당신 알고 있어야합니다.

  1. 잎 : void foo(void) {};
  2. 테일 전화 : int foo(void) { return bar(); };
  3. 중급 : int foo(void) { int i; i = bar() + 4; return i; };

는 이러한 호출을 구현하는 방법에는 여러 가지가 있습니다. 다음은 일부 샘플 뿐이며 ARM 어셈블러에서 에필로그 및 프롤로그를 구현하는 유일한 방법은 아닙니다.


잎 funtions

많은 기능은 의 유형과 lr의 저장을 필요로하지 않는다. 반환하려면 bx lr을 사용하기 만하면됩니다. 예를 들어,

SubRoutine: 
    PUSH {r1,r2} 
    //code that changes r1 and r2 
    POP {r1,r2} 
    bx lr 

또한, R1 및 R2는 파라미터를 전달하는 데 사용되는 전형적인 및 루틴 그들을 파괴/자유롭게 사용할 수있다. ARM calling conventions 어셈블러에서 'C'함수를 호출하는 경우에 해당됩니다. 그래서 전형적으로 아무도 r1과 r2를 저장할 수 없지만 어셈블러이므로 여러분이 좋아하는 것을 할 수 있습니다 (나쁜 생각이라 할지라도). 따라서 표준을 따를 경우 실제로 예제는 bx lr입니다.


테일 전화

함수가 잎 다음 바로 가기를 사용할 수있는 또 다른 함수에 대한 최종 호출을 제외하고 인 경우,

Sub_w_tail: 
// Save callee-saved regs (for whatever calling convention you need) 
// Leave LR as is. 
// ... do stuff 
B tail_call 

LR이 저장됩니다 발신자가 Sub_w_tail으로 변경하면 원래 발신자에게 돌아 오는 tail_call으로 바로 바로 이동할 수 있습니다.


중급 기능

이 가장 복잡합니다. 다음은 가능한 순서는, 이전 호출 규칙의

SubRoutine: 
    PUSH {r1,r2,lr} 

    //code that changes r1 and r2 
    bl AnotherRoutine (where bx lr will be used to return from it) 

    // more code 
    POP {r1,r2,pc} // returns to caller of 'SubRoutine' 

일부 세부 사항은 ARM Link and frame registers 질문에 있습니다. 이 규칙을 사용할 수 있습니다. ARM 어셈블러에서 에필로그프롤로그을 수행하는 여러 가지 방법이 있습니다.


마지막은 꽤 복잡합니다. 적어도 코드를 작성하는 것이 지루합니다. 컴파일러가 사용할 레지스터와 스택에 배치 할 레지스터를 결정하게하는 것이 훨씬 낫습니다. 그러나 일반적으로 어셈블러 작성시 첫 번째 코드 (LEAF)를 코딩하는 방법 만 알고 있으면됩니다. 어셈블러에서 고급 언어에서 호출 된 최적화 된 서브 루틴을 코딩하는 것이 가장 생산성이 좋습니다. 모두가 컴파일 된 코드를 이해하는 방법을 아는 것이 유용합니다. 이러한 뉘앙스를 처리 할 필요가 없도록 인라인 어셈블러도 고려해야합니다.

+0

tail call 메서드는 * 실제 * 루틴을 호출하기 전에 책을 보관하는 어셈블러 * 베니어 * 또는 * 심 *을 만드는 경우 매우 유용 할 수 있습니다. 예를 들어,'malloc' 디버그 코드 등. 그러나,'void * malloc (size) {return real_malloc (size); }; '당신은 똑같은 것을 얻을 수 있습니다. 공유 라이브러리 또는 다른 메모리 공간이 활성화되어있을 때 더 유용 할 수 있습니다. –

관련 문제