Browse Source

把ELF负载绑定到dragonstub内,并检测ELF header (#5)

LoGin 5 months ago
parent
commit
fe0ee6eaf3
6 changed files with 159 additions and 7 deletions
  1. 3 1
      .vscode/settings.json
  2. 16 2
      README.md
  3. 18 2
      apps/Makefile
  4. 11 0
      apps/dragon_stub-main.c
  5. 94 1
      apps/stub.c
  6. 17 1
      inc/dragonstub/dragonstub.h

+ 3 - 1
.vscode/settings.json

@@ -33,6 +33,8 @@
         "kernel.h": "c",
         "sysinfo.h": "c",
         "const.h": "c",
-        "string.h": "c"
+        "string.h": "c",
+        "elf.h": "c",
+        "stddef.h": "c"
     }
 }

+ 16 - 2
README.md

@@ -16,10 +16,24 @@ sudo apt install -y gcc-riscv64-linux-gnu
 ARCH=riscv64 make -j $(nproc)
 ```
 
+build with payload:
+
+```bash
+ARCH=riscv64 PAYLOAD_ELF=path/to/payload.elf make -j $(nproc)
+```
+
 ## Run
 
+Dry run:
+
+```bash
+ARCH=riscv64 make run
+```
+
+Run with payload:
+
 ```bash
-make run
+ARCH=riscv64 PAYLOAD_ELF=path/to/payload.elf make run
 ```
 
 ## Maintainer
@@ -33,4 +47,4 @@ DragonStub is licensed under the GPLv2 License. See [LICENSE](LICENSE) for detai
 ## References
 
 - GNU-EFI: DragonStub built with gnu-efi
-- Linux-EFIStub: In Linux kernel source tree: firmware/efi/libstub
+- Linux-EFIStub: In Linux kernel source tree: drivers/firmware/efi/libstub

+ 18 - 2
apps/Makefile

@@ -96,14 +96,30 @@ endif
 
 # 把*.c的列表转换为*.o的列表
 DRAGON_STUB_OBJS := $(patsubst %.c,%.o,$(DRAGON_STUB_FILES))
+PAYLOAD_ELF_OBJ=
+# 将'/'替换为'_'
+# 将'.'替换为'_'
+PAYLOAD_PATH_REPLACEMENT=_binary_$(shell echo "$(PAYLOAD_ELF)" | sed 's/\//_/g' | sed 's/\./_/g')
 
 
 dragon_stub: $(DRAGON_STUB_OBJS)
 	@echo "Building dragon_stub..."
 
-
-	
+ifeq ($(PAYLOAD_ELF),)
+	@echo "PAYLOAD_ELF is not set, not merging..."
 	$(LD) $(LDFLAGS) $^ -o dragon_stub.so $(LOADLIBES)
+else
+# 把DragonStub和目标ELF合并
+	@echo "Merging DragonStub and $(PAYLOAD_ELF)..."
+	$(LD) -r -b binary $(PAYLOAD_ELF) -o payload.o.stage1 --no-relax
+	$(OBJCOPY) --redefine-sym $(PAYLOAD_PATH_REPLACEMENT)_start=_binary_payload_start \
+		   --redefine-sym $(PAYLOAD_PATH_REPLACEMENT)_end=_binary_payload_end \
+		   --redefine-sym $(PAYLOAD_PATH_REPLACEMENT)_size=_binary_payload_size \
+		   payload.o.stage1 payload.o
+	$(LD) $(LDFLAGS) --no-relax $^ payload.o -o dragon_stub.so $(LOADLIBES)
+	
+endif
+	
 	$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .rodata -j .rel \
 		    -j .rela -j .rel.* -j .rela.* -j .rel* -j .rela* \
 		    -j .areloc -j .reloc $(FORMAT) dragon_stub.so dragon_stub.efi

+ 11 - 0
apps/dragon_stub-main.c

@@ -46,6 +46,12 @@ efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
 	else
 		efi_info("Command line: %s\n", cmdline_ptr);
 
+	struct payload_info payload;
+	status = find_payload(image_handle, loaded_image, &payload);
+	if (EFI_ERROR(status)) {
+		efi_err("Could not find payload, efi error code: %d\n", status);
+		return status;
+	}
 	efi_info("Booting DragonOS kernel...\n");
 
 	efi_todo("Boot DragonOS kernel");
@@ -68,4 +74,9 @@ void print_dragonstub_banner(void)
 		"|____/|_|  \\__,_|\\__, |\\___/|_| |_|____/ \\__|\\__,_|_.__/ \n");
 	efi_printk(
 		"                 |___/                                   \n");
+
+	efi_printk("\n@Copyright 2022-2023 DragonOS Community.\n");
+	efi_printk(
+		"\nDragonStub official repo: https://github.com/DragonOS-Community/DragonStub\n");
+	efi_printk("\nDragonStub is licensed under GPLv2\n\n");
 }

+ 94 - 1
apps/stub.c

@@ -1,7 +1,14 @@
 #include <efi.h>
 #include <efilib.h>
+#include <elf.h>
 #include <dragonstub/dragonstub.h>
 
+extern void _image_end(void);
+
+static u64 image_base = 0;
+static u64 image_size = 0;
+static u64 image_end = 0;
+
 EFI_STATUS efi_handle_cmdline(EFI_LOADED_IMAGE *image, char **cmdline_ptr)
 {
 	int cmdline_size = 0;
@@ -44,4 +51,90 @@ EFI_STATUS efi_handle_cmdline(EFI_LOADED_IMAGE *image, char **cmdline_ptr)
 fail_free_cmdline:
 	efi_bs_call(FreePool, cmdline_ptr);
 	return status;
-}
+}
+
+static efi_status_t init_efi_program_info(efi_loaded_image_t *loaded_image)
+{
+	image_base = (u64)loaded_image->ImageBase;
+	image_size = loaded_image->ImageSize;
+	image_end = (u64)_image_end;
+	efi_info("DragonStub loaded at 0x%p\n", image_base);
+	efi_info("DragonStub + payload size: 0x%p\n", image_size);
+	efi_info("DragonStub end addr: 0x%p\n", image_end);
+	return EFI_SUCCESS;
+}
+
+/// @brief payload_info的构造函数
+static struct payload_info payload_info_new(u64 payload_addr, u64 payload_size)
+{
+	struct payload_info info = { .payload_addr = payload_addr,
+				     .payload_size = payload_size };
+	return info;
+}
+static efi_status_t find_elf(struct payload_info *info)
+{
+	extern __weak void _binary_payload_start(void);
+	extern __weak void _binary_payload_end(void);
+	extern __weak void _binary_payload_size(void);
+	u64 payload_start = (u64)_binary_payload_start;
+	u64 payload_end = (u64)_binary_payload_end;
+
+	u64 payload_size = payload_end - payload_start;
+
+	efi_info("payload_addr: %p\n", payload_start);
+	efi_info("payload_end: %p\n", payload_end);
+	efi_info("payload_size: %p\n", payload_size);
+
+	if (payload_start == 0 || payload_end <= payload_start + 4 ||
+	    payload_size == 0) {
+		return EFI_NOT_FOUND;
+	}
+
+	efi_debug("Checking payload's ELF header...\n");
+	bool found = true;
+	for (int i = 0; i < 4; i++) {
+		u8 c = *(u8 *)(payload_start + i);
+		if (c != ELFMAG[i]) {
+			// 不是ELF magic number,跳过
+			found = false;
+			break;
+		}
+	}
+
+	// 如果找到了ELF magic number,就认为找到了ELF header,稍后再验证
+	if (found) {
+		info->payload_addr = payload_start;
+		info->payload_size = payload_size;
+		return EFI_SUCCESS;
+	}
+
+	return EFI_NOT_FOUND;
+}
+
+/// @brief 寻找要加载的内核负载
+/// @param handle efi_handle
+/// @param image efi_loaded_image_t
+/// @param ret_info 返回的负载信息
+/// @return
+efi_status_t find_payload(efi_handle_t handle, efi_loaded_image_t *loaded_image,
+			  struct payload_info *ret_info)
+{
+	efi_info("Try to find payload to boot\n");
+	efi_status_t status = init_efi_program_info(loaded_image);
+	if (status != EFI_SUCCESS) {
+		efi_err("Failed to init efi program info\n");
+		return status;
+	}
+
+	struct payload_info info = payload_info_new(0, 0);
+
+	status = find_elf(&info);
+	if (status != EFI_SUCCESS) {
+		efi_err("Payload not found: Did you forget to add the payload by setting PAYLOAD_ELF at compile time?\n"
+			"Or the payload is not an ELF file?\n");
+		return status;
+	}
+
+    *ret_info = info;
+    return EFI_SUCCESS;
+}

+ 17 - 1
inc/dragonstub/dragonstub.h

@@ -170,4 +170,20 @@ static inline bool strstarts(const char *str, const char *prefix)
 	return strncmp(str, prefix, strlen(prefix)) == 0;
 }
 
-efi_status_t efi_parse_options(char const *cmdline);
+efi_status_t efi_parse_options(char const *cmdline);
+
+/// @brief 要加载的内核负载信息
+struct payload_info {
+	/// @brief 负载起始地址
+	u64 payload_addr;
+	/// @brief 负载大小
+	u64 payload_size;
+};
+
+/// @brief 寻找要加载的内核负载
+/// @param handle efi_handle
+/// @param image efi_loaded_image_t
+/// @param ret_info 返回的负载信息
+/// @return
+efi_status_t find_payload(efi_handle_t handle, efi_loaded_image_t *loaded_image,
+			  struct payload_info *ret_info);