programing

레지스터 값을 C 변수로 읽기

kingscode 2022. 8. 25. 22:10
반응형

레지스터 값을 C 변수로 읽기

확장 gcc 인라인어셈블리를 사용하여 레지스터 값을 읽고 C 변수에 저장하는 방법을 본 기억이 있습니다.

나는 아무리 생각해도 asm 문을 어떻게 형성하는지 기억할 수 없다.

편집자 주의: 로컬 register-asm 변수를 사용하는 이러한 방법GCC에 의해 "지원되지 않음"으로 문서화되어 있습니다.지금도 GCC에서는 정상적으로 동작하고 있습니다만, 쨍그랑 소리가 납니다.(이 답변이 게재된 후에 문서의 문구가 추가되었다고 생각합니다.)

글로벌 고정 레지스터 변수 버전에서는 32비트x86의 퍼포먼스 비용이 많이 듭니다.x86에는 GP 정수 레지스터가 7개밖에 없습니다(스택 포인터는 카운트되지 않습니다).그러면 6이 됩니다.모든 코드가 많이 사용하는 글로벌 변수가 있는 경우에만 이 점을 고려하십시오.


지금까지의 답변과는 다른 방향으로 가고 있습니다.왜냐하면 당신이 무엇을 원하는지 잘 모르기 때문입니다.

GCC 매뉴얼 © 5.40 지정된 레지스터의 변수

register int *foo asm ("a5");

여기서a5사용할 레지스터 이름입니다.

당연히 레지스터 이름은 CPU에 의존하지만, 이것은 문제가 되지 않습니다.특정 레지스터는 명시적인 어셈블러 명령에서 가장 유용하기 때문입니다(확장 Asm 참조).일반적으로 두 가지 모두 CPU 유형에 따라 프로그램을 조정해야 합니다.

이러한 레지스터 변수를 정의해도 레지스터는 예약되지 않습니다. 흐름 제어가 변수의 값을 활성화하지 않았다고 판단하는 장소에서는 다른 용도로 사용할 수 있습니다.

GCC 매뉴얼 © 3.18 코드 생성 표기법 옵션

-ffixed-조정하다

reg라는 이름의 레지스터는 고정 레지스터로 취급합니다.생성된 코드는 이를 참조해서는 안 됩니다(스택 포인터, 프레임 포인터 또는 기타 고정 역할 제외).

이렇게 하면 리처드의 대답을 더 쉽게 재현할 수 있어요

int main() {
    register int i asm("ebx");
    return i + 1;
}

이것은 다소 무의미하지만, 당신은 그 안에 무엇이 있는지 모르기 때문에ebx등록하세요.

이 두 가지를 조합하면 이 컴파일을gcc -ffixed-ebx,

#include <stdio.h>
register int counter asm("ebx");
void check(int n) {
    if (!(n % 2 && n % 3 && n % 5)) counter++;
}
int main() {
    int i;
    counter = 0;
    for (i = 1; i <= 100; i++) check(i);
    printf("%d Hamming numbers between 1 and 100\n", counter);
    return 0;
}

사용하는 C 변수가 항상 레지스터에 상주하여 빠른 액세스를 보장하고 다른 생성된 코드에 의해 클로버되지 않도록 할 수 있습니다(간단순히,ebxcalllee-save는 통상의 x86 호출 규약에 따라 이루어지기 때문에 다른 함수에 대한 호출에 의해 clobler가 되어도-ffixed-*(복원도 마찬가지입니다.

한편, 컴파일러의 자유를 제한하고 있기 때문에, 이것은 확실히 휴대성이 없고, 통상 퍼포먼스에도 도움이 되지 않습니다.

ebx를 얻는 방법은 다음과 같습니다.

int main()
{
    int i;
    asm("\t movl %%ebx,%0" : "=r"(i));
    return i + 1;
}

그 결과:

main:
    subl    $4, %esp
    #APP
             movl %ebx,%eax
    #NO_APP
    incl    %eax
    addl    $4, %esp
    ret


Edit:

"=r"(i)는 첫 번째 출력 (%0)이 변수 "i"에 배치되어야 하는 레지스터임을 컴파일러에게 알려주는 출력 제약 조건이다.이 최적화 레벨(-O5)에서 변수 i는 메모리에 저장되지 않고 eax 레지스터에 유지되며, eax 레지스터는 반환값 레지스터이기도 합니다.

gcc에 대해서는 잘 모르지만 VS에서는 다음과 같습니다.

int data = 0;   
__asm
{
    mov ebx, 30
    mov data, ebx
}
cout<<data;

본질적으로, 나는 데이터를 이동시켰다.ebx변수에 따라data.

그러면 스택 포인터 레지스터가 sp 변수로 이동합니다.

intptr_t sp;
asm ("movl %%esp, %0" : "=r" (sp) );

'esp'를 관심 있는 실제 레지스터로 바꾸기만 하면 됩니다(단, %%를 잃지 않도록 하고 변수와 함께 'sp'를 잃지 않도록 하십시오).

GCC 문서 자체:http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html

#include <stdio.h>

void gav(){
        //rgv_t argv = get();
        register unsigned long long i asm("rax");
        register unsigned long long ii asm("rbx");
        printf("I`m gav - first arguman is: %s - 2th arguman is: %s\n", (char *)i, (char *)ii);
}

int main(void)
{
    char *test = "I`m main";
    char *test1 = "I`m main2";
    printf("0x%llx\n", (unsigned long long)&gav);
    asm("call %P0" : :"i"((unsigned long long)&gav), "a"(test), "b"(test1));
    return 0;
}

컴파일러가 생성한 코드가 어떤 레지스터에 저장될지 알 수 없습니다.asm스테이트먼트가 실행되기 때문에 값은 보통 의미가 없습니다.브레이크 포인트에서 정지했을 때 디버거를 사용하여 레지스터 값을 확인하는 것이 훨씬 좋습니다.

그렇다고는 해도, 이 이상한 일을 하려면, 효율적으로 하는 것이 좋을지도 모릅니다.

(x86) 일부 타깃에서는 특정 레지스터 출력 제약을 사용하여 컴파일러에 어떤 레지스터 출력이 입력되는지 알릴 수 있습니다.빈 asm 템플릿과 함께 특정 레지스터 출력 제약조건을 사용하여 asm 문이 입력 시 레지스터 값에는 관심이 없지만 이후 지정된 C 변수가 해당 레지스터에 있음을 컴파일러에 알립니다.

#include <stdint.h>

int foo() {
    uint64_t rax_value;           // type width determines register size
    asm("" : "=a"(rax_value));  // =letter determines which register (or partial reg)

    uint32_t ebx_value;
    asm("" : "=b"(ebx_value));

    uint16_t si_value;
    asm("" : "=S"(si_value) );

    uint8_t sil_value;  // x86-64 required to use the low 8 of a reg other than a-d
       // With -m32:  error: unsupported size for integer register
    asm("# Hi mom, my output constraint picked %0" : "=S"(sil_value) );

    return sil_value + ebx_value;
}

x86-64용 Godbolt에 clang5.0으로 컴파일.사용하지 않는 2개의 출력 값은 최적화되어 있습니다.#APP/#NO_APP컴파일러에 의해 생성된 asm-syslog 쌍(어셈블러를 고속 스위칭 모드로 전환하거나 적어도 그것이 더 이상 문제가 되지 않는 경우에는 사용됩니다).이것은 내가 그것을 사용하지 않았기 때문이다.asm volatile출력 오퍼랜드가 있기 때문에 암묵적으로 피연산자는 아닙니다.volatile.

foo():                                # @foo()
# BB#0:
    push    rbx
    #APP
    #NO_APP
    #DEBUG_VALUE: foo:ebx_value <- %EBX
    #APP
    # Hi mom, my output constraint picked %sil
    #NO_APP
    #DEBUG_VALUE: foo:sil_value <- %SIL
    movzx   eax, sil
    add     eax, ebx
    pop     rbx
    ret
                                    # -- End function
                                    # DW_AT_GNU_pubnames
                                    # DW_AT_external

지정된 레지스터에서 직접 2개의 출력을 추가하는 컴파일러 생성 코드입니다.또한 RBX는 x86-64 System V 호출규칙에서는 콜프리저브 레지스터이기 때문에 RBX의 푸시/팝에 주의해 주십시오(기본적으로 32비트 및 64비트 x86 호출규칙 모두 포함).그러나 우리는 컴파일러에게 asm 스테이트먼트가 거기에 값을 쓴다고 말했습니다.(빈 asm 스테이트먼트를 사용하는 것은 일종의 해킹입니다.아까 말씀드렸듯이 asm 스테이트먼트가 삽입되었을 때 컴파일러가 레지스터로 무엇을 하고 있었는지 모르기 때문에 레지스터를 읽고 싶다고 컴파일러에 직접 말하는 구문은 없습니다.)

컴파일러는 당신의 asm 문을 실제로 그 레지스터를 쓴 것처럼 취급합니다.따라서 나중에 값이 필요할 경우 asm 문이 실행될 때 다른 레지스터에 복사(또는 메모리에 흘려보내기)합니다.


다른 x86 레지스터제약은 (bl/bx/ebx/rbx), (.../rcx), (.../rdx), (sil/si/esi/rsi), (.../rdi)입니다.bpl/bp/ebp/rbp에는 특별한 제약이 없지만 프레임 포인터가 없는 기능에서는 특별한 제약이 없습니다.(아마도 이 기능을 사용하면 코드가 컴파일러가 되지 않기 때문에-fno-omit-frame-pointer.)

사용할 수 있습니다.register uint64_t rbp_var asm ("rbp")이 경우asm("" : "=r" (rbp_var));보증은 다음과 같습니다."=r"제약이 있다rbpr8-r15도 마찬가지로 명시적 제약이 없습니다.ARM과 같은 아키텍처에서는 asm-register 변수가 asm 입출력 제약에 대해 원하는 레지스터를 지정하는 유일한 방법입니다(또한 asm 제약이 지원되는 유일변수라는 점에 유의하십시오.변수 값이 그 레지스터에 추가된다는 보장은 없습니다.


컴파일러가 함수(또는 인라인 후 부모 함수) 내에서 원하는 위치에 이러한 asm 문을 배치하는 것을 막을 있는 것은 없습니다.따라서 레지스터의 값을 샘플링하는 위치를 제어할 수 없습니다. asm volatile 변경은 수 , 것에 만 할 수 있습니다.volatile을 사용하다컴파일러에 의해 생성된 asm을 체크하여 원하는 것을 얻을 수 있는지 확인할 수 있지만 우연으로 인해 나중에 고장날 수 있으므로 주의해 주십시오.

의존관계 체인에 asm 문을 배치하여 컴파일러의 위치를 제어할 있습니다.를 사용하다"+rm"컴파일러에게 실제로는 최적화되지 않는 무언가에 사용되는 다른 변수를 수정한다는 것을 알려주는 제약 조건입니다.

uint32_t ebx_value;
asm("" : "=b"(ebx_value), "+rm"(some_used_variable) );

서 ''는some_used_variable(일부 처리 후) arg로서 다른 함수에 전달된 함수의 반환값일 수 있습니다.또는 루프에서 계산되어 함수의 반환값으로 반환됩니다.이 경우 asm 문은 루프가 종료된 후 해당 변수의 이후 값에 의존하는 코드 앞에 반드시 도달합니다.

단, 이 경우 해당 변수에 대한 연속 전파와 같은 최적화가 무효화됩니다.https://gcc.gnu.org/wiki/DontUseInlineAsm 를 참조해 주세요.컴파일러는 출력 값에 대해 아무것도 추정할 수 없습니다. 컴파일러는 이 값이 다음 중 하나임을 확인하지 않습니다.asm문에 지시사항이 없습니다.


출력 오퍼랜드나 클로버로 사용할 수 없는 레지스터(예: 스택포인터)에서는 동작하지 않습니다.

그러나 프로그램이 스택에 대해 특별한 작업을 수행하는 경우 값을 C 변수로 읽어들이면 스택 포인터에 의미가 있을 수 있습니다.

인라인 asm의 대체 수단으로서 스택주소를 취득하는 것이 있습니다.(하지만 IIRC는 그 함수가 풀 스택프레임을 만드는 원인이 됩니다.-fomit-frame-pointer의로 유효하게 되어 ).x86 의 디폴트로 유효하게 되어 있습니다.

그러나 대부분의 함수는 거의 무료입니다(또한 로컬 변수에 대한 RSP 상대 액세스보다 RBP 상대적인 어드레싱 모드가 작기 때문에 스택프레임을 만드는 것이 코드 사이즈에 적합합니다.

「」의 mov inst의 명령어.asm론론진진 통통통통통

이게 당신이 찾고 있는 거 아닌가요?

구문:

 asm ("fsinx %1,%0" : "=f" (result) : "f" (angle));

언급URL : https://stackoverflow.com/questions/2114163/reading-a-register-value-into-a-c-variable

반응형