2019-07-04 23:14:51 +08:00
/*
2020-01-12 01:25:26 +08:00
* Copyright ( C ) 2019 - 2020 Intel Corporation
2019-07-04 23:14:51 +08:00
*
* SPDX - License - Identifier : MIT
*
*/
2020-02-24 05:44:01 +08:00
# include "shared/source/helpers/ptr_math.h"
2020-01-12 01:25:26 +08:00
# include "RelocationInfo.h"
2019-07-04 23:14:51 +08:00
# include "gmock/gmock.h"
# include "gtest/gtest.h"
# include "linker_mock.h"
# include <string>
2020-01-12 01:25:26 +08:00
TEST ( SegmentTypeTests , givenSegmentTypeWhenAsStringIsCalledThenProperRepresentationIsReturned ) {
EXPECT_STREQ ( " UNKOWN " , NEO : : asString ( NEO : : SegmentType : : Unknown ) ) ;
EXPECT_STREQ ( " GLOBAL_CONSTANTS " , NEO : : asString ( NEO : : SegmentType : : GlobalConstants ) ) ;
EXPECT_STREQ ( " GLOBAL_VARIABLES " , NEO : : asString ( NEO : : SegmentType : : GlobalVariables ) ) ;
EXPECT_STREQ ( " INSTRUCTIONS " , NEO : : asString ( NEO : : SegmentType : : Instructions ) ) ;
}
TEST ( LinkerInputTraitsTests , whenPointerSizeNotSizeThenDefaultsToHostPointerSize ) {
using PointerSize = NEO : : LinkerInput : : Traits : : PointerSize ;
auto expectedPointerSize = ( sizeof ( void * ) = = 4 ) ? PointerSize : : Ptr32bit : PointerSize : : Ptr64bit ;
auto pointerSize = NEO : : LinkerInput : : Traits { } . pointerSize ;
EXPECT_EQ ( expectedPointerSize , pointerSize ) ;
}
2019-07-04 23:14:51 +08:00
TEST ( LinkerInputTests , givenGlobalsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants ) {
NEO : : LinkerInput linkerInput ;
2020-01-12 01:25:26 +08:00
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
vISA : : GenSymEntry entry [ 2 ] = { { } , { } } ;
entry [ 0 ] . s_name [ 0 ] = ' A ' ;
entry [ 0 ] . s_offset = 8 ;
entry [ 0 ] . s_size = 16 ;
entry [ 0 ] . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
entry [ 1 ] . s_name [ 0 ] = ' B ' ;
entry [ 1 ] . s_offset = 24 ;
entry [ 1 ] . s_size = 8 ;
entry [ 1 ] . s_type = vISA : : GenSymType : : S_GLOBAL_VAR_CONST ;
EXPECT_EQ ( 0U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
auto decodeResult = linkerInput . decodeGlobalVariablesSymbolTable ( entry , 2 ) ;
EXPECT_TRUE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
EXPECT_EQ ( 2U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfInstructionSegments ) ;
auto symbolA = linkerInput . getSymbols ( ) . find ( " A " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolA ) ;
EXPECT_EQ ( entry [ 0 ] . s_offset , symbolA - > second . offset ) ;
EXPECT_EQ ( entry [ 0 ] . s_size , symbolA - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : GlobalVariables , symbolA - > second . segment ) ;
2019-07-04 23:14:51 +08:00
auto symbolB = linkerInput . getSymbols ( ) . find ( " B " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolB ) ;
EXPECT_EQ ( entry [ 1 ] . s_offset , symbolB - > second . offset ) ;
EXPECT_EQ ( entry [ 1 ] . s_size , symbolB - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : GlobalConstants , symbolB - > second . segment ) ;
2019-07-04 23:14:51 +08:00
auto symbolC = linkerInput . getSymbols ( ) . find ( " C " ) ;
EXPECT_EQ ( linkerInput . getSymbols ( ) . end ( ) , symbolC ) ;
}
TEST ( LinkerInputTests , givenFunctionsSymbolTableThenProperlyDecodesGlobalVariablesAndGlobalConstants ) {
// Note : this is subject to change in IGC shotly.
// GLOBAL_VAR/CONST will be ultimately allowed only in globalVariables symbol table.
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry entry [ 2 ] = { { } , { } } ;
entry [ 0 ] . s_name [ 0 ] = ' A ' ;
entry [ 0 ] . s_offset = 8 ;
entry [ 0 ] . s_size = 16 ;
entry [ 0 ] . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
entry [ 1 ] . s_name [ 0 ] = ' B ' ;
entry [ 1 ] . s_offset = 24 ;
entry [ 1 ] . s_size = 8 ;
entry [ 1 ] . s_type = vISA : : GenSymType : : S_GLOBAL_VAR_CONST ;
EXPECT_EQ ( 0U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
auto decodeResult = linkerInput . decodeExportedFunctionsSymbolTable ( entry , 2 , 3 ) ;
EXPECT_TRUE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
EXPECT_EQ ( 2U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfInstructionSegments ) ;
auto symbolA = linkerInput . getSymbols ( ) . find ( " A " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolA ) ;
EXPECT_EQ ( entry [ 0 ] . s_offset , symbolA - > second . offset ) ;
EXPECT_EQ ( entry [ 0 ] . s_size , symbolA - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : GlobalVariables , symbolA - > second . segment ) ;
2019-07-04 23:14:51 +08:00
auto symbolB = linkerInput . getSymbols ( ) . find ( " B " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolB ) ;
EXPECT_EQ ( entry [ 1 ] . s_offset , symbolB - > second . offset ) ;
EXPECT_EQ ( entry [ 1 ] . s_size , symbolB - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : GlobalConstants , symbolB - > second . segment ) ;
2019-07-04 23:14:51 +08:00
auto symbolC = linkerInput . getSymbols ( ) . find ( " C " ) ;
EXPECT_EQ ( linkerInput . getSymbols ( ) . end ( ) , symbolC ) ;
}
TEST ( LinkerInputTests , givenGlobalsSymbolTableThenFunctionExportsAreNotAllowed ) {
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry entry = { } ;
entry . s_name [ 0 ] = ' A ' ;
entry . s_offset = 8 ;
entry . s_size = 16 ;
entry . s_type = vISA : : GenSymType : : S_FUNC ;
auto decodeResult = linkerInput . decodeGlobalVariablesSymbolTable ( & entry , 1 ) ;
EXPECT_FALSE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_FALSE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
}
TEST ( LinkerInputTests , givenFunctionsSymbolTableThenProperlyDecodesExportedFunctions ) {
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry entry [ 2 ] = { { } , { } } ;
entry [ 0 ] . s_name [ 0 ] = ' A ' ;
entry [ 0 ] . s_offset = 8 ;
entry [ 0 ] . s_size = 16 ;
entry [ 0 ] . s_type = vISA : : GenSymType : : S_FUNC ;
entry [ 1 ] . s_name [ 0 ] = ' B ' ;
entry [ 1 ] . s_offset = 24 ;
entry [ 1 ] . s_size = 8 ;
entry [ 1 ] . s_type = vISA : : GenSymType : : S_FUNC ;
EXPECT_EQ ( 0U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
EXPECT_EQ ( - 1 , linkerInput . getExportedFunctionsSegmentId ( ) ) ;
auto decodeResult = linkerInput . decodeExportedFunctionsSymbolTable ( entry , 2 , 3 ) ;
EXPECT_TRUE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
EXPECT_EQ ( 2U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfInstructionSegments ) ;
auto symbolA = linkerInput . getSymbols ( ) . find ( " A " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolA ) ;
EXPECT_EQ ( entry [ 0 ] . s_offset , symbolA - > second . offset ) ;
EXPECT_EQ ( entry [ 0 ] . s_size , symbolA - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : Instructions , symbolA - > second . segment ) ;
2019-07-04 23:14:51 +08:00
auto symbolB = linkerInput . getSymbols ( ) . find ( " B " ) ;
ASSERT_NE ( linkerInput . getSymbols ( ) . end ( ) , symbolB ) ;
EXPECT_EQ ( entry [ 1 ] . s_offset , symbolB - > second . offset ) ;
EXPECT_EQ ( entry [ 1 ] . s_size , symbolB - > second . size ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( NEO : : SegmentType : : Instructions , symbolB - > second . segment ) ;
2019-07-04 23:14:51 +08:00
EXPECT_EQ ( 3 , linkerInput . getExportedFunctionsSegmentId ( ) ) ;
}
TEST ( LinkerInputTests , givenFunctionsSymbolTableThenUndefIsNotAllowed ) {
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry entry = { } ;
entry . s_name [ 0 ] = ' A ' ;
entry . s_offset = 8 ;
entry . s_size = 16 ;
entry . s_type = vISA : : GenSymType : : S_UNDEF ;
auto decodeResult = linkerInput . decodeExportedFunctionsSymbolTable ( & entry , 1 , 3 ) ;
EXPECT_FALSE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_FALSE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
}
TEST ( LinkerInputTests , givenRelocationTableThenRelocationEntriesAreProperlyParsed ) {
NEO : : LinkerInput linkerInput ;
vISA : : GenRelocEntry entry = { } ;
entry . r_symbol [ 0 ] = ' A ' ;
entry . r_offset = 8 ;
entry . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
auto decodeResult = linkerInput . decodeRelocationTable ( & entry , 1 , 3 ) ;
EXPECT_TRUE ( decodeResult ) ;
EXPECT_EQ ( 0U , linkerInput . getSymbols ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalVariables ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsGlobalConstants ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . exportsFunctions ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . requiresPatchingOfInstructionSegments ) ;
decodeResult = linkerInput . decodeRelocationTable ( & entry , 1 , 1 ) ;
EXPECT_TRUE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerInputTests , givenRelocationTableThenNoneAsRelocationTypeIsNotAllowed ) {
2019-07-04 23:14:51 +08:00
NEO : : LinkerInput linkerInput ;
vISA : : GenRelocEntry entry = { } ;
entry . r_symbol [ 0 ] = ' A ' ;
entry . r_offset = 8 ;
entry . r_type = vISA : : GenRelocType : : R_NONE ;
auto decodeResult = linkerInput . decodeRelocationTable ( & entry , 1 , 3 ) ;
EXPECT_FALSE ( decodeResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_FALSE ( linkerInput . isValid ( ) ) ;
}
TEST ( LinkerInputTests , whenDataRelocationsAreAddedThenProperTraitsAreSet ) {
NEO : : LinkerInput linkerInput ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalConstantsBuffer ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalVariablesBuffer ) ;
NEO : : LinkerInput : : RelocationInfo relocInfo ;
relocInfo . offset = 7U ;
relocInfo . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
relocInfo . symbolName = " aaa " ;
relocInfo . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocInfo . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
linkerInput . addDataRelocationInfo ( relocInfo ) ;
ASSERT_EQ ( 1U , linkerInput . getDataRelocations ( ) . size ( ) ) ;
EXPECT_EQ ( relocInfo . offset , linkerInput . getDataRelocations ( ) [ 0 ] . offset ) ;
EXPECT_EQ ( relocInfo . relocationSegment , linkerInput . getDataRelocations ( ) [ 0 ] . relocationSegment ) ;
EXPECT_EQ ( relocInfo . symbolName , linkerInput . getDataRelocations ( ) [ 0 ] . symbolName ) ;
EXPECT_EQ ( relocInfo . symbolSegment , linkerInput . getDataRelocations ( ) [ 0 ] . symbolSegment ) ;
EXPECT_EQ ( relocInfo . type , linkerInput . getDataRelocations ( ) [ 0 ] . type ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalConstantsBuffer ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalVariablesBuffer ) ;
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
linkerInput = { } ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalConstantsBuffer ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalVariablesBuffer ) ;
relocInfo . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocInfo . symbolSegment = NEO : : SegmentType : : GlobalConstants ;
linkerInput . addDataRelocationInfo ( relocInfo ) ;
ASSERT_EQ ( 1U , linkerInput . getDataRelocations ( ) . size ( ) ) ;
EXPECT_FALSE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalConstantsBuffer ) ;
EXPECT_TRUE ( linkerInput . getTraits ( ) . requiresPatchingOfGlobalVariablesBuffer ) ;
EXPECT_TRUE ( linkerInput . isValid ( ) ) ;
2019-07-04 23:14:51 +08:00
}
TEST ( LinkerTests , givenEmptyLinkerInputThenLinkerOutputIsEmpty ) {
NEO : : LinkerInput linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVar , globalConst , exportedFunc ;
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
NEO : : Linker : : PatchableSegments patchableInstructionSegments ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
2020-01-12 01:25:26 +08:00
bool linkResult = linker . link ( globalVar , globalConst , exportedFunc ,
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 0U , relocatedSymbols . size ( ) ) ;
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerTests , givenInvalidLinkerInputThenLinkerFails ) {
WhiteBox < NEO : : LinkerInput > mockLinkerInput ;
mockLinkerInput . valid = false ;
NEO : : Linker linker ( mockLinkerInput ) ;
NEO : : Linker : : SegmentInfo globalVar , globalConst , exportedFunc ;
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
NEO : : Linker : : PatchableSegments patchableInstructionSegments ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
bool linkResult = linker . link ( globalVar , globalConst , exportedFunc ,
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
}
TEST ( LinkerTests , givenUnresolvedExternalWhenPatchingInstructionsThenLinkerFails ) {
2019-07-04 23:14:51 +08:00
NEO : : LinkerInput linkerInput ;
vISA : : GenRelocEntry entry = { } ;
entry . r_symbol [ 0 ] = ' A ' ;
entry . r_offset = 8 ;
entry . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
auto decodeResult = linkerInput . decodeRelocationTable ( & entry , 1 , 0 ) ;
EXPECT_TRUE ( decodeResult ) ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVar , globalConst , exportedFunc ;
2019-07-04 23:14:51 +08:00
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > instructionSegment ;
instructionSegment . resize ( 64 ) ;
NEO : : Linker : : PatchableSegment seg0 ;
seg0 . hostPointer = instructionSegment . data ( ) ;
seg0 . segmentSize = instructionSegment . size ( ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
NEO : : Linker : : PatchableSegments patchableInstructionSegments { seg0 } ;
2020-01-12 01:25:26 +08:00
bool linkResult = linker . link ( globalVar , globalConst , exportedFunc ,
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_FALSE ( linkResult ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 0U , relocatedSymbols . size ( ) ) ;
ASSERT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
EXPECT_EQ ( 0U , unresolvedExternals [ 0 ] . instructionsSegmentId ) ;
EXPECT_FALSE ( unresolvedExternals [ 0 ] . internalError ) ;
EXPECT_EQ ( entry . r_offset , unresolvedExternals [ 0 ] . unresolvedRelocation . offset ) ;
EXPECT_EQ ( std : : string ( entry . r_symbol ) , std : : string ( unresolvedExternals [ 0 ] . unresolvedRelocation . symbolName ) ) ;
}
TEST ( LinkerTests , givenValidSymbolsAndRelocationsThenInstructionSegmentsAreProperlyPatched ) {
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry symGlobalVariable = { } ;
symGlobalVariable . s_name [ 0 ] = ' A ' ;
symGlobalVariable . s_offset = 4 ;
symGlobalVariable . s_size = 16 ;
symGlobalVariable . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
bool decodeSymSuccess = linkerInput . decodeGlobalVariablesSymbolTable ( & symGlobalVariable , 1 ) ;
vISA : : GenSymEntry symGlobalConstant = { } ;
symGlobalConstant . s_name [ 0 ] = ' B ' ;
symGlobalConstant . s_offset = 20 ;
symGlobalConstant . s_size = 8 ;
symGlobalConstant . s_type = vISA : : GenSymType : : S_GLOBAL_VAR_CONST ;
decodeSymSuccess = decodeSymSuccess & & linkerInput . decodeGlobalVariablesSymbolTable ( & symGlobalConstant , 1 ) ;
vISA : : GenSymEntry symExportedFunc = { } ;
symExportedFunc . s_name [ 0 ] = ' C ' ;
symExportedFunc . s_offset = 16 ;
symExportedFunc . s_size = 32 ;
symExportedFunc . s_type = vISA : : GenSymType : : S_FUNC ;
decodeSymSuccess = decodeSymSuccess & & linkerInput . decodeExportedFunctionsSymbolTable ( & symExportedFunc , 1 , 0 ) ;
EXPECT_TRUE ( decodeSymSuccess ) ;
vISA : : GenRelocEntry relocA = { } ;
relocA . r_symbol [ 0 ] = ' A ' ;
relocA . r_offset = 0 ;
relocA . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
vISA : : GenRelocEntry relocB = { } ;
relocB . r_symbol [ 0 ] = ' B ' ;
relocB . r_offset = 8 ;
relocB . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
vISA : : GenRelocEntry relocC = { } ;
relocC . r_symbol [ 0 ] = ' C ' ;
relocC . r_offset = 16 ;
relocC . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
2019-11-26 05:43:21 +08:00
vISA : : GenRelocEntry relocCPartHigh = { } ;
relocCPartHigh . r_symbol [ 0 ] = ' C ' ;
relocCPartHigh . r_offset = 28 ;
relocCPartHigh . r_type = vISA : : GenRelocType : : R_SYM_ADDR_32_HI ;
vISA : : GenRelocEntry relocCPartLow = { } ;
relocCPartLow . r_symbol [ 0 ] = ' C ' ;
relocCPartLow . r_offset = 36 ;
relocCPartLow . r_type = vISA : : GenRelocType : : R_SYM_ADDR_32 ;
vISA : : GenRelocEntry relocs [ ] = { relocA , relocB , relocC , relocCPartHigh , relocCPartLow } ;
bool decodeRelocSuccess = linkerInput . decodeRelocationTable ( & relocs , 5 , 0 ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( decodeRelocSuccess ) ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVarSegment , globalConstSegment , exportedFuncSegment ;
2019-07-04 23:14:51 +08:00
globalVarSegment . gpuAddress = 8 ;
globalVarSegment . segmentSize = 64 ;
globalConstSegment . gpuAddress = 128 ;
globalConstSegment . segmentSize = 256 ;
exportedFuncSegment . gpuAddress = 4096 ;
exportedFuncSegment . segmentSize = 1024 ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > instructionSegment ;
2019-11-26 05:43:21 +08:00
uint32_t initData = 0x77777777 ;
instructionSegment . resize ( 64 , static_cast < char > ( initData ) ) ;
2019-07-04 23:14:51 +08:00
NEO : : Linker : : PatchableSegment seg0 ;
seg0 . hostPointer = instructionSegment . data ( ) ;
seg0 . segmentSize = instructionSegment . size ( ) ;
NEO : : Linker : : PatchableSegments patchableInstructionSegments { seg0 } ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
bool linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments , unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( linkResult ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
EXPECT_EQ ( 3U , relocatedSymbols . size ( ) ) ;
ASSERT_EQ ( 1U , relocatedSymbols . count ( symGlobalVariable . s_name ) ) ;
ASSERT_EQ ( 1U , relocatedSymbols . count ( symGlobalConstant . s_name ) ) ;
ASSERT_EQ ( 1U , relocatedSymbols . count ( symGlobalVariable . s_name ) ) ;
EXPECT_EQ ( relocatedSymbols [ symGlobalVariable . s_name ] . gpuAddress , globalVarSegment . gpuAddress + symGlobalVariable . s_offset ) ;
EXPECT_EQ ( relocatedSymbols [ symGlobalConstant . s_name ] . gpuAddress , globalConstSegment . gpuAddress + symGlobalConstant . s_offset ) ;
EXPECT_EQ ( relocatedSymbols [ symExportedFunc . s_name ] . gpuAddress , exportedFuncSegment . gpuAddress + symExportedFunc . s_offset ) ;
EXPECT_EQ ( relocatedSymbols [ symGlobalVariable . s_name ] . gpuAddress , * reinterpret_cast < const uintptr_t * > ( instructionSegment . data ( ) + relocA . r_offset ) ) ;
EXPECT_EQ ( relocatedSymbols [ symGlobalConstant . s_name ] . gpuAddress , * reinterpret_cast < const uintptr_t * > ( instructionSegment . data ( ) + relocB . r_offset ) ) ;
EXPECT_EQ ( relocatedSymbols [ symExportedFunc . s_name ] . gpuAddress , * reinterpret_cast < const uintptr_t * > ( instructionSegment . data ( ) + relocC . r_offset ) ) ;
2019-11-26 05:43:21 +08:00
auto funcGpuAddressAs64bit = static_cast < uint64_t > ( relocatedSymbols [ symExportedFunc . s_name ] . gpuAddress ) ;
auto funcAddressLow = static_cast < uint32_t > ( funcGpuAddressAs64bit & 0xffffffff ) ;
auto funcAddressHigh = static_cast < uint32_t > ( ( funcGpuAddressAs64bit > > 32 ) & 0xffffffff ) ;
EXPECT_EQ ( funcAddressLow , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartLow . r_offset ) ) ;
EXPECT_EQ ( initData , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartLow . r_offset - sizeof ( uint32_t ) ) ) ;
EXPECT_EQ ( initData , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartLow . r_offset + sizeof ( uint32_t ) ) ) ;
EXPECT_EQ ( funcAddressHigh , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartHigh . r_offset ) ) ;
EXPECT_EQ ( initData , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartHigh . r_offset - sizeof ( uint32_t ) ) ) ;
EXPECT_EQ ( initData , * reinterpret_cast < const uint32_t * > ( instructionSegment . data ( ) + relocCPartHigh . r_offset + sizeof ( uint32_t ) ) ) ;
2019-07-04 23:14:51 +08:00
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerTests , givenInvalidSymbolOffsetWhenPatchingInstructionsThenRelocationFails ) {
2019-07-04 23:14:51 +08:00
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry symGlobalVariable = { } ;
symGlobalVariable . s_name [ 0 ] = ' A ' ;
symGlobalVariable . s_offset = 64 ;
symGlobalVariable . s_size = 16 ;
symGlobalVariable . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
bool decodeSymSuccess = linkerInput . decodeGlobalVariablesSymbolTable ( & symGlobalVariable , 1 ) ;
EXPECT_TRUE ( decodeSymSuccess ) ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVarSegment , globalConstSegment , exportedFuncSegment ;
2019-07-04 23:14:51 +08:00
globalVarSegment . gpuAddress = 8 ;
globalVarSegment . segmentSize = symGlobalVariable . s_offset ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > instructionSegment ;
instructionSegment . resize ( 64 , 0 ) ;
NEO : : Linker : : PatchableSegment seg0 ;
seg0 . hostPointer = instructionSegment . data ( ) ;
seg0 . segmentSize = instructionSegment . size ( ) ;
NEO : : Linker : : PatchableSegments patchableInstructionSegments { seg0 } ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
bool linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_FALSE ( linkResult ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
EXPECT_EQ ( 0U , relocatedSymbols . size ( ) ) ;
globalVarSegment . segmentSize = symGlobalVariable . s_offset + symGlobalVariable . s_size ;
linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments , unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( linkResult ) ;
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerTests , givenInvalidRelocationOffsetThenPatchingOfInstructionsFails ) {
2019-07-04 23:14:51 +08:00
NEO : : LinkerInput linkerInput ;
vISA : : GenSymEntry symGlobalVariable = { } ;
symGlobalVariable . s_name [ 0 ] = ' A ' ;
symGlobalVariable . s_offset = 64 ;
symGlobalVariable . s_size = 16 ;
symGlobalVariable . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
bool decodeSymSuccess = linkerInput . decodeGlobalVariablesSymbolTable ( & symGlobalVariable , 1 ) ;
EXPECT_TRUE ( decodeSymSuccess ) ;
vISA : : GenRelocEntry relocA = { } ;
relocA . r_symbol [ 0 ] = ' A ' ;
relocA . r_offset = 32 ;
relocA . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
bool decodeRelocSuccess = linkerInput . decodeRelocationTable ( & relocA , 1 , 0 ) ;
EXPECT_TRUE ( decodeRelocSuccess ) ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVarSegment , globalConstSegment , exportedFuncSegment ;
2019-07-04 23:14:51 +08:00
globalVarSegment . gpuAddress = 8 ;
globalVarSegment . segmentSize = symGlobalVariable . s_offset + symGlobalVariable . s_size ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > instructionSegment ;
instructionSegment . resize ( relocA . r_offset + sizeof ( uintptr_t ) , 0 ) ;
NEO : : Linker : : PatchableSegment seg0 ;
seg0 . hostPointer = instructionSegment . data ( ) ;
seg0 . segmentSize = relocA . r_offset ;
NEO : : Linker : : PatchableSegments patchableInstructionSegments { seg0 } ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
bool linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_FALSE ( linkResult ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 1U , relocatedSymbols . size ( ) ) ;
ASSERT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
EXPECT_TRUE ( unresolvedExternals [ 0 ] . internalError ) ;
patchableInstructionSegments [ 0 ] . segmentSize = relocA . r_offset + sizeof ( uintptr_t ) ;
linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( linkResult ) ;
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerTests , givenUnknownSymbolTypeWhenPatchingInstructionsThenRelocationFails ) {
2019-07-04 23:14:51 +08:00
WhiteBox < NEO : : LinkerInput > linkerInput ;
vISA : : GenSymEntry symGlobalVariable = { } ;
symGlobalVariable . s_name [ 0 ] = ' A ' ;
symGlobalVariable . s_offset = 0 ;
symGlobalVariable . s_size = 16 ;
symGlobalVariable . s_type = vISA : : GenSymType : : S_GLOBAL_VAR ;
bool decodeSymSuccess = linkerInput . decodeGlobalVariablesSymbolTable ( & symGlobalVariable , 1 ) ;
EXPECT_TRUE ( decodeSymSuccess ) ;
vISA : : GenRelocEntry relocA = { } ;
relocA . r_symbol [ 0 ] = ' A ' ;
relocA . r_offset = 0 ;
relocA . r_type = vISA : : GenRelocType : : R_SYM_ADDR ;
bool decodeRelocSuccess = linkerInput . decodeRelocationTable ( & relocA , 1 , 0 ) ;
EXPECT_TRUE ( decodeRelocSuccess ) ;
NEO : : Linker linker ( linkerInput ) ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : SegmentInfo globalVarSegment , globalConstSegment , exportedFuncSegment ;
2019-07-04 23:14:51 +08:00
globalVarSegment . gpuAddress = 8 ;
globalVarSegment . segmentSize = 64 ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > instructionSegment ;
instructionSegment . resize ( 64 , 0 ) ;
NEO : : Linker : : PatchableSegment seg0 ;
seg0 . hostPointer = instructionSegment . data ( ) ;
seg0 . segmentSize = instructionSegment . size ( ) ;
NEO : : Linker : : PatchableSegments patchableInstructionSegments { seg0 } ;
2020-01-12 01:25:26 +08:00
NEO : : Linker : : PatchableSegment patchableGlobalVarSeg , patchableConstVarSeg ;
2019-07-04 23:14:51 +08:00
ASSERT_EQ ( 1U , linkerInput . symbols . count ( " A " ) ) ;
2020-01-12 01:25:26 +08:00
linkerInput . symbols [ " A " ] . segment = NEO : : SegmentType : : Unknown ;
2019-07-04 23:14:51 +08:00
bool linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_FALSE ( linkResult ) ;
auto relocatedSymbols = linker . extractRelocatedSymbols ( ) ;
EXPECT_EQ ( 0U , relocatedSymbols . size ( ) ) ;
ASSERT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
2020-01-12 01:25:26 +08:00
linkerInput . symbols [ " A " ] . segment = NEO : : SegmentType : : GlobalVariables ;
2019-07-04 23:14:51 +08:00
linkResult = linker . link ( globalVarSegment , globalConstSegment , exportedFuncSegment ,
2020-01-12 01:25:26 +08:00
patchableGlobalVarSeg , patchableConstVarSeg , patchableInstructionSegments ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
}
TEST ( LinkerTests , givenInvalidSourceSegmentWhenPatchingDataSegmentsThenLinkerFails ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
NEO : : Linker : : SegmentInfo emptySegmentInfo ;
NEO : : Linker : : PatchableSegment emptyPatchableSegment ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > nonEmptypatchableSegmentData ;
nonEmptypatchableSegmentData . resize ( 64 , 8U ) ;
NEO : : Linker : : PatchableSegment nonEmptypatchableSegment ;
nonEmptypatchableSegment . hostPointer = nonEmptypatchableSegmentData . data ( ) ;
nonEmptypatchableSegment . segmentSize = nonEmptypatchableSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocInfo ;
relocInfo . offset = 0U ;
relocInfo . symbolName = " aaa " ;
relocInfo . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
linkerInput . dataRelocations . push_back ( relocInfo ) ;
{
linkerInput . traits . requiresPatchingOfGlobalVariablesBuffer = true ;
linkerInput . traits . requiresPatchingOfGlobalConstantsBuffer = false ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : Unknown ;
bool linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
EXPECT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
unresolvedExternals . clear ( ) ;
linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
}
{
linkerInput . traits . requiresPatchingOfGlobalVariablesBuffer = false ;
linkerInput . traits . requiresPatchingOfGlobalConstantsBuffer = true ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : Unknown ;
unresolvedExternals . clear ( ) ;
bool linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
emptyPatchableSegment , nonEmptypatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
EXPECT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
unresolvedExternals . clear ( ) ;
linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
emptyPatchableSegment , nonEmptypatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
}
}
TEST ( LinkerTests , givenUnknownRelocationSegmentWhenPatchingDataSegmentsThenLinkerFails ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
NEO : : Linker : : SegmentInfo emptySegmentInfo ;
NEO : : Linker : : PatchableSegment emptyPatchableSegment ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > nonEmptypatchableSegmentData ;
nonEmptypatchableSegmentData . resize ( 64 , 8U ) ;
NEO : : Linker : : PatchableSegment nonEmptypatchableSegment ;
nonEmptypatchableSegment . hostPointer = nonEmptypatchableSegmentData . data ( ) ;
nonEmptypatchableSegment . segmentSize = nonEmptypatchableSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocInfo ;
relocInfo . offset = 0U ;
relocInfo . symbolName = " aaa " ;
relocInfo . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
linkerInput . dataRelocations . push_back ( relocInfo ) ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : Unknown ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . traits . requiresPatchingOfGlobalVariablesBuffer = true ;
bool linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
EXPECT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
unresolvedExternals . clear ( ) ;
linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
2019-07-04 23:14:51 +08:00
EXPECT_TRUE ( linkResult ) ;
2020-01-12 01:25:26 +08:00
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
2019-07-04 23:14:51 +08:00
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerTests , givenRelocationTypeWithHighPartOfAddressWhenPatchingDataSegmentsThenLinkerFails ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
NEO : : Linker : : SegmentInfo emptySegmentInfo ;
NEO : : Linker : : PatchableSegment emptyPatchableSegment ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > nonEmptypatchableSegmentData ;
nonEmptypatchableSegmentData . resize ( 64 , 8U ) ;
NEO : : Linker : : PatchableSegment nonEmptypatchableSegment ;
nonEmptypatchableSegment . hostPointer = nonEmptypatchableSegmentData . data ( ) ;
nonEmptypatchableSegment . segmentSize = nonEmptypatchableSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocInfo ;
relocInfo . offset = 0U ;
relocInfo . symbolName = " aaa " ;
relocInfo . type = NEO : : LinkerInput : : RelocationInfo : : Type : : AddressHigh ;
linkerInput . dataRelocations . push_back ( relocInfo ) ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . traits . requiresPatchingOfGlobalVariablesBuffer = true ;
bool linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
EXPECT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
linkerInput . dataRelocations [ 0 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : AddressLow ;
unresolvedExternals . clear ( ) ;
linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
}
TEST ( LinkerTests , givenValidSymbolsAndRelocationsThenDataSegmentsAreProperlyPatched ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
std : : vector < char > globalConstantsSegmentData ;
globalConstantsSegmentData . resize ( 128 , 7U ) ;
std : : vector < char > globalVariablesSegmentData ;
globalVariablesSegmentData . resize ( 256 , 13U ) ;
NEO : : Linker : : SegmentInfo globalConstantsSegmentInfo , globalVariablesSegmentInfo ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
NEO : : Linker : : PatchableSegment globalConstantsPatchableSegment , globalVariablesPatchableSegment ;
globalConstantsSegmentInfo . gpuAddress = reinterpret_cast < uintptr_t > ( globalConstantsSegmentData . data ( ) ) ;
globalConstantsSegmentInfo . segmentSize = globalConstantsSegmentData . size ( ) ;
globalConstantsPatchableSegment . hostPointer = globalConstantsSegmentData . data ( ) ;
globalConstantsPatchableSegment . segmentSize = globalConstantsSegmentData . size ( ) ;
globalVariablesSegmentInfo . gpuAddress = reinterpret_cast < uintptr_t > ( globalVariablesSegmentData . data ( ) ) ;
globalVariablesSegmentInfo . segmentSize = globalVariablesSegmentData . size ( ) ;
globalVariablesPatchableSegment . hostPointer = globalVariablesSegmentData . data ( ) ;
globalVariablesPatchableSegment . segmentSize = globalVariablesSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocationInfo [ 5 ] ;
// GlobalVar -> GlobalVar
relocationInfo [ 0 ] . offset = 8U ;
relocationInfo [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 0 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalConst -> GlobalVar
relocationInfo [ 1 ] . offset = 24U ;
relocationInfo [ 1 ] . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 1 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 1 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalConst -> GlobalConst
relocationInfo [ 2 ] . offset = 40U ;
relocationInfo [ 2 ] . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 2 ] . symbolSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 2 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalVar -> GlobalConst
relocationInfo [ 3 ] . offset = 56U ;
relocationInfo [ 3 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 3 ] . symbolSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 3 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalVar Low -> GlobalVar
relocationInfo [ 4 ] . offset = 72 ;
relocationInfo [ 4 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 4 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 4 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : AddressLow ;
uint32_t initValue = 0 ;
for ( const auto & reloc : relocationInfo ) {
linkerInput . addDataRelocationInfo ( reloc ) ;
void * dstRaw = ( reloc . relocationSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
if ( reloc . type = = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ) {
* reinterpret_cast < uintptr_t * > ( ptrOffset ( dstRaw , static_cast < size_t > ( reloc . offset ) ) ) = initValue * 4 ; // relocations to global data are currently based on patchIncrement, simulate init data
} else {
* reinterpret_cast < uint32_t * > ( ptrOffset ( dstRaw , static_cast < size_t > ( reloc . offset ) ) ) = initValue * 4 ; // relocations to global data are currently based on patchIncrement, simulate init data
}
+ + initValue ;
}
auto linkResult = linker . link ( globalVariablesSegmentInfo , globalConstantsSegmentInfo , { } ,
globalVariablesPatchableSegment , globalConstantsPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
EXPECT_EQ ( 7U , * reinterpret_cast < uint8_t * > ( globalConstantsPatchableSegment . hostPointer ) ) ;
EXPECT_EQ ( 13U , * reinterpret_cast < uint8_t * > ( globalVariablesPatchableSegment . hostPointer ) ) ;
initValue = 0 ;
for ( const auto & reloc : relocationInfo ) {
void * srcRaw = ( reloc . symbolSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
void * dstRaw = ( reloc . relocationSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
uint8_t * src = reinterpret_cast < uint8_t * > ( srcRaw ) ;
uint8_t * dst = reinterpret_cast < uint8_t * > ( dstRaw ) ;
// make sure no buffer underflow occured
EXPECT_EQ ( dst [ 0 ] , dst [ reloc . offset - 1 ] ) ;
// check patch-incremented value
if ( reloc . type = = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ) {
// make sure no buffer overflow occured
EXPECT_EQ ( dst [ 0 ] , dst [ reloc . offset + sizeof ( uintptr_t ) ] ) ;
EXPECT_EQ ( reinterpret_cast < uintptr_t > ( src ) + initValue * 4 , * reinterpret_cast < uintptr_t * > ( dst + reloc . offset ) ) < < initValue ;
} else {
// make sure no buffer overflow occured
EXPECT_EQ ( dst [ 0 ] , dst [ reloc . offset + sizeof ( uint32_t ) ] ) ;
EXPECT_EQ ( static_cast < uint32_t > ( reinterpret_cast < uintptr_t > ( src ) ) + initValue * 4 , * reinterpret_cast < uint32_t * > ( dst + reloc . offset ) ) < < initValue ;
}
+ + initValue ;
}
}
TEST ( LinkerTests , givenValidSymbolsAndRelocationsWhenPatchin32bitBinaryThenDataSegmentsAreProperlyPatchedWithLowerPartOfTheAddress ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
linkerInput . setPointerSize ( NEO : : LinkerInput : : Traits : : PointerSize : : Ptr32bit ) ;
NEO : : Linker linker ( linkerInput ) ;
std : : vector < char > globalConstantsSegmentData ;
globalConstantsSegmentData . resize ( 128 , 7U ) ;
std : : vector < char > globalVariablesSegmentData ;
globalVariablesSegmentData . resize ( 256 , 13U ) ;
NEO : : Linker : : SegmentInfo globalConstantsSegmentInfo , globalVariablesSegmentInfo ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
NEO : : Linker : : PatchableSegment globalConstantsPatchableSegment , globalVariablesPatchableSegment ;
globalConstantsSegmentInfo . gpuAddress = reinterpret_cast < uintptr_t > ( globalConstantsSegmentData . data ( ) ) ;
globalConstantsSegmentInfo . segmentSize = globalConstantsSegmentData . size ( ) ;
globalConstantsPatchableSegment . hostPointer = globalConstantsSegmentData . data ( ) ;
globalConstantsPatchableSegment . segmentSize = globalConstantsSegmentData . size ( ) ;
globalVariablesSegmentInfo . gpuAddress = reinterpret_cast < uintptr_t > ( globalVariablesSegmentData . data ( ) ) ;
globalVariablesSegmentInfo . segmentSize = globalVariablesSegmentData . size ( ) ;
globalVariablesPatchableSegment . hostPointer = globalVariablesSegmentData . data ( ) ;
globalVariablesPatchableSegment . segmentSize = globalVariablesSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocationInfo [ 5 ] ;
// GlobalVar -> GlobalVar
relocationInfo [ 0 ] . offset = 8U ;
relocationInfo [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 0 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalConst -> GlobalVar
relocationInfo [ 1 ] . offset = 24U ;
relocationInfo [ 1 ] . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 1 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 1 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalConst -> GlobalConst
relocationInfo [ 2 ] . offset = 40U ;
relocationInfo [ 2 ] . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 2 ] . symbolSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 2 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalVar -> GlobalConst
relocationInfo [ 3 ] . offset = 56U ;
relocationInfo [ 3 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 3 ] . symbolSegment = NEO : : SegmentType : : GlobalConstants ;
relocationInfo [ 3 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
// GlobalVar Low -> GlobalVar
relocationInfo [ 4 ] . offset = 72 ;
relocationInfo [ 4 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 4 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
relocationInfo [ 4 ] . type = NEO : : LinkerInput : : RelocationInfo : : Type : : AddressLow ;
uint32_t initValue = 0 ;
for ( const auto & reloc : relocationInfo ) {
linkerInput . addDataRelocationInfo ( reloc ) ;
void * dstRaw = ( reloc . relocationSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
* reinterpret_cast < uint32_t * > ( ptrOffset ( dstRaw , static_cast < size_t > ( reloc . offset ) ) ) = initValue * 4 ; // relocations to global data are currently based on patchIncrement, simulate init data
+ + initValue ;
}
auto linkResult = linker . link ( globalVariablesSegmentInfo , globalConstantsSegmentInfo , { } ,
globalVariablesPatchableSegment , globalConstantsPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
EXPECT_EQ ( 7U , * reinterpret_cast < uint8_t * > ( globalConstantsPatchableSegment . hostPointer ) ) ;
EXPECT_EQ ( 13U , * reinterpret_cast < uint8_t * > ( globalVariablesPatchableSegment . hostPointer ) ) ;
initValue = 0 ;
for ( const auto & reloc : relocationInfo ) {
void * srcRaw = ( reloc . symbolSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
void * dstRaw = ( reloc . relocationSegment = = NEO : : SegmentType : : GlobalVariables ) ? globalVariablesPatchableSegment . hostPointer : globalConstantsPatchableSegment . hostPointer ;
uint8_t * src = reinterpret_cast < uint8_t * > ( srcRaw ) ;
uint8_t * dst = reinterpret_cast < uint8_t * > ( dstRaw ) ;
// make sure no buffer under/overflow occured
EXPECT_EQ ( dst [ 0 ] , dst [ reloc . offset - 1 ] ) ;
EXPECT_EQ ( dst [ 0 ] , dst [ reloc . offset + sizeof ( uint32_t ) ] ) ;
// check patch-incremented value
EXPECT_EQ ( static_cast < uint32_t > ( reinterpret_cast < uintptr_t > ( src ) ) + initValue * 4 , * reinterpret_cast < uint32_t * > ( dst + reloc . offset ) ) < < initValue ;
+ + initValue ;
}
}
TEST ( LinkerTests , givenInvalidRelocationOffsetThenPatchingOfDataSegmentsFails ) {
WhiteBox < NEO : : LinkerInput > linkerInput ;
NEO : : Linker linker ( linkerInput ) ;
NEO : : Linker : : SegmentInfo emptySegmentInfo ;
NEO : : Linker : : PatchableSegment emptyPatchableSegment ;
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < char > nonEmptypatchableSegmentData ;
nonEmptypatchableSegmentData . resize ( 64 , 8U ) ;
NEO : : Linker : : PatchableSegment nonEmptypatchableSegment ;
nonEmptypatchableSegment . hostPointer = nonEmptypatchableSegmentData . data ( ) ;
nonEmptypatchableSegment . segmentSize = nonEmptypatchableSegmentData . size ( ) ;
NEO : : LinkerInput : : RelocationInfo relocInfo ;
relocInfo . offset = 64U ;
relocInfo . symbolName = " aaa " ;
relocInfo . type = NEO : : LinkerInput : : RelocationInfo : : Type : : Address ;
linkerInput . dataRelocations . push_back ( relocInfo ) ;
linkerInput . dataRelocations [ 0 ] . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . dataRelocations [ 0 ] . symbolSegment = NEO : : SegmentType : : GlobalVariables ;
linkerInput . traits . requiresPatchingOfGlobalVariablesBuffer = true ;
bool linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_FALSE ( linkResult ) ;
EXPECT_EQ ( 1U , unresolvedExternals . size ( ) ) ;
linkerInput . dataRelocations [ 0 ] . offset = 32 ;
unresolvedExternals . clear ( ) ;
linkResult = linker . link ( emptySegmentInfo , emptySegmentInfo , emptySegmentInfo ,
nonEmptypatchableSegment , emptyPatchableSegment , { } ,
unresolvedExternals ) ;
EXPECT_TRUE ( linkResult ) ;
EXPECT_EQ ( 0U , unresolvedExternals . size ( ) ) ;
}
TEST ( LinkerErrorMessageTests , whenListOfUnresolvedExternalsIsEmptyThenErrorTypeDefaultsToInternalError ) {
2019-07-04 23:14:51 +08:00
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < std : : string > segmentsNames { " kernel1 " , " kernel2 " } ;
auto err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , segmentsNames ) ;
EXPECT_EQ ( std : : string ( " Internal linker error " ) , err ) ;
}
2020-01-12 01:25:26 +08:00
TEST ( LinkerErrorMessageTests , givenListOfUnresolvedExternalsThenSymbolNameOrSymbolSegmentTypeGetsEmbededInErrorMessage ) {
2019-07-04 23:14:51 +08:00
NEO : : Linker : : UnresolvedExternals unresolvedExternals ;
std : : vector < std : : string > segmentsNames { " kernel1 " , " kernel2 " } ;
NEO : : Linker : : UnresolvedExternal unresolvedExternal = { } ;
unresolvedExternal . instructionsSegmentId = 1 ;
unresolvedExternal . internalError = false ;
unresolvedExternal . unresolvedRelocation . offset = 64 ;
unresolvedExternal . unresolvedRelocation . symbolName = " arrayABC " ;
2020-01-12 01:25:26 +08:00
unresolvedExternal . unresolvedRelocation . relocationSegment = NEO : : SegmentType : : Instructions ;
2019-07-04 23:14:51 +08:00
unresolvedExternals . push_back ( unresolvedExternal ) ;
auto err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , segmentsNames ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( unresolvedExternal . unresolvedRelocation . symbolName . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( segmentsNames [ unresolvedExternal . instructionsSegmentId ] . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : Not ( : : testing : : HasSubstr ( " internal error " ) ) ) ;
unresolvedExternals [ 0 ] . internalError = true ;
err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , segmentsNames ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( unresolvedExternal . unresolvedRelocation . symbolName . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( segmentsNames [ unresolvedExternal . instructionsSegmentId ] . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( " internal linker error " ) ) ;
err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , { } ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( unresolvedExternal . unresolvedRelocation . symbolName . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : Not ( : : testing : : HasSubstr ( segmentsNames [ unresolvedExternal . instructionsSegmentId ] . c_str ( ) ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( " internal linker error " ) ) ;
2020-01-12 01:25:26 +08:00
unresolvedExternals [ 0 ] . unresolvedRelocation . relocationSegment = NEO : : SegmentType : : GlobalConstants ;
err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , { } ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( NEO : : asString ( NEO : : SegmentType : : GlobalConstants ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
unresolvedExternals [ 0 ] . unresolvedRelocation . relocationSegment = NEO : : SegmentType : : GlobalVariables ;
err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , { } ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( NEO : : asString ( NEO : : SegmentType : : GlobalVariables ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
unresolvedExternals [ 0 ] . unresolvedRelocation . relocationSegment = NEO : : SegmentType : : Unknown ;
err = NEO : : constructLinkerErrorMessage ( unresolvedExternals , { } ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( NEO : : asString ( NEO : : SegmentType : : Unknown ) ) ) ;
EXPECT_THAT ( err . c_str ( ) , : : testing : : HasSubstr ( std : : to_string ( unresolvedExternal . unresolvedRelocation . offset ) . c_str ( ) ) ) ;
2019-07-04 23:14:51 +08:00
}
2020-04-28 20:00:15 +08:00
TEST ( RelocationsDebugMessageTests , givenEmptyListOfRelocatedSymbolsTheReturnsEmptyString ) {
auto message = NEO : : constructRelocationsDebugMessage ( { } ) ;
EXPECT_EQ ( 0U , message . size ( ) ) < < message ;
}
TEST ( RelocationsDebugMessageTests , givenListOfRelocatedSymbolsTheReturnsProperDebugMessage ) {
NEO : : Linker : : RelocatedSymbolsMap symbols ;
auto & funcSymbol = symbols [ " foo " ] ;
auto & constDataSymbol = symbols [ " constInt " ] ;
auto & globalVarSymbol = symbols [ " intX " ] ;
funcSymbol . symbol . segment = NEO : : SegmentType : : Instructions ;
funcSymbol . symbol . offset = 64U ;
funcSymbol . symbol . size = 1024U ;
funcSymbol . gpuAddress = 4096U ;
constDataSymbol . symbol . segment = NEO : : SegmentType : : GlobalConstants ;
constDataSymbol . symbol . offset = 32U ;
constDataSymbol . symbol . size = 16U ;
constDataSymbol . gpuAddress = 8U ;
globalVarSymbol . symbol . segment = NEO : : SegmentType : : GlobalVariables ;
globalVarSymbol . symbol . offset = 72U ;
globalVarSymbol . symbol . size = 8U ;
globalVarSymbol . gpuAddress = 256U ;
auto message = NEO : : constructRelocationsDebugMessage ( symbols ) ;
std : : stringstream expected ;
expected < < " Relocations debug informations : \n " ;
for ( const auto & symbol : symbols ) {
if ( symbol . first = = " foo " ) {
expected < < " * \" foo \" [1024 bytes] INSTRUCTIONS_SEGMENT@64 -> 0x1000 GPUVA \n " ;
} else if ( symbol . first = = " constInt " ) {
expected < < " * \" constInt \" [16 bytes] GLOBAL_CONSTANTS_SEGMENT@32 -> 0x8 GPUVA \n " ;
} else {
expected < < " * \" intX \" [8 bytes] GLOBAL_VARIABLES_SEGMENT@72 -> 0x100 GPUVA \n " ;
}
}
EXPECT_STREQ ( expected . str ( ) . c_str ( ) , message . c_str ( ) ) ;
}