1 # a waf tool to extract symbols from object files or libraries
2 # using nm, producing a set of exposed defined/undefined symbols
4 import Utils
, Build
, subprocess
, Logs
5 from samba_wildcard
import fake_build_environment
6 from samba_utils
import *
8 def symbols_extract(objfiles
, dynamic
=False):
9 '''extract symbols from objfile, returning a dictionary containing
10 the set of undefined and public symbols for each file'''
16 # needed for some .so files
20 nmpipe
= subprocess
.Popen(cmd
, stdout
=subprocess
.PIPE
).stdout
21 if len(objfiles
) == 1:
22 filename
= objfiles
[0]
23 ret
[filename
] = { "PUBLIC": set(), "UNDEFINED" : set()}
27 if line
.endswith(':'):
29 ret
[filename
] = { "PUBLIC": set(), "UNDEFINED" : set() }
31 cols
= line
.split(" ")
34 # see if the line starts with an address
41 if symbol_type
in "BDGTRVWSi":
43 ret
[filename
]["PUBLIC"].add(symbol
)
44 elif symbol_type
in "U":
45 ret
[filename
]["UNDEFINED"].add(symbol
)
51 if name
.find(".objlist") != -1:
56 def find_syslib_path(bld
, libname
, deps
):
57 '''find the path to the syslib we will link against'''
58 # the strategy is to use the targets that depend on the library, and run ldd
59 # on it to find the real location of the library that is used
61 linkpath
= deps
[0].link_task
.outputs
[0].abspath(bld
.env
)
63 if libname
== "python":
64 libname
+= bld
.env
.PYTHON_VERSION
68 lddpipe
= subprocess
.Popen(['ldd', linkpath
], stdout
=subprocess
.PIPE
).stdout
71 cols
= line
.split(" ")
72 if len(cols
) < 3 or cols
[1] != "=>":
74 if cols
[0].startswith("lib%s." % libname
.lower()):
76 if cols
[0].startswith("libc."):
78 bld
.env
.libc_path
= cols
[2]
82 def build_symbol_sets(bld
, tgt_list
):
83 '''build the public_symbols and undefined_symbols attributes for each target'''
85 objlist
= [] # list of object file
86 objmap
= {} # map from object filename to target
90 t
.public_symbols
= set()
91 t
.undefined_symbols
= set()
92 for tsk
in getattr(t
, 'compiled_tasks', []):
93 for output
in tsk
.outputs
:
94 objpath
= output
.abspath(bld
.env
)
95 objlist
.append(objpath
)
98 symbols
= symbols_extract(objlist
)
101 t
.public_symbols
= t
.public_symbols
.union(symbols
[obj
]["PUBLIC"])
102 t
.undefined_symbols
= t
.undefined_symbols
.union(symbols
[obj
]["UNDEFINED"])
104 t
.undefined_symbols
= t
.undefined_symbols
.difference(t
.public_symbols
)
106 # and the reverse map of public symbols to subsystem name
107 bld
.env
.symbol_map
= {}
110 for s
in t
.public_symbols
:
111 bld
.env
.symbol_map
[s
] = real_name(t
.sname
)
113 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
115 bld
.env
.public_symbols
= {}
117 name
= real_name(t
.sname
)
118 if name
in bld
.env
.public_symbols
:
119 bld
.env
.public_symbols
[name
] = bld
.env
.public_symbols
[name
].union(t
.public_symbols
)
121 bld
.env
.public_symbols
[name
] = t
.public_symbols
122 if t
.samba_type
== 'LIBRARY':
123 for dep
in t
.add_objects
:
124 t2
= bld
.name_to_obj(dep
, bld
.env
)
125 bld
.ASSERT(t2
is not None, "Library '%s' has unknown dependency '%s'" % (name
, dep
))
126 bld
.env
.public_symbols
[name
] = bld
.env
.public_symbols
[name
].union(t2
.public_symbols
)
129 def build_syslib_sets(bld
, tgt_list
):
130 '''build the public_symbols for all syslibs'''
132 # work out what syslibs we depend on, and what targets those are used in
136 if getattr(t
, 'uselib', []) and t
.samba_type
in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
138 if lib
in ['PYEMBED', 'PYEXT']:
140 if not lib
in syslibs
:
142 syslibs
[lib
].append(t
)
144 # work out the paths to each syslib
147 path
= find_syslib_path(bld
, lib
, syslibs
[lib
])
149 Logs
.warn("Unable to find syslib path for %s" % lib
)
151 syslib_paths
.append(path
)
152 objmap
[path
] = lib
.lower()
155 syslib_paths
.append(bld
.env
.libc_path
)
156 objmap
[bld
.env
.libc_path
] = 'c'
158 symbols
= symbols_extract(syslib_paths
, dynamic
=True)
160 # keep a map of syslib names to public symbols
161 bld
.env
.syslib_symbols
= {}
163 bld
.env
.syslib_symbols
[lib
] = symbols
[lib
]["PUBLIC"]
165 # add to the map of symbols to dependencies
167 for sym
in symbols
[lib
]["PUBLIC"]:
168 bld
.env
.symbol_map
[sym
] = objmap
[lib
]
170 # keep the libc symbols as well, as these are useful for some of the
172 bld
.env
.libc_symbols
= symbols
[bld
.env
.libc_path
]["PUBLIC"]
174 # add to the combined map of dependency name to public_symbols
175 for lib
in bld
.env
.syslib_symbols
:
176 bld
.env
.public_symbols
[objmap
[lib
]] = bld
.env
.syslib_symbols
[lib
]
178 def build_autodeps(bld
, t
):
179 '''build the set of dependencies for a target'''
181 name
= real_name(t
.sname
)
183 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
185 for sym
in t
.undefined_symbols
:
186 if sym
in t
.public_symbols
:
188 if sym
in bld
.env
.symbol_map
:
189 depname
= bld
.env
.symbol_map
[sym
]
191 # self dependencies aren't interesting
193 if t
.in_library
== [depname
]:
194 # no need to depend on the library we are part of
196 if depname
in ['c', 'python']:
197 # these don't go into autodeps
199 if targets
[depname
] in [ 'SYSLIB' ]:
202 t2
= bld
.name_to_obj(depname
, bld
.env
)
203 if len(t2
.in_library
) != 1:
206 if t2
.in_library
== t
.in_library
:
207 # if we're part of the same library, we don't need to autodep
209 deps
.add(t2
.in_library
[0])
213 def build_library_names(bld
, tgt_list
):
214 '''add a in_library attribute to all targets that are part of a library'''
219 if t
.samba_type
in [ 'LIBRARY' ]:
220 for obj
in t
.samba_deps_extended
:
221 t2
= bld
.name_to_obj(obj
, bld
.env
)
222 if t2
and t2
.samba_type
in [ 'SUBSYSTEM', 'ASN1' ]:
223 if not t
.sname
in t2
.in_library
:
224 t2
.in_library
.append(t
.sname
)
227 def check_library_deps(bld
, t
):
228 '''check that all the autodeps that have mutual dependency of this
229 target are in the same library as the target'''
231 name
= real_name(t
.sname
)
233 if len(t
.in_library
) > 1:
234 Logs
.warn("WARNING: Target '%s' in multiple libraries: %s" % (t
.sname
, t
.in_library
))
236 for dep
in t
.autodeps
:
237 t2
= bld
.name_to_obj(dep
, bld
.env
)
240 for dep2
in t2
.autodeps
:
241 if dep2
== name
and t
.in_library
!= t2
.in_library
:
242 Logs
.warn("WARNING: mutual dependency %s <=> %s" % (name
, real_name(t2
.sname
)))
243 Logs
.warn("Libraries should match. %s != %s" % (t
.in_library
, t2
.in_library
))
244 # raise Utils.WafError("illegal mutual dependency")
247 def check_syslib_collisions(bld
, tgt_list
):
248 '''check if a target has any symbol collisions with a syslib
250 We do not want any code in Samba to use a symbol name from a
251 system library. The chance of that causing problems is just too
252 high. Note that libreplace uses a rep_XX approach of renaming
258 for lib
in bld
.env
.syslib_symbols
:
259 common
= t
.public_symbols
.intersection(bld
.env
.syslib_symbols
[lib
])
261 Logs
.error("ERROR: Target '%s' has symbols '%s' which is also in syslib '%s'" % (t
.sname
, common
, lib
))
264 raise Utils
.WafError("symbols in common with system libraries")
267 def check_dependencies(bld
, t
):
268 '''check for depenencies that should be changed'''
270 if bld
.name_to_obj(t
.sname
+ ".objlist", bld
.env
):
273 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
275 remaining
= t
.undefined_symbols
.copy()
276 remaining
= remaining
.difference(t
.public_symbols
)
278 sname
= real_name(t
.sname
)
280 deps
= set(t
.samba_deps
)
281 for d
in t
.samba_deps
:
282 if targets
[d
] in [ 'EMPTY', 'DISABLED', 'SYSLIB' ]:
284 bld
.ASSERT(d
in bld
.env
.public_symbols
, "Failed to find symbol list for dependency '%s'" % d
)
285 diff
= remaining
.intersection(bld
.env
.public_symbols
[d
])
286 if not diff
and targets
[sname
] != 'LIBRARY':
287 Logs
.info("Target '%s' has no dependency on %s" % (sname
, d
))
289 remaining
= remaining
.difference(diff
)
291 t
.unsatisfied_symbols
= set()
293 for sym
in remaining
:
294 if sym
in bld
.env
.symbol_map
:
295 dep
= bld
.env
.symbol_map
[sym
]
296 if not dep
in needed
:
300 t
.unsatisfied_symbols
.add(sym
)
303 Logs
.info("Target '%s' should add dep '%s' for symbols %s" % (sname
, dep
, " ".join(needed
[dep
])))
307 def check_syslib_dependencies(bld
, t
):
308 '''check for syslib depenencies'''
310 if bld
.name_to_obj(t
.sname
+ ".objlist", bld
.env
):
313 sname
= real_name(t
.sname
)
317 features
= TO_LIST(t
.features
)
318 if 'pyembed' in features
or 'pyext' in features
:
319 t
.unsatisfied_symbols
= t
.unsatisfied_symbols
.difference(bld
.env
.public_symbols
['python'])
322 for sym
in t
.unsatisfied_symbols
:
323 if sym
in bld
.env
.symbol_map
:
324 dep
= bld
.env
.symbol_map
[sym
]
327 if not dep
in needed
:
334 Logs
.info("Target '%s' should add syslib dep '%s' for symbols %s" % (sname
, dep
, " ".join(needed
[dep
])))
337 debug("deps: Target '%s' has unsatisfied symbols: %s" % (sname
, " ".join(remaining
)))
341 def symbols_symbolcheck(task
):
342 '''check the internal dependency lists'''
344 tgt_list
= get_tgt_list(bld
)
346 build_symbol_sets(bld
, tgt_list
)
347 build_library_names(bld
, tgt_list
)
351 if getattr(t
, 'source', ''):
352 build_autodeps(bld
, t
)
355 check_dependencies(bld
, t
)
358 check_library_deps(bld
, t
)
360 def symbols_syslibcheck(task
):
361 '''check the syslib dependencies'''
363 tgt_list
= get_tgt_list(bld
)
365 build_syslib_sets(bld
, tgt_list
)
366 check_syslib_collisions(bld
, tgt_list
)
369 check_syslib_dependencies(bld
, t
)
372 def SYMBOL_CHECK(bld
):
373 '''check our dependency lists'''
374 if Options
.options
.SYMBOLCHECK
:
375 bld
.SET_BUILD_GROUP('symbolcheck')
376 task
= bld(rule
=symbols_symbolcheck
, always
=True, name
='symbol checking')
379 bld
.SET_BUILD_GROUP('syslibcheck')
380 task
= bld(rule
=symbols_syslibcheck
, always
=True, name
='syslib checking')
382 Build
.BuildContext
.SYMBOL_CHECK
= SYMBOL_CHECK