From 66473201ebb24608a0aaf3e2184579b152ca5023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Thu, 2 Apr 2020 03:00:02 +0000 Subject: [PATCH] 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 --- README.adoc | 13 ++- baremetal/link.ld | 7 +- build-baremetal | 42 ++------- build-dhrystone | 15 +--- build-userland | 7 +- common.py | 129 ++++++++++++++++++++-------- run | 13 ++- submodules/dhrystone | 2 +- userland/c/command_line_arguments.c | 8 +- 9 files changed, 136 insertions(+), 100 deletions(-) diff --git a/README.adoc b/README.adoc index cd9a8d0..5a5b8ad 100644 --- a/README.adoc +++ b/README.adoc @@ -16449,13 +16449,22 @@ Run natively on the host: "$(./getvar --host userland_build_dir)/submodules/dhrystone/dhrystone" .... -Build for <> 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 <> 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: diff --git a/baremetal/link.ld b/baremetal/link.ld index ec13c61..d6c0a4c 100644 --- a/baremetal/link.ld +++ b/baremetal/link.ld @@ -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! */ diff --git a/build-baremetal b/build-baremetal index aaa8d14..ba6c086 100755 --- a/build-baremetal +++ b/build-baremetal @@ -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'], diff --git a/build-dhrystone b/build-dhrystone index 9273b65..8624118 100755 --- a/build-dhrystone +++ b/build-dhrystone @@ -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 diff --git a/build-userland b/build-userland index edeb140..8d15a22 100755 --- a/build-userland +++ b/build-userland @@ -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'] diff --git a/common.py b/common.py index 11c60d2..39c343c 100644 --- a/common.py +++ b/common.py @@ -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. diff --git a/run b/run index cff39d6..8692cef 100755 --- a/run +++ b/run @@ -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 diff --git a/submodules/dhrystone b/submodules/dhrystone index 1621d23..f2dfb8c 160000 --- a/submodules/dhrystone +++ b/submodules/dhrystone @@ -1 +1 @@ -Subproject commit 1621d234df82f406bd74d0231d13e6a6f3ebae9c +Subproject commit f2dfb8c4200fa18b7581a5a2d3c7321fea8d265f diff --git a/userland/c/command_line_arguments.c b/userland/c/command_line_arguments.c index e046ced..e985e8f 100644 --- a/userland/c/command_line_arguments.c +++ b/userland/c/command_line_arguments.c @@ -3,8 +3,10 @@ #include 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; }