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