c++ 如何将llvm::outs()重定向到文件?

ddrv8njm  于 2023-06-25  发布在  其他
关注(0)|答案(3)|浏览(218)

我使用一些LLVM工具(如llvm-nm)作为静态库。也就是说,我复制了源代码llvm-nm.cpp,将main(..)重命名为llvm_nm(..),并将其编译为静态库。我想将标准输出转发到我的文件中。
我尝试使用下一种方法:

  1. int out_fd, err_fd;
  2. fpos_t out_pos, err_pos;
  3. // redirect out
  4. fflush(stdout);
  5. fgetpos(stdout, &out_pos);
  6. out_fd = dup(fileno(stdout));
  7. freopen(outFilename, "w", stdout);
  8. // execute
  9. int ret = llvm_nm(argc_, argv_);
  10. // restore output
  11. fflush(stdout);
  12. dup2(out_fd, fileno(stdout));
  13. close(out_fd);
  14. clearerr(stdout);
  15. fsetpos(stdout, &out_pos);

问题是它没有被转发(如果我在nm源代码中添加printf(),它可以工作,但对于nm输出则不行)。我查看了源代码,可以看到输出是使用llvm::outs()流完成的:

  1. outs() << "Archive map" << "\n";

it's implemented的下一种方式:

  1. /// outs() - This returns a reference to a raw_ostream for standard output.
  2. 00702 /// Use it like: outs() << "foo" << "bar";
  3. 00703 raw_ostream &llvm::outs() {
  4. 00704 // Set buffer settings to model stdout behavior.
  5. 00705 // Delete the file descriptor when the program exits, forcing error
  6. 00706 // detection. If you don't want this behavior, don't use outs().
  7. 00707 static raw_fd_ostream S(STDOUT_FILENO, true);
  8. 00708 return S;
  9. 00709 }

如何将输出重定向到我的文件?

brjng4g3

brjng4g31#

我意识到这是一个老问题,然而,我在查找llvm的outs()流的一些简单信息时遇到了这个问题,我找到了here
llvm“BrainF”附带的一个示例是这样使用的:

  1. ...
  2. raw_ostream *out = &outs();
  3. if (!JIT) {
  4. if (OutputFilename == "") {
  5. std::string base = InputFilename;
  6. if (InputFilename == "-") { base = "a"; }
  7. // Use default filename.
  8. OutputFilename = base+".bc";
  9. }
  10. if (OutputFilename != "-") {
  11. std::error_code EC;
  12. out = new raw_fd_ostream(OutputFilename, EC, sys::fs::F_None);
  13. }
  14. }
  15. ...
  16. if (out != &outs())
  17. delete out;
  18. ...

所以它似乎表明你可以安全地重新定向。

注意:在本例中,OutputFilename/InputFilename是使用llvm的Support Library CommandLine创建的std::string类型。

展开查看全部
mefy6pfw

mefy6pfw2#

似乎没有一个简单的方法可以做到这一点:在llvm::raw_fd_ostreamllvm::raw_ostream中都没有复制/赋值构造函数。freopen技巧也不起作用,因为文件描述器整数用于初始化llvm::outs()返回的对象。
我看到的唯一方法是使用LD_PRELOAD动态地更改llvm::outs()的实现,或者类似的链接器技巧,但这对我来说听起来非常古怪。也许把原始符号标记为弱符号,然后在你的库中覆盖它?

6bc51xsx

6bc51xsx3#

如果你愿意容忍一些未定义的行为,你可以这样做:

  1. #include <llvm/Support/raw_ostream.h>
  2. static void hijack_log_line(const std::string &line) {
  3. // Do whatever logging you want here
  4. logInfo("[llvm] {}", line);
  5. }
  6. class hijack_raw_fd_ostream : public llvm::raw_fd_ostream {
  7. public:
  8. explicit hijack_raw_fd_ostream()
  9. : llvm::raw_fd_ostream(-1, false, false) {
  10. }
  11. protected:
  12. void write_impl(const char *Ptr, size_t Size) override {
  13. static std::string CurrentLine;
  14. std::string_view sv(Ptr, Ptr + Size);
  15. for (char ch : sv) {
  16. if (ch == '\n') {
  17. hijack_log_line(CurrentLine);
  18. CurrentLine.clear();
  19. } else {
  20. CurrentLine += ch;
  21. }
  22. }
  23. }
  24. uint64_t current_pos() const override {
  25. llvm::report_fatal_error("current_pos not implemented!");
  26. }
  27. size_t preferred_buffer_size() const override {
  28. return 0;
  29. }
  30. } hijack_stream;
  31. static_assert(sizeof(hijack_raw_fd_ostream) == sizeof(llvm::raw_fd_ostream));
  32. int main(int argc, char **argv) {
  33. // Disable buffering in the LLVM streams
  34. llvm::outs().SetUnbuffered();
  35. llvm::errs().SetUnbuffered();
  36. // NOTE: This is technically undefined behaviour, but it works in practice
  37. #pragma clang diagnostic push
  38. #pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
  39. std::memcpy(&llvm::outs(), &hijack_stream, sizeof(llvm::outs()));
  40. std::memcpy(&llvm::errs(), &hijack_stream, sizeof(llvm::errs()));
  41. #pragma clang diagnostic pop
  42. llvm::outs() << "Hello ";
  43. llvm::outs() << "from LLVM!\n";
  44. return 0;
  45. }

这是通过劫持vtable指针来挂钩write_implcurrent_pospreferred_buffer_size函数。这些行被累加并传递给hijack_log_line函数。
这取决于llvm::outs()在内部使用static raw_fd_ostream S;的实现细节。我们从这个类继承来确保内存布局是兼容的,但是如果这个实现改变,它可能会中断。

展开查看全部

相关问题