Kernel in ELF Format - Potential Linker Issue?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
shickey
Posts: 5
Joined: Wed Jan 06, 2016 7:44 pm

Kernel in ELF Format - Potential Linker Issue?

Post by shickey »

Just to see if it is possible, I decided to try to build the Bare Bones kernel using only tools from the LLVM project (i.e., clang instead of gcc and lld instead of ld). After much battling with the toolchain, I was able to link a kernel binary together, package an iso with GRUB, and attempted to execute it in virtualbox. This resulted in

Code: Select all

Error 7: Loading below 1MB is not supported
After getting more information about the binary via readelf and objdump, I believe what I'm seeing is a bug in the lld linker, but I am trying to make sure I fully understand exactly what went wrong.

I found two things of note from readelf, the first is the program header table:

Code: Select all

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  INTERP         0x0008e5 0x000008e5 0x000008e5 0x0001c 0x0001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x000000 0x00000000 0x00000000 0x00b50 0x00b50 R E 0x1000
  LOAD           0x001000 0x00001000 0x00001000 0x00000 0x14010 RW  0x1000
  DYNAMIC        0x000b20 0x00000b20 0x00000b20 0x00030 0x00030 R   0x4
From what I understand, it looks like the executable section is set up to load at 0x00000000 which is what is causing the Error 7 mentioned above (if my understanding is correct).

Second, the .symtab appears to be corrupted:

Code: Select all

Symbol table '.symtab' contains 1 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00010101     0 NOTYPE  LOCAL  DEFAULT  UND <corrupt>
and the symbols that I would have expected to be there appear in a separate symbol table called .hash:

Code: Select all

Symbol table '.hash' contains 66 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000930     0 OBJECT  LOCAL  DEFAULT   11 .LCPI4_0
     2: 00000940    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_1
     3: 00000950    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_2
     4: 00000960    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_3
     5: 00000970    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_4
     6: 00000980    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_5
     7: 00000990    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_6
     8: 000009a0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_7
     9: 000009b0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_8
    10: 000009c0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI4_9
    11: 000009d0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_0
    12: 000009e0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_1
    13: 000009f0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_2
    14: 00000a00    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_3
    15: 00000a10    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_4
    16: 00000a20    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_5
    17: 00000a30    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_6
    18: 00000a40    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_7
    19: 00000a50    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_8
    20: 00000a60    16 OBJECT  LOCAL  DEFAULT   11 .LCPI6_9
    21: 00000a70    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_0
    22: 00000a80    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_1
    23: 00000a90    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_2
    24: 00000aa0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_3
    25: 00000ab0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_4
    26: 00000ac0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_5
    27: 00000ad0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_6
    28: 00000ae0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_7
    29: 00000af0    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_8
    30: 00000b00    16 OBJECT  LOCAL  DEFAULT   11 .LCPI7_9
    31: 00000b10     0 OBJECT  LOCAL  DEFAULT   11 .L.str
    32: 00001000     0 OBJECT  LOCAL  DEFAULT   14 stack_bottom
    33: 00005000 65536 OBJECT  LOCAL  DEFAULT   14 stack_top
    34: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __preinit_array_start
    35: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __preinit_array_end
    36: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __init_array_start
    37: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __init_array_end
    38: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __fini_array_start
    39: 00000000     0 OBJECT  LOCAL  HIDDEN   ABS __fini_array_end
    40: 00000001     0 OBJECT  LOCAL  DEFAULT  ABS ALIGN
    41: e4524ffb     0 OBJECT  LOCAL  DEFAULT  ABS CHECKSUM
    42: 00000003     0 OBJECT  LOCAL  DEFAULT  ABS FLAGS
    43: 1badb002     0 OBJECT  LOCAL  DEFAULT  ABS MAGIC
    44: 00000002     0 OBJECT  LOCAL  DEFAULT  ABS MEMINFO
    45: 00000000     0 OBJECT  LOCAL  DEFAULT  ABS kernel.c
    46: 000000c0    14 FUNC    GLOBAL DEFAULT    1 _start
    47: 000000d0    32 FUNC    GLOBAL DEFAULT    1 make_color
    48: 000000f0    32 FUNC    GLOBAL DEFAULT    1 make_vgaentry
    49: 00000110    32 FUNC    GLOBAL DEFAULT    1 strlen
    50: 00000130   128 FUNC    GLOBAL DEFAULT    1 terminal_initialize
    51: 000001b0   464 FUNC    GLOBAL DEFAULT    1 move_buffer_up
    52: 00000380    48 FUNC    GLOBAL DEFAULT    1 terminal_write_char_at
    53: 000003b0   560 FUNC    GLOBAL DEFAULT    1 terminal_write_char
    54: 000005e0   640 FUNC    GLOBAL DEFAULT    1 terminal_write_str
    55: 00000860   133 FUNC    GLOBAL DEFAULT    1 kernel_main
    56: 00015000     4 OBJECT  GLOBAL DEFAULT   14 terminal_buffer
    57: 00015004     1 OBJECT  GLOBAL DEFAULT   14 terminal_color
    58: 00015008     4 OBJECT  GLOBAL DEFAULT   14 terminal_column
    59: 0001500c     4 OBJECT  GLOBAL DEFAULT   14 terminal_row
    60: 00001000     0 OBJECT  GLOBAL DEFAULT  ABS __bss_start
    61: 00015010     0 OBJECT  GLOBAL DEFAULT  ABS __bss_end
    62: 00015010     0 OBJECT  GLOBAL DEFAULT  ABS _end
    63: 00015010     0 OBJECT  GLOBAL DEFAULT  ABS end
    64: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS __rela_iplt_start
    65: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS __rela_iplt_end
Trying to run the binary through objdump simply exits with the error

Code: Select all

objdump: durian.bin: Bad value
Is my understanding correct that the lld linker has not linked this binary together into a well-formed ELF executable?

(P.S. I know that I can just build a usable gcc cross-compiler to get this up and working. I'm ultimately less interested in getting this to boot than am I understanding what went wrong in the compilation/linking process.)
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by kzinti »

shickey wrote:

Code: Select all

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  INTERP         0x0008e5 0x000008e5 0x000008e5 0x0001c 0x0001c R   0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
That is a problem. Your ELF is targeted at 64 bits Linux. You want to build a free-standing 32 bits ELF binary.
shickey
Posts: 5
Joined: Wed Jan 06, 2016 7:44 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by shickey »

Oh, good eye. This seems odd, though, since the ELF header shows that it's targeting a 32-bit Intel 80386 architecture. Here's the readelf output:

Code: Select all

ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0xc0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          9392 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         4
  Size of section headers:           40 (bytes)
  Number of section headers:         15
  Section header string table index: 4
shickey
Posts: 5
Joined: Wed Jan 06, 2016 7:44 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by shickey »

Ah, okay! I think I solved that problem for the moment. Here are readelf program headers now:

Code: Select all

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x000000 0x00000000 0x00000000 0x004aa 0x004aa R E 0x1000
  LOAD           0x001000 0x00001000 0x00001000 0x00000 0x04010 RW  0x1000
Still, same `Error 7: Loading below 1MB is not supported` when I attempt to run it.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by kzinti »

Then link your ELF above 1M. =) It's at 0x00000000 right now...
shickey
Posts: 5
Joined: Wed Jan 06, 2016 7:44 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by shickey »

Ha. Yup. I did just that now before I saw your message and I think I'm making progress finally. I thought I had set the offset correctly in my linker script:

Code: Select all

ENTRY(_start)
SECTIONS
{
  
  . = 1M;
  
  .text BLOCK(4K) : ALIGN(4K)
  {
    *(.multiboot)
    *(.text)
  }
  
  .rodata BLOCK(4K) : ALIGN(4K)
  {
    *(.rodata)
  }
  
  .data BLOCK(4K) : ALIGN(4K)
  {
    *(.data)
  }
  
  .bss BLOCK(4K) : ALIGN(4K)
  {
    *(COMMON)
    *(.bss)
    *(.bootstrap_stack)
  }
  
}
But I was able to get it to work by passing --image-base 0x100000 directly to lld on the command line. Now I'm getting a triple-fault when I boot:

Code: Select all

[Multiboot-elf, <0x100000:0x1000:0x4010>, shtab=0x106190(bad), entry=0x1000600])
But hey, progress!
FusT
Member
Member
Posts: 91
Joined: Wed Sep 19, 2012 3:43 am
Location: The Netherlands

Re: Kernel in ELF Format - Potential Linker Issue?

Post by FusT »

It could be me misunderstanding things but I think you should specify the base address as a hex value in your linker script:

Code: Select all

ENTRY(_start)
SECTIONS
{
  
  . = 0x100000;
  
  .text BLOCK(4K) : ALIGN(4K)
  {
........
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by kzinti »

No, the following is perfectly valid:

Code: Select all

    . = 1M;
User avatar
Roman
Member
Member
Posts: 568
Joined: Thu Mar 27, 2014 3:57 am
Location: Moscow, Russia
Contact:

Re: Kernel in ELF Format - Potential Linker Issue?

Post by Roman »

Make sure you use "clang --target=i686-pc-none-elf". Personally, I use a GNU binutils + clang setup.
"If you don't fail at least 90 percent of the time, you're not aiming high enough."
- Alan Kay
shickey
Posts: 5
Joined: Wed Jan 06, 2016 7:44 pm

Re: Kernel in ELF Format - Potential Linker Issue?

Post by shickey »

Thanks, all. Roman, yeah, I've been using that target triple which looks like it's creating the right object files. At this point, I'm thinking that the major thing going on here is that lld has poor support for all but the simplest linker scripts. I've removed the

Code: Select all

. = 1M;
entirely and am instead passing the --image-base flag on the command line which seems to be doing the trick much better. Interestingly, removing the *(.multiboot) declaration from the .text section seems to have fixed the corrupt .symtab issue and I'm no longer getting the `(bad)` indicator about .shtab upon booting (though, the kernel is still crashing immediately). Looks like objdump is also finally able to read the binary. Getting closer...
Post Reply