pixiv:92983280
这一期没什么比较硬的重点知识,仅做为补全整个过程来补充,可以轻松愉快的食用。
write dependency 1 2 3 if (!ctx.arg.dependency_file.empty ()) write_dependency_file (ctx);
将所有依赖,也就是链接过程中所有读取的文件,并且写入到文件中。可以用于确认某个文件是否被加入到链接过程中。
–dependency-file=FILE Write Makefile-style dependency rules to FILE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 template <typename E>void write_dependency_file (Context<E> &ctx) { std::vector<std::string> deps; std::unordered_set<std::string> seen; for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool) if (!mf->parent) if (std::string path = path_clean (mf->name); seen.insert (path).second) deps.push_back (path); std::ofstream out; out.open (ctx.arg.dependency_file); if (out.fail ()) Fatal (ctx) << "--dependency-file: cannot open " << ctx.arg.dependency_file << ": " << errno_string (); out << ctx.arg.output << ":" ; for (std::string &s : deps) out << " " << s; out << "\n" ; for (std::string &s : deps) out << "\n" << s << ":\n" ; out.close (); }
clean lto object 1 2 if (ctx.has_lto_object) lto_cleanup (ctx);
清理lto相关的文件,lto相关的操作都是类似于插件的形式执行的,以适配不同编译器产生的lto文件
1 2 3 4 5 6 7 template <typename E>void lto_cleanup (Context<E> &ctx) { Timer t (ctx, "lto_cleanup" ) ; if (cleanup_hook) cleanup_hook (); }
这个cleanup_hook也是在前面注册插件的时候要注册的
1 2 3 4 5 6 template <typename E>static PluginStatus register_cleanup_hook (CleanupHandler fn) { LOG << "register_cleanup_hook\n" ; cleanup_hook = fn; return LDPS_OK; }
print map 1 2 if (ctx.arg.print_map) print_map (ctx);
–Map FILE Write map file to a given file
收集信息并建立了section到symbol的map,之后遍历所有的chunk,进行打印。
首先会打印一行chunk的信息
如果不是osec那么会继续打印下一个chunk,否则之后会接着打印osec内部的所有members的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 template <typename E>void print_map (Context<E> &ctx) { std::ostream *out = &std::cout; std::unique_ptr<std::ofstream> file; if (!ctx.arg.Map.empty ()) { file = open_output_file (ctx); out = file.get (); } Map<E> map = get_map (ctx); *out << " VMA Size Align Out In Symbol\n" ; for (Chunk<E> *osec : ctx.chunks) { *out << std::showbase << std::setw (18 ) << std::hex << (u64)osec->shdr.sh_addr << std::dec << std::setw (11 ) << (u64)osec->shdr.sh_size << std::setw (6 ) << (u64)osec->shdr.sh_addralign << " " << osec->name << "\n" ; if (osec->kind () != OUTPUT_SECTION) continue ; std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members; std::vector<std::string> bufs (members.size()) ; tbb::parallel_for ((i64)0 , (i64)members.size (), [&](i64 i) { InputSection<E> *mem = members[i]; std::ostringstream ss; opt_demangle = ctx.arg.demangle; u64 addr = osec->shdr.sh_addr + mem->offset; ss << std::showbase << std::setw (18 ) << std::hex << addr << std::dec << std::setw (11 ) << (u64)mem->sh_size << std::setw (6 ) << (1 << (u64)mem->p2align) << " " << *mem << "\n" ; typename Map<E>::const_accessor acc; if (map.find (acc, mem)) for (Symbol<E> *sym : acc->second) ss << std::showbase << std::setw (18 ) << std::hex << sym->get_addr (ctx) << std::dec << " 0 0 " << *sym << "\n" ; bufs[i] = ss.str (); }); for (std::string &str : bufs) *out << str; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 template <typename E>static Map<E> get_map (Context<E> &ctx) { Map<E> map; tbb::parallel_for_each (ctx.objs, [&](ObjectFile<E> *file) { for (Symbol<E> *sym : file->symbols) { if (sym->file != file || sym->get_type () == STT_SECTION) continue ; if (InputSection<E> *isec = sym->get_input_section ()) { assert (file == &isec->file); typename Map<E>::accessor acc; map.insert (acc, {isec, {}}); acc->second.push_back (sym); } } }); if (map.size () <= 1 ) return map; tbb::parallel_for (map.range (), [](const typename Map<E>::range_type &range) { for (auto it = range.begin (); it != range.end (); it++) { std::vector<Symbol<E> *> &vec = it->second; sort (vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; }); } }); return map; }
stats 1 2 3 if (ctx.arg.stats) show_stats (ctx);
在链接的过程中对于许多操作都会使用一个Counter记录数量,比如说符号的个数等,这里就是打印那些记录的信息
–stats Print input statistics
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 template <typename E>void show_stats (Context<E> &ctx) { for (ObjectFile<E> *obj : ctx.objs) { static Counter defined ("defined_syms" ) ; defined += obj->first_global - 1 ; static Counter undefined ("undefined_syms" ) ; undefined += obj->symbols.size () - obj->first_global; for (std::unique_ptr<InputSection<E>> &sec : obj->sections) { if (!sec || !sec->is_alive) continue ; static Counter alloc ("reloc_alloc" ) ; static Counter nonalloc ("reloc_nonalloc" ) ; if (sec->shdr ().sh_flags & SHF_ALLOC) alloc += sec->get_rels (ctx).size (); else nonalloc += sec->get_rels (ctx).size (); } static Counter comdats ("comdats" ) ; comdats += obj->comdat_groups.size (); static Counter removed_comdats ("removed_comdat_mem" ) ; for (ComdatGroupRef<E> &ref : obj->comdat_groups) if (ref.group->owner != obj->priority) removed_comdats += ref.members.size (); static Counter num_cies ("num_cies" ) ; num_cies += obj->cies.size (); static Counter num_unique_cies ("num_unique_cies" ) ; for (CieRecord<E> &cie : obj->cies) if (cie.is_leader) num_unique_cies++; static Counter num_fdes ("num_fdes" ) ; num_fdes += obj->fdes.size (); } static Counter num_bytes ("total_input_bytes" ) ; for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool) num_bytes += mf->size; static Counter num_input_sections ("input_sections" ) ; for (ObjectFile<E> *file : ctx.objs) num_input_sections += file->sections.size (); static Counter num_output_chunks ("output_chunks" , ctx.chunks.size()) ; static Counter num_objs ("num_objs" , ctx.objs.size()) ; static Counter num_dsos ("num_dsos" , ctx.dsos.size()) ; if constexpr (needs_thunk<E>) { static Counter thunk_bytes ("thunk_bytes" ) ; for (Chunk<E> *chunk : ctx.chunks) if (OutputSection<E> *osec = chunk->to_osec ()) for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks) thunk_bytes += thunk->size (); } Counter::print (); for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections) sec->print_stats (ctx); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Counter {public : Counter (std::string_view name, i64 value = 0 ) : name (name), values (value) { static std::mutex mu; std::scoped_lock lock (mu) ; instances.push_back (this ); } Counter &operator ++(int ) { if (enabled) values.local ()++; return *this ; } Counter &operator +=(int delta) { if (enabled) values.local () += delta; return *this ; } static void print () ; static inline bool enabled = false ; private : i64 get_value () ; std::string_view name; tbb::enumerable_thread_specific<i64> values; static inline std::vector<Counter *> instances; }; void Counter::print () { sort (instances, [](Counter *a, Counter *b) { return a->get_value () > b->get_value (); }); for (Counter *c : instances) std::cout << std::setw (20 ) << std::right << c->name << "=" << c->get_value () << "\n" ; }
1 2 3 4 5 6 7 8 9 10 void MergedSection<E>::print_stats (Context<E> &ctx) { i64 used = 0 ; for (i64 i = 0 ; i < map.nbuckets; i++) if (map.keys[i]) used++; SyncOut (ctx) << this ->name << " estimation=" << estimator.get_cardinality () << " actual=" << used; }
perf 之前在各个过程中都会创建许多timer,在这个过程中把timer收集到的时间信息全部打印出来
1 2 if (ctx.arg.perf) print_timer_records (ctx.timer_records);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void print_timer_records ( tbb::concurrent_vector<std::unique_ptr<TimerRecord>> &records) { for (i64 i = records.size () - 1 ; i >= 0 ; i--) records[i]->stop (); for (i64 i = 0 ; i < records.size (); i++) { TimerRecord &inner = *records[i]; if (inner.parent) continue ; for (i64 j = i - 1 ; j >= 0 ; j--) { TimerRecord &outer = *records[j]; if (outer.start <= inner.start && inner.end <= outer.end) { inner.parent = &outer; outer.children.push_back (&inner); break ; } } } std::cout << " User System Real Name\n" ; for (std::unique_ptr<TimerRecord> &rec : records) if (!rec->parent) print_rec (*rec, 0 ); std::cout << std::flush; }
on_complete 1 2 if (on_complete) on_complete ();
1 2 3 4 #if !defined(_WIN32) && !defined(__APPLE__) if (ctx.arg.fork) on_complete = fork_child(); #endif
因为退出一个大量内存占用的程序很慢,因此这里会fork一个子进程来进行实际的清理工作,主进程直接退出,能够提升结束的速度,让用户不可见的清理操作放到后台执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #ifdef MOLD_X86_64 std::function<void () > fork_child () { int pipefd[2 ]; if (pipe (pipefd) == -1 ) { perror ("pipe" ); exit (1 ); } pid_t pid = fork(); if (pid == -1 ) { perror ("fork" ); exit (1 ); } if (pid > 0 ) { close (pipefd[1 ]); char buf[1 ]; if (read (pipefd[0 ], buf, 1 ) == 1 ) _exit(0 ); int status; waitpid (pid, &status, 0 ); if (WIFEXITED (status)) _exit(WEXITSTATUS (status)); if (WIFSIGNALED (status)) raise (WTERMSIG (status)); _exit(1 ); } close (pipefd[0 ]); return [=] { char buf[] = {1 }; [[maybe_unused]] int n = write (pipefd[1 ], buf, 1 ); assert (n == 1 ); }; } #endif
on_exit 1 2 3 4 5 6 7 if (ctx.arg.quick_exit) _exit(0 ); for (std::function<void ()> &fn : ctx.on_exit) fn (); ctx.checkpoint ();
直接exit或者调用exit的清理的函数
–quick-exit Use quick_exit to exit (default) –no-quick-exit
在mold中有的只有一处,在icf_sections中创建的map需要在这里销毁,但是也可能在lto的过程中注册了其他的exit函数。
1 2 ctx.on_exit.push_back ([=] { delete map; });
最后在返回之前会再调用checkpoint检查是否有错误。
至此,整个mold的链接过程已经完全结束了。下一期会进行一个总结,并且记录一下一些自己的想法