elf-pvh-note.c (3615B)
1 /* elf-pvh-note — append a PT_NOTE program header pointing at the 2 * existing `.note.Xen` section, and retype that section as SHT_NOTE. 3 * 4 * Why: tcc 0.9.26's linker emits only PT_LOAD program headers and 5 * marks every assembler-created section as SHT_PROGBITS, so QEMU's 6 * PVH `-kernel` path (which scans PT_NOTE phdrs for the Xen 18 note 7 * to find the 32-bit entry) refuses the kernel with "Error loading 8 * uncompressed kernel without PVH ELF Note". Patching the linker is 9 * a much bigger change than rewriting six fields after the fact; 10 * tcc reserves the post-Ehdr program-header area only large enough 11 * for its declared phnum, but the next section content lives at 12 * 0x200000 (s_align), so writing one extra Phdr at phoff+phnum*phentsize 13 * fits in the gap. 14 * 15 * Usage: elf-pvh-note <elf-path> 16 */ 17 18 extern int open(const char *, int, ...); 19 extern long lseek(int, long, int); 20 extern long read(int, void *, unsigned long); 21 extern long write(int, const void *, unsigned long); 22 extern int close(int); 23 extern void *malloc(unsigned long); 24 extern int strcmp(const char *, const char *); 25 extern int printf(const char *, ...); 26 27 #define O_RDWR 2 28 #define SEEK_SET 0 29 #define SEEK_END 2 30 31 #define PT_NOTE 4 32 #define PF_R 4 33 #define SHT_NOTE 7 34 35 typedef unsigned long u64; 36 typedef unsigned int u32; 37 typedef unsigned short u16; 38 typedef unsigned char u8; 39 40 static u16 r16(u8 *p) { return (u16)p[0] | ((u16)p[1] << 8); } 41 static u32 r32(u8 *p) { 42 return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24); 43 } 44 static u64 r64(u8 *p) { 45 return (u64)r32(p) | ((u64)r32(p + 4) << 32); 46 } 47 static void w16(u8 *p, u16 v) { p[0] = v; p[1] = v >> 8; } 48 static void w32(u8 *p, u32 v) { 49 p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; 50 } 51 static void w64(u8 *p, u64 v) { w32(p, (u32)v); w32(p + 4, (u32)(v >> 32)); } 52 53 int main(int argc, char **argv) { 54 if (argc != 2) { printf("usage: elf-pvh-note <elf>\n"); return 2; } 55 int fd = open(argv[1], O_RDWR); 56 if (fd < 0) { printf("elf-pvh-note: open failed\n"); return 1; } 57 long size = lseek(fd, 0, SEEK_END); 58 lseek(fd, 0, SEEK_SET); 59 u8 *buf = (u8 *)malloc(size); 60 if (read(fd, buf, size) != size) { printf("elf-pvh-note: short read\n"); return 1; } 61 62 u64 phoff = r64(buf + 0x20); 63 u16 phentsize = r16(buf + 0x36); 64 u16 phnum = r16(buf + 0x38); 65 u64 shoff = r64(buf + 0x28); 66 u16 shentsize = r16(buf + 0x3a); 67 u16 shnum = r16(buf + 0x3c); 68 u16 shstrndx = r16(buf + 0x3e); 69 70 u64 shstrtab_off = r64(buf + shoff + shstrndx * shentsize + 0x18); 71 72 u64 note_off = 0, note_size = 0, note_addr = 0; 73 int found = 0; 74 for (int i = 0; i < shnum; i++) { 75 u8 *sh = buf + shoff + i * shentsize; 76 u32 name_off = r32(sh); 77 if (strcmp((char *)(buf + shstrtab_off + name_off), ".note.Xen") == 0) { 78 note_addr = r64(sh + 0x10); 79 note_off = r64(sh + 0x18); 80 note_size = r64(sh + 0x20); 81 w32(sh + 4, SHT_NOTE); 82 found = 1; 83 break; 84 } 85 } 86 if (!found) { printf("elf-pvh-note: no .note.Xen section\n"); return 1; } 87 88 u8 *ph = buf + phoff + phnum * phentsize; 89 w32(ph + 0, PT_NOTE); 90 w32(ph + 4, PF_R); 91 w64(ph + 8, note_off); 92 w64(ph + 16, note_addr); 93 w64(ph + 24, note_addr); 94 w64(ph + 32, note_size); 95 w64(ph + 40, note_size); 96 w64(ph + 48, 4); 97 w16(buf + 0x38, phnum + 1); 98 99 lseek(fd, 0, SEEK_SET); 100 if (write(fd, buf, size) != size) { printf("elf-pvh-note: short write\n"); return 1; } 101 close(fd); 102 return 0; 103 }