@@ -122,17 +122,29 @@ def main():
122122 argparser .add_argument ('-F' , '--funcs' ,
123123 action = 'store_true' ,
124124 help = 'Extract all public functions' )
125+ argparser .add_argument ('-T' , '--tls-defs' ,
126+ action = 'store_true' ,
127+ help = 'Extract TLS symbol offsets to a .S file' )
125128 argparser .add_argument ('file' ,
126129 help = 'ELF file to parse' )
127130 argparser .add_argument ('syms' , nargs = '*' ,
128131 help = 'Symbols to export' )
129132
130133 args = argparser .parse_intermixed_args ()
131134
135+ wants_provides = bool (args .syms or args .funcs or args .llext )
136+ if wants_provides and args .tls_defs :
137+ sys .stderr .write ("Cannot generate TLS defs when also generating PROVIDEs.\n " )
138+ sys .exit (1 )
139+ elif not wants_provides and not args .tls_defs :
140+ sys .stderr .write ("Nothing specified for export.\n " )
141+ sys .exit (1 )
142+
132143 exact_syms = set ()
133144 regex_syms = set ()
134145 deref_syms = set ()
135146 sized_syms = set ()
147+ tls_syms = set () # tuple of (name, value, size)
136148 rename_map = {}
137149 for sym in args .syms :
138150 sym_class = None
@@ -184,6 +196,11 @@ def main():
184196 fail = False
185197
186198 for name , sym in all_syms .items ():
199+ if sym ['st_info' ]['type' ] == 'STT_TLS' :
200+ # Collect TLS symbol information, ignore for PROVIDEs
201+ tls_syms .add ((name , sym ['st_value' ], sym ['st_size' ]))
202+ continue
203+
187204 value = None
188205 comment = []
189206 if name in exact_syms or any (re .match (r , name ) for r in regex_syms ):
@@ -232,20 +249,46 @@ def main():
232249 */
233250""" )
234251
235- if not out_syms :
236- print ("/* No symbols found matching the criteria */" )
237- sys .stderr .write ("warning: no symbols found matching the criteria.\n " )
238- else :
239- sym_comment = nul_comment = ""
240- for name , (value , comments ) in sorted (out_syms .items (), key = lambda x : x [0 ]):
241- if args .verbose :
242- comment = ', ' .join (sorted (comments ))
243- sym_comment = f"/* { comment } */"
244- nul_comment = f" ({ comment } )"
245- if value :
246- print (f"PROVIDE({ name } = { value :#010x} );{ sym_comment } " )
247- else :
248- print (f"/* NULL { name } { nul_comment } */" )
252+ if wants_provides :
253+ if not out_syms :
254+ print ("/* No symbols found matching the criteria */" )
255+ sys .stderr .write ("warning: no symbols found matching the criteria.\n " )
256+ else :
257+ sym_comment = nul_comment = ""
258+ for name , (value , comments ) in sorted (out_syms .items (), key = lambda x : x [0 ]):
259+ if args .verbose :
260+ comment = ', ' .join (sorted (comments ))
261+ sym_comment = f"/* { comment } */"
262+ nul_comment = f" ({ comment } )"
263+ if value :
264+ print (f"PROVIDE({ name } = { value :#010x} );{ sym_comment } " )
265+ else :
266+ print (f"/* NULL { name } { nul_comment } */" )
267+
268+ if args .tls_defs :
269+ # ARM and AArch64 use '@' as a line-comment character in GAS, so the
270+ # assembler requires '%' as the type prefix there. All other targets use '@'.
271+ prefix = '%' if elf ['e_machine' ] in ('EM_ARM' , 'EM_AARCH64' ) else '@'
272+
273+ # The TLS Variant 1 layout (used by ARM/AArch64) places a Thread
274+ # Control Block (TCB) before the TLS data. The thread pointer
275+ # returned by __aeabi_read_tp() points to the TCB, so the actual
276+ # runtime address of a TLS variable must be offset by that size.
277+ #
278+ # TCB is 2 pointers: 8 bytes on 32-bit, 16 bytes on 64-bit.
279+ # See zephyr/arch/arm/core/tls.c: arch_tls_stack_setup().
280+ tcb_size = (elf .elfclass // 8 ) * 2
281+ print (f"/* Offsets include { tcb_size } bytes for TCB data */" )
282+
283+ # Sort by offset first, then size
284+ for name , offset , size in sorted (tls_syms , key = lambda x : (x [1 ], x [2 ])):
285+ offset += tcb_size
286+ print (
287+ f"\n /* TLS offset { offset :#x} : { name } ({ size } bytes) */\n "
288+ f".global { name } \n "
289+ f".type { name } , { prefix } tls_object\n "
290+ f".set { name } , { offset } "
291+ )
249292
250293#-------------------------------------------------------------------------------
251294if __name__ == '__main__' :
0 commit comments