linux - con - Syscall/sysenter en LLVM
llvm clang for windows (1)
¿Cómo escribo el código de bits LLVM requerido para emitir una instrucción de llamada al sistema específica de la arquitectura?
Más específicamente, clang
admite el ensamblaje en línea y claramente admite la emisión de llamadas al sistema (de lo contrario no se podrían compilar libc
y vdso
). ¿Cómo funciona la traducción para esto, y cómo puedo hacer que la reproduzca para reproducir este comportamiento?
Entiendo que el mismo LLVM puede no entender la interfaz de llamada y el cronograma de registro utilizados por varias arquitecturas de una manera suficientemente alta para expresarse en el código de bytes de LLVM (por ejemplo, se puede completar en otra parte). Sin embargo, hay claramente una etapa en la que se puede agregar esta información.
¿Cómo hago esto, comenzando en cualquier etapa después de "fuente C con ensamblaje en línea"?
Una respuesta satisfactoria incluiría un ejemplo de cómo invocar una llamada al sistema int 0x80
cinco argumentos. Elijo cinco ya que eso requiere derramar en la pila, y elijo int 0x80
ya que es fácil de entender y en la plataforma más común.
Publicar una respuesta aquí ya que exa ha ofrecido una recompensa.
Me di cuenta de que esta era una pregunta un tanto tonta después de los comentarios de Ross Ridge, y algunos estaban jugando con un sonido metálico.
Supongamos que tenemos el siguiente programa, que utiliza el ensamblaje en línea para llamar directamente a 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;
}
Podemos compilar esto con gcc
o clang
y obtener aproximadamente el mismo resultado.
$ gcc -o syscall.gcc syscall.c
$ clang -o syscall.clang syscall.c
$ ./syscall.gcc
test
$ ./syscall.clang
test
Si deseamos ver las instrucciones exactas de LLVM que se usarían para emitir este código, simplemente podemos usar el -emit-llvm
. Como puede ver, hay una línea de call i64 asm sideeffect
que tiene la cadena de ensamblaje en línea 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}