}
}
-int ElfFile::openNoThrow(const char* name, bool readOnly, const char** msg)
- noexcept {
+int ElfFile::openNoThrow(const char* name,
+ bool readOnly,
+ const char** msg) noexcept {
FOLLY_SAFE_CHECK(fd_ == -1, "File already open");
fd_ = ::open(name, readOnly ? O_RDONLY : O_RDWR);
if (fd_ == -1) {
return kSuccess;
}
+int ElfFile::openAndFollow(const char* name,
+ bool readOnly,
+ const char** msg) noexcept {
+ auto result = openNoThrow(name, readOnly, msg);
+ if (!readOnly || result != kSuccess) return result;
+
+ /* NOTE .gnu_debuglink specifies only the name of the debugging info file
+ * (with no directory components). GDB checks 3 different directories, but
+ * ElfFile only supports the first version:
+ * - dirname(name)
+ * - dirname(name) + /.debug/
+ * - X/dirname(name)/ - where X is set in gdb's `debug-file-directory`.
+ */
+ auto dirend = strrchr(name, '/');
+ // include ending '/' if any.
+ auto dirlen = dirend != nullptr ? dirend + 1 - name : 0;
+
+ auto debuginfo = getSectionByName(".gnu_debuglink");
+ if (!debuginfo) return result;
+
+ // The section starts with the filename, with any leading directory
+ // components removed, followed by a zero byte.
+ auto debugFileName = getSectionBody(*debuginfo);
+ auto debugFileLen = strlen(debugFileName.begin());
+ if (dirlen + debugFileLen >= PATH_MAX) {
+ return result;
+ }
+
+ char linkname[PATH_MAX];
+ memcpy(linkname, name, dirlen);
+ memcpy(linkname + dirlen, debugFileName.begin(), debugFileLen + 1);
+ reset();
+ result = openNoThrow(linkname, readOnly, msg);
+ if (result == kSuccess) return result;
+ return openNoThrow(name, readOnly, msg);
+}
+
ElfFile::~ElfFile() {
reset();
}
kSystemError = -1,
kInvalidElfFile = -2,
};
+ // Open the ELF file. Does not throw on error.
int openNoThrow(const char* name, bool readOnly=true,
const char** msg=nullptr) noexcept;
+ // Like openNoThrow, but follow .gnu_debuglink if present
+ int openAndFollow(const char* name, bool readOnly=true,
+ const char** msg=nullptr) noexcept;
+
// Open the ELF file. Throws on error.
void open(const char* name, bool readOnly=true);
auto& f = slots_[n];
const char* msg = "";
- int r = f->openNoThrow(path.data(), true, &msg);
+ int r = f->openAndFollow(path.data(), true, &msg);
if (r != ElfFile::kSuccess) {
return nullptr;
}
// No negative caching
const char* msg = "";
- int r = entry->file.openNoThrow(path.c_str(), true, &msg);
+ int r = entry->file.openAndFollow(path.c_str(), true, &msg);
if (r != ElfFile::kSuccess) {
return nullptr;
}
--- /dev/null
+#!/bin/bash
+
+crash="$1"
+rm -f "$crash."{debuginfo,strip}
+objcopy --only-keep-debug "$crash" "$crash.debuginfo"
+objcopy --strip-debug --add-gnu-debuglink="$crash.debuginfo" "$crash" "$crash.strip"
+
+echo '{"op":"start","test":"gnu_debuglink_test"}';
+start=$(date +%s)
+if "$crash.strip" 2>&1 | grep 'Crash.cpp:[0-9]*$' > /dev/null; then
+ result='"status":"passed"';
+else
+ result='"status":"failed"';
+fi
+end=$(date +%s)
+echo '{"op":"test_done","test":"gnu_debuglink_test",'"$result"'}'
+echo '{"op":"all_done","results":[{"name":"gnu_debuglink_test",'"$result"',"start_time":'"$start"',"end_time":'"$end"',"details":"nothing"}]}'