mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-25 11:11:35 +01:00
baremetal: build userland programs using userland_and_baremetal instead of symlinks
Otherwise I'll go crazy with symlink action.
This commit is contained in:
64
README.adoc
64
README.adoc
@@ -1206,35 +1206,6 @@ The terminal prints:
|
||||
hello
|
||||
....
|
||||
|
||||
Now let's run link:baremetal/arch/aarch64/add.S[]:
|
||||
|
||||
....
|
||||
./run --arch aarch64 --baremetal baremetal/arch/aarch64/add.S
|
||||
....
|
||||
|
||||
This time, the terminal does not print anything, which indicates success.
|
||||
|
||||
If you look into the source, you will see that we just have an assertion there.
|
||||
|
||||
You can see a sample assertion fail in link:baremetal/interactive/assert_fail.c[]:
|
||||
|
||||
....
|
||||
./run --arch aarch64 --baremetal baremetal/interactive/assert_fail.c
|
||||
....
|
||||
|
||||
and the terminal contains:
|
||||
|
||||
....
|
||||
lkmc_test_fail
|
||||
error: simulation error detected by parsing logs
|
||||
....
|
||||
|
||||
and the exit status of our script is 1:
|
||||
|
||||
....
|
||||
echo $?
|
||||
....
|
||||
|
||||
To modify a baremetal program, simply edit the file, e.g.
|
||||
|
||||
....
|
||||
@@ -1252,17 +1223,44 @@ and rebuild:
|
||||
|
||||
`./build-baremetal` uses crosstool-NG, and so it must be preceded by link:build-crosstool-ng[], which `./build qemu-baremetal` also calls.
|
||||
|
||||
Alternatively, for the sake of tab completion, we also accept relative paths inside `baremetal/`, for example the following also work:
|
||||
|
||||
Now let's run link:baremetal/arch/aarch64/add.S[]:
|
||||
|
||||
....
|
||||
./run --arch aarch64 --baremetal baremetal/c/hello.c
|
||||
./run --arch aarch64 --baremetal baremetal/arch/aarch64/add.S
|
||||
....
|
||||
|
||||
Absolute paths however are used as is and must point to the actual executable:
|
||||
This time, the terminal does not print anything, which indicates success: if you look into the source, you will see that we just have an assertion there.
|
||||
|
||||
You can see a sample assertion fail in link:baremetal/interactive/assert_fail.c[]:
|
||||
|
||||
....
|
||||
./run --arch aarch64 --baremetal "$(./getvar --arch aarch64 baremetal_build_dir)/hello.elf"
|
||||
./run --arch aarch64 --baremetal userland/c/assert_fail.c
|
||||
....
|
||||
|
||||
and the terminal contains:
|
||||
|
||||
....
|
||||
lkmc_test_fail
|
||||
error: simulation error detected by parsing logs
|
||||
....
|
||||
|
||||
and the exit status of our script is 1:
|
||||
|
||||
....
|
||||
echo $?
|
||||
....
|
||||
|
||||
As suggested by the above example, several of the <<userland-content,userland examples>> can also be run in baremetal! This is largely due to the <<about-the-baremetal-setup,awesomeness of Newlib>>.
|
||||
|
||||
The examples that work include most <<c,C examples>> that don't rely on complicated syscalls such as threads, and almost all the <<userland-assembly>> examples.
|
||||
|
||||
The exact list of userland programs that work in baremetal is specified in <<path-properties>> with the `baremetal` property.
|
||||
|
||||
You can run all the baremetal examples in one go and check that all assertions passed with:
|
||||
|
||||
....
|
||||
./test-baremetal --arch aarch64
|
||||
....
|
||||
|
||||
To use gem5 instead of QEMU do:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/add.c
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/add.py
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/assert_fail.c
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/hello.c
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/infinite_loop.c
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/stderr.c
|
||||
@@ -20,6 +20,7 @@ mystart:
|
||||
bl on_exit
|
||||
|
||||
/* Run main. */
|
||||
mov x0, 0
|
||||
bl main
|
||||
|
||||
/* If main returns, exit. */
|
||||
|
||||
@@ -10,6 +10,7 @@ mystart:
|
||||
bl on_exit
|
||||
|
||||
/* Run main. */
|
||||
mov r0, 0
|
||||
bl main
|
||||
|
||||
/* If main returns, exit. */
|
||||
|
||||
@@ -14,6 +14,7 @@ class Main(common.BuildCliFunction):
|
||||
description='''\
|
||||
Build the baremetal examples with crosstool-NG.
|
||||
''',
|
||||
is_baremetal=True,
|
||||
supported_archs=common.consts['crosstool_ng_supported_archs']
|
||||
)
|
||||
self._add_argument('--ccflags')
|
||||
@@ -137,7 +138,10 @@ Build the baremetal examples with crosstool-NG.
|
||||
|
||||
def setup_one(self):
|
||||
self.env['targets'] = self.resolve_targets(
|
||||
self.env['baremetal_source_dir'],
|
||||
[
|
||||
self.env['baremetal_source_dir'],
|
||||
self.env['userland_source_dir']
|
||||
],
|
||||
self.env['targets']
|
||||
)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ class Main(common.BuildCliFunction):
|
||||
kwargs['description'] = '''\
|
||||
Build our compiled userland examples.
|
||||
'''
|
||||
super().__init__(*args, **kwargs)
|
||||
super().__init__(*args, is_userland=True, **kwargs)
|
||||
self._add_argument('--ccflags')
|
||||
self._add_argument('--force-rebuild')
|
||||
self._add_argument('--optimization-level')
|
||||
@@ -109,7 +109,7 @@ Default: build all examples that have their package dependencies met, e.g.:
|
||||
|
||||
def setup_one(self):
|
||||
self.env['targets'] = self.resolve_targets(
|
||||
self.env['userland_source_dir'],
|
||||
[self.env['userland_source_dir']],
|
||||
self.env['targets']
|
||||
)
|
||||
|
||||
|
||||
33
common.py
33
common.py
@@ -162,6 +162,7 @@ class LkmcCliFunction(cli_function.CliFunction):
|
||||
def __init__(
|
||||
self,
|
||||
*args,
|
||||
is_baremetal=False,
|
||||
is_userland=False,
|
||||
defaults=None,
|
||||
supported_archs=None,
|
||||
@@ -175,7 +176,8 @@ class LkmcCliFunction(cli_function.CliFunction):
|
||||
kwargs['extra_config_params'] = os.path.basename(inspect.getfile(self.__class__))
|
||||
if defaults is None:
|
||||
defaults = {}
|
||||
self._is_userland = is_userland
|
||||
self.is_baremetal = is_baremetal
|
||||
self.is_userland = is_userland
|
||||
self._defaults = defaults
|
||||
self._is_common = True
|
||||
self._common_args = set()
|
||||
@@ -1035,8 +1037,12 @@ lunch aosp_{}-eng
|
||||
self._common_args.add(key)
|
||||
super().add_argument(*args, **kwargs)
|
||||
|
||||
def assert_is_subpath(self, subpath, parent):
|
||||
if not self.is_subpath(subpath, parent):
|
||||
def assert_is_subpath(self, subpath, parents):
|
||||
is_subpath = False
|
||||
for parent in parents:
|
||||
if self.is_subpath(subpath, parent):
|
||||
is_subpath = True
|
||||
if not is_subpath:
|
||||
raise Exception(
|
||||
'Can only accept targets inside {}, given: {}'.format(
|
||||
parent,
|
||||
@@ -1204,7 +1210,7 @@ lunch aosp_{}-eng
|
||||
continue
|
||||
else:
|
||||
raise Exception('native emulator only supported in if target arch == host arch')
|
||||
if env['userland'] is None and not self._is_userland:
|
||||
if env['userland'] is None and not self.is_userland:
|
||||
if real_all_emulators:
|
||||
continue
|
||||
else:
|
||||
@@ -1346,13 +1352,13 @@ lunch aosp_{}-eng
|
||||
else:
|
||||
return in_path
|
||||
|
||||
def resolve_targets(self, source_dir, targets):
|
||||
def resolve_targets(self, source_dirs, targets):
|
||||
if not targets:
|
||||
targets = [source_dir]
|
||||
targets = source_dirs.copy()
|
||||
new_targets = []
|
||||
for target in targets:
|
||||
target = self.toplevel_to_source_dir(target, source_dir)
|
||||
self.assert_is_subpath(target, source_dir)
|
||||
target = self.toplevel_to_source_dir(target, source_dirs)
|
||||
self.assert_is_subpath(target, source_dirs)
|
||||
new_targets.append(target)
|
||||
return new_targets
|
||||
|
||||
@@ -1386,10 +1392,10 @@ lunch aosp_{}-eng
|
||||
'''
|
||||
pass
|
||||
|
||||
def toplevel_to_source_dir(self, path, source_dir):
|
||||
def toplevel_to_source_dir(self, path, source_dirs):
|
||||
path = os.path.abspath(path)
|
||||
if path == self.env['root_dir']:
|
||||
return source_dir
|
||||
return source_dirs
|
||||
else:
|
||||
return path
|
||||
|
||||
@@ -1491,7 +1497,12 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#gem5-debug-build
|
||||
dirpath_relative_root,
|
||||
in_basename
|
||||
))
|
||||
if my_path_properties.should_be_built(self.env, link):
|
||||
if my_path_properties.should_be_built(
|
||||
self.env,
|
||||
link,
|
||||
is_baremetal=self.is_baremetal,
|
||||
is_userland=self.is_userland
|
||||
):
|
||||
if extra_objs is None:
|
||||
extra_objs= []
|
||||
if link:
|
||||
|
||||
13
lkmc/c/add.c
13
lkmc/c/add.c
@@ -1,13 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
int i, j, k;
|
||||
i = 1;
|
||||
/* test-gdb-op1 */
|
||||
j = 2;
|
||||
/* test-gdb-op2 */
|
||||
k = i + j;
|
||||
/* test-gdb-result */
|
||||
if (k != 3)
|
||||
assert(0);
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
def test(self):
|
||||
self.sendline('tbreak main')
|
||||
self.sendline('continue')
|
||||
self.continue_to('op1')
|
||||
assert self.get_int('i') == 1
|
||||
self.continue_to('op2')
|
||||
assert self.get_int('j') == 2
|
||||
self.continue_to('result')
|
||||
assert self.get_int('k') == 3
|
||||
@@ -1,18 +0,0 @@
|
||||
/* Let's see what happens when an assert fails.
|
||||
*
|
||||
* Outcome on Ubuntu 19.04 shows the failure line:
|
||||
*
|
||||
* assert_fail.out: /path/to/linux-kernel-module-cheat/userland/c/assert_fail.c:15: main: Assertion `0' failed.
|
||||
*
|
||||
* and exit status 134 == 128 + 6, which corresponds to SIGABORT (6).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
assert(0);
|
||||
puts("here");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/* Get on character from stdin, and then print it back out.
|
||||
*
|
||||
* Same as getc(stdin).
|
||||
*
|
||||
* You have to press enter for the character to go through:
|
||||
* https://stackoverflow.com/questions/1798511/how-to-avoid-pressing-enter-with-getchar
|
||||
*
|
||||
* Used at:
|
||||
* https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1/53937376#53937376
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
char c;
|
||||
printf("enter a character: ");
|
||||
c = getchar();
|
||||
printf("you entered: %c\n", c);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/* Print hello to stdout ;-) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
puts("hello");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/* Loop infinitely. Print an integer whenever a period is reached:
|
||||
*
|
||||
* ....
|
||||
* ./infinite_loop [period]
|
||||
* ....
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uintmax_t i, j, period;
|
||||
if (argc > 1) {
|
||||
period = strtoumax(argv[1], NULL, 10);
|
||||
} else {
|
||||
period = 100000000;
|
||||
}
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (1) {
|
||||
i++;
|
||||
if (i % period == 0) {
|
||||
printf("%ju\n", j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/* Print hello to stderr. */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
fputs("hello\n", stderr);
|
||||
}
|
||||
@@ -9,6 +9,8 @@ class PathProperties:
|
||||
default_cxx_std = 'c++17'
|
||||
default_properties = {
|
||||
'allowed_archs': None,
|
||||
# Examples that can be built in baremetal.
|
||||
'baremetal': False,
|
||||
'c_std': default_c_std,
|
||||
'cc_flags': [
|
||||
'-Wall', LF,
|
||||
@@ -68,6 +70,8 @@ class PathProperties:
|
||||
# Aruments added automatically to run when running tests,
|
||||
# but not on manual running.
|
||||
'test_run_args': {},
|
||||
# Examples that can be built in userland.
|
||||
'userland': False,
|
||||
}
|
||||
|
||||
'''
|
||||
@@ -93,7 +97,13 @@ class PathProperties:
|
||||
def set_path_components(self, path_components):
|
||||
self.path_components = path_components
|
||||
|
||||
def should_be_built(self, env, link=False):
|
||||
def should_be_built(
|
||||
self,
|
||||
env,
|
||||
link=False,
|
||||
is_baremetal=False,
|
||||
is_userland=False,
|
||||
):
|
||||
if len(self.path_components) > 1 and \
|
||||
self.path_components[1] == 'libs' and \
|
||||
not env['package_all'] and \
|
||||
@@ -105,6 +115,10 @@ class PathProperties:
|
||||
self['allowed_archs'] is None or
|
||||
env['arch'] in self['allowed_archs']
|
||||
) and \
|
||||
(
|
||||
( is_userland and self['userland'] ) or
|
||||
(not is_baremetal and self['baremetal'])
|
||||
) and \
|
||||
not (
|
||||
link and
|
||||
self['no_executable']
|
||||
@@ -203,6 +217,7 @@ gnu_extension_properties = {
|
||||
'cxx_std': 'gnu++17'
|
||||
}
|
||||
freestanding_properties = {
|
||||
'baremetal': False,
|
||||
'cc_flags': [
|
||||
'-ffreestanding', LF,
|
||||
'-nostdlib', LF,
|
||||
@@ -216,6 +231,7 @@ path_properties_tuples = (
|
||||
{
|
||||
'baremetal': (
|
||||
{
|
||||
'baremetal': True,
|
||||
'extra_objs_baremetal_bootloader': True,
|
||||
'extra_objs_lkmc_common': True,
|
||||
},
|
||||
@@ -257,13 +273,6 @@ path_properties_tuples = (
|
||||
)
|
||||
}
|
||||
),
|
||||
'c': (
|
||||
{},
|
||||
{
|
||||
'assert_fail.c': {'exit_status': 134},
|
||||
'infinite_loop.c': {'more_than_1s': True},
|
||||
}
|
||||
),
|
||||
'exit1.c': {'exit_status': 1},
|
||||
'lib': {'no_executable': True},
|
||||
'getchar.c': {'interactive': True},
|
||||
@@ -271,6 +280,10 @@ path_properties_tuples = (
|
||||
'return2.c': {'exit_status': 2},
|
||||
}
|
||||
),
|
||||
'lkmc.c': {
|
||||
'baremetal': True,
|
||||
'userland': True,
|
||||
}
|
||||
'userland': (
|
||||
{
|
||||
'cc_flags': [
|
||||
@@ -279,10 +292,12 @@ path_properties_tuples = (
|
||||
'cc_flags_after': [
|
||||
'-pthread', LF,
|
||||
],
|
||||
'userland': True,
|
||||
},
|
||||
{
|
||||
'arch': (
|
||||
{
|
||||
'baremetal': True,
|
||||
'extra_objs_lkmc_common': True,
|
||||
},
|
||||
{
|
||||
@@ -366,7 +381,9 @@ path_properties_tuples = (
|
||||
}
|
||||
),
|
||||
'c': (
|
||||
{},
|
||||
{
|
||||
'baremetal': True,
|
||||
},
|
||||
{
|
||||
'assert_fail.c': {
|
||||
'exit_status': 134,
|
||||
|
||||
@@ -11,6 +11,7 @@ import thread_pool
|
||||
class Main(common.TestCliFunction):
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
is_baremetal=True,
|
||||
supported_archs=common.consts['crosstool_ng_supported_archs'],
|
||||
)
|
||||
self.add_argument(
|
||||
@@ -23,7 +24,10 @@ If given, run only the given tests. Otherwise, run all tests.
|
||||
|
||||
def setup_one(self):
|
||||
self.env['tests'] = self.resolve_targets(
|
||||
self.env['baremetal_source_dir'],
|
||||
[
|
||||
self.env['baremetal_source_dir'],
|
||||
self.env['userland_source_dir']
|
||||
],
|
||||
self.env['tests']
|
||||
)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ If given, run only the given tests. Otherwise, run all tests.
|
||||
|
||||
def setup_one(self):
|
||||
self.env['tests'] = self.resolve_targets(
|
||||
self.env['userland_source_dir'],
|
||||
[self.env['userland_source_dir']],
|
||||
self.env['tests']
|
||||
)
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/add.c
|
||||
13
userland/c/add.c
Normal file
13
userland/c/add.c
Normal file
@@ -0,0 +1,13 @@
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
int i, j, k;
|
||||
i = 1;
|
||||
/* test-gdb-op1 */
|
||||
j = 2;
|
||||
/* test-gdb-op2 */
|
||||
k = i + j;
|
||||
/* test-gdb-result */
|
||||
if (k != 3)
|
||||
assert(0);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/add.py
|
||||
9
userland/c/add.py
Normal file
9
userland/c/add.py
Normal file
@@ -0,0 +1,9 @@
|
||||
def test(self):
|
||||
self.sendline('tbreak main')
|
||||
self.sendline('continue')
|
||||
self.continue_to('op1')
|
||||
assert self.get_int('i') == 1
|
||||
self.continue_to('op2')
|
||||
assert self.get_int('j') == 2
|
||||
self.continue_to('result')
|
||||
assert self.get_int('k') == 3
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/assert_fail.c
|
||||
18
userland/c/assert_fail.c
Normal file
18
userland/c/assert_fail.c
Normal file
@@ -0,0 +1,18 @@
|
||||
/* Let's see what happens when an assert fails.
|
||||
*
|
||||
* Outcome on Ubuntu 19.04 shows the failure line:
|
||||
*
|
||||
* assert_fail.out: /path/to/linux-kernel-module-cheat/userland/c/assert_fail.c:15: main: Assertion `0' failed.
|
||||
*
|
||||
* and exit status 134 == 128 + 6, which corresponds to SIGABORT (6).
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
assert(0);
|
||||
puts("here");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/getchar.c
|
||||
21
userland/c/getchar.c
Normal file
21
userland/c/getchar.c
Normal file
@@ -0,0 +1,21 @@
|
||||
/* Get on character from stdin, and then print it back out.
|
||||
*
|
||||
* Same as getc(stdin).
|
||||
*
|
||||
* You have to press enter for the character to go through:
|
||||
* https://stackoverflow.com/questions/1798511/how-to-avoid-pressing-enter-with-getchar
|
||||
*
|
||||
* Used at:
|
||||
* https://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1/53937376#53937376
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
char c;
|
||||
printf("enter a character: ");
|
||||
c = getchar();
|
||||
printf("you entered: %c\n", c);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/hello.c
|
||||
9
userland/c/hello.c
Normal file
9
userland/c/hello.c
Normal file
@@ -0,0 +1,9 @@
|
||||
/* Print hello to stdout ;-) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(void) {
|
||||
puts("hello");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/infinite_loop.c
|
||||
29
userland/c/infinite_loop.c
Normal file
29
userland/c/infinite_loop.c
Normal file
@@ -0,0 +1,29 @@
|
||||
/* Loop infinitely. Print an integer whenever a period is reached:
|
||||
*
|
||||
* ....
|
||||
* ./infinite_loop [period]
|
||||
* ....
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
uintmax_t i, j, period;
|
||||
if (argc > 1) {
|
||||
period = strtoumax(argv[1], NULL, 10);
|
||||
} else {
|
||||
period = 100000000;
|
||||
}
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (1) {
|
||||
i++;
|
||||
if (i % period == 0) {
|
||||
printf("%ju\n", j);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../lkmc/c/stderr.c
|
||||
7
userland/c/stderr.c
Normal file
7
userland/c/stderr.c
Normal file
@@ -0,0 +1,7 @@
|
||||
/* Print hello to stderr. */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
fputs("hello\n", stderr);
|
||||
}
|
||||
Reference in New Issue
Block a user