Skip to content

Commit 212ec9a

Browse files
dcpleungnashif
authored andcommitted
linker: sort app shared mem partition by alignment
If CONFIG_MPU_REQUIRES_POWER_OF_TWO_ALIGNMENT is enabled, the app shared memory partition may cause waste of memory due to the need for padding. For example, tests/subsys/jwt and board mps2_an385: z_test_mem_partition: addr 0x20000000, size 52 z_libc_partition : addr 0x20000040, size 4 k_mbedtls_partition : addr 0x20008000, size 32736 ending at 0x2000ffff, taking up 65536 bytes With power-of-two size and alignment requirement, k_mbedtls_partition takes up 32KB memory and needs to be aligned on 32KB boundary. If the above partitions are ordered as shown, there needs to be a lot of padding after z_libc_partition before k_mbedtls_partition can start. In order to minimize padding, these partitions need to be sort by size in descending order. After the changes here, the partitions are: k_mbedtls_partition : addr 0x20000000, size 32736 z_test_mem_partition: addr 0x20008000, size 52 z_libc_partition : addr 0x20008040, size 4 ending at 0x2000805f, taking up 32864 bytes With the above example, sorting results in a saving of 32672 bytes of saving. Fixes #14121 Signed-off-by: Daniel Leung <[email protected]>
1 parent e8a2348 commit 212ec9a

File tree

6 files changed

+201
-34
lines changed

6 files changed

+201
-34
lines changed

CMakeLists.txt

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ zephyr_cc_option(-Wpointer-arith)
363363
# Declare MPU userspace dependencies before the linker scripts to make
364364
# sure the order of dependencies are met
365365
if(CONFIG_USERSPACE)
366-
set(APP_SMEM_DEP app_smem_linker)
366+
set(APP_SMEM_ALIGNED_DEP app_smem_aligned_linker)
367+
set(APP_SMEM_UNALIGNED_DEP app_smem_unaligned_linker)
367368
if(CONFIG_ARM)
368369
set(PRIV_STACK_DEP priv_stacks_prebuilt)
369370
endif()
@@ -446,6 +447,8 @@ function(construct_add_custom_command_for_linker_pass linker_output_name output_
446447

447448
if (${linker_output_name} MATCHES "^linker_pass_final$")
448449
set(linker_pass_define -DLINKER_PASS2)
450+
elseif (${linker_output_name} MATCHES "^linker_app_smem_unaligned$")
451+
set(linker_pass_define -DLINKER_APP_SMEM_UNALIGNED)
449452
else()
450453
set(linker_pass_define "")
451454
endif()
@@ -761,7 +764,7 @@ construct_add_custom_command_for_linker_pass(
761764
custom_command
762765
${ALIGN_SIZING_DEP}
763766
${PRIV_STACK_DEP}
764-
${APP_SMEM_DEP}
767+
${APP_SMEM_ALIGNED_DEP}
765768
${CODE_RELOCATION_DEP}
766769
${OFFSETS_H_TARGET}
767770
)
@@ -1134,14 +1137,29 @@ configure_file(
11341137
$ENV{ZEPHYR_BASE}/include/linker/app_smem.ld
11351138
${PROJECT_BINARY_DIR}/include/generated/app_smem.ld)
11361139

1140+
configure_file(
1141+
$ENV{ZEPHYR_BASE}/include/linker/app_smem_aligned.ld
1142+
${PROJECT_BINARY_DIR}/include/generated/app_smem_aligned.ld)
1143+
1144+
configure_file(
1145+
$ENV{ZEPHYR_BASE}/include/linker/app_smem_unaligned.ld
1146+
${PROJECT_BINARY_DIR}/include/generated/app_smem_unaligned.ld)
1147+
11371148
if(CONFIG_USERSPACE)
1138-
set(APP_SMEM_LD "${PROJECT_BINARY_DIR}/include/generated/app_smem.ld")
1149+
set(APP_SMEM_ALIGNED_LD "${PROJECT_BINARY_DIR}/include/generated/app_smem_aligned.ld")
1150+
set(APP_SMEM_UNALIGNED_LD "${PROJECT_BINARY_DIR}/include/generated/app_smem_unaligned.ld")
11391151
set(OBJ_FILE_DIR "${PROJECT_BINARY_DIR}/../")
11401152

11411153
add_custom_target(
1142-
${APP_SMEM_DEP}
1154+
${APP_SMEM_ALIGNED_DEP}
1155+
DEPENDS
1156+
${APP_SMEM_ALIGNED_LD}
1157+
)
1158+
1159+
add_custom_target(
1160+
${APP_SMEM_UNALIGNED_DEP}
11431161
DEPENDS
1144-
${APP_SMEM_LD}
1162+
${APP_SMEM_UNALIGNED_LD}
11451163
)
11461164

11471165
if(CONFIG_NEWLIB_LIBC)
@@ -1152,18 +1170,65 @@ if(CONFIG_USERSPACE)
11521170
endif()
11531171

11541172
add_custom_command(
1155-
OUTPUT ${APP_SMEM_LD}
1173+
OUTPUT ${APP_SMEM_UNALIGNED_LD}
11561174
COMMAND ${PYTHON_EXECUTABLE}
11571175
${ZEPHYR_BASE}/scripts/gen_app_partitions.py
11581176
-d ${OBJ_FILE_DIR}
1159-
-o ${APP_SMEM_LD}
1177+
-o ${APP_SMEM_UNALIGNED_LD}
1178+
${NEWLIB_PART} ${MBEDTLS_PART}
1179+
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
1180+
DEPENDS
1181+
kernel
1182+
${ZEPHYR_LIBS_PROPERTY}
1183+
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/
1184+
COMMENT "Generating app_smem_unaligned linker section"
1185+
)
1186+
1187+
construct_add_custom_command_for_linker_pass(
1188+
linker_app_smem_unaligned
1189+
custom_command
1190+
${ALIGN_SIZING_DEP}
1191+
${CODE_RELOCATION_DEP}
1192+
${APP_SMEM_UNALIGNED_DEP}
1193+
${APP_SMEM_UNALIGNED_LD}
1194+
${OFFSETS_H_TARGET}
1195+
)
1196+
add_custom_command(
1197+
${custom_command}
1198+
)
1199+
1200+
add_custom_target(
1201+
linker_app_smem_unaligned_script
1202+
DEPENDS
1203+
linker_app_smem_unaligned.cmd
1204+
)
1205+
1206+
set_property(TARGET
1207+
linker_app_smem_unaligned_script
1208+
PROPERTY INCLUDE_DIRECTORIES
1209+
${ZEPHYR_INCLUDE_DIRS}
1210+
)
1211+
1212+
set(APP_SMEM_UNALIGNED_LIB app_smem_unaligned_output_obj_renamed_lib)
1213+
add_executable( app_smem_unaligned_prebuilt misc/empty_file.c)
1214+
target_link_libraries(app_smem_unaligned_prebuilt ${TOPT} ${PROJECT_BINARY_DIR}/linker_app_smem_unaligned.cmd ${zephyr_lnk} ${CODE_RELOCATION_DEP})
1215+
set_property(TARGET app_smem_unaligned_prebuilt PROPERTY LINK_DEPENDS ${PROJECT_BINARY_DIR}/linker_app_smem_unaligned.cmd)
1216+
add_dependencies( app_smem_unaligned_prebuilt ${ALIGN_SIZING_DEP} linker_app_smem_unaligned_script ${OFFSETS_LIB})
1217+
1218+
add_custom_command(
1219+
OUTPUT ${APP_SMEM_ALIGNED_LD}
1220+
COMMAND ${PYTHON_EXECUTABLE}
1221+
${ZEPHYR_BASE}/scripts/gen_app_partitions.py
1222+
-e $<TARGET_FILE:app_smem_unaligned_prebuilt>
1223+
-o ${APP_SMEM_ALIGNED_LD}
11601224
${NEWLIB_PART} ${MBEDTLS_PART}
11611225
$<$<BOOL:${CMAKE_VERBOSE_MAKEFILE}>:--verbose>
11621226
DEPENDS
11631227
kernel
11641228
${ZEPHYR_LIBS_PROPERTY}
1229+
app_smem_unaligned_prebuilt
11651230
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/
1166-
COMMENT "Generating app_smem linker section"
1231+
COMMENT "Generating app_smem_aligned linker section"
11671232
)
11681233
endif()
11691234

@@ -1173,8 +1238,8 @@ if(CONFIG_USERSPACE AND CONFIG_ARM)
11731238
custom_command
11741239
${ALIGN_SIZING_DEP}
11751240
${CODE_RELOCATION_DEP}
1176-
${APP_SMEM_DEP}
1177-
${APP_SMEM_LD}
1241+
${APP_SMEM_ALIGNED_DEP}
1242+
${APP_SMEM_ALIGNED_LD}
11781243
${OFFSETS_H_TARGET}
11791244
)
11801245
add_custom_command(

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@
186186
/include/kernel_version.h @andrewboie @andyross
187187
/include/led.h @Mani-Sadhasivam
188188
/include/led_strip.h @mbolivar
189+
/include/linker/app_smem*.ld @andrewboie
189190
/include/linker/linker-defs.h @andrewboie @andyross
190191
/include/linker/linker-tool-gcc.h @andrewboie @andyross
191192
/include/linker/linker-tool.h @andrewboie @andyross

include/linker/app_smem.ld

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,36 @@
1-
/* space holder */
2-
APP_SMEM_SECTION()
1+
/*
2+
* This hackish way of including files is due to CMake issues:
3+
* https://gitlab.kitware.com/cmake/cmake/issues/11985
4+
* https://gitlab.kitware.com/cmake/cmake/issues/13718
5+
*
6+
* When using the "Unix Makefiles" generator, CMake simply
7+
* greps for "#include" to generate dependency list.
8+
* So if doing it normally, both files are being included
9+
* in the dependency list. This creates weird dependency
10+
* issue:
11+
*
12+
* 1. Using A.ld to create a linker script A.cmd.
13+
* 2. Using A.cmd to generate A_prebuilt.elf.
14+
* 3. Using A_prebuilt.elf to create B.ld.
15+
* 4. Creating B.cmd with B.ld.
16+
* 5. Creating B_prebuilt.elf using B.cmd.
17+
*
18+
* Since the dependency list of A.cmd contains both
19+
* A.ld and B.ld, when make is invoked again, B.ld
20+
* is newer than A.cmd so everything from this point on
21+
* gets rebuilt. In order to break this cycle, this
22+
* hackish needs to be used since CMake does not parse
23+
* macros, and thus these will not appear in
24+
* the dependency list. The dependencies should then be
25+
* put in CMakeLists.txt instead.
26+
*
27+
* Note: Ninja generator does not suffer from this issue.
28+
*/
29+
#ifdef LINKER_APP_SMEM_UNALIGNED
30+
#define APP_SMEM_LD <app_smem_unaligned.ld>
31+
#else
32+
#define APP_SMEM_LD <app_smem_aligned.ld>
33+
#endif
34+
35+
#include APP_SMEM_LD
36+
#undef APP_SMEM_LD

include/linker/app_smem_aligned.ld

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* space holder */
2+
APP_SMEM_SECTION()

include/linker/app_smem_unaligned.ld

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/* space holder */
2+
APP_SMEM_SECTION()

scripts/gen_app_partitions.py

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@
3737
import os
3838
import re
3939
import string
40+
import subprocess
41+
from collections import OrderedDict
4042
from elf_helper import ElfHelper
4143
from elftools.elf.elffile import ELFFile
44+
from elftools.elf.sections import SymbolTableSection
45+
from operator import itemgetter
4246

47+
SZ = 'size'
48+
SRC = 'sources'
49+
LIB = 'libraries'
4350

4451
# This script will create sections and linker variables to place the
4552
# application shared memory partitions.
@@ -91,7 +98,9 @@
9198

9299
section_regex = re.compile(r'data_smem_([A-Za-z0-9_]*)_(data|bss)')
93100

94-
def find_partitions(filename, partitions, sources):
101+
elf_part_size_regex = re.compile(r'z_data_smem_(.*)_part_size')
102+
103+
def find_obj_file_partitions(filename, partitions):
95104
with open(filename, 'rb') as f:
96105
full_lib = ELFFile( f)
97106
if (not full_lib):
@@ -106,23 +115,67 @@ def find_partitions(filename, partitions, sources):
106115

107116
partition_name = m.groups()[0]
108117
if partition_name not in partitions:
109-
partitions[partition_name] = []
118+
partitions[partition_name] = {SZ: section.header.sh_size}
119+
110120
if args.verbose:
111-
sources.update({partition_name: filename})
121+
partitions[partition_name][SRC] = filename
122+
123+
else:
124+
partitions[partition_name][SZ] += section.header.sh_size
125+
126+
127+
return partitions
128+
129+
130+
def parse_obj_files(partitions):
131+
# Iterate over all object files to find partitions
132+
for dirpath, dirs, files in os.walk(args.directory):
133+
for filename in files:
134+
if re.match(".*\.obj$",filename):
135+
fullname = os.path.join(dirpath, filename)
136+
find_obj_file_partitions(fullname, partitions)
137+
138+
139+
def parse_elf_file(partitions):
140+
with open(args.elf, 'rb') as f:
141+
elffile = ELFFile(f)
112142

113-
return (partitions, sources)
143+
symbol_tbls = [s for s in elffile.iter_sections()
144+
if isinstance(s, SymbolTableSection)]
145+
146+
for section in symbol_tbls:
147+
for nsym, symbol in enumerate(section.iter_symbols()):
148+
if symbol['st_shndx'] != "SHN_ABS":
149+
continue
150+
151+
x = elf_part_size_regex.match(symbol.name)
152+
if not x:
153+
continue
154+
155+
partition_name = x.groups()[0]
156+
size = symbol['st_value']
157+
if partition_name not in partitions:
158+
partitions[partition_name] = {SZ: size}
159+
160+
if args.verbose:
161+
partitions[partition_name][SRC] = args.elf
162+
163+
else:
164+
partitions[partition_name][SZ] += size
114165

115166

116167
def generate_final_linker(linker_file, partitions):
117168
string = linker_start_seq
118169
size_string = ''
119-
for partition, libs in partitions.items():
170+
for partition, item in partitions.items():
120171
string += data_template.format(partition)
121-
for lib in libs:
122-
string += library_data_template.format(lib)
172+
if LIB in item:
173+
for lib in item[LIB]:
174+
string += library_data_template.format(lib)
123175
string += bss_template.format(partition)
124-
for lib in libs:
125-
string += library_bss_template.format(lib)
176+
if LIB in item:
177+
for lib in item[LIB]:
178+
string += library_bss_template.format(lib)
126179
string += footer_template.format(partition)
127180
size_string += size_cal_string.format(partition)
128181

@@ -137,8 +190,10 @@ def parse_args():
137190
parser = argparse.ArgumentParser(
138191
description=__doc__,
139192
formatter_class=argparse.RawDescriptionHelpFormatter)
140-
parser.add_argument("-d", "--directory", required=True,
193+
parser.add_argument("-d", "--directory", required=False, default=None,
141194
help="Root build directory")
195+
parser.add_argument("-e", "--elf", required=False, default=None,
196+
help="ELF file")
142197
parser.add_argument("-o", "--output", required=False,
143198
help="Output ld file")
144199
parser.add_argument("-v", "--verbose", action="count", default =0,
@@ -154,26 +209,34 @@ def main():
154209
parse_args()
155210
linker_file = args.output
156211
partitions = {}
157-
sources = {}
158212

159-
for dirpath, dirs, files in os.walk(args.directory):
160-
for filename in files:
161-
if re.match(".*\.obj$",filename):
162-
fullname = os.path.join(dirpath, filename)
163-
find_partitions(fullname, partitions,
164-
sources)
213+
if args.directory is not None:
214+
parse_obj_files(partitions)
215+
elif args.elf is not None:
216+
parse_elf_file(partitions)
217+
else:
218+
return
165219

166220
for lib, ptn in args.library:
167221
if ptn not in partitions:
168-
partitions[ptn] = [lib]
222+
partitions[ptn] = {}
223+
224+
if LIB not in partitions[ptn]:
225+
partitions[ptn][LIB] = [lib]
169226
else:
170-
partitions[ptn].append(lib)
227+
partitions[ptn][LIB].append(lib)
228+
229+
partsorted = OrderedDict(sorted(partitions.items(),
230+
key=lambda x: x[1][SZ], reverse=True))
171231

172-
generate_final_linker(args.output, partitions)
232+
generate_final_linker(args.output, partsorted)
173233
if args.verbose:
174234
print("Partitions retrieved:")
175-
for key in partitions:
176-
print(" %s: %s\n", key, sources[key])
235+
for key in partsorted:
236+
print(" {0}: size {1}: {2}".format(key,
237+
partsorted[key][SZ],
238+
partsorted[key][SRC]))
239+
177240

178241
if __name__ == '__main__':
179242
main()

0 commit comments

Comments
 (0)