checkrt.c
1 /* Copyright (c) 2022 <djcj@gmx.de> 2 * 3 * Permission is hereby granted, free of charge, to any person obtaining a copy 4 * of this software and associated documentation files (the "Software"), to deal 5 * in the Software without restriction, including without limitation the rights 6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 * copies of the Software, and to permit persons to whom the Software is 8 * furnished to do so, subject to the following conditions: 9 * 10 * The above copyright notice and this permission notice shall be included in all 11 * copies or substantial portions of the Software. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 * SOFTWARE. 20 */ 21 22 #ifndef _GNU_SOURCE 23 #define _GNU_SOURCE 24 #endif 25 #include <link.h> 26 #include <dlfcn.h> 27 #include <elf.h> 28 #include <fcntl.h> 29 #include <libgen.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sys/stat.h> 34 #include <sys/types.h> 35 #include <sys/mman.h> 36 #include <sys/wait.h> 37 #include <unistd.h> 38 39 #include "res.h" 40 41 #ifdef __x86_64__ 42 typedef Elf64_Ehdr Elf_Ehdr; 43 typedef Elf64_Shdr Elf_Shdr; 44 typedef Elf64_Sym Elf_Sym; 45 #else 46 typedef Elf32_Ehdr Elf_Ehdr; 47 typedef Elf32_Shdr Elf_Shdr; 48 typedef Elf32_Sym Elf_Sym; 49 #endif 50 51 #define LIBGCC_S_SO "libgcc_s.so.1" 52 #define LIBSTDCXX_SO "libstdc++.so.6" 53 #define LIBGCC_DIR "gcc" 54 #define STDCXX_DIR "cxx" 55 56 #define MAX(X,Y) (((X) > (Y)) ? (X) : (Y)) 57 #define PRINT_VERBOSE(FMT, ...) if (verbose) { fprintf(stderr, FMT, __VA_ARGS__); } 58 59 60 static char *get_libpath(const char *lib, char verbose) 61 { 62 struct link_map *map = NULL; 63 char *path; 64 65 /* It's very important to use dlmopen() together with the argument LM_ID_NEWLM, 66 * otherwise the bundled library and the system library will use the same 67 * namespace and the path of the bundled library might be returned when you 68 * expected the system one. 69 */ 70 void *handle = dlmopen(LM_ID_NEWLM, lib, RTLD_LAZY); 71 if (!handle) { 72 PRINT_VERBOSE("error: failed to dlmopen() file: %s\n", lib); 73 return NULL; 74 } 75 76 if (dlinfo(handle, RTLD_DI_LINKMAP, &map) == -1) { 77 PRINT_VERBOSE("error: could not retrieve information: %s\n", lib); 78 dlclose(handle); 79 return NULL; 80 } 81 82 path = strdup(map->l_name); 83 dlclose(handle); 84 85 return path; 86 } 87 88 static int symbol_version(const char *lib, const char *sym_prefix, char verbose) 89 { 90 int fd = -1; 91 void *addr = NULL; 92 const char *error = ""; 93 94 /* let dlopen() do all the compatibility checks */ 95 char *orig = get_libpath(lib, verbose); 96 if (!orig) return -1; 97 98 fd = open(orig, O_RDONLY); 99 if (fd < 0) { 100 PRINT_VERBOSE("error: failed to open() file: %s\n", orig); 101 free(orig); 102 return -1; 103 } 104 105 if (verbose) { 106 if (strcmp(lib, orig) == 0) { 107 fprintf(stderr, "%s\n", orig); 108 } else { 109 fprintf(stderr, "%s -> %s\n", lib, orig); 110 } 111 } 112 113 free(orig); 114 115 /* make sure file size is larger than the required ELF header size */ 116 struct stat st; 117 if (fstat(fd, &st) < 0 || st.st_size < sizeof(Elf_Ehdr)) { 118 error = "fstat() failed or returned a too low file size"; 119 goto symbol_version_error; 120 } 121 122 /* mmap() library */ 123 addr = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 124 if (addr == MAP_FAILED) { 125 error = "mmap() failed"; 126 goto symbol_version_error; 127 } 128 129 /* ELF header */ 130 Elf_Ehdr *ehdr = addr; 131 error = "offset exceeding filesize"; 132 if (ehdr->e_shoff > st.st_size) { 133 goto symbol_version_error; 134 } 135 136 Elf_Shdr *shdr = addr + ehdr->e_shoff; 137 int shnum = ehdr->e_shnum; 138 Elf_Shdr *sh_strtab = &shdr[ehdr->e_shstrndx]; 139 140 if (sh_strtab->sh_offset > st.st_size) { 141 goto symbol_version_error; 142 } 143 const char *sh_strtab_p = addr + sh_strtab->sh_offset; 144 const char *sh_dynstr_p = sh_strtab_p; 145 const char * const sh_strtab_p_const = sh_strtab_p; 146 147 /* get strtab/dynstr */ 148 for (int i = 0; i < shnum; ++i) { 149 if (shdr[i].sh_type != SHT_STRTAB) continue; 150 const char *name = sh_strtab_p_const + shdr[i].sh_name; 151 152 if (strcmp(name, ".strtab") == 0) { 153 if (shdr[i].sh_offset > st.st_size) { 154 goto symbol_version_error; 155 } 156 sh_strtab_p = addr + shdr[i].sh_offset; 157 break; 158 } else if (strcmp(name, ".dynstr") == 0) { 159 if (shdr[i].sh_offset > st.st_size) { 160 goto symbol_version_error; 161 } 162 sh_dynstr_p = addr + shdr[i].sh_offset; 163 break; 164 } 165 } 166 167 const char *symbol = NULL; 168 size_t len = strlen(sym_prefix); 169 170 /* iterate through sections */ 171 for (int i = 0; i < shnum; ++i) { 172 if (shdr[i].sh_type != SHT_SYMTAB && shdr[i].sh_type != SHT_DYNSYM) { 173 continue; 174 } 175 176 if (shdr[i].sh_offset > st.st_size) { 177 goto symbol_version_error; 178 } 179 Elf_Sym *syms_data = addr + shdr[i].sh_offset; 180 181 /* iterate through symbols */ 182 for (size_t j = 0; j < shdr[i].sh_size / sizeof(Elf_Sym); ++j) { 183 if (syms_data[j].st_shndx != SHN_ABS) { 184 continue; 185 } 186 187 const char *name; 188 if (shdr[i].sh_type == SHT_DYNSYM) { 189 name = sh_dynstr_p + syms_data[j].st_name; 190 } else { 191 name = sh_strtab_p + syms_data[j].st_name; 192 } 193 194 if (strncmp(name, sym_prefix, len) != 0) { 195 continue; 196 } 197 198 if (!symbol) { 199 symbol = name; 200 continue; 201 } 202 203 if (strverscmp(name, symbol) > 0) { 204 symbol = name; 205 } 206 } 207 } 208 209 int maj = 0, min = 0, pat = 0; 210 if (sscanf(symbol + len, "%d.%d.%d", &maj, &min, &pat) < 1) { 211 goto symbol_version_error; 212 } 213 214 PRINT_VERBOSE("%s%d.%d.%d\n", sym_prefix, maj, min, pat); 215 munmap(addr, st.st_size); 216 close(fd); 217 return (pat + min*1000 + maj*1000000); 218 219 symbol_version_error: 220 PRINT_VERBOSE("error: %s: %s\n", error, orig); 221 munmap(addr, st.st_size); 222 close(fd); 223 return -1; 224 } 225 226 static void dump_file(const char *dest, unsigned char *data, unsigned int len) 227 { 228 int fd = creat(dest, DEFFILEMODE); 229 if (fd == -1) return; 230 if (write(fd, data, len) != len) unlink(dest); 231 close(fd); 232 } 233 234 static int copy_lib(const char *lib, const char *destDir, char verbose) 235 { 236 char *destFull = NULL; 237 int fdIn = -1, fdOut = -1; 238 239 /* get full source and target paths */ 240 char *srcFull = get_libpath(lib, verbose); 241 if (!srcFull) goto copy_lib_error; 242 243 char *base = basename(srcFull); 244 if (!base) goto copy_lib_error; 245 246 size_t len = strlen(destDir); 247 size_t len2 = strlen(base); 248 destFull = malloc(len + MAX(len2,32) + 2); 249 sprintf(destFull, "%s/%s", destDir, base); 250 251 /* open source for reading */ 252 fdIn = open(srcFull, O_RDONLY|O_CLOEXEC); 253 if (fdIn == -1) goto copy_lib_error; 254 255 /* open target for writing */ 256 mkdir(destDir, ACCESSPERMS); 257 fdOut = creat(destFull, DEFFILEMODE); 258 if (fdOut == -1) goto copy_lib_error; 259 260 /* copy data into target */ 261 ssize_t n; 262 unsigned char buf[512*1024]; 263 while ((n = read(fdIn, &buf, sizeof(buf))) > 0) { 264 if (write(fdOut, &buf, n) != n) { 265 goto copy_lib_error; 266 } 267 } 268 269 int rv = 0; 270 fprintf(stderr, ">> %s\ncopied to -> %s\n", srcFull, destFull); 271 272 /* dump COPYING files */ 273 char *p = destFull + len + 1; 274 strcpy(p, "COPYING.RUNTIME.gz"); 275 dump_file(destFull, COPYING_RUNTIME_gz, COPYING_RUNTIME_gz_len); 276 p += 7; 277 strcpy(p, "3.gz"); 278 dump_file(destFull, COPYING3_gz, COPYING3_gz_len); 279 strcpy(p, ".libgcc"); 280 dump_file(destFull, COPYING_libgcc, COPYING_libgcc_len); 281 strcpy(p, ".libstdc++"); 282 dump_file(destFull, COPYING_libstdc__, COPYING_libstdc___len); 283 fprintf(stderr, "(COPYING files were added too)\n"); 284 285 goto copy_lib_end; 286 287 copy_lib_error: 288 rv = -1; 289 290 copy_lib_end: 291 if (fdOut != -1) close(fdOut); 292 if (fdIn != -1) close(fdIn); 293 if (srcFull) free(srcFull); 294 if (destFull) free(destFull); 295 296 return rv; 297 } 298 299 300 int main(int argc, char **argv) 301 { 302 #if CHECKRT_TEST == 1 303 304 printf("Test:\n\n"); 305 copy_lib(LIBGCC_S_SO, "./" LIBGCC_DIR, 1); 306 copy_lib(LIBSTDCXX_SO, "./" STDCXX_DIR, 1); 307 putchar('\n'); 308 symbol_version("./" LIBGCC_DIR "/" LIBGCC_S_SO, "GCC_", 1); 309 putchar('\n'); 310 symbol_version("./" STDCXX_DIR "/" LIBSTDCXX_SO, "GLIBCXX_", 1); 311 putchar('\n'); 312 symbol_version(LIBGCC_S_SO, "GCC_", 1); 313 putchar('\n'); 314 symbol_version(LIBSTDCXX_SO, "GLIBCXX_", 1); 315 316 #else 317 318 char v = 0, copy = 0; 319 320 for (int i=1; i < argc; i++) { 321 if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { 322 fprintf(stderr, 323 "usage:\n" 324 " %s -c|--copy-libraries\n" 325 " %s -h|--help\n" 326 " %s -v|--verbose\n" 327 "\n" 328 "This program will look for the following libraries relative to its\n" 329 "location, check if they are usable and add them to LD_LIBRARY_PATH\n" 330 "if they are newer than the system's equivalent:\n" 331 "\n" 332 " " LIBGCC_DIR "/" LIBGCC_S_SO "\n" 333 " " STDCXX_DIR "/" LIBSTDCXX_SO "\n", argv[0], argv[0], argv[0]); 334 return 0; 335 } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) { 336 v = 1; 337 } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--copy-libraries") == 0) { 338 copy = 1; 339 } else { 340 fprintf(stderr, "error: invalid argument: %s\nTry `%s --help' for more information.\n", argv[i], argv[0]); 341 return 1; 342 } 343 } 344 345 char *currdir = realpath("/proc/self/exe", NULL); 346 if (!currdir) { 347 fprintf(stderr, "error: realpath() failed to resolve /proc/self/exe\n"); 348 return 1; 349 } 350 351 char *p = strrchr(currdir, '/'); 352 if (!p) { 353 perror("strrchr()"); 354 free(currdir); 355 return 1; 356 } 357 *(p+1) = 0; 358 359 size_t len = strlen(currdir); 360 char *libpath = malloc(len + MAX(sizeof(STDCXX_DIR), sizeof(LIBGCC_DIR)) + 361 MAX(sizeof(LIBSTDCXX_SO), sizeof(LIBGCC_S_SO)) + 2); 362 strcpy(libpath, currdir); 363 p = libpath + len; 364 365 if (copy) { 366 /* copy system libraries */ 367 strcpy(p, LIBGCC_DIR); 368 copy_lib(LIBGCC_S_SO, libpath, v); 369 strcpy(p, STDCXX_DIR); 370 copy_lib(LIBSTDCXX_SO, libpath, v); 371 } else { 372 /* get symbol versions */ 373 374 strcpy(p, LIBGCC_DIR "/" LIBGCC_S_SO); 375 int ver = symbol_version(libpath, "GCC_", v); 376 if (ver != -1 && ver > symbol_version(LIBGCC_S_SO, "GCC_", v)) { 377 printf("%s" LIBGCC_DIR ":", currdir); 378 } 379 380 strcpy(p, STDCXX_DIR "/" LIBSTDCXX_SO); 381 ver = symbol_version(libpath, "GLIBCXX_", v); 382 if (ver != -1 && ver > symbol_version(LIBSTDCXX_SO, "GLIBCXX_", v)) { 383 printf("%s" STDCXX_DIR ":", currdir); 384 } 385 386 putchar('\n'); 387 } 388 389 free(libpath); 390 free(currdir); 391 #endif 392 393 return 0; 394 } 395 396 397