2

オーバーフロー、

インラインアセンブリのみを使用してputchar(char)プロシージャを実装するにはどうすればよいですか?これをx86-64アセンブリで実行したいと思います。私がこれを行う理由は、私自身のstandard-lib(または少なくともその一部)を実装するためです。これが私がこれまでに持っているものです:

void putchar(char c)
{   
    /* your code here: print character c on stdout */
    asm(...);
}   

void _start()
{   
    /* exit system call */
    asm("mov $1,%rax;"
        "xor %rbx,%rbx;"
        "int  $0x80"
    );  
}

私はコンパイルしています:

gcc -nostdlib -o putchar putchar.c

私を助けてくれてありがとう!

4

3 に答える 3

3

これは、GCC x86-64インラインアセンブリの例my_putcharです(Intel構文では、AT&Tへの変換は簡単です)。

コンパイル:

gcc -ggdb -masm = intel -o gcc_asm_putchar gcc_asm_putchar.c

編集: rdi破壊されたレジスタから欠落していました。修理済み。

コードは次のとおりです。

int main(void)
{
    char my_char;

    for (my_char = 'a'; my_char <= 'z'; my_char++)
            my_putchar(my_char);

    my_char = '\n';
    my_putchar(my_char);
    return 0;
}

void my_putchar(char my_char)
{
    int dword_char;
    dword_char = (int)my_char;
    asm volatile(
                    ".intel_syntax noprefix;"
                    "mov r10,rsp;"   // save rsp.
                    "sub rsp,8;"     // space for buffer, align by 8.
                    "mov [rsp],al;"  // store the character into buffer.
                    "mov edi,1;"     // STDOUT.
                    "mov rsi,rsp;"   // pointer to buffer.
                    "mov edx,1;"     // string length in bytes.
                    "mov eax,1;"     // WRITE.
                    "syscall;"       // clobbers rcx & r11.
                    "mov rsp,r10;"   // restore rsp.
                    ".att_syntax prefix;"
                    /* outputs */
                    :
                    /* inputs: eax */
                    : "a"(dword_char)
                    /* clobbered regs */
                    : "rcx", "rdx", "rsi", "rdi", "r10", "r11"
                );
}
于 2013-03-24T11:18:27.780 に答える
2

getchar(3)/は、 /の構造内の複雑なデータ、特にバッファリングなどを処理putchar(3)するマクロ(パフォーマンス用)であることに注意してください。nrzによる答えは、ファイル記述子1に対して1文字を実行するだけで、非常に異なります。FILEstdinstdoutwrite(3)

于 2013-03-25T01:56:31.230 に答える
2

GNU Cインラインasmを使用する場合は、asmテンプレート内の命令を使用して「手動で」実行するのではなく、制約を使用してコンパイラに必要な場所を指示します。

writecharおよびについては、x86-64 Linuxシステムコール規約(非常に厳密に)に従って、レジスタ内のすべての入力(およびシステムコール用メモリ内のポイント)を設定するための制約を伴う、テンプレートとしてのreadcharaのみが必要です。System V ABIの関数呼び出し規約に一致します)。 i386およびx86-64でのUNIXおよびLinuxシステムコールの呼び出し規約は何ですか"syscall"charwrite(2)

これにより、コンパイラが値を保持している可能性のあるレッドゾーン(RSPより128バイト下)が壊れるのを簡単に回避できます。インラインasmからそれを壊してはいけません(したがって、最初に使用しない限りpush/popは使用できませんsub rsp, 128C ++インラインasmでのベースポインタレジスタの使用と、GNU Cインラインasmに関する多くの有用なリンクを参照してください)。コンパイラに通知する方法はありません。それを覆い隠す。を使用してビルドすることもできます-mno-redzoneが、この場合、入出力オペランドの方がはるかに優れています。


私はこれらputcharと呼ぶのをためらっていますgetchar。まだバッファリングをサポートしていない独自のstdioを実装している場合はこれを行うことができますが、一部の関数は正しく実装するために入力バッファリングを必要とします。たとえば、scanf文字を調べてフォーマット文字列と一致するかどうかを確認し、一致しない場合は「未読」のままにする必要があります。ただし、出力バッファリングはオプションです。プライベートバッファとそれを作成する関数、または直接それらの入力ポインタを使用してstdioを完全に実装すると思いますwrite()write()

writechar()

int writechar(char my_char)
{
    int retval;  // sys_write uses ssize_t, but we only pass len=1
                 // so the return value is either 1 on success or  -1..-4095 for error
                 // and thus fits in int

    asm volatile("syscall  #dummy arg picked %[dummy]\n"
                    : "=a" (retval)  /* output in EAX */
                    /* inputs: ssize_t read(int fd, const void *buf, size_t count); */
                    : "D"(1),         // RDI = fd=stdout
                      "S"(&my_char),  // RSI = buf
                      "d"(1)          // RDX = length
                      , [dummy]"m" (my_char) // dummy memory input, otherwise compiler doesn't store the arg
                    /* clobbered regs */
                    : "rcx", "r11"  // clobbered by syscall
                );
    // It doesn't matter what addressing mode "m"(my_char) picks,
    // as long as it refers to the same memory as &my_char so the compiler actually does a store

    return retval;
}

これは、Godboltコンパイラエクスプローラでgcc-O3を使用して非常に効率的にコンパイルされます。

writechar:
    movb    %dil, -4(%rsp)        # store my_char into the red-zone
    movl    $1, %edi
    leaq    -4(%rsp), %rsi
    movl    %edi, %edx            # optimize because fd = len
    syscall               # dummy arg picked -4(%rsp)

    ret

@nrzのテストは、ほとんどのレジスタを変更せずに1回だけ設定できるという事実を利用して、その回答の安全でない(レッドゾーンクロバリング)バージョンよりもはるかに効率的mainにインライン化します。syscall

main:
    movl    $97, %r8d            # my_char = 'a'
    leaq    -1(%rsp), %rsi       # rsi = &my_char
    movl    $1, %edx             # len
.L6:                           # do {
    movb    %r8b, -1(%rsp)       # store the char into the buffer
    movl    %edx, %edi           # silly compiler doesn't hoist this out of the loop
    syscall  #dummy arg picked -1(%rsp)

    addl    $1, %r8d
    cmpb    $123, %r8b
    jne     .L6                # } while(++my_char < 'z'+1)

    movb    $10, -1(%rsp)
    syscall  #dummy arg picked -1(%rsp)

    xorl    %eax, %eax         # return 0
    ret

readchar()、同じ方法で行います:

int readchar(void)
{
    int retval;
    unsigned char my_char;
    asm volatile("syscall  #dummy arg picked %[dummy]\n"
                    /* outputs */
                    : "=a" (retval)
                     ,[dummy]"=m" (my_char) // tell the compiler the asm dereferences &my_char

                    /* inputs: ssize_t read(int fd, void *buf, size_t count); */
                    : "D"(0),         // RDI = fd=stdin
                      "S" (&my_char), // RDI = buf
                      "d"(1)          // RDX = length

                    : "rcx", "r11"  // clobbered by syscall
                );
    if (retval < 0)   // -1 .. -4095 are -errno values
        return retval;
    return my_char;   // else a 0..255 char / byte
}

発信者は、をチェックすることでエラーをチェックできますc < 0

于 2018-06-05T02:35:19.363 に答える