Diferencia entre __asm__ volatile (“jmp 0x0000”) y ((void (*) (void)) 0x0000) ()?

3

¿Hay algo ligeramente diferente entre las dos llamadas? ¿Cuál es la versión "mejor"?

    
pregunta

3 respuestas

3

Uno específicamente ejecuta una instrucción JMP y el otro la trata como una llamada de función.

Dependiendo del chip que estés programando, lo más probable es que use una instrucción diferente a la JMP, y probablemente alterará algunos registros de alguna manera que la JMP no lo haría.

Por ejemplo, en MIPS, usaría una instrucción JAL (Jump And Link), y la dirección de retorno se colocaría en $ ra.

    
respondido por el Majenko
2

No dices qué microcontrolador estás usando, pero me parece mucho como un salto a un vector de reinicio, lo que provoca un reinicio del software en el dispositivo. La primera forma inyecta un código de operación en lenguaje ensamblador que crea un salto (ciego) en esa dirección, lo que provoca que el flujo de control se desvíe inmediatamente al comienzo del espacio de direcciones desde la función en la que está escrito.

La segunda forma crea un puntero a una función nula y llama a esa función (los últimos paréntesis). En lugar de saltar a ciegas a la dirección cero, este mecanismo crea un nuevo marco de pila y, como mínimo, empuja una dirección de retorno a la pila para que el código pueda regresar de cualquier función que esté compilada a la dirección cero o vectorizada desde la dirección cero.

Si la dirección cero es de hecho un vector de reinicio en el microcontrolador objetivo, entonces el desvío del flujo del programa de alguna manera a esa dirección ejecutará las rutinas de inicio, lo que restablece la pila y muy comúnmente también inicializa cualquier variable inicializada en el sistema. Entonces, en ese caso, realmente no hay diferencia si la persona que llama ha creado un marco de pila de devolución o no, el sistema simplemente se reiniciará y nunca regresará a la persona que llama.

En el otro caso, si el microcontrolador no inicia su ejecución desde la dirección cero, entonces esa dirección podría contener cualquier función o vector a una función, que posiblemente podría regresar. En ese caso, es importante escribir la llamada como un puntero a función. Un buen ejemplo de este tipo de sistema sería un microcontrolador que tiene el programa RAM en la dirección cero y el programa ROM en alguna otra dirección como 0x8000. Al reiniciar el hardware, la ejecución del software podría comenzar en la dirección 0x8000 en lugar de 0x0000 y la dirección 0x0000 podría contener, por ejemplo, una captura de llamadas de puntero cero, un viejo truco de programación utilizado para detectar llamadas de función a métodos faltantes o métodos de objetos no inicializados.

El caso de restablecimiento es el caso más común, y la diferencia real (y la razón para usar el estilo de puntero de función) puede ser que el compilador de C no admita la palabra clave asm . Pero cualquier compilador de C podrá compilar el puntero de la función, por lo que ese estilo también es más portátil.

    
respondido por el PkP
0

La primera instrucción realizará un salto a la dirección cero; el segundo podría realizar una llamada a la dirección cero, pero también podría hacer otra cosa. Por ejemplo, una implementación podría definir un método NULL_DEREFERENCE_TRAP() con una implementación predeterminada while(1); , y tener cualquier intento de llamar a un puntero nulo invocar esa función en lugar de saltar a la ubicación cero. Si el procesador tiene un watchdog, el bucle while(1); puede resultar en que el watchdog active y reinicie el sistema, pero el sistema puede bloquearse por un segundo o más.

Además, dependiendo de cómo se escriba el código de inicialización del sistema, sería posible que el código de inicio verifique si ciertas cosas que el hardware forzaría normalmente en un cierto estado en el reinicio están de hecho en ese estado, y utilizar el hecho de que no son una indicación de que el sistema se está "reiniciando por software". Activar un watchdog podría evitar que dichos mecanismos funcionen como se pretende.

Finalmente, se debe tener en cuenta que no es necesario que el formato de almacenamiento para ((void(*)(void))0x1234) tenga ninguna relación con el formato de almacenamiento del número 0x1234 . Si un sistema quería interceptar accesos de puntero nulo, pero aún así proporcionar un medio para crear un puntero a un registro de hardware en la dirección cero, y si ninguna dirección legítima tenía el bit 31 establecido, podría legítimamente (desde el punto de vista del estándar C) digamos que el patrón de bits almacenado para cualquier puntero será la dirección xor'ed con 0x80000000. En un sistema de este tipo, la conversión de 0 a un puntero produciría un puntero no válido (como debería), pero la captura de acceso nulo requeriría que un puntero a la dirección física cero se construya por algún medio que no sea la conversión de un número entero de cero. p>     

respondido por el supercat

Lea otras preguntas en las etiquetas

Comentarios Recientes

v _ad2 _ad3 inv __int 8 (call va, si, int 2 - 1, uri, getq, addr, noop) (k addr, one int, largeInt) Como con __int __ (), así que podría preguntar, ¿por qué abordarlo a 0x0000 en lugar de la dirección sin formato? Bueno, no fue esta característica lo que me molestó, sino que __int __ () toma pruebas "antiguas" de estilo de atributo mmap + y falla. Estas pruebas son largas. Las principales críticas a __int __ () se basan en tipos que podría introducir fácilmente en un entorno de subprocesos dinámicos para... Lees verder