software - ¿Qué hace, o hizo, la "función de vacío volátil(...)"?
volatile char in c (3)
Referencias
https://github.com/nmoinvaz/minizip/blob/master/aes/aes_via_ace.h Vea las líneas 323 a 333, y las líneas 399 a 492. Hay muchos otros lugares para encontrar este código, este fue solo el primero Me tropecé
open-std.org/jtc1/sc22/wg14/docs/rr/dr_113.html Gracias @ouah!
http://opencores.org/ocsvn/openrisc/openrisc/trunk/gnu-old/binutils-2.18.50/gas/testsuite/gas/i386/padlock.d Encontramos la búsqueda de "f3 0f a7", y identifica los códigos de operación como operaciones especializadas de encriptación.
La documentación de "información" de GCC.
EVIDENCIA
volatile void function(...)
no se ajusta estrictamente a C99. (Gracias, @Adam y @ouah. @Adam por profundizar en la especificación C99, y @ouah por señalarme el DR que se indica más arriba).GCC agregó
__attribute__((noreturn))
en la versión 2.5 como un reemplazo para elvolatile void
, pero ha continuado aceptando elvolatile void
tan tarde como la versión 4.6.3 para admitir la compatibilidad del código con los compiladores antes de la versión 2.5. (Documentación GCC.)De hecho, el código al que se hace referencia anteriormente devuelve el control al lugar desde donde se llamó, ya que las instrucciones no parecen manipular los registros de direcciones, ni ejecutan un comando de salto. En su lugar, cargan varios valores en los registros de 32 bits. (Examen del código).
Los comandos en las líneas 323 a 333 implementan códigos de operación especiales para el soporte de las operaciones de cifrado. (Examen del código más el código de ''candado'').
El código que usa las funciones de ensamblaje obviamente espera que regresen. (Examen del código).
El atributo
noreturn
le dice al compilador que la función no regresa, por lo que el compilador puede hacer optimizaciones basadas en eso. (Documentación GCC.)De la documentación de GCC: No asuma que los registros guardados por la función de llamada se restauran antes de llamar a la función noreturn.
SOLUCIÓN
Fue una discusión con un compañero de trabajo que finalmente me dio una pista. El compilador debe hacer algo diferente cuando una función declara que no va a volver. El examen de la documentación de GCC confirmó esto.
A. La razón original
Necesitas hacerte la siguiente pregunta.
Pregunta: El código AES carga valores específicamente en los registros de 32 bits y realiza operaciones en ellos. ¿Cómo hace que las respuestas vuelvan al resto del código?
Respuesta: Las optimizaciones de GCC significan que los registros de la función de llamada, que de lo contrario hubieran sobrescrito los valores al regresar, no se guardan. Los resultados de los cálculos en las funciones del lenguaje ensamblador permanecen en los registros para que el código posterior los utilice.
B. Logrando el efecto deseado ahora:
Bastante dejarlo solo. Lo único que podría hacer es reemplazar el tipo de retorno volatile void
con simplemente void
, y agregar el atributo noreturn
a las funciones. Teóricamente, eso debería tener exactamente el mismo efecto. En la práctica, no está roto, no lo arregles.
ATRACTIVO
El uso extensivo de esta técnica es definitivamente desaconsejado. Primero, depende de la personalización para cada compilador. En segundo lugar, depende de que los compiladores no cambien la forma en que manejan el caso de "no retorno". En tercer lugar, es potencialmente confuso para los mantenedores posteriores.
La única situación en la que algo como esto tiene sentido es cuando se está aprovechando de un código de máquina altamente especializado, para lograr una mejora en la velocidad que de otra manera sería imposible. Incluso entonces, debería equilibrarse con las compensaciones.
En este ejemplo, precisamente se admiten dos compiladores, y solo si las máquinas tienen el soporte de hardware específico para aprovechar. De lo contrario, todo se maneja a través del código C estándar. Eso es mucho esfuerzo. Asegúrate de que valga la pena antes de hacerlo.
He visto ¿Cuántos usos tiene la palabra clave "volátil" en la función C ++, desde la perspectiva de la gramática? sobre el uso de la palabra clave volátil en las funciones, pero no hubo una explicación clara de lo que hizo el Caso 1 de esa pregunta. Sólo una declaración de uno de los encuestados que parecía inútil / inútil.
Sin embargo, no puedo aceptar esa afirmación, ya que las implementaciones de software AES para GNUC se han utilizado durante años y tienen una serie de funciones como esta:
INLINE volatile void functionname( /* ... */ ) {
/* ... */
asm( /* ... */ ) // embedded assembly statements
/* ... */
}
Tiene que haber una razón para ese uso. Puede alguien:
A dime cual fue la razón original; y
B. ¿Cómo lograr el efecto deseado ahora?
Estoy usando Ubuntu, y GCC 4.6.3.
Nota: lo más cercano que he encontrado a una explicación es que antes de GCC 2.5, podría falsificar el atributo ''noreturn'' que se implementó en 2.5 a través de lo siguiente:void fatal( /* ... */ ) { /* ... */ exit(1); }
typedef void voidfn ();
volatile voidfn fatal;
Esto permitiría al compilador reconocer que ''fatal'' no volvería.
Pero ese escenario no parece aplicarse al código AES. Ha pasado mucho tiempo desde que hice algo en ensamblaje, pero creo que reconocería un salto o algo así.
El estándar C99 dice esto en §6.7.3 / 3:
Las propiedades asociadas con tipos calificados son significativas solo para expresiones que son valores l. 114)
114) La implementación puede colocar un objeto
const
que no seavolatile
en una región de almacenamiento de solo lectura. Además, la implementación no necesita asignar almacenamiento para dicho objeto si su dirección nunca se usa
§6.2.5 / 19 dice:
El tipo
void
comprende un conjunto de valores vacío; es un tipo incompleto que no se puede completar.
Y §6.3.2.1 / 1 dice:
Un lvalue es una expresión con un tipo de objeto o un tipo incompleto distinto de
void
; 53) [...]
Por lo tanto, void
no es un lvalue, por lo que los calificadores de tipo ( const
, volatile
y restrict
) no son significativos para las expresiones de tipo void
. Por lo tanto, en cualquier compilador compatible con C99, const void
y volatile void
tienen sentido (aunque los punteros a const void
y const volatile
son significativos).
Además, las restricciones de §6.9.1 / 3 no permiten que una función devuelva un tipo calificado de void
:
El tipo de retorno de una función será
void
o un tipo de objeto distinto del tipo de matriz.
Dado que esta es una restricción, un compilador conforme debe emitir un diagnóstico (§5.1.1.3 / 1). Por lo tanto, una función que devuelve un volatile void
no está permitida en C99.
En cuanto a lo que el volatile void
pudo haber hecho, no tengo ni idea y no puedo especular. El código AES que estás viendo probablemente tiene un antiguo crucero que nunca se limpió, supongo.
Según la documentación de gcc (hasta febrero de 2015) , el volatile void
como valor de retorno de la función en C (pero no en C ++) es equivalente a __attribute__((noreturn))
en la función y le dice al compilador que la función nunca regresa.