Commit 38256f47 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Let ElfFile open functions return their errors

Summary: [Folly] Let ElfFile open functions return their error messages v.s. taking pointers to where to place them. Overall, making these functions a bit more C++.

Reviewed By: luciang

Differential Revision: D18668222

fbshipit-source-id: 395c20c5d0862c2b1dcc7f6f9db56b7958c5fc90
parent a8f93172
...@@ -52,19 +52,17 @@ ElfFile::ElfFile(const char* name, bool readOnly) ...@@ -52,19 +52,17 @@ ElfFile::ElfFile(const char* name, bool readOnly)
} }
void ElfFile::open(const char* name, bool readOnly) { void ElfFile::open(const char* name, bool readOnly) {
const char* msg = ""; auto r = openNoThrow(name, readOnly);
int r = openNoThrow(name, readOnly, &msg);
if (r == kSystemError) { if (r == kSystemError) {
throwSystemError(msg); throwSystemError(r.msg);
} else { } else {
CHECK_EQ(r, kSuccess) << msg; CHECK_EQ(r, kSuccess) << r.msg;
} }
} }
int ElfFile::openNoThrow( ElfFile::OpenResult ElfFile::openNoThrow(
const char* name, const char* name,
bool readOnly, bool readOnly) noexcept {
const char** msg) noexcept {
FOLLY_SAFE_CHECK(fd_ == -1, "File already open"); FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
// Always close fd and unmap in case of failure along the way to avoid // Always close fd and unmap in case of failure along the way to avoid
// check failure above if we leave fd != -1 and the object is recycled // check failure above if we leave fd != -1 and the object is recycled
...@@ -73,18 +71,12 @@ int ElfFile::openNoThrow( ...@@ -73,18 +71,12 @@ int ElfFile::openNoThrow(
strncat(filepath_, name, kFilepathMaxLen - 1); strncat(filepath_, name, kFilepathMaxLen - 1);
fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR); fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
if (fd_ == -1) { if (fd_ == -1) {
if (msg) { return {kSystemError, "open"};
*msg = "open";
}
return kSystemError;
} }
struct stat st; struct stat st;
int r = fstat(fd_, &st); int r = fstat(fd_, &st);
if (r == -1) { if (r == -1) {
if (msg) { return {kSystemError, "fstat"};
*msg = "fstat";
}
return kSystemError;
} }
length_ = st.st_size; length_ = st.st_size;
...@@ -94,25 +86,22 @@ int ElfFile::openNoThrow( ...@@ -94,25 +86,22 @@ int ElfFile::openNoThrow(
} }
file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0)); file_ = static_cast<char*>(mmap(nullptr, length_, prot, MAP_SHARED, fd_, 0));
if (file_ == MAP_FAILED) { if (file_ == MAP_FAILED) {
if (msg) { return {kSystemError, "mmap"};
*msg = "mmap";
}
return kSystemError;
} }
if (!init(msg)) { auto const initOpenResult = init();
if (initOpenResult != kSuccess) {
reset(); reset();
errno = EINVAL; errno = EINVAL;
return kInvalidElfFile; return initOpenResult;
} }
guard.dismiss(); guard.dismiss();
return kSuccess; return {kSuccess, nullptr};
} }
int ElfFile::openAndFollow( ElfFile::OpenResult ElfFile::openAndFollow(
const char* name, const char* name,
bool readOnly, bool readOnly) noexcept {
const char** msg) noexcept { auto result = openNoThrow(name, readOnly);
auto result = openNoThrow(name, readOnly, msg);
if (!readOnly || result != kSuccess) { if (!readOnly || result != kSuccess) {
return result; return result;
} }
...@@ -145,11 +134,11 @@ int ElfFile::openAndFollow( ...@@ -145,11 +134,11 @@ int ElfFile::openAndFollow(
memcpy(linkname, name, dirlen); memcpy(linkname, name, dirlen);
memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1); memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);
reset(); reset();
result = openNoThrow(linkname, readOnly, msg); result = openNoThrow(linkname, readOnly);
if (result == kSuccess) { if (result == kSuccess) {
return result; return result;
} }
return openNoThrow(name, readOnly, msg); return openNoThrow(name, readOnly);
} }
ElfFile::~ElfFile() { ElfFile::~ElfFile() {
...@@ -204,32 +193,21 @@ void ElfFile::reset() { ...@@ -204,32 +193,21 @@ void ElfFile::reset() {
} }
} }
bool ElfFile::init(const char** msg) { ElfFile::OpenResult ElfFile::init() {
if (length_ < 4) { if (length_ < 4) {
if (msg) { return {kInvalidElfFile, "not an ELF file (too short)"};
*msg = "not an ELF file (too short)";
}
return false;
} }
std::array<char, 5> elfMagBuf = {{0, 0, 0, 0, 0}}; std::array<char, 5> elfMagBuf = {{0, 0, 0, 0, 0}};
if (::lseek(fd_, 0, SEEK_SET) != 0 || ::read(fd_, elfMagBuf.data(), 4) != 4) { if (::lseek(fd_, 0, SEEK_SET) != 0 || ::read(fd_, elfMagBuf.data(), 4) != 4) {
if (msg) { return {kInvalidElfFile, "unable to read ELF file for magic number"};
*msg = "unable to read ELF file for magic number";
}
return false;
} }
if (std::strncmp(elfMagBuf.data(), ELFMAG, sizeof(ELFMAG)) != 0) { if (std::strncmp(elfMagBuf.data(), ELFMAG, sizeof(ELFMAG)) != 0) {
if (msg) { return {kInvalidElfFile, "invalid ELF magic"};
*msg = "invalid ELF magic";
}
return false;
} }
if (::lseek(fd_, 0, SEEK_SET) != 0) { if (::lseek(fd_, 0, SEEK_SET) != 0) {
if (msg) { return {kInvalidElfFile,
*msg = "unable to reset file descriptor after reading ELF magic number"; "unable to reset file descriptor after reading ELF magic number"};
}
return false;
} }
auto& elfHeader = this->elfHeader(); auto& elfHeader = this->elfHeader();
...@@ -239,10 +217,7 @@ bool ElfFile::init(const char** msg) { ...@@ -239,10 +217,7 @@ bool ElfFile::init(const char** msg) {
#define P2(a, b) a##b #define P2(a, b) a##b
// Validate ELF class (32/64 bits) // Validate ELF class (32/64 bits)
if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) { if (elfHeader.e_ident[EI_CLASS] != EXPECTED_CLASS) {
if (msg) { return {kInvalidElfFile, "invalid ELF class"};
*msg = "invalid ELF class";
}
return false;
} }
#undef P1 #undef P1
#undef P2 #undef P2
...@@ -252,47 +227,30 @@ bool ElfFile::init(const char** msg) { ...@@ -252,47 +227,30 @@ bool ElfFile::init(const char** msg) {
static constexpr auto kExpectedEncoding = static constexpr auto kExpectedEncoding =
kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; kIsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB;
if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) { if (elfHeader.e_ident[EI_DATA] != kExpectedEncoding) {
if (msg) { return {kInvalidElfFile, "invalid ELF encoding"};
*msg = "invalid ELF encoding";
}
return false;
} }
// Validate ELF version (1) // Validate ELF version (1)
if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT || if (elfHeader.e_ident[EI_VERSION] != EV_CURRENT ||
elfHeader.e_version != EV_CURRENT) { elfHeader.e_version != EV_CURRENT) {
if (msg) { return {kInvalidElfFile, "invalid ELF version"};
*msg = "invalid ELF version";
}
return false;
} }
// We only support executable and shared object files // We only support executable and shared object files
if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN) { if (elfHeader.e_type != ET_EXEC && elfHeader.e_type != ET_DYN) {
if (msg) { return {kInvalidElfFile, "invalid ELF file type"};
*msg = "invalid ELF file type";
}
return false;
} }
if (elfHeader.e_phnum == 0) { if (elfHeader.e_phnum == 0) {
if (msg) { return {kInvalidElfFile, "no program header!"};
*msg = "no program header!";
}
return false;
} }
if (elfHeader.e_phentsize != sizeof(ElfPhdr)) { if (elfHeader.e_phentsize != sizeof(ElfPhdr)) {
if (msg) { return {kInvalidElfFile, "invalid program header entry size"};
*msg = "invalid program header entry size";
}
return false;
} }
if (elfHeader.e_shentsize != sizeof(ElfShdr)) { if (elfHeader.e_shentsize != sizeof(ElfShdr)) {
if (msg) { return {kInvalidElfFile, "invalid section header entry size"};
*msg = "invalid section header entry size";
}
} }
// Program headers are sorted by load address, so the first PT_LOAD // Program headers are sorted by load address, so the first PT_LOAD
...@@ -301,14 +259,11 @@ bool ElfFile::init(const char** msg) { ...@@ -301,14 +259,11 @@ bool ElfFile::init(const char** msg) {
iterateProgramHeaders([](auto& h) { return h.p_type == PT_LOAD; }); iterateProgramHeaders([](auto& h) { return h.p_type == PT_LOAD; });
if (!programHeader) { if (!programHeader) {
if (msg) { return {kInvalidElfFile, "could not find base address"};
*msg = "could not find base address";
}
return false;
} }
baseAddress_ = programHeader->p_vaddr; baseAddress_ = programHeader->p_vaddr;
return true; return {kSuccess, nullptr};
} }
const ElfShdr* ElfFile::getSectionByIndex(size_t idx) const { const ElfShdr* ElfFile::getSectionByIndex(size_t idx) const {
......
...@@ -61,22 +61,24 @@ class ElfFile { ...@@ -61,22 +61,24 @@ class ElfFile {
// on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid // on IO error, kInvalidElfFile (and sets errno to EINVAL) for an invalid
// Elf file. On error, if msg is not nullptr, sets *msg to a static string // Elf file. On error, if msg is not nullptr, sets *msg to a static string
// indicating what failed. // indicating what failed.
enum { enum OpenResultCode : int {
kSuccess = 0, kSuccess = 0,
kSystemError = -1, kSystemError = -1,
kInvalidElfFile = -2, kInvalidElfFile = -2,
}; };
struct OpenResult {
OpenResultCode code{};
char const* msg{};
/* implicit */ constexpr operator OpenResultCode() const noexcept {
return code;
}
};
// Open the ELF file. Does not throw on error. // Open the ELF file. Does not throw on error.
int openNoThrow( OpenResult openNoThrow(const char* name, bool readOnly = true) noexcept;
const char* name,
bool readOnly = true,
const char** msg = nullptr) noexcept;
// Like openNoThrow, but follow .gnu_debuglink if present // Like openNoThrow, but follow .gnu_debuglink if present
int openAndFollow( OpenResult openAndFollow(const char* name, bool readOnly = true) noexcept;
const char* name,
bool readOnly = true,
const char** msg = nullptr) noexcept;
// Open the ELF file. Throws on error. // Open the ELF file. Throws on error.
void open(const char* name, bool readOnly = true); void open(const char* name, bool readOnly = true);
...@@ -231,7 +233,7 @@ class ElfFile { ...@@ -231,7 +233,7 @@ class ElfFile {
const ElfShdr* getSectionContainingAddress(ElfAddr addr) const; const ElfShdr* getSectionContainingAddress(ElfAddr addr) const;
private: private:
bool init(const char** msg); OpenResult init();
void reset(); void reset();
ElfFile(const ElfFile&) = delete; ElfFile(const ElfFile&) = delete;
ElfFile& operator=(const ElfFile&) = delete; ElfFile& operator=(const ElfFile&) = delete;
......
...@@ -79,8 +79,7 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) { ...@@ -79,8 +79,7 @@ std::shared_ptr<ElfFile> SignalSafeElfCache::getFile(StringPiece p) {
auto& f = slots_[n]; auto& f = slots_[n];
const char* msg = ""; int r = f->openAndFollow(scratchpad_.data(), true);
int r = f->openAndFollow(scratchpad_.data(), true, &msg);
if (r != ElfFile::kSuccess) { if (r != ElfFile::kSuccess) {
return nullptr; return nullptr;
} }
...@@ -108,8 +107,7 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) { ...@@ -108,8 +107,7 @@ std::shared_ptr<ElfFile> ElfCache::getFile(StringPiece p) {
auto& path = entry->path; auto& path = entry->path;
// No negative caching // No negative caching
const char* msg = ""; int r = entry->file.openAndFollow(path.c_str(), true);
int r = entry->file.openAndFollow(path.c_str(), true, &msg);
if (r != ElfFile::kSuccess) { if (r != ElfFile::kSuccess) {
return nullptr; return nullptr;
} }
......
...@@ -58,10 +58,9 @@ TEST_F(ElfTest, TinyNonElfFile) { ...@@ -58,10 +58,9 @@ TEST_F(ElfTest, TinyNonElfFile) {
folly::writeFull(tmpFile.fd(), contents.data(), contents.size()); folly::writeFull(tmpFile.fd(), contents.data(), contents.size());
ElfFile elfFile; ElfFile elfFile;
const char* msg = nullptr; auto res = elfFile.openNoThrow(tmpFile.path().c_str(), true);
auto res = elfFile.openNoThrow(tmpFile.path().c_str(), true, &msg); EXPECT_EQ(ElfFile::kInvalidElfFile, res.code);
EXPECT_EQ(ElfFile::kInvalidElfFile, res); EXPECT_STREQ("not an ELF file (too short)", res.msg);
EXPECT_STREQ("not an ELF file (too short)", msg);
} }
TEST_F(ElfTest, NonElfScript) { TEST_F(ElfTest, NonElfScript) {
...@@ -71,10 +70,9 @@ TEST_F(ElfTest, NonElfScript) { ...@@ -71,10 +70,9 @@ TEST_F(ElfTest, NonElfScript) {
folly::writeFull(tmpFile.fd(), contents.data(), contents.size()); folly::writeFull(tmpFile.fd(), contents.data(), contents.size());
ElfFile elfFile; ElfFile elfFile;
const char* msg = nullptr; auto res = elfFile.openNoThrow(tmpFile.path().c_str(), true);
auto res = elfFile.openNoThrow(tmpFile.path().c_str(), true, &msg); EXPECT_EQ(ElfFile::kInvalidElfFile, res.code);
EXPECT_EQ(ElfFile::kInvalidElfFile, res); EXPECT_STREQ("invalid ELF magic", res.msg);
EXPECT_STREQ("invalid ELF magic", msg);
} }
TEST_F(ElfTest, FailToOpenLargeFilename) { TEST_F(ElfTest, FailToOpenLargeFilename) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment