Skip to content

Commit bbb5e5e

Browse files
committed
gen_provides: export per-variant TLS symbol offsets to sketches
Add an option to export TLS symbol offsets to an assembly wrapper in each board's variant folder, so that they can be used at core compile time. This will allow 'errno' and other thread-local symbols to be properly resolved in the sketches. Also export the '__aebabi_read_tp' function on ARM targets, which is used by the compiler-generated TLS code to compute absolute symbol addresses. Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
1 parent 0218103 commit bbb5e5e

5 files changed

Lines changed: 69 additions & 14 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ cxxflags.txt
88
includes.txt
99
syms-dynamic.ld
1010
syms-static.ld
11+
tls-syms.S

cores/arduino/llext_wrappers.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@
6262
__real_##name(a); \
6363
}
6464

65+
#ifdef CONFIG_ARM
66+
/* ARM EABI thread pointer access */
67+
W0(size_t, __aeabi_read_tp)
68+
#endif
69+
6570
/* string.h */
6671
W3(void *, memcpy, void *, const void *, size_t)
6772
W3(void *, memmove, void *, const void *, size_t)

extra/build.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ cp ${BUILD_DIR}/zephyr/.config firmwares/zephyr-$variant.config
102102

103103
# Generate the provides.ld file for linked builds
104104
echo "Generating exported symbol scripts"
105+
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -T > ${VARIANT_DIR}/tls-syms.S
105106
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -L > ${VARIANT_DIR}/syms-dynamic.ld
106107
extra/gen_provides.py "${BUILD_DIR}/zephyr/zephyr.elf" -LF \
107108
"+kheap_llext_heap" \

extra/gen_provides.py

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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
#-------------------------------------------------------------------------------
251294
if __name__ == '__main__':

loader/llext_exports.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ EXPORT_SYMBOL(ring_buf_area_finish);
289289
#endif
290290

291291
EXPORT_SYMBOL(sys_clock_cycle_get_32);
292+
293+
#if defined(CONFIG_ARM)
294+
extern uint32_t __aeabi_read_tp(void);
295+
EXPORT_LIBC_SYM(__aeabi_read_tp);
292296
FORCE_EXPORT_SYM(__aeabi_dcmpun);
293297
FORCE_EXPORT_SYM(__aeabi_dcmple);
294298
FORCE_EXPORT_SYM(__aeabi_d2lz);
@@ -318,6 +322,7 @@ FORCE_EXPORT_SYM(__aeabi_idivmod);
318322
FORCE_EXPORT_SYM(__aeabi_ldivmod);
319323
FORCE_EXPORT_SYM(__aeabi_ul2f);
320324
FORCE_EXPORT_SYM(__aeabi_dcmpge);
325+
#endif
321326

322327
#if defined (CONFIG_CPP)
323328
FORCE_EXPORT_SYM(__cxa_pure_virtual);

0 commit comments

Comments
 (0)