flat assembler
Message board for the users of flat assembler.

Index > Linux > Alignment issues on dynamically linked program [SOLVED]

Author
Thread Post new topic Reply to topic
reyuki



Joined: 24 Jan 2025
Posts: 12
reyuki 26 Jan 2025, 13:21
I tried to write an ELF executable from scratch and use this example as reference

however, my program encounter a segfault in the loader (ld-linux.so), and there's a post that mention the same case and the cause is alignment, which previously (before getting involved with the loader and shared lib stuff) I didn't pay attention because it runs without encounter any segfault (probably because it was statically linked?)

my question is, why linux only force alignment when the program was dynamically linked?

and how to make sure that the ELF program is properly aligned? I'm confused with the ELF standard specification and not quite understand what it means:
Quote:

As ‘‘Program Loading’’ later in this part describes, loadable process segments must have
congruent values for p_vaddr and p_offset, modulo the page size. This member
gives the value to which the segments are aligned in memory and in the file. Values 0
and 1 mean no alignment is required. Otherwise, p_align should be a positive, integral
power of 2, and p_vaddr should equal p_offset, modulo p_align.


the paragraph is too academic for me personally, if I specify p_align to 0x100000 (like the reference's example) at PT_LOAD entry, how much bytes/padding should I add to meet alignment requirement in the _start section?

the assembly code:
Code:
use64

BASE=0x400000
OFFSETOF equ -BASE+
ELF64_PHEADER_ENTRY_SZ = 56

org BASE
Elf64_Ehdr:
        db 0x7F, "ELF", 2, 1, 1                         ; e_ident[...]          = 7 bytes
        times 9 db 0                                    ; e_ident[EI_PAD]       = 9 bytes
        dw 2                                            ; e_type ET_EXEC        = 2 bytes
        dw 0x3e                                         ; e_machine AMD x86-64  = 2 bytes
        dd 1                                            ; e_version             = 4 bytes
        dq _start                                       ; e_entry entry point   = 8 bytes
        dq OFFSETOF Elf64_Phdr                          ; e_phoff               = 8 bytes
        dq 0                                            ; e_shoff               = 8 bytes
        dd 0                                            ; e_flags               = 4 bytes
        dw Elf64_Ehdr.SIZE                              ; e_ehsize              = 2 bytes
        dw ELF64_PHEADER_ENTRY_SZ                       ; e_phentsize           = 2 bytes
        dw Elf64_Phdr.SIZE/ELF64_PHEADER_ENTRY_SZ       ; e_phnum               = 2 bytes
        dw 0                                            ; e_shentsize           = 2 bytes
        dw 0                                            ; e_shnum               = 2 bytes
        dw 0                                            ; e_shstrndx            = 2 bytes

                                                        ; total                 = 64 bytes
        .SIZE = $-Elf64_Ehdr

Elf64_Phdr:
        dd 3                                            ; p_type PT_INTERP      = 4 bytes
        dd 4                                            ; p_flags RO            = 4 bytes
        dq OFFSETOF _interpreter_name_                  ; p_offset              = 8 bytes
        dq _interpreter_name_                           ; p_vaddr               = 8 bytes
        dq 0                                            ; p_paddr               = 8 bytes
        dq _interpreter_name_.SIZE                      ; p_filesz              = 8 bytes
        dq _interpreter_name_.SIZE                      ; p_memsz               = 8 bytes
        dq 0                                            ; p_align               = 8 bytes
        
        dd 2                                            ; p_type PT_DYNAMIC     = 4 bytes
        dd 4                                            ; p_flags RO            = 4 bytes
        dq OFFSETOF DYNAMIC                             ; p_offset              = 8 bytes
        dq DYNAMIC                                      ; p_vaddr               = 8 bytes
        dq 0                                            ; p_paddr               = 8 bytes
        dq DYNAMIC.SIZE                                 ; p_filesz              = 8 bytes
        dq DYNAMIC.SIZE                                 ; p_memsz               = 8 bytes
        dq 0                                            ; p_align               = 8 bytes

        dd 1                                            ; p_type PT_LOAD        = 4 bytes
        dd 7                                            ; p_flags RWX           = 4 bytes
        dq OFFSETOF _start                              ; p_offset              = 8 bytes
        dq _start                                       ; p_vaddr               = 8 bytes
        dq 0                                            ; p_paddr               = 8 bytes
        dq FILE.SIZE                                    ; p_filesz              = 8 bytes
        dq FILE.SIZE                                    ; p_memsz               = 8 bytes
        dq 0x100000                                     ; p_align               = 8 bytes

                                                        ; total 56 * 3          = 168 bytes

        .SIZE = $-Elf64_Phdr

SYS_exit        = 60

_start:
        call    [print]

        mov     eax, SYS_exit
        xor     edi, edi
        syscall

        print   dq ?
                                                        ; total 6+5+2+2+8       = 23 bytes

DYNAMIC:
        .DT_NULL         =  0 ; ignored = Marks the end of the dynamic array
        .DT_NEEDED       =  1 ; d_val = The string table offset of the name of a needed library.
        .DT_STRTAB       =  5 ; d_ptr = Address of the dynamic string table.
        .DT_SYMTAB       =  6 ; d_ptr = Address of the dynamic symbol table.
        .DT_RELA         =  7 ; d_ptr = Address of a relocation table with Elf64_Rela entries.
        .DT_RELASZ       =  8 ; d_val = Total size, in bytes, of the DT_RELA relocation table.
        .DT_RELAENT      =  9 ; d_val = Size, in bytes, of each DT_RELA relocation entry.
        .DT_STRSZ        = 10 ; d_val = Total size, in bytes, of the string table.
        .DT_SYMENT       = 11 ; d_val = Size, in bytes, of each symbol table entry.

        dq DYNAMIC.DT_NEEDED,  STRTAB.libshared
        dq DYNAMIC.DT_STRTAB,  STRTAB
        dq DYNAMIC.DT_SYMTAB,  SYMTAB
        dq DYNAMIC.DT_STRSZ,   STRTAB.SIZE
        dq DYNAMIC.DT_SYMENT,  24
        dq DYNAMIC.DT_RELA,    RELA
        dq DYNAMIC.DT_RELASZ,  RELA.SIZE
        dq DYNAMIC.DT_RELAENT, 24
        dq DYNAMIC.DT_NULL,    0

        .SIZE = $-DYNAMIC                               ; total 16 * 9          = 144 bytes

SYMTAB:
        .STB_GLOBAL     = 1
        .STT_FUNC       = 2
        .STN_UNDEF      = ($-SYMTAB)/24 
        dd 0                                            ; u32 st_name   = 0 
        db 0                                            ; u8  st_info   = 0
        db 0                                            ; u8  st_other  = 0
        dw 0                                            ; u16 st_shndx  = SHN_UNDEF
        dq 0                                            ; u64 st_value  = 0
        dq 0                                            ; u64 st_size   = 0

        .print=($-SYMTAB)/24 
        dd STRTAB.print                                 ; u32 st_name
        db SYMTAB.STB_GLOBAL shl 4 + SYMTAB.STT_FUNC    ; u8  st_info
        db 0                                            ; u8  st_other
        dw 0                                            ; u16 st_shndx
        dq 0                                            ; u64 st_value
        dq 0                                            ; u64 st_size
  
        .SIZE=$-SYMTAB                                  ; total 24 * 2          = 48 bytes

RELA:
        .R_AMD64_64 = 1 
        dq print                                        ; reloc addess 
        dq SYMTAB.print shl 32 + RELA.R_AMD64_64        ; symtab_index shl 32 + type 
        dq 0                                            ; addend 

        .SIZE=$-RELA                                    ; total                 = 24 bytes

STRTAB:
        .null=$-STRTAB 
        db 0 
        
        .libshared=$-STRTAB 
        db "libshared.so", 0 
        
        .print=$-STRTAB 
        db "print", 0 
        
        .SIZE=$-STRTAB                                  ; total                 =  20 bytes

_interpreter_name_:
        db "/lib/ld-linux-x86-64.so.2", 0
        .SIZE = $-_interpreter_name_                    ; total                 = 26 bytes

FILE.SIZE=$-$$                                          ; file size in total is 517 bytes
    


Last edited by reyuki on 26 Jan 2025, 15:19; edited 1 time in total
Post 26 Jan 2025, 13:21
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 20513
Location: In your JS exploiting you and your system
revolution 26 Jan 2025, 13:36
Try putting align before DYNAMIC
Code:
align 8
DYNAMIC:    
Post 26 Jan 2025, 13:36
View user's profile Send private message Visit poster's website Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4153
Location: vpcmpistri
bitRAKE 26 Jan 2025, 14:18
reyuki wrote:
how much bytes/padding should I add to meet alignment requirement in the _start section?
The ELF specification mandates that the p_vaddr (virtual address) and p_offset (file offset) of a PT_LOAD segment must be congruent modulo p_align.

If you want the PT_LOAD to have a 1MB alignment (0x100000) then _start would need to be padded to a file offset (p_offset) that is modulo 1MB. Note: typically, code is page aligned (0x1000).
Code:
PT_LOAD.Align = 0x100000
; insure that file offset is aligned
    rb (PT_LOAD.Align - (($-$$) mod PT_LOAD.Align)) mod PT_LOAD.Align
_start:
    ...    
... the virtual address (p_vaddr) would also be aligned because of your BASE selection, 0x400000.

You can confirm this is the case within the example as well.

Edit: that seems excessive when the values from the example can be used, and then no alignment of _start is needed:
Code:
  dq  0x0                     ; u64 p_offset
  dq  $$                      ; u64 p_vaddr (i.e. BASE)
  dq  0                       ; u64 p_paddr    
... and maybe add an assert to insure the BASE address is modulo alignment.
Post 26 Jan 2025, 14:18
View user's profile Send private message Visit poster's website Reply with quote
reyuki



Joined: 24 Jan 2025
Posts: 12
reyuki 26 Jan 2025, 15:18
Ah, yes I forgot to re-adjust the p_offset and p_vaddr of PT_LOAD (previously I only load the the _start segment instead of a whole file content but then STRTAB, SYMTAB and RELA need to be loaded to the memory as well, so I choose to load the whole content XD)

and as you said, no need alignment if the whole content is loaded into the memory, but wouldn't this make the flags on PT_INTERP and PT_DYNAMIC become RWX because they are overwritten? I guess it doesn't really matter or important though, the segfault is gone anyway.

thank you for the help revolution, bitRAKE ^^


Last edited by reyuki on 26 Jan 2025, 15:33; edited 1 time in total
Post 26 Jan 2025, 15:18
View user's profile Send private message Send e-mail Reply with quote
reyuki



Joined: 24 Jan 2025
Posts: 12
reyuki 26 Jan 2025, 15:31
by the way, the readelf's output show some error:
Code:
'RELA' relocation section at offset 0x4001bf contains 24 bytes:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
00000000004000f7  0000000100000001 R_X86_64_64           readelf: Error:  bad symbol index: 00000001 in reloc
    


what do I need to fix the error? If I read the readelf's source code it seems have todo with symtab? I guess this is normal if I create the executable from scratch, I assume because the libc example have same issue


Last edited by reyuki on 26 Jan 2025, 23:43; edited 1 time in total
Post 26 Jan 2025, 15:31
View user's profile Send private message Send e-mail Reply with quote
bitRAKE



Joined: 21 Jul 2003
Posts: 4153
Location: vpcmpistri
bitRAKE 26 Jan 2025, 15:52
reyuki wrote:
but wouldn't this make the flags on PT_INTERP and PT_DYNAMIC become RWX because they are overwritten?
Page permission are probably set after the loader does its business - I'm guessing.

_________________
¯\(°_o)/¯ “languages are not safe - uses can be” Bjarne Stroustrup
Post 26 Jan 2025, 15:52
View user's profile Send private message Visit poster's website Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.