Fwd: [PATCH v2] cmd: move ELF load and boot to lib/elf.c

Максим Москалец maximmosk4 at gmail.com
Mon May 20 13:51:20 CEST 2024


On 18.05.2024 12:40, Heinrich Schuchardt wrote:
> On 5/13/24 20:17, Maxim Moskalets wrote:
>> From: Maxim Moskalets <maximmosk4 at gmail.com>
>>
>> Loading and running the ELF image is the responsibility of the
>> library and should not be associated with the command line interface.
>>
>> It is also required to run ELF images from FIT with the bootm command
>> so as not to depend on the command line interface.
>>
>> Signed-off-by: Maxim Moskalets <maximmosk4 at gmail.com>
>> ---
>>   cmd/elf.c     | 49 +++++++++++++++++--------------------------------
>>   include/elf.h | 10 ++++++++++
>>   lib/elf.c     | 38 ++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 65 insertions(+), 32 deletions(-)
>>
>> diff --git a/cmd/elf.c b/cmd/elf.c
>> index a02361f9f5..1fb955ae41 100644
>> --- a/cmd/elf.c
>> +++ b/cmd/elf.c
>> @@ -19,21 +19,6 @@
>>   #include <linux/linkage.h>
>>   #endif
>>
>> -/* Allow ports to override the default behavior */
>> -static unsigned long do_bootelf_exec(ulong (*entry)(int, char *
>> const[]),
>> -                     int argc, char *const argv[])
>> -{
>> -    unsigned long ret;
>> -
>> -    /*
>> -     * pass address parameter as argv[0] (aka command name),
>> -     * and all remaining args
>
> Carving out the library function is a good idea.
>
> Nits
>
> %s/args/arguments/
>
>> -     */
>> -    ret = entry(argc, argv);
>> -
>> -    return ret;
>> -}
>> -
>>   /* Interpreter command to boot an arbitrary ELF image from memory */
>
> Please, document functions fully:
>
>
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
>
>
> The preferred place for documenting exported functions is in the header
> file.
>

Not my code, but okay.

>>   int do_bootelf(struct cmd_tbl *cmdtp, int flag, int argc, char
>> *const argv[])
>>   {
>> @@ -43,8 +28,8 @@ int do_bootelf(struct cmd_tbl *cmdtp, int flag, int
>> argc, char *const argv[])
>>   #endif
>>       unsigned long addr; /* Address of the ELF image */
>>       unsigned long rc; /* Return value from user code */
>> -    char *sload = NULL;
>>       int rcode = 0;
>
> Maybe
>
> %s/0/CMD_RET_SUCCESS/
>
>> +    Bootelf_flags flags = {0};
>>
>>       /* Consume 'bootelf' */
>>       argc--; argv++;
>> @@ -52,7 +37,10 @@ int do_bootelf(struct cmd_tbl *cmdtp, int flag,
>> int argc, char *const argv[])
>>       /* Check for [-p|-s] flag. */
>>       if (argc >= 1 && (argv[0][0] == '-' && \
>>                   (argv[0][1] == 'p' || argv[0][1] == 's'))) {
>> -        sload = argv[0];
>> +        if (argv[0][1] == 'p')
>> +            flags.phdr = 1;
>> +        printf("## Try to elf hdr format %s\n",
>> +                flags.phdr ? "phdr" : "shdr");
>
> Please, use log_debug() for debug messages and avoid the '## ' noise.
>
> Do you mean:
>
> log_debug("Using ELF header format %s\n",
>

It was in original implementation, but i can fix.

>>           /* Consume flag. */
>>           argc--; argv++;
>>       }
>> @@ -75,14 +63,6 @@ int do_bootelf(struct cmd_tbl *cmdtp, int flag,
>> int argc, char *const argv[])
>>       } else
>>           addr = image_load_addr;
>>
>> -    if (!valid_elf_image(addr))
>> -        return 1;
>> -
>> -    if (sload && sload[1] == 'p')
>> -        addr = load_elf_image_phdr(addr);
>> -    else
>> -        addr = load_elf_image_shdr(addr);
>> -
>>   #if CONFIG_IS_ENABLED(CMD_ELF_FDT_SETUP)
>>       if (fdt_addr) {
>>           printf("## Setting up FDT at 0x%08lx ...\n", fdt_addr);
>
> This looks like a debug message too.
>
>> @@ -93,21 +73,26 @@ int do_bootelf(struct cmd_tbl *cmdtp, int flag,
>> int argc, char *const argv[])
>>       }
>>   #endif
>>
>> -    if (!env_get_autostart())
>> -        return rcode;
>> -
>> -    printf("## Starting application at 0x%08lx ...\n", addr);
>> -    flush();
>> +    if (env_get_autostart()) {
>> +        flags.autostart = 1;
>> +        printf("## Starting application at 0x%08lx ...\n", addr);
>
> log_debug("Starting application at 0x%08lx ...\n", addr);
>
>> +        flush();
>> +    }
>>
>>       /*
>>        * pass address parameter as argv[0] (aka command name),
>>        * and all remaining args
>>        */
>> -    rc = do_bootelf_exec((void *)addr, argc, argv);
>> +    rc = bootelf(addr, flags, argc, argv);
>>       if (rc != 0)
>>           rcode = 1;
>
> %s/1/CMD_RET_FAILURE/
>
>>
>> -    printf("## Application terminated, rc = 0x%lx\n", rc);
>> +    if (flags.autostart) {
>> +        if (ENOEXEC == errno)
>
> if (rc && ENOEXEC == errno)
>
>> +            printf("## Invalid ELF image\n");
>
> log_err("Invalid ELF image\n");
>
>> +        else
>> +            printf("## Application terminated, rc = 0x%lx\n", rc);
>
> This message is only relevant if the application failed:
>
> else if (rc)
>     log_err("Application failed, rc = 0x%lx\n", rc);
>
>> +    }
>>
>>       return rcode;
>>   }
>> diff --git a/include/elf.h b/include/elf.h
>> index a4ba74d8ab..b88e6cf403 100644
>> --- a/include/elf.h
>> +++ b/include/elf.h
>> @@ -12,6 +12,12 @@
>>   #ifndef __ASSEMBLY__
>>   #include "compiler.h"
>>
>> +/* Flag param bits for bootelf() function */
>> +typedef struct {
>> +    unsigned phdr      : 1; /* load via program (not section)
>> headers */
>> +    unsigned autostart : 1; /* Start ELF after loading */
>> +} Bootelf_flags;
>> +
>>   /* This version doesn't work for 64-bit ABIs - Erik */
>
> If the elf command does not work in certain ABIs we need to ensure via
> Kconfig that it is not build for these.
>

Not my comment, i don't know.

>>
>>   /* These typedefs need to be handled better */
>> @@ -700,6 +706,10 @@ unsigned long elf_hash(const unsigned char *name);
>>   #define R_RISCV_RELATIVE    3
>>
>>   #ifndef __ASSEMBLY__
>> +unsigned long bootelf_exec(ulong (*entry)(int, char * const[]),
>> +               int argc, char *const argv[]);
>> +unsigned long bootelf(unsigned long addr, Bootelf_flags flags,
>> +              int argc, char *const argv[]);
>>   int valid_elf_image(unsigned long addr);
>>   unsigned long load_elf64_image_phdr(unsigned long addr);
>>   unsigned long load_elf64_image_shdr(unsigned long addr);
>> diff --git a/lib/elf.c b/lib/elf.c
>> index 9a794f9cba..f8eeceef3d 100644
>> --- a/lib/elf.c
>> +++ b/lib/elf.c
>> @@ -7,6 +7,7 @@
>>   #include <cpu_func.h>
>>   #include <elf.h>
>>   #include <env.h>
>> +#include <errno.h>
>>   #include <net.h>
>>   #include <vxworks.h>
>>   #ifdef CONFIG_X86
>> @@ -15,6 +16,43 @@
>>   #include <linux/linkage.h>
>>   #endif
>>
>> +/* Allow ports to override the default behavior */
>
> Please, fully document the function.
>
>> +unsigned long bootelf_exec(ulong (*entry)(int, char * const[]),
>> +               int argc, char *const argv[])
>> +{
>> +    return entry(argc, argv);
>> +}
>> +
>> +/*
>> + * @brief Boot ELF from memory
>> + *
>> + * @param addr       Loading address of ELF in memory
>> + * @param flags      Bits like ELF_PHDR to control boot details
>> + * @param argc, argv May be used to pass command line arguments
>> (maybe unused)
>> + *             Necessary for backward compatibility with the CLI
>> command
>> + * @retuen Number returned by ELF application
>
> This does not match our documentation style, see
>
>
https://www.kernel.org/doc/html/latest/doc-guide/kernel-doc.html#function-documentation
>
>
>> + *
>> + * Sets errno = ENOEXEC if ELF image is not valid
>
> %s/if ELF/if the ELF/
>
>> + */
>> +unsigned long bootelf(unsigned long addr, Bootelf_flags flags,
>> +              int argc, char *const argv[])
>> +{
>> +    unsigned long entry_addr;
>> +
>> +    if (!valid_elf_image(addr)) {
>> +        errno = ENOEXEC;
>> +        return 1;
>> +    }
>> +
>> +    entry_addr = flags.phdr ? load_elf_image_phdr(addr)
>> +                        : load_elf_image_shdr(addr);
>> +
>> +    if (!flags.autostart)
>> +        return 0;
>
> Please, return early. We don't need entry_addr here.
>
>> +
>> +    return bootelf_exec((void *)entry_addr, argc, argv);;
>
> Duplicate ';'.
>
> An ELF binary may expect that argc >= 1 and argv[0] being the binary
> name. How do we ensure this?
>

Can i use dummy argc = 1 and argv =  {"", NULL}; if (!argc && !argv)?

> We should describe in a man-page doc/usage/cmd/elf.rst that the first
> extraneous argument passed to the elf command is argv[0]?
>
It is impossible for bootelf CLI command. In FIT we can describe behavior
(in next patch with ELFs in FIT).

> On some paths we have not set errno which means it has a random value
> set in unrelated functions. How would we able to evaluate errno in the
> caller if the return value is non-zero?
>
> Please, initialize errno to 0 at the start of the function.
>
> We need a test case. E.g. we could build an ELF binary returning the
> CRC32 of the concatenated arguments.
>
Not in this patch, but okay. What about example in sandbox tests
or/and example app?

Best regards

Maxim

> Best regards
>
> Heinrich
>
>> +}
>> +
>>   /*
>>    * A very simple ELF64 loader, assumes the image is valid, returns the
>>    * entry point address.
>


More information about the U-Boot mailing list