]> git.saurik.com Git - apple/dyld.git/blob - interlinked-dylibs/MachOProxy.cpp
e1923f80b72f3771cc0f20c9b8826cbe1fce32ff
[apple/dyld.git] / interlinked-dylibs / MachOProxy.cpp
1 //
2 // DylibProxy.cpp
3 // dyld
4 //
5 // Created by Louis Gerbarg on 1/27/16.
6 //
7 //
8
9 #include <mach-o/loader.h>
10 #include <mach-o/fat.h>
11
12 #include "mega-dylib-utils.h"
13 #include "Logging.h"
14
15 #include "MachOProxy.h"
16
17 namespace {
18 std::map<std::string, MachOProxy*> mapMachOFile( const std::string& path ) {
19 std::map<std::string, MachOProxy*> retval;
20 const uint8_t* p = (uint8_t*)( -1 );
21 struct stat stat_buf;
22 bool rootless;
23
24 std::tie( p, stat_buf, rootless ) = fileCache.cacheLoad( path );
25
26 if ( p == (uint8_t*)( -1 ) ) {
27 return retval;
28 }
29
30 // if fat file, process each architecture
31 const fat_header* fh = (fat_header*)p;
32 const mach_header* mh = (mach_header*)p;
33 if ( OSSwapBigToHostInt32( fh->magic ) == FAT_MAGIC ) {
34 // Fat header is always big-endian
35 const fat_arch* slices = (const fat_arch*)( (char*)fh + sizeof( fat_header ) );
36 const uint32_t sliceCount = OSSwapBigToHostInt32( fh->nfat_arch );
37 for ( uint32_t i = 0; i < sliceCount; ++i ) {
38 // FIXME Should we validate the fat header matches the slices?
39 ArchPair arch( OSSwapBigToHostInt32( slices[i].cputype ), OSSwapBigToHostInt32( slices[i].cpusubtype ) );
40 uint32_t fileOffset = OSSwapBigToHostInt32( slices[i].offset );
41 const mach_header* th = (mach_header*)(p+fileOffset);
42 if ( ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( th->magic ) == MH_MAGIC_64 ) ) {
43 uint32_t fileSize = static_cast<uint32_t>( stat_buf.st_size );
44 retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless );
45 }
46 }
47 } else if ( ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC ) || ( OSSwapLittleToHostInt32( mh->magic ) == MH_MAGIC_64 ) ) {
48 ArchPair arch( OSSwapLittleToHostInt32( mh->cputype ), OSSwapLittleToHostInt32( mh->cpusubtype ) );
49 uint32_t fileOffset = OSSwapBigToHostInt32( 0 );
50 uint32_t fileSize = static_cast<uint32_t>( stat_buf.st_size );
51 retval[stringForArch( arch )] = new MachOProxy( path, stat_buf.st_ino, stat_buf.st_mtime, fileOffset, fileSize, rootless );
52 } else {
53 // warning( "file '%s' is not contain requested a MachO", path.c_str() );
54 }
55 return retval;
56 }
57 }
58
59 template <typename P>
60 std::string MachOProxy::machoParser(bool ignoreUncacheableDylibsInExecutables)
61 {
62 const uint8_t* buffer = getBuffer();
63 bool hasSplitSegInfo = false;
64 bool hasDylidInfo = false;
65 const macho_header<P>* mh = (const macho_header<P>*)buffer;
66 const macho_symtab_command<P>* symTab = nullptr;
67 const macho_dysymtab_command<P>* dynSymTab = nullptr;
68 const macho_load_command<P>* const cmds = (macho_load_command<P>*)((uint8_t*)mh + sizeof(macho_header<P>));
69 const uint32_t cmd_count = mh->ncmds();
70 const macho_load_command<P>* cmd = cmds;
71 _dylib = (mh->filetype() == MH_DYLIB);
72 _executable = (mh->filetype() == MH_EXECUTE);
73 if (mh->filetype() == MH_DYLIB_STUB) {
74 return "stub dylib";
75 }
76 for (uint32_t i = 0; i < cmd_count; ++i) {
77 switch (cmd->cmd()) {
78 case LC_ID_DYLIB: {
79 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
80 if (dylib->name()[0] != '/') {
81 if (strncmp(dylib->name(), "@rpath", 6) == 0)
82 return "@rpath cannot be used in -install_name for OS dylibs";
83 else
84 return "-install_name is not an absolute path";
85 }
86 installName = dylib->name();
87 installNameOffsetInTEXT = (uint32_t)((uint8_t*)cmd - buffer) + dylib->name_offset();
88 } break;
89 case LC_UUID: {
90 const macho_uuid_command<P>* uuidCmd = (macho_uuid_command<P>*)cmd;
91 ::memcpy(uuid, uuidCmd->uuid(), sizeof(uuid_t));
92 } break;
93 case LC_LOAD_DYLIB:
94 case LC_LOAD_WEAK_DYLIB:
95 case LC_REEXPORT_DYLIB:
96 case LC_LOAD_UPWARD_DYLIB: {
97 macho_dylib_command<P>* dylib = (macho_dylib_command<P>*)cmd;
98 std::string depName = dylib->name();
99 if ( _executable && ignoreUncacheableDylibsInExecutables && !has_prefix(depName, "/usr/lib/") && !has_prefix(depName, "/System/Library/") ) {
100 // <rdar://problem/25918268> in update_dyld_shared_cache don't warn if root executable links with something not eligible for shared cache
101 break;
102 }
103 else if ( depName[0] != '/' ) {
104 return "linked against a dylib whose -install_name was non-absolute (e.g. @rpath)";
105 }
106 dependencies.insert(depName);
107 } break;
108 case macho_segment_command<P>::CMD: {
109 const macho_segment_command<P>* segCmd = (macho_segment_command<P>*)cmd;
110 MachOProxy::Segment seg;
111 seg.name = segCmd->segname();
112 seg.size = align(segCmd->vmsize(), 12);
113 seg.diskSize = (uint32_t)segCmd->filesize();
114 seg.fileOffset = (uint32_t)segCmd->fileoff();
115 seg.protection = segCmd->initprot();
116 if (segCmd->nsects() > 0) {
117 seg.p2align = 0;
118 const macho_section<P>* const sectionsStart = (macho_section<P>*)((uint8_t*)segCmd + sizeof(macho_segment_command<P>));
119 const macho_section<P>* const sectionsLast = &sectionsStart[segCmd->nsects() - 1];
120 const macho_section<P>* const sectionsEnd = &sectionsStart[segCmd->nsects()];
121 for (const macho_section<P>* sect = sectionsStart; sect < sectionsEnd; ++sect) {
122 if (sect->align() > seg.p2align)
123 seg.p2align = sect->align();
124 }
125 seg.sizeOfSections = sectionsLast->addr() + sectionsLast->size() - segCmd->vmaddr();
126 } else {
127 seg.p2align = 12;
128 }
129 segments.push_back(seg);
130 } break;
131 case LC_SEGMENT_SPLIT_INFO:
132 hasSplitSegInfo = true;
133 break;
134 case LC_SYMTAB:
135 symTab = (macho_symtab_command<P>*)cmd;
136 break;
137 case LC_DYSYMTAB:
138 dynSymTab = (macho_dysymtab_command<P>*)cmd;
139 break;
140 case LC_DYLD_INFO:
141 case LC_DYLD_INFO_ONLY:
142 hasDylidInfo = true;
143 break;
144 }
145 cmd = (const macho_load_command<P>*)(((uint8_t*)cmd) + cmd->cmdsize());
146 }
147
148 if (!_dylib) {
149 return "";
150 }
151
152 if (!hasDylidInfo) {
153 return "built for old OS";
154 }
155
156 if ((mh->flags() & MH_TWOLEVEL) == 0) {
157 return "built with -flat_namespace";
158 }
159
160 if (!hasSplitSegInfo) {
161 bool inUsrLib = (installName.size() > 9) && (installName.substr(0, 9) == "/usr/lib/");
162 bool inSystemLibrary = (installName.size() > 16) && (installName.substr(0, 16) == "/System/Library/");
163 if (!inUsrLib && !inSystemLibrary) {
164 return "-install_name not /usr/lib/* or /System/Library/*";
165 }
166 return "no shared region info";
167 }
168
169 if ((symTab == nullptr) && (dynSymTab == nullptr)) {
170 return "no symbol table";
171 }
172
173 if (installName.empty()) {
174 return "dylib missing install name";
175 }
176
177 // scan undefines looking for invalid ordinals
178 const macho_nlist<P>* symbolTable = (macho_nlist<P>*)((uint8_t*)mh + symTab->symoff());
179 const uint32_t startUndefs = dynSymTab->iundefsym();
180 const uint32_t endUndefs = startUndefs + dynSymTab->nundefsym();
181 for (uint32_t i = startUndefs; i < endUndefs; ++i) {
182 uint8_t ordinal = GET_LIBRARY_ORDINAL(symbolTable[i].n_desc());
183 if (ordinal == DYNAMIC_LOOKUP_ORDINAL) {
184 return "built with '-undefined dynamic_lookup'";
185 } else if (ordinal == EXECUTABLE_ORDINAL) {
186 return "built with -bundle_loader";
187 }
188 }
189
190 return "";
191 }
192
193 const bool MachOProxy::isDylib()
194 {
195 return _dylib;
196 }
197
198 const bool MachOProxy::isExecutable()
199 {
200 return _executable;
201 }
202
203 std::map<std::string, MachOProxy*> MachOProxy::findDylibInfo(const std::string& path, bool warnOnProblems, bool ignoreUncacheableDylibsInExecutables) {
204 std::map<std::string, MachOProxy*> slices = mapMachOFile( path );
205 std::vector<std::string> errorSlices;
206
207 for ( auto& slice : slices ) {
208 std::string errorMessage;
209 verboseLog( "analyzing file '%s'", path.c_str() );
210 switch ( archForString( slice.first ).arch ) {
211 case CPU_TYPE_ARM:
212 case CPU_TYPE_I386:
213 errorMessage = slice.second->machoParser<Pointer32<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
214 break;
215 case CPU_TYPE_X86_64:
216 case CPU_TYPE_ARM64:
217 errorMessage = slice.second->machoParser<Pointer64<LittleEndian>>(ignoreUncacheableDylibsInExecutables);
218 break;
219 default:
220 errorMessage = "unsupported arch '" + slice.first + "'";
221 break;
222 }
223
224 if ( !errorMessage.empty() ) {
225 if ( warnOnProblems )
226 warning( "%s (%s)", errorMessage.c_str(), path.c_str() );
227 errorSlices.push_back( slice.first );
228 }
229 }
230
231 for ( const auto& slice : errorSlices ) {
232 slices.erase( slice );
233 }
234
235 return slices;
236 }
237
238 const uint8_t* MachOProxy::getBuffer() {
239 const uint8_t* p = (uint8_t*)( -1 );
240 struct stat stat_buf;
241 bool rootless;
242 std::tie(p, stat_buf,rootless) = fileCache.cacheLoad(path);
243 return p + fatFileOffset;
244 }
245
246 bool MachOProxy::addAlias( const std::string& alias ) {
247 if (!has_prefix(alias, "/usr/lib/") && !has_prefix(alias, "/System/Library/"))
248 return false;
249 if ( alias != installName && installNameAliases.count( alias ) == 0 ) {
250 installNameAliases.insert( alias );
251 return true;
252 }
253 return false;
254 }