1 /** 2 * A library in the ELF format, used on Unix. 3 * 4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/libelf.d, _libelf.d) 8 * Documentation: https://dlang.org/phobos/dmd_libelf.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/libelf.d 10 */ 11 12 module dmd.libelf; 13 14 version(Windows) {} 15 else version(OSX) {} 16 else: 17 18 import core.stdc.time; 19 import core.stdc.string; 20 import core.stdc.stdlib; 21 import core.stdc.stdio; 22 import core.sys.posix.sys.stat; 23 import core.sys.posix.unistd; 24 25 import dmd.globals; 26 import dmd.lib; 27 import dmd.utils; 28 29 import dmd.root.array; 30 import dmd.root.file; 31 import dmd.root.filename; 32 import dmd.root.outbuffer; 33 import dmd.root.port; 34 import dmd.root.rmem; 35 import dmd.root.string; 36 import dmd.root.stringtable; 37 38 import dmd.scanelf; 39 40 // Entry point (only public symbol in this module). 41 public extern (C++) Library LibElf_factory() 42 { 43 return new LibElf(); 44 } 45 46 private: // for the remainder of this module 47 48 enum LOG = false; 49 50 struct ElfObjSymbol 51 { 52 const(char)[] name; 53 ElfObjModule* om; 54 } 55 56 alias ElfObjModules = Array!(ElfObjModule*); 57 alias ElfObjSymbols = Array!(ElfObjSymbol*); 58 59 final class LibElf : Library 60 { 61 ElfObjModules objmodules; // ElfObjModule[] 62 ElfObjSymbols objsymbols; // ElfObjSymbol[] 63 StringTable!(ElfObjSymbol*) tab; 64 65 extern (D) this() 66 { 67 tab._init(14_000); 68 } 69 70 /*************************************** 71 * Add object module or library to the library. 72 * Examine the buffer to see which it is. 73 * If the buffer is NULL, use module_name as the file name 74 * and load the file. 75 */ 76 override void addObject(const(char)[] module_name, const ubyte[] buffer) 77 { 78 static if (LOG) 79 { 80 printf("LibElf::addObject(%.*s)\n", 81 cast(int)module_name.length, module_name.ptr); 82 } 83 84 void corrupt(int reason) 85 { 86 error("corrupt ELF object module %.*s %d", 87 cast(int)module_name.length, module_name.ptr, reason); 88 } 89 90 int fromfile = 0; 91 auto buf = buffer.ptr; 92 auto buflen = buffer.length; 93 if (!buf) 94 { 95 assert(module_name.length); 96 // read file and take buffer ownership 97 auto data = readFile(Loc.initial, module_name).extractSlice(); 98 buf = data.ptr; 99 buflen = data.length; 100 fromfile = 1; 101 } 102 if (buflen < 16) 103 { 104 static if (LOG) 105 { 106 printf("buf = %p, buflen = %d\n", buf, buflen); 107 } 108 return corrupt(__LINE__); 109 } 110 if (memcmp(buf, "!<arch>\n".ptr, 8) == 0) 111 { 112 /* Library file. 113 * Pull each object module out of the library and add it 114 * to the object module array. 115 */ 116 static if (LOG) 117 { 118 printf("archive, buf = %p, buflen = %d\n", buf, buflen); 119 } 120 uint offset = 8; 121 char* symtab = null; 122 uint symtab_size = 0; 123 char* filenametab = null; 124 uint filenametab_size = 0; 125 uint mstart = cast(uint)objmodules.dim; 126 while (offset < buflen) 127 { 128 if (offset + ElfLibHeader.sizeof >= buflen) 129 return corrupt(__LINE__); 130 ElfLibHeader* header = cast(ElfLibHeader*)(cast(ubyte*)buf + offset); 131 offset += ElfLibHeader.sizeof; 132 char* endptr = null; 133 uint size = cast(uint)strtoul(header.file_size.ptr, &endptr, 10); 134 if (endptr >= header.file_size.ptr + 10 || *endptr != ' ') 135 return corrupt(__LINE__); 136 if (offset + size > buflen) 137 return corrupt(__LINE__); 138 if (header.object_name[0] == '/' && header.object_name[1] == ' ') 139 { 140 /* Instead of rescanning the object modules we pull from a 141 * library, just use the already created symbol table. 142 */ 143 if (symtab) 144 return corrupt(__LINE__); 145 symtab = cast(char*)buf + offset; 146 symtab_size = size; 147 if (size < 4) 148 return corrupt(__LINE__); 149 } 150 else if (header.object_name[0] == '/' && header.object_name[1] == '/') 151 { 152 /* This is the file name table, save it for later. 153 */ 154 if (filenametab) 155 return corrupt(__LINE__); 156 filenametab = cast(char*)buf + offset; 157 filenametab_size = size; 158 } 159 else 160 { 161 auto om = new ElfObjModule(); 162 om.base = cast(ubyte*)buf + offset; /*- sizeof(ElfLibHeader)*/ 163 om.length = size; 164 om.offset = 0; 165 if (header.object_name[0] == '/') 166 { 167 /* Pick long name out of file name table 168 */ 169 uint foff = cast(uint)strtoul(header.object_name.ptr + 1, &endptr, 10); 170 uint i; 171 for (i = 0; 1; i++) 172 { 173 if (foff + i >= filenametab_size) 174 return corrupt(__LINE__); 175 char c = filenametab[foff + i]; 176 if (c == '/') 177 break; 178 } 179 auto n = cast(char*)Mem.check(malloc(i + 1)); 180 memcpy(n, filenametab + foff, i); 181 n[i] = 0; 182 om.name = n[0 .. i]; 183 } 184 else 185 { 186 /* Pick short name out of header 187 */ 188 auto n = cast(char*)Mem.check(malloc(ELF_OBJECT_NAME_SIZE)); 189 for (int i = 0; 1; i++) 190 { 191 if (i == ELF_OBJECT_NAME_SIZE) 192 return corrupt(__LINE__); 193 char c = header.object_name[i]; 194 if (c == '/') 195 { 196 n[i] = 0; 197 om.name = n[0 .. i]; 198 break; 199 } 200 n[i] = c; 201 } 202 } 203 om.name_offset = -1; 204 om.file_time = strtoul(header.file_time.ptr, &endptr, 10); 205 om.user_id = cast(uint)strtoul(header.user_id.ptr, &endptr, 10); 206 om.group_id = cast(uint)strtoul(header.group_id.ptr, &endptr, 10); 207 om.file_mode = cast(uint)strtoul(header.file_mode.ptr, &endptr, 8); 208 om.scan = 0; // don't scan object module for symbols 209 objmodules.push(om); 210 } 211 offset += (size + 1) & ~1; 212 } 213 if (offset != buflen) 214 return corrupt(__LINE__); 215 /* Scan the library's symbol table, and insert it into our own. 216 * We use this instead of rescanning the object module, because 217 * the library's creator may have a different idea of what symbols 218 * go into the symbol table than we do. 219 * This is also probably faster. 220 */ 221 uint nsymbols = Port.readlongBE(symtab); 222 char* s = symtab + 4 + nsymbols * 4; 223 if (4 + nsymbols * (4 + 1) > symtab_size) 224 return corrupt(__LINE__); 225 for (uint i = 0; i < nsymbols; i++) 226 { 227 const(char)[] name = s.toDString(); 228 s += name.length + 1; 229 if (s - symtab > symtab_size) 230 return corrupt(__LINE__); 231 uint moff = Port.readlongBE(symtab + 4 + i * 4); 232 //printf("symtab[%d] moff = %x %x, name = %s\n", i, moff, moff + sizeof(Header), name.ptr); 233 for (uint m = mstart; 1; m++) 234 { 235 if (m == objmodules.dim) 236 return corrupt(__LINE__); // didn't find it 237 ElfObjModule* om = objmodules[m]; 238 //printf("\t%x\n", (char *)om.base - (char *)buf); 239 if (moff + ElfLibHeader.sizeof == cast(char*)om.base - cast(char*)buf) 240 { 241 addSymbol(om, name, 1); 242 //if (mstart == m) 243 // mstart++; 244 break; 245 } 246 } 247 } 248 return; 249 } 250 /* It's an object module 251 */ 252 auto om = new ElfObjModule(); 253 om.base = cast(ubyte*)buf; 254 om.length = cast(uint)buflen; 255 om.offset = 0; 256 // remove path, but not extension 257 om.name = FileName.name(module_name); 258 om.name_offset = -1; 259 om.scan = 1; 260 if (fromfile) 261 { 262 stat_t statbuf; 263 int i = module_name.toCStringThen!(slice => stat(slice.ptr, &statbuf)); 264 if (i == -1) // error, errno is set 265 return corrupt(__LINE__); 266 om.file_time = statbuf.st_ctime; 267 om.user_id = statbuf.st_uid; 268 om.group_id = statbuf.st_gid; 269 om.file_mode = statbuf.st_mode; 270 } 271 else 272 { 273 /* Mock things up for the object module file that never was 274 * actually written out. 275 */ 276 __gshared uid_t uid; 277 __gshared gid_t gid; 278 __gshared int _init; 279 if (!_init) 280 { 281 _init = 1; 282 uid = getuid(); 283 gid = getgid(); 284 } 285 time(&om.file_time); 286 om.user_id = uid; 287 om.group_id = gid; 288 om.file_mode = (1 << 15) | (6 << 6) | (4 << 3); // 0100640 289 } 290 objmodules.push(om); 291 } 292 293 /*****************************************************************************/ 294 295 void addSymbol(ElfObjModule* om, const(char)[] name, int pickAny = 0) 296 { 297 static if (LOG) 298 { 299 printf("LibElf::addSymbol(%s, %s, %d)\n", om.name.ptr, name.ptr, pickAny); 300 } 301 auto s = tab.insert(name.ptr, name.length, null); 302 if (!s) 303 { 304 // already in table 305 if (!pickAny) 306 { 307 s = tab.lookup(name.ptr, name.length); 308 assert(s); 309 ElfObjSymbol* os = s.value; 310 error("multiple definition of %s: %s and %s: %s", om.name.ptr, name.ptr, os.om.name.ptr, os.name.ptr); 311 } 312 } 313 else 314 { 315 auto os = new ElfObjSymbol(); 316 os.name = xarraydup(name); 317 os.om = om; 318 s.value = os; 319 objsymbols.push(os); 320 } 321 } 322 323 private: 324 /************************************ 325 * Scan single object module for dictionary symbols. 326 * Send those symbols to LibElf::addSymbol(). 327 */ 328 void scanObjModule(ElfObjModule* om) 329 { 330 static if (LOG) 331 { 332 printf("LibElf::scanObjModule(%s)\n", om.name.ptr); 333 } 334 335 extern (D) void addSymbol(const(char)[] name, int pickAny) 336 { 337 this.addSymbol(om, name, pickAny); 338 } 339 340 scanElfObjModule(&addSymbol, om.base[0 .. om.length], om.name.ptr, loc); 341 } 342 343 /*****************************************************************************/ 344 /*****************************************************************************/ 345 /********************************************** 346 * Create and write library to libbuf. 347 * The library consists of: 348 * !<arch>\n 349 * header 350 * dictionary 351 * object modules... 352 */ 353 protected override void WriteLibToBuffer(OutBuffer* libbuf) 354 { 355 static if (LOG) 356 { 357 printf("LibElf::WriteLibToBuffer()\n"); 358 } 359 /************* Scan Object Modules for Symbols ******************/ 360 foreach (om; objmodules) 361 { 362 if (om.scan) 363 { 364 scanObjModule(om); 365 } 366 } 367 /************* Determine string section ******************/ 368 /* The string section is where we store long file names. 369 */ 370 uint noffset = 0; 371 foreach (om; objmodules) 372 { 373 size_t len = om.name.length; 374 if (len >= ELF_OBJECT_NAME_SIZE) 375 { 376 om.name_offset = noffset; 377 noffset += len + 2; 378 } 379 else 380 om.name_offset = -1; 381 } 382 static if (LOG) 383 { 384 printf("\tnoffset = x%x\n", noffset); 385 } 386 /************* Determine module offsets ******************/ 387 uint moffset = 8 + ElfLibHeader.sizeof + 4; 388 foreach (os; objsymbols) 389 { 390 moffset += 4 + os.name.length + 1; 391 } 392 uint hoffset = moffset; 393 static if (LOG) 394 { 395 printf("\tmoffset = x%x\n", moffset); 396 } 397 moffset += moffset & 1; 398 if (noffset) 399 moffset += ElfLibHeader.sizeof + noffset; 400 foreach (om; objmodules) 401 { 402 moffset += moffset & 1; 403 om.offset = moffset; 404 moffset += ElfLibHeader.sizeof + om.length; 405 } 406 libbuf.reserve(moffset); 407 /************* Write the library ******************/ 408 libbuf.write("!<arch>\n"); 409 ElfObjModule om; 410 om.name_offset = -1; 411 om.base = null; 412 om.length = cast(uint)(hoffset - (8 + ElfLibHeader.sizeof)); 413 om.offset = 8; 414 om.name = ""; 415 .time(&om.file_time); 416 om.user_id = 0; 417 om.group_id = 0; 418 om.file_mode = 0; 419 ElfLibHeader h; 420 ElfOmToHeader(&h, &om); 421 libbuf.write((&h)[0 .. 1]); 422 char[4] buf; 423 Port.writelongBE(cast(uint)objsymbols.dim, buf.ptr); 424 libbuf.write(buf[0 .. 4]); 425 foreach (os; objsymbols) 426 { 427 Port.writelongBE(os.om.offset, buf.ptr); 428 libbuf.write(buf[0 .. 4]); 429 } 430 foreach (os; objsymbols) 431 { 432 libbuf.writestring(os.name); 433 libbuf.writeByte(0); 434 } 435 static if (LOG) 436 { 437 printf("\tlibbuf.moffset = x%x\n", libbuf.length); 438 } 439 /* Write out the string section 440 */ 441 if (noffset) 442 { 443 if (libbuf.length & 1) 444 libbuf.writeByte('\n'); 445 // header 446 memset(&h, ' ', ElfLibHeader.sizeof); 447 h.object_name[0] = '/'; 448 h.object_name[1] = '/'; 449 size_t len = sprintf(h.file_size.ptr, "%u", noffset); 450 assert(len < 10); 451 h.file_size[len] = ' '; 452 h.trailer[0] = '`'; 453 h.trailer[1] = '\n'; 454 libbuf.write((&h)[0 .. 1]); 455 foreach (om2; objmodules) 456 { 457 if (om2.name_offset >= 0) 458 { 459 libbuf.writestring(om2.name); 460 libbuf.writeByte('/'); 461 libbuf.writeByte('\n'); 462 } 463 } 464 } 465 /* Write out each of the object modules 466 */ 467 foreach (om2; objmodules) 468 { 469 if (libbuf.length & 1) 470 libbuf.writeByte('\n'); // module alignment 471 assert(libbuf.length == om2.offset); 472 ElfOmToHeader(&h, om2); 473 libbuf.write((&h)[0 .. 1]); // module header 474 libbuf.write(om2.base[0 .. om2.length]); // module contents 475 } 476 static if (LOG) 477 { 478 printf("moffset = x%x, libbuf.length = x%x\n", moffset, libbuf.length); 479 } 480 assert(libbuf.length == moffset); 481 } 482 } 483 484 /*****************************************************************************/ 485 /*****************************************************************************/ 486 struct ElfObjModule 487 { 488 ubyte* base; // where are we holding it in memory 489 uint length; // in bytes 490 uint offset; // offset from start of library 491 const(char)[] name; // module name (file name) with terminating 0 492 int name_offset; // if not -1, offset into string table of name 493 time_t file_time; // file time 494 uint user_id; 495 uint group_id; 496 uint file_mode; 497 int scan; // 1 means scan for symbols 498 } 499 500 enum ELF_OBJECT_NAME_SIZE = 16; 501 502 struct ElfLibHeader 503 { 504 char[ELF_OBJECT_NAME_SIZE] object_name; 505 char[12] file_time; 506 char[6] user_id; 507 char[6] group_id; 508 char[8] file_mode; // in octal 509 char[10] file_size; 510 char[2] trailer; 511 } 512 513 extern (C++) void ElfOmToHeader(ElfLibHeader* h, ElfObjModule* om) 514 { 515 char* buffer = cast(char*)h; 516 // user_id and group_id are padded on 6 characters in Header struct. 517 // Squashing to 0 if more than 999999. 518 if (om.user_id > 999_999) 519 om.user_id = 0; 520 if (om.group_id > 999_999) 521 om.group_id = 0; 522 size_t len; 523 if (om.name_offset == -1) 524 { 525 // "name/ 1423563789 5000 5000 100640 3068 `\n" 526 // |^^^^^^^^^^^^^^^|^^^^^^^^^^^|^^^^^|^^^^^|^^^^^^^|^^^^^^^^^|^^ 527 // name file_time u_id gr_id fmode fsize trailer 528 len = snprintf(buffer, ElfLibHeader.sizeof, "%-16s%-12llu%-6u%-6u%-8o%-10u`", om.name.ptr, cast(long)om.file_time, om.user_id, om.group_id, om.file_mode, om.length); 529 // adding '/' after the name field 530 const(size_t) name_length = om.name.length; 531 assert(name_length < ELF_OBJECT_NAME_SIZE); 532 buffer[name_length] = '/'; 533 } 534 else 535 { 536 // "/162007 1423563789 5000 5000 100640 3068 `\n" 537 // |^^^^^^^^^^^^^^^|^^^^^^^^^^^|^^^^^|^^^^^|^^^^^^^|^^^^^^^^^|^^ 538 // name_offset file_time u_id gr_id fmode fsize trailer 539 len = snprintf(buffer, ElfLibHeader.sizeof, "/%-15d%-12llu%-6u%-6u%-8o%-10u`", om.name_offset, cast(long)om.file_time, om.user_id, om.group_id, om.file_mode, om.length); 540 } 541 assert(ElfLibHeader.sizeof > 0 && len == ElfLibHeader.sizeof - 1); 542 // replace trailing \0 with \n 543 buffer[len] = '\n'; 544 }