ORC es una herramienta para encontrar violaciones de la regla de definición única de C++ en la cadena de herramientas de OSX.
ORC es una obra de DWARF que es una obra de ELF. ORC es un acrónimo; mientras que la O significa ODR, en un ataque de ironía, la R y la C representan múltiples palabras (posiblemente conflictivas).
Hay muchos artículos sobre la regla de una definición (ODR), incluido el propio estándar C++. La esencia de la regla es que si un símbolo se define en un programa, sólo se permite definirlo una vez. A algunos símbolos se les concede una excepción a esta regla y se les permite definirlos varias veces. Sin embargo, esos símbolos deben estar definidos por secuencias de tokens idénticas .
Tenga en cuenta que algunas configuraciones del compilador también pueden afectar las secuencias de tokens; por ejemplo, la activación o desactivación de RTTI puede alterar la definición de un símbolo (en este caso, la tabla virtual de una clase).
Cualquier símbolo que infrinja la regla anterior es una infracción ODR (ODRV). En algunos casos, el vinculador puede detectar la definición del símbolo duplicado y emitir una advertencia o un error. Sin embargo, el Estándar establece que no se requiere que un enlazador lo haga. Andy G lo describe bien:
Por razones de rendimiento, el estándar C++ dicta que si se viola la regla de una definición con respecto a las plantillas, el comportamiento simplemente no está definido. Dado que al vinculador no le importa, las violaciones de esta regla son silenciosas. fuente
Los ODRV que no son de plantilla son posibles, y el vinculador también puede guardar silencio al respecto.
Un ODRV generalmente significa que tiene un símbolo cuyo diseño binario difiere según la unidad de compilación que lo creó. Sin embargo, debido a la regla, cuando un vinculador encuentra múltiples definiciones, es libre de elegir cualquiera de ellas y usarla como diseño binario para un símbolo. Cuando el diseño elegido no coincide con el diseño binario interno del símbolo en una unidad de compilación, el comportamiento no está definido.
A menudo, el depurador resulta inútil en estos escenarios. También utilizará una definición única de un símbolo para todo el programa, y cuando intente depurar un ODRV, el depurador puede proporcionarle datos incorrectos o señalar una ubicación en un archivo que no parece correcta. Al final, el depurador parecerá estar mintiéndole, pero silenciosamente no ofrecerá pistas sobre cuál es el problema subyacente.
Como todos los errores, los ODRV tardan en corregirse, entonces, ¿por qué debería corregir una infracción de ODR en un código probado (y presumiblemente funcional)?
ORC es una herramienta que realiza lo siguiente:
Salvo que haya un error en la herramienta, ORC no genera falsos positivos. Todo lo que informa es un ODRV.
En este momento, ORC no detecta todas las posibles violaciones de la regla de una definición. Esperamos ampliar y mejorar lo que puede capturar con el tiempo. Hasta entonces, esto significa que si bien ORC es una verificación valiosa, un escaneo limpio no garantiza que un programa esté libre de ODRV.
ORC puede encontrar:
Una nota sobre vtables: ORC detectará métodos virtuales que se encuentren en diferentes ranuras. (Lo cual es un tipo desagradable de programa corrupto). En este punto, no detectará una clase que tenga métodos virtuales que sean un "superconjunto" de una clase duplicada que viola ODR.
Además de las principales fuentes ORC, intentamos proporcionar una serie de aplicaciones de ejemplo que contienen ODRV que la herramienta debería detectar.
ORC se concibió originalmente en macOS. Si bien su implementación actual se centra allí, no tiene por qué limitarse a esa cadena de herramientas.
ORC es administrado por cmake y se construye utilizando las convenciones de compilación típicas de un proyecto administrado por CMake:
mkdir build
cd build
cmake -GXcode ..
Hay un puñado de aplicaciones de muestra en las que se integra ORC con fines de prueba. Estos se pueden seleccionar a través de la ventana emergente de objetivos en Xcode.
ORC utiliza Tracy como su herramienta de creación de perfiles preferida y está habilitada de forma predeterminada. Para deshabilitar Tracy, especifique la línea de comando cmake de esta manera:
cmake .. -GXcode -DTRACY_ENABLE=OFF
La dependencia de Tracy es necesaria incluso si la creación de perfiles está deshabilitada (se compilará fuera del tiempo de ejecución). Tenga en cuenta que esta opción está almacenada en caché, por lo que debe OFF
o ON
explícitamente. Volver a ejecutar la invocación de la línea de comando sin la opción hará que se utilice su valor anterior.
ORC se puede llamar directamente desde la línea de comando o insertarse en la cadena de herramientas en el paso del vinculador. La salida no cambia; es simplemente una cuestión de comodidad en su flujo de trabajo.
Este modo es útil si tiene el comando del vinculador y sus argumentos, y desea buscar ODRV separados de la compilación real.
Archivo de configuración (ver más abajo)
'forward_to_linker' = false
'standalone_mode' = false
Necesita los argumentos de la línea de comando ld
de XCode. Compile con Xcode (si no puede vincular, ORC no puede ayudar), copie el comando de vínculo y péguelo después de la invocación de ORC. Algo como:
/path/to/orc /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -target ... Debug/lem_mac
(Es una línea de comando enorme, abreviada aquí).
ORC se ejecutará y registrará las violaciones de ODR en la consola.
Si tiene una lista de archivos de biblioteca para que ORC los procese, también puede hacerlo.
Archivo de configuración (ver más abajo)
'forward_to_linker' = false
'standalone_mode' = true
En este modo, simplemente pase una lista de archivos de biblioteca a ORC para procesarlos.
Archivo de configuración (ver más abajo)
'forward_to_linker' = true
'standalone_mode' = false
Para usar ORC dentro de su proyecto de compilación de Xcode, anule las siguientes variables con una ruta completa a los scripts ORC:
"LIBTOOL": "/absolute/path/to/orc",
"LDTOOL": "/absolute/path/to/orc",
"ALTERNATE_LINKER": "/absolute/path/to/orc",
Con esas configuraciones implementadas, Xcode debería usar ORC como herramienta para las fases libtool
y ld
de la construcción del proyecto. Debido a la configuración forward_to_linker
, ORC invocará la herramienta de enlace adecuada para producir un archivo binario. Una vez que se complete, ORC comenzará su escaneo.
Entre otras configuraciones, ORC se puede configurar para salir o simplemente advertir cuando se detecta un ODRV.
ORC recorrerá el directorio actual en busca de un archivo de configuración llamado:
.orc-config
, o_orc-config
Si se encuentran, muchos interruptores pueden controlar la lógica de ORC. Consulte _orc_config
en el repositorio para ver ejemplos. ORC preferirá .orc-config
por lo que es sencillo copiar el _orc_config
original y cambiar los valores localmente en .orc-config
.
Por ejemplo:
error: ODRV (structure:byte_size); conflict in `object`
compilation unit: a.o:
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 4 (0x4)
compilation unit: main.o:
definition location: /Volumes/src/orc/extras/struct0/src/main.cpp:3
calling_convention: pass by value; 5 (0x5)
name: object
byte_size: 1 (0x1)
structure:byte_size
se conoce como categoría ODRV y detalla exactamente qué tipo de infracción representa este error. Luego se generan las dos unidades de compilación que están en conflicto, junto con la información DWARF que resultó en la colisión.
struct object { ... }
In ao:
y In main.o
están los 2 archivos objeto o archivos comprimidos que no coinciden. Es probable que la ODR se deba a una configuración de compilación o #define que no coincide al compilar estos archivos. byte_size
es el valor real que causa un error.
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
En qué línea y archivo se declaró el objeto. Entonces, la línea 3 de a.cpp
en este ejemplo.
Para la misma versión de ORC y la misma entrada, ORC siempre escribirá la misma salida. Donde "lo mismo" es un byte idéntico y una herramienta de diferencias no mostrará diferencias.
Lograr (y probablemente mantener) un resultado consistente es sorprendentemente desafiante en una aplicación con muchos subprocesos.
Sin embargo, tenga en cuenta que esto NO se aplica a diferentes versiones de ORC. Es casi seguro que los cambios en ORC darán lugar a cambios en la salida.
Tampoco hay garantía de que un cambio "pequeño" en los archivos de entrada garantice un cambio "pequeño" en la salida ORC. Este comportamiento es deseable y probablemente será un área de mejora futura.
orc_test
) Se proporciona una aplicación de prueba unitaria para garantizar que ORC detecte lo que pretende detectar. orc_test
introduce un "sistema de compilación" en miniatura para generar archivos objeto de fuentes conocidas para producir violaciones ODR conocidas. Luego procesa los archivos objeto usando el mismo motor que la herramienta de línea de comando ORC y compara los resultados con una lista de informes ODRV esperada.
Cada prueba unitaria en la batería es discreta y contiene:
odrv_test.toml
, un archivo TOML de alto nivel que describe los parámetros de la pruebaEn general, una sola prueba debería provocar una única infracción de ODR, pero esto puede no ser posible en todos los casos.
Estos archivos son archivos fuente estándar de C++. Su cantidad y tamaño deben ser muy pequeños, sólo lo suficientemente grandes como para causar el ODRV deseado.
odrv_test.toml
El archivo de configuración describe a la aplicación de prueba qué fuentes deben compilarse, qué indicadores de compilación deben usarse para la prueba y qué ODRV debe tener en cuenta el sistema como resultado de vincular los archivos de objetos generados. ) juntos.
Las fuentes de prueba se especifican con una directiva [[source]]
:
[[ source ]]
path = " one.cpp "
obj = " one "
flags = [
" -Dfoo=1 "
]
El campo path
describe una ruta al archivo relativa a odrv_test.toml
. Es el único campo obligatorio.
El campo obj
especifica el nombre del archivo objeto (temporal) que se creará. Si se omite este nombre, se utilizará un nombre pseudoaleatorio.
El campo flags
especifica los indicadores de compilación que se usarán específicamente para esta unidad de compilación. Al utilizar este campo, es posible reutilizar el mismo archivo fuente con diferentes indicadores de compilación para generar un ODRV.
Los ODRV se especifican con la directiva [[odrv]]
:
[[ odrv ]]
category = " subprogram:vtable_elem_location "
linkage_name = " _ZNK6object3apiEv "
El campo category
describe el tipo específico de infracción ODR que la aplicación de prueba debería esperar encontrar.
El campo linkage_name
describe el símbolo específico que causó el ODRV. Actualmente no se utiliza, pero se aplicará a medida que la aplicación de prueba madure.
Las siguientes banderas no están en uso actualmente o sufrirán cambios importantes a medida que la aplicación de prueba unitaria continúe madurando.
[compile_flags]
: una serie de indicadores de compilación que deben aplicarse a cada archivo fuente en la prueba unitaria.
[orc_test_flags]
: una serie de configuraciones de tiempo de ejecución para pasar a la aplicación de prueba para esta prueba.
[orc_flags]
: una serie de configuraciones de tiempo de ejecución para pasar al motor ORC para esta prueba.