ORC는 OSX 툴체인에서 C++의 단일 정의 규칙 위반을 찾기 위한 도구입니다.
ORC는 ELF의 변형인 DWARF의 변형입니다. ORC는 약어입니다. O 는 ODR을 나타내는 반면, 아이러니하게도 R 과 C는 여러(아마도 상충되는) 단어를 나타냅니다.
C++ 표준 자체를 포함하여 ODR(One Definition Rule)에 대한 많은 글이 있습니다. 규칙의 요점은 프로그램에서 기호를 정의하면 한 번만 정의할 수 있다는 것입니다. 일부 기호에는 이 규칙에 대한 예외가 부여되며 여러 번 정의할 수 있습니다. 그러나 해당 기호는 동일한 토큰 시퀀스로 정의되어야 합니다.
일부 컴파일러 설정은 토큰 시퀀스에도 영향을 미칠 수 있습니다. 예를 들어 RTTI를 활성화하거나 비활성화하면 기호 정의(이 경우 클래스의 vtable)가 변경될 수 있습니다.
위의 규칙을 위반하는 모든 기호는 ODR 위반(ODRV)입니다. 어떤 경우에는 링커가 중복된 기호 정의를 포착하고 경고나 오류를 내보낼 수 있습니다. 그러나 표준에는 링커가 그렇게 할 필요가 없다고 명시되어 있습니다. Andy G는 이를 잘 설명합니다.
성능상의 이유로 C++ 표준에서는 템플릿과 관련하여 단일 정의 규칙을 위반하면 동작이 정의되지 않을 것이라고 규정합니다. 링커에서는 신경 쓰지 않으므로 이 규칙을 위반해도 아무런 문제가 없습니다. 원천
템플릿이 아닌 ODRV도 가능하며 링커도 이에 대해 똑같이 침묵할 수 있습니다.
ODRV는 일반적으로 이를 빌드한 컴파일 단위에 따라 바이너리 레이아웃이 다른 기호가 있음을 의미합니다. 그러나 규칙 때문에 링커가 여러 정의를 만나면 자유롭게 그 중 하나 를 선택하여 기호의 이진 레이아웃으로 사용할 수 있습니다. 선택한 레이아웃이 컴파일 단위에 있는 기호의 내부 바이너리 레이아웃과 일치하지 않으면 동작이 정의되지 않습니다.
이러한 시나리오에서는 디버거가 쓸모가 없는 경우가 많습니다. 또한 전체 프로그램에 대해 기호의 단일 정의를 사용하게 되며 ODRV를 디버깅하려고 하면 디버거가 잘못된 데이터를 제공하거나 올바르지 않은 파일의 위치를 가리킬 수 있습니다. 결국 디버거는 거짓말을 하는 것처럼 보이지만 근본적인 문제가 무엇인지에 대한 단서를 자동으로 제공하지 않습니다.
모든 버그와 마찬가지로 ODRV도 수정하는 데 시간이 걸립니다. 그렇다면 테스트된(그리고 아마도 작동하는) 코드에서 ODR 위반을 수정해야 하는 이유는 무엇입니까?
ORC는 다음을 수행하는 도구입니다.
도구에 버그가 없으면 ORC는 오탐지를 생성하지 않습니다. 보고되는 모든 것은 ODRV입니다.
현재 ORC는 단일 정의 규칙에 대한 위반 가능성을 모두 감지하지 못합니다. 우리는 시간이 지남에 따라 포착할 수 있는 내용을 확장하고 개선할 수 있기를 바랍니다. 그때까지 이는 ORC가 중요한 검사이기는 하지만 클린 스캔이 프로그램에 ODRV가 없음을 보장하지는 않는다는 것을 의미합니다.
ORC는 다음을 찾을 수 있습니다.
vtable에 대한 참고 사항: ORC는 다른 슬롯에 있는 가상 메서드를 감지합니다. (심각하게 손상된 프로그램입니다.) 이 시점에서는 중복 클래스를 위반하는 ODR의 "상위 집합"인 가상 메서드가 있는 클래스를 감지하지 못합니다.
주요 ORC 소스 외에도 도구가 포착해야 하는 ODRV가 포함된 다양한 예제 애플리케이션을 제공하려고 노력합니다.
ORC는 원래 macOS에서 고안되었습니다. 현재 구현은 여기에 초점을 두고 있지만 해당 툴체인으로 제한될 필요는 없습니다.
ORC는 cmake에 의해 관리되며 CMake 관리 프로젝트의 일반적인 빌드 규칙을 사용하여 구축됩니다.
mkdir build
cd build
cmake -GXcode ..
테스트 목적으로 ORC가 통합된 몇 가지 샘플 애플리케이션이 있습니다. Xcode의 대상 팝업을 통해 선택할 수 있습니다.
ORC는 선택한 프로파일링 도구로 Tracy를 사용하며 기본적으로 활성화되어 있습니다. Tracy를 비활성화하려면 다음과 같이 cmake 명령줄을 지정하십시오.
cmake .. -GXcode -DTRACY_ENABLE=OFF
프로파일링이 비활성화된 경우에도 Tracy 종속성이 필요합니다(런타임 외부에서 컴파일됨). 이 옵션은 캐시되므로 명시적으로 OFF
또는 ON
설정해야 합니다. 옵션이 누락된 상태에서 명령줄 호출을 다시 실행하면 이전 값이 사용됩니다.
ORC는 명령줄에서 직접 호출하거나 링커 단계에서 도구 체인에 삽입할 수 있습니다. 출력은 변경되지 않습니다. 이는 단순히 작업 흐름의 편의성 문제일 뿐입니다.
이 모드는 링커 명령과 해당 인수가 있고 실제 빌드와 별도로 ODRV를 검색하려는 경우 유용합니다.
구성 파일(아래 참조)
'forward_to_linker' = false
'standalone_mode' = false
XCode의 ld
명령줄 인수가 필요합니다. Xcode로 빌드하고(링크할 수 없는 경우 ORC가 도움을 줄 수 없음) 링크 명령을 복사하여 ORC 호출 뒤에 붙여넣습니다. 다음과 같은 것 :
/path/to/orc /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -target ... Debug/lem_mac
(여기서 축약된 거대한 명령줄입니다.)
ORC가 실행되고 ODR 위반이 콘솔에 기록됩니다.
ORC가 처리할 라이브러리 파일 목록이 있는 경우에도 ORC에서 처리할 수 있습니다.
구성 파일(아래 참조)
'forward_to_linker' = false
'standalone_mode' = true
이 모드에서는 처리할 라이브러리 파일 목록을 ORC에 전달하기만 하면 됩니다.
구성 파일(아래 참조)
'forward_to_linker' = true
'standalone_mode' = false
Xcode 빌드 프로젝트 내에서 ORC를 사용하려면 ORC 스크립트에 대한 정규화된 경로로 다음 변수를 재정의하세요.
"LIBTOOL": "/absolute/path/to/orc",
"LDTOOL": "/absolute/path/to/orc",
"ALTERNATE_LINKER": "/absolute/path/to/orc",
이러한 설정이 적용되면 Xcode는 ORC를 프로젝트 빌드의 libtool
및 ld
단계 모두에 대한 도구로 사용해야 합니다. forward_to_linker
설정으로 인해 ORC는 적절한 링크 도구를 호출하여 바이너리 파일을 생성합니다. 완료되면 ORC가 스캔을 시작합니다.
다른 설정 중에서 ODRV가 감지되면 종료하거나 경고만 하도록 ORC를 구성할 수 있습니다.
ORC는 현재 디렉터리를 탐색하여 다음 중 하나라는 이름의 구성 파일을 찾습니다.
.orc-config
또는_orc-config
발견되면 많은 스위치가 ORC의 논리를 제어할 수 있습니다. 예제는 저장소의 _orc_config
를 참조하세요. ORC는 .orc-config
선호하므로 원본 _orc_config
복사하고 .orc-config
에서 로컬로 값을 변경하는 것이 간단합니다.
예를 들어:
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
ODRV 범주로 알려져 있으며 이 오류가 나타내는 위반의 종류를 정확하게 자세히 설명합니다. 그러면 충돌을 일으킨 DWARF 정보와 함께 충돌하는 두 개의 컴파일 단위가 출력됩니다.
struct object { ... }
In ao:
및 In main.o
일치하지 않는 2개의 개체 파일 또는 아카이브가 있습니다. ODR은 이러한 아카이브를 컴파일할 때 일치하지 않는 컴파일 또는 #define 설정으로 인해 발생할 수 있습니다. byte_size
오류를 일으키는 실제 값입니다.
definition location: /Volumes/src/orc/extras/struct0/src/a.cpp:3
객체가 선언된 라인과 파일. 따라서 이 예에서는 a.cpp
의 라인 3입니다.
동일한 버전의 ORC 및 동일한 입력에 대해 ORC는 항상 동일한 출력을 작성합니다. "동일"은 바이트가 동일하고 diff 도구는 차이점을 표시하지 않습니다.
다중 스레드 애플리케이션에서는 일관된 출력을 달성(및 유지)하는 것이 놀라울 정도로 어렵습니다.
그러나 이는 다른 ORC 버전에는 적용되지 않는다는 점을 명심하십시오. ORC를 변경하면 출력이 변경되는 것이 거의 확실합니다.
입력 파일의 "작은" 변경이 ORC 출력의 "작은" 변경을 보장한다는 보장도 없습니다. 이러한 동작은 바람직하며 향후 개선이 필요한 영역이 될 것입니다.
orc_test
) ORC가 포착하려는 내용을 포착하는지 확인하기 위해 단위 테스트 애플리케이션이 제공됩니다. orc_test
알려진 ODR 위반을 생성하기 위해 알려진 소스에서 객체 파일을 생성하는 소형 "빌드 시스템"을 도입합니다. 그런 다음 ORC 명령줄 도구와 동일한 엔진을 사용하여 개체 파일을 처리하고 결과를 예상 ODRV 보고서 목록과 비교합니다.
배터리의 모든 단위 테스트는 개별적이며 다음을 포함합니다.
odrv_test.toml
- 테스트 매개변수를 설명하는 상위 레벨 TOML 파일일반적으로 단일 테스트는 단일 ODR 위반을 유발해야 하지만 모든 경우에 가능하지는 않습니다.
이러한 파일은 표준 C++ 소스 파일입니다. 그 양과 크기는 매우 작아야 합니다. 의도한 ODRV를 유발하는 데 필요한 만큼만 커야 합니다.
odrv_test.toml
파일설정 파일은 컴파일해야 하는 소스, 테스트에 사용해야 하는 컴파일 플래그, 생성된 개체 파일을 링크한 결과 시스템이 감시해야 하는 ODRV를 테스트 애플리케이션에 설명합니다. ) 함께.
테스트 소스는 [[source]]
지시문으로 지정됩니다.
[[ source ]]
path = " one.cpp "
obj = " one "
flags = [
" -Dfoo=1 "
]
path
필드는 odrv_test.toml
기준으로 파일의 경로를 설명합니다. 유일한 필수 필드입니다.
obj
필드는 생성될 (임시) 개체 파일의 이름을 지정합니다. 이 이름을 생략하면 의사 난수 이름이 사용됩니다.
flags
필드는 이 컴파일 단위에 특별히 사용될 컴파일 플래그를 지정합니다. 이 필드를 사용하면 동일한 소스 파일을 다른 컴파일 플래그와 함께 재사용하여 ODRV를 유도할 수 있습니다.
ODRV는 [[odrv]]
지시어로 지정됩니다.
[[ odrv ]]
category = " subprogram:vtable_elem_location "
linkage_name = " _ZNK6object3apiEv "
category
필드는 테스트 앱이 발견해야 하는 특정 유형의 ODR 위반을 설명합니다.
linkage_name
필드는 ODRV를 발생시킨 특정 기호를 설명합니다. 현재는 사용되지 않지만 테스트 앱이 완성되면 시행될 예정입니다.
다음 플래그는 현재 사용되지 않거나 단위 테스트 앱이 계속해서 발전함에 따라 큰 변화를 겪을 것입니다.
[compile_flags]
: 단위 테스트의 모든 소스 파일에 적용되어야 하는 일련의 컴파일 플래그입니다.
[orc_test_flags]
: 이 테스트를 위해 테스트 앱에 전달할 일련의 런타임 설정입니다.
[orc_flags]
: 이 테스트를 위해 ORC 엔진에 전달할 일련의 런타임 설정입니다.