-
Notifications
You must be signed in to change notification settings - Fork 65
/
VMProfileLinuxSupportPlugin.class.st
195 lines (184 loc) · 8.32 KB
/
VMProfileLinuxSupportPlugin.class.st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
"
This class provides support for the VMProfiler on Linux (at least linux versions that provide dl_iterate_phdr). This support is for reading symbols from the executable. We can use the OS's nm command to list symbols in the VM executable and loaded libraries. To do this we need to know what libraries are loaded, not simply which libraries are linked against, since some libraries are loaded dynamically, and for each loaded library what the vm address relocation, if any, is for the loaded library.
Further, we need to know the actual addresses in memory of symbols in the program and in memory. Since the OS may be using address randomization we cannot assume that the addresses answered by nm for symbols in the program and/or libraries match the addresses of the same symbols in memory. Instead we must correlate. dlsym can be used to look up symbols in loaded dynamic load libraries, but it does _not_ answer the addresses of symbols in the main program. Back in the day nlist could be used to do this, but it is no more. Instead this module contains a reference to interpret and answers its address ia a primitive (alas this means the plugin must be internal, but it's extremely small, and the VM links against libdl.so anyway). Any address space shift will therefore be the difference between nm's output for interpret and the primitive's value. We can similarly compute the address shift for libraries by using dlsym to lookup a symbol in a library and comparing it to nm's output for the library.
The primitive primitiveExecutableModules returns the names of the executable and the loaded libraries.
"
Class {
#name : #VMProfileLinuxSupportPlugin,
#superclass : #InterpreterPlugin,
#instVars : [
'numModules',
'primErr'
],
#category : #'VMMaker-Plugins'
}
{ #category : #translation }
VMProfileLinuxSupportPlugin class >> declareHeaderFilesIn: cg [
cg
addHeaderFile: '<limits.h>';
addHeaderFile: '#if defined(HAVE_DLFCN_H)\# include <dlfcn.h>\#endif' withCRs;
addHeaderFile: '<link.h>';
addHeaderFile: '#ifndef RTLD_NODELETE\# define RTLD_NODELETE 0\#endif' withCRs
]
{ #category : #'iteration callbacks' }
VMProfileLinuxSupportPlugin >> count: info num: size modules: ignored [
<returnTypeC: #int>
<var: #info type: #'struct dl_phdr_info *'>
<var: #size type: #'size_t'>
<var: #ignored type: #'void *'>
numModules := numModules + 1.
^0
]
{ #category : #primitives }
VMProfileLinuxSupportPlugin >> primitiveDLSymInLibrary [
"Answer the address of the symbol whose name is the first argument
in the library whose name is the second argument, or nil if none."
| nameObj symName libName lib sz addr ok |
<export: true>
<var: #symName type: #'char *'>
<var: #libName type: #'char *'>
<var: #lib type: #'void *'>
<var: #addr type: #'void *'>
nameObj := interpreterProxy stackValue: 0.
(interpreterProxy isBytes: nameObj) ifFalse:
[^interpreterProxy primitiveFailFor: PrimErrBadArgument].
sz := interpreterProxy byteSizeOf: nameObj.
libName := self malloc: sz+1.
self strncpy: libName _: (interpreterProxy firstIndexableField: nameObj) _: sz.
libName at: sz put: 0.
nameObj := interpreterProxy stackValue: 1.
(interpreterProxy isBytes: nameObj) ifFalse:
[self free: libName.
^interpreterProxy primitiveFailFor: PrimErrBadArgument].
sz := interpreterProxy byteSizeOf: nameObj.
symName := self malloc: sz+1.
self strncpy: symName _: (interpreterProxy firstIndexableField: nameObj) _: sz.
symName at: sz put: 0.
lib := self dl: libName open: (#'RTLD_LAZY' bitOr: #'RTLD_NODELETE').
lib ifNil:
[self free: libName; free: symName.
^interpreterProxy primitiveFailFor: PrimErrInappropriate].
self dlerror. "clear dlerror"
addr := self dl: lib sym: symName.
ok := self dlerror isNil.
self free: symName.
self free: libName.
self dlclose: lib.
ok ifFalse:
[^interpreterProxy primitiveFailFor: PrimErrNotFound].
^interpreterProxy methodReturnValue: (interpreterProxy positiveMachineIntegerFor: addr asUnsignedIntegerPtr)
]
{ #category : #primitives }
VMProfileLinuxSupportPlugin >> primitiveExecutableModules [
"Answer an Array of pairs of strings for executable modules (the VM executable and loaded libraries).
The first element in each pair is the filename of the module. The second element is either nil or
the symlink's target, if the filename is a symlink."
<export: true>
| resultObj |
numModules := 0.
self cCode: 'dl_iterate_phdr(countnummodules,0)' inSmalltalk: [0].
resultObj := interpreterProxy
instantiateClass: interpreterProxy classArray
indexableSize: numModules - 1 * 2. "skip the fake linux-gate.so.1"
resultObj = 0 ifTrue:
[^interpreterProxy primitiveFailFor: PrimErrNoMemory].
interpreterProxy pushRemappableOop: resultObj.
primErr := numModules := 0.
self cCode: 'dl_iterate_phdr(reapmodulesymlinks,0)' inSmalltalk: [0].
resultObj := interpreterProxy popRemappableOop.
primErr ~= 0 ifTrue:
[^interpreterProxy primitiveFailFor: primErr].
^interpreterProxy methodReturnValue: resultObj
]
{ #category : #primitives }
VMProfileLinuxSupportPlugin >> primitiveInterpretAddress [
"Answer the address of the interpret routine."
<export: true>
| interpret |
<var: #interpret declareC: 'extern void interpret()'>
^interpreterProxy methodReturnValue: (interpreterProxy positiveMachineIntegerFor: interpret asUnsignedIntegerPtr)
]
{ #category : #'iteration callbacks' }
VMProfileLinuxSupportPlugin >> reap: info module: size names: ignored [
<returnTypeC: #int>
| elfModuleName len moduleNameObj GetAttributeString |
<var: #info type: #'struct dl_phdr_info *'>
<var: 'elfModuleName' type: #'const char *'>
<var: #GetAttributeString declareC: 'extern char *GetAttributeString(sqInt)'>
<var: #size type: #'size_t'>
<var: #ignored type: #'void *'>
self touch: GetAttributeString.
elfModuleName := self cCode: 'numModules ? info->dlpi_name : GetAttributeString(0)'.
(elfModuleName isNil
or: [(len := self strlen: elfModuleName) = 0]) ifTrue:
[^0]. "skip the fake linux-gate.so.1"
moduleNameObj := interpreterProxy
instantiateClass: interpreterProxy classString
indexableSize: len.
moduleNameObj = 0 ifTrue:
[primErr := PrimErrNoMemory.
^1]. "stop iteration"
self strncpy: (interpreterProxy arrayValueOf: moduleNameObj)
_: elfModuleName
_: len. "(char *)strncpy()"
interpreterProxy
storePointer: numModules
ofObject: interpreterProxy topRemappableOop
withValue: moduleNameObj.
numModules := numModules + 1.
^0
]
{ #category : #'iteration callbacks' }
VMProfileLinuxSupportPlugin >> reap: info module: size symlinks: ignored [
"like reap:module:names:, but follows symlinks"
<returnTypeC: #int>
| elfModuleName len moduleNameObj GetAttributeString symLinkBuf |
<var: #info type: #'struct dl_phdr_info *'>
<var: 'elfModuleName' type: #'const char *'>
<var: #GetAttributeString declareC: 'extern char *GetAttributeString(sqInt)'>
<var: #symLinkBuf declareC: 'char symLinkBuf[PATH_MAX]'>
<var: #size type: #'size_t'>
<var: #ignored type: #'void *'>
self touch: GetAttributeString.
elfModuleName := self cCode: 'numModules ? info->dlpi_name : GetAttributeString(0)'.
(elfModuleName isNil
or: [(len := self strlen: elfModuleName) = 0]) ifTrue:
[^0]. "skip the fake linux-gate.so.1"
moduleNameObj := interpreterProxy
instantiateClass: interpreterProxy classString
indexableSize: len.
moduleNameObj = 0 ifTrue:
[primErr := PrimErrNoMemory.
^1]. "stop iteration"
self strncpy: (interpreterProxy arrayValueOf: moduleNameObj)
_: elfModuleName
_: len. "(char *)strncpy()"
interpreterProxy
storePointer: numModules
ofObject: interpreterProxy topRemappableOop
withValue: moduleNameObj.
"now dereference the symlink, if it exists"
self str: symLinkBuf cpy: elfModuleName.
(len := self read: elfModuleName li: symLinkBuf nk: #'PATH_MAX') > 0
ifTrue:
[moduleNameObj := interpreterProxy
instantiateClass: interpreterProxy classString
indexableSize: len.
moduleNameObj = 0 ifTrue:
[primErr := PrimErrNoMemory.
^1]. "stop iteration"
self strncpy: (interpreterProxy arrayValueOf: moduleNameObj)
_: symLinkBuf
_: len. "(char *)strncpy()"
interpreterProxy
storePointer: numModules + 1
ofObject: interpreterProxy topRemappableOop
withValue: moduleNameObj]
ifFalse:
[interpreterProxy
storePointer: numModules + 1
ofObject: interpreterProxy topRemappableOop
withValue: interpreterProxy nilObject].
numModules := numModules + 2.
^0
]