2014-09-26 14 views
16

Come si scrive il codice di bit LLVM richiesto per emettere un'istruzione di chiamata di sistema specifica dell'architettura?Syscall/sysenter su LLVM

In particolare, clang supporta l'assemblaggio in linea e supporta chiaramente l'emissione di chiamate di sistema (altrimenti libc e vdso non possono essere compilati). Come funziona la traduzione per questo, e come posso solleticarlo per riprodurre questo comportamento?

Comprendo che lo stesso LLVM potrebbe non comprendere l'interfaccia di chiamata e la pianificazione di registrazione utilizzate da varie architetture in un modo sufficientemente elevato da essere espressa nel codice byte LLVM (ad esempio, che può essere compilato altrove). Tuttavia, c'è chiaramente una fase in cui queste informazioni possono essere aggiunte a.

Come si esegue questa operazione, a partire da quale fase viene dopo "C source with inline assembly"?

Una risposta soddisfacente include un esempio di come richiamare una chiamata di sistema int 0x80 a cinque argomenti. Ne scelgo cinque in quanto richiede lo sversamento in pila e scelgo lo int 0x80 poiché è facilmente comprensibile e sulla piattaforma più comune.

+0

Non esiste un esempio open source pertinente da studiare? –

+0

Perché non utilizzare l'assembly inline per effettuare chiamate di sistema? –

+0

Chris: No, non che ho trovato. –

risposta

3

Pubblicare una risposta da quando l'exa ha emesso una taglia.

Mi sono reso conto che questa era una domanda un po 'sciocca da chiedere dopo i commenti di Ross Ridge, e alcuni in giro con clang.

Supponiamo di avere il seguente programma, che utilizza l'assembly inline per chiamare direttamente write().

#include <stdio.h> 
int main(void) 
{ 
    char *buf = "test\n"; 
    ssize_t n; 
    asm volatile (
     "movl $0x00000002, %%edi\n" /* first argument == stderr */ 
     "movl $0x00000006, %%edx\n" /* third argument == number of bytes */ 
     "movl $1, %%eax\n" /* syscall number == write on amd64 linux */ 
     "syscall\n" 
     : "=A"(n)   /* %rax: return value */ 
     : "S"(buf));  /* %rsi: second argument == address of data to write */ 
    return n; 
} 

Possiamo compilare questo sia con gcc o clang e ottenere più o meno lo stesso risultato.

$ gcc -o syscall.gcc syscall.c 
$ clang -o syscall.clang syscall.c 
$ ./syscall.gcc 
test 
$ ./syscall.clang 
test 

Se vogliamo vedere le istruzioni esatte LLVM, che sarebbero stati utilizzati per emettere questo codice, si può semplicemente utilizzare il flag -emit-llvm. Come puoi vedere, c'è una riga call i64 asm sideeffect che ha la stringa di assemblaggio inline completa.

$ clang -S -emit-llvm syscall.c 
$ cat syscall.ll 
; ModuleID = 'syscall.c' 
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" 
target triple = "x86_64-pc-linux-gnu" 

@.str = private unnamed_addr constant [6 x i8] c"test\0A\00", align 1 

; Function Attrs: nounwind uwtable 
define i32 @main() #0 { 
    %1 = alloca i32, align 4 
    %buf = alloca i8*, align 8 
    %n = alloca i64, align 8 
    store i32 0, i32* %1 
    store i8* getelementptr inbounds ([6 x i8]* @.str, i32 0, i32 0), i8** %buf, align 8 
    %2 = load i8** %buf, align 8 
    %3 = call i64 asm sideeffect "movl $$0x00000002, %edi\0Amovl $$0x00000006, %edx\0Amovl $$1, %eax\0Asyscall\0A", "=A,{si},~{dirflag},~{fpsr},~{flags}"(i8* %2) #1, !srcloc !1 
    store i64 %3, i64* %n, align 8 
    %4 = load i64* %n, align 8 
    %5 = trunc i64 %4 to i32 
    ret i32 %5 
} 

attributes #0 = { nounwind uwtable "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" } 
attributes #1 = { nounwind } 

!llvm.ident = !{!0} 

!0 = metadata !{metadata !"Ubuntu clang version 3.5-1ubuntu1 (trunk) (based on LLVM 3.5)"} 
!1 = metadata !{i32 134, i32 197, i32 259, i32 312} 
+0

Sembra giusto. Non avevo capito di poter usare clang per ottenere la demo con la risposta corretta:] – exa