|
12 | 12 | #include "ExportTrie.h"
|
13 | 13 | #include "InputFiles.h"
|
14 | 14 | #include "MachOStructs.h"
|
| 15 | +#include "ObjC.h" |
15 | 16 | #include "OutputSegment.h"
|
16 | 17 | #include "SymbolTable.h"
|
17 | 18 | #include "Symbols.h"
|
@@ -1974,6 +1975,234 @@ void InitOffsetsSection::setUp() {
|
1974 | 1975 | }
|
1975 | 1976 | }
|
1976 | 1977 |
|
| 1978 | +ObjCMethListSection::ObjCMethListSection() |
| 1979 | + : SyntheticSection(segment_names::text, section_names::objcMethList) { |
| 1980 | + flags = S_ATTR_NO_DEAD_STRIP; |
| 1981 | + align = m_align; |
| 1982 | +} |
| 1983 | + |
| 1984 | +// Go through all input method lists and ensure that we have selrefs for all |
| 1985 | +// their method names. The selrefs will be needed later by ::writeTo. We need to |
| 1986 | +// create them early on here to ensure they are processed correctly by the lld |
| 1987 | +// pipeline. |
| 1988 | +void ObjCMethListSection::setUp() { |
| 1989 | + for (const ConcatInputSection *isec : inputs) { |
| 1990 | + uint32_t structSizeAndFlags = 0, structCount = 0; |
| 1991 | + readMethodListHeader(isec->data.data(), structSizeAndFlags, structCount); |
| 1992 | + uint32_t structSize = structSizeAndFlags & m_structSizeMask; |
| 1993 | + |
| 1994 | + // Method name is immediately after header |
| 1995 | + uint32_t methodNameOff = m_methodListHeaderSize; |
| 1996 | + |
| 1997 | + // Loop through all methods, and ensure a selref for each of them exists. |
| 1998 | + while (methodNameOff < isec->data.size()) { |
| 1999 | + const Reloc *reloc = isec->getRelocAt(methodNameOff); |
| 2000 | + assert(reloc && "Relocation expected at method list name slot"); |
| 2001 | + auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>()); |
| 2002 | + assert(def && "Expected valid Defined at method list name slot"); |
| 2003 | + auto *cisec = cast<CStringInputSection>(def->isec); |
| 2004 | + assert(cisec && "Expected method name to be in a CStringInputSection"); |
| 2005 | + auto methname = cisec->getStringRefAtOffset(def->value); |
| 2006 | + if (!in.objcSelRefs->getSelRef(methname)) |
| 2007 | + in.objcSelRefs->makeSelRef(methname); |
| 2008 | + |
| 2009 | + // Jump to method name offset in next struct |
| 2010 | + methodNameOff += structSize; |
| 2011 | + } |
| 2012 | + } |
| 2013 | +} |
| 2014 | + |
| 2015 | +// Calculate section size and final offsets for where InputSection's need to be |
| 2016 | +// written. |
| 2017 | +void ObjCMethListSection::finalize() { |
| 2018 | + // m_size will be the total size of the __objc_methlist section |
| 2019 | + m_size = 0; |
| 2020 | + for (ConcatInputSection *isec : inputs) { |
| 2021 | + // We can also use m_size as write offset for isec |
| 2022 | + assert(m_size == alignToPowerOf2(m_size, m_align) && |
| 2023 | + "expected __objc_methlist to be aligned by default with the " |
| 2024 | + "required section alignment"); |
| 2025 | + isec->outSecOff = m_size; |
| 2026 | + |
| 2027 | + isec->isFinal = true; |
| 2028 | + uint32_t relativeListSize = |
| 2029 | + methodListSizeToRelativeMethodListSize(isec->data.size()); |
| 2030 | + m_size += relativeListSize; |
| 2031 | + |
| 2032 | + // If encoding the method list in relative offset format shrinks the size, |
| 2033 | + // then we also need to adjust symbol sizes to match the new size. Note that |
| 2034 | + // on 32bit platforms the size of the method list will remain the same when |
| 2035 | + // encoded in relative offset format. |
| 2036 | + if (relativeListSize != isec->data.size()) { |
| 2037 | + for (Symbol *sym : isec->symbols) { |
| 2038 | + assert(isa<Defined>(sym) && |
| 2039 | + "Unexpected undefined symbol in ObjC method list"); |
| 2040 | + auto *def = cast<Defined>(sym); |
| 2041 | + // There can be 0-size symbols, check if this is the case and ignore |
| 2042 | + // them. |
| 2043 | + if (def->size) { |
| 2044 | + assert( |
| 2045 | + def->size == isec->data.size() && |
| 2046 | + "Invalid ObjC method list symbol size: expected symbol size to " |
| 2047 | + "match isec size"); |
| 2048 | + def->size = relativeListSize; |
| 2049 | + } |
| 2050 | + } |
| 2051 | + } |
| 2052 | + } |
| 2053 | +} |
| 2054 | + |
| 2055 | +void ObjCMethListSection::writeTo(uint8_t *bufStart) const { |
| 2056 | + uint8_t *buf = bufStart; |
| 2057 | + for (const ConcatInputSection *isec : inputs) { |
| 2058 | + assert(buf - bufStart == long(isec->outSecOff) && |
| 2059 | + "Writing at unexpected offset"); |
| 2060 | + uint32_t writtenSize = writeRelativeMethodList(isec, buf); |
| 2061 | + buf += writtenSize; |
| 2062 | + } |
| 2063 | + assert(buf - bufStart == m_size && |
| 2064 | + "Written size does not match expected section size"); |
| 2065 | +} |
| 2066 | + |
| 2067 | +// Check if an InputSection is a method list. To do this we scan the |
| 2068 | +// InputSection for any symbols who's names match the patterns we expect clang |
| 2069 | +// to generate for method lists. |
| 2070 | +bool ObjCMethListSection::isMethodList(const ConcatInputSection *isec) { |
| 2071 | + const char *symPrefixes[] = {objc::symbol_names::classMethods, |
| 2072 | + objc::symbol_names::instanceMethods, |
| 2073 | + objc::symbol_names::categoryInstanceMethods, |
| 2074 | + objc::symbol_names::categoryClassMethods}; |
| 2075 | + if (!isec) |
| 2076 | + return false; |
| 2077 | + for (const Symbol *sym : isec->symbols) { |
| 2078 | + auto *def = dyn_cast_or_null<Defined>(sym); |
| 2079 | + if (!def) |
| 2080 | + continue; |
| 2081 | + for (const char *prefix : symPrefixes) { |
| 2082 | + if (def->getName().starts_with(prefix)) { |
| 2083 | + assert(def->size == isec->data.size() && |
| 2084 | + "Invalid ObjC method list symbol size: expected symbol size to " |
| 2085 | + "match isec size"); |
| 2086 | + assert(def->value == 0 && |
| 2087 | + "Offset of ObjC method list symbol must be 0"); |
| 2088 | + return true; |
| 2089 | + } |
| 2090 | + } |
| 2091 | + } |
| 2092 | + |
| 2093 | + return false; |
| 2094 | +} |
| 2095 | + |
| 2096 | +// Encode a single relative offset value. The input is the data/symbol at |
| 2097 | +// (&isec->data[inSecOff]). The output is written to (&buf[outSecOff]). |
| 2098 | +// 'createSelRef' indicates that we should not directly use the specified |
| 2099 | +// symbol, but instead get the selRef for the symbol and use that instead. |
| 2100 | +void ObjCMethListSection::writeRelativeOffsetForIsec( |
| 2101 | + const ConcatInputSection *isec, uint8_t *buf, uint32_t &inSecOff, |
| 2102 | + uint32_t &outSecOff, bool useSelRef) const { |
| 2103 | + const Reloc *reloc = isec->getRelocAt(inSecOff); |
| 2104 | + assert(reloc && "Relocation expected at __objc_methlist Offset"); |
| 2105 | + auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>()); |
| 2106 | + assert(def && "Expected all syms in __objc_methlist to be defined"); |
| 2107 | + uint32_t symVA = def->getVA(); |
| 2108 | + |
| 2109 | + if (useSelRef) { |
| 2110 | + auto *cisec = cast<CStringInputSection>(def->isec); |
| 2111 | + auto methname = cisec->getStringRefAtOffset(def->value); |
| 2112 | + ConcatInputSection *selRef = in.objcSelRefs->getSelRef(methname); |
| 2113 | + assert(selRef && "Expected all selector names to already be already be " |
| 2114 | + "present in __objc_selrefs"); |
| 2115 | + symVA = selRef->getVA(); |
| 2116 | + assert(selRef->data.size() == sizeof(target->wordSize) && |
| 2117 | + "Expected one selref per ConcatInputSection"); |
| 2118 | + } |
| 2119 | + |
| 2120 | + uint32_t currentVA = isec->getVA() + outSecOff; |
| 2121 | + uint32_t delta = symVA - currentVA; |
| 2122 | + write32le(buf + outSecOff, delta); |
| 2123 | + |
| 2124 | + inSecOff += target->wordSize; |
| 2125 | + outSecOff += sizeof(uint32_t); |
| 2126 | +} |
| 2127 | + |
| 2128 | +// Write a relative method list to buf, return the size of the written |
| 2129 | +// information |
| 2130 | +uint32_t |
| 2131 | +ObjCMethListSection::writeRelativeMethodList(const ConcatInputSection *isec, |
| 2132 | + uint8_t *buf) const { |
| 2133 | + // Copy over the header, and add the "this is a relative method list" magic |
| 2134 | + // value flag |
| 2135 | + uint32_t structSizeAndFlags = 0, structCount = 0; |
| 2136 | + readMethodListHeader(isec->data.data(), structSizeAndFlags, structCount); |
| 2137 | + structSizeAndFlags |= m_relMethodHeaderFlag; |
| 2138 | + writeMethodListHeader(buf, structSizeAndFlags, structCount); |
| 2139 | + |
| 2140 | + assert(m_methodListHeaderSize + |
| 2141 | + (structCount * m_pointersPerStruct * target->wordSize) == |
| 2142 | + isec->data.size() && |
| 2143 | + "Invalid computed ObjC method list size"); |
| 2144 | + |
| 2145 | + uint32_t inSecOff = m_methodListHeaderSize; |
| 2146 | + uint32_t outSecOff = m_methodListHeaderSize; |
| 2147 | + |
| 2148 | + // Go through the method list and encode input absolute pointers as relative |
| 2149 | + // offsets. writeRelativeOffsetForIsec will be incrementing inSecOff and |
| 2150 | + // outSecOff |
| 2151 | + for (uint32_t i = 0; i < structCount; i++) { |
| 2152 | + // Write the name of the method |
| 2153 | + writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, true); |
| 2154 | + // Write the type of the method |
| 2155 | + writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, false); |
| 2156 | + // Write reference to the selector of the method |
| 2157 | + writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, false); |
| 2158 | + } |
| 2159 | + |
| 2160 | + // Expecting to have read all the data in the isec |
| 2161 | + assert(inSecOff == isec->data.size() && |
| 2162 | + "Invalid actual ObjC method list size"); |
| 2163 | + assert( |
| 2164 | + outSecOff == methodListSizeToRelativeMethodListSize(inSecOff) && |
| 2165 | + "Mismatch between input & output size when writing relative method list"); |
| 2166 | + return outSecOff; |
| 2167 | +} |
| 2168 | + |
| 2169 | +// Given the size of an ObjC method list InputSection, return the size of the |
| 2170 | +// method list when encoded in relative offsets format. We can do this without |
| 2171 | +// decoding the actual data, as it can be directly infered from the size of the |
| 2172 | +// isec. |
| 2173 | +uint32_t |
| 2174 | +ObjCMethListSection::methodListSizeToRelativeMethodListSize(uint32_t iSecSize) { |
| 2175 | + uint32_t oldPointersSize = iSecSize - m_methodListHeaderSize; |
| 2176 | + uint32_t pointerCount = oldPointersSize / target->wordSize; |
| 2177 | + assert(((pointerCount % m_pointersPerStruct) == 0) && |
| 2178 | + "__objc_methlist expects method lists to have multiple-of-3 pointers"); |
| 2179 | + |
| 2180 | + constexpr uint32_t sizeOfRelativeOffset = sizeof(uint32_t); |
| 2181 | + uint32_t newPointersSize = pointerCount * sizeOfRelativeOffset; |
| 2182 | + uint32_t newTotalSize = m_methodListHeaderSize + newPointersSize; |
| 2183 | + |
| 2184 | + assert((newTotalSize <= iSecSize) && |
| 2185 | + "Expected relative method list size to be smaller or equal than " |
| 2186 | + "original size"); |
| 2187 | + return newTotalSize; |
| 2188 | +} |
| 2189 | + |
| 2190 | +// Read a method list header from buf |
| 2191 | +void ObjCMethListSection::readMethodListHeader(const uint8_t *buf, |
| 2192 | + uint32_t &structSizeAndFlags, |
| 2193 | + uint32_t &structCount) { |
| 2194 | + structSizeAndFlags = read32le(buf); |
| 2195 | + structCount = read32le(buf + sizeof(uint32_t)); |
| 2196 | +} |
| 2197 | + |
| 2198 | +// Write a method list header to buf |
| 2199 | +void ObjCMethListSection::writeMethodListHeader(uint8_t *buf, |
| 2200 | + uint32_t structSizeAndFlags, |
| 2201 | + uint32_t structCount) { |
| 2202 | + write32le(buf, structSizeAndFlags); |
| 2203 | + write32le(buf + sizeof(structSizeAndFlags), structCount); |
| 2204 | +} |
| 2205 | + |
1977 | 2206 | void macho::createSyntheticSymbols() {
|
1978 | 2207 | auto addHeaderSymbol = [](const char *name) {
|
1979 | 2208 | symtab->addSynthetic(name, in.header->isec, /*value=*/0,
|
|
0 commit comments