Commit version 1.10 of mkelfImage. The big change is that it now generates

ELF notes especially an ELF checksum of the entire image.  This finishes
my proof of concept implementation of all significant features with respect
to ELF booting.  Now to do some native ports...
This commit is contained in:
Eric W. Biederman 2002-01-22 18:03:40 +00:00
parent bf85f95212
commit 595220a3ab
6 changed files with 425 additions and 257 deletions

View file

@ -6,8 +6,8 @@
# the result will be a directory tree that you can run mkelfImage in
PREFIX=/usr/local/
PERLPATH=/usr/bin/perl
VERSION="1.9"
DATE="7 January 2002"
VERSION="1.10"
DATE="21 January 2002"
SHAREDIR=$(PREFIX)/share/mkelfImage
BINDIR=$(PREFIX)/bin

View file

@ -202,128 +202,11 @@ struct param_info {
typedef unsigned size_t;
#define _NOTE __attribute__((__section__(".rodata")))
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[8];
unsigned char desc[6];
unsigned char dummy[2];
} program _NOTE = {
sizeof(program.name),
sizeof(program.desc),
EIN_PROGRAM_NAME,
"ElfBoot",
"Linux"
};
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[8];
unsigned char desc[64];
} program_version _NOTE = {
sizeof(program.name),
sizeof(program.desc),
EIN_PROGRAM_NAME,
"ElfBoot",
"2.2.17 (eric@DLT) #21 Wed Jan 3 14:44:09 MST 2001"
};
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[6];
unsigned char dummy[2];
char command_line[256];
} note_command_line _NOTE = {
sizeof(program.name),
sizeof(program.desc),
LIN_COMMAND_LINE,
"Linux", { 0, 0 },
DEFAULT_COMMAND_LINE,
};
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[6];
unsigned char dummy[2];
unsigned short root_dev;
unsigned char dummy2[2];
} note_root_dev _NOTE = {
sizeof(program.name),
sizeof(program.desc),
LIN_ROOT_DEV,
"Linux", { 0, 0 },
DEFAULT_ROOT_DEV, { 0, 0 },
};
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[6];
unsigned char dummy[2];
unsigned short ramdisk_flags;
unsigned char dummy2[2];
} note_ramdisk_flags _NOTE = {
sizeof(program.name),
sizeof(program.desc),
LIN_RAMDISK_FLAGS,
"Linux", { 0, 0 },
0
#if defined(DEFAULT_RAMDISK_IMAGE_START)
| (DEFAULT_RAMDISK_IMAGE_START & RAMDISK_IMAGE_START_MASK)
#endif
#if defined(DEFAULT_RAMDISK_PROMPT_FLAG)
| RAMDISK_PROMPT_FLAG
#endif
#if defined(DEFAULT_RAMDISK_LOAD_FLAG)
| RAMDISK_LOAD_FLAG
#endif
, { 0 , 0 },
};
extern char ramdisk_data[], ramdisk_data_size[];
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[6];
unsigned char dummy[2];
unsigned initrd_start;
} note_initrd_start _NOTE = {
sizeof(program.name),
sizeof(program.desc),
LIN_INITRD_START,
"Linux", { 0, 0 },
(unsigned)&ramdisk_data,
};
static const struct {
Elf_Word namesz;
Elf_Word descsz;
Elf_Word type;
unsigned char name[6];
unsigned char dummy[2];
unsigned initrd_size;
} note_initrd_size _NOTE = {
sizeof(program.name),
sizeof(program.desc),
LIN_INITRD_SIZE,
"Linux", { 0, 0 },
((unsigned)&ramdisk_data_size),
};
extern char note_command_line[];
extern unsigned short note_root_dev;
extern unsigned short note_ramdisk_flags;
extern unsigned long note_initrd_start;
extern unsigned long note_initrd_size;
/* FIXME handle systems with large EBDA's */
static struct parameters *faked_real_mode = (void *)0x90000;
@ -805,52 +688,36 @@ static void hardware_setup(struct param_info *info)
* =============================================================================
*/
static inline unsigned long elf_note_size(Elf_Nhdr *hdr)
{
unsigned long size;
size = sizeof(*hdr);
size += hdr->n_namesz;
if (size & 3) {
size += 4 - (size & 3);
}
size = hdr->n_descsz;
if (size & 3) {
size += 4 - (size & 3);
}
return size;
}
static inline Elf_Nhdr *next_elf_note(Elf_Nhdr *hdr)
{
return (void *)(((char *)hdr) + elf_note_size(hdr));
}
static inline unsigned char *elf_note_name(Elf_Nhdr *hdr)
{
return (void *)(((char *)hdr) + sizeof(*hdr));
}
static inline unsigned char *elf_note_desc(Elf_Nhdr *hdr)
{
int offset;
offset = sizeof(*hdr);
offset = hdr->n_namesz;
if (offset & 3) {
offset += 4 - (offset & 3);
}
return (void *)(((char *)hdr) + offset);
}
static int count_elf_notes(Elf_Bhdr *bhdr)
{
Elf_Nhdr *hdr;
void *start = (void *)bhdr;
void *end = ((char *)start) + bhdr->b_size;
unsigned char *note, *end;
int count;
count = 0;
for(hdr = start; ((void *)hdr < end) &&
(elf_note_size(hdr) <= (end - (void *)hdr));
hdr = next_elf_note(hdr)) {
note = ((char *)bhdr) + sizeof(*bhdr);
end = ((char *)bhdr) + bhdr->b_size;
#if 0
puts("count_elf_notes"); put_hex((unsigned long)bhdr); puts("\n");
#endif
while (note < end) {
Elf_Nhdr *hdr;
unsigned char *n_name, *n_desc, *next;
hdr = (Elf_Nhdr *)note;
n_name = note + sizeof(*hdr);
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
next = n_desc + ((hdr->n_descsz + 3) & ~3);
#if 0
puts("elf_note = "); put_hex((unsigned long)note); puts("\n");
puts("elf_namesz = "); put_hex(hdr->n_namesz); puts("\n");
puts("elf_descsz = "); put_hex(hdr->n_descsz); puts("\n");
puts("elf_type = "); put_hex(hdr->n_type); puts("\n");
puts("elf_name = "); put_hex((unsigned long)n_name); puts("\n");
puts("elf_desc = "); put_hex((unsigned long)n_desc); puts("\n");
#endif
if (next > end)
break;
count++;
note = next;
}
return count;
}
@ -858,22 +725,24 @@ static int count_elf_notes(Elf_Bhdr *bhdr)
static Elf_Nhdr *find_elf_note(Elf_Bhdr *bhdr,
Elf_Word namesz, unsigned char *name, Elf_Word type)
{
Elf_Nhdr *hdr;
void *start = (void *)bhdr;
void *end = ((char *)start) + bhdr->b_size;
for(hdr = start; ((void *)hdr < end) &&
(elf_note_size(hdr) <= (end - (void *)hdr));
hdr = next_elf_note(hdr)) {
unsigned char *n_name;
unsigned char *n_desc;
n_name = elf_note_name(hdr);
n_desc = elf_note_desc(hdr);
unsigned char *note, *end;
note = ((char *)bhdr) + sizeof(*bhdr);
end = ((char *)bhdr) + bhdr->b_size;
while(note < end) {
Elf_Nhdr *hdr;
unsigned char *n_name, *n_desc, *next;
hdr = (Elf_Nhdr *)note;
n_name = note + sizeof(*hdr);
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
next = n_desc + ((hdr->n_descsz + 3) & ~3);
if (next > end)
break;
if ((hdr->n_type == type) &&
(hdr->n_namesz == namesz) &&
(memcmp(n_name, name, namesz) == 0)) {
return hdr;
}
note = next;
}
return 0;
}
@ -896,17 +765,19 @@ struct {
static void convert_elf_boot(struct param_info *info, Elf_Bhdr *bhdr)
{
Elf_Nhdr *hdr;
int i;
void *start = (void *)bhdr;
void *end = ((char *)start) + bhdr->b_size;
for(hdr = start; ((void *)hdr < end) &&
(elf_note_size(hdr) <= (end - (void *)hdr));
hdr = next_elf_note(hdr)) {
unsigned char *n_name;
unsigned char *n_desc;
n_name = elf_note_name(hdr);
n_desc = elf_note_desc(hdr);
unsigned char *note, *end;
note = ((char *)bhdr) + sizeof(*bhdr);
end = ((char *)bhdr) + bhdr->b_size;
while(note < end) {
Elf_Nhdr *hdr;
unsigned char *n_name, *n_desc, *next;
int i;
hdr = (Elf_Nhdr *)note;
n_name = note + sizeof(*hdr);
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
next = n_desc + ((hdr->n_descsz + 3) & ~3);
if (next > end)
break;
for(i = 0; i < sizeof(elf_notes)/sizeof(elf_notes[0]); i++) {
if ((hdr->n_type == elf_notes[i].type) &&
(hdr->n_namesz == elf_notes[i].namesz) &&
@ -915,6 +786,7 @@ static void convert_elf_boot(struct param_info *info, Elf_Bhdr *bhdr)
break;
}
}
note = next;
}
}
@ -949,7 +821,7 @@ static int count_lb_records(void *start, unsigned long length)
return count;
}
static int find_lb_table(struct param_info *info, void *start, void *end)
static struct lb_header *__find_lb_table(void *start, void *end)
{
unsigned char *ptr;
/* For now be stupid.... */
@ -966,14 +838,32 @@ static int find_lb_table(struct param_info *info, void *start, void *end)
(count_lb_records(ptr + sizeof(*head), head->table_bytes) ==
head->table_entries)
) {
info->has_linuxbios = 1;
info->lb_table = (void *)ptr;
return 1;
return head;
}
};
return 0;
}
static int find_lb_table(struct param_info *info)
{
struct lb_header *head;
head = 0;
if (!head) {
/* First try at address 0 */
head = __find_lb_table((void *)0x00000, (void *)0x1000);
}
if (!head) {
/* Then try at address 0xf0000 */
head = __find_lb_table((void *)0xf0000, (void *)0x100000);
}
if (head) {
info->has_linuxbios = 1;
info->lb_table = head;
return 1;
}
return 0;
}
static void convert_lb_memory(struct param_info *info, struct lb_memory *mem)
{
int i;
@ -1148,6 +1038,12 @@ static void query_bootloader_param_class(struct param_info *info)
}
else {
puts("Bad ELF parameter table!\n");
puts(" checksum = "); put_hex(checksum); puts("\n");
puts(" count = "); put_hex(count); puts("\n");
puts(" hdr = "); put_hex((unsigned long)hdr); puts("\n");
puts(" b_size = "); put_hex(hdr->b_size); puts("\n");
puts("b_signature = "); put_hex(hdr->b_signature); puts("\n");
puts(" b_records = "); put_hex(hdr->b_records); puts("\n");
}
}
if (!has_bootloader_type) {
@ -1178,33 +1074,52 @@ static void query_bootloader_values(struct param_info *info)
static int bootloader_query_firmware_class(struct param_info *info)
{
Elf_Nhdr *hdr;
unsigned char *note, *n_name, *n_desc;
int detected_firmware_type;
if (!info->has_elf_boot) {
/* Only the elf boot tables gives us a firmware type */
return 0;
}
detected_firmware_type = 0;
hdr = find_elf_note(info->data, 0, 0, EBN_FIRMWARE_TYPE);
if (!hdr) {
info->has_pcbios = 1;
detected_firmware_type = 1;
} else {
note = (char *)hdr;
n_name = note + sizeof(*hdr);
n_desc = n_name + ((hdr->n_namesz + 3) & ~3);
}
else if ((hdr->n_descsz == 7) &&
(memcmp(elf_note_desc(hdr), "PCBIOS", 7) == 0)) {
if (!detected_firmware_type && hdr &&
(hdr->n_descsz == 7) &&
(memcmp(n_desc, "PCBIOS", 7) == 0)) {
info->has_pcbios = 1;
detected_firmware_type = 1;
}
else if ((hdr->n_descsz == 10) &&
(memcmp(elf_note_desc(hdr), "LinuxBIOS", 10) == 0)) {
info->has_linuxbios = 1;
if (!detected_firmware_type && hdr &&
(hdr->n_descsz == 10) &&
(memcmp(n_desc, "LinuxBIOS", 10) == 0)) {
/* Don't believe I'm linuxBIOS unless I can
* find the linuxBIOS table..
*/
detected_firmware_type = find_lb_table(info);
}
else if (hdr->n_descsz == 0) {
if (!detected_firmware_type && hdr &&
(hdr->n_descsz == 0)) {
/* No firmware is present */
detected_firmware_type = 1;
}
else if ((hdr->n_descsz == 1) &&
(memcmp(elf_note_desc(hdr), "", 1) == 0)) {
if (!detected_firmware_type && hdr &&
(hdr->n_descsz == 1) &&
(memcmp(n_desc, "", 1) == 0)) {
/* No firmware is present */
detected_firmware_type = 1;
}
else {
puts("Unknow firmware type!");
if (!detected_firmware_type && hdr) {
puts("Unknown firmware type:"); puts(n_desc); puts("\n");
}
return 1;
return detected_firmware_type;
}
static void query_firmware_class(struct param_info *info)
@ -1222,15 +1137,7 @@ static void query_firmware_class(struct param_info *info)
/* See if we can detect linuxbios. */
if (!detected_firmware_type) {
/* First try at address 0 */
detected_firmware_type =
find_lb_table(info, (void*)0x00000, (void*)0x1000);
}
if (!detected_firmware_type) {
/* Then try at address 0xf0000 */
detected_firmware_type =
find_lb_table(info, (void*)0xf0000, (void*)0x100000);
detected_firmware_type = find_lb_table(info);
}
if (!detected_firmware_type) {
@ -1424,8 +1331,8 @@ void initialize_linux_params(struct param_info *info)
info->real_mode->cl_offset = 2048;
/* Now set the command line */
len = strnlen(note_command_line.command_line, sizeof(info->real_mode->command_line) -1);
memcpy(info->real_mode->command_line, note_command_line.command_line, len);
len = strnlen(note_command_line, sizeof(info->real_mode->command_line) -1);
memcpy(info->real_mode->command_line, note_command_line, len);
info->real_mode->command_line[len] = '\0';
/* from the bios initially */
@ -1446,12 +1353,12 @@ void initialize_linux_params(struct param_info *info)
/* old ramdisk options, These really should be command line
* things...
*/
info->real_mode->ramdisk_flags = note_ramdisk_flags.ramdisk_flags;
info->real_mode->ramdisk_flags = note_ramdisk_flags;
/* default to /dev/hda.
* Override this on the command line if necessary
*/
info->real_mode->orig_root_dev = note_root_dev.root_dev;
info->real_mode->orig_root_dev = note_root_dev;
/* Originally from the bios? */
info->real_mode->aux_device_info = 0;
@ -1469,9 +1376,9 @@ void initialize_linux_params(struct param_info *info)
/* Ramdisk address and size ... */
info->real_mode->initrd_start = 0;
info->real_mode->initrd_size = 0;
if (note_initrd_size.initrd_size) {
info->real_mode->initrd_start = note_initrd_start.initrd_start;
info->real_mode->initrd_size = note_initrd_size.initrd_size;
if (note_initrd_size) {
info->real_mode->initrd_start = note_initrd_start;
info->real_mode->initrd_size = note_initrd_size;
}
/* Now remember those things that I need */

View file

@ -18,25 +18,31 @@
INCLUDE mkelfImage.lds
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
OUTPUT_ARCH(i386)
MEMORY
PHDRS
{
/* 0x10000 - 0x90000 A good safe area in low memory I can use */
low (rwx) : ORIGIN = 0x010000, LENGTH = 0x0080000
middle (rwx) : ORIGIN = 0x091000, LENGTH = 0x0001000
upper (rwx) : ORIGIN = 0x100000, LENGTH = 0x37f00000
note PT_NOTE ;
low PT_LOAD ;
middle PT_LOAD ;
upper PT_LOAD ;
ramdisk PT_LOAD ;
}
ENTRY(startup_32)
SECTIONS
{
. = 0x10000 ;
_text = .; /* Text and read-only data */
.text (.): {
.note . : {
*(.note.data)
} :note :low
.text . : {
*(.text)
*(.fixup)
*(.gnu.warning)
} > low = 0x9090
.rodata (.): { *(.rodata) *(.note.data)} > low
.kstrtab (.): { *(.kstrtab) } > low
} :low = 0x9090
.rodata (.): { *(.rodata) } :low
.kstrtab (.): { *(.kstrtab) } :low
. = ALIGN(16); /* Exception table */
@ -45,29 +51,29 @@ SECTIONS
.data (.): { /* Data */
*(.data)
CONSTRUCTORS
} > low
} :low
_edata = .; /* End of data section */
__bss_start = .; /* BSS */
.bss (.): {
*(.bss)
} > low
} :low
_end = . ;
. = 0x91000 ;
.nokill (.): {
*(.nokill)
} > middle
} :middle
. = 0x100000 ;
.kernel (.): {
*(.kernel)
} > upper
} :upper
. = initrd_base;
.ramdisk (ALIGN(4096)) : {
*(.ramdisk)
} > upper
} :ramdisk
ramdisk_data_size = (ramdisk_data_end - ramdisk_data);
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }

View file

@ -1,6 +1,7 @@
#ifndef ELF_BOOT_H
#define ELF_BOOT_H
#ifndef ASSEMBLY
#include <stdint.h>
/* This defines the structure of a table of parameters useful for ELF
@ -38,6 +39,8 @@ typedef struct
Elf_Word n_type; /* Type of the note. */
} Elf_Nhdr;
#endif /* ASSEMBLY */
/* For standard notes n_namesz must be zero */
/* All of the following standard note types provide a single null
* terminated string in the descriptor.

View file

@ -1,13 +1,15 @@
#
# exec_kernel/user_space/head.S
#
# Copyright (C) 2000 Eric Biederman
#
# Parts of this code were take from the linux startup
# code of linux-2.4.0-test9
#
# Other parts were taken from etherboot-5.0.5
#
/*
* exec_kernel/user_space/head.S
*
* Copyright (C) 2000 Eric Biederman
*
* Parts of this code were take from the linux startup
* code of linux-2.4.0-test9
*
* Other parts were taken from etherboot-5.0.5
*/
#define ASSEMBLY 1
#define RELOC 0x10000
#define KERN_CODE_SEG 0x10
@ -17,6 +19,7 @@
.equ CR0_PE,1
#if 0
#define TTYS0_BASE 0x3f8
#define TTYS0_RBR (TTYS0_BASE+0x00)
#define TTYS0_TBR TTYS0_RBR
@ -38,6 +41,7 @@
#define TTYS0_TX_CHAR(byte) \
mov byte, %al ; \
TTYS0_TX_AL
#endif
.text
.code32
@ -112,6 +116,8 @@ startup_32:
/* Routines to query the BIOS... */
#if 0
.globl noop
noop:
TTYS0_TX_CHAR($'a')
@ -128,6 +134,7 @@ noop:
TTYS0_TX_CHAR($'\r')
TTYS0_TX_CHAR($'\n')
ret
#endif
/**************************************************************************
E820_MEMSIZE - Get a listing of memory regions
@ -388,3 +395,117 @@ gdt:
.byte (RELOC>>16),0x93,0x00,(RELOC>>24)
.section ".note.data", "a", @progbits
#include "elf_boot.h"
/* You can probably do this in C if everything was all in one structure,
* but doing it pieces resulted in strange alignments of the data structures.
* Since that breaks the definition of the .note section I do it here in
* in assembly. The control is better and for simplicity it is a toss up.
*/
.balign 4
.int 2f - 1f
.int 4f - 3f
.int EIN_PROGRAM_NAME
1: .asciz "ELFBoot"
2:
.balign 4
3:
.asciz "Linux"
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int EIN_PROGRAM_VERSION
1: .asciz "ELFBoot"
2:
.balign 4
3:
.asciz DEFAULT_PROGRAM_VERSION
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int EIN_PROGRAM_CHECKSUM
1: .asciz "ELFBoot"
2:
.balign 4
3:
.word 0
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int LIN_COMMAND_LINE
1: .asciz "Linux"
2:
.balign 4
3:
.globl note_command_line
note_command_line:
.asciz DEFAULT_COMMAND_LINE
33:
.fill 256 - (33b - 3b), 1, 0
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int LIN_ROOT_DEV
1: .asciz "Linux"
2:
.balign 4
3:
.globl note_root_dev
note_root_dev:
.word DEFAULT_ROOT_DEV
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int LIN_RAMDISK_FLAGS
1: .asciz "Linux"
2:
.balign 4
3:
.globl note_ramdisk_flags
note_ramdisk_flags:
.word 0
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int LIN_INITRD_START
1: .asciz "Linux"
2:
.balign 4
3:
.globl note_initrd_start
note_initrd_start:
.int ramdisk_data
4:
.balign 4
.int 2f - 1f
.int 4f - 3f
.int LIN_INITRD_SIZE
1: .asciz "Linux"
2:
.balign 4
3:
.globl note_initrd_size
note_initrd_size:
.int ramdisk_data_size
4:
.balign 4
.previous

View file

@ -20,8 +20,9 @@ $params{PREFIX}=undef();
$params{RAMDISK}="";
$params{VMLINUX}="/usr/src/linux/vmlinux";
$params{TARGET}="elfImage";
$params{ROOT_DEV}='((0x3<<16)| 0)';
$params{ROOT_DEV}='((0x3<<8)| 0)';
$params{COMMAND_LINE}='';
$params{PROGRAM_VERSION}='';
$params{OUTPUT_FORMAT}='elf32-i386';
$params{INITRD_BASE}=0x00800000; # 8MB
@ -30,18 +31,9 @@ sub compile_file
{
my ($params, $src, $dst) = @_;
my ($options, $cmd);
$options = "-DDEFAULT_ROOT_DEV='((unsigned short)$params->{ROOT_DEV})'";
$options = "-DDEFAULT_ROOT_DEV='($params->{ROOT_DEV})'";
$options .= " -DDEFAULT_COMMAND_LINE='\"$params->{COMMAND_LINE}\"'";
if (defined($params->{RAMDISK_IMAGE_START})) {
$options .= " -DDEFAULT_RAMDISK_IMAGE_START=" .
"'((unsigned short)$params->{RAMDISK_IMAGE_START})'";
}
if ($params->{RAMDISK_PROMPT_FLAG}) {
$options .= " -DDEFAULT_RAMDISK_PROMPT_FLAG";
}
if ($params->{RAMDISK_LOAD_FLAG}) {
$options .= " -DDEFAULT_RAMDISK_LOAD_FLAG";
}
$options .= " -DDEFAULT_PROGRAM_VERSION='\"$params->{PROGRAM_VERSION}\"'";
$cmd = "$params->{CC} $params->{CFLAGS} $options -c $params->{PREFIX}/$src -o $dst";
print " Running $cmd \n";
system($cmd);
@ -73,6 +65,18 @@ sub build_kernel_piggy
$ramsize, $vidmode, $rootdev);
(undef, $setupsects, $flags, $syssize, $swapdev, $ramsize,
$vidmode, $rootdev) = unpack('a497Cv6', $buffer);
$fd_in->read($buffer, 32768) == 32768
or die "Error reading setup sector of $src";
my (undef, $header, $version, undef, $start_sys, $kver_addr)
= unpack('a2a4Sa4SS', $buffer);
if ($header ne 'HdrS') {
die "Not an linux kernel";
}
if ($setupsects == 0) {
$setupsects = 4;
}
$params->{PROGRAM_VERSION} = unpack('Z32768', substr($buffer, $kver_addr));
my $fd_out = new FileHandle;
$fd_out->open(">${dst}.obj") or die "Cannot open ${dst}.obj";
$fd_in->seek(512 + (512*$setupsects), 0);
@ -152,6 +156,136 @@ sub build_elf_image
return $dst;
}
sub compute_ip_checksum
{
my ($str) = @_;
my ($checksum, $i, $size, $shorts);
$checksum = 0;
$size = length($str);
$shorts = $size >> 1;
for($i = 0; $i < $shorts; $i++) {
$checksum += unpack('S', substr($str, $i <<1, 2));
$checksum -= 0xFFFF unless ($checksum <= 0xFFFF);
}
if ($size & 1) {
$checksum -= 0xFFFF unless ($checksum <= 0xFFFF);
$checksum += unpack('C', substr($str, -1, 1));
}
return (~$checksum) & 0xFFFF;
}
sub add_ip_checksums
{
my ($offset, $sum, $new) = @_;
my $checksum;
$sum = ~$sum & 0xFFFF;
$new = ~$new & 0xFFFF;
if ($offset & 1) {
$new = (($new >> 8) & 0xff) | (($new << 8) & 0xff00);
}
$checksum = $sum + $new;
if ($checksum > 0xFFFF) {
$checksum -= 0xFFFF;
}
return (~$checksum) & 0xFFFF;
}
sub write_ip_checksum
{
my ($file) = @_;
my ($fd, $buffer, %ehdr, @phdrs, $key, $size, $i);
my ($checksum, $offset) = 0;
$fd = new FileHandle;
$fd->open("+<$file") or die "Cannot open $file\n";
$fd->read($buffer, 52) == 52 or die "Cannot read ELF header\n";
@ehdr{e_ident, e_type, e_machine, e_version, e_entry, e_phoff, e_shoff,
e_flags, e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum,
e_shstrndx} = unpack('a16SSLLLLLSSSSSS', $buffer);
$checksum = compute_ip_checksum($buffer);
$offset += 52;
## FIXME add some sanity checks here...
#foreach $key (keys(%ehdr)) {
# print "$key: $ehdr{$key}\n";
#}
$fd->seek($ehdr{e_phoff}, 0) or die "Cannot seek to $ehdr{e_phoff}\n";
$size = $ehdr{e_phnum}*32;
$fd->read($buffer, $size) == $size or die "Cannot read ELF Program header $size\n";
for($i = 0; $i < $ehdr{e_phnum} ; $i++) {
my %phdr;
@phdr{p_type, p_offset, p_vaddr, p_paddr, p_filesz, p_memsz,
p_flags, p_align}
= unpack('LLLLLLLL', substr($buffer, $i*32, 32));
push(@phdrs, \%phdr);
}
$checksum = add_ip_checksums($offset, $checksum,
compute_ip_checksum($buffer));
$offset += $size;
for($i = 0; $i < scalar(@phdrs); $i++) {
my $phdr = $phdrs[$i];
#print("\n");
#foreach $key (keys(%$phdr)) {
# printf("%10s: %08x\n", $key, $phdr->{$key});
#}
# Only worry about PT_LOAD segments
next unless ($phdr->{p_type} == 1);
$fd->seek($phdr->{p_offset}, 0) or die "Cannot seek to $phdr->{p_offset}\n";
$fd->read($buffer, $phdr->{p_filesz}) == $phdr->{p_filesz}
or die "Cannot read ELF segment $phdr->{p_filesz}\n";
$buffer .= "\0" x ($phdr->{p_memsz} - $phdr->{p_filesz});
$checksum = add_ip_checksums($offset, $checksum,
compute_ip_checksum($buffer));
$offset += $phdr->{p_memsz}
}
printf("checksum: %04x\n", $checksum);
my $phdr = $phdrs[0];
# Do I have a PT_NOTE segment?
if ($phdr->{p_type} == 4) {
my $offset;
$fd->seek($phdr->{p_offset}, 0) or die "Cannot seek to $phdr->{p_offset}\n";
$fd->read($buffer, $phdr->{p_filesz}) == $phdr->{p_filesz}
or die "Cannot read ELF segment $phdr->{p_filesz}\n";
$offset = 0;
while($offset < length($buffer)) {
my %note;
@note{n_namesz, n_descsz, n_type}
= unpack('LLL', substr($buffer, $offset, 12));
$offset += 12;
$note{n_name} = unpack("a$note{n_namesz}",
substr($buffer, $offset, $note{n_namesz}));
$offset += ($note{n_namesz} + 3) & ~3;
$note{n_desc} = unpack("a$note{n_descsz}",
substr($buffer, $offset, $note{n_descsz}));
#printf("n_type: %08x n_name(%d): %s n_desc(%d): %s\n",
# $note{n_type},
# $note{n_namesz}, $note{n_name},
# $note{n_descsz}, $note{n_desc});
if (($note{n_namesz} == 8) &&
($note{n_name} eq "ELFBoot\0") &&
($note{n_type} == 3)) {
my ($foffset, $buffer);
$foffset = $phdr->{p_offset} + $offset;
$buffer = pack('S', $checksum);
$fd->seek($foffset, 0) or die "Cannot seek to $foffset";
$fd->print($buffer);
#print("checksum note...\n");
}
$offset += ($note{n_descsz} + 3) & ~3;
}
}
$fd->close();
}
sub build
{
my ($params) = @_;
@ -171,6 +305,7 @@ sub build
"$tempdir/head_$$.o"));
build_elf_image($params, $params->{TARGET}, @objects);
unlink(@objects);
write_ip_checksum($params->{TARGET});
}
@ -186,10 +321,6 @@ sub main
'output=s' => \$params->{TARGET},
'version' => \$wantversion,
'ramdisk-base=i' =>\$params->{INITRD_BASE},
# FIXME figure out what the old RAMDISK_IMAGE flags are about.
#'ramdisk-image-start=i' => \$params{RAMDISK_IMAGE_START},
#'ramdisk-prompt-flag!' => \$params{RAMDISK_PROMPT_FLAG},
#'ramdisk-load-flag!' => \$params{RAMDISK_LOAD_FLAG},
);
if (defined($wantversion) && $wantversion) {
print "$0 $VERSION\n";