2012-11-29 3 views
0

C++에서 16 비트 (말장난 의도) 코드를 작성하고 G ++로 컴파일합니다. 여기에서 컴파일하는 컨텍스트에 대한 자세한 내용 : Force GCC to push arguments on the stack before calling function (using PUSH instruction)GCC에서 레지스터를 밀어 넣기 전에 레지스터에 GCC를 묻습니다.

현재 직면하고있는 문제는 내 개체 파일을 연결할 때 LD가 throw되는 오류와 관련되어 있습니다. (작성 어셈블리의 관련 부분)이 번역 할 -S와 -mno-축적 출사-인수 옵션 G ++와 어셈블리 코드에서

asm(".code16gcc\n"); 
void f(const char*); 
int main(){ 
    f("A constant string put in section .rodata at link-time"); 
} 
void f(const char* s){ } 

: : 특히, 여기에 코드 상황이다

/APP 
    .code16gcc 

    .section .rodata 
.LC0: 
    .string "A constant string put in section .rodata at link-time" 
main: 
.LFB0: 
    /* here would be main's prologue, not put because it ain't relevant */ 

    // THIS IS THE CALL f("A constant string put in section .rodata at link-time"); 
    push OFFSET FLAT:.LC0 
    call _Z1fPKc 

이 응용 프로그램은 내가 개발중인 OS의 일부입니다. 특히 부트 로더는이 코드를 BIOS 메모리의 주소 0x70D00에로드합니다. 그러면 .rodata의 주소가 0x70D00보다 커집니다. GCC에는 순수한 16 비트 코드에 대한 지원 기능이 내장되어 있지 않으므로 'OFFSET FLAT : .LC0'을 실행하면 PURE 16 비트 조건에서 단어를 푸시한다는 것을 알 수 없습니다. 즉, .rodata의 주소가 - 0x70DAA 인 경우 명령은 'push 0x70DAA'가됩니다. 링커가 오류가 발생 이유입니다 : 기능에서

main': relocation truncated to fit: R_386_16 against .rodata '

- 링커가 0x70DAA이 단어에 맞지 않습니다 것을 알고 있기 때문이다. 문제를 해결할 수있는 방법은 GCC가 MOV에 인수를하기 전에 인수를 요구하는 것입니다. 예 :

/APP 
    .code16gcc 

    .section .rodata 
.LC0: 
    .string "A constant string put in section .rodata at link-time" 
main: 
.LFB0: 
    /* here would be main's prologue, not put because it ain't relevant */ 

    // THIS IS THE CALL f("A constant string put in section .rodata at link-time"); , now using EAX before pushing the string literal's offset in .rodata 
    mov eax, OFFSET FLAT:.LC0 // move in eax instead 
    push eax // and push eax! 
    call _Z1fPKc 

일부 상황에서는 MSVC이 최적화됩니다. 나는 GCC가 똑같은 일을하도록 강제하는 방법이 있는지 궁금해서 ... 분명히 작동 할 수있는 대안 중 하나는 속성 ((regparm (N)))을 함수 f에 연결하는 것입니다. 하지만 이것은 실제로 좋은 대안이 아닙니다. 스택의 레지스터를 f에서 직접 사용하는 것이 아니라 실제로 레지스터를 푸시하는 것이 아니므로 모든 기능에 대해이 작업을 수행 할 수 없습니다. 당신은 짧은 구글 검색을 수행하여 이것에 대해 더 많은 것을 알 수 있으며 필요한 경우이 옵션이 무엇을하는지 정확히 게시하고 왜 실제로는 효과가 없는지 게시 할 것입니다. 그러나이 질문 - 포스트는 너무 길어지기 시작합니다.

간단히 말해서, 내 질문은 : GCC에게 MOV로 함수에 전달 된 인수를 푸시하기 전에 레지스터에 요청할 수 있습니까?

미리 감사드립니다.

답변

0

MOV-REG-and-PUSH 정렬 방법을 선호했지만이 문제의 해결 방법을 생각해 보았습니다. 필자가 생각한 것은 컴파일러가 컴파일 타임에 계산할 수있는 주소 (예 : .rodata에 입력 한 문자열의 주소)에서만 발생합니다. 그것을 알고

, 나는 주에 로컬 변수를 생성하고 사용하고 그는 다음과 같이 대신 인수를 전달 같이

asm(".code16gcc\n"); 
void f(const char*); 
int main(){ 
    const char* s = "A constant string put in section .rodata at link-time"; 
    // Now use 's' as the argument instead of the string literal 
    f(s); 
} 
void f(const char* s){ } 

이 효율적으로 생성 된 어셈블리 코드를 변경합니다

/APP 
    .code16gcc 

    .section .rodata 
.LC0: 
    .string "A constant string put in section .rodata at link-time" 
main: 
.LFB0: 
    /* here would be main's prologue, not put because it ain't relevant */ 

    // THIS IS THE CALL f(s); 
    mov DWORD PTR [ebp-12], OFFSET FLAT:.LC0 // now specifically loaded in the DWORD 's' 
    sub esp, 12 
    push DWORD PTR [ebp-12] 
    call _Z1fPKc 

볼 수 있듯이 로컬 변수가 대신 사용되며 문자열 리터럴 (.rodata)의 주소가 DWORD로 전송됩니다. 이로 인해 링커 오류가 효과적으로 방지되지만 일부 추가 스택 공간이 사용됩니다.