Skip to content

Commit fb2dfa3

Browse files
committed
Relocation: Change strategy
Previously we were searching a set of prefixes for DLLs during relocation. If we found a dll that matched the dll we were looking for (based on file name) we performed relocation based on that path. This is both dangerous and extraneous. This is dangerous as mutli config layouts may have the same binary with the same name in mutliple different paths for different configs or variations. Since we were previously only checking the filename, this could lead to a false positive detection and bad relocation not detected until runtime. This is extraneous as we should never need to search. We have the dll locations before and after relocation, whether from the stage to install prefix or from buildcache to buildcache, so rather than a filesystem search, we can have a linear time operation where we search through a list of relocation old->new prefix mappings. Spack core will set an environment variable of the structure: old_prefix|new_prefix and the compiler wrapper now composes a map out of that list and then PE files looking to relocate their internal dll references get a constant time lookup. Signed-off-by: John Parent <john.parent@kitware.com>
1 parent dfd67a9 commit fb2dfa3

4 files changed

Lines changed: 117 additions & 31 deletions

File tree

Makefile

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,10 @@ install : cl.exe
6969
mklink $(PREFIX)\relocate.exe $(PREFIX)\cl.exe
7070

7171
setup_test: cl.exe
72-
echo "-------------------"
73-
echo "Running Test Setup"
74-
echo "-------------------"
72+
@echo \n
73+
@echo -------------------
74+
@echo Running Test Setup
75+
@echo -------------------
7576
-@ if NOT EXIST "tmp\test" mkdir "tmp\test"
7677
cd tmp\test
7778
copy ..\..\cl.exe cl.exe
@@ -83,9 +84,9 @@ setup_test: cl.exe
8384
# * space in a path - preserved by quoted arguments
8485
# * escaped quoted arguments
8586
build_and_check_test_sample : setup_test
86-
echo "--------------------"
87-
echo "Building Test Sample"
88-
echo "--------------------"
87+
@echo --------------------
88+
@echo Building Test Sample
89+
@echo --------------------
8990
cd tmp\test
9091
cl /c /EHsc "..\..\test\src file\calc.cxx" /DCALC_EXPORTS /DCALC_HEADER="\"calc header/calc.h\"" /I ..\..\test\include
9192
cl /c /EHsc ..\..\test\main.cxx /I ..\..\test\include
@@ -97,9 +98,10 @@ build_and_check_test_sample : setup_test
9798
# Test basic wrapper behavior - did the absolute path to the DLL get injected
9899
# into the executable
99100
test_wrapper : build_and_check_test_sample
100-
echo "--------------------"
101-
echo "Running Wrapper Test"
102-
echo "--------------------"
101+
@echo \n
102+
@echo --------------------
103+
@echo Running Wrapper Test
104+
@echo --------------------
103105
cd tmp
104106
move test\tester.exe .\tester.exe
105107
.\tester.exe
@@ -113,23 +115,25 @@ test_wrapper : build_and_check_test_sample
113115

114116
# Test relocating an executable - re-write internal paths to dlls
115117
test_relocate_exe: build_and_check_test_sample
116-
echo "--------------------------"
117-
echo "Running Relocate Exe Test"
118-
echo "--------------------------"
118+
@echo \n
119+
@echo --------------------------
120+
@echo Running Relocate Exe Test
121+
@echo --------------------------
119122
cd tmp\test
120123
-@ if NOT EXIST "relocate.exe" mklink relocate.exe cl.exe
121124
move calc.dll ..\calc.dll
122-
relocate.exe --pe tester.exe --deploy --full
123-
relocate.exe --pe tester.exe --export --full
125+
SET SPACK_RELOCATE_PATH=$(MAKEDIR)\tmp\test\calc.dll|$(MAKEDIR)\tmp\calc.dll
126+
relocate.exe --pe tester.exe --full
124127
tester.exe
125128
move ..\calc.dll calc.dll
126129
cd ../..
127130

128131
# Test relocating a dll - re-write import library
129132
test_relocate_dll: build_and_check_test_sample
130-
echo "--------------------------"
131-
echo "Running Relocate DLL test"
132-
echo "--------------------------"
133+
@echo \n
134+
@echo --------------------------
135+
@echo Running Relocate DLL test
136+
@echo --------------------------
133137
cd tmp/test
134138
-@ if NOT EXIST "relocate.exe" mklink relocate.exe cl.exe
135139
cd ..
@@ -168,18 +172,20 @@ build_zerowrite_test: test\writezero.obj
168172
link $(LFLAGS) $** $(API_LIBS) /out:writezero.exe
169173

170174
test_zerowrite: build_zerowrite_test
171-
echo "-----------------------"
172-
echo "Running zerowrite test"
173-
echo "-----------------------"
175+
@echo \n
176+
@echo -----------------------
177+
@echo Running zerowrite test
178+
@echo -----------------------
174179
set SPACK_CC_TMP=%SPACK_CC%
175180
set SPACK_CC=$(MAKEDIR)\writezero.exe
176181
cl /c EHsc "test\src file\calc.cxx"
177182
set SPACK_CC=%SPACK_CC_TMP%
178183

179184
test_long_paths: build_and_check_test_sample
180-
echo "------------------------"
181-
echo "Running long paths test"
182-
echo "------------------------"
185+
@echo \n
186+
@echo ------------------------
187+
@echo Running long paths test
188+
@echo ------------------------
183189
mkdir tmp\tmp\verylongdirectoryname\evenlongersubdirectoryname
184190
xcopy /E test\include tmp\tmp\verylongdirectoryname\evenlongersubdirectoryname
185191
xcopy /E "test\src file" tmp\tmp\verylongdirectoryname\evenlongersubdirectoryname
@@ -196,9 +202,10 @@ test_long_paths: build_and_check_test_sample
196202
cd ../../../..
197203

198204
test_relocate_long_paths: test_long_paths
199-
echo "---------------------------------"
200-
echo "Running relocate logn paths test"
201-
echo "---------------------------------"
205+
@echo \n
206+
@echo ---------------------------------
207+
@echo Running relocate logn paths test
208+
@echo ---------------------------------
202209
cd tmp\tmp\verylongdirectoryname\evenlongersubdirectoryname
203210
-@ if NOT EXIST "relocate.exe" mklink relocate.exe cl.exe
204211
cd ..
@@ -214,9 +221,10 @@ test_relocate_long_paths: test_long_paths
214221
cd ../../../..
215222

216223
test_exe_with_exports:
217-
echo ------------------------------
218-
echo Running exe with exports test
219-
echo ------------------------------
224+
@echo \n
225+
@echo ------------------------------
226+
@echo Running exe with exports test
227+
@echo ------------------------------
220228
mkdir tmp\test\exe_with_exports
221229
xcopy /E test\include tmp\test\exe_with_exports
222230
xcopy /E "test\src file" tmp\test\exe_with_exports
@@ -237,6 +245,10 @@ test_exe_with_exports:
237245
cd ../../..
238246

239247
test_def_file_name_override:
248+
@echo
249+
@echo ------------------------------------
250+
@echo Running Def file name override test
251+
@echo ------------------------------------
240252
mkdir tmp\test\def\def_override
241253
xcopy /E test\include tmp\test\def\def_override
242254
xcopy /E "test\src file" tmp\test\def\def_override

src/utils.cxx

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,13 +877,74 @@ std::string LibraryFinder::Finder(const std::string& pth,
877877
return std::string();
878878
}
879879

880+
PathRelocator::PathRelocator() {
881+
this->new_prefix_ = GetSpackEnv("SPACK_INSTALL_PREFIX");
882+
this->parseRelocate();
883+
}
884+
885+
void PathRelocator::parseRelocate() {
886+
const std::string relocations = GetSpackEnv("SPACK_RELOCATE_PATH");
887+
// relocations is a semi colon separated list of
888+
// | separated pairs, of old_prefix|new_prefix
889+
// where old prefix is either the stage or the
890+
// old install root and new prefix is the dll location in the
891+
// install tree or just the new install prefix
892+
if (relocations.empty()) {
893+
return;
894+
}
895+
const StrList mappings = split(relocations, ";");
896+
for (const auto& pair : mappings) {
897+
const StrList old_new = split(pair, "|");
898+
const std::string& old = old_new[0];
899+
const std::string& new_ = old_new[1];
900+
this->old_new_map[old] = new_;
901+
if (endswith(old, ".dll") || endswith(old, ".exe")) {
902+
this->bc_ = false;
903+
}
904+
}
905+
}
906+
907+
std::string PathRelocator::getRelocation(std::string const& pe) {
908+
if (this->bc_) {
909+
return this->relocateBC(pe);
910+
}
911+
return this->relocateStage(pe);
912+
}
913+
914+
std::string PathRelocator::relocateBC(std::string const& pe) {
915+
for (auto& root : this->old_new_map) {
916+
if (startswith(pe, root.first)) {
917+
std::array<wchar_t, MAX_PATH> rel_root;
918+
if (PathRelativePathToW(
919+
&rel_root[0], ConvertASCIIToWide(root.first).c_str(),
920+
FILE_ATTRIBUTE_DIRECTORY, ConvertASCIIToWide(pe).c_str(),
921+
FILE_ATTRIBUTE_NORMAL) != 0) {
922+
// we have the pe's relative root in the old
923+
// prefix, slap the new prefix on it and return
924+
std::string const real_rel(
925+
ConvertWideToASCII(std::wstring(&rel_root[0])));
926+
return join({root.second, real_rel}, "\\");
927+
}
928+
}
929+
}
930+
return std::string();
931+
}
932+
933+
std::string PathRelocator::relocateStage(std::string const& pe) {
934+
try {
935+
std::string prefix_loc = this->old_new_map.at(pe);
936+
return prefix_loc;
937+
} catch (std::out_of_range& e) {
938+
return std::string();
939+
}
940+
}
941+
880942
namespace {
881943
std::vector<std::string> system_locations = {
882944
"api-ms-", "ext-ms-", "ieshims", "emclient", "devicelock",
883945
"wpax", "vcruntime", "WINDOWS", "system32", "KERNEL32",
884946
"WS2_32", "dbghelp", "bcrypt", "ADVAPI32", "SHELL32",
885947
"CRYPT32", "USER32", "ole32", "OLEAUTH32"};
886-
887948
}
888949

889950
bool LibraryFinder::IsSystem(const std::string& pth) {

src/utils.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,20 @@ class LibraryFinder {
293293
void EvalSearchPaths();
294294
};
295295

296+
class PathRelocator {
297+
private:
298+
bool bc_;
299+
std::string new_prefix_;
300+
std::map<std::string, std::string> old_new_map;
301+
std::string relocateBC(std::string const& pe);
302+
std::string relocateStage(std::string const& pe);
303+
void parseRelocate();
304+
305+
public:
306+
PathRelocator();
307+
std::string getRelocation(std::string const& pe);
308+
};
309+
296310
using ScopedLocalInfo = std::unique_ptr<void, LocalFreeDeleter>;
297311

298312
using ScopedSid = std::unique_ptr<void, FreeDeleter>;

test/setup_and_drive_test.bat

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,5 @@ SET SPACK_DEBUG_LOG_ID=TEST
1717
SET SPACK_SHORT_SPEC=test%msvc
1818
SET SPACK_SYSTEM_DIRS=%PATH%
1919
SET SPACK_MANAGED_DIRS=%CD%\tmp
20-
SET SPACK_RELOCATE_PATH=%CD%\tmp
2120

2221
nmake test

0 commit comments

Comments
 (0)