| 114 | typedef int (*RunTestsFunc)(int argc, const char** argv); |
| 115 | |
| 116 | int main(int argc, char** argv) FL_NOEXCEPT { |
| 117 | // Setup crash handler BEFORE loading any shared libraries |
| 118 | runner_setup_crash_handler(); |
| 119 | |
| 120 | std::string so_path; |
| 121 | const std::string shared_lib_ext = ".so"; |
| 122 | |
| 123 | // Determine shared library path: explicit argument or inferred from exe name |
| 124 | if (argc > 1 && argv[1][0] != '-') { |
| 125 | // First argument is a path (doesn't start with -) |
| 126 | so_path = argv[1]; |
| 127 | } else { |
| 128 | // No explicit path: infer from exe name |
| 129 | // On Linux, /proc/self/exe is a symlink to the executable |
| 130 | std::string full_exe_path; |
| 131 | char path_buf[1024]; |
| 132 | ssize_t len = readlink("/proc/self/exe", path_buf, sizeof(path_buf) - 1); |
| 133 | if (len != -1) { |
| 134 | path_buf[len] = '\0'; |
| 135 | full_exe_path = path_buf; |
| 136 | } else { |
| 137 | // Fallback to argv[0] if readlink fails |
| 138 | full_exe_path = argv[0]; |
| 139 | } |
| 140 | |
| 141 | if (full_exe_path.empty()) { |
| 142 | std::cout << "Error: Failed to get executable path" << std::endl; |
| 143 | return 1; |
| 144 | } |
| 145 | |
| 146 | // Extract directory and filename |
| 147 | size_t last_slash = full_exe_path.find_last_of('/'); |
| 148 | std::string exe_dir = (last_slash != std::string::npos) ? full_exe_path.substr(0, last_slash) : "."; |
| 149 | std::string exe_file = (last_slash != std::string::npos) ? full_exe_path.substr(last_slash + 1) : full_exe_path; |
| 150 | |
| 151 | // Remove executable extension (if any) |
| 152 | size_t dot_pos = exe_file.find_last_of('.'); |
| 153 | std::string exe_name = (dot_pos != std::string::npos) ? exe_file.substr(0, dot_pos) : exe_file; |
| 154 | |
| 155 | // Construct shared library path |
| 156 | std::string so_name = exe_name + shared_lib_ext; |
| 157 | so_path = exe_dir + "/" + so_name; |
| 158 | } |
| 159 | |
| 160 | // Load shared library with RTLD_NOW for immediate symbol resolution |
| 161 | // This helps ASAN properly track symbols from the loaded library |
| 162 | void* handle = dlopen(so_path.c_str(), RTLD_NOW | RTLD_GLOBAL); |
| 163 | if (!handle) { |
| 164 | std::cout << "Error: Failed to load " << so_path << " (" << dlerror() << ")" << std::endl; |
| 165 | return 1; |
| 166 | } |
| 167 | |
| 168 | // Get run_tests function |
| 169 | RunTestsFunc run_tests = (RunTestsFunc)dlsym(handle, "run_tests"); |
| 170 | if (!run_tests) { |
| 171 | std::cout << "Error: Failed to find run_tests() in " << so_path << " (" << dlerror() << ")" << std::endl; |
| 172 | dlclose(handle); |
| 173 | return 1; |
nothing calls this directly
no test coverage detected