dhrystone baremetal!!!

Factor out --optimization-level and --static to all builds

More conventionally set argv[0] to be the basename of the image.

Fix https://github.com/cirosantilli/linux-kernel-module-cheat/issues/90
This commit is contained in:
Ciro Santilli 六四事件 法轮功
2020-04-02 03:00:02 +00:00
parent b7887ac06b
commit 66473201eb
9 changed files with 136 additions and 100 deletions

View File

@@ -16449,13 +16449,22 @@ Run natively on the host:
"$(./getvar --host userland_build_dir)/submodules/dhrystone/dhrystone"
....
Build for <<baremetal>> execution and run it in baremetal QEMU. TODO: fix the build, just need to factor out all run arguments from link:build-baremetal[] into link:common.py[] and it should just work, no missing syscalls.
Build Dhrystone for <<baremetal>> and run it in on QEMU:
....
# Build our Newlib stubs.
./build-baremetal --arch aarch64
./build-dhrystone --arch aarch64 --mode baremetal
./run --arch aarch64 --baremetal "$(./getvar baremetal_build_dir)/submodules/dhrystone/dhrystone"
./run --arch aarch64 --baremetal "$(./getvar --arch aarch64 baremetal_build_dir)/submodules/dhrystone/dhrystone" --cli-args 10000
....
or with gem5:
....
# Build our Newlib stubs.
./build-baremetal --arch aarch64
./build-dhrystone --arch aarch64 --emulator gem5 --mode baremetal
./run --arch aarch64 --baremetal "$(./getvar --arch aarch64 --emulator gem5 baremetal_build_dir)/submodules/dhrystone/dhrystone" --cli-args 10000 --emulator gem5
....
If you really want the Buildroot package for some reason, build it with:

View File

@@ -9,8 +9,13 @@ SECTIONS
*(.data)
*(COMMON)
}
/* gem5 uses the bss as a measure of the kernel size. */
/* gem5 uses the bss as a measure of the kernel size.
* b7887ac06bd7fc8011fbf595b1d50ea67960d28c required __bss_start__
* when doing ./build-dhrystone --arch aarch64 --mode baremetal
*/
__bss_start__ = .;
.bss : { *(.bss) }
__bss_end__ = .;
/* Fix the addresses of everything that comes after, no matter
* the exact size of the code present in .text. This allows us to
* place CLI arguments in memory at a known location! */

View File

@@ -28,33 +28,9 @@ Build the baremetal examples with crosstool-NG.
def build(self):
build_dir = self.get_build_dir()
extra_obj_baremetal_bootloader = os.path.join(
self.env['baremetal_build_lib_dir'],
'bootloader{}'.format(self.env['obj_ext'])
)
extra_obj_lkmc_common = os.path.join(
self.env['baremetal_build_lib_dir'],
self.env['common_basename_noext'] + self.env['obj_ext']
)
cc_flags = [
'-I', self.env['root_dir'], LF,
'-O{}'.format(self.env['optimization_level']), LF,
'-nostartfiles', LF,
]
if self.env['arch'] == 'arm':
cc_flags.extend([
'-mhard-float', LF,
# This uses the soft float ABI for calling functions from objets in Newlib which
# our crosstool-NG config compiles with soft floats, while emiting hard float
# from C and allowing us to use it from assembly, e.g. for the VMRS instruction:
# which would otherwise fail "with selected processor does not support XXX in ARM mode"
# Bibliography:
# - https://stackoverflow.com/questions/9753749/arm-compilation-error-vfp-registered-used-by-executable-not-object-file
# - https://stackoverflow.com/questions/41131432/cross-compiling-error-selected-processor-does-not-support-fmrx-r3-fpexc-in/41131782#41131782
# - https://embeddedartistry.com/blog/2017/10/9/r1q7pksku2q3gww9rpqef0dnskphtc
'-mfloat-abi=softfp', LF,
'-mfpu=crypto-neon-fp-armv8', LF,
])
if self.env['emulator'] == 'gem5':
cc_flags.extend([
'-DLKMC_GEM5=1', LF,
@@ -63,7 +39,7 @@ Build the baremetal examples with crosstool-NG.
else:
cc_flags.extend(['-D', 'LKMC_QEMU=1', LF])
cc_flags.extend(['-D', 'LKMC_UART0_ADDR={:#x}'.format(self.env['uart_address']), LF])
cc_flags.extend(self.sh.shlex_split(self.env['ccflags']))
cc_flags.extend(self.env['ccflags'])
bootloader_src = os.path.join(
self.env['baremetal_source_lib_dir'],
'{}{}'.format(
@@ -72,8 +48,8 @@ Build the baremetal examples with crosstool-NG.
)
)
for in_path, out_path in [
(bootloader_src, extra_obj_baremetal_bootloader),
(self.env['common_c'], extra_obj_lkmc_common),
(bootloader_src, self.env['baremetal_extra_obj_bootloader']),
(self.env['common_c'], self.env['baremetal_extra_obj_lkmc_common']),
(
self.env['baremetal_syscalls_src'],
self.env['baremetal_syscalls_obj']
@@ -90,11 +66,7 @@ Build the baremetal examples with crosstool-NG.
extra_deps=[self.env['common_h']],
link=False,
)
cc_flags.extend([
'-Wl,--section-start=.text={:#x}'.format(self.env['entry_address']), LF,
# '-Wl,--section-start=.lkmc_memory={:#x}'.format(self.env['entry_address'] + 0x1000000), LF,
'-T', self.env['baremetal_link_script'], LF,
])
cc_flags.extend(self.env['ldflags'])
with thread_pool.ThreadPool(
self._build_one,
nthreads=self.env['nproc'],
@@ -117,8 +89,8 @@ Build the baremetal examples with crosstool-NG.
self.env['baremetal_syscalls_obj'],
self.env['baremetal_syscalls_asm_obj']
],
'extra_objs_baremetal_bootloader': [extra_obj_baremetal_bootloader],
'extra_objs_lkmc_common': [extra_obj_lkmc_common],
'extra_objs_baremetal_bootloader': [self.env['baremetal_extra_obj_bootloader']],
'extra_objs_lkmc_common': [self.env['baremetal_extra_obj_lkmc_common']],
'in_path': in_path,
'out_path': self.resolve_baremetal_executable(in_path),
})
@@ -127,7 +99,7 @@ Build the baremetal examples with crosstool-NG.
def get_build_dir(self):
return self.env['baremetal_build_dir']
def setup_one(self):
def setup_one_build(self):
self.env['targets'] = self.resolve_targets(
[
self.env['baremetal_source_dir'],

View File

@@ -23,27 +23,18 @@ https://cirosantilli.com/linux-kernel-module-cheat#dhrystone
def build(self):
build_dir = self.get_build_dir()
cflags = ['-O{}'.format(self.env['optimization_level'])]
extra_flags = []
if self.env['static']:
cflags.extend(['-static'])
if self.env['force_rebuild']:
extra_flags.extend(['-B', LF])
if self.env['mode'] == 'baremetal':
extra_objs = [
self.env['baremetal_syscalls_obj'],
self.env['baremetal_syscalls_asm_obj']
]
else:
extra_objs = []
ret = self.sh.run_cmd(
[
'make', LF,
'-j', str(self.env['nproc']), LF,
'-C', os.path.join(self.env['submodules_dir'], 'dhrystone'), LF,
'CC={}'.format(self.env['gcc_path']), LF,
'CFLAGS_EXTRA={}'.format(' '.join(cflags)), LF,
'EXTRA_OBJS={}'.format(' '.join(extra_objs)), LF,
'CFLAGS_EXTRA={}'.format(self.sh.cmd_to_string(self.env['ccflags'], force_oneline=True)), LF,
'LDFLAGS_EXTRA={}'.format(self.sh.cmd_to_string(self.env['ldflags'], force_oneline=True)), LF,
'EXTRA_OBJS={}'.format(' '.join(self.env['extra_objs'])), LF,
'OUT_DIR={}'.format(build_dir), LF,
]
+ extra_flags

View File

@@ -49,10 +49,7 @@ Default: build all examples that have their package dependencies met, e.g.:
build_dir = self.get_build_dir()
cc_flags = [
'-I', self.env['root_dir'], LF,
'-O{}'.format(self.env['optimization_level']), LF,
] + self.sh.shlex_split(self.env['ccflags'])
if self.env['static']:
cc_flags.extend(['-static', LF])
] + self.env['ccflags']
extra_obj_lkmc_common = os.path.join(
build_dir,
self.env['common_basename_noext'] + self.env['obj_ext']
@@ -114,7 +111,7 @@ Default: build all examples that have their package dependencies met, e.g.:
def get_build_dir(self):
return self.env['userland_build_dir']
def setup_one(self):
def setup_one_build(self):
self.env['targets'] = self.resolve_targets(
[self.env['userland_source_dir']],
self.env['targets']

129
common.py
View File

@@ -530,7 +530,7 @@ https://cirosantilli.com/linux-kernel-module-cheat#gem5-arm-platforms
default=True,
help='''\
Copy userland build outputs to the overlay directory which will be put inside
the image. If not given explicitly, this is disabled automatically when certain
the disk image. If not given explicitly, this is disabled automatically when certain
options are given, for example --static, since users don't usually want
static executables to be placed in the final image, but rather only for
user mode simulations in simulators that don't support dynamic linking like gem5.
@@ -1013,12 +1013,17 @@ Incompatible archs are skipped.
# Userland
env['userland_source_arch_arch_dir'] = join(env['userland_source_arch_dir'], env['arch'])
if env['in_tree']:
env['userland_build_dir'] = self.env['userland_source_dir']
env['userland_build_dir'] = env['userland_source_dir']
else:
env['userland_build_dir'] = join(env['out_dir'], 'userland', env['userland_build_id'], env['arch'])
env['package'] = set(env['package'])
if not env['_args_given']['copy_overlay']:
if self.env['in_tree'] or self.env['static'] or self.env['host']:
if (
env['in_tree'] or
env['static'] or
env['host'] or
env['mode'] == 'baremetal'
):
env['copy_overlay'] = False
# Kernel modules.
@@ -1048,35 +1053,43 @@ Incompatible archs are skipped.
env['baremetal_syscalls_basename_noext'] = 'syscalls'
env['baremetal_syscalls_src'] = os.path.join(
env['baremetal_source_lib_dir'],
env['baremetal_syscalls_basename_noext'] + self.env['c_ext']
env['baremetal_syscalls_basename_noext'] + env['c_ext']
)
env['baremetal_syscalls_obj'] = os.path.join(
self.env['baremetal_build_lib_dir'],
env['baremetal_syscalls_basename_noext'] + self.env['obj_ext']
env['baremetal_build_lib_dir'],
env['baremetal_syscalls_basename_noext'] + env['obj_ext']
)
env['baremetal_syscalls_asm_src'] = os.path.join(
self.env['baremetal_source_lib_dir'],
env['baremetal_syscalls_basename_noext'] + '_asm' + self.env['asm_ext']
env['baremetal_source_lib_dir'],
env['baremetal_syscalls_basename_noext'] + '_asm' + env['asm_ext']
)
env['baremetal_syscalls_asm_obj'] = os.path.join(
self.env['baremetal_build_lib_dir'],
env['baremetal_syscalls_basename_noext'] + '_asm' + self.env['obj_ext']
env['baremetal_build_lib_dir'],
env['baremetal_syscalls_basename_noext'] + '_asm' + env['obj_ext']
)
if env['emulator'] == 'gem5':
if env['machine'] == 'VExpress_GEM5_V1':
env['entry_address'] = 0x80000000
env['uart_address'] = 0x1c090000
elif self.env['machine'] == 'RealViewPBX':
elif env['machine'] == 'RealViewPBX':
env['entry_address'] = 0x10000
env['uart_address'] = 0x10009000
else:
raise Exception('unknown machine: ' + self.env['machine'])
raise Exception('unknown machine: ' + env['machine'])
else:
env['entry_address'] = 0x40000000
env['uart_address']= 0x09000000
env['uart_address'] = 0x09000000
env['common_basename_noext'] = env['repo_short_id']
env['baremetal_extra_obj_bootloader'] = join(
env['baremetal_build_lib_dir'],
'bootloader{}'.format(env['obj_ext'])
)
env['baremetal_extra_obj_lkmc_common'] = join(
env['baremetal_build_lib_dir'],
env['common_basename_noext'] + env['obj_ext']
)
# Userland / baremetal common source.
env['common_basename_noext'] = env['repo_short_id']
env['common_c'] = os.path.join(
env['root_dir'],
env['common_basename_noext'] + env['c_ext']
@@ -1087,8 +1100,38 @@ Incompatible archs are skipped.
)
if env['mode'] == 'baremetal':
env['build_dir'] = env['baremetal_build_dir']
env['extra_objs'] = [
env['baremetal_extra_obj_bootloader'],
env['baremetal_extra_obj_lkmc_common'],
env['baremetal_syscalls_asm_obj'],
env['baremetal_syscalls_obj'],
]
env['ccflags_default'] = [
'-nostartfiles', LF,
]
if env['arch'] == 'arm':
env['ccflags_default'].extend([
'-mhard-float', LF,
# This uses the soft float ABI for calling functions from objets in Newlib which
# our crosstool-NG config compiles with soft floats, while emiting hard float
# from C and allowing us to use it from assembly, e.g. for the VMRS instruction:
# which would otherwise fail "with selected processor does not support XXX in ARM mode"
# Bibliography:
# - https://stackoverflow.com/questions/9753749/arm-compilation-error-vfp-registered-used-by-executable-not-object-file
# - https://stackoverflow.com/questions/41131432/cross-compiling-error-selected-processor-does-not-support-fmrx-r3-fpexc-in/41131782#41131782
# - https://embeddedartistry.com/blog/2017/10/9/r1q7pksku2q3gww9rpqef0dnskphtc
'-mfloat-abi=softfp', LF,
'-mfpu=crypto-neon-fp-armv8', LF,
])
env['ldflags'] = [
'-Wl,--section-start=.text={:#x}'.format(env['entry_address']), LF,
'-T', env['baremetal_link_script'], LF,
]
else:
env['build_dir'] = env['userland_build_dir']
env['ccflags_default'] = []
env['extra_objs'] = []
env['ldflags'] = []
# Docker
env['docker_build_dir'] = join(env['out_dir'], 'docker', env['arch'])
@@ -1247,25 +1290,6 @@ lunch aosp_{}-eng
)
)
@staticmethod
def python_escape_double_quotes(s):
s2 = []
for c in s:
if c == '"':
s2.append('\\"')
else:
s2.append(c)
return ''.join(s2)
@staticmethod
def python_struct_int_format(size):
if size == 4:
return 'i'
elif size == 8:
return 'Q'
else:
raise 'unknown size {}'.format(size)
def get_elf_entry(self, elf_file_path):
readelf_header = self.sh.check_output([
self.get_toolchain_tool('readelf'),
@@ -1449,11 +1473,11 @@ lunch aosp_{}-eng
env['_args_given']['emulators'] = True
env['all_emulators'] = False
self.env = env.copy()
self._init_env(self.env)
self.sh = shell_helpers.ShellHelpers(
dry_run=self.env['dry_run'],
quiet=(not show_cmds),
)
self._init_env(self.env)
self.setup_one()
ret = self.timed_main()
if not env['dry_run']:
@@ -1487,6 +1511,25 @@ lunch aosp_{}-eng
os.makedirs(self.env['p9_dir'], exist_ok=True)
os.makedirs(self.env['qemu_run_dir'], exist_ok=True)
@staticmethod
def python_escape_double_quotes(s):
s2 = []
for c in s:
if c == '"':
s2.append('\\"')
else:
s2.append(c)
return ''.join(s2)
@staticmethod
def python_struct_int_format(size):
if size == 4:
return 'i'
elif size == 8:
return 'Q'
else:
raise 'unknown size {}'.format(size)
@staticmethod
def seconds_to_hms(seconds):
'''
@@ -1906,6 +1949,24 @@ after configure, e.g. SCons. Usually contains specific targets or other build fl
return True
return False
def setup_one(self):
ccflags = []
ccflags.extend(self.env['ccflags_default'])
if 'optimization_level' in self.env:
ccflags.extend(['-O{}'.format(self.env['optimization_level']), LF])
if self.env['static']:
ccflags.extend(['-static', LF])
if 'ccflags' in self.env:
ccflags.extend(self.sh.shlex_split(self.env['ccflags']))
self.env['ccflags'] = ccflags
self.setup_one_build()
def setup_one_build(self):
'''
Called once before every build type, after BuildCliFunction::setup_one
'''
pass
def timed_main(self):
'''
Parse CLI, and to the build based on it.

13
run
View File

@@ -485,23 +485,22 @@ Extra options to append at the end of the emulator command line.
# argv[1][0] data
# argv[1][1] data
# ...
cli_args = [os.path.basename(self.env['image'])]
if self.env['cli_args'] is not None:
cli_args_split = shlex.split(self.env['cli_args'])
else:
cli_args_split = []
cli_args.extend(shlex.split(self.env['cli_args']))
argc_addr = self.env['entry_address'] + self.env['baremetal_max_text_size'] + self.env['baremetal_memory_size']
argv_addr = argc_addr + self.env['int_size']
argv_data_addr = argv_addr + len(cli_args_split) * self.env['address_size']
argv_data_addr = argv_addr + len(cli_args) * self.env['address_size']
argv_addr_data = []
argv_addr_cur = argv_data_addr
for arg in cli_args_split:
for arg in cli_args:
argv_addr_data.append(struct.pack('<{}'.format(self.python_struct_int_format(self.env['address_size'])), argv_addr_cur))
argv_addr_cur += len(arg) + 1
baremetal_cli_path = os.path.join(self.env['run_dir'], 'baremetal_cli.raw')
with open(baremetal_cli_path, 'wb') as f:
f.write(struct.pack('<{}'.format(self.python_struct_int_format(self.env['int_size'])), len(cli_args_split)))
f.write(struct.pack('<{}'.format(self.python_struct_int_format(self.env['int_size'])), len(cli_args)))
f.write(b''.join(argv_addr_data))
f.write(b'\0'.join(arg.encode() for arg in cli_args_split) + b'\0')
f.write(b'\0'.join(arg.encode() for arg in cli_args) + b'\0')
if self.env['emulator'] == 'gem5':
if self.env['quiet']:
show_stdout = False

View File

@@ -3,8 +3,10 @@
#include <stdio.h>
int main(int argc, char **argv) {
size_t i;
for (i = 0; i < (size_t)argc; ++i)
printf("%s\n", argv[i]);
int i;
printf("argc = %d\n", argc);
for (i = 0; i < argc; ++i) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}