@@ -58,6 +58,9 @@ pub type ParsedRelocCache = HashMap<(InputId, u8), Vec<Reloc>>; |
| 58 | 58 | #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] |
| 59 | 59 | pub struct LinkEditBuildTimings { |
| 60 | 60 | pub symbol_plan: Duration, |
| 61 | + pub symbol_plan_locals: Duration, |
| 62 | + pub symbol_plan_globals: Duration, |
| 63 | + pub symbol_plan_strtab: Duration, |
| 61 | 64 | pub dyld_info: Duration, |
| 62 | 65 | pub metadata_tables: Duration, |
| 63 | 66 | pub code_signature: Duration, |
@@ -66,6 +69,9 @@ pub struct LinkEditBuildTimings { |
| 66 | 69 | impl std::ops::AddAssign for LinkEditBuildTimings { |
| 67 | 70 | fn add_assign(&mut self, rhs: Self) { |
| 68 | 71 | self.symbol_plan += rhs.symbol_plan; |
| 72 | + self.symbol_plan_locals += rhs.symbol_plan_locals; |
| 73 | + self.symbol_plan_globals += rhs.symbol_plan_globals; |
| 74 | + self.symbol_plan_strtab += rhs.symbol_plan_strtab; |
| 69 | 75 | self.dyld_info += rhs.dyld_info; |
| 70 | 76 | self.metadata_tables += rhs.metadata_tables; |
| 71 | 77 | self.code_signature += rhs.code_signature; |
@@ -902,7 +908,7 @@ fn build_linkedit_plan_profiled( |
| 902 | 908 | .map(|record| (record.symbol, record)) |
| 903 | 909 | .collect(); |
| 904 | 910 | let visibility = SymbolVisibilityPolicy::from_opts(opts)?; |
| 905 | | - let symbol_plan = build_output_symbols( |
| 911 | + let (symbol_plan, symbol_plan_timings) = build_output_symbols_profiled( |
| 906 | 912 | layout, |
| 907 | 913 | kind, |
| 908 | 914 | opts.dead_strip, |
@@ -912,6 +918,9 @@ fn build_linkedit_plan_profiled( |
| 912 | 918 | &imports, |
| 913 | 919 | )?; |
| 914 | 920 | timings.symbol_plan += phase_started.elapsed(); |
| 921 | + timings.symbol_plan_locals += symbol_plan_timings.locals; |
| 922 | + timings.symbol_plan_globals += symbol_plan_timings.globals; |
| 923 | + timings.symbol_plan_strtab += symbol_plan_timings.strtab; |
| 915 | 924 | let mut symtab_bytes = Vec::new(); |
| 916 | 925 | write_nlist_table(&symbol_plan.symbols, &mut symtab_bytes); |
| 917 | 926 | |
@@ -1480,19 +1489,18 @@ fn build_data_in_code( |
| 1480 | 1489 | } |
| 1481 | 1490 | |
| 1482 | 1491 | let atoms_by_input_section = atom_table.by_input_section(); |
| 1492 | + let atom_ranges = build_atom_range_index(atom_table, &atoms_by_input_section, icf_redirects); |
| 1483 | 1493 | let mut remapped = Vec::new(); |
| 1484 | 1494 | for (input_order, input) in inputs.iter().enumerate() { |
| 1485 | 1495 | for (input_entry_index, entry) in input.object.data_in_code.iter().copied().enumerate() { |
| 1486 | 1496 | let (section_index, section_relative) = |
| 1487 | 1497 | remap_data_in_code_to_section(input.object, entry)?; |
| 1488 | 1498 | let (atom_id, atom_delta) = find_containing_atom_range( |
| 1489 | | - atom_table, |
| 1490 | | - &atoms_by_input_section, |
| 1499 | + &atom_ranges, |
| 1491 | 1500 | input.id, |
| 1492 | 1501 | section_index, |
| 1493 | 1502 | section_relative, |
| 1494 | 1503 | entry.length as u32, |
| 1495 | | - icf_redirects, |
| 1496 | 1504 | ) |
| 1497 | 1505 | .ok_or_else(|| { |
| 1498 | 1506 | WriteError::MalformedDataInCode( |
@@ -1652,7 +1660,14 @@ fn collect_imports( |
| 1652 | 1660 | Ok(out) |
| 1653 | 1661 | } |
| 1654 | 1662 | |
| 1655 | | -fn build_output_symbols( |
| 1663 | +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] |
| 1664 | +struct SymbolPlanBuildTimings { |
| 1665 | + locals: Duration, |
| 1666 | + globals: Duration, |
| 1667 | + strtab: Duration, |
| 1668 | +} |
| 1669 | + |
| 1670 | +fn build_output_symbols_profiled( |
| 1656 | 1671 | layout: &Layout, |
| 1657 | 1672 | kind: OutputKind, |
| 1658 | 1673 | dead_strip: bool, |
@@ -1660,10 +1675,15 @@ fn build_output_symbols( |
| 1660 | 1675 | visibility: &SymbolVisibilityPolicy, |
| 1661 | 1676 | inputs: LinkEditInputs<'_>, |
| 1662 | 1677 | imports: &[ImportSymbolRecord], |
| 1663 | | -) -> Result<SymbolTablePlan, WriteError> { |
| 1678 | +) -> Result<(SymbolTablePlan, SymbolPlanBuildTimings), WriteError> { |
| 1664 | 1679 | let sym_table = inputs.0.sym_table; |
| 1665 | 1680 | let atom_sections = atom_section_ordinals(layout); |
| 1666 | 1681 | let atoms_by_input_section = inputs.0.atom_table.by_input_section(); |
| 1682 | + let atom_ranges = build_atom_range_index( |
| 1683 | + inputs.0.atom_table, |
| 1684 | + &atoms_by_input_section, |
| 1685 | + inputs.0.icf_redirects, |
| 1686 | + ); |
| 1667 | 1687 | let file_index_by_input: HashMap<InputId, usize> = inputs |
| 1668 | 1688 | .0 |
| 1669 | 1689 | .layout_inputs |
@@ -1672,6 +1692,7 @@ fn build_output_symbols( |
| 1672 | 1692 | .map(|(idx, input)| (input.id, idx + 1)) |
| 1673 | 1693 | .collect(); |
| 1674 | 1694 | let image_base = layout.segment("__TEXT").map(|seg| seg.vm_addr).unwrap_or(0); |
| 1695 | + let mut timings = SymbolPlanBuildTimings::default(); |
| 1675 | 1696 | let mut locals = Vec::new(); |
| 1676 | 1697 | let mut external_defineds = Vec::new(); |
| 1677 | 1698 | let mut undefineds = Vec::with_capacity(imports.len()); |
@@ -1706,19 +1727,21 @@ fn build_output_symbols( |
| 1706 | 1727 | }); |
| 1707 | 1728 | } |
| 1708 | 1729 | |
| 1730 | + let phase_started = std::time::Instant::now(); |
| 1709 | 1731 | for input in inputs.0.layout_inputs { |
| 1710 | 1732 | let ctx = LocalSymbolContext { |
| 1711 | 1733 | atom_table: inputs.0.atom_table, |
| 1712 | | - atoms_by_input_section: &atoms_by_input_section, |
| 1734 | + atom_ranges: &atom_ranges, |
| 1713 | 1735 | atom_sections: &atom_sections, |
| 1714 | | - icf_redirects: inputs.0.icf_redirects, |
| 1715 | 1736 | input_id: input.id, |
| 1716 | 1737 | file_index: file_index_by_input[&input.id], |
| 1717 | 1738 | }; |
| 1718 | 1739 | collect_local_symbols(layout, &ctx, input.object, &mut locals)?; |
| 1719 | 1740 | } |
| 1720 | 1741 | collect_synthetic_local_symbols(layout, inputs.0.synthetic_plan, &mut locals)?; |
| 1742 | + timings.locals += phase_started.elapsed(); |
| 1721 | 1743 | |
| 1744 | + let phase_started = std::time::Instant::now(); |
| 1722 | 1745 | for (symbol_id, symbol) in sym_table.iter() { |
| 1723 | 1746 | let Symbol::Defined { |
| 1724 | 1747 | name, |
@@ -1812,6 +1835,7 @@ fn build_output_symbols( |
| 1812 | 1835 | }); |
| 1813 | 1836 | } |
| 1814 | 1837 | undefineds.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name)); |
| 1838 | + timings.globals += phase_started.elapsed(); |
| 1815 | 1839 | |
| 1816 | 1840 | let exports = if matches!(kind, OutputKind::Dylib | OutputKind::Executable) { |
| 1817 | 1841 | external_defineds |
@@ -1832,6 +1856,7 @@ fn build_output_symbols( |
| 1832 | 1856 | Vec::new() |
| 1833 | 1857 | }; |
| 1834 | 1858 | |
| 1859 | + let phase_started = std::time::Instant::now(); |
| 1835 | 1860 | let local_count = if strip_locals { 0 } else { locals.len() }; |
| 1836 | 1861 | let mut specs = Vec::with_capacity(local_count + external_defineds.len() + undefineds.len()); |
| 1837 | 1862 | if !strip_locals { |
@@ -1886,23 +1911,27 @@ fn build_output_symbols( |
| 1886 | 1911 | symbol_indices.insert(symbol, idx as u32); |
| 1887 | 1912 | } |
| 1888 | 1913 | } |
| 1914 | + timings.strtab += phase_started.elapsed(); |
| 1889 | 1915 | |
| 1890 | | - Ok(SymbolTablePlan { |
| 1891 | | - symbols, |
| 1892 | | - map_symbols, |
| 1893 | | - strtab_bytes, |
| 1894 | | - symbol_indices, |
| 1895 | | - exports, |
| 1896 | | - dysymtab: DysymtabCmd { |
| 1897 | | - ilocalsym: 0, |
| 1898 | | - nlocalsym, |
| 1899 | | - iextdefsym: nlocalsym, |
| 1900 | | - nextdefsym, |
| 1901 | | - iundefsym: nlocalsym + nextdefsym, |
| 1902 | | - nundefsym, |
| 1903 | | - ..DysymtabCmd::default() |
| 1916 | + Ok(( |
| 1917 | + SymbolTablePlan { |
| 1918 | + symbols, |
| 1919 | + map_symbols, |
| 1920 | + strtab_bytes, |
| 1921 | + symbol_indices, |
| 1922 | + exports, |
| 1923 | + dysymtab: DysymtabCmd { |
| 1924 | + ilocalsym: 0, |
| 1925 | + nlocalsym, |
| 1926 | + iextdefsym: nlocalsym, |
| 1927 | + nextdefsym, |
| 1928 | + iundefsym: nlocalsym + nextdefsym, |
| 1929 | + nundefsym, |
| 1930 | + ..DysymtabCmd::default() |
| 1931 | + }, |
| 1904 | 1932 | }, |
| 1905 | | - }) |
| 1933 | + timings, |
| 1934 | + )) |
| 1906 | 1935 | } |
| 1907 | 1936 | |
| 1908 | 1937 | fn sort_local_symbols(locals: &mut [OutputSymbolSpec]) { |
@@ -1971,12 +2000,10 @@ fn collect_local_symbols( |
| 1971 | 2000 | .expect("section symbol without section"); |
| 1972 | 2001 | let offset = input_sym.value().saturating_sub(section.addr) as u32; |
| 1973 | 2002 | let (atom_id, delta) = find_containing_atom( |
| 1974 | | - ctx.atom_table, |
| 1975 | | - ctx.atoms_by_input_section, |
| 2003 | + ctx.atom_ranges, |
| 1976 | 2004 | ctx.input_id, |
| 1977 | 2005 | input_sym.sect_idx(), |
| 1978 | 2006 | offset, |
| 1979 | | - ctx.icf_redirects, |
| 1980 | 2007 | ) |
| 1981 | 2008 | .ok_or(WriteError::MissingSegment("__UNKNOWN"))?; |
| 1982 | 2009 | let addr = |
@@ -2023,57 +2050,72 @@ fn collect_local_symbols( |
| 2023 | 2050 | |
| 2024 | 2051 | struct LocalSymbolContext<'a> { |
| 2025 | 2052 | atom_table: &'a AtomTable, |
| 2026 | | - atoms_by_input_section: &'a HashMap<(InputId, u8), Vec<crate::resolve::AtomId>>, |
| 2053 | + atom_ranges: &'a AtomRangeIndex, |
| 2027 | 2054 | atom_sections: &'a HashMap<crate::resolve::AtomId, u8>, |
| 2028 | | - icf_redirects: Option<&'a HashMap<crate::resolve::AtomId, crate::resolve::AtomId>>, |
| 2029 | 2055 | input_id: InputId, |
| 2030 | 2056 | file_index: usize, |
| 2031 | 2057 | } |
| 2032 | 2058 | |
| 2059 | +#[derive(Debug, Clone, Copy)] |
| 2060 | +struct AtomRange { |
| 2061 | + atom: crate::resolve::AtomId, |
| 2062 | + start: u32, |
| 2063 | + end: u32, |
| 2064 | +} |
| 2065 | + |
| 2066 | +type AtomRangeIndex = HashMap<(InputId, u8), Vec<AtomRange>>; |
| 2067 | + |
| 2033 | 2068 | fn is_assembler_temporary_symbol(name: &str) -> bool { |
| 2034 | 2069 | name.starts_with('L') || name.starts_with("ltmp") |
| 2035 | 2070 | } |
| 2036 | 2071 | |
| 2037 | | -fn find_containing_atom( |
| 2072 | +fn build_atom_range_index( |
| 2038 | 2073 | atom_table: &AtomTable, |
| 2039 | 2074 | atoms_by_input_section: &HashMap<(InputId, u8), Vec<crate::resolve::AtomId>>, |
| 2075 | + icf_redirects: Option<&HashMap<crate::resolve::AtomId, crate::resolve::AtomId>>, |
| 2076 | +) -> AtomRangeIndex { |
| 2077 | + let mut out = HashMap::with_capacity(atoms_by_input_section.len()); |
| 2078 | + for (&key, ids) in atoms_by_input_section { |
| 2079 | + let mut ranges = Vec::with_capacity(ids.len()); |
| 2080 | + for atom_id in ids { |
| 2081 | + let atom = atom_table.get(*atom_id); |
| 2082 | + ranges.push(AtomRange { |
| 2083 | + atom: canonical_atom(*atom_id, icf_redirects), |
| 2084 | + start: atom.input_offset, |
| 2085 | + end: atom.input_offset.saturating_add(atom.size), |
| 2086 | + }); |
| 2087 | + } |
| 2088 | + ranges.sort_by(|lhs, rhs| { |
| 2089 | + lhs.start |
| 2090 | + .cmp(&rhs.start) |
| 2091 | + .then_with(|| lhs.end.cmp(&rhs.end)) |
| 2092 | + }); |
| 2093 | + out.insert(key, ranges); |
| 2094 | + } |
| 2095 | + out |
| 2096 | +} |
| 2097 | + |
| 2098 | +fn find_containing_atom( |
| 2099 | + atom_ranges: &AtomRangeIndex, |
| 2040 | 2100 | input_id: InputId, |
| 2041 | 2101 | input_section: u8, |
| 2042 | 2102 | offset: u32, |
| 2043 | | - icf_redirects: Option<&HashMap<crate::resolve::AtomId, crate::resolve::AtomId>>, |
| 2044 | 2103 | ) -> Option<(crate::resolve::AtomId, u32)> { |
| 2045 | | - find_containing_atom_range( |
| 2046 | | - atom_table, |
| 2047 | | - atoms_by_input_section, |
| 2048 | | - input_id, |
| 2049 | | - input_section, |
| 2050 | | - offset, |
| 2051 | | - 1, |
| 2052 | | - icf_redirects, |
| 2053 | | - ) |
| 2104 | + find_containing_atom_range(atom_ranges, input_id, input_section, offset, 1) |
| 2054 | 2105 | } |
| 2055 | 2106 | |
| 2056 | 2107 | fn find_containing_atom_range( |
| 2057 | | - atom_table: &AtomTable, |
| 2058 | | - atoms_by_input_section: &HashMap<(InputId, u8), Vec<crate::resolve::AtomId>>, |
| 2108 | + atom_ranges: &AtomRangeIndex, |
| 2059 | 2109 | input_id: InputId, |
| 2060 | 2110 | input_section: u8, |
| 2061 | 2111 | offset: u32, |
| 2062 | 2112 | len: u32, |
| 2063 | | - icf_redirects: Option<&HashMap<crate::resolve::AtomId, crate::resolve::AtomId>>, |
| 2064 | 2113 | ) -> Option<(crate::resolve::AtomId, u32)> { |
| 2065 | | - atoms_by_input_section |
| 2066 | | - .get(&(input_id, input_section)) |
| 2067 | | - .and_then(|ids| { |
| 2068 | | - ids.iter().find_map(|atom_id| { |
| 2069 | | - let atom = atom_table.get(*atom_id); |
| 2070 | | - let start = atom.input_offset; |
| 2071 | | - let end = atom.input_offset.saturating_add(atom.size); |
| 2072 | | - let range_end = offset.checked_add(len)?; |
| 2073 | | - (start <= offset && range_end <= end) |
| 2074 | | - .then_some((canonical_atom(*atom_id, icf_redirects), offset - start)) |
| 2075 | | - }) |
| 2076 | | - }) |
| 2114 | + let ranges = atom_ranges.get(&(input_id, input_section))?; |
| 2115 | + let range_end = offset.checked_add(len)?; |
| 2116 | + let idx = ranges.partition_point(|range| range.start <= offset); |
| 2117 | + let range = idx.checked_sub(1).and_then(|idx| ranges.get(idx))?; |
| 2118 | + (range.start <= offset && range_end <= range.end).then_some((range.atom, offset - range.start)) |
| 2077 | 2119 | } |
| 2078 | 2120 | |
| 2079 | 2121 | fn canonical_atom( |