Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Temas - desmon_hak

#1
C / C++ / Desensamblador para el 8086 en C
Noviembre 09, 2024, 11:20:49 PM
Hola chicos, os traigo un desensamblador que he estado construyendo en C para el 8086, aun me falta algo para terminar, pero creo que puede ser útil si alguien mas quiere meterse a hacer uno.

Aquí el repo donde ando subiendo todo esto: No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

En este archivo podréis encontrar comentarios de como funciona un poco la descodificación de instrucciones: No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Os adjunto aquí el código principal que se encarga de descodificar y desensamblar una instrucción:
Código: c
/*
 *	Licencia Apache, Version 2.0 con Modificacion
 *	
 *	Copyright 2023 Desmon (David)
 *	
 *	Se concede permiso, de forma gratuita, a cualquier persona que obtenga una copia de 
 *	este software y archivos de documentacion asociados (el "Software"), para tratar el 
 *	Software sin restricciones, incluidos, entre otros, los derechos de uso, copia, 
 *	modificacion, fusion, publicacion, distribucion, sublicencia y/o venta de copias del 
 *	Software, y para permitir a las personas a quienes se les proporcione el Software 
 *	hacer lo mismo, sujeto a las siguientes condiciones:
 *	
 *	El anterior aviso de copyright y este aviso de permiso se incluiran en todas las 
 *	copias o partes sustanciales del Software.
 *	
 *	EL SOFTWARE SE PROPORCIONA "TAL CUAL", SIN GARANTiA DE NINGÚN TIPO, EXPRESA O 
 *	IMPLiCITA, INCLUYENDO PERO NO LIMITADO A LAS GARANTiAS DE COMERCIABILIDAD, IDONEIDAD 
 *	PARA UN PROPoSITO PARTICULAR Y NO INFRACCIoN. EN NINGÚN CASO LOS TITULARES DEL 
 *	COPYRIGHT O LOS TITULARES DE LOS DERECHOS DE AUTOR SERaN RESPONSABLES DE NINGÚN 
 *	RECLAMO, DAnO U OTRA RESPONSABILIDAD, YA SEA EN UNA ACCIoN DE CONTRATO, AGRAVIO O DE 
 *	OTRA MANERA, QUE SURJA DE, FUERA DE O EN CONEXIoN CON EL SOFTWARE O EL USO U OTRO TIPO
 *	DE ACCIONES EN EL SOFTWARE.
 *	
 *	Ademas, cualquier modificacion realizada por terceros se considerara propiedad del 
 *	titular original de los derechos de autor. Los titulares de derechos de autor 
 *	originales no se responsabilizan de las modificaciones realizadas por terceros.
 *	
 *	Queda explicitamente establecido que no es obligatorio especificar ni notificar los 
 *	cambios realizados entre versiones, ni revelar porciones especificas de codigo 
 *	modificado.
 */
#ifndef __OPCODES_PREFIX_C_
#define __OPCODES_PREFIX_C_

#include "opcodes_prefix.h"

int dissamble_8087(const uint8_t* start, const uint8_t* end, size_t* position, Instruction_info* instruction){
    /*
     * El 8087 es un coprocesador matematico que se conecta al 8086, asi que se ignoran los prefijos.
     * el 8087(x87) no usaba registros de la misma manera que el 8086 lo hace, el x87 tiene 8 registros
     * que van del st(0) al st(7), cada registro tiene 80 bits(16bits * 5) de largo.
     * Los registros st(n) funciona en forma de pila, siendo ST(0) el tope de la pila, y ST(7) el fondo.
     * Esto se le conoce como pila de 8 niveles.
     * Por tanto se entiende que las instrucciones del x87 funcionan apilando(push), calculando, y desapilando(pop).
     * Los registros tope (ST(0) y ST(1)) se pueden usar como operandos explicitos en memoria o registros,
     * por lo que el registro ST(0) puede ser usado como acumulador. Tambien se puede intercambiar el contenido
     * de los registros cib FXCH st(X), lo que hace que se pueda usar 7 registros libremente direccionales mas
     * un acumulador
     *
     * Las instrucciones del x87 empiezan por 1101 1 MF x, donde MF son dos bits que indican el valor con el que trabajan
     */
    if (0 == (size_t)(end - start)) return 0;
    uint8_t *code = start;
    instruction->opcode1.opcode_byte.byte = *code;
    *position += 1; code++;

    // solo queremos obtener los ultimos 3 bytes del opcode de escape para poder saber
    // que flags tiene el opcode 2 para el coprocesador, la tabla my_instruccion_8087_table contiene
    // un puntero a una tabla de flags para la instruccion del 8087
    volatile uint8_t index = instruction->opcode1.opcode_byte.byte & 0b00000111;
    uint8_t *table    = my_instruccion_8087_table[index];
    size_t size_table = my_instruccion_8087_table_sizes[index];
    printf("table[%d]: %p size_table %zu\n", index, table, size_table);

    // obtener las flags de coprocesador de la tabla con indice "index":
    instruction->flags_x87 = table[*code];
    instruction->Mod_rm.byte = *code; // obtener el Mod_rm
    // se puede reciclar el campo Mod_rm para almacenar el opcode 2
    *position += 1; code++;

    if (instruction->flags_x87 & MOD_RM_REG_MASK){
        //instruction->Mod_rm.byte = *code;
        //*position += 1; code++;
        if (instruction->Mod_rm.fields.mod == 0b00) {
            // si 0b110 acceso directo 
            if (instruction->Mod_rm.fields.R_M == 0b110) 
            exit(0);
        } else  if (instruction->Mod_rm.fields.mod == 0b01) {
            instruction->displacement.ui8 =  *code;
            *position += 1; code++;
        } else if (instruction->Mod_rm.fields.mod == 0b10) {
            instruction->displacement.ui16 =  *(uint16_t*)(code);
            *position += 2; code+=2;
        }
    }

    return 1;
}

int dissamble(const uint8_t* start, const uint8_t* end, size_t* position, Instruction_info* instruction, encoder_x86 encoder){
    /*
     * i8086 - datos:
     *
     * Los prefijos no son parte del 8086, asi que se ignoran a no ser que sea un 80386 en modo real u otro
     *  ________________________________________________________________________________________________
     * | opcode  | d | w || mod | reg | r/m || low disp/data || high disp/data || low data || high data |
     * |-----------------||-----------------||---------------||----------------||----------||-----------|
     * |      byte 1     ||      byte 2     ||     byte 3    ||     byte 4     ||  byte 5  ||  byte 6   |
     * |-----------------||-----------------||---------------||----------------||----------||-----------|
     * S = 0 (No extension de signo) ; S = 1 (extender signo de 8bits a 16bits si W = 1)
     * W = 0 (operando de un byte)   ; W = 1 (operando de 2bytes o de palabra)
     * D = 0 (campo REG es el origen); D = 1 (campo REG es el destino)
     * 
     * El 8086 dispone de un byte de opcode maximo, donde se encuentra el Bit D y el bit W normalmente
     * El segundo byte suele codificar Mod/RM
     *      - mod(00, memoria, no desplazamiento)
     *      - mod(01, memoria, desplazamiento de 8bits)
     *      - mod(10, memoria, desplazamiento de 16bits)
     *      - mod(11, modo registro, no desplazamiento)
     *           *excepto R/M = 110, que codifica un desplazamiento de 16 bits
     * 
     * el tercer byte codifica un desplazamiento bajo o datos
     * el cuarto byte codifica direcciones altas o datos
     * el quito byte codifica un dato bajo
     * el sexto byte codifica un dato alto
     * maximo 6 bytes por instruccion.
     * 
     *      | R/M |	                       MOD
     *      |     |   00            01 	              10	         11
     *      | 000 |	[BX+SI] 	[BX+SI+disp8]	[BX+SI+disp16]	   AL / AX
     *      | 001 |	[BX+DI] 	[BX+DI+disp8]	[BX+DI+disp16]	   CL / CX
     *      | 010 |	[BP+SI] 	[BP+SI+disp8]	[BP+SI+disp16]	   DL / DX
     *      | 011 |	[BP+DI] 	[BP+DI+disp8]	[BP+DI+disp16]	   BL / BX
     *      | 100 |	[SI]	    [SI+disp8]	    [SI+disp16]	       AH / SP
     *      | 101 |	[DI]	    [DI+disp8]	    [DI+disp16]	       CH / BP
     *      | 110 |	[disp16]	[BP+disp8]	    [BP+disp16]	       DH / SI
     *      | 111 |	[BX]	    [BX+disp8]	    [BX+disp16]	       BH / DI
     * 
     * Los modos de direccionamiento permitidos:
     * [BX + SI]
     * [BX + DI]
     * [BP + SI]
     * [BP + DI]
     * 
     */
    if (0 == (size_t)(end - start)) return 0;
    uint8_t *code = start;

    // si el opcode actual, tiene el X87_MASK activo, entonces no se usa un procesamiento
    // convencional, se debe usar el procesamiento de desensamblador para el 8087
    if (my_instruccion_8086[*code] & X87_MASK) {
        instruction->flags = my_instruccion_8086[*code];
        return dissamble_8087(start, end, position, instruction);
    } else {

        switch (*code)
        {
            //case Prefix_addr_size: 
            //    instruction->flags_prefix |= FLAG_PREFIX_Prefix_addr_size; // se encontro prefijo 0x67
            //    *position += 1; code++;
            //    break;
            case Prefix_SS:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_SS; // se encontro prefijo 0x36
                *position += 1; code++;
                break;
            case Prefix_CS:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_CS; // se encontro prefijo 0x2E
                *position += 1; code++;
                break;
            case Prefix_ES:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_ES; // se encontro prefijo 0x26
                *position += 1; code++;
                break;
            case Prefix_DS:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_DS; // se encontro prefijo 0x3E
                *position += 1; code++;
                break;
            /*
            // en el 8086 no habia registro  FS y GS
            case Prefix_FS:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_FS; // se encontro prefijo 0x64
                *position += 1; code++;
                break;
            case Prefix_GS:
                instruction->flags_prefix |= FLAG_PREFIX_Prefix_GS; // se encontro prefijo 0x65
                *position += 1; code++;
                break;
            */
            default:
                instruction->flags_prefix = 0;
                break;
        }
        instruction->flags = my_instruccion_8086[*code];
        instruction->opcode1.opcode_byte.byte = *code;
        *position += 1; code++;

        // si la unica flag activa es que tiene registro de segmento, salir
        // posiblemente se trate de una instruccion como push/pop <reg_seg>:
        if (REG_SEG_MASK == instruction->flags) return 1;

        if (instruction->flags & TTTN_MASK) {
            instruction->displacement.ui8 = *code;
            *position += 1; code++;
        }

        if (instruction->flags & MOD_RM_REG_MASK){
            instruction->Mod_rm.byte = *code;
            *position += 1; code++;
            if (instruction->Mod_rm.fields.mod == 0b00) {
                // si 0b110 acceso directo 
                if (instruction->Mod_rm.fields.R_M == 0b110) 
                    exit(0);
            } else {
                if ((instruction->flags & DISP_LOW_MASK) || (instruction->flags & DISP_HIGH_MASK)){
                    if (instruction->Mod_rm.fields.mod == 0b01) {
                    instruction->displacement.ui8 =  *code;
                    *position += 1; code++;
                    } else if (instruction->Mod_rm.fields.mod == 0b10) {
                        instruction->displacement.ui16 =  *(uint16_t*)(code);
                        *position += 2; code+=2;
                    }
                }
            }
        }
        if (instruction->flags & INMED8_MASK){
            instruction->immediate.ui8 =  *code;
            *position += 1; code++;
        } else if (instruction->flags & INMED16_MASK) {
            instruction->immediate.ui16 =  *(uint16_t*)(code);
            *position += 2; code+=2;
        } else if (instruction->flags & DATA_SX_MASK){ // inmediato de 8bits a extender a 16
            instruction->immediate.ui8 =  *code;
            *position += 1; code++;
        }
        if ( instruction->flags & DIS_HIGH_MASK) {
            instruction->displacement.ui16 = *(uint16_t*)(code);
            *position += 2; code+=2;
        }

        return 1; // mientras no se llegue al final queda por desensamblar
    }
}

#define level_tab "\t"

void print_flags(uint16_t flags) {
    if (flags == 0) {
        printf(level_tab"NONE_FLAGS");}
    if (DISP_LOW_MASK    & flags){
        printf("DISP_LOW_MASK | ");}
    if (DISP_HIGH_MASK   & flags){
        printf("DISP_HIGH_MASK | ");}
    if (DATA_MASK_8086   & flags){
        printf("DATA_MASK_8086 | ");}
    if (MASK_PREFIX & flags){
        printf("MASK_PREFIX | ");}
    if (REG_MEM_16_MASK & flags){
        printf("REG_MEM_16_MASK | ");}
    if (INMED8_MASK & flags){
        printf("INMED8_MASK | ");}
    if (DATA_SX_MASK & flags){
        printf("DATA_SX_MASK | ");}
    if (REG_MEM_8_MASK & flags){
        printf("REG_MEM_8_MASK | ");}
    if (INMED16_MASK & flags){
        printf("INMED16_MASK | ");}
    if (REG_SEG_MASK & flags){
        printf("REG_SEG_MASK | ");}
    if (UNDOCUMENTED_OPCODE_MASK & flags){
        printf("UNDOCUMENTED_OPCODE_MASK | ");}
    if (TTTN_MASK & flags){
        printf("TTTN_MASK ");}
    if (DIS_HIGH_MASK & flags){
        printf("DIS_HIGH_MASK ");}
    if (MOD_RM_REG_MASK & flags){
        printf("MOD_RM_REG_MASK ");}
    if (X87_MASK & flags){
        printf("X87_MASK ");}

    printf("\n");
}

void print_flags_x87(uint16_t flags) {
    if (flags == 0) {
        printf(level_tab"NONE_FLAGS_x87");}
    if (ST_REGISTER    & flags){
        printf("ST_REGISTER");}
    if (REG_MEM_8_MASK    & flags){
        printf("\t REG_MEM_8_MASK | ");}
    if (REG_MEM_16_MASK    & flags){
        printf("\t REG_MEM_16_MASK | ");}
    if (MOD_RM_REG_MASK    & flags){
        printf("MOD_RM_REG_MASK");}

    printf("\n");
}

void print_Instruction_info(Instruction_info* instruction, encoder_x86 encode){
    printf("Instruction_info->flags %04hx\n", instruction->flags);
    print_flags(instruction->flags);
    printf("instruction->opcode1.opcode_byte.byte %02x\n", instruction->opcode1.opcode_byte.byte);
    if (instruction->flags == 0) {
        printf("NONE_FLAGS\n");
        return;
    }
    char* string = get_addr_to_encoder_x86(instruction->displacement.ui64, encode);
    printf("instruction->displacement %s\n", string);
    free(string); string = get_addr_to_encoder_x86(instruction->immediate.ui64, encode);
    printf("instruction->immediate %s\n", string);
    free(string); string = NULL;
    if (MOD_RM_REG_MASK  & instruction->flags || X87_MASK & instruction->flags){
        printf("MOD_RM_REG_MASK = 0x%x\n", instruction->Mod_rm.byte);
        printf("\tmod %02x\n", instruction->Mod_rm.fields.mod);
        printf("\treg %02x\n", instruction->Mod_rm.fields.reg);
        printf("\tR_M %02x\n", instruction->Mod_rm.fields.R_M);
    }

    // solo imprimir si es una instruccion para coprocesadores
    if (X87_MASK & instruction->flags ){
        printf("instruction->flags_x87 =  0x%x\n", instruction->flags_x87);
        print_flags_x87(instruction->flags_x87);
    }

    printf("instruction->flags_prefix %02x\n", instruction->flags_prefix);    
    if (FLAG_PREFIX_Prefix_addr_size  & instruction->flags_prefix){
        printf("FLAG_PREFIX_Prefix_addr_size\n");
    }
}

static inline char *get_string_instruction_by_id_8086(string_instrution_id_8086 id) {
    /*
     * 
     * Retorna el string de la instruccion correspondiente a la id string.
     * Se uso en este caso un array para ingresar los valores, en lugar de un switch case
     * que retornara un string por cuestiones de optimizacion. El switch case generara
     * un caso por instruccion agregada. Cada case genera este codigo ensamblador:
     * 
     * loc_140003061:          ; jumptable 0000000140002FAE case 7
     * lea     rax, aEdi_0     ; "aaa"
     * jmp     loc_140002FB7
     * 
     * Lo que quiere decir que con lea se carga el string a devolver y con jmp se salta a la parte de la funcion
     * donde se retorna, por lo tanto 2 instrucciones por caso por 150 instrucciones corresponde a 300 instrucciones
     * assembly para devolver el string correspondiente. el metodo del array es mas eficiente en codigo generado
     * pues solo se necesita verificar los limites antes de acceder a retornar el valor del string
     * 
     */

    if (id >= STRING_INSTRUCTION8086(ADD) && id <= STRING_INSTRUCTION8086(DEC)) return instruction_8086[id];
    else return "error - not exits this instruttion.";
}

static inline const char* get_reg_8086(uint8_t reg, uint8_t w) {
    // esta funcion se usa para obtener un registro atraves del capo RM y REG de mod/rm:
    return reg_8086[w][reg];
}    // en caso de que W = 0 entonces reg8, en caso de W = 1 entonces reg16

static const char* get_mod_rm_8086(Instruction_info *Instruction) {
    Mod_rm mod = Instruction->Mod_rm;
    // en el caso de ser mod 0b11 el campo rm se usa como otro registro:
    // Instruction->opcode1.opcode_bits.b1 es el bit W 0000 000W
    if (mod.fields.mod == 3) return get_reg_8086(mod.fields.R_M, Instruction->opcode1.opcode_bits.b1);
    else return Mod_rm_disp_8086[mod.fields.mod][mod.fields.R_M];
}

const char* get_prefix_by_flags_prefix(uint8_t flags_prefix){
    switch (flags_prefix)
    {
        case FLAG_PREFIX_Prefix_ES: return reg_seg[0];
        case FLAG_PREFIX_Prefix_CS: return reg_seg[1];
        case FLAG_PREFIX_Prefix_SS: return reg_seg[2];
        case FLAG_PREFIX_Prefix_DS: return reg_seg[3];
        case FLAG_PREFIX_Prefix_FS: return reg_seg[4];
        case FLAG_PREFIX_Prefix_GS: return reg_seg[5];
        default: return NULL;
    }
}

void get_string_Instruction_info_8086(Instruction_info *instruction, char* string, size_t size) {
    uint32_t opcode = instruction->opcode1.opcode_byte.byte;
    //print_Instruction_info(instruction, ENCODER_IN_16bits);
    char* string_opcode = NULL;
    if (opcode >= 0xd8 && opcode <= 0xdf) {

    } else {
        char* string_rg = NULL, *string_modrm = NULL;
        char* temp = NULL;  // se usa para operaciones temporales o intermedias como formateos. recordar liberar siempre al finalizar
        char estado = 0;   // se usa en algunos casos para indicar si es necesario liberar o no un buffer
        if (opcode >= 0x80 && opcode <= 0x83){
            // en estas instrucciones el campo reg especifica la instruccion
            // ademas son del tipo "instruccion mem/reg, inmediato"
            string_rg = malloc(15);
            uint32_t id_string_opcode = 0;
            switch (instruction->Mod_rm.fields.reg) {
                case 0b000: id_string_opcode = STRING_INSTRUCTION8086(ADD); break;
                case 0b001: id_string_opcode = STRING_INSTRUCTION8086(OR);  break;
                case 0b010: id_string_opcode = STRING_INSTRUCTION8086(ADC); break;
                case 0b011: id_string_opcode = STRING_INSTRUCTION8086(SBB); break;
                case 0b100: id_string_opcode = STRING_INSTRUCTION8086(AND); break;
                case 0b101: id_string_opcode = STRING_INSTRUCTION8086(SUB); break;
                case 0b110: id_string_opcode = STRING_INSTRUCTION8086(XOR); break;
                case 0b111: id_string_opcode = STRING_INSTRUCTION8086(CMP); break;
            }

            if       (opcode == 0x80 || opcode == 0x81) {  // reg8/mem8,   inmed16; W = 0
                                                           // reg16/mem16, inmed16; W = 1
                snprintf(string_rg, size, "0x%04x", instruction->immediate.ui16);
            }else if (opcode == 0x82 || opcode == 0x83) {  // reg8/mem8,   inmed8;  W = 0
                                                           // reg16/mem16, inmed8;  W = 1
                snprintf(string_rg, size, "0x%02x", instruction->immediate.ui8); 
            }
            char* temp2 = malloc(10);
            if(instruction->flags_prefix != 0) {
                temp = string_modrm = malloc(10);
                snprintf(string_modrm, size, "%s:%s", get_prefix_by_flags_prefix(instruction->flags_prefix), get_mod_rm_8086(instruction)); 
                snprintf(temp2, size, string_modrm, instruction->displacement.ui16); // añadir el desplazamiento del mod rm
                free(temp);
            } else {
                string_modrm = get_mod_rm_8086(instruction);
                snprintf(temp2, size, string_modrm, instruction->displacement.ui16); // añadir el desplazamiento del mod rm
            }
            temp = string_modrm = temp2;
        
            snprintf(string, size, "%s %s %s, %s", 
                get_string_instruction_by_id_8086(id_string_opcode), // 0
                (instruction->opcode1.opcode_bits_final.d == 0 && instruction->Mod_rm.fields.mod != 0b11) ? 
                (instruction->opcode1.opcode_bits.b1 == 0) ? "byte" : "word" : "", // 1
            string_modrm, string_rg ); 

            if (temp != NULL) free(temp); // liberar la memoria reservada
            return;
        } else if (opcode >= 0xd0 && opcode <= 0xd3){
            // en estas instrucciones el campo reg especifica la instruccion
            // ademas son del tipo "instruccion mem/reg, inmediato"
            string_rg = malloc(15);
            uint32_t id_string_opcode = 0;
            switch (instruction->Mod_rm.fields.reg) {
                case 0b000: id_string_opcode = STRING_INSTRUCTION8086(ROL); break;
                case 0b001: id_string_opcode = STRING_INSTRUCTION8086(ROR);  break;
                case 0b010: id_string_opcode = STRING_INSTRUCTION8086(RCL); break;
                case 0b011: id_string_opcode = STRING_INSTRUCTION8086(RCR); break;
                case 0b100: id_string_opcode = STRING_INSTRUCTION8086(SHL); break;
                case 0b101: id_string_opcode = STRING_INSTRUCTION8086(SHR); break;
                case 0b110: id_string_opcode = STRING_INSTRUCTION8086(NOP); break; // invalida
                case 0b111: id_string_opcode = STRING_INSTRUCTION8086(SAR); break;
            }

            if        (opcode == 0xd0 || opcode == 0xd1) {  
                snprintf(string_rg, size, "%s", "1");
            } else if (opcode == 0xd2 || opcode == 0xd3) {
                snprintf(string_rg, size, "%s", "CL"); 
            }
            char* temp2 = malloc(10);
            if(instruction->flags_prefix != 0) {
                temp = string_modrm = malloc(10);
                snprintf(string_modrm, size, "%s:%s", get_prefix_by_flags_prefix(instruction->flags_prefix), get_mod_rm_8086(instruction)); 
                snprintf(temp2, size, string_modrm, instruction->displacement.ui16); // añadir el desplazamiento del mod rm
                free(temp);
            } else {
                string_modrm = get_mod_rm_8086(instruction);
                snprintf(temp2, size, string_modrm, instruction->displacement.ui16); // añadir el desplazamiento del mod rm
            }
            temp = string_modrm = temp2;
        
            snprintf(string, size, "%s %s %s, %s", 
                get_string_instruction_by_id_8086(id_string_opcode), // 0
                (instruction->opcode1.opcode_bits.b1 & 1) ? "word" : "byte", // 1
            string_modrm, string_rg ); 

            if (temp != NULL) free(temp); // liberar la memoria reservada
            return;
        }


        // obtener el string de la instruccion atrabes de su flags obteniada atraves de la tabla de instrucciones
        string_opcode = get_string_instruction_by_id_8086(
            (my_instruccion_8086[instruction->opcode1.opcode_byte.byte]) >> 24);

        if (instruction->flags & MOD_RM_REG_MASK){
            uint8_t opcode = instruction->opcode1.opcode_byte.byte;
            if(
                opcode == 0xc6 || opcode == 0xc7
            ) goto call_far_create;
            // si se trata de la instruccion LEA  reg, [BX + DI + mem16] solo hay una forma de analizarla:
            if (
                opcode == 0x8D
                ) instruction->opcode1.opcode_bits_final.d = 1;
            else if (opcode == 0xc4 || 0xc5 == opcode) {
                instruction->opcode1.opcode_bits_final.d = 1;
                instruction->opcode1.opcode_bits.b1 = 1; // estas instrucciones solo son en 16bits
            }

            if (instruction->opcode1.opcode_bits_final.d == 0) {
                if (instruction->flags_prefix != 0) {
                    
                    // la instruccion usa un registro de segmento
                    temp = string_modrm = malloc(10);
                    snprintf(string_modrm, size, "%s:%s", get_prefix_by_flags_prefix(instruction->flags_prefix), get_mod_rm_8086(instruction)); 
                } else string_modrm = get_mod_rm_8086(instruction);

                // para el opcode 0x8c el campo reg especifica un registro de segmento:
                string_rg    = (opcode == 0x8c) ?
                    reg_seg[instruction->Mod_rm.fields.reg] :
                    get_reg_8086(instruction->Mod_rm.fields.reg, instruction->opcode1.opcode_bits.b1);

            } else {
                 // para el opcode 0x8c el campo reg especifica un registro de segmento:
                string_modrm = (opcode == 0x8e) ?
                    reg_seg[instruction->Mod_rm.fields.reg] :
                    get_reg_8086(instruction->Mod_rm.fields.reg, instruction->opcode1.opcode_bits.b1);
                if (instruction->flags_prefix != 0) {
                    // la instruccion usa un registro de segmento
                    temp = string_rg = malloc(10);
                    snprintf(string_rg, size, "%s:%s", get_prefix_by_flags_prefix(instruction->flags_prefix), get_mod_rm_8086(instruction)); 
                } else string_rg = get_mod_rm_8086(instruction);
            }
            if (instruction->opcode1.opcode_byte.byte == 0x8F) {
                // solo para "pop word ptr [reg + reg]"
                snprintf(string, size, "%s %s %s", string_opcode, 
                    (instruction->opcode1.opcode_bits.b1 == 0) ? "byte" : "word",
                    string_rg
                );
            }else {
                snprintf(string, size, "%s %s %s, %s", string_opcode,
                    (instruction->opcode1.opcode_bits_final.d == 0 && instruction->Mod_rm.fields.mod != 0b11) 
                    ? (instruction->opcode1.opcode_bits.b1 == 0) ? "byte" : "word" : "",
                    string_modrm, string_rg ); 
            }
            if (temp != NULL) free(temp); // liberar la memoria reservada
            snprintf(string, size, string, instruction->displacement.ui16 ); 
            return;
        }
        uint64_t mem = "0x%x\0";
        if (
            (instruction->opcode1.opcode_byte.byte >= 0xa0 && instruction->opcode1.opcode_byte.byte <= 0xa3) ||
            (instruction->opcode1.opcode_byte.byte >= 0xa8 && instruction->opcode1.opcode_byte.byte <= 0xa9)
        ){
            mem = "[0x%x]\0";
        }
        if (instruction->flags & INMED16_MASK || instruction->flags & INMED8_MASK) {
            uint8_t opcode = instruction->opcode1.opcode_byte.byte;
            if(
                opcode == 0x9a || // call far
                (
                    // mov reg, inmed
                    opcode >= 0xB0 && opcode <= 0xBF) || (

                    // ret near inmed16
                    opcode == 0xc0 ) || (opcode == 0xc2) ||( // ret(0xc2) near inmed16 no documentado
                    // el opcode 0xc2 es ret near solo en el 8086

                    // ret far inmed16
                    opcode == 0xc8 ) || (opcode == 0xca)  // ret(0xca) far inmed16 no documentado
                    // el opcode 0xca es ret far solo en el 8086

            ) goto call_far_create;
            if (instruction->opcode1.opcode_bits_final.d == 1) {
                temp = string_modrm = malloc(10);
                snprintf(string_modrm, size, (char*)mem, instruction->immediate.ui16);
                string_rg    = get_reg_8086(instruction->Mod_rm.fields.reg, instruction->opcode1.opcode_bits.b1);
            } else {
                string_modrm = get_reg_8086(instruction->Mod_rm.fields.reg, instruction->opcode1.opcode_bits.b1);
                temp = string_rg = malloc(10);
                snprintf(string_rg, size, (char*)mem, instruction->immediate.ui16);
            }
            snprintf(string, size, "%s %s, %s", string_opcode,string_modrm, string_rg );
            free(temp); // liberar la memoria reservada
            return;
        }
        if (instruction->flags & REG_SEG_MASK) {
            switch (instruction->opcode1.opcode_byte.byte)
            {
                // 00 -> es, 01 -> cs,
                // 10 -> ss, 11 -> ds,
                // opreaciones push & pop
                case 0b00000110: // push es
                case 0b00000111: // pop  es
                case 0b00001110: // push cs
                case 0b00001111: // pop  cs
                case 0b00010110: // push ss
                case 0b00010111: // pop  ss
                case 0b00011110: // push ds
                case 0b00011111: // pop  ds
                    snprintf(string, size, "%s %s",  
                        // obtener si es push o pop
                        (instruction->opcode1.opcode_byte.byte & 0b00000001) 
                            ? instruction_8086[STRING_INSTRUCTION8086(POP)]
                            : instruction_8086[STRING_INSTRUCTION8086(PUSH)],
                        reg_seg[instruction->opcode1.opcode_byte.byte >> 3]);
                        // obtener el registro de segmento
                    return;
            
            default:
                break;
            }
        }
        if (instruction->flags & TTTN_MASK) {
            // todas las instrucciones de salto se describren aqui, estas usan un desplazamiento relativo
            // si son condicionales, el cual es un desplazamiento con signo.
            uint8_t bit_tttn = (instruction->opcode1.opcode_byte.byte >= 0x60 && instruction->opcode1.opcode_byte.byte <= 0x6f) ?
                instruction->opcode1.opcode_byte.byte & 0x0F : instruction->opcode1.opcode_byte.byte & 0x0F;
            /*
             * En el 8086 las instrucciones que van de 0x60 a 0x6f son instrucciones de salto condicionales
             * Las instrucciones que van desde 0x70 a 0x7f no documentadas, son validas en el 8086 como saltos condicionales
             * por lo que la instruccion 0x70 y la instruccion 0x60 son la misma, a de identificarse entonces los bits
             * tttn los cuales estan en uno de los nibbles -> (60...) 0110 tttn o (70...) 0111 tttn
             *
             * El desplazamiento de la instruccion de salto se calcula apartir de ella, por lo tanto se suma los
             * 2 bytes de la instruccion al desplazamiento
             */
            snprintf(string, size, "%s %hhd",
                        // obtener la instruccion via el ID flags de su tabla
                        instruction_8086[STRING_INSTRUCTION8086(JO) + bit_tttn],
                                (int8_t)instruction->displacement.ui8 +2
                            );
            return;
        }
        call_far_create:
        if (
            instruction->flags == 0 || instruction->flags & INMED8_MASK || 
            instruction->flags & INMED16_MASK || instruction->flags & UNDOCUMENTED_OPCODE_MASK
        ) { // si NONE_FLAGS
            volatile uint8_t disp_sub = 0;
            volatile register uint8_t opcode = instruction->opcode1.opcode_byte.byte;
            switch (opcode) {
                /*
                 * hay dos tipos de instrucciones que tienen NONE_FLAGS normalmente. las que no usan registros
                 * como aaa o aas, y las que si lo hacen como inc ax
                 */
                case 0xa4: snprintf(string, size, "MOVSB BYTE ES:[DI], BYTE [SI]"); return;
                case 0xa5: snprintf(string, size, "MOVSW WORD ES:[DI], WORD [SI]"); return;
                case 0xA6: snprintf(string, size, "CMPSB BYTE ES:[SI], BYTE [DI]"); return;
                case 0xA7: snprintf(string, size, "CMPSW WORD ES:[SI], WORD [DI]"); return;
                case 0xaa: snprintf(string, size, "STOSB BYTE ES:[DI], AL");        return;
                case 0xab: snprintf(string, size, "STOSW WORD ES:[DI], AX");        return;
                case 0xAC: snprintf(string, size, "LODSB AL, BYTE PTR [SI]");       return;
                case 0xAD: snprintf(string, size, "LODSW AX, WORD PTR [SI]");       return;
                case 0xAE: snprintf(string, size, "SCASB AL, BYTE PTR ES:[DI]");    return;
                case 0xAF: snprintf(string, size, "SCASW AX, WORD PTR ES:[DI]");    return;
                case 0xCB: snprintf(string, size, "RET FAR");                       return; // retf
                case 0xc0: // no documentada
                case 0xc2: snprintf(string, size, "RET NEAR 0x%04x", instruction->immediate.ui16);    return;

                // estos dos casos solo pueden usar mod 00
                case 0xc6: // mov  byte ptr [reg + reg], inmed8
                case 0xc7: // mov  word ptr [reg + reg], inmed16
                    snprintf(string, size, "%s %s %s, 0x%x",
                        // obtener la instruccion via el ID flags de su tabla
                            get_string_instruction_by_id_8086(
                                STRING_INSTRUCTION8086(MOV)),
                                (opcode == 0xc6) ? "byte" : "word",
                                Mod_rm_disp_8086[0][instruction->Mod_rm.fields.R_M],
                                instruction->immediate.ui16

                            );
                    return;
                case 0xc1: // ret (no documentada) solo para el 8086
                case 0xc3: // ret
                case 0xcc: // int 0x3
                case 0xce: // into
                case 0xcf: // iret
                case 0xd4: // aam
                case 0xd5: // aad
                case 0xd6: // salc
                case 0xd7: // xlat
                case 0x2F:
                case 0x27:
                case 0x37:
                case 0x3F:
                //case 0x90: // nop
                case 0x98: // cbw
                case 0x99: // cwd
                case 0x9b: // wait
                case 0x9c: // pushf
                case 0x9d: // popf
                case 0x9e: // sahf
                case 0x9f: // lahf
                    // las instrucciones que no tienen flags no necesitan ser procesadas para estos casos:
                    snprintf(string, size, "%s",  
                            // obtener la instruccion via el ID flags de su tabla
                                get_string_instruction_by_id_8086(
                                    // acceder a las flags de la instruccion atraves de su opcode
                                    (my_instruccion_8086[opcode]
                                    // obtener el ID string de la instruccion actual
                                    & 0xff000000) >> 24)
                                );
                    return;
                    
                default: // el resto de caso son instrucciones a procesar de distinta manera

                    if      (opcode >= 0x40 && opcode <= 0x47) disp_sub = 0x40;
                        // inc ax; inc cx; inc dx; inc bx; inc sp; inc bp; inc si; inc di
                    else if (opcode >= 0x48 && opcode <= 0x4f) disp_sub = 0x48;
                        // dec ax; dec cx; dec dx; dec bx; dec sp; dec bp; dec si; dec di
                    else if (opcode >= 0x50 && opcode <= 0x57) disp_sub = 0x50;
                        // push ax; push cx; push dx; push bx; push sp; push bp; push si; push di
                    else if (opcode >= 0x58 && opcode <= 0x5f) disp_sub = 0x58;
                        // pop ax; pop cx; pop dx; pop bx; pop sp; pop bp; pop si; pop di
                    else if (opcode >= 0x90 && opcode <= 0x97) {
                        // xchg ax, ax; xchg ax, cx; xchg ax, dx; xchg ax, bx; xchg ax, sp; xchg ax, bp; xchg ax, si; xchg ax, di
                        snprintf(string, size, "%s AX, %s",
                        // obtener la instruccion via el ID flags de su tabla
                            get_string_instruction_by_id_8086(
                                STRING_INSTRUCTION8086(XCHG)),
                                reg_8086[1][instruction->opcode1.opcode_byte.byte - 0x90]
                            );
                        return;
                    } else if (opcode == 0x9a) {
                        snprintf(string, size, "%s FAR 0x%04x:0x%04x",
                        // obtener la instruccion via el ID flags de su tabla
                            get_string_instruction_by_id_8086(STRING_INSTRUCTION8086(CALL)),
                                instruction->displacement.ui16, instruction->immediate.ui16
                            );
                        return;
                    } else if (opcode == 0XC8 || opcode == 0xCA) {
                        snprintf(string, size, "%s FAR 0x%04x",
                        // obtener la instruccion via el ID flags de su tabla
                            get_string_instruction_by_id_8086(STRING_INSTRUCTION8086(RET)),
                                instruction->immediate.ui16
                            );
                        return;
                    } else if(opcode >= 0xB0 && opcode <= 0xBf ) {
                        snprintf(string, size, "%s %s, 0x%04x",
                        // obtener la instruccion via el ID flags de su tabla
                        get_string_instruction_by_id_8086(STRING_INSTRUCTION8086(MOV)),
                            // las instrucciones "mov   reg, inmed" son las siguientes:
                            // entre 0xB0 y 0xB7 se usa reg8 y desde 0xB8 a 0xB9 reg16
                            // al hacer (opcode & 0b1000) >> 3 indicaremos que el bit W sera activo si se da
                            // que el bit de la cuarta posicion esta activo. mientras (opcode - 0xB0) obtendra
                            // uno de los nibbles, donde se descibre el registro a usar, este va de 0x0 a 0xf
                            // pero la tabla reg_8086 es de 2 * 8
                            // 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
                            reg_8086[(opcode & 0b1000) >> 3][(opcode - 0xB0) % 0b1000 ],
                            instruction->immediate.ui16
                        );
                        return;
                    }
                    else {
                        snprintf(string, size, "error unkonow opcode"); return;
                    }
                    // formatear la instruccion:
                    snprintf(string, size, "%s %s",
                        // obtener la instruccion via el ID flags de su tabla
                            get_string_instruction_by_id_8086(
                                // acceder a las flags de la instruccion atraves de su opcode
                                (my_instruccion_8086[opcode]
                                // obtener el ID string de la instruccion actual
                                & 0xff000000) >> 24),
                                reg_8086[1][opcode - disp_sub]
                            );
                    return;
            }
        } else snprintf(string, size, "error unkonow opcode"); 
    }
}
    


#endif
#2
No tienes permitido ver enlaces. Registrate o Entra a tu cuenta

Hola buenas, soy Desmon y soy un usuario nuevo en este foro, no se si incumpla alguna de las reglas del foro en este post pero si así que alguien me lo reporte para corregirlo cuanto antes.

Bueno, viniendo a lo que vengo. Estaba o mejor dicho, quería diseñar un allocator de memoria con GC(garbage collection), cual es la salsa del asunto, bueno, quiero hacerlo lo mas eficiente posible, multiplataforma, funcional, con protecciones de memoria y de acceso y quería hacerlo usando C puro, usando las libC lo menos posible y sus cabeceras(esto implica implementar las funciones necesarias para obtener largos de cadenas, imprimir texto, copiar bloques de memoria y etc).

Hoy lo que vengo a preguntar mas concretamente es si conocen algoritmos eficientes para la tarea de diseñar tal pieza de software, o si me recomiendan alguno en especifico.

Necesito uno o varios algoritmos para poder reorganizar la memoria si hay bloques liberados, o que hacer cuando hay memoria liberada de por medio y hay que reorganizar todo para poder hacer buen uso de ella.

Algoritmos para la recolección de basura, pues si, he visto algoritmos y tal, pero no el como se podría crear una implementación como tal por que me falta documentación, información o etc. Si alguien tiene DOC de haber realizado un proyecto similar, o que tiene algún consejo o etc, cualquier cosa es bien venida.

Por ahora he visto varias funciones para reservar memoria, una de ellas es "NtAllocateVirtualMemory" en el caso de Windows, esta la podemos situar en ntdll.dll, lo que estoy haciendo es cargarla en run time si se encuentra en Windows.

Yo estoy haciendo uso de una subrutina constructora que se llama antes que el main sin que el programador convencional lo sepa ni tenga que ser consciente de ello:
Código: c
void __init1__()
{

#ifndef __DEBUG__
    LPSTR errorText = NULL;
    DWORD error = NULL;
#endif

#if defined(WIN32) || defined(_WIN32) || defined(_WIN64) || defined(__WIN32__) || defined(__NT__)
    // cargar la ntdll
    ntdll = LoadLibraryA("ntdll.dll");
    if (ntdll == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener el handle de la libreria ntdll.dll.\n", error);
        printf("Error al cargar ntdll.dll\n");
#endif
        ROW_ERROR_RET(ERROR_LOAD_LIB)
    }

    // cargar la funcion NtAllocateVirtualMemory de ntdll
    NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t)GetProcAddress(ntdll, "NtAllocateVirtualMemory");
    if (NtAllocateVirtualMemory == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener NtAllocateVirtualMemory de ntdll.dll.\n", error);

        printf("Error al obtener puntero a NtAllocateVirtualMemory\n");
#endif
        TROW_ERROR_RET(ERROR_LOAD_FUNC)
    }
#endif
    NtFreeVirtualMemory = (NtFreeVirtualMemory_t)GetProcAddress(ntdll, "NtFreeVirtualMemory");
    if (NtFreeVirtualMemory == NULL)
    {
#ifdef __DEBUG__
        GET_LAST_ERROR_WIN()
        printf("Error: %s", errorText);
        printf("Error %p: No se pudo obtener NtFreeVirtualMemory de ntdll.dll.\n", error);

        printf("Error al obtener puntero a NtFreeVirtualMemory\n");
#endif
        TROW_ERROR_RET(ERROR_LOAD_FUNC)
    }

#ifdef __DEBUG__
    debug_print("__init1__ __attribute__((constructor))")
#endif
        RegistroTablas.n_tablas = (_uint64_t){0};
    RegistroTablas.tablas = NULL;
    /*
     *  Usar create_table_blocks() para reservar la primera memoria del programa
     *  y que el pogramador no necesite crear una por su cuenta
     *  sino que pueda reservar memoria en ella directamente
     */
    return;
}

primero voy a decir que la macro __DEBUG__ solo la defino a la hora de compilar con -D__DEBUG__ para que pueda ir viendo la información de todo mientras se corre la aplicación que hace uso de esta. La defunción de ntdll esta definido en mi header MemoryAddressBase.h como "HMODULE ntdll;", y el pedazo de codigo que acabáis de ver se encuentra en MemoryAddressBase.c.
La declaración de la función __init1__ se encuentra en el header principal como:
Código: text
void __attribute__((constructor)) __attribute__((visibility("hidden"))) __init1__();
void __attribute__((destructor)) __attribute__((visibility("hidden"))) __end1__();

al igual la rutina destructora. Volviendo a nuestra subrutina constructora, usamos LoadLibraryA, una función de la WinApi que carga ntdll.dll en tiempo de ejecución, como es LoadLibraryA se esta usando la versión ANSI de la función. Si al cargarla da algun error ntdll queda con valor nulo y se ejecuta un ROW_ERROR_RET se este definiendo __DEBUG__ o no. TROW_ERROR_RET se define en el header "ErrorAllocator.h" de la siguiente manera:
Código: c
#define TROW_ERROR_RET(ERROR)                   \
    printf("Error(ErrorAllocator): %p", ERROR); \
    return;
una simple definición que hace uso de un printf para mostrar el error y un return;. El error ERROR_LOAD_LIB se define en la misma header mediante un enum que podemos ver:
Código: text
typedef enum ErrorAllocator
{
    SUCESS,
    ERROR_LOAD_LIB,
    ERROR_LOAD_FUNC
} ErrorAllocator;
Adicionalmente decir que tenemos una función "printErrorAllocator" que recibe el codigo de error y imprime un mensaje acorde a este:
Código: c
void printErrorAllocator(ErrorAllocator err)
{

    switch (err)
    {
    case SUCESS:
        puts(MSG_ERROR_SUCESS);
        return;
    case ERROR_LOAD_LIB:
        puts(MSG_ERROR_LOAD_LIB);
        return;
    case ERROR_LOAD_FUNC:
        puts(MSG_ERROR_LOAD_FUNC);
        return;
    default:
        puts(MSG_ERROR_UNKNOW_ERROR);
        return;
    }
}

Volviendo a la función constructora, en el caso de que se use la macro __DEBUG__ usamos GET_LAST_ERROR_WIN que se define en MemoryAddressBase.h de la siguiente manera:
Código: c
#ifdef __DEBUG__
    LPSTR errorText = NULL;
    DWORD error = NULL;
#endif
#define GET_LAST_ERROR_WIN() error = GetLastError(); \
    FormatMessage( \
        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, \
        NULL,\
        error, \
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\
        (LPSTR)&errorText, \
        0,\
        NULL \
    );

Dicha me permite obtener información mas detallada sobre el error que se produjo al cargar la librería. Prosiguiendo con la subrutina, se obtiene la dirección "NtAllocateVirtualMemory" usando la función GetProcAddress, NtAllocateVirtualMemory y NtAllocateVirtualMemory_t se definen en el header principal de la siguiente manera:
Código: c
typedef NTSTATUS(__stdcall *NtAllocateVirtualMemory_t)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    ULONG_PTR ZeroBits,
    PSIZE_T RegionSize,
    ULONG AllocationType,
    ULONG Protect);
NtAllocateVirtualMemory_t NtAllocateVirtualMemory;

y de igual manera hacemos con "NtFreeVirtualMemory", aquí sus declaraciones:
Código: c
typedef NTSTATUS(__stdcall *NtFreeVirtualMemory_t)(
    HANDLE ProcessHandle,
    PVOID *BaseAddress,
    PSIZE_T RegionSize,
    ULONG FreeType);
NtFreeVirtualMemory_t NtFreeVirtualMemory;

Esto es en el caso de windows, pero en el caso de linux en un inicio he pensado usar mmap, una syscall que permite al kernel pedir memoria para tratarla como queramos, se podria decir que es la función NtAllocateVirtualMemory para linux.
Se que dicha funcion se puede usar con sus cabeceras, pero no lo quise, en su lugar opte mejor usar Assembly si era posible, entonces primero veamos la declaración de mmap en nuestro header principal como:
Código: c
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

y su respectiva declaración para Linux en arquitectura de 32 y 64 bits:
Código: c
#if CPU_BITS_X86 == 32 && defined(__linux__)
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset){
    void *result;
    asm(
        "mov %1, %%eax\n\t"      // Carga el número de llamada del sistema
        "mov %2, %%ebx\n\t"      // addr -> ebx
        "mov %3, %%ecx\n\t"      // length -> ecx
        "mov %4, %%edx\n\t"      // prot -> edx
        "mov %5, %%esi\n\t"      // flags -> esi
        "mov %6, %%edi\n\t"      // fd -> edi
        "mov %7, %%ebp\n\t"      // offset -> ebp
        "int $0x80\n\t"          // Llama al sistema
        "mov %%eax, %0\n\t"      // Guarda el resultado
        : "=r" (result)          // Salida
        : "i" (__NR_mmap), "r" (addr), "r" (length), "r" (prot), "r" (flags), "r" (fd), "r" (offset) // Entradas
        : "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "%ebp" // Registros modificados
    );
    return result;
}
#elif CPU_BITS_X86 == 64 && defined(__linux__)
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
    void *result;
    asm(
        "mov %1, %%rax\n\t"      // Carga el número de llamada del sistema
        "mov %2, %%rdi\n\t"      // addr -> rdi
        "mov %3, %%rsi\n\t"      // length -> rsi
        "mov %4, %%rdx\n\t"      // prot -> rdx
        "mov %5, %%r10\n\t"      // flags -> r10
        "mov %6, %%r8\n\t"       // fd -> r8
        "mov %7, %%r9\n\t"       // offset -> r9
        "syscall\n\t"            // Llama al sistema
        "mov %%rax, %0\n\t"      // Guarda el resultado
        : "=r" (result)          // Salida
        : "i" (__NR_mmap), "r" (addr), "r" (length), "r" (prot), "r" (flags), "r" (fd), "r" (offset) // Entradas
        : "%rax", "%rdi", "%rsi", "%rdx", "%r10", "%r8", "%r9", "memory" // Registros modificados
    );
    return result;
}
#elif defined(__linux__)
#warning  "La arquitectura actual no es de 64bits o 32bits"
void *mmap_syscall(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
    return (void*) syscall(__NR_mmap, addr, length, prot, flags, fd, offset);
}

Los lectores atentos habrán visto que hay una ultima directiva #elif al final, hago uso de esta para, en el caso de que no se pueda detectar el tamaño de palabra como 32 o 64bits, usar un comportamiento default donde se usa syscall y __NR_mmap definidas en cabeceras de C para usar mmap sin tener que hacerlo en asm, podría incluir una condicional mas al elif para que se use esta opción si la maquina en la que se esta compilando no es x86, lo que lo haría mas portable en teoría. Por lo demas, la función mmap_syscall es definida con una versión de 32bits donde se usa registros de 32bits (eax, ebx, ...) y la mas conocida interrupción 80h. En el caso de estar compilándose el código en 64bits(-m64), tenemos una versión que usa los registros de 64bits (rax, rbx, ...) y usamos la instrucción syscall exclusiva de maquinas x86 de 64bits en lugar de int.

Ahora, habréis notado que ambas syscall's, la versión de linux y la versión de windows reciben una serie de argumentos, bastante similares en cuanto a su uso, por ejemplo tenemos un argumento para definir la cantidad de memoria que queremos reservar, lo que seria ¿una pagina de memoria quizas? habría que verlo, pero el campo que nos interesa es ULONG Protect en el caso de "NtAllocateVirtualMemory" o "int prot" en el caso de "mmap". Estos campos se usan para lo mismo y es básicamente para definir los permisos que tiene la "sección" de memoria que queremos pedir al kernel. Esto lo podría especificar el programador y para ello, hemos creado un enum que apenas cambia independientemente de la plataforma para especificar los permisos, aqui podemos verlo, y se situa en la header principal:
Código: c
// permisos permitidos para mmap y NtAllocateVirtualMemory 
typedef enum PermissionMemory 
{
    /*
     *  get_permission_memory:
     *      Obtener los permisos de un bloque.
     *  set_permission_memory:
     *      Cambiar los permisos de un bloque.
     * get_char_permission_memory:
     *      Obtener los permisos de un bloque pero
     *      representado con los caracteres (r,w,x).
     */

    NONE,            // 0 + 0 + 0 = --- = 0
    READ,            // 0 + 0 + 1 = r-- = 1
    WRITE,           // 0 + 0 + 2 = -w- = 2

    READ_WRITE,      // 0 + 1 + 2 = rw- = 3
    EXEC,            // 0 + 0 + 4 = --x = 4
    READ_EXEC,       // 0 + 4 + 1 = r-x = 5
    WRITE_EXEC,      // 0 + 2 + 4 = -wx = 6
    READ_WRITE_EXEC, // 1 + 2 + 4 = rwx = 7

    #ifdef __linux__
    GROWSDOWN = 0x01000000,
    GROWSUP   = 0x02000000,
    SEM       = 0x04000000,
    LOOSE     = 0x10000000,
    HUGETLB   = 0x00040000,
    SAO       = 0x08000000,
    #endif

} PermissionMemory;
Cabe mencionar que estos son permisos de PAGINAS y creo que esto es necesario recalcarlo. Si mal no he visto, estos permisos se especifican de igual manera en Linux como en Windows, que es con valores enteros, 0(nada)(-), 1(leer)(r), 2(escribir)(w) y 4(ejecutar)(x). Los mas linuxeros apreciaran que esto es como los valores octales en chmod. Podemos combinar permisos simplemente sumando valores, por ejemplo para tener permisos de lectura y escritura, sumamos el valor 1(leer) con el valor 2(escribir), lo que da el valor 3(leer y escribir pero no ejecutar). De igual manera podemos dar todos los permisos sumando 1(leer), 2(escribir), 4(ejecutar), lo que da como resultado 7(1+2+4=rwx), lo que se corresponde al 7 de cuando ponemos "chmod 777 <file>" por ejemplo.

Ahora, entremos en declaraciones de los bloques de memoria y etc:
Código: c
typedef struct block_memory
{
    struct
    {                                     // cabecera de los bloques
        _uint64_t id;                     // id del bloque.
        _uint64_t size;                   // tamaño del bloque escluyendo la cabecera. sizeof(addr_memory)
        _uint64_t is_free;                // espacio libre del bloque (size == is_free[el bloque no fue usado])
        struct block_memory *next;        // direcion del siguiente bloque de memoria
        PermissionMemory PermissionBlock; // permisos del bloque.
    } cabecera_bloque;                    // datos de cabecera del bloque
    void *addr_memory;                    // direccion de memoria donde se almacenan los datos del bloque
} block_memory;
#define size_block_memory sizeof(block_memory)


Cabe mencionar que aquí el concepto de "bloque" no es mas que una abstracción de una representación de datos guardados en la misma memoria de la pagina, donde alojamos información de los permisos que tiene una "seccion" que pedimos al allocator, lo que vendria siendo en C el puntero que nos retorna malloc, algo similar, solo que nuestro malloc en teoria debería ser mas complejo. Dichos bloques se alojan en tablas, tablas de bloques valga la redundancia y las tablas se alojan en paginas de memoria, podemos tener una o mas tablas de memoria con distintos permisos y distintos bloques a lo largo de nuestro programa. Y claro, como no, decir que una paginas puede tener mas de una tabla, esto va a depende de si la tabla es mas grande que la pagina, en ese caso necesitaremos mas de una pagina para guardar una tabla. Si la tabla es 1/4 de la pagina, pues en  una misma pagina puede entrar 4 tablas, entonces aqui debemos hacer un diseño razonablemente inteligente mas adelante.

Prosigamos, cada bloque de memoria tiene un header, una cabecera de bloque para que nos entendamos, y esta la hemos visto ya en la definición de block_memory:
Código: c
#define size_block_memory sizeof(block_memory)

typedef struct header
{                                     // cabecera de los bloques
    _uint64_t id;                     // id del bloque
    _uint64_t size;                   // tamaño del bloque escluyendo la cabecera
    _uint64_t is_free;                // espacio libre del bloque (size == is_free[el bloque no fue usado])
    block_memory *next;               // direcion del siguiente bloque de memoria
    PermissionMemory PermissionBlock; // permisos del bloque.
} header;
#define size_header sizeof(header)

En la cabecera guardaríamos un id de 64bits, aunque estoy pensando en cambiar estos valores de 64bits a datos que cambien entre 32, 64 y etc dependiendo del largo de palabra del procesador. Este ID, es un ID relativo entorno a una tabla, que quiero decir con esto. Pues que puede haber mas de un bloque de memoria que tenga el ID 1, mientras se han tablas diferentes, pero en una misma tabla, solo puede haber un bloque con el mismo ID que identifique a este mismo, sera su DNI en la table. Tambien tenemos un campo para indicar el tamaño del bloque, esto lo hacemos para conocer el offset de cada bloque de memoria, y a lo largo de nuestro allocator no pasarnos escribiendo memoria. is_free seria otro valor que indicaría el espacio libre en el bloque. Si size y is_free tienen el mismo valor podemos deducir que el bloque no tiene nada dentro, esta vacio, o que el bloque tiene un tamaño de 0. El tamaño ocupado lo podríamos obtener mediante una simple resta, que seria size-is_free, eso nos daría el tamaño ya ocupado del bloque. El siguiente miembro es next, que es un puntero de la estructura block_memory donde se especificaría la dirección del siguiente bloque de memoria con su propia cabecera. Hay que decir que esto es similar a una lista enlazada, tal vez agregue el que sea una doble lista enlaza, para eso habría que incluir un mismo puntero al bloque anterior. Otro miembro de la cabecera de bloque es el de PermissionBlock, este es un valor el cual indica los permisos que hay en dicho bloque de memoria, hay que decir que sus permisos no pueden ser superior a los permisos de la tabla, eso quiere decir que si la tabla solo tiene permisos de lectura, no podemos hacer que el bloque tenga permisos de ejecución, sino que además todos los bloques de esta solo tendrán permisos de lectura o ninguno, los permisos pueden ser los mismos o inferiores, pero NUNCA SUPERIORES. Esto lo hacemos como un posible mecanismo de seguridad, cabe mencionar que tendremos que ser nosotros luego el que nos aseguremos que no se violen estos permisos.

Ahora, el bloque de memoria aparte del header, tiene un puntero void donde tiene que ser un puntero que cumpla algunas características, como que tenga el tamaño adecuado(size) y que no invada las direcciones de otros bloques, por que en ese caso estaríamos sobrescribiendo bloques y esto es una falla muy gorda. El miembro addr_memory seria como tal el puntero que el programador usara de forma directa u indirecta para lo que el quiera, como puede ser guardar estructuras o leer archivos entre otros.

Veamos como seria la definicion de table_blocks, o tabla de bloques:
Código: c
typedef struct table_blocks
{                                     // tabla de bloques para el allocator
    _uint64_t numbre_blocks;          // cantidad de bloques dentro de la tabla
    block_memory *first;              // primer bloque
    block_memory *last;               // ultimo bloque
    PermissionMemory PermissionTable; // Permisos de la tabla.
    struct table_blocks *next;        // siguiente tabla
} table_blocks;

Los comentarios deben hablar por si mismo, pero aun asi os explicare. Como ya hemos mencionado los bloques se alojan en tablas y las tablas en paginas. numbre_blocks es un miembro donde especificamos la cantidad de bloques, podríamos hacer también un campo donde especificar la cantidad de bloques máxima que podemos tener en la tabla. Otro miembro es first y  last. El primer mencionado es un puntero al primer bloque de la tabla, y el segundo, es un puntero al ultimo bloque, esto tal vez podemos hacerlo para hacer sorting, o simplemente para conocer como se aloja nuestros bloques en la tabla. Hay que mencionar que el miembro last cambiara cada vez que añadamos un bloque nuevo al final de la tabla, y que first cambiara si el bloque al que apunta es eliminado.

PermissionTable podéis deducir que hace(especificar los permisos de la tabla). Y un puntero importante es next, este al igual que en header, hacer referencia a la siguiente tabla, tal vez también este bien hacer una doble lista enlazada.

Luego tendriamos un registro de tablas donde se guardaria las direcciones de cada tabla, este registro tendria que permitir que se actualice elimando y añadiendo tablas, sin que las demas se vean afectadas, actualmente y posiblemente temporalmente esta definida asi:
Código: c
static struct
{
    table_blocks **tablas; // lista donde se almacenan todas las tablas en run time
    _uint64_t n_tablas;    // Numero de tablas almacenadas a lo  largo del programa
} RegistroTablas;

Ahora bien, algunas declaraciones de funciones que hay por ahora:
Código: c
info_os get_info_system();
table_blocks create_table_blocks(ui64 size_memory_resb, PermissionMemory permisos);

bool is_free(block_memory bloque_memoria);

uint64_t get_real_size_block(block_memory bloque_memoria);
uint64_t get_size_block(block_memory bloque_memoria);

block_memory*malloc_c(table_blocks _table_blocks, ui64 size);
size_t create_hash_align(ui8 *data, type_hash type_hash);

void __attribute__((constructor)) __attribute__((visibility("hidden"))) __init1__();
void __attribute__((destructor)) __attribute__((visibility("hidden"))) __end1__();


get_info_system es una función que retorna información acerca de la plataforma actual, esta se retorna en forma de una estructura de tipo info_os la cual varia segun sea windows o linux. Declaraciones:

Para Linux:
Código: c
typedef struct info_os
{
    struct sysinfo info;
    struct utsname sysinfo;
} info_os;

Para Windows:
typedef struct info_os
{
    LPSYSTEM_INFO info;
} info_os;

y aqui su definicion:
Código: c

info_os get_info_system()
{
    info_os data;
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
    // Si el sistema es windows, GetSystemInfo guarda la info posible
    // en la estructura info_os la cual varia entre plataforma.
    GetSystemInfo(data.info);
    /*
     *  void GetSystemInfo([out] LPSYSTEM_INFO lpSystemInfo);
     *
     *  typedef struct _SYSTEM_INFO {
     *
     *      union {
     *          DWORD dwOemId;
     *          struct {
     *
     *              WORD wProcessorArchitecture;
     *              WORD wReserved;
     *
     *          } DUMMYSTRUCTNAME;
     *      } DUMMYUNIONNAME;
     *
     *      DWORD     dwPageSize;
     *      LPVOID    lpMinimumApplicationAddress;
     *      LPVOID    lpMaximumApplicationAddress;
     *      DWORD_PTR dwActiveProcessorMask;
     *      DWORD     dwNumberOfProcessors;
     *      DWORD     dwProcessorType;
     *      DWORD     dwAllocationGranularity;
     *      WORD      wProcessorLevel;
     *      WORD      wProcessorRevision;
     *  } SYSTEM_INFO, *LPSYSTEM_INFO;
     *
     */
#elif __linux__
    /*
     *  #include <sys/sysinfo.h>
     *  int sysinfo(struct sysinfo *info);
     *
     *  Until Linux 2.3.16:
     *  struct sysinfo
     *  {
     *      long uptime;             // Seconds since boot
     *      unsigned long loads[3];  // 1, 5, and 15 minute load averages
     *      unsigned long totalram;  // Total usable main memory size
     *      unsigned long freeram;   // Available memory size
     *      unsigned long sharedram; // Amount of shared memory
     *      unsigned long bufferram; // Memory used by buffers
     *      unsigned long totalswap; // Total swap space size
     *      unsigned long freeswap;  // Swap space still available
     *      unsigned short procs;    // Number of current processes
     *      char _f[22];             // Pads structure to 64 bytes
     *  };
     *
     *  Since Linux 2.3.23 (i386) and Linux 2.3.48 (all architectures) the structure is
     *  struct sysinfo {
     *      long uptime;             // Seconds since boot
     *      unsigned long loads[3];  // 1, 5, and 15 minute load averages
     *      unsigned long totalram;  // Total usable main memory size
     *      unsigned long freeram;   // Available memory size
     *      unsigned long sharedram; // Amount of shared memory
     *      unsigned long bufferram; // Memory used by buffers
     *      unsigned long totalswap; // Total swap space size
     *      unsigned long freeswap;  // Swap space still available
     *      unsigned short procs;    // Number of current processes
     *      unsigned long totalhigh; // Total high memory size
     *      unsigned long freehigh;  // Available high memory size
     *      unsigned int mem_unit;   // Memory unit size in bytes
     *      char _f[20-2*sizeof(long)-sizeof(int)]; //Padding to 64 bytes
     *  };
     *
     *
     *  #include <sys/utsname.h>
     *  int uname(struct utsname *buf);
     *  struct utsname {
     *      char sysname[];    // Operating system name (e.g., "Linux")
     *      char nodename[];   // Name within "some implementation-defined network"
     *      char release[];    // Operating system release (e.g., "2.6.28")
     *      char version[];    // Operating system version
     *      char machine[];    // Hardware identifier
     *      #ifdef _GNU_SOURCE
     *          char domainname[]; // NIS or YP domain name
     *      #endif
     *  };
     */
    // si el sistema es linux, se usa la funcion uname y sysinfo
    // para obtener informacion
    uname(&data.sysinfo);
    sysinfo(&data.info);
#endif
    return data;
}
Como podéis ver hace uso de las funciones de sus propios sistemas para obtener información útil.

La siguiente función seria create_table_blocks, esta recibira un valor de 64bits que seria el tamaño maximo para la tabla de bloques, y un argumento donde se le especifica los permisos de la tabla. Esta funcion se encarga de inicializar una tabla de bloques con los permisos y memoria necesaria. Por ahora la definición sin desarrollar se ve asi:
Código: c
table_blocks create_table_blocks(ui64 size_memory_resb, PermissionMemory permisos)
{
    /*
     * - Esta funcion crea una primera tabla con una
     *      cantidad determinada de buffer(SIZE_TABLE_DEFAULT).
     *
     *  size_memory_resb: memoria a reservar. 0 es reservar
     *                      una tabla con SIZE_TABLE_DEFAULT.
     *                      Un valor distinto impplica reserva
     *                      esa cantidad de bytes.
     */

    if (size_memory_resb == 0)
        size_memory_resb = SIZE_TABLE_DEFAULT;

    // se tiene que almacenar en memoria, y pasarse a tabla
    block_memory bloqueNULL = {
        // falta desarollar:
        //.addr_memory = resb_memory(size_memory_resb); // Esta funcion reserva memoria(conceptualmente)
        .cabecera_bloque = {
            .id = 0,                     // ID 0 para el primer bloque
            .is_free = size_memory_resb, // inicialmente al primer bloque se le asigna la memoria de toda la tabla
            .size = size_memory_resb,
            .next = NULL,           // Aun no se a agregado un primer bloque.
            .PermissionBlock = NONE // Este bloque no tiene ningun tipo de permiso
        }};

    table_blocks tabla = {
        .numbre_blocks = 1,          // numero de bloques iniciales.
        .PermissionTable = permisos, // tabla con los permisos especificados por argumentos
        .first = bloqueNULL,         // primer bloque
        .last = bloqueNULL           // ultimo bloque.
    };
}


La función is_free es de mis favoritas por su simplificad:
Código: c
bool is_free(block_memory bloque_memoria)
{
    /*
     *  - Esta funcion retorna 1 si el bloque esta "vacio" o si tiene algo.
     *  Se recibe un bloque de memoria y se compara el tamano del bloque(size) con la cantidad
     *  de espacio libre(is_free), es deducible que si la cantidad libre es igual al tamano del
     *  bloque quiere decir que el mismo esta vacio, o mejor dicho no contiene nada exceptuando
     * la cabecera.
     */
    if ((bloque_memoria.cabecera_bloque.size._uint64_t - bloque_memoria.cabecera_bloque.is_free._uint64_t) == 0)
        return true;
    else
        return false;
}

Simplemente ejercemos la resta tal como hemos mencionado en la parte de arriba, y si el resultado de la resta es 0 es que esta libre, me recuerda a la resta que hace cmp por alguna razon JSJSJJS.

get_real_size_block daria el tamaño del bloque real, eso quiere decir, que no se te dara el tamaño que como programador pediste, si no una suma de este y el tamaño de la cabecera:
Código: c
#define get_real_size_block_DEF(bloque_memoria) bloque_memoria.cabecera_bloque.size._uint64_t+ size_header

uint64_t get_real_size_block(block_memory bloque_memoria)
{
    /*
     *  - Esta funcion devuelve el tamano real del bloque incluyendo la cabecera del bloque.
     *  Para la operacion se obtendra el tamano ocupado del bloque(size-is_free) y se le suma
     *  el tamano de la cabecera.
     */
    return get_real_size_block_DEF(bloque_memoria);
}
Lo que hacemos es solo sumarle lo que el programador especifico "size" el tamaño del header. De igual manera tenemos get_size_block que retorna el pseudo-tamaño del bloque, un tamaño que el programador pueda usar, ya que get_real_size_block es raro que lo use el usuario, esta función debería usarla mas el allocator para comprender el tamaño real que se esta reservando, ya que en los allocator reales, aunque nosotros en malloc realicemos malloc(16), no se esta reservando únicamente el 16Bytes, sino que internamente esta misma función reserva memoria para sus propias estructuras, solo que como usuario de mas alto nivel no apreciamos esto.

Otra función importante, y que como tal seria la que usualmente quizas usaría el programador:
Código: c
block_memory *malloc_c(table_blocks _table_blocks, ui64 size)
{
    /*
     *
     *  table_blocks _table_blocks: esta estructura es una tabla que contiene los distintos
     *                              datos almacenador por el programa en forma de bloques.
     *                              El programa puede estar formado por un array de tablas
     *                              que contenda cada uno colecciones de bloques de memoria.
     *
     * ui64 size: permite especificar el tamaño de bloque a reservar en la tabla.
     *
     */
    block_memory *ptr = NULL;

    if (_table_blocks.numbre_blocks._uint64_t != 0)
    {
        // aumentar en uno la cantidad de bloques si no esta vacia
        _table_blocks.numbre_blocks._uint64_t++;
    }
    else
    {
        // si la tabla esta vacia
    }

    return ptr;
}

Esta función cogería una tabla que recibe como argumento, y un valor de 64bits(size) el cual seria el tamaño del bloque de memoria. Se me olvido agregar otro para especificar los permisos pero bueno. Esta función se encargaría de entre otras cosas, ver si en la tabla queda memoria para dicho bloque, y en cado contrario retornar NULL. También aumentaría en 1 el tamaño de bloques si el valor no es 0, en ese caso tmb deberíamos actualizar el miembro next del anterior bloque, al bloque actual por ejemplo. En el caso de que la tabla este vacia, la inicializamos si no lo esta, y en next colocaríamos NULL pues no hay bloque de memoria al que apuntar o como mucho, le pondríamos que apunte a el mismo.


Ahora, una función a tener en cuenta es create_hash_align, esta función es algo característica. Esta se encarga de que, dada x entrada(data), se cree un hash que puede estar alineado en memoria y del largo de palabra del procesador, independientemente de la función hash usada. Cabe mencionar que la utilidad de esta función no es completamente criptografica, se usara mas para la comprobación de errores por ejemplo, o para la detención de alteraciones de memoria, seria algo así como nuestro checksum y que este alineado con la memoria seria opcional.

Todo se especifica mediante de un enum type_hash:
Código: c
typedef struct type_hash
{
    enum
    {
        MurmurHash128Bits_x86_32,
        MurmurHash128Bits_x86_64,
        MurmurHash32Bits,
        MD5,
        md4,
        sha256
    } type_hash;
    enum
    {
        no,
        yes
    } create_hash_align;
    union
    {
        ui64 hash_128bits[2];
        ui32 hash_32bits;
        ui16 hash_16bits;
    } hash;

} type_hash;

en el primer miembro de type_hash, el miembro type_hash, se usa para especificar el algoritmo hash que he implementado a usar. cabe mencionar que MurmurHash128Bits_x86_64 y MurmurHash128Bits_x86_32 son versiones de Murmur hash de claves de 128bits optimizadas para sus correspondientes arquitecturas. Tenemos MurmurHash32Bits que seria una versión de la misma pero con una clave de 32 bits, la posibilidad de usar la función MD5 y MD4 y la conocida función SHA256.

El siguiente campo es create_hash_align donde se le especifica si queremos un hash del largo de palabra del procesador para su alineación en memoria o si preferimos que el largo se defina por la función y el miembro hash.

El miembro hash, es un miembro donde se guarda el hash si no es alineado, en caso de que el hash este alineado, se retorna desde la función como un valor size_t. Estos campos miembros se define para claves de 16, 32 y 128 bits que son las que necesarias para las funciones hash. Decir que la función create_hash_align  no esta terminada:
Código: c
size_t create_hash_align(ui8 *data, type_hash type_hash)
{
    /*
     * Se creara una func hash que recibe un vector.
     * El vector a de ser mayor o igual que el tamano
     * de palabra del procesador. En el caso de no ser asi
     * se usara un padding de 0's para llegar a completar el vector.
     * Una vez se tiene el vetor, se le aplicar una funcion H() para
     * obtener un hash h. h = H(x). Donde x es el vector.
     * h sera del tamano de palabra del procesador, es decir, en un
     * procesador de 32bits, h es de 32/8bits=4 y en uno de 64bits
     * h es 64/8=8bytes.
     *
     * 32bits => 0000_0000.0000_0000
     *
     * 32bits => 0000_0000.0000_0000.0000_0000.0000_0000
     *
     * 64bits => 0000_0000.0000_0000.0000_0000.0000_0000
     *           0000_0000.0000_0000.0000_0000.0000_0000
     */

#if CPU_BITS_X86 == 64
#define SIZE_WORD 8
    ui8 hash[SIZE_WORD] = {0, 0, 0, 0, 0, 0, 0, 0};
#elif CPU_BITS_X86 == 32
#define SIZE_WORD 4
    ui8 hash[SIZE_WORD] = {0, 0, 0, 0};
#elif CPU_BITS_X86 == 16
#define SIZE_WORD 2
    ui8 hash[SIZE_WORD] = {0, 0};
#endif
    if (type_hash.create_hash_align == yes)
    {
        ui32 size = ((_uint32_t)strlen_c((string_c)data))._uint32_t;
        ui32 size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);

        switch (type_hash.type_hash)
        {
        case MurmurHash128Bits_x86_32:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash128Bits_x86_32\n");
#endif
            MurmurHash3_x32_128(data, size, size, type_hash.hash.hash_128bits);
            /*
            uint32_t value = 0xaabbccdd;
            uint8_t num[4];
            memcpy(num, &value, sizeof(value));
            */
            data = (ui8 *)type_hash.hash.hash_128bits;
            size = sizeof(type_hash.hash.hash_128bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %llx%llx\n", type_hash.hash.hash_128bits[0], type_hash.hash.hash_128bits[1]);
#endif
            break;
        case MurmurHash128Bits_x86_64:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash128Bits_x86_64\n");
#endif
            MurmurHash3_x64_128(data, size, size, type_hash.hash.hash_128bits);
            data = (ui8 *)type_hash.hash.hash_128bits;
            size = sizeof(type_hash.hash.hash_128bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %llx%llx\n", type_hash.hash.hash_128bits[0], type_hash.hash.hash_128bits[1]);
#endif
            break;
        case MurmurHash32Bits:
#ifdef __DEBUG__
            printf("Aplicando MurmurHash32Bits\n");
#endif
            type_hash.hash.hash_32bits = murmur3_32(data, size, size);
            data = (ui8 *)type_hash.hash.hash_32bits;
            size = sizeof(type_hash.hash.hash_32bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %x\n", type_hash.hash.hash_32bits);
#endif
            break;
        case MD5:
            ui8 *a = (ui8 *)&type_hash.hash.hash_32bits;
#ifdef __DEBUG__
            printf("Aplicando md5\n");
#endif

            md5(data, (_uint32_t){size}, a);
            data = a;
            size = sizeof(type_hash.hash.hash_32bits);
            size_ = size + ((SIZE_WORD - (size % SIZE_WORD)) % SIZE_WORD);
#ifdef __DEBUG__
            printf("hash %x\n", type_hash.hash.hash_32bits);
#endif
            break;
        case md4:
#ifdef __DEBUG__
            printf("Aplicando md4\n");
#endif
            break;
        case sha256:
#ifdef __DEBUG__
            printf("Aplicando sha256\n");
#endif
            break;
            /*default MurmurHash128Bits_x86_64:
                break;*/
        }

#ifdef __DEBUG__

        printf("Aplicando create_hash_align, size: %d %d\n", size, size_);
#endif
        for (ui32 i = 0; i < size_; i++)
        {

            if (size < i)
            {
                hash[i % SIZE_WORD] = XOR_operator(hash[i % SIZE_WORD], 0b01010101); // data[i];//
            }
            else
            {
                hash[i % SIZE_WORD] = XOR_operator(hash[i % SIZE_WORD], data[i + 1]); // data[i];//
                hash[i % SIZE_WORD + 1] = XOR_operator(hash[i % SIZE_WORD], data[i]); // data[i];//
            }

#if CPU_BITS_X86 == 64 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui64 " i = %d %d %c\n", bin_ui64(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#elif CPU_BITS_X86 == 32 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui32 " i = %d %d %c\n", bin_ui32(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#elif CPU_BITS_X86 == 16 && __DEBUG__
            printf("HASH bin \t-> " bin_pattern_ui16 " i = %d %d %c\n", bin_ui16(*((size_t *)hash)), i % SIZE_WORD, i, data[i]);
#endif
        }
    }
    return *((size_t *)hash);
}

Esto lo hacemos, por que alinear la memoria mejora el acceso a la memoria, permite que se acceda con mayor velocidad que es algo importante en un allocator.

Para alinear una direccion de memoria o un valor o cualquier otro podemos usar la siguiente formula:
Código: text
#define MEMORY_ALIGN(to_be_aligned) AND_operator((to_be_aligned + sizeof(size_t) - 1), NOT_operator(sizeof(size_t) - 1));
básicamente es hacer una operación NOT y AND con un par de resta y una suma.

Para crear un hash de tamaño de palabra de procesador, se crea un array hash con el tamaño que se define por SIZE_WORD, este valor cambia si se compila para 16bits(-m16), 32bits(-m32) o 64bits(-m64) y se define haciendo uso de la macro CPU_BITS_X86 que se define en una header personal he indica la cantidad de bits del procesador.

Este array se inicializa en 0, usamos la funcion hash especificada por argumento y la guardamos en un segundo array, y aposterior realizamos una serie de operaciones XOR con los valores del hash para obtener un hash de tamaño constante, independientemente del tamaño del hash de entrada.

Tengo que decir, que este codigo no os funcionara por varias razones, algunas de ellas es que obviamente es código de POC, es decir, mayormente son comentarios diciendo que debería hacerse, definiciones de funciones, estructuras y etc. Pero aparte, usa una serie de headers que son propias y no puedo seguir mostrando aqui por que este post daria para un pequeño libro de un par de cuantas paginas, pero son las siguientes como curiosidad:
Código: php
#include "MurmurHash.h"
#include "md4.h"
#include "md5.h"
#include "sha256.h"
#include "string_c.h"
#include "type_data_c.h"
#include "operators_logics.h"
#include "ErrorAllocator.h"
Las primeras 4 son implementaciones propias de los distintos hash's. string_c es una cabecera personal para manejar arrays de forma dinámica en C. operators_logics.h seria una cabecera donde se define operaciones binarias a nivel de Bits como son OR, XOR, NAND, desplazamiento de bits izquierdos y derechos y etc. Ademas de definicion de macros para la impresion de numeros en formato binario(poco optimizado). Una header importante type_data_c.h donde defino entre otras cosas tipos de datos propios multiplataforma y que no cambian de tamaño independientemente del sistema, compilador o tamaño de palabra del procesador, se define macros para identificar la arquitectura, saber si la maquina es little o big endian, definir limites de tipos de datos, definiciones estándares como las de stdbool.h pero propias, definiciones de tipos de datos de windows, por si queremos usar cosas como DWORD en codigos no destinados a windows. definición de tipos de datos tipo NASM, con db, dw, dd, o dq. definición de estructuras de datos de tamaño constante, definición de estructuras anonimas para multi-tipos de datos, definición para la obtención del byte alto y bajo y otras