+// Plugin_recorder logs plugin actions and saves intermediate files
+// for later replay.
+
+class Plugin_recorder
+{
+ public:
+ Plugin_recorder() : file_count_(0), tempdir_(NULL), logfile_(NULL)
+ { }
+
+ bool
+ init();
+
+ void
+ claimed_file(const std::string& obj_name, off_t offset, off_t filesize,
+ const std::string& plugin_name);
+
+ void
+ unclaimed_file(const std::string& obj_name, off_t offset, off_t filesize);
+
+ void
+ replacement_file(const char* name, bool is_lib);
+
+ void
+ record_symbols(const Object* obj, int nsyms,
+ const struct ld_plugin_symbol* syms);
+
+ void
+ finish()
+ { ::fclose(this->logfile_); }
+
+ private:
+ unsigned int file_count_;
+ const char* tempdir_;
+ FILE* logfile_;
+};
+
+bool
+Plugin_recorder::init()
+{
+ // Create a temporary directory where we can stash the log and
+ // copies of replacement files.
+ char dir_template[] = "gold-recording-XXXXXX";
+#ifdef HAVE_MKDTEMP
+ if (mkdtemp(dir_template) == NULL)
+ return false;
+#else
+ if (mktemp(dir_template) == NULL)
+ return false;
+#if defined (_WIN32) && !defined (__CYGWIN32__)
+ if (mkdir(dir_template) != 0)
+ return false;
+#else
+ if (mkdir(dir_template, 0700) != 0)
+ return false;
+#endif
+#endif
+
+ size_t len = strlen(dir_template) + 1;
+ char* tempdir = new char[len];
+ strncpy(tempdir, dir_template, len);
+
+ // Create the log file.
+ std::string logname(tempdir);
+ logname.append("/log");
+ FILE* logfile = ::fopen(logname.c_str(), "w");
+ if (logfile == NULL)
+ return false;
+
+ this->tempdir_ = tempdir;
+ this->logfile_ = logfile;
+
+ gold_info(_("%s: recording to %s"), program_name, this->tempdir_);
+
+ return true;
+}
+
+void
+Plugin_recorder::claimed_file(const std::string& obj_name,
+ off_t offset,
+ off_t filesize,
+ const std::string& plugin_name)
+{
+ fprintf(this->logfile_, "PLUGIN: %s\n", plugin_name.c_str());
+ fprintf(this->logfile_, "CLAIMED: %s", obj_name.c_str());
+ if (offset > 0)
+ fprintf(this->logfile_, " @%ld", static_cast<long>(offset));
+ fprintf(this->logfile_, " %ld\n", static_cast<long>(filesize));
+}
+
+void
+Plugin_recorder::unclaimed_file(const std::string& obj_name,
+ off_t offset,
+ off_t filesize)
+{
+ fprintf(this->logfile_, "UNCLAIMED: %s", obj_name.c_str());
+ if (offset > 0)
+ fprintf(this->logfile_, " @%ld", static_cast<long>(offset));
+ fprintf(this->logfile_, " %ld\n", static_cast<long>(filesize));
+}
+
+// Make a hard link to INNAME from OUTNAME, if possible.
+// If not, copy the file.
+
+static bool
+link_or_copy_file(const char* inname, const char* outname)
+{
+ static char buf[4096];
+
+#ifdef HAVE_LINK
+ if (::link(inname, outname) == 0)
+ return true;
+#endif
+
+ int in = ::open(inname, O_RDONLY);
+ if (in < 0)
+ {
+ gold_warning(_("%s: can't open (%s)"), inname, strerror(errno));
+ return false;
+ }
+ int out = ::open(outname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ if (out < 0)
+ {
+ gold_warning(_("%s: can't create (%s)"), outname, strerror(errno));
+ ::close(in);
+ return false;
+ }
+ ssize_t len;
+ while ((len = ::read(in, buf, sizeof(buf))) > 0)
+ {
+ if (::write(out, buf, len) != len)
+ {
+ gold_warning(_("%s: write error while making copy of file (%s)"),
+ inname, strerror(errno));
+ break;
+ }
+ }
+ ::close(in);
+ ::close(out);
+ return true;
+}
+
+void
+Plugin_recorder::replacement_file(const char* name, bool is_lib)
+{
+ fprintf(this->logfile_, "REPLACEMENT: %s", name);
+ if (is_lib)
+ fprintf(this->logfile_, "(lib)");
+ else
+ {
+ char counter[10];
+ const char* basename = lbasename(name);
+ snprintf(counter, sizeof(counter), "%05d", this->file_count_);
+ ++this->file_count_;
+ std::string outname(this->tempdir_);
+ outname.append("/");
+ outname.append(counter);
+ outname.append("-");
+ outname.append(basename);
+ if (link_or_copy_file(name, outname.c_str()))
+ fprintf(this->logfile_, " -> %s", outname.c_str());
+ }
+ fprintf(this->logfile_, "\n");
+}
+
+void
+Plugin_recorder::record_symbols(const Object* obj, int nsyms,
+ const struct ld_plugin_symbol* syms)
+{
+ fprintf(this->logfile_, "SYMBOLS: %d %s\n", nsyms, obj->name().c_str());
+ for (int i = 0; i < nsyms; ++i)
+ {
+ const struct ld_plugin_symbol* isym = &syms[i];
+
+ const char* def;
+ switch (isym->def)
+ {
+ case LDPK_DEF:
+ def = "D";
+ break;
+ case LDPK_WEAKDEF:
+ def = "WD";
+ break;
+ case LDPK_UNDEF:
+ def = "U";
+ break;
+ case LDPK_WEAKUNDEF:
+ def = "WU";
+ break;
+ case LDPK_COMMON:
+ def = "C";
+ break;
+ default:
+ def = "?";
+ break;
+ }
+
+ char vis;
+ switch (isym->visibility)
+ {
+ case LDPV_PROTECTED:
+ vis = 'P';
+ break;
+ case LDPV_INTERNAL:
+ vis = 'I';
+ break;
+ case LDPV_HIDDEN:
+ vis = 'H';
+ break;
+ case LDPV_DEFAULT:
+ vis = 'D';
+ break;
+ default:
+ vis = '?';
+ break;
+ }
+
+ fprintf(this->logfile_, " %5d: %-2s %c %s", i, def, vis, isym->name);
+ if (isym->version != NULL && isym->version[0] != '\0')
+ fprintf(this->logfile_, "@%s", isym->version);
+ if (isym->comdat_key != NULL && isym->comdat_key[0] != '\0')
+ {
+ if (strcmp(isym->name, isym->comdat_key) == 0)
+ fprintf(this->logfile_, " [comdat]");
+ else
+ fprintf(this->logfile_, " [comdat: %s]", isym->comdat_key);
+ }
+ fprintf(this->logfile_, "\n");
+ }
+}
+