comparison dynload/dynload_syms_mach-o.c @ 219:912efa3cfd4b

- mach-o symbol iteration stability fixes
author Tassilo Philipp
date Tue, 11 Apr 2017 09:37:14 +0200
parents 87c695673522
children 94dc0bdd7dbe
comparison
equal deleted inserted replaced
218:cb56f077fd37 219:912efa3cfd4b
1 /* 1 /*
2 2
3 Package: dyncall 3 Package: dyncall
4 Library: dynload 4 Library: dynload
5 File: dynload/dynload_syms_mach-o.c 5 File: dynload/dynload_syms_mach-o.c
6 Description: 6 Description:
7 License: 7 License:
8 8
9 Copyright (c) 2007-2015 Olivier Chafik <olivier.chafik@gmail.com>, 9 Copyright (c) 2007-2015 Olivier Chafik <olivier.chafik@gmail.com>,
10 2017 refactored for stability, API consistency 10 2017 refactored completely for stability, API
11 and portability by Tassilo Philipp. 11 consistency and portability by Tassilo Philipp.
12 12
13 Permission to use, copy, modify, and distribute this software for any 13 Permission to use, copy, modify, and distribute this software for any
14 purpose with or without fee is hereby granted, provided that the above 14 purpose with or without fee is hereby granted, provided that the above
15 copyright notice and this permission notice appear in all copies. 15 copyright notice and this permission notice appear in all copies.
16 16
25 */ 25 */
26 26
27 27
28 28
29 /* 29 /*
30 30
31 dynamic symbol resolver for Mach-O 31 dynamic symbol resolver for Mach-O
32 32
33 */ 33 */
34 34
35 #include "dynload.h" 35 #include "dynload.h"
42 #include <dlfcn.h> 42 #include <dlfcn.h>
43 #include <string.h> 43 #include <string.h>
44 44
45 #if defined(ARCH_X64) || defined(ARCH_PPC64) || defined(ARCH_ARM64) //@@@ use dyncall_macros.h 45 #if defined(ARCH_X64) || defined(ARCH_PPC64) || defined(ARCH_ARM64) //@@@ use dyncall_macros.h
46 #define MACH_HEADER_TYPE mach_header_64 46 #define MACH_HEADER_TYPE mach_header_64
47 #define SEGMENT_COMMAND segment_command_64
47 #define NLIST_TYPE nlist_64 48 #define NLIST_TYPE nlist_64
48 #else 49 #else
49 #define MACH_HEADER_TYPE mach_header 50 #define MACH_HEADER_TYPE mach_header
50 #define NLIST_TYPE nlist 51 #define NLIST_TYPE nlist
52 #define SEGMENT_COMMAND segment_command
51 #endif 53 #endif
52 54
53 55
54 struct DLSyms_ 56 struct DLSyms_
55 { 57 {
62 64
63 DLSyms* dlSymsInit(const char* libPath) 65 DLSyms* dlSymsInit(const char* libPath)
64 { 66 {
65 DLLib* pLib; 67 DLLib* pLib;
66 DLSyms* pSyms; 68 DLSyms* pSyms;
67 uint32_t iImage, nImages; 69 uint32_t i, n;
68 struct stat st0; 70 struct stat st0;
71 const struct MACH_HEADER_TYPE* pHeader = NULL;
69 72
70 if(stat(libPath, &st0) == -1) 73 if(stat(libPath, &st0) == -1)
71 return NULL; 74 return NULL;
72 75
73 pLib = dlLoadLibrary(libPath); 76 pLib = dlLoadLibrary(libPath);
74 if(!pLib) 77 if(!pLib)
75 return NULL; 78 return NULL;
76 79
77 // Loop over all dynamically linked images. 80 // Loop over all dynamically linked images to find ours.
78 for (iImage = 0, nImages = _dyld_image_count(); iImage < nImages; iImage++) 81 for(i = 0, n = _dyld_image_count(); i < n; ++i)
79 { 82 {
80 struct stat st1; 83 struct stat st1;
81 const char* name = _dyld_get_image_name(iImage); 84 const char* name = _dyld_get_image_name(i);
82 85
83 if(name && (stat(name, &st1) != -1)) 86 if(name && (stat(name, &st1) != -1))
84 { 87 {
85 // Don't rely on name comparison alone, as libPath might be relative, symlink, differently 88 // Don't rely on name comparison alone, as libPath might be relative, symlink, differently
86 // cased, etc., but compare inode number with the one of the mapped dyld image. 89 // cased, etc., but compare inode number with the one of the mapped dyld image.
87 if (st0.st_ino == st1.st_ino/*!strcmp(name, libPath)*/) 90 if(st0.st_ino == st1.st_ino/*!strcmp(name, libPath)*/)
88 { 91 {
89 const struct MACH_HEADER_TYPE* pHeader = (const struct MACH_HEADER_TYPE*) _dyld_get_image_header(iImage); 92 pHeader = (const struct MACH_HEADER_TYPE*) _dyld_get_image_header(i);
90 const char* pBase = ((const char*)pHeader); 93 break; // found header
91 if (pHeader->filetype != MH_DYLIB)
92 return NULL;
93 if (pHeader->flags & MH_SPLIT_SEGS)
94 return NULL;
95
96 if (pHeader)
97 {
98 uint32_t iCmd, nCmds = pHeader->ncmds;
99 const struct load_command* cmd = (const struct load_command*)(pBase + sizeof(struct MACH_HEADER_TYPE));
100
101 for (iCmd = 0; iCmd < nCmds; iCmd++)
102 {
103 if (cmd->cmd == LC_SYMTAB)
104 {
105 const struct symtab_command* scmd = (const struct symtab_command*)cmd;
106
107 pSyms = (DLSyms*)( dlAllocMem(sizeof(DLSyms)) );
108 pSyms->symbolCount = scmd->nsyms;
109 pSyms->pStringTable = pBase + scmd->stroff;
110 pSyms->pSymbolTable = (struct NLIST_TYPE*)(pBase + scmd->symoff);
111 pSyms->pLib = pLib;
112
113 return pSyms;
114 }
115 cmd = (const struct load_command*)(((char*)cmd) + cmd->cmdsize);
116 }
117 }
118 break;
119 } 94 }
120 } 95 }
121 } 96 }
97
98 if(pHeader && (pHeader->filetype == MH_DYLIB) && !(pHeader->flags & MH_SPLIT_SEGS))
99 {
100 const char* pBase = (const char*)pHeader;
101 uintptr_t slide = 0;
102 const struct load_command* cmd = (const struct load_command*)(pBase + sizeof(struct MACH_HEADER_TYPE));
103
104 for(i = 0, n = pHeader->ncmds; i < n; ++i, cmd = (const struct load_command*)((const char*)cmd + cmd->cmdsize))
105 {
106 if(cmd->cmd == LC_SEGMENT)
107 {
108 const struct SEGMENT_COMMAND* seg = (struct SEGMENT_COMMAND*)cmd;
109 if((seg->fileoff == 0) && (seg->filesize != 0)) // Count segment sizes to slide over...@@@?
110 slide = (uintptr_t)pHeader - seg->vmaddr;
111 if(strcmp(seg->segname, "__LINKEDIT") == 0)
112 pBase = (const char*)(seg->vmaddr - seg->fileoff + slide); // Adjust pBase depending on __LINKEDIT segment
113 }
114 else if(cmd->cmd == LC_SYMTAB)
115 {
116 const struct symtab_command* scmd = (const struct symtab_command*)cmd;
117
118 // cmd->cmdsize must be size of struct, otherwise something is off; abort
119 if(cmd->cmdsize != sizeof(struct symtab_command))
120 break;
121
122 pSyms = (DLSyms*)dlAllocMem(sizeof(DLSyms));
123 pSyms->symbolCount = scmd->nsyms;
124 pSyms->pStringTable = pBase + scmd->stroff;
125 pSyms->pSymbolTable = (struct NLIST_TYPE*)(pBase + scmd->symoff);
126 pSyms->pLib = pLib;
127
128 return pSyms;
129 }
130 //@@@ handle also LC_DYSYMTAB
131 }
132 }
133
134 // Couldn't init syms, so free lib and return error.
135 dlFreeLibrary(pLib);
122 return NULL; 136 return NULL;
123 } 137 }
124 138
125 139
126 void dlSymsCleanup(DLSyms* pSyms) 140 void dlSymsCleanup(DLSyms* pSyms)
134 int dlSymsCount(DLSyms* pSyms) 148 int dlSymsCount(DLSyms* pSyms)
135 { 149 {
136 return pSyms ? pSyms->symbolCount : 0; 150 return pSyms ? pSyms->symbolCount : 0;
137 } 151 }
138 152
139 static const struct NLIST_TYPE* get_nlist(DLSyms* pSyms, int index) 153
154 const char* dlSymsName(DLSyms* pSyms, int index)
140 { 155 {
141 const struct NLIST_TYPE* nl; 156 const struct NLIST_TYPE* nl;
142 if (!pSyms) 157 unsigned char t;
158
159 //@@@ mach-o ref: http://www.cilinder.be/docs/next/NeXTStep/3.3/nd/DevTools/14_MachO/MachO.htmld/index.html
160
161 if(!pSyms)
143 return NULL; 162 return NULL;
144 163
145 nl = pSyms->pSymbolTable + index; 164 nl = pSyms->pSymbolTable + index;
146 if (nl->n_un.n_strx <= 1) 165
147 return NULL; // would be empty string anyway 166 // Mach-O manual: Symbols with an index into the string table of zero
167 // (n_un.n_strx == 0) are defined to have a null ("") name.
168 if(nl->n_un.n_strx == 0)
169 return NULL; //@@@ have return pointer to some static "" string?
170
171 // Skip undefined symbols. @@@ should we?
172 t = nl->n_type & N_TYPE;
173 if(t == N_UNDF || t == N_PBUD) // @@@ check if N_PBUD is defined, it's not in the NeXT manual, but on Darwin 8.0.1
174 return NULL;
148 175
149 //TODO skip more symbols based on nl->n_desc and nl->n_type ? 176 //TODO skip more symbols based on nl->n_desc and nl->n_type ?
150 return nl;
151 }
152 177
153 178 // Return name - handles lookup of indirect names.
154 const char* dlSymsName(DLSyms* pSyms, int index) 179 return &pSyms->pStringTable[t == N_INDR ? nl->n_value : nl->n_un.n_strx];
155 {
156 const struct NLIST_TYPE* nl = get_nlist(pSyms, index);
157 return nl ? pSyms->pStringTable + nl->n_un.n_strx : NULL;
158 } 180 }
159 181
160 182
161 const char* dlSymsNameFromValue(DLSyms* pSyms, void* value) 183 const char* dlSymsNameFromValue(DLSyms* pSyms, void* value)
162 { 184 {