2014-09-26 10 views
16

Wie schreibe ich den LLVM-Bitcode, der erforderlich ist, um eine architekturspezifische Systemaufrufanweisung auszugeben?Syscall/sysenter auf LLVM

Genauer gesagt unterstützt clang Inline-Assembly und unterstützt eindeutig das Senden von Systemaufrufen (andernfalls konnten libc und vdso nicht kompiliert werden). Wie funktioniert die Übersetzung dafür und wie kann ich sie kitzeln, um dieses Verhalten zu reproduzieren? Ich verstehe, dass LLVM selbst die Aufrufschnittstelle und den Registerplan, die von verschiedenen Architekturen verwendet werden, auf eine ausreichend hohe Art und Weise nicht verstehen kann, um in LLVM-Bytecode ausgedrückt zu werden (der z. B. anderweitig ausgefüllt werden kann). Es gibt jedoch eindeutig eine Phase, in der diese Information hinzugefügt werden kann.

Wie mache ich das, beginnend in welcher Phase auch immer nach "C-Quelle mit Inline-Montage"?

Eine zufriedenstellende Antwort würde ein Beispiel für das Aufrufen eines Systemaufrufs mit fünf Argumenten int 0x80 enthalten. Ich wähle fünf, da dies das Verschütten auf den Stapel erfordert, und wähle int 0x80, da es leicht zu verstehen ist und auf der gebräuchlichsten Plattform.

+0

Gibt es kein relevantes Open-Source-Beispiel, das Sie studieren können? –

+0

Warum nicht Inline-Assembly verwenden, um Systemaufrufe zu tätigen? –

+0

Chris: Nein, das habe ich nicht gefunden. –

Antwort

3

Hier eine Antwort schreiben, da exa eine Prämie ausgestellt hat.

Ich erkannte, dass dies eine etwas alberne Frage nach Ross Ridge's Kommentare war, und einige spielten mit Klängen herum.

Nehmen wir an, wir haben das folgende Programm, das Inline-Assembly verwendet, um write() direkt aufzurufen.

#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; 
} 

Wir können dies entweder mit kompilieren gcc oder clang und etwa das gleiche Ergebnis.

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

Wenn wir die genauen LLVM Anweisungen sehen wollen, die verwendet werden würde, diesen Code zu emittieren, können wir einfach nutzen die -emit-llvm Flagge. Wie Sie sehen können, gibt es eine call i64 asm sideeffect Zeile, die die vollständige Inline-Assembly-Zeichenfolge enthält.

$ 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

Scheint richtig. Ich wusste nicht, dass ich mit clang die Demo mit richtiger Antwort bekommen kann:] – exa