flat assembler
Message board for the users of flat assembler.

flat assembler > Linux > Improved ELF executable support

Goto page 1, 2  Next
Author
Thread Post new topic Reply to topic
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6969
Location: Kraków, Poland
With the new 1.69.05 release I introduced two small extensions to ELF executable formatter, in order to make a little bit more usable and analogous to the PE formatter fasm offers for Windows.

The first addition is that you can now specify a branding value to mark executable as targeted for specific system. To mark ELF as Linux executable, use directive like:
Code:
format ELF executable 3    

Linux actually ignores this brand value, but it may be important for instance on BSD systems, which are able to emulate Linux syscalls as long as the executable is marked properly.

The second addition is the ability to create some special segments. Three new keywords are introduced for this purpose: "interpreter", "dynamic" and "note".

By creating the "interpreter" and "dynamic" segments and filling them with appropriate data, you can now make an "import section" for your ELF executable and make it dynamically linked.
Here's the example program that imports the addresses for libc function into data fields, which are then used to indirectly call those function (it's quite like the importing mechanism used by default fasm's macros for PE format):
Code:
format ELF executable
entry start

include 'elf.inc'

segment interpreter readable

 db '/lib/ld-linux.so.2',0

segment dynamic readable

  dd DT_NEEDED,_libc-strtab
  dd DT_STRTAB,strtab
  dd DT_STRSZ,strsz
  dd DT_SYMTAB,symtab
  dd DT_SYMENT,sizeof.Elf32_Sym
  dd DT_REL,rel
  dd DT_RELSZ,relsz
  dd DT_RELENT,sizeof.Elf32_Rel
  dd DT_HASH,hash
  dd DT_NULL,0

segment readable writeable

  symtab:
   Elf32_Sym                                            ; 0 - NULL
   Elf32_Sym _printf-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 1 - printf
   Elf32_Sym _exit-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0   ; 2 - exit

  strtab:
  _null db 0
  _libc db 'libc.so.6',0
  _printf db 'printf',0
  _exit db 'exit',0
  strsz = $-strtab

  rel:
   Elf32_Rel printf,1,R_386_32
   Elf32_Rel exit,2,R_386_32
  relsz = $-rel

  hash:
   dd 1,3       ; size of bucket and size of chain
   dd 0         ; fake bucket, just one hash value
   times 3 dd % ; chain for all symbol table entries

segment readable executable

start:

        push    msg
        call    [printf]

        call    [exit]

segment readable writeable

printf dd 0
exit dd 0

msg db 'Hello world!',0xA,0    

Second example does the same, but this time it creates the "jump gates" for the imported functions - the similar mechanism that is used by some linkers:
Code:
format ELF executable
entry start

include 'elf.inc'

segment interpreter readable

 db '/lib/ld-linux.so.2',0

segment dynamic readable

  dd DT_NEEDED,_libc-strtab
  dd DT_STRTAB,strtab
  dd DT_STRSZ,strsz
  dd DT_SYMTAB,symtab
  dd DT_SYMENT,sizeof.Elf32_Sym
  dd DT_REL,rel
  dd DT_RELSZ,relsz
  dd DT_RELENT,sizeof.Elf32_Rel
  dd DT_HASH,hash
  dd DT_NULL,0

segment readable writeable

  symtab:
   Elf32_Sym                                            ; 0 - NULL
   Elf32_Sym _printf-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 1 - printf
   Elf32_Sym _exit-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0   ; 2 - exit

  strtab:
  _null db 0
  _libc db 'libc.so.6',0
  _printf db 'printf',0
  _exit db 'exit',0
  strsz = $-strtab

  rel:
   Elf32_Rel _imp_printf,1,R_386_PC32
   Elf32_Rel _imp_exit,2,R_386_PC32
  relsz = $-rel

  hash:
   dd 1,3       ; size of bucket and size of chain
   dd 0         ; fake bucket, just one hash value
   times 3 dd % ; chain for all symbol table entries

segment readable writeable executable

start:

        push    msg
        call    printf

        call    exit

printf:
        jmp     near _imp_printf
        label   _imp_printf at $-4

exit:
        jmp     near _imp_exit
        label   _imp_exit at $-4

segment readable writeable

msg db 'Hello world!',0xA,0    
Note that code segment had to be made writeable for this to work.

Of course this all can be made automatically, just some nice and easy to use macros have to be designed, and we can start to make some standard headers for Linux programming in fasm.

PS. You need ELF.INC to assemble the above samples, you can find the complete set of sources in the attachment.

PPS. My credits and thanks go to nocona, who made some dynamically linked ELF executables from scratch, and whose research became an inspiration to go into this direction.


Description: Dynamic ELF executable examples
Download
Filename: dynelfexe.zip
Filesize: 2.03 KB
Downloaded: 168 Time(s)



Last edited by Tomasz Grysztar on 14 Sep 2009, 20:48; edited 1 time in total
Post 14 Sep 2009, 20:07
View user's profile Send private message Visit poster's website Reply with quote
Fanael



Joined: 03 Jul 2009
Posts: 168
"nocode"? Not "nocona"?
Post 14 Sep 2009, 20:46
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6969
Location: Kraków, Poland
Sorry, a typo. Smile
Post 14 Sep 2009, 20:49
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6969
Location: Kraków, Poland
64-bit example (uses the same definitions as the ones above):
Code:
format ELF64 executable
entry start

include 'elf.inc'

segment interpreter readable

 db '/lib64/ld-linux-x86-64.so.2',0

segment dynamic readable

  dq DT_NEEDED,_libc-strtab
  dq DT_STRTAB,strtab
  dq DT_STRSZ,strsz
  dq DT_SYMTAB,symtab
  dq DT_SYMENT,sizeof.Elf64_Sym
  dq DT_RELA,rela
  dq DT_RELASZ,relasz
  dq DT_RELAENT,sizeof.Elf64_Rela
  dq DT_HASH,hash
  dq DT_NULL,0

segment readable writeable

  symtab:
   Elf64_Sym                                            ; 0 - NULL
   Elf64_Sym _printf-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 1 - printf
   Elf64_Sym _exit-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0   ; 2 - exit

  strtab:
  _null db 0
  _libc db 'libc.so.6',0
  _printf db 'printf',0
  _exit db 'exit',0
  strsz = $-strtab

  rela:
   Elf64_Rela printf,1,R_X86_64_64
   Elf64_Rela exit,2,R_X86_64_64
  relasz = $-rela

  hash:
   dd 1,3       ; size of bucket and size of chain
   dd 0         ; fake bucket, just one hash value
   times 3 dd % ; chain for all symbol table entries

segment readable executable

start:

        lea     rdi,[msg]
        xor     eax,eax
        call    [printf]

        call    [exit]

segment readable writeable

printf dq 0
exit dq 0

msg db 'Hello world!',0xA,0    
Post 15 Sep 2009, 08:27
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6969
Location: Kraków, Poland
32-bit example simplified with macros:
Code:
format ELF executable
entry start

include 'import32.inc'
include 'proc32.inc'

interpreter '/lib/ld-linux.so.2'
needed 'libc.so.6'
import printf,exit

segment readable executable

start:
        cinvoke printf,msg
        cinvoke exit

segment readable writeable

msg db 'Hello world!',0xA,0    

Note that proc32.inc has simply been copied from the Win32 includes (this cannot be done with proc64.inc, because calling convention in 64-bit Linux is way different from the one from Win64).
The complete set of sources in attachment.


Description: Simple dynamically linked ELF executable examples
Download
Filename: dynelfexe.zip
Filesize: 4.43 KB
Downloaded: 172 Time(s)

Post 15 Sep 2009, 17:38
View user's profile Send private message Visit poster's website Reply with quote
nocona



Joined: 04 Aug 2007
Posts: 35
thanx Smile.
Post 27 Sep 2009, 16:35
View user's profile Send private message Reply with quote
kohlrak



Joined: 21 Jul 2006
Posts: 1421
Location: Uncle Sam's Pad
Perhaps some of the code i posted in the macros section could be used for proc64. I've pretty much left linux development at this point, but i'm sure some one could clean them up a little and make them a little more stable and usable.

Anyway, thanks thomasz, lack of linux support has been one of the detering factors of me promoting people to attempt asm on linux, now that everything's getting cleaner, soon i may be able to convince people to give it a shot.
Post 08 Oct 2009, 03:44
View user's profile Send private message Visit poster's website AIM Address Yahoo Messenger MSN Messenger Reply with quote
gunblade



Joined: 19 Feb 2004
Posts: 209
Just wanted to add my thanks for adding these features..

I didnt like having to use ld for linking the ELF objects into an executable, especially when developing on machines without a gcc/binutils toolchain (the HD's too small Laughing ).
Post 15 Oct 2009, 20:18
View user's profile Send private message Reply with quote
Rahsennor



Joined: 07 Jul 2007
Posts: 54
I'd almost given up on fasm Linux coding. Thank you! Very Happy
Uh-huh, I should visit the forum more often...
Post 09 Nov 2009, 02:12
View user's profile Send private message Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1046
Location: Michigan, USA
A bit late, but hopefully someone can help me out here. How would I define multiple import libraries?
Example:
interpreter "/lib64/ld-linux-x86-64.so.2", "/lib64/libgtk-x11-2.0.so.0"

Using this gives me an error.
Post 01 Feb 2010, 20:57
View user's profile Send private message Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4634
Location: Argentina
madmatt, interpreter is to set the dynamic linker. You have to use "needed" for that.
Post 01 Feb 2010, 21:57
View user's profile Send private message Reply with quote
madmatt



Joined: 07 Oct 2003
Posts: 1046
Location: Michigan, USA
LocoDelAssembly wrote:
madmatt, interpreter is to set the dynamic linker. You have to use "needed" for that.


All right, thanks. Until I get a decent linux programming book, I'll be shooting in the dark for a while. Smile

_________________
Gimme a sledge hammer! I'LL FIX IT!
Post 02 Feb 2010, 00:59
View user's profile Send private message Reply with quote
d.j.peters



Joined: 11 Aug 2004
Posts: 7
Location: Germany
hello,

first sorry about my bad english

i use FASM as an backend for an neuron simulator and i create executable
binarys for linux and windows (currently under win32)

my tests works great so far on lin x86 and win 32

but now i need dynamic libs on linux too

the 'hand coded' libc.so.6 example works great for my needs
but i need help from you to expand the example for using two *.so libs.

the sencond lib is named "libother.so" and the exported function is "other"

the first lib are "libc.so.6" the second is "libother.so"
so i will call "printf", "other", and "exit"

how must i cahnge the working example to use dynamical 2 libs ?

thank you so mutch

DJ

see for "<--"
Code:
format ELF executable
entry start

include 'elf.inc'

segment interpreter readable

 db '/lib/ld-linux.so.2',0

segment dynamic readable
  ; how to expand this block for two dynamic libs ?
  ; the second lib are ' libother.so'  ???
  dd DT_NEEDED,_libc-strtab
  dd DT_STRTAB,strtab
  dd DT_STRSZ, strsz
  dd DT_SYMTAB,symtab
  dd DT_SYMENT,sizeof.Elf32_Sym
  dd DT_REL,   rel
  dd DT_RELSZ, relsz
  dd DT_RELENT,sizeof.Elf32_Rel
  dd DT_HASH,  hash
  dd DT_NULL,  0

segment readable writeable

  symtab:
   Elf32_Sym                                            ; 0 - NULL
   Elf32_Sym _printf-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 1 - printf
   Elf32_Sym _exit-strtab  ,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 2 - exit
  ; is this the right place ?
   Elf32_Sym _other-strtab ,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 3 - other <--

  strtab:
  _null    db 0
  _libc    db 'libc.so.6',0

  _printf  db 'printf',0
  _exit    db 'exit',0

  _libother db 'libother.so',0 ; <--
  _other    db 'other',0       ; <-- 

  strsz = $-strtab

  rel:
   Elf32_Rel printf,1,R_386_32
   Elf32_Rel exit  ,2,R_386_32
   Elf32_Rel other ,3,R_386_32 ; <-- 
  relsz = $-rel

  hash:
   dd 1,3       ; must i change 3 to  4  ? <---
   dd 0         ; fake bucket, just one hash value
   times 3 dd % ; mus i change this from 3 to 4  ? <-- 

segment readable executable

start:

        push    msg
        call    [printf]
        add     esp,4    ; <-- 
        call    [other]   ; <-- here i call a function named 'other' from lib 'libother.so'
        call    [exit]

segment readable writeable

printf dd 0
exit   dd 0
other  dd 0 ; <--

msg db 'Hello world!',0xA,0
     

_________________
(sorry about my bad English)
Post 07 Aug 2010, 06:12
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4634
Location: Argentina
d.j.peters, using the simplified macros from here(it is in this thread some posts above), doesn't this work?
Code:
format ELF executable
entry start

include 'import32.inc'
include 'proc32.inc'

interpreter '/lib/ld-linux.so.2'
needed 'libc.so.6', 'libother.so'
import printf, exit, other

segment readable executable

start:
        cinvoke printf, msg
        cinvoke other
        cinvoke exit

segment readable writeable

msg db 'Hello world!',0xA,0    
If it does you could study how proc32.inc does it (or just use it if it is not a problem).
Post 07 Aug 2010, 16:43
View user's profile Send private message Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4634
Location: Argentina
Code:
   dd 1,3       ; must i change 3 to  4  ? <---     

BTW, Tomasz I have the same question, this is also done this way in your simplified macros but isn't 3 accounting for nchain? In that case it should be always equal to the number of symbols ^^
Post 07 Aug 2010, 16:58
View user's profile Send private message Reply with quote
Tomasz Grysztar
Assembly Artist


Joined: 16 Jun 2003
Posts: 6969
Location: Kraków, Poland
LocoDelAssembly wrote:
BTW, Tomasz I have the same question, this is also done this way in your simplified macros but isn't 3 accounting for nchain? In that case it should be always equal to the number of symbols ^^
Yes, this is a mistake in the macro.
I will correct it before including those examples in the official package (as I plan to do so).
Post 07 Aug 2010, 21:27
View user's profile Send private message Visit poster's website Reply with quote
d.j.peters



Joined: 11 Aug 2004
Posts: 7
Location: Germany
Hello Tomasz,
first thanks for your really fast responce.

Macros are not a part of my neuron "simple" code generator and will never be.

The neuronal network pushes only values or pointers on the stack
and call's [addresses] imported from *.so libs (and *.dll's).

The windows part works so far
i saw an hard coded example (without macros) with more than one *.dll.

please give only one hard coded linux sample with two shared libs.

With this i can expand it for any number of libs and functions.

Or exist a way i can see the result of the FASM preprocessor after the macro expansion ?

for you it is a work of 5 minutes or may be lesser
but for me i must learn how the macros works first
and i need hours or days to get a hard coded result from it. Sad

thank you really mutch for your help.

DJ

_________________
(sorry about my bad English)
Post 08 Aug 2010, 09:35
View user's profile Send private message Visit poster's website Reply with quote
LocoDelAssembly
Your code has a bug


Joined: 06 May 2005
Posts: 4634
Location: Argentina
In the TOOLS directory you should find PREPSRC.ASM in LIBC subdirectory. Compile, link and use it (see readme file for usage instructions).

In case you have a problem, here is a fragment of the output:
Code:
;interpreter '/lib/ld-linux.so.2'

segment interpreter readable
db '/lib/ld-linux.so.2',0

;needed 'libc.so.6','libother.so'

; str?0
;match needed,{define needed@dynamic needed,str?0:'libc.so.6'}
;match,{define needed@dynamic str?0:'libc.so.6'}
;define needed@dynamic str?0:'libc.so.6'


; str?1
;match needed,str?0:'libc.so.6'{define needed@dynamic needed,str?1:'libother.so'}
;define needed@dynamic str?0:'libc.so.6',str?1:'libother.so'
;match,str?0:'libc.so.6',str?1:'libother.so'{define needed@dynamic str?1:'libother.so'}

;import printf,exit,other







; strtab?2 strsz?3 symtab?4 rel?5 relsz?6 hash?7
segment dynamic readable
;match needed,str?0:'libc.so.6',str?1:'libother.so'
;{irp item,needed \{ match str:library,item \\{ dd DT_NEEDED,str-strtab?2 \\} \}}
;irp item,str?0:'libc.so.6',str?1:'libother.so'{match str:library,item \{ dd DT_NEEDED,str-strtab?2 \}}
;match str:library,str?0:'libc.so.6'{dd DT_NEEDED,str-strtab?2}
dd DT_NEEDED,str?0-strtab?2
;match str:library,str?1:'libother.so'{dd DT_NEEDED,str-strtab?2}
dd DT_NEEDED,str?1-strtab?2
dd DT_STRTAB,strtab?2
dd DT_STRSZ,strsz?3
dd DT_SYMTAB,symtab?4
dd DT_SYMENT,sizeof.Elf32_Sym
dd DT_REL,rel?5
dd DT_RELSZ,relsz?6
dd DT_RELENT,sizeof.Elf32_Rel
dd DT_HASH,hash?7
dd DT_NULL,0
segment readable writeable
symtab?4:;Elf32_Sym

dd+0
dd+0
dd+0
db(+0)shl 4+(+0)
db+0
dw+0



; fstr?8
;Elf32_Sym fstr?8-strtab?2,0,0,STB_GLOBAL,STT_FUNC,0,0

dd fstr?8-strtab?2+0
dd 0+0
dd 0+0
db(STB_GLOBAL+0)shl 4+(STT_FUNC+0)
db 0+0
dw 0+0



; fstr?9
;Elf32_Sym fstr?9-strtab?2,0,0,STB_GLOBAL,STT_FUNC,0,0

dd fstr?9-strtab?2+0
dd 0+0
dd 0+0
db(STB_GLOBAL+0)shl 4+(STT_FUNC+0)
db 0+0
dw 0+0



; fstr?A
;Elf32_Sym fstr?A-strtab?2,0,0,STB_GLOBAL,STT_FUNC,0,0

dd fstr?A-strtab?2+0
dd 0+0
dd 0+0
db(STB_GLOBAL+0)shl 4+(STT_FUNC+0)
db 0+0
dw 0+0



rel?5:
; counter?B
counter?B=1


;Elf32_Rel printf,counter?B,R_386_32

dd printf+0
dd(counter?B+0)shl 8+(R_386_32+0)

counter?B=counter?B+1


;Elf32_Rel exit,counter?B,R_386_32

dd exit+0
dd(counter?B+0)shl 8+(R_386_32+0)

counter?B=counter?B+1


;Elf32_Rel other,counter?B,R_386_32

dd other+0
dd(counter?B+0)shl 8+(R_386_32+0)

counter?B=counter?B+1


relsz?6=$-rel?5
hash?7:
dd 1,3
dd 0
repeat counter?B
if %=counter?B
dd 0
else
dd %
end if
end repeat
strtab?2 db 0


fstr?8 db 'printf',0


fstr?9 db 'exit',0


fstr?A db 'other',0


;match needed,str?0:'libc.so.6',str?1:'libother.so'
;{irp item,needed \{ match str:library,item \\{ str db library,0 \\} \}}
;irp item,str?0:'libc.so.6',str?1:'libother.so'{match str:library,item \{ str db library,0 \}}
;match str:library,str?0:'libc.so.6'{str db library,0}
str?0 db 'libc.so.6',0
;match str:library,str?1:'libother.so'{str db library,0}
str?1 db 'libother.so',0
strsz?3=$-strtab?2


printf dd 0


exit dd 0


other dd 0


segment readable executable

start:
;cinvoke printf,msg


size@ccall=0
if~msg eq


pushd msg
size@ccall=size@ccall+4


end if
call[printf]    
(Remember to fix the aforementioned error)
Post 08 Aug 2010, 18:05
View user's profile Send private message Reply with quote
d.j.peters



Joined: 11 Aug 2004
Posts: 7
Location: Germany
Hello LocoDelAssembly,

i will do an deeper lock to "PREPSRC.ASM" thank you for the tip.

i tryed your example and it works so far

but the problem are the same

if my neuronal network will add more functions

e.g.

other2
other3

i don't understand the rule behind the numbering

you use 8,9,A for the functions

now if i use B for function "other2" and C for function "other3"

i can see below B is in use too

what is it for a numbering is it hex and i can use any number like

C0001
C0002

thank you ?

For an auto gnerated *.asm file the example Tomasz Grysztar
posted would be the right thing for my needs.

It use only 'names' and labels so it is easy for the neuronal network to create a list with diffrent names and labels.

But i don't know how to use a second *.so lib with this short example.

Do you know how i must change it for a second *.so lib entry ?

is a secend DT_NEEDED item the key ?

dd DT_NEEDED,_other-strtab
...
...

DJ

Code:
format ELF executable
entry start

include 'elf.inc'

segment interpreter readable

 db '/lib/ld-linux.so.2',0

segment dynamic readable

  dd DT_NEEDED,_libc-strtab
  dd DT_STRTAB,strtab
  dd DT_STRSZ,strsz
  dd DT_SYMTAB,symtab
  dd DT_SYMENT,sizeof.Elf32_Sym
  dd DT_REL,rel
  dd DT_RELSZ,relsz
  dd DT_RELENT,sizeof.Elf32_Rel
  dd DT_HASH,hash
  dd DT_NULL,0

segment readable writeable

  symtab:
   Elf32_Sym                                            ; 0 - NULL
   Elf32_Sym _printf-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0 ; 1 - printf
   Elf32_Sym _exit-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0   ; 2 - exit

  strtab:
  _null db 0
  _libc db 'libc.so.6',0
  _printf db 'printf',0
  _exit db 'exit',0
  strsz = $-strtab

  rel:
   Elf32_Rel printf,1,R_386_32
   Elf32_Rel exit,2,R_386_32
  relsz = $-rel

  hash:
   dd 1,3       ; size of bucket and size of chain
   dd 0         ; fake bucket, just one hash value
   times 3 dd % ; chain for all symbol table entries

segment readable executable

start:
        push    msg
        call    [printf]
        call    [exit]

segment readable writeable

printf dd 0
exit dd 0

msg db 'Hello world!',0xA,0    

_________________
(sorry about my bad English)


Last edited by d.j.peters on 06 Jan 2012, 12:35; edited 1 time in total
Post 09 Aug 2010, 09:57
View user's profile Send private message Visit poster's website Reply with quote
d.j.peters



Joined: 11 Aug 2004
Posts: 7
Location: Germany
Hello LocoDelAssembly, i'm again Laughing

after i tried "prepsrc" now i know what are the source of the numbers are.

looks like i must use macros to support linux.

first time that Windows PE files are simpler to generate without macros as ELF files.

DJ

_________________
(sorry about my bad English)
Post 09 Aug 2010, 10:42
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:  
Goto page 1, 2  Next

< 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-2018, Tomasz Grysztar.

Powered by rwasa.