[dsymutil] Check archive members timestamps.

The debug map contains the timestamp of the object files in references.
We do not check these in the general case, but it's really useful if
you have archives where different versions of an object file have been
appended. This allows llvm-dsymutil to find the right one.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@242965 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Frederic Riss
2015-07-22 23:24:00 +00:00
parent e2b5bcf781
commit 00d14ddf9d
8 changed files with 93 additions and 28 deletions

View File

@@ -0,0 +1,24 @@
# RUN: llvm-dsymutil -no-output -v -oso-prepend-path=%p -y %s 2>&1 | FileCheck %s
# This is the archive member part of basic-archive.macho.x86_64 debug map with corrupted timestamps.
# CHECK: warning: {{.*}}libbasic.a(basic2.macho.x86_64.o): {{[Nn]o}} such file
# CHECK: warning: {{.*}}libbasic.a(basic3.macho.x86_64.o): {{[Nn]o}} such file
---
triple: 'x86_64-unknown-unknown-macho'
objects:
- filename: '/Inputs/libbasic.a(basic2.macho.x86_64.o)'
timestamp: 141869239
symbols:
- { sym: _foo, objAddr: 0x0000000000000020, binAddr: 0x0000000100000ED0, size: 0x00000050 }
- { sym: _private_int, objAddr: 0x0000000000000560, binAddr: 0x0000000100001004, size: 0x00000000 }
- { sym: _inc, objAddr: 0x0000000000000070, binAddr: 0x0000000100000F20, size: 0x00000017 }
- { sym: _baz, objAddr: 0x0000000000000310, binAddr: 0x0000000100001000, size: 0x00000000 }
- filename: '/Inputs/libbasic.a(basic3.macho.x86_64.o)'
timestamp: 418692393
symbols:
- { sym: _val, objAddr: 0x0000000000000004, binAddr: 0x0000000100001008, size: 0x00000000 }
- { sym: _bar, objAddr: 0x0000000000000020, binAddr: 0x0000000100000F40, size: 0x00000050 }
- { sym: _inc, objAddr: 0x0000000000000070, binAddr: 0x0000000100000F90, size: 0x00000019 }
...

View File

@@ -8,9 +8,11 @@
# CHECK-NEXT: triple:{{.*}}'x86_64-unknown-unknown-macho' # CHECK-NEXT: triple:{{.*}}'x86_64-unknown-unknown-macho'
# CHECK-NEXT: objects: # CHECK-NEXT: objects:
# CHECK-NEXT: filename:{{.*}}/Inputs/basic1.macho.x86_64.o # CHECK-NEXT: filename:{{.*}}/Inputs/basic1.macho.x86_64.o
# CHECK-NEXT: timestamp: 0
# CHECK-NEXT: symbols: # CHECK-NEXT: symbols:
# CHECK-NEXT: sym: _main, objAddr: 0x0000000000000000, binAddr: 0x0000000100000EA0, size: 0x00000024 # CHECK-NEXT: sym: _main, objAddr: 0x0000000000000000, binAddr: 0x0000000100000EA0, size: 0x00000024
# CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic2.macho.x86_64.o)' # CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic2.macho.x86_64.o)'
# CHECK-NEXT: timestamp: 0
# CHECK-NEXT: symbols: # CHECK-NEXT: symbols:
# CHECK-DAG: sym: _foo, objAddr: 0x0000000000000020, binAddr: 0x0000000100000ED0, size: 0x00000050 # CHECK-DAG: sym: _foo, objAddr: 0x0000000000000020, binAddr: 0x0000000100000ED0, size: 0x00000050
# CHECK-DAG: sym: _private_int, objAddr: 0x0000000000000560, binAddr: 0x0000000100001004, size: 0x00000000 # CHECK-DAG: sym: _private_int, objAddr: 0x0000000000000560, binAddr: 0x0000000100001004, size: 0x00000000
@@ -18,6 +20,7 @@
# CHECK-DAG: sym: _baz, objAddr: 0x0000000000000310, binAddr: 0x0000000100001000, size: 0x00000000 # CHECK-DAG: sym: _baz, objAddr: 0x0000000000000310, binAddr: 0x0000000100001000, size: 0x00000000
# CHECK-NOT: { sym: # CHECK-NOT: { sym:
# CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic3.macho.x86_64.o)' # CHECK-NEXT: filename:{{.*}}/Inputs/./libbasic.a(basic3.macho.x86_64.o)'
# CHECK-NEXT: timestamp: 0
# CHECK-NEXT: symbols: # CHECK-NEXT: symbols:
# CHECK-DAG: sym: _val, objAddr: 0x0000000000000004, binAddr: 0x0000000100001008, size: 0x00000000 # CHECK-DAG: sym: _val, objAddr: 0x0000000000000004, binAddr: 0x0000000100001008, size: 0x00000000
# CHECK-DAG: sym: _bar, objAddr: 0x0000000000000020, binAddr: 0x0000000100000F40, size: 0x00000050 # CHECK-DAG: sym: _bar, objAddr: 0x0000000000000020, binAddr: 0x0000000100000F40, size: 0x00000050

View File

@@ -19,18 +19,20 @@ namespace llvm {
namespace dsymutil { namespace dsymutil {
ErrorOr<MemoryBufferRef> ErrorOr<MemoryBufferRef>
BinaryHolder::GetMemoryBufferForFile(StringRef Filename) { BinaryHolder::GetMemoryBufferForFile(StringRef Filename,
sys::TimeValue Timestamp) {
if (Verbose) if (Verbose)
outs() << "trying to open '" << Filename << "'\n"; outs() << "trying to open '" << Filename << "'\n";
// Try that first as it doesn't involve any filesystem access. // Try that first as it doesn't involve any filesystem access.
if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename)) if (auto ErrOrArchiveMember = GetArchiveMemberBuffer(Filename, Timestamp))
return *ErrOrArchiveMember; return *ErrOrArchiveMember;
// If the name ends with a closing paren, there is a huge chance // If the name ends with a closing paren, there is a huge chance
// it is an archive member specification. // it is an archive member specification.
if (Filename.endswith(")")) if (Filename.endswith(")"))
if (auto ErrOrArchiveMember = MapArchiveAndGetMemberBuffer(Filename)) if (auto ErrOrArchiveMember =
MapArchiveAndGetMemberBuffer(Filename, Timestamp))
return *ErrOrArchiveMember; return *ErrOrArchiveMember;
// Otherwise, just try opening a standard file. If this is an // Otherwise, just try opening a standard file. If this is an
@@ -50,7 +52,8 @@ BinaryHolder::GetMemoryBufferForFile(StringRef Filename) {
} }
ErrorOr<MemoryBufferRef> ErrorOr<MemoryBufferRef>
BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) { BinaryHolder::GetArchiveMemberBuffer(StringRef Filename,
sys::TimeValue Timestamp) {
if (!CurrentArchive) if (!CurrentArchive)
return make_error_code(errc::no_such_file_or_directory); return make_error_code(errc::no_such_file_or_directory);
@@ -64,6 +67,12 @@ BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
for (const auto &Child : CurrentArchive->children()) { for (const auto &Child : CurrentArchive->children()) {
if (auto NameOrErr = Child.getName()) if (auto NameOrErr = Child.getName())
if (*NameOrErr == Filename) { if (*NameOrErr == Filename) {
if (Timestamp != sys::TimeValue::PosixZeroTime() &&
Timestamp != Child.getLastModified()) {
if (Verbose)
outs() << "\tmember had timestamp mismatch.\n";
continue;
}
if (Verbose) if (Verbose)
outs() << "\tfound member in current archive.\n"; outs() << "\tfound member in current archive.\n";
return Child.getMemoryBufferRef(); return Child.getMemoryBufferRef();
@@ -74,7 +83,8 @@ BinaryHolder::GetArchiveMemberBuffer(StringRef Filename) {
} }
ErrorOr<MemoryBufferRef> ErrorOr<MemoryBufferRef>
BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) { BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename,
sys::TimeValue Timestamp) {
StringRef ArchiveFilename = Filename.substr(0, Filename.find('(')); StringRef ArchiveFilename = Filename.substr(0, Filename.find('('));
auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename); auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename);
@@ -90,12 +100,12 @@ BinaryHolder::MapArchiveAndGetMemberBuffer(StringRef Filename) {
CurrentArchive = std::move(*ErrOrArchive); CurrentArchive = std::move(*ErrOrArchive);
CurrentMemoryBuffer = std::move(*ErrOrBuff); CurrentMemoryBuffer = std::move(*ErrOrBuff);
return GetArchiveMemberBuffer(Filename); return GetArchiveMemberBuffer(Filename, Timestamp);
} }
ErrorOr<const object::ObjectFile &> ErrorOr<const object::ObjectFile &>
BinaryHolder::GetObjectFile(StringRef Filename) { BinaryHolder::GetObjectFile(StringRef Filename, sys::TimeValue Timestamp) {
auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename); auto ErrOrMemBufferRef = GetMemoryBufferForFile(Filename, Timestamp);
if (auto Err = ErrOrMemBufferRef.getError()) if (auto Err = ErrOrMemBufferRef.getError())
return Err; return Err;

View File

@@ -19,6 +19,7 @@
#include "llvm/Object/ObjectFile.h" #include "llvm/Object/ObjectFile.h"
#include "llvm/Support/Errc.h" #include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h" #include "llvm/Support/ErrorOr.h"
#include "llvm/Support/TimeValue.h"
namespace llvm { namespace llvm {
namespace dsymutil { namespace dsymutil {
@@ -46,12 +47,14 @@ class BinaryHolder {
/// This function performs no system calls, it just looks up a /// This function performs no system calls, it just looks up a
/// potential match for the given \p Filename in the currently /// potential match for the given \p Filename in the currently
/// mapped archive if there is one. /// mapped archive if there is one.
ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename); ErrorOr<MemoryBufferRef> GetArchiveMemberBuffer(StringRef Filename,
sys::TimeValue Timestamp);
/// \brief Interpret Filename as an archive member specification, /// \brief Interpret Filename as an archive member specification,
/// map the corresponding archive to memory and return the /// map the corresponding archive to memory and return the
/// MemoryBufferRef corresponding to the described member. /// MemoryBufferRef corresponding to the described member.
ErrorOr<MemoryBufferRef> MapArchiveAndGetMemberBuffer(StringRef Filename); ErrorOr<MemoryBufferRef>
MapArchiveAndGetMemberBuffer(StringRef Filename, sys::TimeValue Timestamp);
/// \brief Return the MemoryBufferRef that holds the memory /// \brief Return the MemoryBufferRef that holds the memory
/// mapping for the given \p Filename. This function will try to /// mapping for the given \p Filename. This function will try to
@@ -61,7 +64,8 @@ class BinaryHolder {
/// The returned MemoryBufferRef points to a buffer owned by this /// The returned MemoryBufferRef points to a buffer owned by this
/// object. The buffer is valid until the next call to /// object. The buffer is valid until the next call to
/// GetMemoryBufferForFile() on this object. /// GetMemoryBufferForFile() on this object.
ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename); ErrorOr<MemoryBufferRef> GetMemoryBufferForFile(StringRef Filename,
sys::TimeValue Timestamp);
public: public:
BinaryHolder(bool Verbose) : Verbose(Verbose) {} BinaryHolder(bool Verbose) : Verbose(Verbose) {}
@@ -72,11 +76,15 @@ public:
/// ///
/// Calling this function invalidates the previous mapping owned by /// Calling this function invalidates the previous mapping owned by
/// the BinaryHolder. /// the BinaryHolder.
ErrorOr<const object::ObjectFile &> GetObjectFile(StringRef Filename); ErrorOr<const object::ObjectFile &>
GetObjectFile(StringRef Filename,
sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime());
/// \brief Wraps GetObjectFile() to return a derived ObjectFile type. /// \brief Wraps GetObjectFile() to return a derived ObjectFile type.
template <typename ObjectFileType> template <typename ObjectFileType>
ErrorOr<const ObjectFileType &> GetFileAs(StringRef Filename) { ErrorOr<const ObjectFileType &>
GetFileAs(StringRef Filename,
sys::TimeValue Timestamp = sys::TimeValue::PosixZeroTime()) {
auto ErrOrObjFile = GetObjectFile(Filename); auto ErrOrObjFile = GetObjectFile(Filename);
if (auto Err = ErrOrObjFile.getError()) if (auto Err = ErrOrObjFile.getError())
return Err; return Err;

View File

@@ -20,8 +20,9 @@ namespace dsymutil {
using namespace llvm::object; using namespace llvm::object;
DebugMapObject::DebugMapObject(StringRef ObjectFilename) DebugMapObject::DebugMapObject(StringRef ObjectFilename,
: Filename(ObjectFilename) {} sys::TimeValue Timestamp)
: Filename(ObjectFilename), Timestamp(Timestamp) {}
bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress, bool DebugMapObject::addSymbol(StringRef Name, uint64_t ObjectAddress,
uint64_t LinkedAddress, uint32_t Size) { uint64_t LinkedAddress, uint32_t Size) {
@@ -58,8 +59,9 @@ void DebugMapObject::print(raw_ostream &OS) const {
void DebugMapObject::dump() const { print(errs()); } void DebugMapObject::dump() const { print(errs()); }
#endif #endif
DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath) { DebugMapObject &DebugMap::addDebugMapObject(StringRef ObjectFilePath,
Objects.emplace_back(new DebugMapObject(ObjectFilePath)); sys::TimeValue Timestamp) {
Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp));
return *Objects.back(); return *Objects.back();
} }
@@ -121,11 +123,12 @@ namespace yaml {
// Normalize/Denormalize between YAML and a DebugMapObject. // Normalize/Denormalize between YAML and a DebugMapObject.
struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO {
YamlDMO(IO &io) {} YamlDMO(IO &io) { Timestamp = 0; }
YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); YamlDMO(IO &io, dsymutil::DebugMapObject &Obj);
dsymutil::DebugMapObject denormalize(IO &IO); dsymutil::DebugMapObject denormalize(IO &IO);
std::string Filename; std::string Filename;
sys::TimeValue::SecondsType Timestamp;
std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries;
}; };
@@ -141,6 +144,7 @@ void MappingTraits<dsymutil::DebugMapObject>::mapping(
IO &io, dsymutil::DebugMapObject &DMO) { IO &io, dsymutil::DebugMapObject &DMO) {
MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO);
io.mapRequired("filename", Norm->Filename); io.mapRequired("filename", Norm->Filename);
io.mapOptional("timestamp", Norm->Timestamp);
io.mapRequired("symbols", Norm->Entries); io.mapRequired("symbols", Norm->Entries);
} }
@@ -192,6 +196,7 @@ void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping(
MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO(
IO &io, dsymutil::DebugMapObject &Obj) { IO &io, dsymutil::DebugMapObject &Obj) {
Filename = Obj.Filename; Filename = Obj.Filename;
Timestamp = Obj.getTimestamp().toEpochTime();
Entries.reserve(Obj.Symbols.size()); Entries.reserve(Obj.Symbols.size());
for (auto &Entry : Obj.Symbols) for (auto &Entry : Obj.Symbols)
Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue())); Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue()));
@@ -224,7 +229,9 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) {
} }
} }
dsymutil::DebugMapObject Res(Path); sys::TimeValue TV;
TV.fromEpochTime(Timestamp);
dsymutil::DebugMapObject Res(Path, TV);
for (auto &Entry : Entries) { for (auto &Entry : Entries) {
auto &Mapping = Entry.second; auto &Mapping = Entry.second;
uint64_t ObjAddress = Mapping.ObjectAddress; uint64_t ObjAddress = Mapping.ObjectAddress;

View File

@@ -29,6 +29,7 @@
#include "llvm/Support/ErrorOr.h" #include "llvm/Support/ErrorOr.h"
#include "llvm/Support/Format.h" #include "llvm/Support/Format.h"
#include "llvm/Support/Path.h" #include "llvm/Support/Path.h"
#include "llvm/Support/TimeValue.h"
#include "llvm/Support/YAMLTraits.h" #include "llvm/Support/YAMLTraits.h"
#include <vector> #include <vector>
@@ -89,7 +90,8 @@ public:
/// This function adds an DebugMapObject to the list owned by this /// This function adds an DebugMapObject to the list owned by this
/// debug map. /// debug map.
DebugMapObject &addDebugMapObject(StringRef ObjectFilePath); DebugMapObject &addDebugMapObject(StringRef ObjectFilePath,
sys::TimeValue Timestamp);
const Triple &getTriple() const { return BinaryTriple; } const Triple &getTriple() const { return BinaryTriple; }
@@ -139,6 +141,8 @@ public:
llvm::StringRef getObjectFilename() const { return Filename; } llvm::StringRef getObjectFilename() const { return Filename; }
sys::TimeValue getTimestamp() const { return Timestamp; }
iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const { iterator_range<StringMap<SymbolMapping>::const_iterator> symbols() const {
return make_range(Symbols.begin(), Symbols.end()); return make_range(Symbols.begin(), Symbols.end());
} }
@@ -150,9 +154,10 @@ public:
private: private:
friend class DebugMap; friend class DebugMap;
/// DebugMapObjects can only be constructed by the owning DebugMap. /// DebugMapObjects can only be constructed by the owning DebugMap.
DebugMapObject(StringRef ObjectFilename); DebugMapObject(StringRef ObjectFilename, sys::TimeValue Timestamp);
std::string Filename; std::string Filename;
sys::TimeValue Timestamp;
StringMap<SymbolMapping> Symbols; StringMap<SymbolMapping> Symbols;
DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; DenseMap<uint64_t, DebugMapEntry *> AddressToMapping;
@@ -167,12 +172,14 @@ private:
public: public:
DebugMapObject &operator=(DebugMapObject RHS) { DebugMapObject &operator=(DebugMapObject RHS) {
std::swap(Filename, RHS.Filename); std::swap(Filename, RHS.Filename);
std::swap(Timestamp, RHS.Timestamp);
std::swap(Symbols, RHS.Symbols); std::swap(Symbols, RHS.Symbols);
std::swap(AddressToMapping, RHS.AddressToMapping); std::swap(AddressToMapping, RHS.AddressToMapping);
return *this; return *this;
} }
DebugMapObject(DebugMapObject &&RHS) { DebugMapObject(DebugMapObject &&RHS) {
Filename = std::move(RHS.Filename); Filename = std::move(RHS.Filename);
Timestamp = std::move(RHS.Timestamp);
Symbols = std::move(RHS.Symbols); Symbols = std::move(RHS.Symbols);
AddressToMapping = std::move(RHS.AddressToMapping); AddressToMapping = std::move(RHS.AddressToMapping);
} }

View File

@@ -3027,7 +3027,8 @@ bool DwarfLinker::link(const DebugMap &Map) {
if (Options.Verbose) if (Options.Verbose)
outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n";
auto ErrOrObj = BinHolder.GetObjectFile(Obj->getObjectFilename()); auto ErrOrObj =
BinHolder.GetObjectFile(Obj->getObjectFilename(), Obj->getTimestamp());
if (std::error_code EC = ErrOrObj.getError()) { if (std::error_code EC = ErrOrObj.getError()) {
reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message()); reportWarning(Twine(Obj->getObjectFilename()) + ": " + EC.message());
continue; continue;

View File

@@ -55,7 +55,7 @@ private:
const char *CurrentFunctionName; const char *CurrentFunctionName;
uint64_t CurrentFunctionAddress; uint64_t CurrentFunctionAddress;
void switchToNewDebugMapObject(StringRef Filename); void switchToNewDebugMapObject(StringRef Filename, sys::TimeValue Timestamp);
void resetParserState(); void resetParserState();
uint64_t getMainBinarySymbolAddress(StringRef Name); uint64_t getMainBinarySymbolAddress(StringRef Name);
void loadMainBinarySymbols(); void loadMainBinarySymbols();
@@ -84,13 +84,15 @@ void MachODebugMapParser::resetParserState() {
/// Create a new DebugMapObject. This function resets the state of the /// Create a new DebugMapObject. This function resets the state of the
/// parser that was referring to the last object file and sets /// parser that was referring to the last object file and sets
/// everything up to add symbols to the new one. /// everything up to add symbols to the new one.
void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) { void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename,
sys::TimeValue Timestamp) {
resetParserState(); resetParserState();
SmallString<80> Path(PathPrefix); SmallString<80> Path(PathPrefix);
sys::path::append(Path, Filename); sys::path::append(Path, Filename);
auto MachOOrError = CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path); auto MachOOrError =
CurrentObjectHolder.GetFileAs<MachOObjectFile>(Path, Timestamp);
if (auto Error = MachOOrError.getError()) { if (auto Error = MachOOrError.getError()) {
Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + Warning(Twine("cannot open debug object \"") + Path.str() + "\": " +
Error.message() + "\n"); Error.message() + "\n");
@@ -98,7 +100,7 @@ void MachODebugMapParser::switchToNewDebugMapObject(StringRef Filename) {
} }
loadCurrentObjectFileSymbols(); loadCurrentObjectFileSymbols();
CurrentDebugMapObject = &Result->addDebugMapObject(Path); CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp);
} }
static Triple getTriple(const object::MachOObjectFile &Obj) { static Triple getTriple(const object::MachOObjectFile &Obj) {
@@ -144,8 +146,11 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex,
const char *Name = &MainBinaryStrings.data()[StringIndex]; const char *Name = &MainBinaryStrings.data()[StringIndex];
// An N_OSO entry represents the start of a new object file description. // An N_OSO entry represents the start of a new object file description.
if (Type == MachO::N_OSO) if (Type == MachO::N_OSO) {
return switchToNewDebugMapObject(Name); sys::TimeValue Timestamp;
Timestamp.fromEpochTime(Value);
return switchToNewDebugMapObject(Name, Timestamp);
}
// If the last N_OSO object file wasn't found, // If the last N_OSO object file wasn't found,
// CurrentDebugMapObject will be null. Do not update anything // CurrentDebugMapObject will be null. Do not update anything