News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

JWASM/HJWASM Linux 64 support

Started by johnsa, February 09, 2016, 03:42:00 AM

Previous topic - Next topic

johnsa

Hi all,

I've already logged two known issues with HJWasm's Linux support (pulled straight over from Jwasm really.. Shared objects and DWARF debugging info).

In an effort to better understand the implications and changes required I'm trying to use (Jwasm or hjwasm) on Linux.. Not my platform of choice and I have relatively little experience on it.
If anyone has any more extensive experience using Linux 64bit with HJWasm/JWasm perhaps they can answer some questions for me and provide some insight:

The ABI for Linux64 is:
System V X86_642   
return: rax, rdx   
arguments: rdi, rsi, rdx, rcx, r8, r9   stack (right to left)
16-byte at call
stackbase: rbp

It doesn't appear like JWASM/HJWasm support this calling convention apart from using SYSCALL_

I am assuming that for a valid linux 64 elf64 object file the entry point function should be declared as c calling convention? (At least that's what I've seen around) and the other functions should be syscall_ ?
If this is the case how much success have people had using the higher level directives under linux, IE:

PROC, PROTO and INVOKE

For example:



MyProc PROTO SYSCALL_ arg1:QWORD, arg2:DWORD

MyProc PROC SYSCALL_ arg1:QWORD, arg2:DWORD
ret
MyProc ENDP

invoke MyProc, rax, ebx   ; this works..
invoke MyProc, ADDR aVariable, ebx   ; this doesn't due to the assembler generating a push offset aVariable instead of using LEA.



Any thoughts?

GoneFishing

Hi JOHNSA,
I've made a limited set of tests with JWASM / JWLINK on Linux .

JWASM manual tells that
QuoteFor -elf64, there is no FASTCALL support implemented yet.
and the calling convention is set to stdcall by default  .

ABI:
      http://www.x86-64.org/documentation_folder/abi-0.99.pdf
     page 18, 3.2.3 Parameter Passing
     page 21, Figure 3.4: Register Usage

IIRC I've been able to use only PROCs without parameters .
I had to write my own macro FCALLTEST to invoke functions from C lib .

johnsa

This is all a bit of a sorry state of affairs  :(
I think we need to update HJWASM to generate the write code for invoke with -elf64 and the right prologue/epilogue and stack usage...

GoneFishing

#3
I think I'm the first on the forum who has successfully built debug version of JWLINK with TRMEM defined .
I had to compile TRMEM  from OW 1.9 as static lib and make all needed adjustments in makefile.
Binaries attached + simple JWLINK test suit
The main mystery is why JWLINK is crashing with SEGFAULT when linking 64 bit app to custom static lib .

A couple of goodies for dessert:
"Open Watcom" free compiler looking for AMD64 help
An article's 10 years old already and still contains up-to-date info  ;) :
QuoteKB: We'd need someone familiar with shared libraries to flesh out the shared library support in the compiler for Linux. Right now the compiler works great on Linux, but all the code has to be static linked and it uses the Open Watcom runtime library. We need the ability to generate ELF PIC compatible code, as well as link against existing SO libraries which would then allow Open Watcom to use the system GLIBC libraries. Unfortunately this also requires internal work on the compiler to make it happen (the ability to generate PIC code).

The second noteworthy link is Open Watcom Linux Port Compiler / Linker Software Requirements Specification
[EDIT] Linux Port - the most recent (20150504) version of the same document in HTML and another good  article : Porting Guide

[EDIT] jwltest.zip is updated : TestGCC example added, see readme.txt

Aeomasdkob

I want to take this knowledge to read more.

jj2007

Quote from: Aeomasdkob on June 02, 2016, 07:04:17 PM
I want to take this knowledge to read more.

Great idea. And tell your "father" that his code sucks (is there something like a "suckbot"?) :bgrin:

GoneFishing

Quote from: jj2007 on June 02, 2016, 07:06:40 PM
...
is there something like a "suckbot"?

coming soon I think : https://www.youtube.com/watch?v=W0_DPi0PmF0  :biggrin:

GoneFishing

#7
Hi all,
Usually every year I do one little step forward in "taking this knowledge" and here I write more  :biggrin:

It was very simple and also creative idea to replace DBG_OLD(0x02) flag with DBG_ALWAYS(0x00) to enable verbose logging .
Adding a couple of checkpoints   did the rest:

../JWLINK/OWLinuxD/jwlink.  form elf file  test.o lib libtest name testD
ResetPermData() enter
SearchPath: jwlink.lnk not foune
AddFile(): new file >test.o<
Adding Object library name libtest.lib
ProcObjFiles enter
JWlink Version 1.9beta 13
Portions Copyright (c) 1985-2002 Sybase, Inc. All Rights Reserved.
Source code is available under the Sybase Open Watcom Public License.
loading object files
DoPass1(test.o) enter
ObjPass1(): enter, file = test.o, module = test.o
objorl.ORLPass1(): CurrMod=test.o
objorl.CheckFlags(): machtype=000c
Set64BitMode() enter
---- ORLFileScan entry
------ ORLFileScan case ELF
-------- ElfFileScan entry
---------- ElfFileScan inside !desired case
------------ ElfFileScan inside for loop = 0 elf_file_hnd->elf_sec_hnd[0] = 139985256 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .text 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .text 
objorl.DeclareSegment(): .text
objorl.DeclareSegment(): .text iscdat=0
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 1 elf_file_hnd->elf_sec_hnd[1] = 139985360 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .data 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .data 
objorl.DeclareSegment(): .data
objorl.DeclareSegment(): .data iscdat=0
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 2 elf_file_hnd->elf_sec_hnd[2] = 139985464 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 5 ,elf_sec_hnd->name : .shstrtab 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 3 elf_file_hnd->elf_sec_hnd[3] = 139985568 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 3 ,elf_sec_hnd->name : .symtab 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 4 elf_file_hnd->elf_sec_hnd[4] = 139985672 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 5 ,elf_sec_hnd->name : .strtab 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 5 elf_file_hnd->elf_sec_hnd[5] = 139985776 
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 7 ,elf_sec_hnd->name : .rela.text 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
-------- ElfFileScan out of for loop
-------- ElfFileScan exit
-- objorl checkpoint 1
-- limit  = 0
-- IterateNodelist checkpoint start
objorl: AllocSeg() - .text iscode=1 isuninit=0 isreadonly=0 isidata=0 iscdat=0 snode.info=00000000
objorl: AllocSeg() - classname=CODE
objpass1.AllocateSegment() enter, calling DoAllocateSegment()
DoAllocateSegment(seg=.text, cls="CODE") [iscode=0001 isreadonly=0000] enter
FindClass("CODE", is32=1, iscode=1, comb=1 )
FindClass("CODE"): new class
AddSegment(seg=.text, cls="CODE"): size=0000000a comb=0001 alignment=0004 code=0001 readonly=0000 is32=0001 class.flags=0011 [code=0001 readonly=0000 32bit=0001]
objpass1.MakeNewLeader(.text, cls="CODE", flags=0600) seg->isreadonly=0000 class->flags=0011
objpass1.MakeNewLeader(): leader.segflags=00000c00
objpass1.AllocateSegment(): info updated, new=00000600
objorl: AllocSeg() - .data iscode=0 isuninit=0 isreadonly=0 isidata=0 iscdat=0 snode.info=00000000
objorl: AllocSeg() - classname=DATA
objpass1.AllocateSegment() enter, calling DoAllocateSegment()
DoAllocateSegment(seg=.data, cls="DATA") [iscode=0000 isreadonly=0000] enter
FindClass("DATA", is32=1, iscode=0, comb=1 )
FindClass("DATA"): new class
AddSegment(seg=.data, cls="DATA"): size=00000000 comb=0001 alignment=0004 code=0000 readonly=0000 is32=0001 class.flags=0001 [code=0000 readonly=0000 32bit=0001]
objpass1.MakeNewLeader(.data, cls="DATA", flags=0400) seg->isreadonly=0000 class->flags=0001
objpass1.MakeNewLeader(): leader.segflags=00000c00
objpass1.AllocateSegment(): info updated, new=00000400
objpass1.SearchGroups(DGROUP): group not found
AllocGroup(DGROUP) enter, NumGroups=0
AllocGroup(DGROUP), after InitGroup flags=0c80
AllocGroup(DGROUP) exit, flags=0c80
AddToGroup(.data) enter, grp->segflags=0c80 seg->segflags=0c00
-- AddToGroup exit
-- IterateNodelist checkpoint end
-- objorl checkpoint 2
---- ORLFileScan entry
------ ORLFileScan case ELF
-------- ElfFileScan entry
---------- ElfFileScan inside !desired case
------------ ElfFileScan inside for loop = 0 elf_file_hnd->elf_sec_hnd[0] = 139985256 
-------------- ProcSymbols entry orl_sec_handle = 360
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .text 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 1 elf_file_hnd->elf_sec_hnd[1] = 139985360 
-------------- ProcSymbols entry orl_sec_handle = 464
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 2 ,elf_sec_hnd->name : .data 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 2 elf_file_hnd->elf_sec_hnd[2] = 139985464 
-------------- ProcSymbols entry orl_sec_handle = 568
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 5 ,elf_sec_hnd->name : .shstrtab 
------------ ElfFileScan return_func result = 2  ORL_OKAY = 2
------------ ElfFileScan inside for loop = 3 elf_file_hnd->elf_sec_hnd[3] = 139985568 
-------------- ProcSymbols entry orl_sec_handle = 672
---------------- ORLSectionGetType entry
------------------ ORLSectionGetType case ORL_ELF
-------------------- ElfSectionGetType elf_sec_hnd->type : 3 ,elf_sec_hnd->name : .symtab 
Segmentation fault



Crash happens in FOR loop on 4th iteration in file 
elfent.c
Quote
orl_return ELFENTRY ElfFileScan( elf_file_handle elf_file_hnd, char *desired, orl_sec_return_func return_func )
{
    orl_hash_data_struct *              data_struct;
    int                                 loop;
    orl_return                          error;
    printf( " -------- ElfFileScan entry \n");
    if( !desired ) {
      printf( " ---------- ElfFileScan inside !desired case \n");
        /* global request */
        for( loop = 0; loop < elf_file_hnd->num_sections; loop++ ) {
         printf( " ------------ ElfFileScan inside for loop = %d elf_file_hnd->elf_sec_hnd[%d] = %d  \n", loop,loop,elf_file_hnd->elf_sec_hnd[loop]);
            error = return_func( (orl_sec_handle) elf_file_hnd->elf_sec_hnd[loop] );
            printf( " ------------ ElfFileScan return_func result = %d  ORL_OKAY = %d \n", error, ORL_OKAY);
            if( error != ORL_OKAY ) return( error );
        }

return_func is  ProcSymbols :
objorl.c
Quote
static orl_return ProcSymbols( orl_sec_handle hdl )
/*********************************************/
{
   DEBUG((DBG_OLD, " -------------- ProcSymbols entry orl_sec_handle = %d", hdl));
    return( (SymbolJumpTable[ORLSecGetType( hdl )])( hdl ) );
   
}

SymbolJumpTable 
objorl.c
Quote
static orl_sec_return_func SymbolJumpTable[] = {
    NullFunc,           // ORL_SEC_TYPE_NONE
    NullFunc,           // ORL_SEC_TYPE_NO_BITS (bss)
    NullFunc,           // ORL_SEC_TYPE_PROG_BITS
    SymTable,           // ORL_SEC_TYPE_SYM_TABLE
    NullFunc,           // ORL_SEC_TYPE_DYN_SYM_TABLE
    NullFunc,           // ORL_SEC_TYPE_STR_TABLE
    NullFunc,           // ORL_SEC_TYPE_RELOCS
    NullFunc,           // ORL_SEC_TYPE_RELOCS_EXPAND
    NullFunc,           // ORL_SEC_TYPE_HASH
    NullFunc,           // ORL_SEC_TYPE_DYNAMIC
    NullFunc,           // ORL_SEC_TYPE_NOTE
    NullFunc            // ORL_SEC_TYPE_LINK_INFO
};

objorl.c
Quote
unsigned long ORLPass1( void )
/***********************************/
// do pass 1 for an object file handled by ORL.
{
    orl_file_handle     filehdl;

    /* jwlink: why setting dosseg? */
    //LinkState |= DOSSEG_FLAG;

    DEBUG((DBG_OLD, "objorl.ORLPass1(): CurrMod=%s", CurrMod->f.source->file->name ));
    PermStartMod( CurrMod );
    filehdl = InitFile();
    if( filehdl == NULL ) {
        LnkMsg( FTL+MSG_BAD_OBJECT, "s", CurrMod->f.source->file->name );
        CurrMod->f.source->file->flags |= INSTAT_IOERR;
        return( -1 );
    }
    if( CheckFlags( filehdl ) ) {
        if( LinkState & HAVE_PPC_CODE && !FmtData.toc_initialized ) {
            InitToc();
            FmtData.toc_initialized = 1;
        }
        if( LinkFlags & DWARF_DBI_FLAG ) {
            CurrMod->modinfo |= MOD_FLATTEN_DBI;
        }
        CurrMod->modinfo |= MOD_NEED_PASS_2;
        ORLFileScan( filehdl, NULL, ProcSegments );
        DEBUG((DBG_OLD, " -- objorl checkpoint 1"));
        IterateNodelist( SegNodes, AllocSeg, NULL );
        DEBUG((DBG_OLD, " -- objorl checkpoint 2 "));
        ORLFileScan( filehdl, NULL, ProcSymbols ); // <----------- crash here
        DEBUG((DBG_OLD, " -- objorl checkpoint 3 "));
        ScanImported();
        ORLFileScan( filehdl, NULL, ProcP1Specific );
        IterateNodelist( SegNodes, DefNosymComdats, NULL );
    }
    FiniFile( filehdl, CurrMod->f.source );
    return( ORLSeek( CurrMod->f.source, 0, SEEK_CUR ) );
}

JWLINK  verbose debug attached

EDIT1: deleted
EDIT2: Still trying to spot the  line of code  causing SEGFAULT ...   
EDIT3: Attach was updated, initial post was severely edited

GoneFishing

Latest news from debugging JWLINK battle field

SEGFAULT in my latest post was caused by DEBUG    output function called with argument name = NULL
I simply commented it out , see line731 ( in my heavily checkpointed version) :
objorl.c
Quote
static orl_return ProcSymbol( orl_symbol_handle symhdl )
/******************************************************/
{
    ... ...
    name = ORLSymbolGetName( symhdl );
    DEBUG((DBG_OLD," -------------------- ProcSymbol checkpoint 3 "));
   //DEBUG((DBG_OLD, "objorl.ProcSymbol(%s) enter", name )); !!! - NB - !!!   
    if( type & ORL_SYM_TYPE_FILE ) {
        if( !(CurrMod->modinfo & MOD_GOT_NAME) ) {
            CurrMod->modinfo |= MOD_GOT_NAME;
            _LnkFree( CurrMod->name );
            CurrMod->name = AddStringStringTable( &PermStrings, name );
        }
        return( ORL_OKAY );
    }

With my fingers crossed I ran it again in hope it'll work till the very happy end ... but  what I saw was a  new , now REAL bug .
See

...
ObjPass1(): exit, file = test.o, module = test.c
DoPass1(test.o) exit
ProcObjFiles exit
ResolveUndefined enter
searching libraries
LibFind( puts_, 0 )
ProcLibFile( puts_ ) enter, mod=test.c, calling SearchLib()
SearchLib( puts_ ): sym found in lib, module pos=0000005c
ProcLibFile( puts_ ): symbol found in puts.o, modinfo=00000300
ObjPass1(): enter, file = libtest.lib, module = puts.o
- ObjPass1(): checkpoint 1
- ObjPass1(): checkpoint 2
- ObjPass1(): checkpoint 3
- ObjPass1(): checkpoint 4
- ObjPass1(): checkpoint 5 CurrMod->name  = puts.o
objorl.ORLPass1(): CurrMod=puts.o
objorl.ORLPass1(): checkpoint 1
---- InitFile entry
---- InitFile exit
------ ORLFileInit entry
-------- ORLFileInit ORL_ELF case entry
---------- ORLFileInit checkpoint 1
---------- ORLFileInit checkpoint 2
---------- ORLFileInit checkpoint 3
------------ ElfFileInit entry
  ------------ ElfFileInit checkpoint 1
  ------------ ElfFileInit checkpoint 2
  ------------ ElfFileInit checkpoint 3
  ------------ ElfFileInit checkpoint 4
  ------------ ElfFileInit checkpoint 5
  ------------ ElfFileInit checkpoint 6
  -------------  ElfLoadFileStructure entry
---------------  ElfLoadFileStructure checkpoint 1
---------------  ElfLoadFileStructure checkpoint 2
---------------  ElfLoadFileStructure checkpoint 3
---------------  ElfLoadFileStructure checkpoint 4
---------------  e_hdr64->e_shoff = 20302020 m e_hdr64->e_ehsize = 20202020
---------------  ElfLoadFileStructure checkpoint 6
---------------  ElfLoadFileStructure checkpoint 7 shoff = 540024864,  ehsize= 538976288
---------------  ElfLoadFileStructure checkpoint 8
---------------  ElfLoadFileStructure checkpoint 9
---------------  ElfLoadFileStructure checkpoint 10 , contents_size1 = 540016640
Segmentation fault


To reach the place of interest in GDB  debugging session you should execute the following sequence of commands  ( NOTE: your paths may vary):
gdb --args ../JWLINK/OWLinuxD/jwlink.  form elf file  test.o lib libtest name testD
symbol-file   ../JWLINK/OWLinuxD/jwlink.sym 
b main
run
b ElfLoadFileStructure  //(in elfload.c )
c          // continue till the first hit with test.o
b 559 // ( breakpoint at line 559)
c
print *e_hdr64 //  (dump ELF header read from file)
all looks good here :
Quote
$3 = {e_ident = 0x80d77d0 "\177ELF\002\001\001", e_type = 1, e_machine = 62, e_version = 1, e_entry = 0, e_phoff = 0,
  e_shoff = 304, e_flags = 0, e_ehsize = 64, e_phentsize = 0, e_phnum = 0, e_shentsize = 64, e_shnum = 12, e_shstrndx = 9}
c    // - continue till the second hit of ElfLoadFileStructure  with libtest.lib file
c   //- till line 559
print *e_hdr64  (dump ELF header read from file)
Quote
$2 = {
  e_ident = 0x80d7998 "!<arch>\n/", ' ' <repeats 15 times>, "1455639231  0     0     0       24      \245\275\275\275\031",
  e_type = 8224, e_machine = 8224, e_version = 538976288, e_entry = 3618980083482833969, e_phoff = 2314885599537934643,
  e_shoff = 2314885530819502112, e_flags = 538976304, e_ehsize = 8224, e_phentsize = 8224, e_phnum = 13362,
  e_shentsize = 8224, e_shnum = 8224, e_shstrndx = 8224}

It looks completely no good !  Function _ClientRead( elf_file_hnd, sizeof( Elf64_Ehdr ) ) reads ELF headers from the start of libtest.lib !
elfload.c
Quote
    if( elf_file_hnd->flags & ORL_FILE_FLAG_64BIT_MACHINE ) {
       printf(" ---------------  ElfLoadFileStructure checkpoint 4 \n");
       e_hdr64 = _ClientRead( elf_file_hnd, sizeof( Elf64_Ehdr ) );

New version with added checkpoints attached . If all will go well next December I may ( or may not , who knows) be able to fix it myself  :biggrin:

GoneFishing

Debugging symbols for jwlink_new_bug

habran

#10
Hi GoneFishing,
Sorry mate, I have no intention, at least in a close future to work on JWlink, you are on your own there :biggrin:
The first reason is that I don't like it's commands and second, I am still busy with "The Rise Of The Jedi HJWasm" 8)
I wouldn't mind if you or someone else take over JWlink, however, If I decide to develop it, I'll change all commands to be compatible with MS Link and rewrite it fully in 64 bit assembly language. I would also rename it to hlink.
Cod-Father

GoneFishing

Hi HABRAN,

No problem . I didn't expect you'll work on it . Moreover once I've already asked John about it and got  definite reply.
If I  will ever be able to fix it I'll preserve  its original weird command line style ... and maybe do it even more weird and also rename it
to GoneFishLink  :biggrin:

P.S.: after that I probably may want to write an assembler  and rename it to GoneFishAs  :biggrin: :biggrin:
P.P.S.: most likely that after that I will need my own GoneFishLib

Yeah

habran

IMHO GoneFishLink is to complex for us assembly programmers, if I were you I would call it gflink 8)
I also think that GoneFish actually means RunawayFish I would change it to CaughtFish. :biggrin:
Cod-Father

GoneFishing

Habran,
I assume nothing can be too complex for real (TM) assembly programmer. You know , while in the sea one never can be sure if it's he who caught the fish or  he's the fish himself .