From 23609331472be22be3be134f6facd8f086c5ea49 Mon Sep 17 00:00:00 2001 From: Amy Huang Date: Tue, 21 Apr 2020 14:25:32 -0700 Subject: [PATCH] Reland "Implement some functions in NativeSession." with fixes so that the tests pass on Linux. Summary: This change implements readFromExe, and calculating VA and RVA, which are some of the functionalities that will be used for native PDB reading for llvm symbolizer. bug: https://bugs.llvm.org/show_bug.cgi?id=41795 --- .../llvm/DebugInfo/PDB/Native/NativeSession.h | 9 ++ .../DebugInfo/PDB/Native/NativeSession.cpp | 143 +++++++++++++++++- llvm/lib/DebugInfo/PDB/PDB.cpp | 24 +-- llvm/unittests/DebugInfo/PDB/CMakeLists.txt | 1 + .../DebugInfo/PDB/Inputs/SimpleTest.cpp | 4 + .../DebugInfo/PDB/Inputs/SimpleTest.exe | Bin 0 -> 7168 bytes .../DebugInfo/PDB/Inputs/SimpleTest.pdb | Bin 0 -> 94208 bytes .../DebugInfo/PDB/NativeSessionTest.cpp | 95 ++++++++++++ 8 files changed, 257 insertions(+), 19 deletions(-) create mode 100644 llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.cpp create mode 100644 llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.exe create mode 100644 llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.pdb create mode 100644 llvm/unittests/DebugInfo/PDB/NativeSessionTest.cpp diff --git a/llvm/include/llvm/DebugInfo/PDB/Native/NativeSession.h b/llvm/include/llvm/DebugInfo/PDB/Native/NativeSession.h index ee7d8cdec93b..26b1992a03d7 100644 --- a/llvm/include/llvm/DebugInfo/PDB/Native/NativeSession.h +++ b/llvm/include/llvm/DebugInfo/PDB/Native/NativeSession.h @@ -26,6 +26,11 @@ class PDBFile; class NativeExeSymbol; class NativeSession : public IPDBSession { + struct PdbSearchOptions { + StringRef ExePath; + // FIXME: Add other PDB search options (_NT_SYMBOL_PATH, symsrv) + }; + public: NativeSession(std::unique_ptr PdbFile, std::unique_ptr Allocator); @@ -33,8 +38,11 @@ public: static Error createFromPdb(std::unique_ptr MB, std::unique_ptr &Session); + static Error createFromPdbPath(StringRef PdbPath, + std::unique_ptr &Session); static Error createFromExe(StringRef Path, std::unique_ptr &Session); + static Expected searchForPdb(const PdbSearchOptions &Opts); uint64_t getLoadAddress() const override; bool setLoadAddress(uint64_t Address) override; @@ -109,6 +117,7 @@ private: SymbolCache Cache; SymIndexId ExeSymbol = 0; + uint64_t LoadAddress = 0; }; } // namespace pdb } // namespace llvm diff --git a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp index b45a5881dcb5..9a9254f4302d 100644 --- a/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp +++ b/llvm/lib/DebugInfo/PDB/Native/NativeSession.cpp @@ -12,6 +12,7 @@ #include "llvm/DebugInfo/CodeView/TypeIndex.h" #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" #include "llvm/DebugInfo/PDB/IPDBSourceFile.h" +#include "llvm/DebugInfo/PDB/Native/DbiStream.h" #include "llvm/DebugInfo/PDB/Native/NativeCompilandSymbol.h" #include "llvm/DebugInfo/PDB/Native/NativeEnumInjectedSources.h" #include "llvm/DebugInfo/PDB/Native/NativeEnumTypes.h" @@ -25,11 +26,14 @@ #include "llvm/DebugInfo/PDB/PDBSymbolCompiland.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/Object/COFF.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include #include @@ -75,14 +79,119 @@ Error NativeSession::createFromPdb(std::unique_ptr Buffer, return Error::success(); } -Error NativeSession::createFromExe(StringRef Path, - std::unique_ptr &Session) { - return make_error(raw_error_code::feature_unsupported); +static Expected> +loadPdbFile(StringRef PdbPath, std::unique_ptr &Allocator) { + ErrorOr> ErrorOrBuffer = + MemoryBuffer::getFile(PdbPath, /*FileSize=*/-1, + /*RequiresNullTerminator=*/false); + if (!ErrorOrBuffer) + return make_error(ErrorOrBuffer.getError()); + std::unique_ptr Buffer = std::move(*ErrorOrBuffer); + + PdbPath = Buffer->getBufferIdentifier(); + file_magic Magic; + auto EC = identify_magic(PdbPath, Magic); + if (EC || Magic != file_magic::pdb) + return make_error(EC); + + auto Stream = std::make_unique(std::move(Buffer), + llvm::support::little); + + auto File = std::make_unique(PdbPath, std::move(Stream), *Allocator); + if (auto EC = File->parseFileHeaders()) + return std::move(EC); + + if (auto EC = File->parseStreamData()) + return std::move(EC); + + return std::move(File); } -uint64_t NativeSession::getLoadAddress() const { return 0; } +Error NativeSession::createFromPdbPath(StringRef PdbPath, + std::unique_ptr &Session) { + auto Allocator = std::make_unique(); + auto PdbFile = loadPdbFile(PdbPath, Allocator); + if (!PdbFile) + return PdbFile.takeError(); -bool NativeSession::setLoadAddress(uint64_t Address) { return false; } + Session = std::make_unique(std::move(PdbFile.get()), + std::move(Allocator)); + return Error::success(); +} + +static Expected getPdbPathFromExe(StringRef ExePath) { + Expected> BinaryFile = + object::createBinary(ExePath); + if (!BinaryFile) + return BinaryFile.takeError(); + + const object::COFFObjectFile *ObjFile = + dyn_cast(BinaryFile->getBinary()); + if (!ObjFile) + return make_error(raw_error_code::invalid_format); + + StringRef PdbPath; + const llvm::codeview::DebugInfo *PdbInfo = nullptr; + if (auto EC = ObjFile->getDebugPDBInfo(PdbInfo, PdbPath)) + return make_error(EC); + + return std::string(PdbPath); +} + +Error NativeSession::createFromExe(StringRef ExePath, + std::unique_ptr &Session) { + Expected PdbPath = getPdbPathFromExe(ExePath); + if (!PdbPath) + return PdbPath.takeError(); + + file_magic Magic; + auto EC = identify_magic(PdbPath.get(), Magic); + if (EC || Magic != file_magic::pdb) + return make_error(EC); + + auto Allocator = std::make_unique(); + auto File = loadPdbFile(PdbPath.get(), Allocator); + if (!File) + return File.takeError(); + + Session = std::make_unique(std::move(File.get()), + std::move(Allocator)); + + return Error::success(); +} + +Expected +NativeSession::searchForPdb(const PdbSearchOptions &Opts) { + Expected PathOrErr = getPdbPathFromExe(Opts.ExePath); + if (!PathOrErr) + return PathOrErr.takeError(); + StringRef PathFromExe = PathOrErr.get(); + sys::path::Style Style = PathFromExe.startswith("/") + ? sys::path::Style::posix + : sys::path::Style::windows; + StringRef PdbName = sys::path::filename(PathFromExe, Style); + + // Check if pdb exists in the executable directory. + SmallString<128> PdbPath = StringRef(Opts.ExePath); + sys::path::remove_filename(PdbPath); + sys::path::append(PdbPath, PdbName); + + auto Allocator = std::make_unique(); + + if (auto File = loadPdbFile(PdbPath, Allocator)) + return std::string(PdbPath); + else + return File.takeError(); + + return make_error("PDB not found"); +} + +uint64_t NativeSession::getLoadAddress() const { return LoadAddress; } + +bool NativeSession::setLoadAddress(uint64_t Address) { + LoadAddress = Address; + return true; +} std::unique_ptr NativeSession::getGlobalScope() { return PDBSymbol::createAs(*this, getNativeGlobalScope()); @@ -95,12 +204,30 @@ NativeSession::getSymbolById(SymIndexId SymbolId) const { bool NativeSession::addressForVA(uint64_t VA, uint32_t &Section, uint32_t &Offset) const { - return false; + uint32_t RVA = VA - getLoadAddress(); + return addressForRVA(RVA, Section, Offset); } -bool NativeSession::addressForRVA(uint32_t VA, uint32_t &Section, +bool NativeSession::addressForRVA(uint32_t RVA, uint32_t &Section, uint32_t &Offset) const { - return false; + auto Dbi = Pdb->getPDBDbiStream(); + if (!Dbi) + return false; + + Section = 0; + Offset = 0; + + if ((int32_t)RVA < 0) + return true; + + Offset = RVA; + for (; Section < Dbi->getSectionHeaders().size(); ++Section) { + auto &Sec = Dbi->getSectionHeaders()[Section]; + if (RVA < Sec.VirtualAddress) + return true; + Offset = RVA - Sec.VirtualAddress; + } + return true; } std::unique_ptr diff --git a/llvm/lib/DebugInfo/PDB/PDB.cpp b/llvm/lib/DebugInfo/PDB/PDB.cpp index e7b968cb7bea..a0d015c7839e 100644 --- a/llvm/lib/DebugInfo/PDB/PDB.cpp +++ b/llvm/lib/DebugInfo/PDB/PDB.cpp @@ -23,15 +23,8 @@ using namespace llvm::pdb; Error llvm::pdb::loadDataForPDB(PDB_ReaderType Type, StringRef Path, std::unique_ptr &Session) { // Create the correct concrete instance type based on the value of Type. - if (Type == PDB_ReaderType::Native) { - ErrorOr> ErrorOrBuffer = - MemoryBuffer::getFileOrSTDIN(Path, /*FileSize=*/-1, - /*RequiresNullTerminator=*/false); - if (!ErrorOrBuffer) - return errorCodeToError(ErrorOrBuffer.getError()); - - return NativeSession::createFromPdb(std::move(*ErrorOrBuffer), Session); - } + if (Type == PDB_ReaderType::Native) + return NativeSession::createFromPdbPath(Path, Session); #if LLVM_ENABLE_DIA_SDK return DIASession::createFromPdb(Path, Session); @@ -43,8 +36,17 @@ Error llvm::pdb::loadDataForPDB(PDB_ReaderType Type, StringRef Path, Error llvm::pdb::loadDataForEXE(PDB_ReaderType Type, StringRef Path, std::unique_ptr &Session) { // Create the correct concrete instance type based on the value of Type. - if (Type == PDB_ReaderType::Native) - return NativeSession::createFromExe(Path, Session); + if (Type == PDB_ReaderType::Native) { + if (auto Err = NativeSession::createFromExe(Path, Session)) { + consumeError(std::move(Err)); + + Expected PdbPath = NativeSession::searchForPdb({Path}); + if (!PdbPath) + return PdbPath.takeError(); + return NativeSession::createFromPdbPath(PdbPath.get(), Session); + } + return Error::success(); + } #if LLVM_ENABLE_DIA_SDK return DIASession::createFromExe(Path, Session); diff --git a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt index 4edfd50c148d..c8c2659277a6 100644 --- a/llvm/unittests/DebugInfo/PDB/CMakeLists.txt +++ b/llvm/unittests/DebugInfo/PDB/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_llvm_unittest_with_input_files(DebugInfoPDBTests HashTableTest.cpp + NativeSessionTest.cpp NativeSymbolReuseTest.cpp StringTableBuilderTest.cpp PDBApiTest.cpp diff --git a/llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.cpp b/llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.cpp new file mode 100644 index 000000000000..5cd4030802c6 --- /dev/null +++ b/llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.cpp @@ -0,0 +1,4 @@ +// Compile with "cl /c /Zi SimpleTest.cpp" +// Link with "link SimpleTest.obj /debug /nodefaultlib /entry:main" + +int main() { return 0; } diff --git a/llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.exe b/llvm/unittests/DebugInfo/PDB/Inputs/SimpleTest.exe new file mode 100644 index 0000000000000000000000000000000000000000..35ae9e2682b839daf6555a8d5e71b9888b2230cc GIT binary patch literal 7168 zcmeI1y-UMT5Wvq?#jh3>QE@SfgNvc!BAtT9K~OBVIB2N0X|$0xDQPNBek@Lky68XP zAK;)MPJ*lAc_*#)A3t!-`CL2(!mk#LpzGI&g*KHa{fzVQvjL0Ae=@|l78$R2B9?&?uAZ_G`i7- zc3erZl{&KI2fovO89)r<)I3TMR(AbE-dT*gm-q|Sc@c7d7YU$=sv-d6zgJgXA==ue zjnzH$7*gup>jOPU1b`LM%B1WRzytP9Z*Xe5o%FYnkdbI80ZdeE z*{uNrcO=jB(Kz1S*v+l$uw?>FfC(@GCcp%k025#WOn?b60Vco%YMnq0Og6ZMY6 znnXEXFt{C1>}D?*hN8XcMKAURcF|q*YL`PWURc=0^P zpUF0dL4Sdt5P$##AOHafKmY;|fB*y_009U*B?5o?_k9EiKmY;|fB*y_009U<00Izz z00i1xU^4uEo8KXB4+0Q?00bZa0SG_<0uX=z1Rwx`Q@3?xQpPIqIDaqlLR31uWte~Z z+xwSYqzp(}gnIg2S&<-RAV2^D5P$##AOHafKmY;|fB*y_aN-1xTK^v)#hV5|;W~dQ zXFGInr||}LdTFUCqgFMIVsWz+t5nPDmg#yMM%}Sp*Q&WSW6sLgZ={{;Wn*b>)<`>* zx?7{w2lsGvaK75xc-{K$@6c@)}@q@EtiWmBeTM*`nZ((FmM=mxD-~+I_28UGxn^V2)W_pmrI4qc zMxWxJBW9|xRa=c^vT7{N7)QUEne0MlK4sXBS*#bV@D2z3m~92%Ycx6;Ki!r!@WUaGO|k{Y{0!xSFuB>77UaSU_p z;Ml`)r*Y!`!@vbW00Izz00bZa0SG_<0#B{LhnlW@JoVAL3g1EWn{)47+73;Bb}w^h z^eb~XxGC8uac}tYo#g(cy}Pjv0SG_<0uX=z1Rwwb2tWV=5P-l55|DBJZ<7+0OFCk| zy|JfHy_{bsnxfaE$~B@d=ouQKPaN7^W_I5;FuME$b+O{wCHt1`+)z{HQpGM>RT`j? z=+=GC0*R_%P9hYN7u{YZ4pDpsuO-c;^O<@B6e^p-WZK0RZIv}1U~6<(wP**qxBN+BUGwTl*_z0wQI+bYLqehSK!(>%*j0z1C{PhJjjl)m16 zfBbvTuWon#YkvJX@0i`=`49vMKmY;|fB*y_009U<00Izzz!MXY`ah55 z_v+2()dkzxu&U~LB8}_t-MsIM-7ASS>+%oR;TO_x|5y%*C_jpld9!fTMg)>U`o|h8 zzklF=k?+MD`Y(g?ThO#2#h9f6kJ@(11?zgQUUZ9gUX2yxQ3f?;Id1i*Q(oJGcnr`h zY}(2Hf_4Li$9Ydg6*}!DJkN~@D$zB~wYFsbgkgF$8l>?iw^Mj~MoQdzBKbm+FHZDV zZd{n=k5(<@+?=0pjvFE*jk8gRf<68|Nt@=~>pa{<`7tB%TPhU4c-}B}J{0pZm{&e|X z+kmtSpl#ro;BA(@zd3G1;h7`a?ACBQ5P$##AOHafKmY;|fB*#AN00Izz00bZa0SG|gu?t8Y|7NeQNZr1RhJMpc zpE&X?zzdx4TV7^4Uf~!7dE~#qQK7;wf;7o1-N5p$0F{=m@q&JR?(@5{BsxVSasY>m z9Gk1&w)|+pi}}j=^;Q+*UsiC9eY~mi+y}w7zxOna%Ycakf*iN^9g4BfwMLNlpt5A& ze{%~?A@5?C=1))#Z+cTI$UFP4di&y!?E5|Md-i~R9o*7$#w$pA>;3+EnfIaBOAGC$ z$s7-UT6i}tcs@kVALNx~h$OS({gizz_=G=Ri zwnNjO-OJn={mLAEiPOdwY-c0wTrbm@lPg&@8mm>!u{A4Ku&ViD+1&6({de(^QbO>e zKbbzTm!ph&Kf_Ui%q}3|%XgB&AZ$MYKOq1C2tWV=5P$##AOHafKmY;|c+3KO`~Um6 zbAOGy{Lk*|{~zURkSOB-;?3(BAVsDEAcgaIZwXT|T^K%vt&R z4QUE7mgZ)Sv{R{b;-;yO$2EDne5cW;`1c<()!3@7#xhwomS&8j-^@&QAv2#cY{x9t z3)XQ11j?!KQkgZ^}CnuWE5kpG;09w75|>tM0y2e`g*FENW}Hs#Q2__Il~C zId0m{;jfav0Y}QWQ8FGix!}|<+fFiJ$oFVyTYwz;lWPtFG)t=ggJI_?BTf6X#Tlj0Rad=00Izz00bZa0SG|g*a9Dn zgy|pKTsz^~1_A^i009U<00Izz00bZa0SG_<0_`dA(hSe@9}3gjPeN4w8;|#YB}_Aa zCQ5#w(An=R^t-Q#{;gF#ZjL8Kh05Q z0gxF2x;dWZ_zXvRKOF)DAOHafKmY;|fB*y_009U<00K`&;JGJr8k_|J5P$##AOHaf SKmY;|fB*y_0D=FZz<&Tlz5>z! literal 0 HcmV?d00001 diff --git a/llvm/unittests/DebugInfo/PDB/NativeSessionTest.cpp b/llvm/unittests/DebugInfo/PDB/NativeSessionTest.cpp new file mode 100644 index 000000000000..c0606315c963 --- /dev/null +++ b/llvm/unittests/DebugInfo/PDB/NativeSessionTest.cpp @@ -0,0 +1,95 @@ +//===- llvm/unittest/DebugInfo/PDB/NativeSessionTest.cpp ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/PDB/Native/NativeSession.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/Support/Path.h" + +#include "llvm/Testing/Support/Error.h" + +#include "gtest/gtest.h" + +#include + +using namespace llvm; +using namespace llvm::pdb; + +extern const char *TestMainArgv0; + +static std::string getExePath() { + SmallString<128> InputsDir = unittest::getInputFileDirectory(TestMainArgv0); + llvm::sys::path::append(InputsDir, "SimpleTest.exe"); + return std::string(InputsDir); +} + +TEST(NativeSessionTest, TestCreateFromExe) { + std::unique_ptr S; + // Tests that the PDB file can be found if it is in the same directory as the + // executable. + Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); +} + +TEST(NativeSessionTest, TestSetLoadAddress) { + std::unique_ptr S; + Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); + + S->setLoadAddress(123); + EXPECT_EQ(S->getLoadAddress(), 123U); +} + +TEST(NativeSessionTest, TestAddressForVA) { + std::unique_ptr S; + Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); + + uint64_t LoadAddr = S->getLoadAddress(); + uint32_t Section; + uint32_t Offset; + ASSERT_TRUE(S->addressForVA(LoadAddr + 5000, Section, Offset)); + EXPECT_EQ(1U, Section); + EXPECT_EQ(904U, Offset); + + ASSERT_TRUE(S->addressForVA(-1, Section, Offset)); + EXPECT_EQ(0U, Section); + EXPECT_EQ(0U, Offset); + + ASSERT_TRUE(S->addressForVA(4, Section, Offset)); + EXPECT_EQ(0U, Section); + EXPECT_EQ(4U, Offset); + + ASSERT_TRUE(S->addressForVA(LoadAddr + 100000, Section, Offset)); + EXPECT_EQ(3U, Section); + EXPECT_EQ(83616U, Offset); +} + +TEST(NativeSessionTest, TestAddressForRVA) { + std::unique_ptr S; + Error E = pdb::loadDataForEXE(PDB_ReaderType::Native, getExePath(), S); + ASSERT_THAT_ERROR(std::move(E), Succeeded()); + + uint32_t Section; + uint32_t Offset; + ASSERT_TRUE(S->addressForVA(5000, Section, Offset)); + EXPECT_EQ(1U, Section); + EXPECT_EQ(904U, Offset); + + ASSERT_TRUE(S->addressForVA(-1, Section, Offset)); + EXPECT_EQ(0U, Section); + EXPECT_EQ(0U, Offset); + + ASSERT_TRUE(S->addressForVA(4, Section, Offset)); + EXPECT_EQ(0U, Section); + EXPECT_EQ(4U, Offset); + + ASSERT_TRUE(S->addressForVA(100000, Section, Offset)); + EXPECT_EQ(3U, Section); + EXPECT_EQ(83616U, Offset); +}