|
7 | 7 | #include <tb_coff.h> |
8 | 8 | #include <file_map.h> |
9 | 9 |
|
10 | | -enum { IMP_PREFIX_LEN = sizeof("__imp_") - 1 }; |
| 10 | +enum { |
| 11 | + IMP_PREFIX_LEN = sizeof("__imp_") - 1, |
| 12 | + PDB_BLOCK_SIZE = 4096, |
| 13 | +}; |
11 | 14 |
|
12 | 15 | typedef struct { |
13 | 16 | uint32_t offset; |
14 | 17 | TB_LinkerSegment* segment; |
15 | 18 | } PE_BaseReloc; |
16 | 19 |
|
| 20 | +// https://llvm.org/docs/PDB/MsfFile.html#the-stream-directory |
| 21 | +typedef struct { |
| 22 | + char file_magic[32]; |
| 23 | + uint32_t block_size; |
| 24 | + uint32_t free_block_map_block; |
| 25 | + uint32_t num_blocks; |
| 26 | + uint32_t num_directory_bytes; |
| 27 | + uint32_t unknown; |
| 28 | + uint32_t dir_blocks[]; |
| 29 | +} PDB_SuperBlock; |
| 30 | + |
| 31 | +// serialized form |
| 32 | +typedef struct { |
| 33 | + uint32_t version; |
| 34 | + uint32_t signature; |
| 35 | + uint32_t age; |
| 36 | + uint32_t unique_id[4]; |
| 37 | +} PDB_InfoHeader; |
| 38 | + |
| 39 | +// https://llvm.org/docs/PDB/DbiStream.html |
| 40 | +typedef struct { |
| 41 | + // I have no idea why they didn't make this |
| 42 | + // part of the streams consistent btw. Info |
| 43 | + // does ver+sign+age, here it's sign+ver+age |
| 44 | + uint32_t signature; |
| 45 | + uint32_t version; |
| 46 | + uint32_t age; |
| 47 | + |
| 48 | + uint16_t global_stream; // StreamIndex |
| 49 | + uint16_t build_num; |
| 50 | + |
| 51 | + uint16_t public_stream; // StreamIndex |
| 52 | + uint16_t pdb_dll_version; |
| 53 | + uint16_t sym_record_stream; // StreamIndex |
| 54 | + uint16_t pdb_dll_rebuild; |
| 55 | + uint32_t mod_info_size; |
| 56 | + uint32_t section_contrib_size; |
| 57 | + uint32_t section_map_size; |
| 58 | + uint32_t source_info_size; |
| 59 | + uint32_t type_server_size; |
| 60 | + uint32_t mfc_type_server; // StreamIndex |
| 61 | + uint32_t optional_debug_header_size; |
| 62 | + uint32_t ec_subsystem_size; |
| 63 | + |
| 64 | + uint16_t flags; |
| 65 | + uint16_t machine; |
| 66 | + |
| 67 | + uint32_t pad; |
| 68 | + uint8_t data[]; |
| 69 | +} PDB_DBIHeader; |
| 70 | + |
| 71 | +typedef struct { |
| 72 | + size_t size; |
| 73 | + // We assume that all blocks are contiguous for now |
| 74 | + size_t first_block; |
| 75 | +} DebugStream; |
| 76 | + |
17 | 77 | typedef struct { |
18 | 78 | TB_Linker* linker; |
19 | 79 | TB_LinkerObject* lib; |
@@ -427,11 +487,6 @@ void pe_append_object(TPool* pool, void** args) { |
427 | 487 |
|
428 | 488 | uint64_t order = obj->time; |
429 | 489 | CUIK_TIMED_BLOCK("parse sections") { |
430 | | - if (strsuffix(obj->name.data, "api-ms-win-core-calendar-l1-1-0.dll", obj->name.length)) { |
431 | | - assert(parser.section_count != 0); |
432 | | - printf("ZZZ %zu\n", parser.section_count); |
433 | | - } |
434 | | - |
435 | 490 | FOR_N(i, 0, parser.section_count) { |
436 | 491 | TB_ObjectSection* restrict s = §ions[i]; |
437 | 492 | tb_coff_parse_section(&parser, i, s); |
@@ -478,10 +533,6 @@ void pe_append_object(TPool* pool, void** args) { |
478 | 533 | pdata_piece = p; |
479 | 534 | } |
480 | 535 | } |
481 | | - |
482 | | - if (strsuffix(obj->name.data, "api-ms-win-core-calendar-l1-1-0.dll", obj->name.length)) { |
483 | | - printf("AAA %.*s %#x\n", (int) s->name.length, s->name.data, p->flags); |
484 | | - } |
485 | 536 | } |
486 | 537 | } |
487 | 538 |
|
@@ -1195,6 +1246,8 @@ static DynArray(PE_BaseReloc) find_base_relocs(TB_Linker* l) { |
1195 | 1246 | return base_relocs; |
1196 | 1247 | } |
1197 | 1248 |
|
| 1249 | +#define WRITE8(data) (output[write_pos++] = (data)) |
| 1250 | +#define WRITE32(data) (memcpy(&output[write_pos], &(uint32_t){ data }, 4), write_pos += (4)) |
1198 | 1251 | #define WRITE(data, size) (memcpy(&output[write_pos], data, size), write_pos += (size)) |
1199 | 1252 | static bool pe_export(TB_Linker* l, const char* file_name) { |
1200 | 1253 | cuikperf_region_start("linker", NULL); |
@@ -1538,9 +1591,113 @@ static bool pe_export(TB_Linker* l, const char* file_name) { |
1538 | 1591 | close_file_map(&fm); |
1539 | 1592 | } |
1540 | 1593 |
|
| 1594 | + CUIK_TIMED_BLOCK("pdb output") { |
| 1595 | + DynArray(DebugStream*) streams = dyn_array_create(DebugStream*, 8); |
| 1596 | + FOR_N(i, 0, 6) { |
| 1597 | + DebugStream* s = tb_arena_alloc(&linker_perm_arena, sizeof(DebugStream)); |
| 1598 | + s->size = 0; |
| 1599 | + if (i == 1) { |
| 1600 | + s->size = 71; // Info stream |
| 1601 | + } else if (i == 3) { |
| 1602 | + s->size = sizeof(PDB_DBIHeader); |
| 1603 | + } |
| 1604 | + dyn_array_put(streams, s); |
| 1605 | + } |
| 1606 | + |
| 1607 | + size_t blocks_needed = 5; |
| 1608 | + dyn_array_for(i, streams) { |
| 1609 | + streams[i]->first_block = blocks_needed; |
| 1610 | + blocks_needed += (streams[i]->size + PDB_BLOCK_SIZE - 1) / PDB_BLOCK_SIZE; |
| 1611 | + } |
| 1612 | + |
| 1613 | + // 0 1 2 3 4 5 6 7 8 |
| 1614 | + // Superblock Free0 Free1 Dirs Headers Block Block Block Block |
| 1615 | + size_t pdb_size = blocks_needed*PDB_BLOCK_SIZE; |
| 1616 | + FileMap fm = open_file_map_write("a.pdb", pdb_size); |
| 1617 | + if (fm.data == NULL) { |
| 1618 | + printf("tblink: could not open file! %s", "a.pdb"); |
| 1619 | + return false; |
| 1620 | + } |
| 1621 | + |
| 1622 | + size_t write_pos = 0; |
| 1623 | + uint8_t* output = fm.data; |
| 1624 | + |
| 1625 | + PDB_SuperBlock super = { |
| 1626 | + .file_magic = "Microsoft C/C++ MSF 7.00\r\n\x1A\x44\x53\0\0", |
| 1627 | + .block_size = PDB_BLOCK_SIZE, |
| 1628 | + .free_block_map_block = 1, |
| 1629 | + .num_blocks = blocks_needed, |
| 1630 | + .num_directory_bytes = 4 + dyn_array_length(streams)*8, |
| 1631 | + }; |
| 1632 | + |
| 1633 | + // SuperBlock + directory page blocks |
| 1634 | + WRITE(&super, sizeof(super)); |
| 1635 | + WRITE32(3); |
| 1636 | + |
| 1637 | + // Block3: Directory |
| 1638 | + write_pos = 3*PDB_BLOCK_SIZE; |
| 1639 | + WRITE32(4); |
| 1640 | + |
| 1641 | + // Block4: Stream Headers |
| 1642 | + write_pos = 4*PDB_BLOCK_SIZE; |
| 1643 | + WRITE32(dyn_array_length(streams)); |
| 1644 | + dyn_array_for(i, streams) { WRITE32(streams[i]->size); } |
| 1645 | + dyn_array_for(i, streams) { |
| 1646 | + size_t block_count = (streams[i]->size + PDB_BLOCK_SIZE - 1) / PDB_BLOCK_SIZE; |
| 1647 | + FOR_N(j, 0, block_count) { |
| 1648 | + WRITE32(streams[i]->first_block + j); |
| 1649 | + } |
| 1650 | + } |
| 1651 | + |
| 1652 | + dyn_array_for(i, streams) { |
| 1653 | + size_t start = write_pos = streams[i]->first_block*PDB_BLOCK_SIZE; |
| 1654 | + if (i == 1) { |
| 1655 | + PDB_InfoHeader stream = { |
| 1656 | + .version = 20000404, // VC70 |
| 1657 | + }; |
| 1658 | + WRITE(&stream, sizeof(stream)); |
| 1659 | + |
| 1660 | + // PDB Names streams |
| 1661 | + WRITE32(sizeof("/names")); |
| 1662 | + WRITE("/names", sizeof("/names")); |
| 1663 | + |
| 1664 | + // https://llvm.org/docs/PDB/HashTable.html |
| 1665 | + // There's a really weird hash map stuffed here rather than |
| 1666 | + // some direct stream indices but whatever, my job isn't to |
| 1667 | + // question microsoft, that's a hobby, my job is to put up |
| 1668 | + // with microsoft. |
| 1669 | + // |
| 1670 | + // Size & Capacity |
| 1671 | + WRITE32(1); |
| 1672 | + WRITE32(1); |
| 1673 | + // Present Bit Vector |
| 1674 | + WRITE32(1); |
| 1675 | + WRITE32(1); |
| 1676 | + // Deleted Bit Vector |
| 1677 | + WRITE32(1); |
| 1678 | + WRITE32(0); |
| 1679 | + // Entries |
| 1680 | + WRITE32(0); |
| 1681 | + WRITE32(5); |
| 1682 | + } else if (i == 3) { |
| 1683 | + PDB_DBIHeader stream = { |
| 1684 | + .signature = -1, |
| 1685 | + .version = 19990903, // VC70 |
| 1686 | + .machine = 0xD0, |
| 1687 | + }; |
| 1688 | + WRITE(&stream, sizeof(stream)); |
| 1689 | + } |
| 1690 | + assert(write_pos - start == streams[i]->size); |
| 1691 | + } |
| 1692 | + |
| 1693 | + close_file_map(&fm); |
| 1694 | + } |
| 1695 | + |
1541 | 1696 | cuikperf_region_end(); |
1542 | 1697 | return true; |
1543 | 1698 | } |
| 1699 | +#undef WRITE32 |
| 1700 | +#undef WRITE8 |
1544 | 1701 | #undef WRITE |
1545 | 1702 |
|
1546 | 1703 | TB_LinkerVtbl tb__linker_pe = { |
|
0 commit comments