2011-11-05 3 views
1

ARM 어셈블리 언어 루틴에서 printf를 호출해야합니다. 동일한 작업 (printf("%d.%d",1,2))을 수행하는 c 프로그램을 작성했습니다. 컴파일러 출력을 디스 어셈블했지만 형식 문자열이 전달되는 방법이 명확하지 않습니다. 이 작업을 수행하는 코드 예제가 있습니까?ARM 어셈블리 언어에서 c 함수 printf 호출

여기 printf를 호출하는 방법을 알아보기 위해 사용했던 테스트 C 루틴이 있습니다. 다음과 같은 메인 루틴 외모에 대한

#include <stdio.h> 
#include <stdlib.h> 

int main(void) { 
     printf("%d.%d\n",1,2); 
     return EXIT_SUCCESS; 
} 

내 분해 :

000081c4 <main>: 
81c4:  e1a0c00d  mov  ip, sp 
81c8:  e92dd800  stmdb sp!, {fp, ip, lr, pc} 
81cc:  e24cb004  sub  fp, ip, #4  ; 0x4 
81d0:  e59f0014  ldr  r0, [pc, #20] ; 81ec <.text+0x11c> 
81d4:  e3a01001  mov  r1, #1 ; 0x1 
81d8:  e3a02002  mov  r2, #2 ; 0x2 
81dc:  eb000212  bl  8a2c <_IO_printf> 
81e0:  e3a03000  mov  r3, #0 ; 0x0 
81e4:  e1a00003  mov  r0, r3 
81e8:  e89da800  ldmia sp, {fp, sp, pc} 
81ec:  00060120  andeq r0, r6, r0, lsr #2 

은 내가 _IO_printf 루틴으로 분기를 볼 수 있지만, 나는 그것을 형식 문자열을 전달하는 방법을 볼 수 없습니다.

+0

해체의 관련 부분을 첨부하면 설명하기가 쉬울 것입니다. (우리는 또한 같은 언어로 말할 수 있습니다). – user786653

+0

내 업데이트 된 질문을 참조하십시오. – ziggle314

+0

형식 문자열이 '00060120'에있는 것처럼 보입니다. 마지막으로 디코딩 된 명령은 실제로 명령으로 해석되지는 않습니다. – user786653

답변

0

C에서 문자열은 일련의 바이트로 저장됩니다. 함수에 문자열을 전달하면 실제로 문자열의 첫 번째 문자 주소가 전달됩니다.

printf() (컴파일러 최적화없이)을 호출하면 인수가 역순으로 오른쪽에서 왼쪽으로 스택에 푸시됩니다. 그런 다음 printf()은 첫 번째 인수를 가져옵니다.이 인수는 형식 문자열에 대한 포인터입니다. 형식 문자열을 분석하여 연속적인 각 인수에 대해 얼마나 많은 바이트를 나타낼 지 결정하고 이들이 나타내는 데이터 형식 (int, string 등)을 기반으로 해석합니다.

업데이트 : ARM 프로세서는 다른 호출 규칙을 사용합니다. 스택을 사용하는 대신 레지스터의 첫 번째 매개 변수를 전달합니다. 그러나 매개 변수의 내용은 스택에서 전달 된 경우와 동일합니다. R0에는 형식 문자열에 대한 포인터가 포함되며 아래의 해당 코드는 여전히 정확합니다.

수정을 제안한 사람에게 감사드립니다.

그래서, 적어도 지금까지 printf()에 관한 한, 당신의 코드는 다음에 해당합니다 :

const char formatString[] = "%d.%d"; 
printf(&formatString[0], 1, 2); 
+0

나와 함께있어주세요! 당신이 내가 알아야 할 것을 알 것 같아요. 내 업데이트 된 질문보기 – ziggle314

+3

4를 초과하지 않는 한 ARM 호출 규칙은 스택에 인수를 사용하지 않습니다. –

+0

dwelch의 대답을 참조하십시오. 이는 매우 명확하고 완전합니다. +1! –

0

나는 _IO_printf 루틴으로 분기를 볼 수 있지만, 내가 전달하는 방법을 볼 수 없습니다 형식 문자열.

안경을 청소하십시오. 레지스터 R0은 문자열의 주소이며 R1은 "1"이고 R2는 "2"입니다. Adam Liss는 잘못되었습니다. ARM에서는 R0-R4를 처음 네 함수 매개 변수로 사용합니다.

라인

81d0: e59f0014 ldr r0, [pc, #20] ; 81ec <.text+0x11c>

부하 R0에 반환 뒤에 함수의 "꼬리"에 저장이 주소.

2
#include <stdio.h> 
#include <stdlib.h> 

int main(void) { 
     printf("%d.%d\n",1,2); 
     return EXIT_SUCCESS; 
} 

컴파일 및 분해 :

0000842c <main>: 
    842c: e92d4008 push {r3, lr} 
    8430: e3a01001 mov r1, #1 
    8434: e3a02002 mov r2, #2 
    8438: e59f0008 ldr r0, [pc, #8] ; 8448 <main+0x1c> 
    843c: ebffffcc bl 8374 <_init+0x44> 
    8440: e3a00000 mov r0, #0 
    8444: e8bd8008 pop {r3, pc} 
    8448: 00008524 andeq r8, r0, r4, lsr #10 

R0는 제 파라미터의 형식 문자열은, R1은 두번째 파라미터 1이고, R2는 세번째 파라미터 2 형식 문자열이 문자열, 바이트 배열에 대한 포인터. r0은 해당 포인터로로드되고 주소는 바이트 문자열로로드됩니다.이 경우 주소는 0x8524입니다.

당신이 0x8524을보고 당신의 문자열을 볼 갈 수 호기심 경우,

8524: 252e6425 strcs r6, [lr, #-1061]! ; 0xfffffbdb 
8528: 00000a64 andeq r0, r0, r4, ror #20 

0x25로, 0x64, 0x2e, 0x25로, 0x64, 0x0A, 당신의 분해 주소로에서 마찬가지로 × 00

당신의 문자열은

81d0:  e59f0014  ldr  r0, [pc, #20] ; 81ec <.text+0x11c> 
... 
81ec:  00060120  andeq r0, r6, r0, lsr #2 

주소가 0x60120 인 경우 문자열을 볼 수 있습니다.