Estoy tratando de escribir mi propia biblioteca de E/S, por lo que el primer paso es poder generar un carácter. Hasta ahora, esto solo necesita funcionar en Linux. Escribí lo que creo que es una etiqueta sólida _putc: en x86 (con sintaxis Intel), y la puse en "io.h", luego la llamé desde mi archivo principal, "test.cpp". Aquí está el contenido de test.cpp:
#include "io.h" extern void _putc(char) __asm__("_putc"); int main(int argc, char** argv){ _putc('c'); return 0; }y aquí está mi io.h:
asm( "\n.intel_syntax noprefix" "\n.globl _putc" "\n_putc:" "\n\tpush [ebp]" "\n\tmov ebp,esp" "\n\tmov eax,4" "\n\tmov ebx,0" "\n\tmov ecx,[ebp+8]" "\n\tmov edx,1" "\n\tint 0x80" "\n\tret" "\n.att_syntax" ;return to g++'s at&t syntax );Estoy desarrollando esto en un sistema operativo Linux x86_64 usando G++
Estoy usando esto para compilar:
g++ -o test test.cpp -Wall Cuando se ejecuta segfaults inmediatamente. Creo que mi problema es llamar a la convención, pero no hay forma de estar absolutamente seguro. Intenté envolver el ensamblaje en una función ( void _putc(char c) ) y eliminar la etiqueta global, pero eso no ayudó. También intenté empujar el contenido de ebp en lugar de las cosas a las que apunta, pero luego no se compilaría.
NOTA: Esto tiene que funcionar solo con la línea #include io.h en el archivo principal (puedo mover el extern void _putc(char) __asm__("_putc"); después de que esto funcione), y tiene que funcionar con C++ , no solo C. Además, no me den lecciones sobre las definiciones de funciones en un archivo de encabezado, eso es justo lo que estoy haciendo.
Con el código de 64 bits, realmente debe considerar usar syscall en lugar de int 0x80 . La llamada al sistema utiliza una convención de 64 bits y los números de llamada al sistema difieren de int 0x80 en código de 32 bits. Puede hacerse una idea de las llamadas al sistema de 64 bits en esta tabla . Las llamadas sys_write y sys_read se ven así:
%rax System call %rdi %rsi %rdx %r10 %r8 %r9 0 sys_read unsigned int fd char *buf size_t count 1 sys_write unsigned int fd const char *buf size_t count
Debe familiarizarse con System V 64-Linux ABI si desea comprender cómo se pasan los parámetros en el código Linux de 64 bits. Entre otras cosas, después de ingresar una llamada, generalmente debe alinear la pila con un límite de 16 bytes si tiene la intención de llamar a otras funciones o llamadas al sistema.
Su código también sufre una falla en la que pasa un carácter a sys_write . sys_write requiere una dirección para un búfer que contiene el carácter, no el carácter en sí.
Este es un volcado de código que funcionaría:
asm( "\n.intel_syntax noprefix" "\n.globl _putc" "\n_putc:" "\n\tadd rsp, -8" // Allocate space on the stack to store the character passed // Also aligns stack back to 16-bye boundary // with return address already on stack "\n\tmov [rsp],rdi" // Move the character passed in RDI to memory(stack) for sys_write "\n\tmov eax,1" // With 64-bit syscalls RAX = 1 for sys_write "\n\txor edi,edi" // 1st parameter to syscall is in RDX. FD = 0 "\n\tlea rsi,[rsp]" // We need to pass pointer to memory containing our char "\n\tmov edx,1" // RDX = 1 character to print. Could reuse EAX instead of 1 for src "\n\tsyscall" // Make syscall (using syscall instead of int 0x80 for 64-bit) "\n\tadd rsp, 8" // Restore stack to state right after function entry "\n\tret" "\n.att_syntax prefix" //return to g++'s at&t syntax );