1
0
mirror of https://github.com/systemd/systemd.git synced 2025-08-02 04:22:27 +03:00

Merge pull request #20438 from medhefgo/boot

sd-boot: Better self-detection and windows loader title
This commit is contained in:
Lennart Poettering
2021-08-16 15:35:08 +02:00
committed by GitHub
15 changed files with 277 additions and 137 deletions

View File

@ -20,7 +20,6 @@
#define _sentinel_ __attribute__((__sentinel__))
#define _destructor_ __attribute__((__destructor__))
#define _deprecated_ __attribute__((__deprecated__))
#define _packed_ __attribute__((__packed__))
#define _malloc_ __attribute__((__malloc__))
#define _weak_ __attribute__((__weak__))
#define _public_ __attribute__((__visibility__("default")))

View File

@ -189,22 +189,6 @@ static inline void strncpy_exact(char *buf, const char *src, size_t buf_len) {
}
REENABLE_WARNING;
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, size_t sz, const char *token) {
assert(token);
size_t n = strlen(token);
if (sz < n)
return NULL;
assert(p);
if (memcmp(p, token, n) != 0)
return NULL;
return (uint8_t*) p + n;
}
/* Like startswith_no_case(), but operates on arbitrary memory blocks.
* It works only for ASCII strings.
*/

View File

@ -22,7 +22,7 @@
#endif
/* magic string to find in the binary image */
static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
static const char _used_ _section_(".sdmagic") magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####";
enum loader_type {
LOADER_UNDEFINED,
@ -1792,6 +1792,29 @@ static ConfigEntry *config_entry_add_loader(
return entry;
}
static BOOLEAN is_sd_boot(EFI_FILE *root_dir, const CHAR16 *loader_path) {
EFI_STATUS err;
const CHAR8 *sections[] = {
(CHAR8 *)".sdmagic",
NULL
};
UINTN offset = 0, size = 0, read;
_cleanup_freepool_ CHAR8 *content = NULL;
assert(root_dir);
assert(loader_path);
err = pe_file_locate_sections(root_dir, loader_path, sections, &offset, &size);
if (EFI_ERROR(err) || size != sizeof(magic))
return FALSE;
err = file_read(root_dir, loader_path, offset, size, &content, &read);
if (EFI_ERROR(err) || size != read)
return FALSE;
return CompareMem(content, magic, sizeof(magic)) == 0;
}
static BOOLEAN config_entry_add_loader_auto(
Config *config,
EFI_HANDLE *device,
@ -1811,25 +1834,23 @@ static BOOLEAN config_entry_add_loader_auto(
assert(root_dir);
assert(id);
assert(title);
assert(loader);
assert(loader || loaded_image_path);
if (!config->auto_entries)
return FALSE;
/* do not add an entry for ourselves */
if (loaded_image_path) {
UINTN len;
_cleanup_freepool_ CHAR8 *content = NULL;
loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
if (StriCmp(loader, loaded_image_path) == 0)
/* We are trying to add the default EFI loader here,
* but we do not want to do that if that would be us.
*
* If the default loader is not us, it might be shim. It would
* chainload GRUBX64.EFI in that case, which might be us.*/
if (StriCmp(loader, loaded_image_path) == 0 ||
is_sd_boot(root_dir, loader) ||
is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI"))
return FALSE;
/* look for systemd-boot magic string */
err = file_read(root_dir, loader, 0, 100*1024, &content, &len);
if (!EFI_ERROR(err))
for (CHAR8 *start = content; start <= content + len - sizeof(magic) - 1; start++)
if (start[0] == magic[0] && CompareMem(start, magic, sizeof(magic) - 1) == 0)
return FALSE;
}
/* check existence */
@ -1876,6 +1897,55 @@ static VOID config_entry_add_osx(Config *config) {
}
}
static VOID config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FILE *root_dir) {
_cleanup_freepool_ CHAR8 *bcd = NULL;
const CHAR16 *title = NULL;
EFI_STATUS err;
UINTN len;
assert(config);
assert(device);
assert(root_dir);
if (!config->auto_entries)
return;
/* Try to find a better title. */
err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
if (!EFI_ERROR(err)) {
static const CHAR16 *versions[] = {
L"Windows 11",
L"Windows 10",
L"Windows 8.1",
L"Windows 8",
L"Windows 7",
L"Windows Vista",
};
CHAR8 *p = bcd;
while (!title) {
CHAR8 *q = mempmem_safe(p, len, versions[0], STRLEN(L"Windows "));
if (!q)
break;
len -= q - p;
p = q;
/* We found the prefix, now try all the version strings. */
for (UINTN i = 0; i < ELEMENTSOF(versions); i++) {
if (memory_startswith(p, len, versions[i] + STRLEN("Windows "))) {
title = versions[i];
break;
}
}
}
}
config_entry_add_loader_auto(config, device, root_dir, NULL,
L"auto-windows", 'w', title ?: L"Windows Boot Manager",
L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
}
static VOID config_entry_add_linux(
Config *config,
EFI_HANDLE *device,
@ -1897,14 +1967,13 @@ static VOID config_entry_add_linux(
CHAR16 buf[256];
UINTN bufsize = sizeof buf;
EFI_FILE_INFO *f;
CHAR8 *sections[] = {
const CHAR8 *sections[] = {
(CHAR8 *)".osrel",
(CHAR8 *)".cmdline",
NULL
};
UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
UINTN addrs[ELEMENTSOF(sections)-1] = {};
CHAR8 *content = NULL;
CHAR8 *line;
UINTN pos = 0;
@ -1931,7 +2000,7 @@ static VOID config_entry_add_linux(
continue;
/* look for .osrel and .cmdline sections in the .efi binary */
err = pe_file_locate_sections(linux_dir, f->FileName, sections, addrs, offs, szs);
err = pe_file_locate_sections(linux_dir, f->FileName, sections, offs, szs);
if (EFI_ERROR(err))
continue;
@ -2410,13 +2479,12 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
config_sort_entries(&config);
/* if we find some well-known loaders, add them to the end of the list */
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-windows", 'w', L"Windows Boot Manager", L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
config_entry_add_osx(&config);
config_entry_add_windows(&config, loaded_image->DeviceHandle, root_dir);
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, NULL,
L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_loader_auto(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
L"auto-efi-default", '\0', L"EFI Default Loader", L"\\EFI\\Boot\\boot" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_osx(&config);
L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
if (config.auto_firmware && efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndicationsSupported", &osind) == EFI_SUCCESS) {
if (osind & EFI_OS_INDICATIONS_BOOT_TO_FW_UI)

View File

@ -2,6 +2,7 @@
#pragma once
#include <efi.h>
#include "macro-fundamental.h"
#define SETUP_MAGIC 0x53726448 /* "HdrS" */
@ -44,7 +45,7 @@ struct setup_header {
UINT64 pref_address;
UINT32 init_size;
UINT32 handover_offset;
} __attribute__((packed));
} _packed_;
/* adapted from linux' bootparam.h */
struct boot_params {
@ -81,7 +82,7 @@ struct boot_params {
UINT8 _pad8[48];
UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR]
UINT8 _pad9[276];
} __attribute__((packed));
} _packed_;
EFI_STATUS linux_exec(EFI_HANDLE *image,
CHAR8 *cmdline, UINTN cmdline_size,

View File

@ -133,13 +133,13 @@ typedef struct {
UINT16 HeaderVersion;
UINT32 PCRIndex;
UINT32 EventType;
} __attribute__((packed)) EFI_TCG2_EVENT_HEADER;
} _packed_ EFI_TCG2_EVENT_HEADER;
typedef struct tdEFI_TCG2_EVENT {
UINT32 Size;
EFI_TCG2_EVENT_HEADER Header;
UINT8 Event[1];
} __attribute__((packed)) EFI_TCG2_EVENT;
} _packed_ EFI_TCG2_EVENT;
typedef EFI_STATUS(EFIAPI * EFI_TCG2_GET_CAPABILITY) (IN EFI_TCG2_PROTOCOL * This,
IN OUT EFI_TCG2_BOOT_SERVICE_CAPABILITY * ProtocolCapability);

View File

@ -292,6 +292,7 @@ if have_gnu_efi
'-j', '.text',
'-j', '.sdata',
'-j', '.sbat',
'-j', '.sdmagic',
'-j', '.data',
'-j', '.dynamic',
'-j', '.dynsym',

View File

@ -120,3 +120,7 @@ typedef struct _EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL {
} EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL;
#endif
#ifndef EFI_IMAGE_MACHINE_RISCV64
#define EFI_IMAGE_MACHINE_RISCV64 0x5064
#endif

View File

@ -6,6 +6,24 @@
#include "pe.h"
#include "util.h"
#define DOS_FILE_MAGIC "MZ"
#define PE_FILE_MAGIC "PE\0\0"
#define MAX_SECTIONS 96
#if defined(__i386__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_IA32
#elif defined(__x86_64__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_X64
#elif defined(__aarch64__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_AARCH64
#elif defined(__arm__)
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_ARMTHUMB_MIXED
#elif defined(__riscv) && __riscv_xlen == 64
#define TARGET_MACHINE_TYPE EFI_IMAGE_MACHINE_RISCV64
#else
#error Unknown EFI arch
#endif
struct DosFileHeader {
UINT8 Magic[2];
UINT16 LastSize;
@ -26,14 +44,9 @@ struct DosFileHeader {
UINT16 OEMInfo;
UINT16 reserved2[10];
UINT32 ExeHeader;
} __attribute__((packed));
} _packed_;
#define PE_HEADER_MACHINE_I386 0x014c
#define PE_HEADER_MACHINE_X64 0x8664
#define PE_HEADER_MACHINE_ARM64 0xaa64
#define PE_HEADER_MACHINE_ARM 0x01c2
#define PE_HEADER_MACHINE_RISCV64 0x5064
struct PeFileHeader {
struct CoffFileHeader {
UINT16 Machine;
UINT16 NumberOfSections;
UINT32 TimeDateStamp;
@ -41,12 +54,13 @@ struct PeFileHeader {
UINT32 NumberOfSymbols;
UINT16 SizeOfOptionalHeader;
UINT16 Characteristics;
} __attribute__((packed));
} _packed_;
struct PeHeader {
struct PeFileHeader {
UINT8 Magic[4];
struct PeFileHeader FileHeader;
} __attribute__((packed));
struct CoffFileHeader FileHeader;
/* OptionalHeader omitted */
} _packed_;
struct PeSectionHeader {
UINT8 Name[8];
@ -59,120 +73,143 @@ struct PeSectionHeader {
UINT16 NumberOfRelocations;
UINT16 NumberOfLinenumbers;
UINT32 Characteristics;
} __attribute__((packed));
} _packed_;
EFI_STATUS pe_memory_locate_sections(CHAR8 *base, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
struct DosFileHeader *dos;
struct PeHeader *pe;
UINTN offset;
static inline BOOLEAN verify_dos(const struct DosFileHeader *dos) {
assert(dos);
return CompareMem(dos->Magic, DOS_FILE_MAGIC, STRLEN(DOS_FILE_MAGIC)) == 0;
}
assert(base);
static inline BOOLEAN verify_pe(const struct PeFileHeader *pe) {
assert(pe);
return CompareMem(pe->Magic, PE_FILE_MAGIC, STRLEN(PE_FILE_MAGIC)) == 0 &&
pe->FileHeader.Machine == TARGET_MACHINE_TYPE &&
pe->FileHeader.NumberOfSections > 0 &&
pe->FileHeader.NumberOfSections <= MAX_SECTIONS;
}
static inline UINTN section_table_offset(const struct DosFileHeader *dos, const struct PeFileHeader *pe) {
assert(dos);
assert(pe);
return dos->ExeHeader + sizeof(struct PeFileHeader) + pe->FileHeader.SizeOfOptionalHeader;
}
static VOID locate_sections(
const struct PeSectionHeader section_table[],
UINTN n_table,
const CHAR8 **sections,
UINTN *addrs,
UINTN *offsets,
UINTN *sizes) {
assert(section_table);
assert(sections);
assert(sizes);
dos = (struct DosFileHeader *)base;
for (UINTN i = 0; i < n_table; i++) {
const struct PeSectionHeader *sect = section_table + i;
if (CompareMem(dos->Magic, "MZ", 2) != 0)
return EFI_LOAD_ERROR;
pe = (struct PeHeader *)&base[dos->ExeHeader];
if (CompareMem(pe->Magic, "PE\0\0", 4) != 0)
return EFI_LOAD_ERROR;
/* PE32+ Subsystem type */
if (pe->FileHeader.Machine != PE_HEADER_MACHINE_X64 &&
pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM64 &&
pe->FileHeader.Machine != PE_HEADER_MACHINE_I386 &&
pe->FileHeader.Machine != PE_HEADER_MACHINE_ARM &&
pe->FileHeader.Machine != PE_HEADER_MACHINE_RISCV64)
return EFI_LOAD_ERROR;
if (pe->FileHeader.NumberOfSections > 96)
return EFI_LOAD_ERROR;
offset = dos->ExeHeader + sizeof(*pe) + pe->FileHeader.SizeOfOptionalHeader;
for (UINTN i = 0; i < pe->FileHeader.NumberOfSections; i++) {
struct PeSectionHeader *sect;
sect = (struct PeSectionHeader *)&base[offset];
for (UINTN j = 0; sections[j]; j++) {
if (CompareMem(sect->Name, sections[j], strlena(sections[j])) != 0)
continue;
if (addrs)
addrs[j] = (UINTN)sect->VirtualAddress;
addrs[j] = sect->VirtualAddress;
if (offsets)
offsets[j] = (UINTN)sect->PointerToRawData;
if (sizes)
sizes[j] = (UINTN)sect->VirtualSize;
offsets[j] = sect->PointerToRawData;
sizes[j] = sect->VirtualSize;
}
offset += sizeof(*sect);
}
}
EFI_STATUS pe_memory_locate_sections(
const CHAR8 *base,
const CHAR8 **sections,
UINTN *addrs,
UINTN *sizes) {
const struct DosFileHeader *dos;
const struct PeFileHeader *pe;
UINTN offset;
assert(base);
assert(sections);
assert(addrs);
assert(sizes);
dos = (const struct DosFileHeader*)base;
if (!verify_dos(dos))
return EFI_LOAD_ERROR;
pe = (const struct PeFileHeader*)&base[dos->ExeHeader];
if (!verify_pe(pe))
return EFI_LOAD_ERROR;
offset = section_table_offset(dos, pe);
locate_sections((struct PeSectionHeader*)&base[offset], pe->FileHeader.NumberOfSections,
sections, addrs, NULL, sizes);
return EFI_SUCCESS;
}
EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path, CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes) {
EFI_FILE_HANDLE handle;
EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir,
const CHAR16 *path,
const CHAR8 **sections,
UINTN *offsets,
UINTN *sizes) {
_cleanup_freepool_ struct PeSectionHeader *section_table = NULL;
_cleanup_(FileHandleClosep) EFI_FILE_HANDLE handle = NULL;
struct DosFileHeader dos;
struct PeHeader pe;
UINTN len;
UINTN headerlen;
struct PeFileHeader pe;
UINTN len, section_table_len;
EFI_STATUS err;
_cleanup_freepool_ CHAR8 *header = NULL;
assert(dir);
assert(path);
assert(sections);
assert(offsets);
assert(sizes);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, path, EFI_FILE_MODE_READ, 0ULL);
err = uefi_call_wrapper(dir->Open, 5, dir, &handle, (CHAR16*)path, EFI_FILE_MODE_READ, 0ULL);
if (EFI_ERROR(err))
return err;
/* MS-DOS stub */
len = sizeof(dos);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &dos);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(dos)) {
err = EFI_LOAD_ERROR;
goto out;
}
return err;
if (len != sizeof(dos) || !verify_dos(&dos))
return EFI_LOAD_ERROR;
err = uefi_call_wrapper(handle->SetPosition, 2, handle, dos.ExeHeader);
if (EFI_ERROR(err))
goto out;
return err;
len = sizeof(pe);
err = uefi_call_wrapper(handle->Read, 3, handle, &len, &pe);
if (EFI_ERROR(err))
goto out;
if (len != sizeof(pe)) {
err = EFI_LOAD_ERROR;
goto out;
}
return err;
if (len != sizeof(pe) || !verify_pe(&pe))
return EFI_LOAD_ERROR;
headerlen = sizeof(dos) + sizeof(pe) + pe.FileHeader.SizeOfOptionalHeader + pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
header = AllocatePool(headerlen);
if (!header) {
err = EFI_OUT_OF_RESOURCES;
goto out;
}
len = headerlen;
err = uefi_call_wrapper(handle->SetPosition, 2, handle, 0);
section_table_len = pe.FileHeader.NumberOfSections * sizeof(struct PeSectionHeader);
section_table = AllocatePool(section_table_len);
if (!section_table)
return EFI_OUT_OF_RESOURCES;
err = uefi_call_wrapper(handle->SetPosition, 2, handle, section_table_offset(&dos, &pe));
if (EFI_ERROR(err))
goto out;
return err;
err = uefi_call_wrapper(handle->Read, 3, handle, &len, header);
len = section_table_len;
err = uefi_call_wrapper(handle->Read, 3, handle, &len, section_table);
if (EFI_ERROR(err))
goto out;
return err;
if (len != section_table_len)
return EFI_LOAD_ERROR;
if (len != headerlen) {
err = EFI_LOAD_ERROR;
goto out;
}
locate_sections(section_table, pe.FileHeader.NumberOfSections,
sections, NULL, offsets, sizes);
err = pe_memory_locate_sections(header, sections, addrs, offsets, sizes);
out:
uefi_call_wrapper(handle->Close, 1, handle);
return err;
return EFI_SUCCESS;
}

View File

@ -1,9 +1,17 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <efi.h>
#include <efidef.h>
EFI_STATUS pe_memory_locate_sections(CHAR8 *base,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
EFI_STATUS pe_file_locate_sections(EFI_FILE *dir, CHAR16 *path,
CHAR8 **sections, UINTN *addrs, UINTN *offsets, UINTN *sizes);
EFI_STATUS pe_memory_locate_sections(
const CHAR8 *base,
const CHAR8 **sections,
UINTN *addrs,
UINTN *sizes);
EFI_STATUS pe_file_locate_sections(
EFI_FILE *dir,
const CHAR16 *path,
const CHAR8 **sections,
UINTN *offsets,
UINTN *sizes);

View File

@ -12,7 +12,7 @@ struct bmp_file {
UINT32 size;
UINT16 reserved[2];
UINT32 offset;
} __attribute__((packed));
} _packed_;
/* we require at least BITMAPINFOHEADER, later versions are
accepted, but their features ignored */
@ -28,14 +28,14 @@ struct bmp_dib {
INT32 y_pixel_meter;
UINT32 colors_used;
UINT32 colors_important;
} __attribute__((packed));
} _packed_;
struct bmp_map {
UINT8 blue;
UINT8 green;
UINT8 red;
UINT8 reserved;
} __attribute__((packed));
} _packed_;
static EFI_STATUS bmp_parse_header(UINT8 *bmp, UINTN size, struct bmp_dib **ret_dib,
struct bmp_map **ret_map, UINT8 **pixmap) {

View File

@ -17,7 +17,7 @@ static const char __attribute__((used)) magic[] = "#### LoaderInfo: systemd-stub
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
EFI_LOADED_IMAGE *loaded_image;
CHAR8 *sections[] = {
const CHAR8 *sections[] = {
(CHAR8 *)".cmdline",
(CHAR8 *)".linux",
(CHAR8 *)".initrd",
@ -25,7 +25,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
NULL
};
UINTN addrs[ELEMENTSOF(sections)-1] = {};
UINTN offs[ELEMENTSOF(sections)-1] = {};
UINTN szs[ELEMENTSOF(sections)-1] = {};
CHAR8 *cmdline = NULL;
UINTN cmdline_len;
@ -39,7 +38,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, offs, szs);
err = pe_memory_locate_sections(loaded_image->ImageBase, sections, addrs, szs);
if (EFI_ERROR(err))
return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);

View File

@ -501,3 +501,17 @@ EFI_STATUS log_oom(void) {
log_error_stall(L"Out of memory.");
return EFI_OUT_OF_RESOURCES;
}
VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
assert(haystack || haystack_len == 0);
assert(needle || needle_len == 0);
if (needle_len == 0)
return (VOID*)haystack;
for (const CHAR8 *h = haystack, *n = needle; haystack_len >= needle_len; h++, haystack_len--)
if (*h == *n && CompareMem(h + 1, n + 1, needle_len - 1) == 0)
return (VOID*)h;
return NULL;
}

View File

@ -84,3 +84,10 @@ EFI_STATUS log_oom(void);
log_error_stall(fmt, ##__VA_ARGS__); \
err; \
})
VOID *memmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len);
static inline VOID *mempmem_safe(const VOID *haystack, UINTN haystack_len, const VOID *needle, UINTN needle_len) {
CHAR8 *p = memmem_safe(haystack, haystack_len, needle, needle_len);
return p ? p + needle_len : NULL;
}

View File

@ -11,6 +11,7 @@
#define _const_ __attribute__((__const__))
#define _pure_ __attribute__((__pure__))
#define _section_(x) __attribute__((__section__(x)))
#define _packed_ __attribute__((__packed__))
#define _used_ __attribute__((__used__))
#define _unused_ __attribute__((__unused__))
#define _cleanup_(x) __attribute__((__cleanup__(x)))

View File

@ -16,6 +16,7 @@
#define strncmp(a, b, n) StrnCmp((a), (b), (n))
#define strcasecmp(a, b) StriCmp((a), (b))
#define STR_C(str) (L ## str)
#define memcmp(a, b, n) CompareMem(a, b, n)
#else
#define STR_C(str) (str)
#endif
@ -65,3 +66,19 @@ static inline const sd_char *yes_no(sd_bool b) {
}
sd_int strverscmp_improved(const sd_char *a, const sd_char *b);
/* Like startswith(), but operates on arbitrary memory blocks */
static inline void *memory_startswith(const void *p, sd_size_t sz, const sd_char *token) {
assert(token);
sd_size_t n = strlen(token) * sizeof(sd_char);
if (sz < n)
return NULL;
assert(p);
if (memcmp(p, token, n) != 0)
return NULL;
return (uint8_t*) p + n;
}