Page 2 of 2

Posted: Fri Feb 08, 2008 5:31 am
by AJ
JamesM wrote:Very interesting. So program headers give the same information as section headers? Why are they duplicated?
I believe the program headers are intended to be used by a program loader, whereas the section headers are more suited to dynamic linking. If you were to strip debug symbols from the object, I believe that the resultant, statically linked binary could contain no section headers - just program headers. Conversely, a shared object could contain no program headers. I have never actually seen this, though.

You will notice that the program header contains no symbol name field. I don't know if the section header actually has any field stating 'this section must be loaded before executing the binary, whereas the program header does (PT_LOAD).

In my case, I use no linker script and just define that I want the start of the kernel to be at 0xFFFFF00000000000 (canonical). It therefore seems appropriate to use program headers for relocation. The kernel itself then loads symbolic information, if available, from the original ELF image. It will use this symbol information from the section headers to dynamically link modules.

Cheers,
Adam

Posted: Fri Feb 08, 2008 5:35 am
by JamesM
I believe that the resultant, statically linked binary could contain no section headers - just program headers.
I don't believe this is possible - I use the section headers for both program loading and program relocation, and stripping symbols hasn't rendered my loader useless yet!

Posted: Fri Feb 08, 2008 5:51 am
by StephanvanSchaik
The specification says that the section headers are optional for the ELF file. The program headers aren't optional.

Posted: Fri Feb 08, 2008 6:00 am
by AJ
Sections contain all information in an object file, except the ELF header, the program header table, and the section header table.
But, as StephanVanSchaik says, in the 'execution view' (ELF specification 1.2, page 1-1) for a linked program, the section header is optional. In the 'linking view', the program header is optional.

So in some architectures and in some instances, you may find no section header in a fully linked executable.

Cheers,
Adam

Posted: Fri Feb 08, 2008 6:04 am
by JamesM
AJ wrote:
Sections contain all information in an object file, except the ELF header, the program header table, and the section header table.
But, as StephanVanSchaik says, in the 'execution view' (ELF specification 1.2, page 1-1) for a linked program, the section header is optional. In the 'linking view', the program header is optional.

So in some architectures and in some instances, you may find no section header in a fully linked executable.

Cheers,
Adam
Very interesting, I'll look into this!

Cheers,

James

Something wrong in my code?

Posted: Sat Feb 09, 2008 7:04 am
by StephanvanSchaik
Hmm, my code doesn't work.

Code: Select all

ParseKernel:
    .ELFStart:
        pusha

Code: Select all

    .ELFHeader:
        mov DWORD [StartAddress], ebx
        
        cmp BYTE [ebx+0], 0x7F
        jne .NoELF
        cmp BYTE [ebx+1], 'E'
        jne .NoELF
        cmp BYTE [ebx+2], 'L'
        jne .NoELF
        cmp BYTE [ebx+3], 'F'
        jne .NoELF

        cmp BYTE [ebx+4], 1
        jne .UnsupportedELF
        cmp BYTE [ebx+5], 1
        jne .UnsupportedELF
        cmp BYTE [ebx+6], 1
        jne .UnsupportedELF
        cmp WORD [ebx+16], 0x0002
        jne .UnsupportedELF
        cmp WORD [ebx+18], 0x3
        jne .UnsupportedELF
        cmp WORD [ebx+20], 1
        jne .UnsupportedELF
        jmp .ProgramHeadersInfo
Check the ELF header.

Code: Select all

    .ProgramHeadersInfo:
        xor eax, eax
        mov eax, DWORD [ebx+28]
        mov DWORD [phOffset], eax
            
        xor eax, eax
        mov ax, WORD [ebx+42]
        mov WORD [phEntrySize], ax
            
        xor eax, eax
        mov ax, WORD [ebx+44]
        mov WORD [phAmount], ax
            
        cmp ax, 3
        jng .ProgramHeaders
        jmp .UnsupportedELF
Get information about the program headers...

Code: Select all

    .NoELF:
        xor ebx, ebx
        mov ebx, msgNoELF
        call PrintString
        popa
        ret

    .UnsupportedELF:
        xor ebx, ebx
        mov ebx, msgUSELF
        popa
        ret
If the ELF is not an ELF or not for this platform or whatever...

Code: Select all

    .ProgramHeaders:
        xor ebx, ebx
        mov ebx, DWORD [StartAddress]
        add ebx, DWORD [phOffset]
            
        xor eax, eax
        mov ax, 1
        cmp WORD [phAmount], ax
        jge .PPH
        jmp .PHMELF
Set ebx to the first entry of the program header table and check if there is at least 1 program header.

Code: Select all

    .PHMELF
        xor ebx, ebx
        mov ebx, msgPHMELF
        call PrintString
        popa
        ret
Tell the user that the ELF file has no program headers.

Code: Select all

    .PPH:
        xor eax, eax
        mov eax, DWORD [ebx]
        mov DWORD [pType1], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+4]
        mov DWORD [pOffset1], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+8]
        mov DWORD [pVirtAddr1], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+16]
        mov DWORD [pFileSize1], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+20]
        mov DWORD [pMemSize1], eax
                
        xor eax, eax
        mov ax, 2
        cmp WORD [phAmount], ax
        jge .SPH
        jmp .PH1
            
    .SPH:
        add ebx, DWORD [phEntrySize]
            
        xor eax, eax
        mov eax, DWORD [ebx]
        mov DWORD [pType2], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+4]
        mov DWORD [pOffset2], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+8]
        mov DWORD [pVirtAddr2], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+16]
        mov DWORD [pFileSize2], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+20]
        mov DWORD [pMemSize2], eax
                
        xor eax, eax
        mov ax, 3
        cmp WORD [phAmount], ax
        jge .TPH
        jmp .PH1
           
    .TPH:
        add ebx, DWORD [phEntrySize]
            
        xor eax, eax
        mov eax, DWORD [ebx]
        mov DWORD [pType3], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+4]
        mov DWORD [pOffset3], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+8]
        mov DWORD [pVirtAddr3], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+16]
        mov DWORD [pFileSize3], eax
                
        xor eax, eax
        mov eax, DWORD [ebx+20]
        mov DWORD [pMemSize3], eax
                
        jmp .PH1
Read out the 3 program headers, if there are 3...

Code: Select all

    .PH1:
        xor ebx, ebx
        mov ebx, 1
        cmp ebx, DWORD [pType1]
        jne .PH2
            
        xor eax, eax
        xor ebx, ebx
        mov ebx, DWORD [pVirtAddr1]
        mov eax, DWORD [StartAddress]
        add eax, DWORD [pOffset1]
            
        xor ecx, ecx
        mov ecx, DWORD [pFileSize1]
        
        .PH1LOOP1:
            xor edx, edx
            mov dl, BYTE [eax]
            mov BYTE [ebx], dl
            inc ebx
            inc eax
            loop .PH1LOOP1
            
        xor ecx, ecx
        mov ecx, DWORD [pMemSize1]
        sub ecx, DWORD [pFileSize1]
       
        .PH1LOOP2:
            xor edx, edx
            mov dl, 0
            mov BYTE [ebx], dl
            inc ebx
            loop .PH1LOOP2
        
        .PH2:
            xor ebx, ebx
            mov ebx, 1
            cmp ebx, DWORD [pType2]
            jne .PH3
            
            xor eax, eax
            xor ebx, ebx
            mov ebx, DWORD [pVirtAddr2]
            mov eax, DWORD [StartAddress]
            add eax, DWORD [pOffset2]
            
            xor ecx, ecx
            mov ecx, DWORD [pFileSize2]
            
            .PH2LOOP1:
                xor edx, edx
                mov dl, BYTE [eax]
                mov BYTE [ebx], dl
                inc ebx
                inc eax
                loop .PH2LOOP1
            
            xor ecx, ecx
            mov ecx, DWORD [pMemSize2]
            sub ecx, DWORD [pFileSize2]
            
            .PH2LOOP2:
                xor edx, edx
                mov dl, 0
                mov BYTE [ebx], dl
                inc ebx
                loop .PH2LOOP2
        
        .PH3:
            xor ebx, ebx
            mov ebx, 1
            cmp ebx, DWORD [pType3]
            jne .ExecAddr
            
            xor eax, eax
            xor ebx, ebx
            mov ebx, DWORD [pVirtAddr3]
            mov eax, DWORD [StartAddress]
            add eax, DWORD [pOffset3]
            
            xor ecx, ecx
            mov ecx, DWORD [pFileSize3]
            
            .PH3LOOP1:
                xor edx, edx
                mov dl, BYTE [eax]
                mov BYTE [ebx], dl
                inc ebx
                inc eax
                loop .PH3LOOP1
            
            xor ecx, ecx
            mov ecx, DWORD [pMemSize3]
            sub ecx, DWORD [pFileSize3]
            
            .PH3LOOP2:
                xor edx, edx
                mov dl, 0
                mov BYTE [ebx], dl
                inc ebx
                loop .PH3LOOP2
Reallocate the segments and fill out the rest of memory with zeroes (if memsize is greater than the filesize).

Code: Select all

        .ExecAddr
            xor ebx, ebx
            mov ebx, msgELF
            call PrintString
            
            xor ebx, ebx
            mov ebx, DWORD [StartAddress]
            jmp .Parsed
Set ebx back to the startaddress... (Maybe it should be the entry address instead of the start address given)

Code: Select all

        .Parsed:
            popa
            ret
Parsing complete, return...

Code: Select all

ExecuteKernel:
    jmp ebx
    ret
Jump to ebx to execute the kernel. (if the kernel is at ebx else it will triple fault or something)

Code: Select all

StartKernel:
    mov ax, 0x10
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov esp, 0x90000
    
    call ClearScreen
    
    mov ebx, 0x100000
    call ParseKernel
    call ExecuteKernel

    cli
    hlt
This is the code when the second stage went to 32-bit.

Code: Select all

StartAddress          DD 0x00000000
phOffset              DD 0x00000000
phEntrySize           DW 0x0000
phAmount              DW 0x0000
phCurrentEntry        DW 0x0000
pType1                DD 0x00000000
pType2                DD 0x00000000
pType3                DD 0x00000000
pOffset1              DD 0x00000000
pOffset2              DD 0x00000000
pOffset3              DD 0x00000000
pVirtAddr1            DD 0x00000000
pVirtAddr2            DD 0x00000000
pVirtAddr3            DD 0x00000000
pFileSize1            DD 0x00000000
pFileSize2            DD 0x00000000
pFileSize3            DD 0x00000000
pMemSize1             DD 0x00000000
pMemSize2             DD 0x00000000
pMemSize3             DD 0x00000000
The variables...

The only thing what could be wrong in my eyes is the startaddress moved to ebx when returning if the code is not wrong...

Are there some mistakes? :?

Sorry for the big code...


Thanks already,
Stephan van Schaik.