Merge pull request #34347 from poettering/uki-with-many-bootctl

bootctl: multi-profile UKI support
This commit is contained in:
Lennart Poettering
2024-09-15 09:06:58 +02:00
committed by GitHub
6 changed files with 359 additions and 130 deletions
+22
View File
@@ -31,3 +31,25 @@ static inline bool unified_section_measure(UnifiedSection section) {
/* Max number of profiles per UKI */
#define UNIFIED_PROFILES_MAX 256U
/* The native PE machine type, if known, for a full list see:
* https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types */
#ifndef _IMAGE_FILE_MACHINE_NATIVE
# if defined(__x86_64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x8664)
# elif defined(__i386__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x014c)
# elif defined(__ia64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x0200)
# elif defined(__aarch64__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0xaa64)
# elif defined(__arm__)
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x01c0)
# elif defined(__riscv)
# if __SIZEOF_POINTER__ == 4
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x5032)
# elif __SIZEOF_POINTER__ == 8
# define _IMAGE_FILE_MACHINE_NATIVE UINT16_C(0x5064)
# endif
# endif
#endif
+256 -100
View File
@@ -23,6 +23,7 @@
#include "string-table.h"
#include "strv.h"
#include "terminal-util.h"
#include "uki.h"
#include "unaligned.h"
static const char* const boot_entry_type_table[_BOOT_ENTRY_TYPE_MAX] = {
@@ -48,6 +49,7 @@ static void boot_entry_free(BootEntry *entry) {
free(entry->id);
free(entry->id_old);
free(entry->id_without_profile);
free(entry->path);
free(entry->root);
free(entry->title);
@@ -529,10 +531,18 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) {
return r;
}
r = -strverscmp_improved(a->id, b->id);
r = -strverscmp_improved(a->id_without_profile ?: a->id, b->id_without_profile ?: b->id);
if (r != 0)
return r;
if (a->id_without_profile && b->id_without_profile) {
/* The strverscmp_improved() call above already established that we are talking about the
* same image here, hence order by profile, if there is one */
r = CMP(a->profile, b->profile);
if (r != 0)
return r;
}
if (a->tries_left != UINT_MAX || b->tries_left != UINT_MAX)
return 0;
@@ -636,28 +646,30 @@ static int boot_entries_find_type1(
static int boot_entry_load_unified(
const char *root,
const char *path,
const char *osrelease,
const char *cmdline,
unsigned profile,
const char *osrelease_text,
const char *profile_text,
const char *cmdline_text,
BootEntry *ret) {
_cleanup_free_ char *fname = NULL, *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL,
*os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL;
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_UNIFIED);
const char *k, *good_name, *good_version, *good_sort_key;
_cleanup_fclose_ FILE *f = NULL;
int r;
assert(root);
assert(path);
assert(osrelease);
assert(osrelease_text);
assert(ret);
k = path_startswith(path, root);
if (!k)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path);
f = fmemopen_unlocked((void*) osrelease, strlen(osrelease), "r");
f = fmemopen_unlocked((void*) osrelease_text, strlen(osrelease_text), "r");
if (!f)
return log_error_errno(errno, "Failed to open os-release buffer: %m");
return log_oom();
r = parse_env_file(f, "os-release",
"PRETTY_NAME", &os_pretty_name,
@@ -685,10 +697,28 @@ static int boot_entry_load_unified(
&good_sort_key))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path);
_cleanup_free_ char *profile_id = NULL, *profile_title = NULL;
if (profile_text) {
fclose(f);
f = fmemopen_unlocked((void*) profile_text, strlen(profile_text), "r");
if (!f)
return log_oom();
r = parse_env_file(
f, "profile",
"ID", &profile_id,
"TITLE", &profile_title);
if (r < 0)
return log_error_errno(r, "Failed to parse profile data from unified kernel image '%s': %m", path);
}
r = path_extract_filename(path, &fname);
if (r < 0)
return log_error_errno(r, "Failed to extract file name from '%s': %m", path);
_cleanup_(boot_entry_free) BootEntry tmp = BOOT_ENTRY_INIT(BOOT_ENTRY_UNIFIED);
r = boot_filename_extract_tries(fname, &tmp.id, &tmp.tries_left, &tmp.tries_done);
if (r < 0)
return r;
@@ -696,6 +726,19 @@ static int boot_entry_load_unified(
if (!efi_loader_entry_name_valid(tmp.id))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id);
tmp.profile = profile;
if (profile_id || profile > 0) {
tmp.id_without_profile = TAKE_PTR(tmp.id);
if (profile_id)
tmp.id = strjoin(tmp.id_without_profile, "@", profile_id);
else
(void) asprintf(&tmp.id, "%s@%u", tmp.id_without_profile, profile);
if (!tmp.id)
return log_oom();
}
if (os_id && os_version_id) {
tmp.id_old = strjoin(os_id, "-", os_version_id);
if (!tmp.id_old)
@@ -714,13 +757,18 @@ static int boot_entry_load_unified(
if (!tmp.kernel)
return log_oom();
tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE));
tmp.options = strv_new(cmdline_text);
if (!tmp.options)
return log_oom();
delete_trailing_chars(tmp.options[0], WHITESPACE);
tmp.title = strdup(good_name);
if (profile_title)
tmp.title = strjoin(good_name, " (", profile_title, ")");
else if (profile_id)
tmp.title = strjoin(good_name, " (", profile_id, ")");
else if (profile > 0)
(void) asprintf(&tmp.title, "%s (@%u)", good_name, profile);
else
tmp.title = strdup(good_name);
if (!tmp.title)
return log_oom();
@@ -740,11 +788,7 @@ static int boot_entry_load_unified(
return 0;
}
/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
* the ones we do care about and we are willing to load into memory have this size limit.) */
#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
static int find_sections(
static int pe_load_headers_and_sections(
int fd,
const char *path,
IMAGE_SECTION_HEADER **ret_sections,
@@ -774,92 +818,177 @@ static int find_sections(
return 0;
}
static int find_cmdline_section(
int fd,
const char *path,
IMAGE_SECTION_HEADER *sections,
PeHeader *pe_header,
char **ret_cmdline) {
static const IMAGE_SECTION_HEADER* pe_find_profile_section_table(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
unsigned profile,
size_t *ret_n_sections) {
int r;
char *cmdline = NULL, *t = NULL;
_cleanup_free_ char *word = NULL;
assert(pe_header);
assert(path);
/* Looks for the part of the section table that defines the specified profile. If 'profile' is
* specified as UINT_MAX this will look for the base profile. */
if (!ret_cmdline)
if (le16toh(pe_header->pe.NumberOfSections) == 0)
return NULL;
assert(sections);
const IMAGE_SECTION_HEADER
*p = sections,
*e = sections + le16toh(pe_header->pe.NumberOfSections),
*start = profile == UINT_MAX ? sections : NULL,
*end;
unsigned current_profile = UINT_MAX;
for (;;) {
p = pe_section_table_find(p, e - p, ".profile");
if (!p) {
end = e;
break;
}
if (current_profile == profile) {
end = p;
break;
}
if (current_profile == UINT_MAX)
current_profile = 0;
else
current_profile++;
if (current_profile == profile)
start = p;
p++; /* Continue scanning after the .profile entry we just found */
}
if (!start)
return NULL;
if (ret_n_sections)
*ret_n_sections = end - start;
return start;
}
static int trim_cmdline(char **cmdline) {
assert(cmdline);
/* Strips leading and trailing whitespace from command line */
if (!*cmdline)
return 0;
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_SIZE_MAX, (void**) &cmdline, NULL);
if (r == -ENXIO) { /* cmdline is optional */
*ret_cmdline = NULL;
const char *skipped = skip_leading_chars(*cmdline, WHITESPACE);
if (isempty(skipped)) {
*cmdline = mfree(*cmdline);
return 0;
}
if (r < 0)
return log_warning_errno(r, "Failed to read .cmdline section of '%s': %m", path);
word = strdup(cmdline);
if (!word)
return log_oom();
if (skipped != *cmdline) {
_cleanup_free_ char *c = strdup(skipped);
if (!c)
return -ENOMEM;
/* Quick test to check if there is actual content in the addon cmdline */
t = delete_chars(word, NULL);
if (isempty(t))
*ret_cmdline = NULL;
else
*ret_cmdline = TAKE_PTR(cmdline);
free_and_replace(*cmdline, c);
}
return 0;
delete_trailing_chars(*cmdline, WHITESPACE);
return 1;
}
static int find_osrel_section(
int fd,
const char *path,
IMAGE_SECTION_HEADER *sections,
PeHeader *pe_header,
char **ret_osrelease) {
/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but
* the ones we do care about and we are willing to load into memory have this size limit.) */
#define PE_SECTION_SIZE_MAX (4U*1024U*1024U)
int r;
if (!ret_osrelease)
return 0;
r = pe_read_section_data(fd, pe_header, sections, ".osrel", PE_SECTION_SIZE_MAX, (void**) ret_osrelease, NULL);
if (r < 0)
return log_error_errno(r, "Failed to read .osrel section of '%s': %m", path);
return 0;
}
static int find_uki_sections(
static int pe_find_uki_sections(
int fd,
const char *path,
unsigned profile,
char **ret_osrelease,
char **ret_profile,
char **ret_cmdline) {
_cleanup_free_ char *osrelease_text = NULL, *profile_text = NULL, *cmdline_text = NULL;
_cleanup_free_ IMAGE_SECTION_HEADER *sections = NULL;
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
r = find_sections(fd, path, &sections, &pe_header);
assert(fd >= 0);
assert(path);
assert(profile != UINT_MAX);
assert(ret_osrelease);
assert(ret_profile);
assert(ret_cmdline);
r = pe_load_headers_and_sections(fd, path, &sections, &pe_header);
if (r < 0)
return r;
if (!pe_is_uki(pe_header, sections))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Parsed PE file '%s' is not a UKI.", path);
r = find_osrel_section(fd, path, sections, pe_header, ret_osrelease);
if (r < 0)
return r;
if (!pe_is_native(pe_header)) /* Don't process non-native UKIs */
goto nothing;
r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
if (r < 0)
return r;
/* Find part of the section table for this profile */
size_t n_psections = 0;
const IMAGE_SECTION_HEADER *psections = pe_find_profile_section_table(pe_header, sections, profile, &n_psections);
if (!psections && profile != 0) /* Profile not found? (Profile @0 needs no explicit .profile!) */
goto nothing;
/* Find base profile part of section table */
size_t n_bsections;
const IMAGE_SECTION_HEADER *bsections = ASSERT_PTR(pe_find_profile_section_table(pe_header, sections, UINT_MAX, &n_bsections));
struct {
const char *name;
char **data;
} table[] = {
{ ".osrel", &osrelease_text },
{ ".profile", &profile_text },
{ ".cmdline", &cmdline_text },
};
FOREACH_ELEMENT(t, table) {
const IMAGE_SECTION_HEADER *found;
/* First look in the profile part of the section table, and if we don't find anything there, look into the base part */
found = pe_section_table_find(psections, n_psections, t->name);
if (!found) {
found = pe_section_table_find(bsections, n_bsections, t->name);
if (!found)
continue;
}
/* Permit "masking" of sections in the base profile */
if (found->VirtualSize == 0)
continue;
r = pe_read_section_data(fd, found, PE_SECTION_SIZE_MAX, (void**) t->data, /* ret_data= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to load contents of section '%s': %m", t->name);
}
if (!osrelease_text)
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Unified kernel image lacks .osrel data for profile @%u, refusing.", profile);
if (trim_cmdline(&cmdline_text) < 0)
return log_oom();
*ret_osrelease = TAKE_PTR(osrelease_text);
*ret_profile = TAKE_PTR(profile_text);
*ret_cmdline = TAKE_PTR(cmdline_text);
return 1;
nothing:
*ret_osrelease = *ret_profile = *ret_cmdline = NULL;
return 0;
}
static int find_addon_sections(
static int pe_find_addon_sections(
int fd,
const char *path,
char **ret_cmdline) {
@@ -868,19 +997,39 @@ static int find_addon_sections(
_cleanup_free_ PeHeader *pe_header = NULL;
int r;
r = find_sections(fd, path, &sections, &pe_header);
assert(fd >= 0);
assert(path);
r = pe_load_headers_and_sections(fd, path, &sections, &pe_header);
if (r < 0)
return r;
r = find_cmdline_section(fd, path, sections, pe_header, ret_cmdline);
/* If addon cmdline is empty or contains just separators,
* don't bother tracking it.
* Don't check r because it cannot return <0 if cmdline is empty,
* as cmdline is always optional. */
if (!ret_cmdline)
return log_warning_errno(SYNTHETIC_ERRNO(ENOENT), "Addon %s contains empty cmdline and will be therefore ignored.", path);
if (!pe_is_addon(pe_header, sections))
return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Parse PE file '%s' is not an add-on.", path);
return r;
/* Define early, before the gotos below */
_cleanup_free_ char *cmdline_text = NULL;
if (!pe_is_native(pe_header))
goto nothing;
const IMAGE_SECTION_HEADER *found = pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), ".cmdline");
if (!found)
goto nothing;
r = pe_read_section_data(fd, found, PE_SECTION_SIZE_MAX, (void**) &cmdline_text, /* ret_size= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to load contents of section '.cmdline': %m");
if (trim_cmdline(&cmdline_text) < 0)
return log_oom();
*ret_cmdline = TAKE_PTR(cmdline_text);
return 1;
nothing:
*ret_cmdline = NULL;
return 0;
}
static int insert_boot_entry_addon(
@@ -959,7 +1108,7 @@ static int boot_entries_find_unified_addons(
if (!j)
return log_oom();
if (find_addon_sections(fd, j, &cmdline) < 0)
if (pe_find_addon_sections(fd, j, &cmdline) <= 0)
continue;
location = strdup(j);
@@ -1032,19 +1181,13 @@ static int boot_entries_find_unified(
return log_error_errno(r, "Failed to open '%s/%s': %m", root, dir);
FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", full)) {
_cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL;
_cleanup_close_ int fd = -EBADF;
if (!dirent_is_file(de))
continue;
if (!endswith_no_case(de->d_name, ".efi"))
continue;
if (!GREEDY_REALLOC0(config->entries, config->n_entries + 1))
return log_oom();
fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOFOLLOW|O_NOCTTY);
_cleanup_close_ int fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK|O_NOFOLLOW|O_NOCTTY);
if (fd < 0) {
log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", full, de->d_name);
continue;
@@ -1056,23 +1199,30 @@ static int boot_entries_find_unified(
if (r == 0) /* inode already seen or otherwise not relevant */
continue;
j = path_join(full, de->d_name);
_cleanup_free_ char *j = path_join(full, de->d_name);
if (!j)
return log_oom();
if (find_uki_sections(fd, j, &osrelease, &cmdline) < 0)
continue;
for (unsigned p = 0; p < UNIFIED_PROFILES_MAX; p++) {
_cleanup_free_ char *osrelease = NULL, *profile = NULL, *cmdline = NULL;
r = boot_entry_load_unified(root, j, osrelease, cmdline, config->entries + config->n_entries);
if (r < 0)
continue;
r = pe_find_uki_sections(fd, j, p, &osrelease, &profile, &cmdline);
if (r == 0) /* this profile does not exist, we are done */
break;
if (r < 0)
continue;
/* look for .efi.extra.d */
r = boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, config->entries + config->n_entries);
if (r < 0)
continue;
if (!GREEDY_REALLOC0(config->entries, config->n_entries + 2))
return log_oom();
config->n_entries++;
if (boot_entry_load_unified(root, j, p, osrelease, profile, cmdline, config->entries + config->n_entries) < 0)
continue;
config->n_entries++;
/* look for .efi.extra.d */
(void) boot_entries_find_unified_local_addons(config, dirfd(d), de->d_name, full, config->entries + config->n_entries);
}
}
return 0;
@@ -1648,8 +1798,14 @@ int show_boot_entry(
putchar('\n');
if (e->id)
printf(" id: %s\n", e->id);
if (e->id) {
printf(" id: %s", e->id);
if (e->id_without_profile && !streq_ptr(e->id, e->id_without_profile))
printf(" (without profile: %s)\n", e->id_without_profile);
else
putchar('\n');
}
if (e->path) {
_cleanup_free_ char *text = NULL, *link = NULL;
@@ -1673,7 +1829,7 @@ int show_boot_entry(
if (e->tries_done != UINT_MAX)
printf("; %u done\n", e->tries_done);
else
printf("\n");
putchar('\n');
}
if (e->sort_key)
+2
View File
@@ -38,6 +38,7 @@ typedef struct BootEntry {
bool reported_by_loader;
char *id; /* This is the file basename (including extension!) */
char *id_old; /* Old-style ID, for deduplication purposes. */
char *id_without_profile; /* id without profile suffixed */
char *path; /* This is the full path to the drop-in file */
char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */
char *title;
@@ -55,6 +56,7 @@ typedef struct BootEntry {
char **device_tree_overlay;
unsigned tries_left;
unsigned tries_done;
unsigned profile;
} BootEntry;
#define BOOT_ENTRY_INIT(t) \
+3 -3
View File
@@ -36,7 +36,7 @@ static int uki_read_pretty_name(
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
assert(ret);
r = pe_read_section_data(
r = pe_read_section_data_by_name(
fd,
pe_header,
sections,
@@ -91,13 +91,13 @@ static int inspect_uki(
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
if (ret_cmdline) {
r = pe_read_section_data(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL);
r = pe_read_section_data_by_name(fd, pe_header, sections, ".cmdline", PE_SECTION_READ_MAX, (void**) &cmdline, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
if (ret_uname) {
r = pe_read_section_data(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL);
r = pe_read_section_data_by_name(fd, pe_header, sections, ".uname", PE_SECTION_READ_MAX, (void**) &uname, NULL);
if (r < 0 && r != -ENXIO) /* If the section doesn't exist, that's fine */
return r;
}
+70 -26
View File
@@ -6,6 +6,7 @@
#include "log.h"
#include "pe-binary.h"
#include "string-util.h"
#include "uki.h"
bool pe_header_is_64bit(const PeHeader *h) {
assert(h);
@@ -37,22 +38,21 @@ const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(
return PE_HEADER_OPTIONAL_FIELD(h, DataDirectory) + i;
}
const IMAGE_SECTION_HEADER *pe_header_find_section(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *pe_section_table_find(
const IMAGE_SECTION_HEADER *sections,
size_t n_sections,
const char *name) {
size_t n;
assert(pe_header);
assert(name);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
assert(sections || n_sections == 0);
n = strlen(name);
if (n > sizeof(sections[0].Name)) /* Too long? */
return NULL;
FOREACH_ARRAY(section, sections, le16toh(pe_header->pe.NumberOfSections))
FOREACH_ARRAY(section, sections, n_sections)
if (memcmp(section->Name, name, n) == 0 &&
memeqzero(section->Name + n, sizeof(section->Name) - n))
return section;
@@ -60,6 +60,16 @@ const IMAGE_SECTION_HEADER *pe_header_find_section(
return NULL;
}
const IMAGE_SECTION_HEADER* pe_header_find_section(
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name) {
assert(pe_header);
return pe_section_table_find(sections, le16toh(pe_header->pe.NumberOfSections), name);
}
int pe_load_headers(
int fd,
IMAGE_DOS_HEADER **ret_dos_header,
@@ -173,43 +183,28 @@ int pe_load_sections(
int pe_read_section_data(
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name,
const IMAGE_SECTION_HEADER *section,
size_t max_size,
void **ret,
size_t *ret_size) {
const IMAGE_SECTION_HEADER *section;
_cleanup_free_ void *data = NULL;
size_t n;
ssize_t ss;
assert(fd >= 0);
assert(pe_header);
assert(sections || pe_header->pe.NumberOfSections == 0);
assert(name);
assert(section);
section = pe_header_find_section(pe_header, sections, name);
if (!section)
return -ENXIO;
n = le32toh(section->VirtualSize);
size_t n = le32toh(section->VirtualSize);
if (n > MIN(max_size, (size_t) SSIZE_MAX))
return -E2BIG;
data = malloc(n+1);
_cleanup_free_ void *data = malloc(n+1);
if (!data)
return -ENOMEM;
ss = pread(fd, data, n, le32toh(section->PointerToRawData));
ssize_t ss = pread(fd, data, n, le32toh(section->PointerToRawData));
if (ss < 0)
return -errno;
if ((size_t) ss != n)
return -EIO;
((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
if (ret_size)
*ret_size = n;
else {
@@ -221,12 +216,37 @@ int pe_read_section_data(
if (nul && !memeqzero(nul, n - (nul - (const char*) data))) /* If there's a NUL it must only be NULs from there on */
return -EBADMSG;
}
if (ret)
if (ret) {
((uint8_t*) data)[n] = 0; /* NUL terminate, no matter what */
*ret = TAKE_PTR(data);
}
return 0;
}
int pe_read_section_data_by_name(
int fd,
const PeHeader *pe_header,
const IMAGE_SECTION_HEADER *sections,
const char *name,
size_t max_size,
void **ret,
size_t *ret_size) {
const IMAGE_SECTION_HEADER *section;
assert(fd >= 0);
assert(pe_header);
assert(sections || pe_header->pe.NumberOfSections == 0);
assert(name);
section = pe_header_find_section(pe_header, sections, name);
if (!section)
return -ENXIO;
return pe_read_section_data(fd, section, max_size, ret, ret_size);
}
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
@@ -240,3 +260,27 @@ bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections)
pe_header_find_section(pe_header, sections, ".osrel") &&
pe_header_find_section(pe_header, sections, ".linux");
}
bool pe_is_addon(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections) {
assert(pe_header);
assert(sections || le16toh(pe_header->pe.NumberOfSections) == 0);
if (le16toh(pe_header->optional.Subsystem) != IMAGE_SUBSYSTEM_EFI_APPLICATION)
return false;
/* Add-ons do not have a Linux kernel, but do have either .cmdline or .dtb (currently) */
return !pe_header_find_section(pe_header, sections, ".linux") &&
(pe_header_find_section(pe_header, sections, ".cmdline") ||
pe_header_find_section(pe_header, sections, ".dtb") ||
pe_header_find_section(pe_header, sections, ".ucode"));
}
bool pe_is_native(const PeHeader *pe_header) {
assert(pe_header);
#ifdef _IMAGE_FILE_MACHINE_NATIVE
return le16toh(pe_header->pe.Machine) == _IMAGE_FILE_MACHINE_NATIVE;
#else
return false;
#endif
}
+6 -1
View File
@@ -135,10 +135,15 @@ bool pe_header_is_64bit(const PeHeader *h);
const IMAGE_DATA_DIRECTORY *pe_header_get_data_directory(const PeHeader *h, size_t i);
const IMAGE_SECTION_HEADER *pe_header_find_section(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name);
const IMAGE_SECTION_HEADER *pe_section_table_find(const IMAGE_SECTION_HEADER *sections, size_t n_sections, const char *name);
int pe_load_headers(int fd, IMAGE_DOS_HEADER **ret_dos_header, PeHeader **ret_pe_header);
int pe_load_sections(int fd, const IMAGE_DOS_HEADER *dos_header, const PeHeader *pe_header, IMAGE_SECTION_HEADER **ret_sections);
int pe_read_section_data(int fd, const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name, size_t max_size, void **ret, size_t *ret_size);
int pe_read_section_data(int fd, const IMAGE_SECTION_HEADER *section, size_t max_size, void **ret, size_t *ret_size);
int pe_read_section_data_by_name(int fd, const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections, const char *name, size_t max_size, void **ret, size_t *ret_size);
bool pe_is_uki(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections);
bool pe_is_addon(const PeHeader *pe_header, const IMAGE_SECTION_HEADER *sections);
bool pe_is_native(const PeHeader *pe_header);