diff --git a/Makefile b/Makefile index 05f756f..3a526b9 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ RM = rm -f CC = gcc CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -std=c99 -pedantic -O2 -fomit-frame-pointer -c +CFLAGS = -std=c99 -pedantic -O2 -fomit-frame-pointer -c CONFIG = LD = $(CC) diff --git a/Makefile.68k b/Makefile.68k index 8bcc937..9522901 100644 --- a/Makefile.68k +++ b/Makefile.68k @@ -7,7 +7,7 @@ RM = rm -f CC = vc +aos68k CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c99 -cpu=68020 -c -O1 +CFLAGS = -c99 -cpu=68020 -c -O1 CONFIG = -DAMIGAOS LD = vc +aos68k diff --git a/Makefile.Irix5 b/Makefile.Irix5 index a914cb5..ab7fb97 100644 --- a/Makefile.Irix5 +++ b/Makefile.Irix5 @@ -7,7 +7,7 @@ RM = rm -f CC = gcc CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -O2 -fomit-frame-pointer -c +CFLAGS = -O2 -fomit-frame-pointer -c CONFIG = -DTYPES32BIT LD = gcc diff --git a/Makefile.MOS b/Makefile.MOS index e30f6de..2370020 100644 --- a/Makefile.MOS +++ b/Makefile.MOS @@ -7,7 +7,7 @@ RM = delete force quiet CC = vc +morphos CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c -O1 +CFLAGS = -c -O1 CONFIG = -DAMIGAOS LD = vc +morphos diff --git a/Makefile.MiNT b/Makefile.MiNT index e37e633..4b54785 100644 --- a/Makefile.MiNT +++ b/Makefile.MiNT @@ -7,7 +7,7 @@ RM = rm -f CC = vc +mint CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -DATARI -c99 -cpu=68020 -c -O1 +CFLAGS = -DATARI -c99 -cpu=68020 -c -O1 CONFIG = LD = vc +mint diff --git a/Makefile.OS4 b/Makefile.OS4 index 63fd026..edea5c1 100644 --- a/Makefile.OS4 +++ b/Makefile.OS4 @@ -7,7 +7,7 @@ RM = delete force quiet CC = vc +aosppc CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c -O1 +CFLAGS = -c -O1 CONFIG = -DAMIGAOS -D__USE_INLINE__ LD = vc +aosppc diff --git a/Makefile.PUp b/Makefile.PUp index 12341b2..1e7850d 100644 --- a/Makefile.PUp +++ b/Makefile.PUp @@ -7,7 +7,7 @@ RM = delete force quiet CC = vc +powerup CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c99 -c -O1 +CFLAGS = -c99 -c -O1 CONFIG = -DAMIGAOS LD = vc +powerup diff --git a/Makefile.TOS b/Makefile.TOS index 0a59eea..7cb525f 100644 --- a/Makefile.TOS +++ b/Makefile.TOS @@ -7,7 +7,7 @@ RM = rm -f CC = vc +tos CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c99 -c -O1 -DATARI +CFLAGS = -c99 -c -O1 -DATARI CONFIG = LD = vc +tos diff --git a/Makefile.WOS b/Makefile.WOS index 4d99c72..dc638c3 100644 --- a/Makefile.WOS +++ b/Makefile.WOS @@ -7,7 +7,7 @@ RM = delete force quiet CC = vc +warpos CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -c99 -c -O1 +CFLAGS = -c99 -c -O1 CONFIG = -DAMIGAOS LD = vc +warpos diff --git a/Makefile.Win32 b/Makefile.Win32 index 8586f0c..f7c3e81 100644 --- a/Makefile.Win32 +++ b/Makefile.Win32 @@ -17,12 +17,12 @@ RM = rem CC = cl CCOUT = /Fo # use /Dsnprintf=_snprintf for older VSC++ versions which are not C99-compliant! -#COPTS = /nologo /O2 /MT /Zp1 /Dsnprintf=_snprintf /c # use /MT for static linking -#COPTS = /nologo /O2 /MD /Dsnprintf=_snprintf /c -COPTS = /nologo /O2 /MD /c -COPTS = $(COPTS) /wd4996 # Disable warning regarding deprecated functions +#CFLAGS = /nologo /O2 /MT /Zp1 /Dsnprintf=_snprintf /c # use /MT for static linking +#CFLAGS = /nologo /O2 /MD /Dsnprintf=_snprintf /c +CFLAGS = /nologo /O2 /MD /c +CFLAGS = $(CFLAGS) /wd4996 # Disable warning regarding deprecated functions # ("use strcpy_s instead of strcpy" etc) -COPTS = $(COPTS) $(WIN32_PLATFORMSDK_INCLUDE) +CFLAGS = $(CFLAGS) $(WIN32_PLATFORMSDK_INCLUDE) CONFIG = LD = link diff --git a/Makefile.Win32FromLinux b/Makefile.Win32FromLinux index 3bb5b08..585c419 100644 --- a/Makefile.Win32FromLinux +++ b/Makefile.Win32FromLinux @@ -7,7 +7,7 @@ RM = rm -f CC = /usr/bin/i686-w64-mingw32-gcc CCOUT = -o $(DUMMYVARIABLE) # produces the string "-o " -COPTS = -std=c9x -O2 -fomit-frame-pointer -c +CFLAGS = -std=c9x -O2 -fomit-frame-pointer -c CONFIG = LD = /usr/bin/i686-w64-mingw32-gcc diff --git a/amigahunks.h b/amigahunks.h index d046f4b..5ee17b6 100644 --- a/amigahunks.h +++ b/amigahunks.h @@ -1,8 +1,8 @@ -/* $VER: vlink amigahunks.h V0.15a (09.12.15) +/* $VER: vlink amigahunks.h V0.18 (29.08.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2015 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -74,6 +74,10 @@ /* EHF extensions */ #define EXT_RELREF26 229 +/* GCC BFD amigaos type-flags (m68k only!) */ +#define EXTF_WEAK 0x40 +#define EXTF_LOCAL 0x20 + /* memory attributes */ #define MEMF_PUBLIC 1 #define MEMF_CHIP 2 @@ -103,12 +107,3 @@ struct XRefNode { int noffsets; struct list xreflist; }; - -#define SUBID_LINE 1 -struct LineDebug { - struct TargetExt tgext; /* id = TGEXT_AMIGAOS, subid = SUBID_LINE */ - const char *source_name; /* full path to source text */ - uint32_t num_entries; /* number of entries in line/offset table */ - uint32_t *lines; - uint32_t *offsets; -}; diff --git a/appleomf.h b/appleomf.h new file mode 100644 index 0000000..413fa3b --- /dev/null +++ b/appleomf.h @@ -0,0 +1,84 @@ +/* $VER: vlink xfile.h V0.18 (03.07.24) + * + * This file is part of vlink, a portable linker for multiple + * object formats. + * Copyright (c) 2024 Frank Wille + */ + + +/* OMF v1/v2 segment header */ +typedef struct +{ + uint8_t bytecnt[4]; /* number of bytes(v2)/blocks(v1) to next segment */ + uint8_t resspc[4]; /* cleared data space at the end of the segment */ + uint8_t length[4]; /* total length of segment data (including resspc) */ + uint8_t kindv1; /* OMF v1: segment type and attributes */ + uint8_t lablen; /* label length (0 = first byte has length) */ + uint8_t numlen; /* size of values in bytes */ + uint8_t version; /* 1 for OMF v1, 2 for OMF v2 */ + uint8_t banksize[4]; /* border of banksize should not be crossed by seg */ + uint8_t kindv2[2]; /* OMF v2: segment type and attributes */ + uint8_t reserved[2]; + uint8_t org[4]; /* absolute start address or 0 for relocatable */ + uint8_t align[4]; /* alignment boundary in bytes */ + uint8_t numsex; /* 0 is little-, 1 is big-endian */ + uint8_t lcbank; /* OMF v1: language card bank - unused */ + uint8_t segnum[2]; /* segment number, starting with 1 */ + uint8_t entry[4]; /* offset to entry point of segment */ + uint8_t dispname[2]; /* offset to the loadname field */ + uint8_t dispdata[2]; /* offset to the segment body */ + uint8_t loadname[10]; + /* it follows the segment name (using lablen) and the body */ +} OMFSeghdr; + +/* segment types */ +#define SEGT_CODE 0x00 +#define SEGT_DATA 0x01 +#define SEGT_JUMPTAB 0x02 +#define SEGT_PATHNAME 0x04 +#define SEGT_LIBDICT 0x08 +#define SEGT_INIT 0x10 +#define SEGT_DPSTACK 0x12 + +/* segment attributes (v2) */ +#define SEGA_RELOAD (1<<10) +#define SEGA_ABSBANK (1<<11) +#define SEGA_NOSPEC (1<<12) +#define SEGA_PIC (1<<13) +#define SEGA_PRIVATE (1<<14) +#define SEGA_DYNAMIC (1<<15) + + +/* OMF body opcodes */ +#define OMFOC_END 0x00 +#define OMFOC_RELOC 0xe2 +#define OMFOC_INTERSEG 0xe3 +#define OMFOC_LCONST 0xf2 +#define OMFOC_cRELOC 0xf5 +#define OMFOC_cINTERSEG 0xf6 +#define OMFOC_SUPER 0xf7 + + +/* OMF relocation */ +typedef struct +{ + struct node n; + unsigned flags; + unsigned short fileno; + unsigned short segno; + unsigned short size; /* relocation field size in bytes */ + short shift; /* value shift (right is negative) */ + unsigned long offset; /* reloc-field offset in current segment */ + long addend; /* offset in referenced segno plus addend */ +} OMFReloc; + +/* flags */ +#define OMFRF_SUPER 1 /* used in a SUPER reloc */ +#define OMFRF_RELOC 2 /* used in a RELOC or cRELOC */ +#define OMFRF_INTERSEG 4 /* used in a INTERSEG or cINTERSEG */ +#define OMFRF_DONE (OMFRF_SUPER|OMFRF_RELOC|OMFRF_INTERSEG) + + +/* limits */ +#define OMF_BANKSIZE 0x10000 +#define OMF_MAXDPSTKSIZE 0xc000 diff --git a/config.h b/config.h index 5eac2bf..cd6589a 100644 --- a/config.h +++ b/config.h @@ -1,8 +1,8 @@ -/* $VER: vlink config.h V0.16h (28.01.21) +/* $VER: vlink config.h V0.18 (29.11.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2021 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #ifndef CONFIG_H @@ -46,6 +46,7 @@ #define XFILE /* Sharp X68000 Human68k XFile format */ #define OS_9 /* OS-9 6809 reentrant modules */ #define O65 /* o65 6502/65816 object/executable file format */ +#define APPLE_OMF /* Apple 65816 OMF */ #define ELF32 /* general 32-bit ELF support */ #define ELF32_PPC_BE /* ELF PowerPC 32-Bit Big Endian */ @@ -77,11 +78,11 @@ #define CBMPRG /* Commodore PET, VIC-20, 64, etc. program */ #define COCOML /* Tandy Color Computer machine lang. file */ #define DRAGONBIN /* Dragon DOS binary format */ +#define FOENIX /* Foenix 65816 PGX and PGZ formats */ #define IHEX /* Intel Hex */ #define JAGSRV /* Jaguar Server (SkunkBoard, VirtualJaguar) */ #define ORICMC /* ORIC machine code file header */ -#define RAWBIN1 /* single raw binary file */ -#define RAWBIN2 /* multiple raw binary files */ +#define RAWBIN /* raw binary file (single, multiple, coalesced) */ #define RAWSEG /* multiple raw segment files */ #define SINCQL /* Sinclair QL, QDOS header or XTcc trailer */ #define SHEX1 /* Customer specific hex format */ diff --git a/cpurelocs.h b/cpurelocs.h new file mode 100644 index 0000000..ca95bd0 --- /dev/null +++ b/cpurelocs.h @@ -0,0 +1,25 @@ +/* $VER: vlink cpurelocs.h V0.18 (23.01.24) + * + * This file is part of vlink, a portable linker for multiple + * object formats. + * Copyright (c) 1997-2024 Frank Wille + */ + +/* + * CPU specific relocation types. + * Consists of a base, which represents a CPU id, and the VOBJ-compatible + * type - 0x80. + */ + +#define RELOC_CPU_ID(r) ((r)>>12) /* cpu-id of this reloc type, 0=standard */ +#define RELOC_CPU_TYPE(r) ((r)&0xfff) +#define MAKE_RELOC_CPU_ID(i) ((i)<<12) + +/* PowerPC (1) */ +#define R_PPC MAKE_RELOC_CPU_ID(1) +enum { + RPPC_SD2=R_PPC, + RPPC_SD21,RPPC_SDI16,RPPC_SD2I16,RPPC_MOSDREL,RPPC_AOSBREL +}; + +/* make entries for new CPUs here */ diff --git a/elf.c b/elf.c index 97eef50..11b71bd 100644 --- a/elf.c +++ b/elf.c @@ -1,8 +1,8 @@ -/* $VER: vlink elf.c V0.15e (23.03.17) +/* $VER: vlink elf.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2017 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -1056,8 +1056,8 @@ uint32_t elf_segmentcheck(struct GlobalVars *gv,size_t ehdrsize) unsigned long foffs; uint32_t segcnt; - /* @@@ ELF executables cannot deal with trimmed sections??? */ - untrim_sections(gv); + /* @@@ ELF executables cannot deal with trimmed sections??? FIXMEEEE */ + untrim_sections(gv,0); /* do not trim bss/uninitialized at least */ /* find PHDR segment */ for (phdrs=NULL,p=gv->phdrlist; p; p=p->next) { @@ -1099,8 +1099,8 @@ uint32_t elf_segmentcheck(struct GlobalVars *gv,size_t ehdrsize) ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - if (ls->copybase>=(unsigned long)p->start && ls->size && - (ls->copybase+ls->size)<=(unsigned long)p->mem_end && + if (ls->copybase>=p->start && ls->size && + (ls->copybase+ls->size)<=p->mem_end && (ls->flags & SF_ALLOC)) { p->flags |= conv_perm_to_elf(ls->protection); @@ -1137,10 +1137,10 @@ uint32_t elf_segmentcheck(struct GlobalVars *gv,size_t ehdrsize) } else if (ls->copybase > 0) { ierror("elf_segmentcheck(): overlapping sections " - "%s(%lx-%lx) followed by %s(%lx)", - seg_lastsec->name,seg_lastsec->copybase, - seg_lastsec->copybase + seg_lastsec->filesize, - ls->copybase); + "%s(%llx-%llx) followed by %s(%lx)", + seg_lastsec->name,(unsigned long long)seg_lastsec->copybase, + (unsigned long long)seg_lastsec->copybase + seg_lastsec->filesize, + (unsigned long long)ls->copybase); } } seg_lastsec = ls; @@ -1184,8 +1184,8 @@ uint32_t elf_segmentcheck(struct GlobalVars *gv,size_t ehdrsize) p->flags &= ~PHDR_PFMASK; for (ls=seg_lastdat=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - if (ls->copybase>=(unsigned long)p->start && ls->size && - (ls->copybase+ls->size)<=(unsigned long)p->mem_end && + if (ls->copybase>=p->start && ls->size && + (ls->copybase+ls->size)<=p->mem_end && (ls->flags & SF_ALLOC)) { p->flags |= conv_perm_to_elf(ls->protection); seg_lastdat = ls; @@ -1234,8 +1234,8 @@ void elf_makeshdrs(struct GlobalVars *gv, /* find sections which belong to this segment and set their index */ for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - if (ls->copybase>=(unsigned long)p->start && - (ls->copybase+ls->size)<=(unsigned long)p->mem_end && + if (ls->copybase>=p->start && + (ls->copybase+ls->size)<=p->mem_end && (ls->flags & SF_ALLOC) && ls->index==0) { uint32_t f = SHF_ALLOC; bool bss = ls->type==ST_UDATA || (ls->flags&SF_UNINITIALIZED); @@ -1248,7 +1248,7 @@ void elf_makeshdrs(struct GlobalVars *gv, /* check if section included in other non-LOAD segments as well */ for (p2=gv->phdrlist; p2; p2=p2->next) { if (p2->type!=PT_LOAD && (p2->flags&PHDR_USED) && - p2->start==(lword)ls->copybase) + p2->start==ls->copybase) p2->offset = elfoffset; } @@ -1413,18 +1413,18 @@ void elf_writesegments(struct GlobalVars *gv,FILE *f) if (p->type==PT_LOAD && (p->flags&PHDR_USED) && p->start!=ADDR_NONE && p->start_vma!=ADDR_NONE) { /* write page-alignment gap */ - fwritegap(gv,f,p->alignment_gap); + fwritegap(gv,f,p->alignment_gap,0); /* write section contents */ for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - if (ls->copybase>=(unsigned long)p->start && - (ls->copybase+ls->size)<=(unsigned long)p->mem_end && + if (ls->copybase>=p->start && + (ls->copybase+ls->size)<=p->mem_end && (ls->flags & SF_ALLOC)) { if (ls->filesize) fwritex(f,ls->data,ls->filesize); /* section's contents */ if (ls->gapsize) - fwritegap(gv,f,ls->gapsize); /* inter-section alignment gap */ + fwritegap(gv,f,ls->gapsize,0); /* inter-section alignment gap */ } } } diff --git a/elf32.h b/elf32.h index 909bb2c..430caa9 100644 --- a/elf32.h +++ b/elf32.h @@ -1,8 +1,8 @@ -/* $VER: vlink elf32.h V0.14 (13.06.11) +/* $VER: vlink elf32.h V0.17b (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2011 Frank Wille + * Copyright (c) 1997-2011,2024 Frank Wille */ @@ -29,7 +29,7 @@ struct StabCompUnit { /* Prototypes from t_elf32.c */ void elf32_parse(struct GlobalVars *,struct LinkFile *,struct Elf32_Ehdr *, - uint8_t (*)(uint8_t,struct RelocInsert *)); + int (*)(uint8_t,struct RelocInsert *)); void elf32_initdynlink(struct GlobalVars *); struct Section *elf32_dyntable(struct GlobalVars *,unsigned long,unsigned long, uint8_t,uint8_t,uint8_t,int); diff --git a/elf64.h b/elf64.h index 5c2fee5..6253a62 100644 --- a/elf64.h +++ b/elf64.h @@ -1,8 +1,8 @@ -/* $VER: vlink elf64.h V0.14 (24.06.11) +/* $VER: vlink elf64.h V0.17b (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2011 Frank Wille + * Copyright (c) 1997-2011,2024 Frank Wille */ @@ -31,7 +31,7 @@ struct StabCompUnit { /* Prototypes from t_elf64.c */ void elf64_parse(struct GlobalVars *,struct LinkFile *,struct Elf64_Ehdr *, - uint8_t (*)(uint8_t,struct RelocInsert *)); + int (*)(uint8_t,struct RelocInsert *)); void elf64_initdynlink(struct GlobalVars *); struct Section *elf64_dyntable(struct GlobalVars *,unsigned long,unsigned long, uint8_t,uint8_t,uint8_t,int); diff --git a/elfcommon.h b/elfcommon.h index 2a5bcab..9974e5d 100644 --- a/elfcommon.h +++ b/elfcommon.h @@ -305,7 +305,7 @@ struct RelocNode { /* for conversion from ELF reloc types to vlink internal format */ struct ELF2vlink { - uint8_t rtype; + int rtype; uint16_t bpos; uint16_t bsiz; lword mask; diff --git a/errors.c b/errors.c index 22aa443..f30df8b 100644 --- a/errors.c +++ b/errors.c @@ -1,8 +1,8 @@ -/* $VER: vlink errors.c V0.17a (25.06.22) +/* $VER: vlink errors.c V0.18 (31.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -96,13 +96,13 @@ static struct { "%s: Merging a code section with name \"__MERGED\"",EF_WARNING, "Relative references between %s section \"%s\" and %s section " "\"%s\" (%s) force a combination of the two",EF_WARNING, - "Can't define %s as ctors/dtors label. Symbol already exists.",EF_ERROR, + "Can't define %s as ctors/dtors label. Symbol already exists",EF_ERROR, "%s: ELF section header type %d in %s is not needed in " /* 60 */ "shared objects",EF_WARNING, "%s: Endianness differs from previous objects",EF_FATAL, "Target file format doesn't support relocatable objects",EF_ERROR, "Predefined limits of destination memory region %s " - "for section %s were exceeded (%#llx)",EF_FATAL, + "for output section %s were exceeded (%#llx)",EF_FATAL, "Section %s(%s) was not recognized by target linker script",EF_WARNING, "%s line %d: Unknown keyword <%s> ignored",EF_ERROR, /* 65 */ "%s line %d: '%c' expected",EF_ERROR, @@ -206,12 +206,33 @@ static struct { "Error number %d is not a warning",EF_FATAL, /* 145 */ "%s (%s): alternating bits per byte in object files (from %d to %d)",EF_FATAL, "%s (%s): alternating bytes per address in object files (from %d to %d)",EF_FATAL, - "Endianness is unknown. Default to host endianness.",EF_WARNING, + "Endianness is unknown. Default to host endianness",EF_WARNING, "Mismatching target address sizes in input/output formats",EF_FATAL, "%s: Hunk format corrupted: DEBUG hunk used like a section " /* 150 */ "with name \"%s\" in unit \"%s\". Trying to ignore",EF_WARNING, "%s: Duplicate con/destructor name %s definition ignored",EF_WARNING, "Warnings treated as errors",EF_ERROR, + "%s: Kickstart 1.x cannot initialize bss sections >256k to zero",EF_WARNING, + "No CPU defined for VOBJ output. Consider using -vobjcpu",EF_WARNING, + "Indirect symbol %s -> %s not allowed in %s",EF_FATAL, /* 155 */ + "Alternating CPU definitions from VOBJ input. Keeping the first one (%s)", + EF_WARNING, + "Bad symbol file format",EF_ERROR, + "No space to fit section %s(%s) with end address %#llx into memory region", + EF_ERROR, + "%s (%s+%#lx): Reference from overlayed section %s to %s (symbol %s) " + "is not allowed (NOCROSSREFS)",EF_ERROR, + "Target %s: Too many symbols for selected output format",EF_FATAL,/* 160 */ + "Invalid version. Assuming V%d.%d",EF_WARNING, + "Target %s: Output section %s exceeds maximum size %lu",EF_WARNING, + "%s: No valid bank size defined",EF_ERROR, + "%s (%s): Section exceeds maximum bank size of %lu",EF_ERROR, + "%s (%s+%#lx): output section %s (referenced symbol: %s) has no " /* 165 */ + "memory id defined, assuming zero",EF_WARNING, + "%s (%s+%#lx): reference to memory id of relocatable symbol " /* !!! */ + "%s=%#llx%c%#llx (value to write: %s%#llx) doesn't fit into %d bits",EF_ERROR, + "%s line %d: Maximum of %d sorting levels exceeded",EF_ERROR, + "%s line %d: Multiple EXCLUDE_%s commands in one pattern",EF_ERROR, }; diff --git a/expr.c b/expr.c index e800a55..4d5175e 100644 --- a/expr.c +++ b/expr.c @@ -1,8 +1,8 @@ -/* $VER: vlink expr.c V0.17a (23.04.22) +/* $VER: vlink expr.c V0.18 (26.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -63,14 +63,26 @@ char getchr(void) } -int testchr(char c) +bool testchr(char c) /* check for character, skip it and return true when present */ { if (*s == c) { s++; - return 1; + return TRUE; } - return 0; + return FALSE; +} + + +bool expectchr(char c) +/* expect a character, print error and return false otherwise */ +{ + if (getchr() != c) { + error(66,scriptname,line,c); /* c expected */ + back(1); + return FALSE; + } + return TRUE; } @@ -277,7 +289,7 @@ static struct Expr *primary_expr(void) val = sym->value; } else if (sym->type==SYM_RELOC && sym->relsect->lnksec!=NULL) { - val = (lword)sym->relsect->va + sym->value; + val = sym->relsect->va + sym->value; type = REL; } else { diff --git a/history b/history index 28067fd..f09f0f7 100644 --- a/history +++ b/history @@ -12,10 +12,87 @@ vlink history (aoutmint): changes are relevant for MiNT only (os9): changes are relevant for OS-9 modules (vobj): changes for VOBJ format -(tos): changes for the Atari TOS format +(tos): changes for the Atari TOS/DRI format (xfile): changes for the Sharp X68000 XFile format +- 0.18 (31.12.2024) +o Define for each relocation type whether it is signed, unsigned or + unknown, to provide better range checks. +o Make the range-checking work with sign-extended addresses (Example: + m68k 16-bit absolute addressing). +o When an output section's memory region has not enough space the linker + will look for the next matching output section. +o Linker script function KEEP() now really prevents garbage collection of + sections. +o Sorting and exclusion functions for linker script section patterns: + SORT_BY_ALIGNMENT(), SORT_BY_NAME(), SORT_BY_SIZE(), REVERSE(), EXCLUDE_FILE(), + EXCLUDE_SECTION(). Supporting up to two sorting levels. +o The output section's bank size can be defined by (BANKSIZE=n) in the + script, so input sections may not cross bank borders. In that case they + will be aligned to the beginning of the next bank. +o Linker script MEMORY definitions now support an optional ID=n argument, + where n defines an identification value which can be referenced by a + VOBJ-specific relocation type (MEMID). +o Can find first executable code-section now, when no start symbol is present. +o Portable pattern matching routine, used for Windows and TOS, did + case-insensitive string comparisons in the past. +o Absolute symbols are printed as a separate section into the map file. +o Support LINKONCE section flag. +o New options -symfile, -symfmt and -symctrl to output symbol/value pairs + into a file using the given printf-format string. +o New options -obe and -ole to specify the endianess of octets within + a target-byte (when it has more than 8 bits) when writing the contents + of a section to a file system. +o New option -lineoffsets to output a separate file with source line + offsets per section. +o New option -mattr to merge all sections with same attributes, when + linking without a linker script. +o The target file formats rawbin1 and rawbin2 were replaced by a common + rawbin format. rawbin2 is now -brawbin with -multifile option. The old + target names are still supported for backwards compatibility. +o New target file formats "foenixpgx-c02", "foenixpgx-816", "foenixpgz-24" + and "foenixpgz-32" for Foenix retro computers. +o New target file format "appleomf" can generate relocatable load-files for + Apple IIgs computers. +o Merge options -mrel, -mtype and -mall also work with -r now, when + creating a new relocatable object file. +o Linker script commands BYTE, SHORT, LONG, QUAD, SQUAD do now create + 1, 2, 4 or 8 target-bytes and no longer 8, 16, 32, 64-bit values. +o Relocatable/Abs symbols with the same name have precedence before Common + symbols and do not cause a multiple-definition error. +o Starting vlink without arguments only shows a short list of options now. + For a detailed description use -h, which also includes target-specific + options selected by -b. +o Fixed output section trimming in the linker script. +o Fix error message for unknown target file formats (-b). +o (ados/ehf) New option -kick1 makes sure the generated executable is + comaptible with the Kickstart 1.x loader. +o (ados/ehf) Allow symbol redefinitions from pulled library units, as long + as their target CPU differs (M68k/PPC WarpOS mixed binaries). This + only worked for symbols from code sections, since V0.17. +o (elf) Fixed uninitialized .plt at end of text segment. +o (tos) Can use DRI object files and libraries as input now. HiSoft and + SozobonX symbol name extensions are supported. +o (tos) TOS executable files do work as input and DRI objects as output (-r). +o (tos) Enable SozobonX symbol names with -tos-sozobonx. +o (tos) Disable the HiSoft symbol name extension in executables with + -tos-stddri. +o (rawbin) Fixed missing byte in the ORIC tape header and automatically + remove a ".tap" extension from the name stored there. +o (rawbin) IHEX supports architectures with more than 8 bits per byte. + But note that the address is still incremented in 8-bit steps. +o (rawbin) Make sure the file size is even, when using a QDOS header. +o (rawbin) Fixed raw reloc table output (-q) for a relocation at offset 0. +o (rawbin) New options -fill, -coalesced and -multifile (previously rawbin2). +o (vobj) Supports -r option to output a relocatable VOBJ file. + Option -vobj2 may be used to write that file in VOBJ v2 format. +o (vobj) -vobjcpu option selects the name of the target CPU. +o (vobj) Supports VOBJ v2 format (vasm -Fvobj -vobj2) and PPC non-standard + relocations (EABI, MorphOS, AmigaOS). +o (xfile) Entry address is written to the header. +o (xfile) New option -x-high sets the high-address flag in the header. + - 0.17a (22.09.22) o Fixed segfault following a syntax error in the linker script. o Allow '-' in the linker script for section names. diff --git a/ldscript.c b/ldscript.c index 0dcb641..8f8f3d4 100644 --- a/ldscript.c +++ b/ldscript.c @@ -1,8 +1,8 @@ -/* $VER: vlink ldscript.c V0.17a (19.06.22) +/* $VER: vlink ldscript.c V0.18 (31.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -26,17 +26,13 @@ static struct LinkedSection *current_ls; /* current section in work */ static const char *new_ls_name; /* newly defined sect. name (pass 1) */ /* BYTE, SHORT, LONG, etc. data commands */ -static int datasize,dataalign; /* datasize != 0 enables data command */ +static int datasize; /* datasize != 0 enables data command */ static lword dataval; /* for 2nd pass over the SECTIONS block during linking: */ static char *secblkbase; static int secblkline; -/* for 2nd pass over a section definition block: */ -static char *secdefbase; -static int secdefline; - /* Default segment names (including a blank to prevent redefinitions) */ static const char *defhdr = " headers"; static const char *defint = " interp"; @@ -395,32 +391,29 @@ static struct MemoryDescr *find_memblock(char *name) } -void update_address(struct MemoryDescr *rmd,struct MemoryDescr *dmd, - unsigned long addbytes) +lword update_address(struct MemoryDescr *rmd,struct MemoryDescr *dmd, + unsigned long addbytes) { const char *secname = current_ls ? current_ls->name : defmemname; + if (rmd->current+(lword)addbytes > rmd->org+rmd->len) + return rmd->current+(lword)addbytes; /* failed reloc address */ + if (dmd!=rmd && dmd->current+(lword)addbytes > dmd->org+dmd->len) + return dmd->current+(lword)addbytes; /* failed destination address */ + rmd->current += (lword)addbytes; - if (rmd->current > rmd->org + rmd->len) { - /* Fatal: Size of memory region exceeded! */ - error(63,rmd->name,secname,(unsigned long long)rmd->current); - } - if (dmd != rmd) { - dmd->current += addbytes; - if (dmd->current > dmd->org + dmd->len) { - /* Fatal: Size of memory region exceeded! */ - error(63,dmd->name,secname,(unsigned long long)dmd->current); - } - } + if (dmd != rmd) + dmd->current += (lword)addbytes; + return 0; /* ok */ } -void align_address(struct MemoryDescr *rmd,struct MemoryDescr *dmd, - unsigned long alignment) +lword align_address(struct MemoryDescr *rmd,struct MemoryDescr *dmd, + unsigned long alignment) { unsigned long addbytes = align(rmd->current,alignment); - update_address(rmd,dmd,addbytes); + return update_address(rmd,dmd,addbytes); } @@ -913,50 +906,40 @@ static bool get_dataval(void) static void sc_byte(struct GlobalVars *gv) /* BYTE(data8) */ { - if (get_dataval()) { + if (get_dataval()) datasize = 1; - dataalign = 0; - } } static void sc_short(struct GlobalVars *gv) /* SHORT(data16) */ { - if (get_dataval()) { + if (get_dataval()) datasize = 2; - dataalign = 0; /* @@@ */ - } } static void sc_long(struct GlobalVars *gv) /* LONG(data32) */ { - if (get_dataval()) { + if (get_dataval()) datasize = 4; - dataalign = 0; /* @@@ */ - } } static void sc_quad(struct GlobalVars *gv) /* QUAD(data64) */ { - if (get_dataval()) { + if (get_dataval()) datasize = 8; - dataalign = 0; /* @@@ */ - } } static void sc_reserve(struct GlobalVars *gv) /* RESERVE(space) */ { - if (get_dataval()) { + if (get_dataval()) datasize = -1; - dataalign = 0; - } } @@ -1037,7 +1020,8 @@ static void sc_searchdir(struct GlobalVars *gv) } -static struct MemoryDescr *add_memblock(const char *name,lword org,lword len) +static struct MemoryDescr *add_memblock(const char *name, + lword org,lword len,lword id) { struct MemoryDescr *last,*new = alloc(sizeof(struct MemoryDescr)); @@ -1055,16 +1039,18 @@ static struct MemoryDescr *add_memblock(const char *name,lword org,lword len) if (org+len < org) len -= org+len+1; new->len = len; + new->id = id; return new; } -static int startofsecdef(lword *s_addr,char *s_type,lword *s_lma) +static int startofsecdef(lword *s_addr,unsigned *s_flags,lword *s_lma, + lword *s_banksz,lword *s_bankof) /* Parse syntax of section definition until '{' and return VMA address, LMA address and type, when given. Returns bitfield. 0 when syntax is incorrect, 1 for correct, 3 for s_addr set, 5 for s_lma set and 7 for both. - Syntax: [addr-expr] [(type)] : [AT(lma)] { */ + Syntax: [addr-expr] [(BANKSIZE=n[,o])] [(NOLOAD)] : [AT(lma)] { */ { int ret = 1; lword caddr = -2; /* this prevents expression evaluation during pre-parse */ @@ -1073,7 +1059,8 @@ static int startofsecdef(lword *s_addr,char *s_type,lword *s_lma) if (current_ls) caddr = current_ls->relocmem->current; - *s_type = 0; + *s_flags = 0; + *s_banksz = *s_bankof = 0; c = getchr(); if (c != ':') { @@ -1092,9 +1079,35 @@ static int startofsecdef(lword *s_addr,char *s_type,lword *s_lma) ret |= 2; c = getchr(); } - if (c == '(') { - if (buf = getword()) { - strcpy(s_type,buf); + + while (c == '(') { /* (BANKSIZE=n[,o]), (NOLOAD), etc. */ + if (buf = getalnum()) { + if (!strcmp(buf,"BANKSIZE")) { + if (getchr() == '=') { + if (!preparse) + parse_expr(-1,s_banksz); + else + parse_expr(-2,s_banksz); + if (getchr() == ',') { /* optional offset into bank */ + if (!preparse) + parse_expr(-1,s_bankof); + else + parse_expr(-2,s_bankof); + } + else + back(1); + } + else { + error(66,scriptname,getlineno(),'='); /* '=' expected */ + back(1); + ret = 0; + } + } + else if (!strcmp(buf,"NOLOAD")) + *s_flags = 1; /* NOLOAD-flag */ + else + error(65,scriptname,getlineno(),buf); /* unknown keyword ignored */ + if (getchr() != ')') { error(66,scriptname,getlineno(),')'); /* ')' expected */ back(1); @@ -1102,6 +1115,8 @@ static int startofsecdef(lword *s_addr,char *s_type,lword *s_lma) } c = getchr(); } + else + break; } } @@ -1331,7 +1346,7 @@ static void define_memory(struct GlobalVars *gv) { char memname[MAXLEN]; char *str,c; - lword org,len; + lword org,len,id; if (startofblock('{')) { while (str = getword()) { @@ -1345,7 +1360,14 @@ static void define_memory(struct GlobalVars *gv) org = readmemparam(gv,"ORIGIN"); if (getchr() == ',') { len = readmemparam(gv,"LENGTH"); - add_memblock(memname,org,len); + if (getchr() == ',') { + id = readmemparam(gv,"ID"); + } + else { + back(1); + id = MEM_NOID; + } + add_memblock(memname,org,len,id); } else error(66,scriptname,getlineno(),','); /* ',' expected */ @@ -1446,7 +1468,7 @@ static void define_phdrs(struct GlobalVars *gv) static void predefine_sections(struct GlobalVars *gv) /* Syntax: */ -/* [addr] [(type)] : [AT(lma)] { ... } */ +/* [addr] [BANKSIZE(size[,offs])] [(type)] : [AT(lma)] { ... } */ /* [>region] [AT>lma-region] [:PHDR ...] [=FillExp] */ { char *keyword; @@ -1473,17 +1495,18 @@ static void predefine_sections(struct GlobalVars *gv) struct Section *dummy_sec; struct Phdr **plist; const char *s_name; - char c,s_type[MAXLEN]; - lword s_addr,s_lma; + lword s_addr,s_lma,s_banksz,s_bankof; + unsigned s_flags; + char c; int fl; back(1); s_name = allocstring(keyword); - if (fl = startofsecdef(&s_addr,s_type,&s_lma)) { + if (fl = startofsecdef(&s_addr,&s_flags,&s_lma,&s_banksz,&s_bankof)) { dummy_sec = NULL; ls = create_lnksect(gv,s_name,ST_UNDEFINED,0,0,0,0); - if (!strcmp(s_type,"NOLOAD")) + if (s_flags & 1) ls->ld_flags |= LSF_NOLOAD; if (fl & 4) ldefmem = atdefmem; /* AT(addr) in section definition */ @@ -1633,7 +1656,7 @@ static void add_section_to_segments(struct GlobalVars *gv, p->start = p->file_end = ls->copybase; if (p->start_vma == ADDR_NONE) p->start_vma = ls->base; - if ((lword)ls->copybase < p->mem_end) { + if (ls->copybase < p->mem_end) { /* section conflicts with segment - it doesn't cleanly attach to it */ error(83,ls->name,(unsigned long long)ls->copybase, (unsigned long long)ls->copybase+ls->size,p->name, @@ -1683,133 +1706,232 @@ static void add_section_to_segments(struct GlobalVars *gv, } -void free_patterns(char *fpat,char **spatlist) -/* free file-pattern and section-pattern list, allocated in next_pattern() */ +static void freepatlist(char **patlist) { char **p; - if (fpat) - free(fpat); - - if (p = spatlist) { + if ((p = patlist) != NULL) { while (*p) { free(*p); p++; } - free(spatlist); + free(patlist); + } +} + + +void free_patterns(struct Patterns *pat) +/* free file-pattern and section-pattern list, allocated in next_pattern(), + clear structure for reuse */ +{ + if (pat->fmatch) + free(pat->fmatch); + freepatlist(pat->fexclude); + freepatlist(pat->smatch); + freepatlist(pat->sexclude); + memset(pat,0,sizeof(struct Patterns)); +} + + +static char **store_pattern(int idx,char *str) +/* store pattern string pointer at index idx of a dynamic array */ +{ + static char **patarr; + static int maxidx; + + if (patarr == NULL) { + maxidx = 4; + patarr = alloc(maxidx*sizeof(char *)); } + if (idx >= maxidx) { + maxidx += maxidx; + patarr = re_alloc(patarr,maxidx*sizeof(char *)); + } + patarr[idx] = str; + return patarr; } -static bool parse_pattern(struct GlobalVars *gv,char *keyword, - char **fpat,char ***spatlist) -/* parse file/section-pattern and allocate pattern-lists */ +static char **makeplist(char **plist,int pcnt) { - const char *sortcmd = "SORT"; - char *patternlist[64]; /* yes... it's ugly :| */ + char **new = alloc((pcnt+1) * sizeof(char *)); + + memcpy(new,plist,pcnt*sizeof(char *)); + new[pcnt] = NULL; + return new; +} + + +static char **getpatlist(void) +/* get list of one or multiple file/section patterns */ +{ + char *pstr,**plist,**newplist; int pcnt = 0; - bool sortsec; - gv->scriptflags &= ~(LDSF_KEEP | LDSF_SORTFIL | LDSF_SORTSEC); + while (pstr = getpattern()) + plist = store_pattern(pcnt++,allocstring(pstr)); + + return makeplist(plist,pcnt); +} + + +static bool filepatcmd(struct GlobalVars *gv,char *keyword,struct Patterns *pat) +{ if (!strcmp(keyword,"KEEP")) { - if (keyword = getpattern()) { - if (getchr() != '(') { - error(66,scriptname,getlineno(),'('); /* '(' expected */ - back(1); - return FALSE; - } - } - else { - error(78,scriptname,getlineno()); /* missing argument */ - return FALSE; - } - gv->scriptflags |= LDSF_KEEP; + pat->flags |= PFL_KEEP; + return TRUE; } + return FALSE; +} - if (!strcmp(keyword,sortcmd)) { - if (keyword = getpattern()) { - if (getchr() != '(') { - error(66,scriptname,getlineno(),'('); /* '(' expected */ - back(1); - return FALSE; - } + +static bool maxsortlev(struct Patterns *pat) +{ + if (pat->ssortlev >= PSORT_MAXLEV) { + error(167,scriptname,getlineno(),PSORT_MAXLEV); /* max sorting level */ + return FALSE; + } + return TRUE; +} + + +static void pat_newsort(struct Patterns *pat,unsigned smode) +{ + if (maxsortlev(pat)) { + pat->ssort[pat->ssortlev] &= ~PSORT_MODE; + pat->ssort[pat->ssortlev++] |= smode; + } +} + + +static bool sectpatcmd(struct GlobalVars *gv,char *keyword,struct Patterns *pat) +{ + if (keyword == NULL) + return FALSE; + + if (getchr() == '(') { + if (!strcmp(keyword,"SORT_BY_NAME") || !strcmp(keyword,"SORT")) { + pat_newsort(pat,PSORT_NAME); + return TRUE; } - else { - error(78,scriptname,getlineno()); /* missing argument */ - return FALSE; + else if (!strcmp(keyword,"SORT_BY_ALIGNMENT")) { + pat_newsort(pat,PSORT_ALIGN); + return TRUE; } - gv->scriptflags |= LDSF_SORTFIL; - } - *fpat = alloc(strlen(keyword)+1); - strcpy(*fpat,keyword); - if (gv->scriptflags & LDSF_SORTFIL) { - if (!endofblock('(',')')) { - free(*fpat); - return FALSE; + else if (!strcmp(keyword,"SORT_BY_SIZE")) { + pat_newsort(pat,PSORT_SIZE); + return TRUE; + } + else if (!strcmp(keyword,"REVERSE")) { + if (maxsortlev(pat)) + pat->ssort[pat->ssortlev] ^= PSORT_REV; + return TRUE; } } - while (keyword = getpattern()) { - sortsec = FALSE; - if (!strcmp(keyword,sortcmd)) { - if (getchr() == '(') { - if (!(keyword = getpattern())) { - error(78,scriptname,getlineno()); /* missing argument */ - free(*fpat); - return FALSE; - } + back(1); + return FALSE; +} + + +static bool excludecmd(struct GlobalVars *gv,char *keyword,struct Patterns *pat) +{ + if (keyword) { + if (getchr() == '(') { + char **plist = NULL; + + if (!strcmp(keyword,"EXCLUDE_FILE")) { + if (pat->fexclude) + error(168,scriptname,getlineno(),"FILE"); /* multiple EXCLUDE_FILE */ + pat->fexclude = plist = getpatlist(); + } + else if (!strcmp(keyword,"EXCLUDE_SECTION")) { + if (pat->sexclude) + error(168,scriptname,getlineno(),"SECTION"); /* multiple EXCLUDE_SECTION */ + pat->sexclude = plist = getpatlist(); } else { - error(66,scriptname,getlineno(),'('); /* '(' expected */ back(1); - free(*fpat); return FALSE; } - if (!endofblock('(',')')) { - free(*fpat); + + if (!expectchr(')')) { + freepatlist(plist); return FALSE; } - gv->scriptflags |= LDSF_SORTSEC; - sortsec = TRUE; + return TRUE; } + back(1); + } + return FALSE; +} - if (pcnt < 63) { - patternlist[pcnt] = alloc(strlen(keyword) + (sortsec ? 2 : 1)); - if (sortsec) { - *patternlist[pcnt] = '$'; /* indicate sort-request */ - strcpy(patternlist[pcnt]+1,keyword); - } - else - strcpy(patternlist[pcnt],keyword); - pcnt++; + +static bool parse_pattern(struct GlobalVars *gv, + char *keyword,struct Patterns *pat) +/* Parse file/section-pattern and allocate pattern-lists. + Note, that a keyword/pattern and an opening parentheses was already parsed! */ +{ + int parens = 1; /* parentheses counter */ + int pcnt = 0; /* number of patterns in list */ + char **plist; + + pat->flags = 0; + pat->ssort[0] = pat->ssort[1] = PSORT_NONE; + + /* must start with a file-pattern or a command */ + while (filepatcmd(gv,keyword,pat)) { + if (keyword = getpattern()) { + if (!expectchr('(')) + return FALSE; } - else - ierror("parse_pattern(): pattern buffer overrun"); + else { + error(78,scriptname,getlineno()); /* missing argument */ + return FALSE; + } + parens++; } - patternlist[pcnt++] = NULL; - *spatlist = alloc(pcnt * sizeof(char *)); - memcpy(*spatlist,patternlist,pcnt*sizeof(char *)); + /* store file pattern */ + pat->fmatch = allocstring(keyword); - if (!endofblock('(',')')) { - free_patterns(*fpat,*spatlist); - return FALSE; + /* exclude-commands come first in front of the section patterns */ + do { + keyword = getpattern(); + } while (excludecmd(gv,keyword,pat)); + + /* handle potential sort-commands arround the section patterns */ + while (sectpatcmd(gv,keyword,pat)) { + if ((keyword = getpattern()) == NULL) { + error(78,scriptname,getlineno()); /* missing argument */ + return FALSE; + } + parens++; } - if (gv->scriptflags & LDSF_KEEP) { + + /* store section patterns */ + if (keyword == NULL) + keyword = "*"; + do { + plist = store_pattern(pcnt++,allocstring(keyword)); + } while (keyword = getpattern()); + pat->smatch = makeplist(plist,pcnt); + + /* finally collect all missing closing parentheses */ + while (parens--) { if (!endofblock('(',')')) { - free_patterns(*fpat,*spatlist); + free_patterns(pat); return FALSE; } } - return TRUE; + return TRUE; /* Pattern record is valid */ } static struct Section *make_data_element(struct GlobalVars *gv) /* Construct a section for a new data element (BYTE, SHORT, ...) */ { - bool be = gv->endianness == _BIG_ENDIAN_; - uint8_t *data = alloc(datasize); + uint8_t *data = alloc(tbytes(gv,datasize)); struct Section *sec; const char *name; @@ -1821,15 +1943,14 @@ static struct Section *make_data_element(struct GlobalVars *gv) name = current_ls->name; /* name is not important */ switch (datasize) { - case 1: *data = dataval; break; - case 2: write16(be,data,dataval); break; - case 4: write32(be,data,dataval); break; - case 8: write64(be,data,dataval); break; + case 1: writetbyte(gv,data,dataval); break; + case 2: writetbytes(gv,2,data,dataval); break; + case 4: writetbytes(gv,4,data,dataval); break; + case 8: writetbytes(gv,8,data,dataval); break; default: ierror("make_data_element"); break; } - sec = create_section(script_obj,name,data, - (datasize*8+gv->bits_per_tbyte-1)/gv->bits_per_tbyte); + sec = create_section(script_obj,name,data,datasize); sec->type = ST_DATA; addtail(&script_obj->sections,&sec->n); return sec; @@ -1875,55 +1996,7 @@ static struct Section *reserve_space(struct GlobalVars *gv) } -int test_pattern(struct GlobalVars *gv,char **fpat,char ***spatlist) -/* Provides next file/section-patterns, but doesn't execute any commands - or assignments. It will remember the initial parsing state to - do it a second time for real with next_pattern(). - Return Codes: 0=no more patterns, -1=pattern, >0=alignment */ -{ - char c,*keyword; - - if (!secdefbase) { - secdefbase = gettxtptr(); - secdefline = getlineno(); - } - *fpat = NULL; - *spatlist = NULL; - level = 2; - - do { - while (keyword = getpattern()) { - if (check_command(gv,keyword,SCMDF_IGNORE|SCMDF_SECDEF)) { - if (datasize) - return dataalign; - continue; - } - else { - c = getchr(); - if (c == '=') { - skip_expr(0); - } - else if (c == '(') { - if (parse_pattern(gv,keyword,fpat,spatlist)) - return -1; - } - else { - /* unknown keyword ignored */ - error(65,scriptname,getlineno(),keyword); - back(1); - } - } - } - } - while (getchr() == ';'); - back(1); - - level = 1; - return 0; -} - - -struct Section *next_pattern(struct GlobalVars *gv,char **fpat,char ***spatlist) +struct Section *next_pattern(struct GlobalVars *gv,struct Patterns *pat) /* Provides next file-pattern and a list of section-patterns, when present. Returns NULL, when section definition is closed, a section pointer for a data element to insert (BYTE, SHORT, LONG, ...), @@ -1935,13 +2008,6 @@ struct Section *next_pattern(struct GlobalVars *gv,char **fpat,char ***spatlist) char c,*keyword; level = 2; - *fpat = NULL; - *spatlist = NULL; - if (secdefbase) { - /* reset to beginning of section definition */ - init_parser(gv,scriptname,secdefbase,secdefline); - secdefbase = NULL; - } do { while (keyword = getpattern()) { @@ -1958,7 +2024,7 @@ struct Section *next_pattern(struct GlobalVars *gv,char **fpat,char ***spatlist) symbol_assignment(gv,keyword,0); } else if (c == '(') { - if (parse_pattern(gv,keyword,fpat,spatlist)) + if (parse_pattern(gv,keyword,pat)) return VALIDPAT; } else @@ -2073,24 +2139,27 @@ struct LinkedSection *next_secdef(struct GlobalVars *gv) else { /* check for section definition */ - char s_type[MAXLEN]; struct LinkedSection *ls; int ret; - lword s_addr,s_lma; + lword s_addr,s_lma,s_banksz,s_bankof; + unsigned s_flags; back(1); if (ls = find_lnksec(gv,keyword,0,0,0,0)) { current_ls = ls; - if (ret = startofsecdef(&s_addr,s_type,&s_lma)) { + if (ret = startofsecdef(&s_addr,&s_flags,&s_lma,&s_banksz,&s_bankof)) { + if (s_banksz) { + ls->banksize = s_banksz; + ls->bankoffs = s_bankof; + } if (ret & 2) change_address(ls->relocmem,s_addr); if (ret & 4) { if (ls->destmem == ls->relocmem) - ls->destmem = add_memblock("lma",MEM_DEFORG,MEM_DEFLEN); + ls->destmem = add_memblock("lma",MEM_DEFORG,MEM_DEFLEN,MEM_NOID); change_address(ls->destmem,s_lma); } - secdefbase = NULL; return ls; } } @@ -2162,8 +2231,8 @@ void init_ld_script(struct GlobalVars *gv) preparse = TRUE; current_ls = NULL; level = 0; /* outside SECTIONS block */ - defmem = add_memblock(defmemname,MEM_DEFORG,MEM_DEFLEN); - atdefmem = add_memblock("lmadefault",MEM_DEFORG,MEM_DEFLEN); + defmem = add_memblock(defmemname,MEM_DEFORG,MEM_DEFLEN,MEM_NOID); + atdefmem = add_memblock("lmadefault",MEM_DEFORG,MEM_DEFLEN,MEM_NOID); vdefmem = ldefmem = defmem; change_address(defmem,gv->start_addr); if (!(scriptname = gv->scriptname)) diff --git a/linker.c b/linker.c index 15620dc..8b1759d 100644 --- a/linker.c +++ b/linker.c @@ -1,13 +1,14 @@ -/* $VER: vlink linker.c V0.17a (21.05.22) +/* $VER: vlink linker.c V0.18 (27.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #define LINKER_C #include "vlink.h" +#include "cpurelocs.h" static char namebuf[FNAMEBUFSIZE]; @@ -352,7 +353,7 @@ static void merge_sec_attrs(struct LinkedSection *lsn,struct Section *sec, if (lsn->type==ST_UDATA && sec->type==ST_DATA) lsn->type = ST_DATA; /* a DATA-BSS section */ lsn->flags &= SF_PORTABLE_MASK; - lsn->flags |= (sec->flags&SF_SMALLDATA) | target_flags; + lsn->flags |= (sec->flags&(SF_SMALLDATA|SF_LINKONCE)) | target_flags; } if (lsn->protection != sec->protection) { @@ -395,6 +396,17 @@ static void merge_sec_attrs(struct LinkedSection *lsn,struct Section *sec, } +static int sec_same_attr(struct LinkedSection *ls,struct Section *sec) +{ + if (ls->protection==sec->protection && + (!ls->memattr || !sec->memattr || ls->memattr==sec->memattr)) { + return (ls->flags & (SF_SMALLDATA|(~SF_PORTABLE_MASK))) == + (sec->flags & (SF_SMALLDATA|(~SF_PORTABLE_MASK))); + } + return 0; +} + + static struct LinkedSection *get_matching_lnksec(struct GlobalVars *gv, struct Section *sec, struct LinkedSection *myls) @@ -409,6 +421,8 @@ static struct LinkedSection *get_matching_lnksec(struct GlobalVars *gv, while (nextlsn = (struct LinkedSection *)lsn->n.next) { if (lsn != myls && ((f = cmpsecflags(gv,lsn,sec)) != 0xff)) { f &= ~SF_PORTABLE_MASK; + if (lsn->flags & sec->flags & SF_LINKONCE) + continue; /* link only once */ if (!gv->dest_object) { /* target-specific linking */ @@ -449,33 +463,34 @@ static struct LinkedSection *get_matching_lnksec(struct GlobalVars *gv, } } - if (gv->auto_merge && checkrelrefs(lsn,sec)) { - /* we must link them together, because there are rel. refs */ - if ((lsn->type==ST_CODE || sec->type==ST_CODE) && - lsn->type != sec->type) - /* forces a maybe unwanted combination of code and data */ - error(58,sec_names[lsn->type],lsn->name,sec_names[sec->type], - sec->name,getobjname(sec->obj)); - Dprintf("relrefs: %s(%s) -> %s\n",getobjname(sec->obj), - sec->name,lsn->name); - merge_sec_attrs(lsn,sec,f); - return (lsn); - } - if (!gv->multibase && (lsn->flags & sec->flags & SF_SMALLDATA)) { Dprintf("sd-refs: %s(%s) -> %s\n",getobjname(sec->obj), sec->name,lsn->name); merge_sec_attrs(lsn,sec,f); return (lsn); } - - if (gv->merge_all || - (gv->merge_same_type && lsn->type==sec->type)) { - merge_sec_attrs(lsn,sec,f); - return (lsn); - } } /* final executable only */ + /* check merge-options -mrel, -mtype, -mattr, -mall */ + if (gv->merge_all || + (gv->merge_same_type && lsn->type==sec->type) || + (gv->merge_same_attr && sec_same_attr(lsn,sec))) { + merge_sec_attrs(lsn,sec,f); + return (lsn); + } + if (gv->auto_merge && checkrelrefs(lsn,sec)) { + /* we must link them together, because there are rel. refs */ + if ((lsn->type==ST_CODE || sec->type==ST_CODE) && + lsn->type != sec->type) + /* forces a maybe unwanted combination of code and data */ + error(58,sec_names[lsn->type],lsn->name,sec_names[sec->type], + sec->name,getobjname(sec->obj)); + Dprintf("relrefs: %s(%s) -> %s\n",getobjname(sec->obj), + sec->name,lsn->name); + merge_sec_attrs(lsn,sec,f); + return (lsn); + } + /* standard check, if sections could be merged */ if (myls == NULL) { /* check for same type and same name or no name */ @@ -528,10 +543,11 @@ static struct Section *last_initialized(struct LinkedSection *ls) } -static unsigned long allocate_common(struct GlobalVars *gv, - struct Section *sec,unsigned long addr) -/* allocate all common symbols to section 'sec' at 'addr', - returns number of total bytes allocated */ +static unsigned long allocate_common(struct GlobalVars *gv,bool really, + struct Section *sec,lword addr) +/* Allocate all common symbols to section 'sec' at 'addr' and + returns number of total bytes allocated. + Only returns total bytes without symbol allocation with 'really' = FALSE. */ { unsigned long abytes,alloc=0; struct Symbol *sym; @@ -544,19 +560,22 @@ static unsigned long allocate_common(struct GlobalVars *gv, /* allocate and transform into SYM_RELOC */ abytes = comalign(addr+alloc,sym->value); - sym->value = (lword)((addr+alloc) - sec->va) + abytes; - sym->type = SYM_RELOC; + if (really) { + sym->value = (lword)((addr+alloc) - sec->va) + abytes; + sym->type = SYM_RELOC; + if (gv->map_file) + fprintf(gv->map_file,"Allocating common %s: %x at %llx hex\n", + sym->name,(int)sym->size, + (unsigned long long)sec->va+sym->value); + } alloc += abytes + sym->size; - - if (gv->map_file) - fprintf(gv->map_file,"Allocating common %s: %x at %llx hex\n", - sym->name,(int)sym->size, - (unsigned long long)sec->va+sym->value); } } } - sec->size += alloc; + if (really) + sec->size += alloc; + return alloc; } @@ -763,15 +782,17 @@ static void init_dynlink(struct GlobalVars *gv) } -static void set_last_sec_reloc(struct Section *s,struct Reloc *r) +static void set_last_sec_reloc(struct GlobalVars *gv, + struct Section *s,struct Reloc *r) /* Check if Reloc is the new last relocation in this section and remember the offset behind its relocation field. */ { + int b = gv->bits_per_tbyte; struct RelocInsert *ri; unsigned long rend; for (ri=r->insert; ri!=NULL; ri=ri->next) { - rend = r->offset + (ri->bpos + ri->bsiz + 7) / 8; + rend = r->offset + (ri->bpos + ri->bsiz + b - 1) / b; if (rend > s->last_reloc) s->last_reloc = rend; } @@ -861,29 +882,116 @@ static void ref_prot_symbols(struct GlobalVars *gv) } -static void merge_ld_section(struct GlobalVars *gv,uint8_t stype, +static lword next_bank_address(struct GlobalVars *gv,struct LinkedSection *ls) +{ + lword bksize,addr; + + if (!(bksize = ls->banksize)) { + error(163,ls->name); /* no valid bank size */ + /* set a dummy to continue and to avoid further errors */ + bksize = 1LL << (gv->bits_per_taddr - 1); + ls->banksize = bksize; + } + addr = ls->relocmem->current; + return addr + (bksize - addr % bksize); +} + + +static lword try_update_address(struct GlobalVars *gv, + struct LinkedSection *ls,struct Section *sec) +/* Update VMA/LMA to see if the input section fits into the given + memory regions, return the failed address on error. + WARNING: VMA and LMA are really updated, although commons are not + really allocated. You have to save/restore the pointers. */ +{ + lword failed; + + if (failed = align_address(ls->relocmem,ls->destmem,sec->alignment)) + return failed; + + if (failed = update_address(ls->relocmem,ls->destmem,sec->size)) + return failed; + + if (is_common_sec(gv,sec) && + (!gv->dest_object || gv->alloc_common) && sec->type==ST_UDATA) { + if (failed = update_address(ls->relocmem,ls->destmem, + allocate_common(gv,FALSE,sec, + ls->relocmem->current))) + return failed; + } + return 0; /* ok */ +} + + +static bool ld_section_has_space(struct GlobalVars *gv, + struct LinkedSection *ls,struct Section *sec) +/* Check, if the associated memory region of the output section 'ls' has + sufficient space for our new section 'sec' to merge. */ +{ + lword ra = ls->relocmem->current; + lword da = ls->destmem->current; + lword failed; + + if (ls->banksize) { + lword ba = next_bank_address(gv,ls); + + if (!(failed = try_update_address(gv,ls,sec)) && + ls->relocmem->current > ba) { + /* bank-alignment required as sec crosses bank borders */ + sec->internal_flags |= ILF_BANKALIGN; + ls->relocmem->current = ra; + ls->destmem->current = da; + update_address(ls->relocmem,ls->destmem,(ba-ra)+ls->bankoffs); + failed = try_update_address(gv,ls,sec); + if (!failed && ls->relocmem->current > ba+ls->bankoffs+ls->banksize) + error(164,getobjname(sec->obj),sec->name,ls->banksize); + } + } + else + failed = try_update_address(gv,ls,sec); + + /* restore memory region pointers after test */ + ls->relocmem->current = ra; + ls->destmem->current = da; + + if (failed) { + /* remember out of range address of section */ + sec->internal_flags |= ILF_BADADDRINVA; + sec->va = failed; + return FALSE; + } + sec->internal_flags &= ~ILF_BADADDRINVA; + return TRUE; +} + + +static void merge_ld_section(struct GlobalVars *gv,struct Patterns *pat, struct LinkedSection *ls,struct Section *sec) -/* Merge Section into a linker-script LinkedSection. */ +/* Merge new section 'sec' into the output section 'ls'. */ { + if (sec->internal_flags & ILF_BANKALIGN) { + /* align to beginning of next bank first */ + update_address(ls->relocmem,ls->destmem, + (next_bank_address(gv,ls) - ls->relocmem->current) + + ls->bankoffs); + } align_address(ls->relocmem,ls->destmem,sec->alignment); - - sec->filldata = gv->filldata; - sec->lnksec = ls; sec->va = ls->relocmem->current; - sec->offset = sec->va - ls->base; update_address(ls->relocmem,ls->destmem,sec->size); /* allocate COMMON symbols, if required */ if (is_common_sec(gv,sec) && - (!gv->dest_object || gv->alloc_common) && stype==ST_UDATA) { + (!gv->dest_object || gv->alloc_common) && sec->type==ST_UDATA) { update_address(ls->relocmem,ls->destmem, - allocate_common(gv,sec,ls->relocmem->current)); + allocate_common(gv,TRUE,sec,ls->relocmem->current)); } + sec->offset = sec->va - ls->base; + sec->filldata = gv->filldata; + sec->lnksec = ls; addtail(&ls->sections,remnode(&sec->n)); - if (!is_ld_script(sec->obj) && - (gv->scriptflags & LDSF_KEEP)) { + if (!is_ld_script(sec->obj) && (pat->flags & PFL_KEEP)) { /* @@@ KEEP for one section will prevent all merged sections from being deleted - ok??? */ ls->ld_flags |= LSF_PRESERVE; @@ -912,8 +1020,13 @@ static void merge_seclist(struct GlobalVars *gv,struct list *seclist) sec->protection,sec->alignment,sec->memattr); create_allowed = FALSE; } - if (ls) + + if (ls) { + /* approximate section's size by assuming a base of 0 */ + ls->size += sec->size + align(ls->size,sec->alignment); addtail(&ls->sections,remnode(&sec->n)); + sec->lnksec = ls; + } sec = nextsec; } @@ -925,8 +1038,8 @@ static void merge_seclist(struct GlobalVars *gv,struct list *seclist) static int sym_addr_cmp(const void *left,const void *right) /* qsort: compare symbol addresses */ { - unsigned long addrl = (*(struct Symbol **)left)->value; - unsigned long addrr = (*(struct Symbol **)right)->value; + uint64_t addrl = (*(struct Symbol **)left)->value; + uint64_t addrr = (*(struct Symbol **)right)->value; return (addrladdrr) ? 1 : 0); } @@ -1060,6 +1173,7 @@ void linker_load(struct GlobalVars *gv) if (gv->bits_per_tbyte == 0) gv->bits_per_tbyte = 8; /* default to 8-bit bytes */ + gv->octets_per_tbyte = (gv->bits_per_tbyte + 7) / 8; /* target format has priority, provided it defines the bits per taddr */ if (fff[gv->dest_format]->addr_bits > 0) { @@ -1356,14 +1470,14 @@ void linker_relrefs(struct GlobalVars *gv) xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) { /* remember offset of sections's last xref */ - set_last_sec_reloc(sec,xref); + set_last_sec_reloc(gv,sec,xref); if (xdef = xref->relocsect.symbol) { if (xdef->relsect!=NULL && (xdef->type==SYM_RELOC || xdef->type==SYM_COMMON)) { - if ((xref->rtype==R_SD || xref->rtype==R_SD21 || - xref->rtype==R_MOSDREL) && xdef->type==SYM_COMMON) { + if ((xref->rtype==R_SD || xref->rtype==RPPC_SD21 || + xref->rtype==RPPC_MOSDREL) && xdef->type==SYM_COMMON) { /* small data common symbol - assign .scommon section */ xdef->relsect = scommon_section(gv,xdef->relsect->obj); } @@ -1373,7 +1487,7 @@ void linker_relrefs(struct GlobalVars *gv) addrelref(rr,xdef->relsect); } - else if (xref->rtype==R_SD || xref->rtype==R_MOSDREL) { + else if (xref->rtype==R_SD || xref->rtype==RPPC_MOSDREL) { /* other section is accessed base relative from this one */ xdef->relsect->flags |= SF_SMALLDATA; if (!gv->textbaserel && xdef->relsect->type==ST_CODE) @@ -1387,7 +1501,7 @@ void linker_relrefs(struct GlobalVars *gv) reloc->n.next!=NULL; reloc=(struct Reloc *)reloc->n.next) { /* remember offset of sections's last reloc */ - set_last_sec_reloc(sec,reloc); + set_last_sec_reloc(gv,sec,reloc); if (reloc->rtype==R_PC && reloc->relocsect.ptr!=sec) { /* relative reference to different section */ @@ -1416,7 +1530,7 @@ void linker_relrefs(struct GlobalVars *gv) error(121,getobjname(sec->obj),sec->name,reloc->offset); } - else if (reloc->rtype == R_SD2) { + else if (reloc->rtype == RPPC_SD2) { if (!sda2base) { /* R_SD2 relocation implies a reference to _SDA2_BASE_ */ if (sda2base = find_any_symbol(gv,sec,sda2base_name)) @@ -1424,11 +1538,11 @@ void linker_relrefs(struct GlobalVars *gv) } } - else if (reloc->rtype == R_MOSDREL) { + else if (reloc->rtype == RPPC_MOSDREL) { /* other section is accessed base relative from this one */ reloc->relocsect.ptr->flags |= SF_SMALLDATA; if (!r13init) { - /* R_MOSDREL relocation implies a reference to __r13_init */ + /* RPPC_MOSDREL relocation implies a reference to __r13_init */ if (r13init = find_any_symbol(gv,sec,r13init_name)) r13init->flags |= SYMF_REFERENCED; } @@ -1495,10 +1609,11 @@ void linker_sectrefs(struct GlobalVars *gv) void linker_gcsects(struct GlobalVars *gv) +/* garbage collection when linking without linker script */ { unsigned gcs; - if ((gcs = gv->gc_sects) != GCS_NONE) { + if (!gv->use_ldscript && (gcs = gv->gc_sects) != GCS_NONE) { struct ObjectUnit *obj; struct Section *sec,*nextsec; @@ -1524,89 +1639,242 @@ void linker_gcsects(struct GlobalVars *gv) } -void linker_join(struct GlobalVars *gv) -/* Join the sections with same name and type, or as defined by a +static bool garbage_collected(struct GlobalVars *gv,struct Patterns *pat, + struct Section *sec) +{ + unsigned gcs; + + if ((gcs = gv->gc_sects) != GCS_NONE && + !(pat->flags & PFL_KEEP) && + !(sec->flags & SF_REFERENCED)) { + + if (gcs == GCS_ALL) { + remnode(&sec->n); /* remove section from the linking process */ + Dprintf(" %s(%s) ignored (unreferenced)\n",getobjname(sec->obj),sec->name); + return TRUE; + } + else if (gcs==GCS_EMPTY && sec->size==0) { + remnode(&sec->n); /* remove section from the linking process */ + Dprintf(" %s(%s) ignored (empty)\n",getobjname(sec->obj),sec->name); + return TRUE; + } + } + return FALSE; +} + + +static struct Section **store_msect(int idx,struct Section *sec) +/* store section pointer at index idx of a dynamic array */ +{ + static struct Section **secarr; + static int maxidx; + + if (secarr == NULL) { + maxidx = 4; + secarr = alloc(maxidx*sizeof(struct Section *)); + } + if (idx >= maxidx) { + maxidx += maxidx; + secarr = re_alloc(secarr,maxidx*sizeof(struct Section *)); + } + secarr[idx] = sec; + return secarr; +} + + +static int sec_sorta_by_name(const void *left,const void *right) +{ + return strcmp((*(struct Section **)left)->name, + (*(struct Section **)right)->name); +} + +static int sec_sortd_by_name(const void *left,const void *right) +{ + return -strcmp((*(struct Section **)left)->name, + (*(struct Section **)right)->name); +} + +static int sec_sorta_by_align(const void *left,const void *right) +{ + unsigned lalign = (*(struct Section **)left)->alignment; + unsigned ralign = (*(struct Section **)right)->alignment; + + return (lalignralign) ? 1 : 0); +} + +static int sec_sortd_by_align(const void *left,const void *right) +{ + unsigned lalign = (*(struct Section **)left)->alignment; + unsigned ralign = (*(struct Section **)right)->alignment; + + return (lalign>ralign) ? -1 : ((lalignsize; + unsigned long rsize = (*(struct Section **)right)->size; + + return (lsizersize) ? 1 : 0); +} + +static int sec_sortd_by_size(const void *left,const void *right) +{ + unsigned long lsize = (*(struct Section **)left)->size; + unsigned long rsize = (*(struct Section **)right)->size; + + return (lsize>rsize) ? -1 : ((lsize 1) { + qsort(slist,numsecs,sizeof(struct Section *),sec_cmp); + + if (pat->ssort[1] != PSORT_NONE) { + /* potential level 2 sorting for elements which are equal on level 1 */ + int i,j; + + for (i=0; i 1) { + /* apply second sort level on this range */ + switch (pat->ssort[1]) { + case PSORT_NAME: + qsort(&slist[i],j,sizeof(struct Section *),sec_sorta_by_name); + break; + case PSORT_NAME|PSORT_REV: + case PSORT_REV: + qsort(&slist[i],j,sizeof(struct Section *),sec_sortd_by_name); + break; + case PSORT_ALIGN: + qsort(&slist[i],j,sizeof(struct Section *),sec_sortd_by_align); + break; + case PSORT_ALIGN|PSORT_REV: + qsort(&slist[i],j,sizeof(struct Section *),sec_sorta_by_align); + break; + case PSORT_SIZE: + qsort(&slist[i],j,sizeof(struct Section *),sec_sortd_by_size); + break; + case PSORT_SIZE|PSORT_REV: + qsort(&slist[i],j,sizeof(struct Section *),sec_sorta_by_size); + break; + default: + ierror("level2-sort: mode %u unhandled",(unsigned)pat->ssort[1]); + break; + } + } + } + } + } +} + + +static void sort_and_merge_sections(struct GlobalVars *gv, + struct Patterns *pat, + struct LinkedSection *ls, + struct Section **secarr,int numsecs) +/* Note, that it may be possible that not all sections from secarr are merged + into the output section here. For example, because the output section has + no sufficient space left. In this case the section remains in the list + of its original object file. */ +{ + if (numsecs) { + struct Section *sec; + uint8_t stype; + int i; + + /* sort section pointers in secarr */ + switch (pat->ssort[0]) { + case PSORT_NAME: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sorta_by_name); + break; + case PSORT_NAME|PSORT_REV: + case PSORT_REV: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sortd_by_name); + break; + case PSORT_ALIGN: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sortd_by_align); + break; + case PSORT_ALIGN|PSORT_REV: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sorta_by_align); + break; + case PSORT_SIZE: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sortd_by_size); + break; + case PSORT_SIZE|PSORT_REV: + sec_sort_lev1(gv,pat,secarr,numsecs,sec_sorta_by_size); + break; + default: + ierror("level1-sort: mode %u unhandled",(unsigned)pat->ssort[0]); + case PSORT_NONE: + break; + } + + /* Merge all sections in the given order into the output section. + But do it by section type: ST_CODE first, then ST_DATA and ST_UDATA + at last, to keep uninitialized sections together. */ + for (stype=0; stype<=ST_LAST; stype++) { + for (i=0; itype==stype && ld_section_has_space(gv,ls,sec)) { + if ((f = cmpsecflags(gv,ls,sec)) == 0xff) { + /* no warning, because the linker-script should know */ + f = ls->flags ? ls->flags : sec->flags; + } + sec->internal_flags &= ~ILF_BADADDRINVA; + merge_sec_attrs(ls,sec,f); + merge_ld_section(gv,pat,ls,sec); + } + } + } + } +} + + +void linker_merge(struct GlobalVars *gv) +/* Merge the sections with same name and type, or as defined by a linker script. Calculate their virtual address and size. */ { struct ObjectUnit *obj; struct Section *sec,*nextsec; struct LinkedSection *ls; + struct list seclist; uint8_t stype; if (gv->trace_file) - fprintf(gv->trace_file,"Joining selected sections:\n"); + fprintf(gv->trace_file,"Merging selected sections:\n"); + + if (fff[gv->dest_format]->init != NULL) + fff[gv->dest_format]->init(gv,FFINI_MERGE); if (gv->use_ldscript) { /* Linkage rules are defined by a linker script, which means there */ /* are predefined LinkedSection structures which can be used. */ struct LinkedSection *maxls=NULL; - char *filepattern,**secpatterns; + struct Patterns pat; unsigned long maxsize = 0; + memset(&pat,0,sizeof(struct Patterns)); init_secdef_parse(gv); /* Handle one section definition after the other from the linker script's SECTIONS block. The script parser cares for commands, symbol-definitions and address-assignments. */ while (ls = next_secdef(gv)) { - int patalign; - - /* Phase 1: read file/section patterns and determine which alignment - and flags are required for the sections to merge with us */ - while (patalign = test_pattern(gv,&filepattern,&secpatterns)) { - if (patalign < 0) { - /* normal case: match selected objects with file/sec. patterns */ - for (obj=(struct ObjectUnit *)gv->selobjects.first; - obj->n.next!=NULL; obj=(struct ObjectUnit *)obj->n.next) { - - if (obj->lnkfile->type != ID_SHAREDOBJ && - pattern_match(filepattern,obj->lnkfile->filename)) { - for (sec=(struct Section *)obj->sections.first; - sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { - if (sec->lnksec==NULL - && patternlist_match(secpatterns,sec->name)) { - /* File name and section name are matching the patterns, - so try to merge and check alignments */ - uint8_t f; - - if ((f = cmpsecflags(gv,ls,sec)) == 0xff) { - /* no warning, because the linker-script should know... */ - f = ls->flags ? ls->flags : sec->flags; - } - merge_sec_attrs(ls,sec,f&~SF_PORTABLE_MASK); - sec->lnksec = ls; /* will be reset for phase 2 */ - } - } - } - } - free_patterns(filepattern,secpatterns); - } - else { - /* A data command (BYTE, SHORT, etc.) defined an alignment */ - if (patalign > ls->alignment) - ls->alignment = patalign; - } - } - if (ls->ld_flags & LSF_USED) error(81,ls->name); /* multiple use of section in linker script */ - /* align this section to the maximum required alignment */ - align_address(ls->relocmem,ls->destmem,ls->alignment); ls->base = ls->relocmem->current; ls->copybase = ls->destmem->current; ls->ld_flags |= LSF_USED; - /* reset lnksec pointers for phase 2 */ - for (obj=(struct ObjectUnit *)gv->selobjects.first; - obj->n.next!=NULL; obj=(struct ObjectUnit *)obj->n.next) { - if (obj->lnkfile->type!=ID_SHAREDOBJ && !listempty(&obj->sections)) { - for (sec=(struct Section *)obj->sections.first; - sec->n.next!=NULL; sec=(struct Section *)sec->n.next) - sec->lnksec = NULL; - } - } - /* The linker scripts create an empty dummy section and adds */ /* it as the first section into the LinkedSection's list. */ /* Its purpose is to keep all linker script symbols. */ @@ -1618,34 +1886,40 @@ void linker_join(struct GlobalVars *gv) sec->va = ls->relocmem->current; } - /* Phase 2: read next patterns and merge matching sections for real */ - while ((sec = next_pattern(gv,&filepattern,&secpatterns)) != NULL) { + /* read next file/section name patterns and merge matching sections */ + while ((sec = next_pattern(gv,&pat)) != NULL) { if (sec == VALIDPAT) { - /* For each pattern, merge ST_CODE first, then ST_DATA and */ - /* ST_UDATA at last, to keep uninitialized sections together. */ - for (stype=0; stype<=ST_LAST; stype++) { - for (obj=(struct ObjectUnit *)gv->selobjects.first; - obj->n.next!=NULL; obj=(struct ObjectUnit *)obj->n.next) { - - if (obj->lnkfile->type != ID_SHAREDOBJ && - pattern_match(filepattern,obj->lnkfile->filename)) { - sec = (struct Section *)obj->sections.first; - while (nextsec = (struct Section *)sec->n.next) { - if (sec->lnksec==NULL && sec->type==stype && - patternlist_match(secpatterns,sec->name)) { - /* File name and section name are matching the patterns, - so join it into the current LinkedSection. */ - merge_ld_section(gv,stype,ls,sec); - } - sec = nextsec; + struct Section **msecs = NULL; + int mcnt = 0; /* number of matches for these patterns */ + + for (obj=(struct ObjectUnit *)gv->selobjects.first; + obj->n.next!=NULL; obj=(struct ObjectUnit *)obj->n.next) { + + if (obj->lnkfile->type != ID_SHAREDOBJ && + pattern_match(pat.fmatch,obj->lnkfile->filename) && + !patternlist_match(pat.fexclude,obj->lnkfile->filename)) { + /* object's file name matches, walk through its sections */ + + sec = (struct Section *)obj->sections.first; + while (nextsec = (struct Section *)sec->n.next) { + if (sec->lnksec==NULL && + patternlist_match(pat.smatch,sec->name) && + !patternlist_match(pat.sexclude,sec->name) && + !garbage_collected(gv,&pat,sec) && + !(ls->flags&sec->flags&SF_LINKONCE)) { + /* section name matches as well and section must not be + ignored, so add to merge list */ + msecs = store_msect(mcnt++,sec); } + sec = nextsec; } } } - free_patterns(filepattern,secpatterns); + sort_and_merge_sections(gv,&pat,ls,msecs,mcnt); + free_patterns(&pat); } else { /* merge art. section created by a data command */ - merge_ld_section(gv,~0,ls,sec); + merge_ld_section(gv,&pat,ls,sec); if (ls->type == ST_UNDEFINED) ls->type = ST_DATA; /* sect. becomes data due to data elements */ ls->flags |= SF_ALLOC; /* @@@ data should allocate the section */ @@ -1690,8 +1964,14 @@ void linker_join(struct GlobalVars *gv) addtail(&maxls->sections,remnode(&sec->n)); } else { - /* Section was not recognized by target linker script */ - error(64,getobjname(obj),sec->name); + if (sec->internal_flags & ILF_BADADDRINVA) { + /* No space to fit section into memory region */ + error(158,getobjname(obj),sec->name,(unsigned long long)sec->va); + } + else { + /* Section was not recognized by target linker script */ + error(64,getobjname(obj),sec->name); + } /* kill unallocated common symbols */ for (i=0; iuse_ldscript */ /* Default linkage rules. Link all code, all data, all bss. */ - unsigned long va = gv->start_addr; + lword va = gv->start_addr; bool baseincr = (fff[gv->dest_format]->flags&FFF_BASEINCR) != 0; struct LinkedSection *ls,*newls; struct list seclist; @@ -1734,7 +2014,7 @@ void linker_join(struct GlobalVars *gv) merge_seclist(gv,&seclist); } else { - /* Join sections, beginning with ST_CODE, then ST_DATA and ST_UDATA. */ + /* Merge sections, beginning with ST_CODE, then ST_DATA and ST_UDATA. */ for (stype=0; stype<=ST_LAST; stype++) { /* collect all sections of current type */ @@ -1789,9 +2069,15 @@ void linker_join(struct GlobalVars *gv) insertbefore(&sec->n,&firstbss->n); else addtail(&newls->sections,&sec->n); + sec->lnksec = newls; } remnode(&ls->n); free(ls); + + /* recalculate the appromixate section size, assuming a base of 0 */ + for (sec=(struct Section *)newls->sections.first,newls->size=0; + sec->n.next!=NULL; sec=(struct Section *)sec->n.next) + newls->size += sec->size + align(newls->size,sec->alignment); break; } } @@ -1802,6 +2088,7 @@ void linker_join(struct GlobalVars *gv) for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { ls->base = ls->copybase = va; + ls->size = ls->filesize = 0; for (sec=(struct Section *)ls->sections.first; sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { @@ -1819,7 +2106,7 @@ void linker_join(struct GlobalVars *gv) /* allocate COMMON symbols, if required */ if (is_common_sec(gv,sec) && (!gv->dest_object || gv->alloc_common)) { - unsigned long n = allocate_common(gv,sec,ls->base+ls->size); + unsigned long n = allocate_common(gv,TRUE,sec,ls->base+ls->size); ls->size += n; if (baseincr) @@ -1901,8 +2188,8 @@ void linker_mapfile(struct GlobalVars *gv) sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { if (sec->obj == obj) { /* section came from this object? */ if (!is_common_sec(gv,sec)) { - fprintf(gv->map_file,"%c %s %lx(%lx)",sep,sec->name, - sec->va,sec->size); + fprintf(gv->map_file,"%c %s %llx(%lx)",sep,sec->name, + (unsigned long long)sec->va,sec->size); sep = ','; } } @@ -1925,7 +2212,8 @@ void linker_mapfile(struct GlobalVars *gv) if (!(ls->size==0 && listempty(&ls->relocs) && listempty(&ls->symbols) && !(ls->ld_flags & LSF_PRESERVE))) { fprintf(gv->map_file,"------------------------------\n" - " %08lx %s (size %lx",ls->copybase,ls->name,ls->size); + " %08llx %s (size %lx", /* @@@ FIXME */ + (unsigned long long)ls->copybase,ls->name,ls->size); if (ls->filesize < ls->size) fprintf(gv->map_file,", allocated %lx",ls->filesize); fprintf(gv->map_file,")\n"); @@ -1933,8 +2221,10 @@ void linker_mapfile(struct GlobalVars *gv) for (sec=(struct Section *)ls->sections.first; sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { if (sec->obj!=NULL && !is_ld_script(sec->obj)) { - fprintf(gv->map_file," %08lx - %08lx %s(%s)\n", - sec->va,sec->va+sec->size,sec->obj->objname,sec->name); + fprintf(gv->map_file," %08llx - %08llx %s(%s)\n", + (unsigned long long)sec->va, + (unsigned long long)sec->va+sec->size, + sec->obj->objname,sec->name); } } } @@ -1950,7 +2240,9 @@ void linker_copy(struct GlobalVars *gv) struct LinkedSection *ls,*maxls=NULL; struct Section *sec; unsigned long maxsize = 0; - struct Symbol *sym; + struct Symbol **abs_ptr_array = NULL; + int i,abs_cnt=0; + struct Symbol *sym,**p; struct ObjectUnit *obj; if (gv->trace_file) @@ -1980,7 +2272,7 @@ void linker_copy(struct GlobalVars *gv) if (ls->data && sec->data) { /* copy section contents, fill gaps */ section_fill(gv,ls->data,lastsecend,sec->filldata, - sec->offset-lastsecend); + (long)(sec->offset-lastsecend)); section_copy(gv,ls->data,sec->offset,sec->data,sec->size); lastsecend = sec->offset + sec->size; } @@ -2009,40 +2301,78 @@ void linker_copy(struct GlobalVars *gv) } } - if (gv->map_file) { + if (gv->map_file!=NULL || gv->sym_file!=NULL) { /* print section's symbols to map file, sorted by address */ - struct Symbol **sym_ptr_array,**p; - int cnt = 0; + struct Symbol **sym_ptr_array; + int cnt,acnt; /* count symbols in this section, then sort them by address */ - for (sym=(struct Symbol *)ls->symbols.first; - sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) - cnt++; - if (cnt > 0) { + for (cnt=0,acnt=0,sym=(struct Symbol *)ls->symbols.first; + sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { + if (sym->type == SYM_ABS) + acnt++; + else + cnt++; + } + + if (acnt) { + abs_ptr_array = re_alloc(abs_ptr_array,(abs_cnt+acnt)*sizeof(void *)); + for (sym=(struct Symbol *)ls->symbols.first; sym->n.next!=NULL; + sym=(struct Symbol *)sym->n.next) { + if (sym->type == SYM_ABS) { + abs_ptr_array[abs_cnt++] = sym; + if (--acnt == 0) + break; + } + } + } + + if (cnt) { sym_ptr_array = alloc(cnt * sizeof(void *)); for (sym=(struct Symbol *)ls->symbols.first,p=sym_ptr_array; - sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) - *p++ = sym; + sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { + if (sym->type != SYM_ABS) + *p++ = sym; + } if (cnt > 1) qsort(sym_ptr_array,cnt,sizeof(void *),sym_addr_cmp); - fprintf(gv->map_file,"\nSymbols of %s:\n",ls->name); - for (p=sym_ptr_array; cnt>0; p++,cnt--) - print_symbol(gv,gv->map_file,*p); + if (gv->map_file) { + fprintf(gv->map_file,"\nSymbols of %s:\n",ls->name); + for (p=sym_ptr_array,i=0; imap_file,*p); + } + + if (gv->sym_file) { + /* output symbol mapping in given format */ + for (p=sym_ptr_array,i=0; itype==SYM_ABS || (*p)->type==SYM_RELOC) { + if ((gv->sym_file_flags & SFF_NOLOCAL) && + (*p)->bind==SYMB_LOCAL) + continue; /* no locals */ + + if (gv->sym_file_flags & SFF_VALFIRST) + fprintf(gv->sym_file,gv->sym_file_format, + (unsigned long long)(*p)->value,(*p)->name); + else if (gv->sym_file_flags & SFF_VALSECOND) + fprintf(gv->sym_file,gv->sym_file_format, + (*p)->name,(unsigned long long)(*p)->value); + fputc('\n',gv->sym_file); + } + } + } free(sym_ptr_array); } } + } - if (gv->vice_file) { - /* Label to address mapping for the VICe emulator */ - for (sym=(struct Symbol *)ls->symbols.first; - sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { - if (sym->type==SYM_ABS || sym->type==SYM_RELOC) { - fprintf(gv->vice_file,"al C:%04x .%s\n", - (unsigned)sym->value,sym->name); - } - } - } + if (gv->map_file && abs_cnt) { + fprintf(gv->map_file,"\nAbsolute symbols:\n"); + if (abs_cnt > 1) + qsort(abs_ptr_array,abs_cnt,sizeof(void *),sym_addr_cmp); + for (p=abs_ptr_array,i=0; imap_file,*p); + free(abs_ptr_array); } if (gv->map_file) @@ -2109,7 +2439,8 @@ void linker_relocate(struct GlobalVars *gv) lword a = 0; rel->offset += sec->offset; - rel->addend += rel->relocsect.ptr->offset; + if (rel->rtype != R_MEMID) + rel->addend += rel->relocsect.ptr->offset; rel->relocsect.lnk = rel->relocsect.ptr->lnksec; switch (rel->rtype) { @@ -2125,8 +2456,8 @@ void linker_relocate(struct GlobalVars *gv) case R_LOCALPC: /* resolve relative relocs from the same section */ if (rel->relocsect.lnk == ls) { - a = ((lword)rel->relocsect.lnk->base + rel->addend) - - ((lword)ls->base + rel->offset); + a = (rel->relocsect.lnk->base + rel->addend) - + (ls->base + rel->offset); a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2140,12 +2471,27 @@ void linker_relocate(struct GlobalVars *gv) } break; + case R_MEMID: /* destination's memory id (bank) */ + if (!gv->dest_object) { + lword id = rel->relocsect.lnk->relocmem ? + rel->relocsect.lnk->relocmem->id:MEM_NOID; + if (id == MEM_NOID) { + error(165,getobjname(sec->obj),sec->name, + rel->offset-sec->offset,rel->relocsect.lnk->name, + rel->relocsect.lnk->name); + id = 0; + } + a = id + rel->addend; + a = writesection(gv,ls->data,rel->offset,rel,a); + keep = FALSE; + } + break; + case R_GOT: /* GOT offset */ case R_GOTOFF: if (!gv->dest_object) { if (gotbase) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - gotbase->value; + a = rel->relocsect.lnk->base + rel->addend - gotbase->value; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2158,8 +2504,7 @@ void linker_relocate(struct GlobalVars *gv) if (!gv->dest_object) { /* resolve base-relative relocation for executable file */ if (sdabase) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - sdabase->value; + a = rel->relocsect.lnk->base + rel->addend - sdabase->value; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2168,12 +2513,11 @@ void linker_relocate(struct GlobalVars *gv) } break; - case R_SD2: /* _SDA2_BASE_ relative reference */ + case RPPC_SD2: /* _SDA2_BASE_ relative reference */ if (!gv->dest_object) { /* resolve base-relative relocation for executable file */ if (sda2base) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - sda2base->value; + a = rel->relocsect.lnk->base + rel->addend - sda2base->value; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2182,7 +2526,7 @@ void linker_relocate(struct GlobalVars *gv) } break; - case R_SD21: /* PPC-EABI base rel. reference */ + case RPPC_SD21: /* PPC-EABI base rel. reference */ if (!gv->dest_object) { /* resolve base-relative relocation for executable file */ const char *secname = rel->relocsect.lnk->name; @@ -2191,8 +2535,7 @@ void linker_relocate(struct GlobalVars *gv) if (!strcmp(secname,sdata_name) || !strcmp(secname,sbss_name)) { if (sdabase) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - sdabase->value; + a = rel->relocsect.lnk->base + rel->addend - sdabase->value; *(ls->data+rel->offset+1) |= 13; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; @@ -2203,8 +2546,7 @@ void linker_relocate(struct GlobalVars *gv) else if (!strcmp(secname,sdata2_name) || !strcmp(secname,sbss2_name)) { if (sda2base) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - sda2base->value; + a = rel->relocsect.lnk->base + rel->addend - sda2base->value; *(ls->data+rel->offset+1) |= 2; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; @@ -2214,7 +2556,7 @@ void linker_relocate(struct GlobalVars *gv) } else if (!strcmp(secname,".PPC.EMB.sdata0") || !strcmp(secname,".PPC.EMB.sbss0")) { - a = (lword)rel->relocsect.lnk->base + rel->addend; + a = rel->relocsect.lnk->base + rel->addend; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2227,12 +2569,11 @@ void linker_relocate(struct GlobalVars *gv) } break; - case R_MOSDREL: /* __r13_init rel. reference */ + case RPPC_MOSDREL: /* __r13_init rel. reference */ if (!gv->dest_object) { /* resolve base-relative relocation for executable file */ if (r13init) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - r13init->value; + a = rel->relocsect.lnk->base + rel->addend - r13init->value; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2241,14 +2582,13 @@ void linker_relocate(struct GlobalVars *gv) } break; - case R_AOSBREL: /* .data rel. reference */ + case RPPC_AOSBREL: /* .data rel. reference */ if (!gv->dest_object) { /* resolve base-relative relocation for executable file */ struct LinkedSection *datals; if (datals = find_lnksec(gv,data_name,0,0,0,0)) { - a = (lword)rel->relocsect.lnk->base + - rel->addend - datals->base; + a = rel->relocsect.lnk->base + rel->addend - datals->base; a = writesection(gv,ls->data,rel->offset,rel,a); keep = FALSE; } @@ -2260,8 +2600,8 @@ void linker_relocate(struct GlobalVars *gv) } break; - case R_ABS: case R_NONE: + case R_ABS: break; default: @@ -2291,6 +2631,7 @@ void linker_relocate(struct GlobalVars *gv) /* resolve, fix and copy x-references */ /*------------------------------------*/ while (xref = (struct Reloc *)remhead(&sec->xrefs)) { + struct LinkedSection *refls = NULL; struct Symbol *xdef; int err_no = 0; lword a = 0; @@ -2314,16 +2655,26 @@ void linker_relocate(struct GlobalVars *gv) } else if (xdef->type == SYM_RELOC) { - if (xdef->relsect->lnksec == NULL) { + if ((refls = xdef->relsect->lnksec) == NULL) { /* Cannot resolve reference to , because section was not recognized by the linker script */ - error(112,getobjname(sec->obj),sec->name,xref->offset, - xref->xrefname,xdef->relsect->name); + error(112,getobjname(sec->obj),sec->name, + xref->offset-sec->offset,xref->xrefname, + xdef->relsect->name); } else { - lword symoffset = xdef->value - - (lword)xdef->relsect->lnksec->base; + lword symoffset; + + if (refls!=ls && + (refls->ld_flags & ls->ld_flags & LSF_NOXREFS)) { + /* reference between overlayed sections (NOCROSSREFS) */ + print_function_name(sec,xref->offset); + error(159,getobjname(sec->obj),sec->name, + xref->offset-sec->offset,ls->name,refls->name, + xdef->name); + } + symoffset = xdef->value - refls->base; a = symoffset + xref->addend; switch (xref->rtype) { @@ -2338,12 +2689,12 @@ void linker_relocate(struct GlobalVars *gv) case R_PC: /* PC relative reference to relocatable symbol */ - if (xdef->relsect->lnksec != ls) { + if (refls != ls) { make_reloc = TRUE; } else { a = (xdef->value + xref->addend) - - ((lword)sec->lnksec->base + (lword)xref->offset); + (sec->lnksec->base + xref->offset); err_no = 28; } break; @@ -2355,6 +2706,24 @@ void linker_relocate(struct GlobalVars *gv) make_reloc = TRUE; break; + case R_MEMID: + /* reference to symbol's destination memory id (bank) */ + err_no = 166; + if (!gv->dest_object) { + lword id = refls->relocmem?refls->relocmem->id:MEM_NOID; + + if (id == MEM_NOID) { + error(165,getobjname(sec->obj),sec->name, + xref->offset-sec->offset,refls->name, + xdef->name); + id = 0; + } + a = id + xref->addend; + } + else /* keep as reference - do not turn into reloc */ + addtail(&ls->xrefs,&xref->n); + break; + case R_GOT: /* _GLOBAL_OFFSET_TABLE_ relative reference to an object's pointer slot in .got */ @@ -2386,7 +2755,7 @@ void linker_relocate(struct GlobalVars *gv) make_reloc = TRUE; break; - case R_SD2: + case RPPC_SD2: /* _SDA2_BASE_ relative reference to relocatable symbol */ err_no = 36; if (!gv->dest_object) { @@ -2400,11 +2769,11 @@ void linker_relocate(struct GlobalVars *gv) make_reloc = TRUE; break; - case R_SD21: + case RPPC_SD21: /* PPC-EABI: base relative reference via base-reg 0,2 or 13 */ err_no = 36; if (!gv->dest_object) { - const char *secname = xdef->relsect->lnksec->name; + const char *secname = refls->name; *(ls->data+xref->offset+1) &= 0xe0; if (!strcmp(secname,sdata_name) || @@ -2440,7 +2809,7 @@ void linker_relocate(struct GlobalVars *gv) make_reloc = TRUE; break; - case R_MOSDREL: + case RPPC_MOSDREL: err_no = 36; if (!gv->dest_object) { if (r13init) { @@ -2453,7 +2822,7 @@ void linker_relocate(struct GlobalVars *gv) make_reloc = TRUE; break; - case R_AOSBREL: + case RPPC_AOSBREL: err_no = 36; if (!gv->dest_object) { struct LinkedSection *datals; @@ -2486,13 +2855,16 @@ void linker_relocate(struct GlobalVars *gv) } } else - ierror("%s Referenced symbol has type %d",fn,(int)xdef->type); + ierror("%sReferenced symbol has type %d",fn,(int)xdef->type); if (make_reloc) { /* turn into a relocation */ + if (refls == NULL) + ierror("%sReferenced output section for %s does not exist", + fn,xdef->name); xref->addend = a; xref->xrefname = NULL; - xref->relocsect.lnk = xdef->relsect->lnksec; + xref->relocsect.lnk = refls; addtail(&ls->relocs,&xref->n); } else { @@ -2519,11 +2891,176 @@ void linker_relocate(struct GlobalVars *gv) } +static char **get_srcnames(struct GlobalVars *gv) +{ + struct LinkedSection *ls; + struct Section *sec; + char **nametab = NULL; + int entries; + + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + for (sec=(struct Section *)ls->sections.first; + sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { + struct SourceLines *sl; + char *fullname; + + if (sl = sec->srclines) { + do { + if (sl->path) { + fullname = alloc(strlen(sl->path)+strlen(sl->name)+2); + sprintf(fullname,"%s%c%s",sl->path,sl->path_sep,sl->name); + } + else + fullname = allocstring(sl->name); + + if (nametab) { + /* first check if name already exists in table */ + int i; + char *p; + + for (i=0; iflags & SLF_NOCASE) ? + !stricmp(nametab[i],fullname) : + !strcmp(nametab[i],fullname)) + break; + } + if (i >= entries) { + /* new table entry required */ + ++entries; + nametab = re_alloc(nametab,(entries+1)*sizeof(char **)); + nametab[i] = fullname; + nametab[i+1] = NULL; + } + else + free(fullname); + sl->nameidx = i; + } + else { /* first entry, create table */ + entries = 1; + nametab = alloc(2 * sizeof(char **)); + nametab[0] = fullname; + nametab[1] = NULL; + sl->nameidx = 0; + } + } + while (sl = sl->next); + } + } + } + return nametab; +} + + +static void write_mapfile_lineoffsets(struct GlobalVars *gv, + const char **nametab) +{ + struct LinkedSection *ls; + struct Section *sec; + int fileid; + + fprintf(gv->map_file,"\n\nSource file line offsets\n" + "------------------------"); + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + /* next output section, print name */ + fprintf(gv->map_file,"\n%s:\n",ls->name); + + for (sec=(struct Section *)ls->sections.first,fileid=-1; + sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { + struct SourceLines *sl; + + if (sl = sec->srclines) { + do { + srclinetype *lptr; + srcoffstype *optr; + unsigned n; + + for (n=sl->entries,lptr=sl->lines,optr=sl->offsets; n; + n--,optr++,lptr++) { + fprintf(gv->map_file," 0x%0*llx line %u",gv->bits_per_taddr/4, + (unsigned long long)*optr+sec->va,(unsigned)*lptr); + if ((int)sl->nameidx != fileid) { + fileid = sl->nameidx; + fprintf(gv->map_file," \"%s\"\n",nametab[fileid]); + } + else + fprintf(gv->map_file,"\n"); + } + } + while (sl = sl->next); + } + } + } +} + +static void write_line_offsets(struct GlobalVars *gv,const char **nametab) +{ + struct LinkedSection *ls; + struct Section *sec; + FILE *f; + int i; + + if (f = fopen(gv->lineoffsfile,"w")) { + /* first print list of source file names */ + for (i=0; nametab[i]!=NULL; i++) + fprintf(f,"%d:\"%s\"\n",i+1,nametab[i]); + + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + /* next output section, print name */ + fprintf(f,"\"%s\"\n",ls->name); + + for (sec=(struct Section *)ls->sections.first; + sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { + struct SourceLines *sl; + + if (sl = sec->srclines) { + do { + srclinetype *lptr; + srcoffstype *optr; + unsigned n; + + for (n=sl->entries,lptr=sl->lines,optr=sl->offsets; n; + n--,optr++,lptr++) { + fprintf(f,"%d:%u:0x%lx\n",(int)sl->nameidx+1,(unsigned)*lptr, + (unsigned long)*optr+sec->va); + } + } + while (sl = sl->next); + } + } + } + } + else + error(8,gv->lineoffsfile); /* cannot open */ +} + + void linker_write(struct GlobalVars *gv) { FILE *f; if (!gv->errflag) { /* no error? */ + if (gv->bits_per_tbyte!=8 && + !(fff[gv->dest_format]->flags&FFF_OUTWORDADDR)) { + /* bits per byte not supported by output format */ + error(113,gv->dest_name,fff[gv->dest_format]->tname, + (int)gv->bits_per_tbyte); + } + + /* source level debugging line-offset tables */ + if (gv->lineoffsfile!=NULL || gv->map_file!=NULL) { + const char **srcnames; + + if (srcnames = (const char **)get_srcnames(gv)) { + if (gv->map_file) + write_mapfile_lineoffsets(gv,srcnames); + if (gv->lineoffsfile) + write_line_offsets(gv,srcnames); + } + } + if (gv->trace_file) { if (!gv->output_sections) fprintf(gv->trace_file,"\nCreating output file %s (%s).\n", diff --git a/main.c b/main.c index 8f9292b..610f2cb 100644 --- a/main.c +++ b/main.c @@ -1,8 +1,8 @@ -/* $VER: vlink main.c V0.17a (26.06.22) +/* $VER: vlink main.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -139,6 +139,55 @@ static int flavours_cmp(const void *f1,const void *f2) } +static unsigned parse_symfile_format(struct GlobalVars *gv,const char *fmt) +{ + int mode=0,cnt=0; + char *p,*q; + + gv->sym_file_format = p = alloc(strlen(fmt)+4); + + while (*fmt) { + if (*fmt == '%') { + *p++ = *fmt++; + if (*fmt == '%') { /* "%%" is no extra argument */ + *p++ = *fmt++; + continue; + } + if (++cnt > 2) + return 0; /* maximum of two arguments (value, name) */ + q = strpbrk(fmt,"diouXxs%"); + if (q==NULL || *q=='%') + return 0; /* missing integer or string format code */ + if (mode == 0) + mode = (*q=='s') ? SFF_VALSECOND : SFF_VALFIRST; + else if ((mode==SFF_VALFIRST && *q!='s') || + (mode==SFF_VALSECOND && *q=='s')) + return 0; /* forbid integer/string format on both sides */ + + while (fmt < q) { + if (*fmt=='#' || *fmt=='-' || *fmt=='+' || *fmt==' ' || *fmt=='.' || + isdigit((unsigned char)*fmt)) + *p++ = *fmt++; + else + fmt++; /* ignore the rest, like 'h' or 'l' or '*' */ + } + + if (*q != 's') { + *p++ = 'l'; /* decimal argument is always long long */ + *p++ = 'l'; + } + *p++ = *q++; + fmt = q; + } + else + *p++ = *fmt++; + } + + *p = '\0'; + return mode; +} + + void cleanup(struct GlobalVars *gv) { if (gv->fail_on_warning && gv->warncnt) @@ -166,6 +215,7 @@ int main(int argc,const char *argv[]) gv->interp_path = DEFAULT_INTERP_PATH; gv->soname = NULL; gv->endianness = -1; /* endianness is unknown */ + gv->sym_file_format = "0x%08llx:%s"; /* initialize targets */ for (j=0; fff[j]; j++) { @@ -195,7 +245,7 @@ int main(int argc,const char *argv[]) gv->osec_base_name = NULL; if (argc<2 || (argc==2 && *argv[1]=='?')) { - show_usage(); + show_usage(gv,0); /* short help text */ exit(EXIT_SUCCESS); } @@ -203,8 +253,9 @@ int main(int argc,const char *argv[]) for (i=1; itname,buf)) break; } if (fff[j]) { gv->dest_format = (uint8_t)j; - argv[ii] = argv[i] = NULL; /* delete option for next pass */ + if (!strcmp(arg,"rawbin2")) { + argv[i] = NULL; + argv[ii] = "-multifile"; /* emulate old -brawbin2 */ + } + else + argv[ii] = argv[i] = NULL; /* delete option for next pass */ } } } @@ -241,8 +302,12 @@ int main(int argc,const char *argv[]) } else if (!strncmp(&argv[i][2],"roken",5)) goto unknown; - else - error(9,buf); /* invalid target format */ + else { /* invalid target format */ + if (!argv[i][2]) + error(9,argv[++i]); + else + error(9,&argv[i][2]); + } break; case 'c': @@ -270,6 +335,9 @@ int main(int argc,const char *argv[]) if (!strcmp(&argv[i][2],"ixunnamed")) { gv->fix_unnamed = TRUE; /* assign a name to unnamed sections */ } + else if (!strncmp(&argv[i][2],"ill",3)) { /* @@@ -fill in rawbin */ + goto unknown; + } else { /* set a flavour */ const char *name,**fl; @@ -300,7 +368,7 @@ int main(int argc,const char *argv[]) case 'h': if (!argv[i][2]) { - show_usage(); /* help text */ + show_usage(gv,1); /* verbose help text */ exit(EXIT_SUCCESS); } else goto unknown; @@ -317,8 +385,12 @@ int main(int argc,const char *argv[]) gv->keep_sect_order = TRUE; break; - case 'l': /* library specifier */ - if (buf = get_option_arg(argc,argv,&i)) { + case 'l': + if (!strcmp(&argv[i][2],"ineoffsets")) { + gv->lineoffsfile = get_arg(argc,argv,&i); + } + else if (buf = get_option_arg(argc,argv,&i)) { + /* library specifier -l */ ifn = alloc(sizeof(struct InputFile)); ifn->name = buf; ifn->lib = TRUE; @@ -347,6 +419,8 @@ int main(int argc,const char *argv[]) gv->auto_merge = TRUE; else if (!strcmp(&argv[i][2],"type")) gv->merge_same_type = TRUE; + else if (!strcmp(&argv[i][2],"attr")) + gv->merge_same_attr = TRUE; else if (!strcmp(&argv[i][2],"all")) gv->merge_all = TRUE; else if (!strcmp(&argv[i][2],"ultibase")) @@ -373,6 +447,10 @@ int main(int argc,const char *argv[]) gv->osec_base_name = &argv[i][6]; gv->output_sections = TRUE; /* output each section as a file */ } + else if (!strcmp(&argv[i][2],"be")) + gv->output_le = FALSE; /* output target-bytes in BE order */ + else if (!strcmp(&argv[i][2],"le")) + gv->output_le = TRUE; /* output target-bytes in LE order */ else if (!strcmp(&argv[i][2],"sec")) gv->output_sections = TRUE; /* output each section as a file */ else if (strncmp(&argv[i][2],"s9-",3) && @@ -415,6 +493,22 @@ int main(int argc,const char *argv[]) gv->soname = get_arg(argc,argv,&i); else if (!strcmp(&argv[i][2],"tatic")) /* -static */ gv->dynamic = FALSE; + else if (!strcmp(&argv[i][2],"ymfile") && + (buf = get_arg(argc,argv,&i)) != NULL) { + /* -symfile */ + gv->sym_file = fopen(buf,"w"); + } + else if (!strcmp(&argv[i][2],"ymfmt")) { /* -symfmt */ + unsigned mode = parse_symfile_format(gv,get_arg(argc,argv,&i)); + if (!mode) + error(157); /* bad symfile format */ + gv->sym_file_flags |= mode; + } + else if (!strncmp(&argv[i][2],"ymctrl=",7)) { /* -symctrl */ + unsigned ctrl; + if (sscanf(argv[i]+9,"%u",&ctrl) == 1) + gv->sym_file_flags |= ctrl & ~(SFF_VALFIRST|SFF_VALSECOND); + } else goto unknown; break; @@ -431,14 +525,15 @@ int main(int argc,const char *argv[]) add_symnames(&gv->undef_syms,buf,0); break; - case 'v': /* show version and target info */ + case 'v': if (!strcmp(&argv[i][2],"icelabels") && (buf = get_arg(argc,argv,&i)) != NULL) { - gv->vice_file = fopen(buf,"w"); + gv->sym_file = fopen(buf,"w"); + gv->sym_file_format = "al C:%04llx .%s"; } else { if (argv[i][2]) goto unknown; - show_version(); + show_version(); /* -v : show version and target info */ printf("Standard library path: " #ifdef LIBPATH LIBPATH @@ -654,6 +749,8 @@ int main(int argc,const char *argv[]) addtail(&gv->inputlist,&ifn->n); } } + if (!(gv->sym_file_flags & (SFF_VALFIRST|SFF_VALSECOND))) + gv->sym_file_flags |= SFF_VALFIRST; /* add default library search path at the end of the list */ if (stdlib) { @@ -679,11 +776,11 @@ int main(int argc,const char *argv[]) linker_dynprep(gv); /* prepare for dynamic linking */ linker_sectrefs(gv); /* find all referenced sections from the start */ linker_gcsects(gv); /* section garbage collection (gc_sects) */ - linker_join(gv); /* join sections with same name and type */ + linker_merge(gv); /* merge sections by linker script or by name/type */ linker_mapfile(gv); /* mapfile output */ linker_copy(gv); /* copy section contents and fix symbol offsets */ linker_delunused(gv);/* delete empty/unused sects. without relocs/symbols */ - linker_relocate(gv); /* relocate addresses in joined sections */ + linker_relocate(gv); /* relocate addresses in merged output sections */ linker_write(gv); /* write output file in selected target format */ linker_cleanup(gv); diff --git a/make.rules b/make.rules index 9712b60..a9076e1 100644 --- a/make.rules +++ b/make.rules @@ -9,11 +9,13 @@ vlinkobjects = $(DIR)/main.o $(DIR)/support.o $(DIR)/errors.o \ $(DIR)/t_aout.o $(DIR)/t_aoutnull.o $(DIR)/t_aoutm68k.o \ $(DIR)/t_aouti386.o $(DIR)/tosopts.o $(DIR)/t_aoutmint.o \ $(DIR)/t_ataritos.o $(DIR)/t_xfile.o $(DIR)/t_os9.o \ - $(DIR)/t_o65.o $(DIR)/t_rawbin.o $(DIR)/t_rawseg.o \ - $(DIR)/t_vobj.o + $(DIR)/t_o65.o $(DIR)/t_appleomf.o \ + $(DIR)/t_rawbin.o $(DIR)/t_rawseg.o $(DIR)/t_vobj.o + +all: $(TARGET) $(TARGET): $(DIR) $(vlinkobjects) - $(CC) $(CCOUT)$(DIR)/version.o $(COPTS) $(CONFIG) version.c + $(CC) $(CCOUT)$(DIR)/version.o $(CFLAGS) $(CONFIG) version.c $(LD) $(LDOUT)$(TARGET) $(LDOPTS) $(vlinkobjects) $(DIR)/version.o $(LIBS) clean: @@ -31,100 +33,103 @@ $(DIR): $(MD) $(DIR) $(DIR)/main.o: main.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) main.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) main.c $(DIR)/support.o: support.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) support.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) support.c $(DIR)/errors.o: errors.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) errors.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) errors.c -$(DIR)/linker.o: linker.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) linker.c +$(DIR)/linker.o: linker.c vlink.h config.h ar.h cpurelocs.h + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) linker.c $(DIR)/dir.o: dir.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) dir.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) dir.c $(DIR)/targets.o: targets.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) targets.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) targets.c $(DIR)/ar.o: ar.c vlink.h config.h ar.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) ar.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) ar.c $(DIR)/pmatch.o: pmatch.c vlink.h config.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) pmatch.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) pmatch.c $(DIR)/ldscript.o: ldscript.c vlink.h config.h ldscript.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) ldscript.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) ldscript.c $(DIR)/expr.o: expr.c vlink.h config.h ldscript.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) expr.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) expr.c $(DIR)/tosopts.o: tosopts.c vlink.h config.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) tosopts.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) tosopts.c $(DIR)/t_amigahunk.o: t_amigahunk.c vlink.h config.h ar.h amigahunks.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_amigahunk.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_amigahunk.c $(DIR)/elf.o: elf.c vlink.h config.h ar.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) elf.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) elf.c $(DIR)/t_elf32.o: t_elf32.c vlink.h config.h ar.h elf32.h elfcommon.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32.c -$(DIR)/t_elf32ppcbe.o: t_elf32ppcbe.c vlink.h config.h ar.h elf32.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32ppcbe.c +$(DIR)/t_elf32ppcbe.o: t_elf32ppcbe.c vlink.h config.h ar.h elf32.h elfcommon.h cpurelocs.h + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32ppcbe.c $(DIR)/t_elf32m68k.o: t_elf32m68k.c vlink.h config.h ar.h elf32.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32m68k.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32m68k.c $(DIR)/t_elf32jag.o: t_elf32jag.c vlink.h config.h ar.h elf32.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32jag.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32jag.c $(DIR)/t_elf32i386.o: t_elf32i386.c vlink.h config.h ar.h elf32.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32i386.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32i386.c $(DIR)/t_elf32arm.o: t_elf32arm.c vlink.h config.h ar.h elf32.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf32arm.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf32arm.c $(DIR)/t_elf64.o: t_elf64.c vlink.h config.h ar.h elf64.h elfcommon.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf64.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf64.c $(DIR)/t_elf64x86.o: t_elf64x86.c vlink.h config.h ar.h elf64.h elfcommon.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_elf64x86.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_elf64x86.c $(DIR)/t_aout.o: t_aout.c vlink.h config.h ar.h aout.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_aout.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_aout.c $(DIR)/t_aoutnull.o: t_aoutnull.c vlink.h config.h ar.h aout.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_aoutnull.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_aoutnull.c $(DIR)/t_aoutm68k.o: t_aoutm68k.c vlink.h config.h ar.h aout.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_aoutm68k.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_aoutm68k.c $(DIR)/t_aouti386.o: t_aouti386.c vlink.h config.h ar.h aout.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_aouti386.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_aouti386.c $(DIR)/t_aoutmint.o: t_aoutmint.c vlink.h config.h ar.h aout.h tosdefs.h aoutmint.h stabdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_aoutmint.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_aoutmint.c $(DIR)/t_ataritos.o: t_ataritos.c vlink.h config.h ar.h tosdefs.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_ataritos.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_ataritos.c $(DIR)/t_xfile.o: t_xfile.c vlink.h config.h xfile.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_xfile.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_xfile.c $(DIR)/t_os9.o: t_os9.c vlink.h config.h os9mod.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_os9.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_os9.c $(DIR)/t_o65.o: t_o65.c vlink.h config.h o65.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_o65.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_o65.c + +$(DIR)/t_appleomf.o: t_appleomf.c vlink.h config.h appleomf.h + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_appleomf.c $(DIR)/t_rawbin.o: t_rawbin.c vlink.h config.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_rawbin.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_rawbin.c $(DIR)/t_rawseg.o: t_rawseg.c vlink.h config.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_rawseg.c + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_rawseg.c -$(DIR)/t_vobj.o: t_vobj.c vlink.h config.h ar.h vobj.h - $(CC) $(CCOUT)$@ $(COPTS) $(CONFIG) t_vobj.c +$(DIR)/t_vobj.o: t_vobj.c vlink.h config.h ar.h vobj.h cpurelocs.h + $(CC) $(CCOUT)$@ $(CFLAGS) $(CONFIG) t_vobj.c diff --git a/pmatch.c b/pmatch.c index 67b8f91..1d87ac1 100644 --- a/pmatch.c +++ b/pmatch.c @@ -105,7 +105,7 @@ static bool portable_pattern_match(const char *mask, const char *name) else { q = 0; } - if ((tolower(*m) != tolower(*n)) && ((*m != '?') || q)) { + if ((*m != *n) && ((*m != '?') || q)) { if (!wild) return(TRUE); m = ma; diff --git a/support.c b/support.c index 4eb24ba..7bc5304 100644 --- a/support.c +++ b/support.c @@ -1,8 +1,8 @@ -/* $VER: vlink support.c V0.17a (17.04.22) +/* $VER: vlink support.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -55,7 +55,7 @@ void *alloczero(size_t size) } -const char *allocstring(const char *s) +char *allocstring(const char *s) /* allocate space for a single string */ /* @@@ this should be improved by some kind of string buffer */ { @@ -229,13 +229,15 @@ char *check_name(char *name) } -bool checkrange(lword val,bool sign,int size) -/* Checks if 'val' (signed or unsigned) fits into 'size' bits. +bool checkrange(lword val,int sign,int size) +/* Checks if 'val' (signed, unsigned or unknown) fits into 'size' bits. + sign==0 is unknown, checks signed and unsigned range, + sign==1 is signed, sign==2 is unsigned. Returns FALSE when 'val' is out of range! */ { - if (size) { - lword min = -(1LL<<(size-1)); - lword max = sign ? ((1LL<<(size-1))-1LL) : ((1LL<=0 && size<=sizeof(lword)*CHAR_BIT) { + lword min = sign!=2 ? -(1LL<<(size-1)) : 0; + lword max = sign==1 ? ((1LL<<(size-1))-1LL) : ((1LL<max) return FALSE; @@ -243,8 +245,8 @@ bool checkrange(lword val,bool sign,int size) return TRUE; } - ierror("checkrange(): size==0 (val=%lld)\n",val); - return FALSE; /* size==0 is illegal */ + ierror("checkrange(): size==%d (val=%lld)\n",size,(long long)val); + return FALSE; /* size is illegal */ } @@ -539,36 +541,101 @@ void writebf(bool be,void *dst,int fldsiz,int pos,int siz,lword d) } -lword readreloc(bool be,void *src,int pos,int siz) -/* Read value from a relocation bitfield. Difference is that there is no - known total field length, so pos/8 always defines the offset to the - first byte, no matter if LE or BE. Then follow ((pos&7)+siz+7)/8 bytes - read in LE or BE format. Note that the bits in a byte are counted from - highest to lowest for BE and from lowest to highest for LE! */ +lword readtbyte(struct GlobalVars *gv,void *src) { + uint8_t *s = src; + int len; + + if ((len = gv->octets_per_tbyte) > 1) { + lword val = 0; + + while (len--) { + val <<= 8; + val += (lword)*s++; + } + return val; + } + else + return *s; +} + + +void writetbyte(struct GlobalVars *gv,void *dest,lword val) +{ + uint8_t *d = dest; + int len; + + if ((len = gv->octets_per_tbyte) > 1) { + d += len; + while (len--) { + *(--d) = (uint8_t)val; + val >>= 8; + } + } + else + *d = (uint8_t)val; +} + + +void writetbytes(struct GlobalVars *gv,int n,void *dest,lword val) +{ + int be = gv->endianness != _LITTLE_ENDIAN_; + uint8_t *d = dest; + + if (be) + d += n * gv->octets_per_tbyte; /* we start with LSB, so move behind it */ + + while (n--) { + if (be) { + /* write right to left, for big-endian target */ + d -= gv->octets_per_tbyte; + writetbyte(gv,d,val); + } + else { + /* write left to right, for little-endian target */ + writetbyte(gv,d,val); + d += gv->octets_per_tbyte; + } + val >>= gv->bits_per_tbyte; + } +} + + +lword readreloc(struct GlobalVars *gv,void *src,int pos,int siz) +/* Read value from a relocation bitfield. The difference is that there is + no known total field length, so pos/B always defines the offset to the + first target byte, no matter if LE or BE (with B being the bits per byte). + Then follow (pos%B+siz+(B-1))/B bytes read in LE or BE format. + Note that the bits in a byte are counted from highest to lowest for BE + and from lowest to highest for LE! */ +{ + int be = gv->endianness != _LITTLE_ENDIAN_; + int b = gv->bits_per_tbyte; uint8_t *p = src; lword d = 0; int n; - /* advance to start-byte (MSB for BE, LSB for LE) */ - p += pos >> 3; + /* advance to start-tbyte (MSB for BE, LSB for LE) */ + p += (pos / b) * gv->octets_per_tbyte; - pos &= 7; - n = (pos + siz + 7) >> 3; /* number of bytes to read */ + pos %= b; + n = (pos + siz + b-1) / b; /* number of tbytes to read */ if (be) { while (n--) { - d <<= 8; - d |= (lword)*p++; + d <<= b; + d |= readtbyte(gv,p); + p += gv->octets_per_tbyte; } /* normalize BE */ - d >>= (8 - ((pos + siz) & 7)) & 7; + d >>= (b - ((pos + siz) % b)) % b; } else { - p += n; + p += n * gv->octets_per_tbyte; while (n--) { - d <<= 8; - d |= (lword)*(--p); + d <<= b; + p -= gv->octets_per_tbyte; + d |= readtbyte(gv,p); } /* normalize LE */ d >>= pos; @@ -579,50 +646,54 @@ lword readreloc(bool be,void *src,int pos,int siz) } -void writereloc(bool be,void *dst,int pos,int siz,lword d) -/* Write value to a relocation bitfield. Difference is that there is no - known total field length, so pos/8 always defines the offset to the - first byte, no matter if LE or BE. Then follow ((pos&7)+siz+7)/8 bytes - written in LE or BE format. Note that the bits in a byte are counted from - highest to lowest for BE and from lowest to highest for LE! */ +void writereloc(struct GlobalVars *gv,void *dst,int pos,int siz,lword d) +/* Write value to a relocation bitfield. The difference is that there is + no known total field length, so pos/B always defines the offset to the + first target byte, no matter if LE or BE (with B being the bits per byte). + Then follow (pos%B+siz+(B-1))/B bytes written in LE or BE format. + Note that the bits in a byte are counted from highest to lowest for BE + and from lowest to highest for LE! */ { + int be = gv->endianness != _LITTLE_ENDIAN_; + int b = gv->bits_per_tbyte; + lword tmask = makemask(b); uint8_t *p = dst; - uint8_t m,b; + lword m; int n,sh; - /* advance to start-byte (MSB for BE, LSB for LE) */ - p += pos >> 3; + /* advance to start-tbyte (MSB for BE, LSB for LE) */ + p += (pos / b) * gv->octets_per_tbyte; - pos &= 7; - n = (pos + siz + 7) >> 3; /* number of bytes to write */ + pos %= b; + n = (pos + siz + (b-1)) / b; /* number of tbytes to write */ if (be) { - p += n; /* we start with the LSB, so move behind it */ - sh = (8 - ((pos + siz) & 7)) & 7; + p += n * gv->octets_per_tbyte; /* we start with LSB, so move behind it */ + sh = (b - ((pos + siz) % b)) % b; } else sh = pos; - m = 0xff << sh; /* initial mask for LSB */ - d <<= sh; /* shift value to match bitfield */ + m = (tmask << sh) & tmask; /* initial mask for LSB */ + d <<= sh; /* shift value to match bitfield */ while (n--) { if (be) { /* write right to left, for big-endian target */ if (n == 0) - m &= (1 << (8 - pos)) - 1; /* apply mask for MSB */ - b = *(--p) & ~m; - *p = b | ((uint8_t)d & m); + m &= (1LL << (b - pos)) - 1; /* apply mask for MSB */ + p -= gv->octets_per_tbyte; + writetbyte(gv,p,(readtbyte(gv,p)&~m)|(d&m)); } else { /* write left to right, for little-endian target */ if (n == 0) - m &= (2 << ((pos + siz - 1) & 7)) - 1; /* apply mask for MSB */ - b = *p & ~m; - *p++ = b | ((uint8_t)d & m); + m &= (2LL << ((pos + siz - 1) % b)) - 1; /* apply mask for MSB */ + writetbyte(gv,p,(readtbyte(gv,p)&~m)|(d&m)); + p += gv->octets_per_tbyte; } - d >>= 8; - m = 0xff; + d >>= b; + m = tmask; } } @@ -694,16 +765,50 @@ void fwrite8(FILE *fp,uint8_t w) } +void fwrite16(int be,FILE *fp,uint16_t w) +{ + if (be) + fwrite16be(fp,w); + else + fwrite16le(fp,w); +} + + +void fwrite32(int be,FILE *fp,uint32_t w) +{ + if (be) + fwrite32be(fp,w); + else + fwrite32le(fp,w); +} + + void fwritetbyte(struct GlobalVars *gv,FILE *fp,lword w) /* write a target-byte */ { - int bpb = gv->bits_per_tbyte; + int o = gv->octets_per_tbyte; uint8_t buf[16]; - if (bpb > 16*sizeof(uint8_t)) - ierror("fwritetbyte(): bpb>128 (%d)",bpb); - writereloc(gv->endianness,buf,0,bpb,w); - fwritex(fp,buf,(bpb+7)/8); +#if 0 + if (o > 16*sizeof(uint8_t)) + ierror("fwritetbyte(): octets per tbyte > 16 (%d)",o); +#endif + if (gv->output_le) { + int i; + + for (i=0; i>= 8; + } + } + else { + while (o--) { + buf[o] = (uint8_t)w; + w >>= 8; + } + o = gv->octets_per_tbyte; + } + fwritex(fp,buf,o); } @@ -732,15 +837,40 @@ void fwrite_align(struct GlobalVars *gv,FILE *fp,uint32_t a,uint32_t n) } -void fwritegap(struct GlobalVars *gv,FILE *f,long bytes) +void fwritegap(struct GlobalVars *gv,FILE *f,long bytes,lword fill) { uint8_t buf[GAPBUFSIZE]; + size_t len = gv->octets_per_tbyte; /* @@@ never larger than GAPBUFSIZE! */ + + if (fill==0 || len==1) { + bytes = tbytes(gv,bytes); + memset(buf,(uint8_t)fill,GAPBUFSIZE); + do + fwritex(f,buf,bytes>GAPBUFSIZE?GAPBUFSIZE:bytes); + while ((bytes-=GAPBUFSIZE) > 0); + } + else { + writetbyte(gv,buf,fill); + while (bytes-- > 0) + fwritex(f,buf,len); + } +} + + +void fwriterawsect(struct GlobalVars *gv,FILE *fp,struct LinkedSection *ls) +{ + if (gv->bits_per_tbyte>8 && gv->output_le) { + /* write the octets inside a target-byte in little-endian order */ + size_t sz = ls->filesize; + uint8_t *p = ls->data; - bytes = tbytes(gv,bytes); - memset(buf,0,GAPBUFSIZE); - do - fwritex(f,buf,bytes>GAPBUFSIZE?GAPBUFSIZE:bytes); - while ((bytes-=GAPBUFSIZE) > 0); + while (sz--) { + fwritetbyte(gv,fp,readtbyte(gv,p)); + p += gv->octets_per_tbyte; + } + } + else /* target-bytes are available in big-endian order by default */ + fwritex(fp,ls->data,tbytes(gv,ls->filesize)); } @@ -748,9 +878,9 @@ void fwritefullsect(struct GlobalVars *gv,FILE *fp,struct LinkedSection *ls) /* write section contents and uninitialized part as zero */ { if (ls != NULL) { - fwritex(fp,ls->data,tbytes(gv,ls->filesize)); + fwriterawsect(gv,fp,ls); if (ls->filesize < ls->size) - fwritegap(gv,fp,ls->size - ls->filesize); + fwritegap(gv,fp,ls->size - ls->filesize,0); } } @@ -771,16 +901,16 @@ unsigned long elf_hash(const char *_name) } -unsigned long align(unsigned long addr,unsigned long alignment) +unsigned long align(lword addr,unsigned long alignment) /* return number of bytes required to achieve alignment */ { - unsigned long a = (1<= set memory attributes for hunk name to \n" + "-kick1 ensure Kickstart 1.x compatiblity\n"); +} + + /*****************************************************************/ /* Read ADOS / EHF */ @@ -539,17 +552,16 @@ static bool create_debuginfo(struct GlobalVars *gv,struct HunkInfo *hi, case 0x4c494e45: /* "LINE" - line/offset table and source name */ { - struct LineDebug *ldb; - uint32_t *lptr,*optr; + struct SourceLines *sl; + srclinetype *lptr; + srcoffstype *optr; - ldb = (struct LineDebug *)addtargetext(s,TGEXT_AMIGAOS, - SUBID_LINE,0, - sizeof(struct LineDebug)); len -= testword32(hi) + 3; /* number of longwords for file name */ - ldb->source_name = gethunkname(hi); - ldb->num_entries = len >> 1; - lptr = ldb->lines = alloc(ldb->num_entries * sizeof(uint32_t)); - optr = ldb->offsets = alloc(ldb->num_entries * sizeof(uint32_t)); + sl = newsourcelines(s,gethunkname(hi)); + sl->flags |= SLF_NOCASE; + allocsrclinetab(sl,len/2); + lptr = sl->lines; + optr = sl->offsets; while (len > 0) { *lptr++ = nextword32(hi); *optr++ = nextword32(hi) + base; @@ -817,7 +829,7 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) } } - /* Sections of an executable should never be joined, or */ + /* Sections of an executable should never be merged, or */ /* dangerous things will happen. */ if (!secname && hi.exec) { static unsigned uniqid[3]; @@ -870,6 +882,7 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) switch (w & 0xffff) { case HUNK_PPC_CODE: s->flags |= SF_EHFPPC; /* section contains PowerPC code */ + u->flags |= OUF_EHFPPC; /* object unit targets PowerPC */ case HUNK_CODE: s->type = ST_CODE; /* @@@@ amigahunk/ehf code is always writeable ??? */ @@ -958,12 +971,23 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) case HUNK_SYMBOL: if (s) { uint32_t xtype; + uint8_t bind; char *xname; nextword32(&hi); while (xtype = testword32(&hi)) { xname = gethunkname(&hi); - switch (xtype >>= 24) { + xtype >>= 24; + + if (!(u->flags & OUF_EHFPPC)) { + /* check for unofficial BFD-amigaos m68k extension */ + bind = (xtype & EXTF_LOCAL) ? SYMB_LOCAL : SYMB_GLOBAL; + if (xtype & EXTF_WEAK) + bind = SYMB_WEAK; + xtype &= ~(EXTF_WEAK|EXTF_LOCAL); + } + + switch (xtype) { /* Symbol Definitions */ case EXT_SYMB: /* local symbol definition (for debugging) */ @@ -972,11 +996,11 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) break; case EXT_DEF: /* global addr. symbol def. (requires reloc) */ create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_RELOC,SYMB_GLOBAL); + SYM_RELOC,bind); break; case EXT_ABS: /* global def. of absolute symbol */ create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_ABS,SYMB_GLOBAL); + SYM_ABS,bind); break; case EXT_RES: /* unsupported type, invalid since OS2.0 */ error(18,lf->pathname,xname,u->objname,EXT_RES); @@ -1018,13 +1042,13 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) /* ABSCOMMON was never used and only supported */ /* by the VERY old ALink linker on AmigaOS. */ create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_COMMON,SYMB_GLOBAL); + SYM_COMMON,bind); create_xrefs(gv,&hi,s,xname,R_ABS,32); break; case EXT_RELCOMMON: /* RELCOMMON was introduced in OS3.1. */ create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_COMMON,SYMB_GLOBAL); + SYM_COMMON,bind); create_xrefs(gv,&hi,s,xname,R_PC,32); break; @@ -1032,25 +1056,25 @@ static void readconv(struct GlobalVars *gv,struct LinkFile *lf) /* common symbol references, created for vbcc */ case EXT_DEXT32COMMON: create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_COMMON,SYMB_GLOBAL); + SYM_COMMON,bind); create_xrefs(gv,&hi,s,xname,R_SD,32); break; case EXT_DEXT16COMMON: create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_COMMON,SYMB_GLOBAL); + SYM_COMMON,bind); create_xrefs(gv,&hi,s,xname,R_SD,16); break; case EXT_DEXT8COMMON: create_xdef(gv,s,xname,(int32_t)nextword32(&hi), - SYM_COMMON,SYMB_GLOBAL); + SYM_COMMON,bind); create_xrefs(gv,&hi,s,xname,R_SD,8); break; default: /* unsupported HUNK_EXT sub type */ - if (xtype & 0x80000000) - error(20,lf->pathname,xname,u->objname,xtype>>24); + if (xtype & 0x80) + error(20,lf->pathname,xname,u->objname,xtype); else - error(18,lf->pathname,xname,u->objname,xtype>>24); + error(18,lf->pathname,xname,u->objname,xtype); } } nextword32(&hi); @@ -1332,7 +1356,7 @@ static void get_resident_sdrelocs(struct GlobalVars *gv) and put it into our own list of Resident relocs. */ remnode(&r->n); r->offset += sec->offset; - r->addend += xdef->value - (lword)xdef->relsect->lnksec->base; + r->addend += xdef->value - xdef->relsect->lnksec->base; addtail(&rrlist,&r->n); ++rrcnt; } @@ -1434,6 +1458,12 @@ static struct Symbol *ehf_findsymbol(struct GlobalVars *gv,struct Section *sec, { struct Symbol *sym,*found; uint32_t minmask = ~0; + uint16_t ofl; + + if (sec!=NULL && sec->obj!=NULL) + ofl = sec->obj->flags & OUF_EHFPPC; + else + ofl = ~0; for (sym=gv->symbols[elf_hash(name)%SYMHTABSIZE],found=NULL; sym!=NULL; sym=sym->glob_chain) { @@ -1465,13 +1495,11 @@ static struct Symbol *ehf_findsymbol(struct GlobalVars *gv,struct Section *sec, if (found) { if (sym->relsect) { if (sec) { - uint8_t f = sec->flags & SF_EHFPPC; - - /* we prefer symbols from a section which has the same CPU-flags - as the referer's section */ + /* we prefer symbols from an object which has the same CPU-flags + as the referer's object */ if (sym->type==SYM_RELOC && found->relsect && - (found->relsect->flags & SF_EHFPPC) != f && - (sym->relsect->flags & SF_EHFPPC) == f) + (found->relsect->obj->flags & OUF_EHFPPC) != ofl && + (sym->relsect->obj->flags & OUF_EHFPPC) == ofl) found = sym; } @@ -1815,28 +1843,38 @@ static void linedebug_hunks(struct GlobalVars *gv,FILE *f, { struct Section *sec = (struct Section *)ls->sections.first; struct Section *nextsec; - struct LineDebug *ldb; + struct SourceLines *sl; while (nextsec = (struct Section *)sec->n.next) { - if (ldb = (struct LineDebug *)sec->special) { + if (sl = sec->srclines) { do { - if (ldb->tgext.id==TGEXT_AMIGAOS && ldb->tgext.sub_id==SUBID_LINE) { - uint32_t i,*lptr,*optr; + srclinetype *lptr; + srcoffstype *optr; + char *srcname; + unsigned n; + + if (n = sl->entries) { + if (sl->path) { + srcname = alloc(strlen(sl->path)+strlen(sl->name)+2); + sprintf(srcname,"%s%c%s",sl->path,sl->path_sep,sl->name); + } + else + srcname = allocstring(sl->name); fwrite32be(f,HUNK_DEBUG); - fwrite32be(f,3 + strlen32(ldb->source_name) + - (ldb->num_entries<<1)); + fwrite32be(f,3+strlen32(srcname)+sl->entries*2); fwrite32be(f,(uint32_t)sec->offset); fwrite32be(f,0x4c494e45); /* "LINE" */ - hunk_name_len(gv,f,ldb->source_name); - for (i=0,lptr=ldb->lines,optr=ldb->offsets; - inum_entries; i++) { + hunk_name_len(gv,f,srcname); + free(srcname); + + for (lptr=sl->lines,optr=sl->offsets; n; n--) { fwrite32be(f,*lptr++); fwrite32be(f,*optr++); } } } - while (ldb = (struct LineDebug *)ldb->tgext.next); + while (sl = sl->next); } sec = nextsec; } @@ -2091,10 +2129,8 @@ static void writeobject(struct GlobalVars *gv,FILE *f,bool ehf) unsupp_symbols(ls); /* print unsupported symbol definitions */ /* line debug hunks */ - if (gv->strip_symbols < STRIP_DEBUG) { - if (checktargetext(ls,TGEXT_AMIGAOS,SUBID_LINE)) - linedebug_hunks(gv,f,ls); - } + if (gv->strip_symbols < STRIP_DEBUG) + linedebug_hunks(gv,f,ls); fwrite32be(f,HUNK_END); /* end of this section */ ls = nextls; @@ -2189,7 +2225,7 @@ static void writeexec(struct GlobalVars *gv,FILE *f) fix_reloc_addends(gv,ls); /* Work around a bug in AmigaOS LoadSeg() (up to dos.library V40), which - gets confused with completely uninitialized data-bss sections. */ + doesn't clear completely uninitialized data-bss sections. */ if (!(ls->flags&SF_UNINITIALIZED) && ls->filesize==0) ls->filesize = ls->size>4 ? 4 : ls->size; @@ -2211,19 +2247,29 @@ static void writeexec(struct GlobalVars *gv,FILE *f) ierror("writeexec(): Res.Relocs found: %d expected: %d\n",i,rrcnt); } else if (ls->flags & SF_UNINITIALIZED) { + if (kick1 && ls->size > 0x40000) + error(153,ls->name); /* warn about kick 1.x bug with bss > 256k */ fwrite32be(f,(ls->size+3)>>2); /* bss - size only */ } else { - fwrite32be(f,(ls->filesize+3)>>2); /* initialized section size */ - fwritex(f,ls->data,ls->filesize); /* write section contents */ - fwrite_align(gv,f,2,ls->filesize); + if (kick1) { /* no data-bss for kickstart 1.x */ + fwrite32be(f,(ls->size+3)>>2); /* complete section size */ + fwritefullsect(gv,f,ls); + fwrite_align(gv,f,2,ls->size); + } + else { + fwrite32be(f,(ls->filesize+3)>>2); /* initialized section size */ + fwritex(f,ls->data,ls->filesize); /* write section contents */ + fwrite_align(gv,f,2,ls->filesize); + } } /* relocation hunks */ - if (gv->reloctab_format==RTAB_SHORTOFF) + if (!kick1 && gv->reloctab_format==RTAB_SHORTOFF) reloc_hunk(gv,f,ls,HUNK_DREL32,R_ABS,32); /* HUNK_RELOC32SHORT */ reloc_hunk(gv,f,ls,HUNK_ABSRELOC32,R_ABS,32); - reloc_hunk(gv,f,ls,HUNK_RELRELOC32,R_PC,32); + if (!kick1) + reloc_hunk(gv,f,ls,HUNK_RELRELOC32,R_PC,32); unsupp_relocs(gv,ls); /* print unsupported relocations */ /* symbol table */ @@ -2241,10 +2287,8 @@ static void writeexec(struct GlobalVars *gv,FILE *f) unsupp_symbols(ls); /* print unsupported symbol definitions */ /* line debug hunks */ - if (gv->strip_symbols < STRIP_DEBUG) { - if (checktargetext(ls,TGEXT_AMIGAOS,SUBID_LINE)) - linedebug_hunks(gv,f,ls); - } + if (gv->strip_symbols < STRIP_DEBUG) + linedebug_hunks(gv,f,ls); fwrite32be(f,HUNK_END); /* end of this section */ ls = nextls; diff --git a/t_aout.c b/t_aout.c index a05b094..e676297 100644 --- a/t_aout.c +++ b/t_aout.c @@ -1,4 +1,4 @@ -/* $VER: vlink t_aout.c V0.16i (14.11.21) +/* $VER: vlink t_aout.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. @@ -22,7 +22,6 @@ static const char *aout_symnames[] = { #define PLTSYM 1 #define DYNAMICSYM 2 -static struct ar_info ai; /* for scanning library archives */ static uint8_t sectype[] = { N_TEXT, N_DATA, N_BSS }; static uint8_t weaktype[] = { N_WEAKT, N_WEAKD, N_WEAKB }; @@ -82,6 +81,7 @@ int aout_identify(struct FFFuncs *ff,char *name,struct aout_hdr *p, /* check a possible a.out file against the requirements, then */ /* return its type (object, library, shared object) */ { + struct ar_info ai; uint32_t mid = ff->id; bool arflag = FALSE; @@ -733,6 +733,8 @@ void aoutstd_read(struct GlobalVars *gv,struct LinkFile *lf, void aoutstd_readconv(struct GlobalVars *gv,struct LinkFile *lf) /* Read a.out executable / object / shared obj. with standard relocs */ { + struct ar_info ai; + if (lf->type == ID_LIBARCH) { if (ar_init(&ai,(char *)lf->data,lf->length,lf->filename)) { while (ar_extract(&ai)) { @@ -1064,14 +1066,14 @@ uint32_t aout_addrelocs(struct GlobalVars *gv,struct LinkedSection **ls, if (rel->rtype == R_SD) { /* GNU-binutiles (Amiga 68k) compatible implementation, .data-based */ if (rsec==2 && ls[1]!=NULL && ls[2]!=NULL) - a = (lword)(ls[2]->base - ls[1]->base) + rel->addend; /* .bss */ + a = (ls[2]->base - ls[1]->base) + rel->addend; /* .bss */ else a = rel->addend; /* .data */ } else if (rel->rtype == R_PC) - a = rel->addend - ((lword)ls[sec]->base + rel->offset); + a = rel->addend - (ls[sec]->base + rel->offset); else - a = (lword)ls[rsec]->base + rel->addend; + a = ls[rsec]->base + rel->addend; /* @@@ calculation for other relocs: jmptab,load-relative? */ writesection(gv,ls[sec]->data,rel->offset,rel,a); @@ -1094,7 +1096,7 @@ uint32_t aout_addrelocs(struct GlobalVars *gv,struct LinkedSection **ls, /* fix addend for a.out and write into the section */ if (rel->rtype == R_PC) - a = rel->addend - (lword)(ls[sec]->base + rel->offset); + a = rel->addend - (ls[sec]->base + rel->offset); else a = rel->addend; /* @@@ calculation for other relocs: baserel,jmptab,load-relative? */ @@ -1209,7 +1211,8 @@ void aout_pagedsection(struct GlobalVars *gv,FILE *f, if (ls[sec]) { fwritex(f,ls[sec]->data,ls[sec]->size); fwritegap(gv,f,aout_getpagedsize(gv,ls,sec) - - (sec ? ls[sec]->size : ls[sec]->size+sizeof(struct aout_hdr))); + (sec ? ls[sec]->size : ls[sec]->size+sizeof(struct aout_hdr)), + 0); } } @@ -1246,10 +1249,8 @@ void aout_writestrings(FILE *f,int be) { if (aoutstrlist.nextoffset > 4) { struct StrTabNode *stn; - uint32_t len; - write32(be,&len,aoutstrlist.nextoffset); - fwritex(f,&len,4); + fwrite32(be,f,aoutstrlist.nextoffset); while (stn = (struct StrTabNode *)remhead(&aoutstrlist.l)) fwritex(f,stn->str,strlen(stn->str)+1); } diff --git a/t_aouti386.c b/t_aouti386.c index 77af866..90b5cd1 100644 --- a/t_aouti386.c +++ b/t_aouti386.c @@ -62,6 +62,7 @@ struct FFFuncs fff_aoutbsdi386 = { NULL, NULL, NULL, + NULL, aout_headersize, aoutbsdi386_identify, aoutstd_readconv, @@ -96,6 +97,7 @@ struct FFFuncs fff_aoutpc386 = { NULL, NULL, NULL, + NULL, aout_headersize, aoutpc386_identify, aoutstd_readconv, diff --git a/t_aoutm68k.c b/t_aoutm68k.c index 57bb60e..fbf45d7 100644 --- a/t_aoutm68k.c +++ b/t_aoutm68k.c @@ -102,6 +102,7 @@ struct FFFuncs fff_aoutbsd68k = { NULL, NULL, NULL, + NULL, aout_headersize, aoutbsd68k_identify, aoutstd_readconv, @@ -136,6 +137,7 @@ struct FFFuncs fff_aoutbsd68k4k = { NULL, NULL, NULL, + NULL, aout_headersize, aoutbsd68k4k_identify, aoutstd_readconv, @@ -170,6 +172,7 @@ struct FFFuncs fff_aoutsun010 = { NULL, NULL, NULL, + NULL, aout_headersize, aoutsun010_identify, aoutstd_readconv, @@ -204,6 +207,7 @@ struct FFFuncs fff_aoutsun020 = { NULL, NULL, NULL, + NULL, aout_headersize, aoutsun020_identify, aoutstd_readconv, @@ -268,6 +272,7 @@ struct FFFuncs fff_aoutjaguar = { NULL, NULL, NULL, + NULL, aout_headersize, aoutjaguar_identify, /* NULL */ NULL, diff --git a/t_aoutmint.c b/t_aoutmint.c index db4a88a..2e3399d 100644 --- a/t_aoutmint.c +++ b/t_aoutmint.c @@ -1,4 +1,4 @@ -/* $VER: vlink t_aoutmint.c V0.16h (16.01.21) +/* $VER: vlink t_aoutmint.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. @@ -57,6 +57,7 @@ struct FFFuncs fff_aoutmint = { NULL, NULL, tos_options, + tos_printhelp, aout_headersize, aoutmint_identify, aoutstd_readconv, @@ -181,11 +182,13 @@ static void aoutmint_writeexec(struct GlobalVars *gv,FILE *f) /* write sections */ fwritex(f,sections[0]->data,sections[0]->filesize); fwritegap(gv,f, - (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize); + (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize, + 0); if (sections[1]) { fwritex(f,sections[1]->data,sections[1]->filesize); fwritegap(gv,f, - (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize); + (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize, + 0); } /* write a.out symbols */ diff --git a/t_aoutnull.c b/t_aoutnull.c index 7f76e33..941719f 100644 --- a/t_aoutnull.c +++ b/t_aoutnull.c @@ -58,6 +58,7 @@ struct FFFuncs fff_aoutnull = { NULL, NULL, NULL, + NULL, aout_headersize, aoutnull_identify, aoutstd_readconv, diff --git a/t_appleomf.c b/t_appleomf.c new file mode 100644 index 0000000..54fdbc7 --- /dev/null +++ b/t_appleomf.c @@ -0,0 +1,668 @@ +/* $VER: vlink t_appleomf.c V0.18 (23.12.24) + * + * This file is part of vlink, a portable linker for multiple + * object formats. + * Copyright (c) 2024 Frank Wille + */ + +#include "config.h" +#ifdef APPLE_OMF +#define T_APPLEOMF_C +#include "vlink.h" +#include "appleomf.h" + + +static void init(struct GlobalVars *,int); +static int options(struct GlobalVars *,int,const char **,int *); +static void printhelp(void); +static int identify(struct GlobalVars *,char *,uint8_t *,unsigned long,bool); +static void readconv(struct GlobalVars *,struct LinkFile *); +static int omf_targetlink(struct GlobalVars *,struct LinkedSection *, + struct Section *); +static struct Symbol *omf_lnksym(struct GlobalVars *,struct Section *, + struct Reloc *); +static void omf_setlnksym(struct GlobalVars *,struct Symbol *); +static unsigned long headersize(struct GlobalVars *); +static void writeobject(struct GlobalVars *,FILE *); +static void writeshared(struct GlobalVars *,FILE *); +static void writeexec(struct GlobalVars *,FILE *); + + +struct FFFuncs fff_appleomf = { + "appleomf", + NULL, + NULL, + init, + options, + printhelp, + headersize, + identify, + readconv, + NULL, + omf_targetlink, + NULL, + omf_lnksym, + omf_setlnksym, + NULL,NULL,NULL, + writeobject, + writeshared, + writeexec, + NULL,NULL, + OMF_BANKSIZE, + 0, /* no small data */ + 0, + 0, + RTAB_STANDARD,RTAB_STANDARD, + _LITTLE_ENDIAN_, + 24,1, + FFF_RELOCATABLE +}; + +static int outver = 1; /* OMF V1.0 is default */ +static int numlen = 4; /* defaults to 4 bytes per value */ +static int nsegs,lablen,extrastack; +static unsigned long maxsegsize; +static struct list segrelocs; + +/* linker symbols */ +static char dbrinit_name[] = "__DBR_init"; +#define DBRINIT 0 + + +static void init(struct GlobalVars *gv,int mode) +{ + if (mode==FFINI_DESTFMT && !maxsegsize) { + maxsegsize = fff[gv->dest_format]->page_size; + } + else if (mode == FFINI_MERGE) { + /* set target-specific flags on all selected sections first */ + struct ObjectUnit *obj; + struct Section *sec; + + for (obj=(struct ObjectUnit *)gv->selobjects.first; + obj->n.next!=NULL; obj=(struct ObjectUnit *)obj->n.next) { + if (obj->lnkfile->type != ID_SHAREDOBJ) { + for (sec=(struct Section *)obj->sections.first; + sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { + if (!(sec->flags & (SF_SMALLDATA|SF_DPAGE|SF_HUGEDATA))) { + /* section data-model flag by name */ + if (strstr(sec->name,".zero") || strstr(sec->name,".dpage")) + sec->flags |= SF_DPAGE; + else if (strstr(sec->name,".near")) + sec->flags |= SF_SMALLDATA; + else if (strstr(sec->name,".huge")) + sec->flags |= SF_HUGEDATA; + } + } + } + } + } +} + + +static int options(struct GlobalVars *gv,int argc,const char **argv,int *i) +{ + if (!strncmp(argv[*i],"-maxsegsize=",12)) { + int mss; + + if (sscanf(&argv[*i][12],"%i",&mss) == 1) { + maxsegsize = mss; + return 1; + } + } + else if (!strncmp(argv[*i],"-stack=",7)) { + if (sscanf(&argv[*i][7],"%i",&extrastack) == 1) + return 1; + else + extrastack = 0; + } + else if (!strncmp(argv[*i],"-version=",9)) { + if (sscanf(&argv[*i][9],"%i",&outver) == 1) { + if (outver<1 || outver>2) { + error(161,1,0); /* assuming 1.0 */ + outver = 1; + } + return 1; + } + } + return 0; +} + + +static void printhelp(void) +{ + printf("-maxsegsize=maximum size for segments with multiple sections\n" + "-stack= stack size to add to DP/Stack segment\n" + "-version= use OMF version 1 (default) or 2\n"); +} + + + +/*****************************************************************/ +/* Read OMF */ +/*****************************************************************/ + + +static int identify(struct GlobalVars *gv,char *name,uint8_t *p, + unsigned long plen,bool lib) +/* identify an XFile-format file */ +{ + return ID_UNKNOWN; /* @@@ no read-support at the moment */ +} + + +static void readconv(struct GlobalVars *gv,struct LinkFile *lf) +{ + ierror("readconv(): Can't read Apple OMF"); +} + + + +/*****************************************************************/ +/* Link OMF */ +/*****************************************************************/ + + +static int omf_targetlink(struct GlobalVars *gv,struct LinkedSection *ls, + struct Section *s) +/* returns 1, if target requires the combination of the two sections, */ +/* returns -1, if target doesn't want to combine them, */ +/* returns 0, if target doesn't care - standard linking rules are used. */ +{ + unsigned long ssize; + + if (s->lnksec) { + if (s->lnksec == ls) + return -1; /* shouldn't happen - section is already linked to ls */ + + /* Section is already in output-section lnksec, so returning -1 would + mean we merge with the full size of lnksec! */ + ssize = s->lnksec->size; + } + else + ssize = s->size; + + if ((ls->flags&(SF_DPAGE|SF_SMALLDATA|SF_HUGEDATA)) != + (s->flags&(SF_DPAGE|SF_SMALLDATA|SF_HUGEDATA))) + return -1; /* DP/Stack, Near or Huge segments must not merge with others */ + + if ((s->flags & SF_DPAGE) && ls->size+ssize>OMF_MAXDPSTKSIZE) + error(162,fff[gv->dest_format]->tname,ls->name, + (unsigned long)OMF_MAXDPSTKSIZE); + else if ((s->flags & SF_SMALLDATA) && ls->size+ssize>OMF_BANKSIZE) + error(162,fff[gv->dest_format]->tname,ls->name,(unsigned long)OMF_BANKSIZE); + else if (!(s->flags & SF_HUGEDATA) && ls->size+ssize>maxsegsize) + return -1; /* merging section would exceed the maximum segment size */ + + return 0; /* merge sections with same name and attributes */ +} + + +static struct Symbol *omf_lnksym(struct GlobalVars *gv,struct Section *sec, + struct Reloc *xref) +{ + struct Symbol *sym; + + if (!gv->dest_object && !gv->use_ldscript) { + if (!strcmp(dbrinit_name,xref->xrefname)) { /* __DBR_init */ + sym = addlnksymbol(gv,dbrinit_name,0, + SYM_ABS,SYMF_LNKSYM,SYMI_OBJECT,SYMB_GLOBAL,0); + sym->extra = DBRINIT; + return sym; /* new linker symbol created */ + } + } + return NULL; +} + + +static void omf_setlnksym(struct GlobalVars *gv,struct Symbol *xdef) +{ + if (xdef->flags & SYMF_LNKSYM) { + struct LinkedSection *ls; + + switch (xdef->extra) { + case DBRINIT: + /* base address of first Near (small-data) segment */ + if (ls = find_lnksec(gv,NULL,ST_DATA,SF_SMALLDATA,SF_SMALLDATA,0)) { + xdef->type = SYM_RELOC; + xdef->relsect = (struct Section *)ls->sections.first; + } + break; + } + } +} + + +/*****************************************************************/ +/* Write OMF */ +/*****************************************************************/ + + +static void omf_initwrite(struct GlobalVars *gv) +{ + struct LinkedSection *ls,*dpstk; + int i; + + /* assign OMF segment numbers 1..nsegs to valid sections, 0 to others */ + for (ls=(struct LinkedSection *)gv->lnksec.first,dpstk=NULL,nsegs=0; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + if ((ls->flags & SF_ALLOC) && ls->size!=0) { + ls->index = ++nsegs; /* OMF segment index */ + if (ls->type!=ST_CODE && (ls->flags & SF_DPAGE)) + dpstk = ls; /* remember last DP section for stack */ + } + else + ls->index = 0; /* section ignored */ + } + + if (extrastack) { + /* add stack space at the end of the last DirectPage/Stack segment */ + if (dpstk == NULL) { + dpstk = create_lnksect(gv,"stack",ST_DATA,SF_ALLOC|SF_DPAGE, + SP_READ|SP_WRITE,0,0); + dpstk->data = (uint8_t *)dpstk; /* any valid pointer, filesize is 0 */ + dpstk->index = ++nsegs; + } + dpstk->size += extrastack; + } + + initlist(&segrelocs); +} + + +static void reset_segrelocs(void) +{ + void *r; + + while (r = (OMFReloc *)remhead(&segrelocs)) + free(r); + initlist(&segrelocs); +} + + +static void collect_segrelocs(struct GlobalVars *gv,struct LinkedSection *ls) +{ + struct Reloc *rel; + struct RelocInsert *ri; + int i; + + reset_segrelocs(); + sort_relocs(&ls->relocs); + + for (rel=(struct Reloc *)ls->relocs.first; rel->n.next!=NULL; + rel=(struct Reloc *)rel->n.next) { + if (ri = rel->insert) { + int rsh = lshiftcnt(ri->mask); + lword vmask = makemask(ri->bsiz) << rsh; + + if (rel->rtype==R_ABS && ri->next==NULL && ri->bpos==0 && !(ri->bsiz&7) + && (ri->mask&vmask)==vmask && rel->relocsect.lnk->index) { + OMFReloc *omfr = alloc(sizeof(OMFReloc)); + + omfr->flags = 0; + omfr->fileno = 1; /* @@@ for plain load files */ + omfr->segno = rel->relocsect.lnk->index; + omfr->size = ri->bsiz / 8; + omfr->shift = -rsh; + omfr->offset = rel->offset; + omfr->addend = rel->addend; + addtail(&segrelocs,&omfr->n); + } + else + error(32,fff[gv->dest_format]->tname,reloc_name[rel->rtype], + (int)ri->bpos,(int)ri->bsiz,mtaddr(gv,ri->mask), + ls->name,rel->offset); + } + else + ierror("collect_segrelocs: missing RelocInsert for rtype %d at %s+%lu", + (int)rel->rtype,ls->name,rel->offset); + } +} + + +static void fwritesuper(struct GlobalVars *gv,FILE *f,uint8_t oc, + uint32_t sz,uint8_t type) +{ + /* write SUPER record header */ + fwrite8(f,oc); + fwrite32(fff[gv->dest_format]->endianness,f,sz); + fwrite8(f,type); +} + + +static size_t omf_relocs(struct GlobalVars *gv,struct LinkedSection *ls,FILE *f) +{ + static const char *fn = "omf_relocs(): "; + OMFReloc *r,*next; + size_t total = 0; + int omfendian = fff[gv->dest_format]->endianness; + int i,cnt; + + if (outver > 1) { + /* try to write SUPER compressed relocs */ + struct list superlist; + int fno,sno,sh,sz,page; + + for (i=0; i<38; i++) { /* find relocs for all types */ + initlist(&superlist); + fno = (i>2 && i<14) ? i-1 : 1; + if (i >= 14) + sno = 1 + ((i-14) % 12); + else + sno = (i < 2) ? ls->index : 0; /* 0 = any segno from 0 to 255 */ + sh = (i >= 26) ? -16 : 0; /* INTERSEG25..36 for bitshift -16 */ + cnt = (i>0 && i<14) ? 3 : 2; + sz = 0; + r = (OMFReloc *)segrelocs.first; + + while (next = (OMFReloc *)r->n.next) { + if ((f!=NULL || !(r->flags&OMFRF_DONE)) && + r->fileno==fno && r->size==cnt && r->shift==sh && + ((!sno && r->segno<256) || (r->segno == sno)) && + r->offset<=0xffff && r->addend>=0 && r->addend<=0xffff) { + if (f) { + addtail(&superlist,remnode(&r->n)); + } + else { + /* patch addend and ref-segment into the current segment data */ + write16(omfendian,ls->data+r->offset,r->addend>>(-sh)); + if (cnt == 3) + ls->data[r->offset+2] = r->segno; + r->flags |= OMFRF_SUPER; + } + + if (!sz) { + sz = 2; + if (r->offset>>8 != 0) { + sz++; + page = r->offset >> 8; + } + else + page = 0; + } + else if (r->offset>>8 != page) { + sz += (r->offset>>8 != page+1) ? 2 : 1; + page = r->offset >> 8; + } + sz++; + } + r = next; + } + if (sz) + total += 5 + sz; + + if (f!=NULL && sz) { + fwritesuper(gv,f,OMFOC_SUPER,sz,i); + page = -1; + while (r = (OMFReloc *)remhead(&superlist)) { + for (next=(OMFReloc *)superlist.first,cnt=0; + next->n.next!=NULL && next->offset>>8==r->offset>>8; + next=(OMFReloc *)next->n.next) + cnt++; + if (++page != r->offset>>8) { + fwrite8(f,0x80+((r->offset>>8)-page)); + page = r->offset >> 8; + } + fwrite8(f,cnt); + fwrite8(f,r->offset&0xff); + free(r); + while (cnt--) { + r = (OMFReloc *)remhead(&superlist); + if (r == NULL) + ierror("%smissing %d super relocs",fn,cnt+1); + fwrite8(f,r->offset&0xff); + free(r); + } + } + } + } + } + + /* looking for cRELOC- and RELOC-type relocactions */ + for (i=0; i<=1; i++) { + static const uint8_t opc[2] = { OMFOC_cRELOC, OMFOC_RELOC }; + + r = (OMFReloc *)segrelocs.first; + while (next = (OMFReloc *)r->n.next) { + if ((f!=NULL || !(r->flags&OMFRF_DONE)) && + r->fileno==1 && r->segno==ls->index && + (i || (r->offset<=0xffff && r->addend>=0 && r->addend<=0xffff))) { + if (f) { + fwrite8(f,opc[i]); + fwrite8(f,r->size); + fwrite8(f,r->shift); + if (i) { + fwrite32(omfendian,f,r->offset); + fwrite32(omfendian,f,r->addend); + } + else { + fwrite16(omfendian,f,r->offset); + fwrite16(omfendian,f,r->addend); + } + free(remnode(&r->n)); + } + else + r->flags |= OMFRF_RELOC; + total += 7 + i*4; + } + r = next; + } + } + + /* looking for cINTERSEG- and INTERSEG-type relocactions */ + for (i=0; i<=1; i++) { + static const uint8_t opc[2] = { OMFOC_cINTERSEG, OMFOC_INTERSEG }; + + r = (OMFReloc *)segrelocs.first; + while (next = (OMFReloc *)r->n.next) { + if ((f!=NULL || !(r->flags&OMFRF_DONE)) && + (i || (r->fileno==1 && r->segno<256 && + r->offset<=0xffff && r->addend>=0 && r->addend<=0xffff))) { + if (f) { + fwrite8(f,opc[i]); + fwrite8(f,r->size); + fwrite8(f,r->shift); + if (i) { + fwrite32(omfendian,f,r->offset); + fwrite16(omfendian,f,r->fileno); + fwrite16(omfendian,f,r->segno); + fwrite32(omfendian,f,r->addend); + } + else { + fwrite16(omfendian,f,r->offset); + fwrite8(f,r->segno); + fwrite16(omfendian,f,r->addend); + } + free(remnode(&r->n)); + } + else + r->flags |= OMFRF_INTERSEG; + total += 8 + i*7; + } + r = next; + } + } + + if (f!=NULL && !listempty(&segrelocs)) + ierror("%ssome segrelocs were left unprocessed"); + + return total; +} + + +static size_t omf_lablen(const char *name) +{ + return lablen ? lablen : 1+strlen(name); +} + + +static void fwritelabname(FILE *f,const char *name) +{ + size_t len = strlen(name); + + if (lablen) { + if (len < lablen) { + fwritex(f,name,len); + for (; lentype==ST_CODE ? SEGT_CODE : SEGT_DATA; + if (k==SEGT_DATA && (ls->flags & SF_DPAGE)) + k = SEGT_DPSTACK; /* zero/direct-page section */ + + /* @@@ attributes? */ + return k; +} + + +static uint32_t omf_align(struct GlobalVars *gv,struct LinkedSection *ls) +{ + uint32_t a = 1L << ls->alignment; + + /* FIXME! @@@ implements restrictions of the Apple IIGS loader only */ + if (a <= 1) + return 0; + return (a <= 0x100) ? 0x100 : OMF_BANKSIZE; +} + + +static void fwriteloadname(FILE *f,const char *name) +{ + char buf[12]; + size_t len; + + /* copy loadname and fill rest of buffer with blanks */ + strncpy(buf,name,10); + buf[10] = '\0'; + len = strlen(buf); + if (len < 10) + memset(buf+len,' ',10-len); + fwritex(f,buf,10); +} + + +static void omf_seghdr(struct GlobalVars *gv,struct LinkedSection *ls, + FILE *f,uint32_t org,size_t length,const char *lname) +{ + int omfendian = fff[gv->dest_format]->endianness; + + if (outver < 2) + fwrite32(omfendian,f,(length+511)/512); /* size in 512-byte blocks */ + else + fwrite32(omfendian,f,length); + fwrite32(omfendian,f,ls->size-ls->filesize); /* RESSPC */ + fwrite32(omfendian,f,ls->size); /* LENGTH */ + if (outver < 2) { + uint16_t kind = omf_kind(gv,ls); + + if (kind & SEGA_ABSBANK) + kind = (kind&0xff00) | SEGT_INIT | SEGT_DATA; /* v1 absolute bank */ + fwrite8(f,((kind&(SEGA_DYNAMIC|SEGA_PRIVATE|SEGA_PIC))>>8)|(kind&0x1f)); + } + else + fwrite8(f,0); + fwrite8(f,lablen); + fwrite8(f,numlen); + fwrite8(f,outver); + if (ls->flags & SF_HUGEDATA) + fwrite32(omfendian,f,0); /* may cross bank boundaries */ + else + fwrite32(omfendian,f,fff[gv->dest_format]->page_size); /* BANKSIZE */ + if (outver > 1) { + fwrite16(omfendian,f,omf_kind(gv,ls)); /* v2 KIND */ + fwritegap(gv,f,2,0); + } + else + fwritegap(gv,f,4,0); + fwrite32(omfendian,f,org); /* ORG */ + fwrite32(omfendian,f,omf_align(gv,ls)); /* ALIGN */ + fwrite8(f,omfendian); /* NUMSEX */ + fwrite8(f,0); /* v1: LCBANK @@@ */ + fwrite16(omfendian,f,ls->index); /* SEGNUM */ + fwrite32(omfendian,f,0); /* FIXME! @@@ ENTRY */ + fwrite16(omfendian,f,offsetof(OMFSeghdr,loadname)); /* v1, v2 only */ + fwrite16(omfendian,f,sizeof(OMFSeghdr)+omf_lablen(ls->name)); + fwriteloadname(f,lname); /* LOADNAME - 10 bytes */ + fwritelabname(f,ls->name); /* SEGNAME */ +} + + +static void omf_data(struct GlobalVars *gv,struct LinkedSection *ls,FILE *f) +{ + /* write LCONST opcode with file size, followed by section contents */ + if (ls->filesize) { + fwrite8(f,OMFOC_LCONST); + fwrite32(fff[gv->dest_format]->endianness,f,ls->filesize); + fwritex(f,ls->data,ls->filesize); + } +} + + +static unsigned long headersize(struct GlobalVars *gv) +{ + return 0; /* irrelevant */ +} + + +static void writeshared(struct GlobalVars *gv,FILE *f) +{ + error(30); /* Target file format doesn't support shared objects */ +} + + +static void writeobject(struct GlobalVars *gv,FILE *f) +/* creates an OMF relocatable object file */ +{ + ierror("Apple OMF object file generation has not yet been implemented"); +} + + +static void writeexec(struct GlobalVars *gv,FILE *f) +/* creates an OMF load file (which is relocatable) */ +{ + struct LinkedSection *ls; + size_t sz; + + omf_initwrite(gv); + + for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; + ls=(struct LinkedSection *)ls->n.next) { + if (ls->index) { + collect_segrelocs(gv,ls); + + /* determine size of segment header + body (including relocs) */ + sz = sizeof(OMFSeghdr) + omf_lablen(ls->name); + if (ls->filesize) + sz += 5 + ls->filesize; + sz += omf_relocs(gv,ls,NULL) + 1; /* including END opcode */ + + /* write segment */ + omf_seghdr(gv,ls,f,0,sz,noname); /* org 0 = relocatable */ + omf_data(gv,ls,f); + omf_relocs(gv,ls,f); + fwrite8(f,OMFOC_END); + + if (outver<2 && (sz&511)) { + /* pad to block boundaries in v1 format */ + fwritegap(gv,f,512-(sz&511),0); + } + } + } +} + +#endif diff --git a/t_ataritos.c b/t_ataritos.c index ff0df23..66ac475 100644 --- a/t_ataritos.c +++ b/t_ataritos.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_ataritos.c V0.16h (16.01.21) +/* $VER: vlink t_ataritos.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2015,2021 Frank Wille + * Copyright (c) 1997-2015,2021,2023,2024 Frank Wille */ #include "config.h" @@ -28,6 +28,7 @@ struct FFFuncs fff_ataritos = { NULL, NULL, tos_options, + tos_printhelp, headersize, identify, readconv, @@ -54,21 +55,615 @@ struct FFFuncs fff_ataritos = { /*****************************************************************/ -/* Read Atari TOS */ +/* Read Atari DRI */ /*****************************************************************/ +static int dri_reloccheck(uint8_t *p,size_t words,int syms) +{ + uint16_t rw; + + while (words--) { + rw = read16be(p); + if ((rw&7) == 4 || (rw&7) == 6) + if ((int)(rw >> 3) >= syms) + return 0; /* illegal symbol index for external reference */ + /* @@@ may also check if other types have 0-index */ + p += 2; + } + + return 1; +} + + +static int tos_reloccheck(uint8_t *p,size_t len,size_t tdsize) +{ + size_t poffs = read32be(p); + size_t roffs = 4; + size_t a; + + if (poffs==0 && roffs==len) + return 1; /* executable with no relocations */ + + p += 4; + while (roffssizeof(struct DRIarheader)+2 && + (read16be(p)==DRI_ARMAGIC || read16be(p)==DRI_ARMAGIC+1)) { + /* DRI library archive, look at first member */ + struct DRIarheader *dahdr = (struct DRIarheader *)(p + 2); + + arflag = TRUE; + hdr = (PH *)(p + 2 + sizeof(struct DRIarheader)); + plen = read32be(dahdr->a_fsize); + } + else { + arflag = FALSE; + hdr = (PH *)p; + } + + if (plen>sizeof(PH) && read16be(hdr->ph_branch)==DRIMAGIC && + read32be(hdr->ph_slen)%sizeof(struct DRIsym)==0) { + size_t tlen = read32be(hdr->ph_tlen); + size_t dlen = read32be(hdr->ph_dlen); + size_t slen = read32be(hdr->ph_slen); + size_t reloffs = sizeof(PH) + tlen + dlen + slen; + int nsym = slen / sizeof(struct DRIsym); + + /* check for DRI object file first */ + if ((plen == reloffs + tlen + dlen) && + dri_reloccheck(p+reloffs,tlen/2,nsym) && + dri_reloccheck(p+reloffs+tlen,dlen/2,nsym)) + return arflag ? ID_LIBARCH : ID_OBJECT; + + /* may be an executable with TOS relocs */ + if (!arflag && ((read16be(hdr->ph_abs)!=0 && plen-reloffs==0) || + (plen-reloffs>=4 && tos_reloccheck(p+reloffs,plen-reloffs,tlen+dlen)))) + return ID_EXECUTABLE; /* cannot be in an library archive */ + } + return ID_UNKNOWN; +} + + +static int check_sozobonx(struct DRIsym *sym) +{ + return read16be(sym->type) == STYP_XFLAGS && + read32be(sym->value) == XVALUE; +} + + +static struct Section *dri_newsec(struct DRIinfo *dri,int idx,uint8_t *data, + unsigned long size) +{ + /* section attributes for: .text, .data, .bss */ + static uint8_t types[] = { + ST_CODE,ST_DATA,ST_UDATA + }; + static uint8_t prots[] = { + SP_READ|SP_EXEC,SP_READ|SP_WRITE,SP_READ|SP_WRITE + }; + static uint8_t flags[] = { + SF_ALLOC,SF_ALLOC,SF_ALLOC|SF_UNINITIALIZED + }; + static const char *names[] = { + TEXTNAME,DATANAME,BSSNAME + }; + + if (idx < 2) { + if (data+size > dri->end) { /* illegal section offset */ + struct LinkFile *lf = dri->object->lnkfile; + error(49,lf->pathname,names[idx],lf->objname); + } + dri->data[idx] = data; + } + return dri->sec[idx] = add_section(dri->object,names[idx],data,size, + types[idx],flags[idx], + prots[idx],TOS_ALIGNMENT,FALSE); +} + + +static struct Section *dri_get_section(struct DRIinfo *dri,int secno) +{ + struct Section *sec; + + if ((sec = dri->sec[secno]) == NULL) { + /* reference to empty section - create it */ + sec = dri_newsec(dri,secno,secno<2?dri->end:NULL,0); + } + return sec; +} + + +static uint8_t *dri_make_sections(struct DRIinfo *dri,uint8_t *p) +{ + uint32_t size; + + if (size = read32be(dri->hdr->ph_tlen)) { + dri_newsec(dri,0,p,size); + p += size; /* @@@ Can we assume ph_tlen is always aligned? */ + } + else + dri->sec[0] = NULL; + + if (size = read32be(dri->hdr->ph_dlen)) { + dri_newsec(dri,1,p,size); + p += size; + } + else + dri->sec[1] = NULL; + + if (size = read32be(dri->hdr->ph_blen)) + dri_newsec(dri,2,NULL,size); + else + dri->sec[2] = NULL; + + return p; +} + + +static lword read_addend(struct DRIinfo *dri,int secno, + unsigned long offs,uint16_t sz) +{ + struct Section *s = dri->sec[secno]; + + if (s == NULL) + ierror("DRI read_addend(): No section #%d (offset %lu)",secno,offs); + + switch (sz) { + case 16: + return sign_extend(read16be(s->data+offs),16); + case 32: + return sign_extend(read32be(s->data+offs),32); + default: + ierror("DRI read_addend(): Bad size %u",(unsigned)sz); + } + return 0; +} + + +static size_t namelen(const char *p,size_t maxlen) +/* similar to strnlen() */ +{ + size_t len = 0; + + while (maxlen-- && *p!='\0') + len++; + return len; +} + + +static char *dri_symname(struct DRIinfo *dri,int symno) +{ + if (symno < dri->nsym) { + char *buf,*p; + int len; + + /* determine symbol name length */ + if (dri->sozobonx) { + int n = symno + 1; + + len = 0; + while (nnsym && check_sozobonx(&dri->symtab[n])) { + len += DRI_NAMELEN; + n++; + } + len += namelen(dri->symtab[n-1].name,DRI_NAMELEN); + } + else { + if (symno+1nsym && + (read16be(dri->symtab[symno].type) & STYP_LONGNAME)==STYP_LONGNAME) + len = DRI_NAMELEN + + namelen(dri->symtab[symno+1].name,sizeof(struct DRIsym)); + else + len = namelen(dri->symtab[symno].name,DRI_NAMELEN); + } + + /* allocate and populate new name buffer */ + p = buf = alloc(len + 1); + + if (dri->sozobonx) { + while (symno+1nsym && check_sozobonx(&dri->symtab[symno+1])) { + memcpy(p,dri->symtab[symno].name,DRI_NAMELEN); + p += DRI_NAMELEN; + len -= DRI_NAMELEN; + symno++; + } + } + else { + if (symno+1nsym && + (read16be(dri->symtab[symno].type)&STYP_LONGNAME)==STYP_LONGNAME) { + memcpy(p,dri->symtab[symno].name,DRI_NAMELEN); + p += DRI_NAMELEN; + len -= DRI_NAMELEN; + symno++; + } + } + strncpy(p,dri->symtab[symno++].name,len); + p[len] = '\0'; + dri->symidx = symno; /* symbol-index for next symbol */ + return buf; + } + else + ierror("dri_symname(): symno %d > %d",symno,dri->nsym); + return NULL; +} + + +static int dri_addreloc(struct GlobalVars *gv,struct DRIinfo *dri, + int secno,int rsecno,int rsymno, + unsigned long offs,uint8_t rtype,uint16_t sz) +{ + struct Section *sec = dri->sec[secno]; + struct Section *rsec = NULL; + char *xname = NULL; + lword a = 0; + + if (rsymno >= 0) { + /* reference to (external) symbol */ + struct DRIsym *rsym; + uint16_t stype; + + if (rsymno >= dri->nsym) { + /* symbol index out of range */ + error(139,getobjname(dri->object),(unsigned)rsymno); + return 0; + } + rsym = &dri->symtab[rsymno]; + stype = read16be(rsym->type); + + if (stype & STYP_EXTERNAL) { + if ((xname = dri_symname(dri,rsymno)) == NULL) + return 0; + } + else if (stype & STYP_TEXT) { + rsecno = 0; + a = read32be(rsym->value); + } + else if (stype & STYP_DATA) { + rsecno = 1; + a = read32be(rsym->value); + } + else if (stype & STYP_BSS) { + rsecno = 2; + a = read32be(rsym->value); + } + else { + /* symbol reference with unsupported type */ + error(20,dri->object->lnkfile->pathname,dri_symname(dri,rsymno), + dri->object->objname,(int)stype); + return 0; + } + } + + if (rsecno >= 0) { + if (rsecno > 2) + ierror("dri_addreloc(): Illegal section number %d",rsecno); + rsec = dri_get_section(dri,rsecno); + } + + a += read_addend(dri,secno,offs,sz); + if (rtype == R_SD) + a &= makemask(sz); /* SD-addends must be unsigned! */ + + if (sec == NULL) + ierror("dri_addreloc(): our section #%d disappered",secno); + addreloc(sec,newreloc(gv,sec,xname,rsec,0,offs,rtype,a),0,sz,-1); + return 1; +} + + +static int dri_make_relocs(struct GlobalVars *gv,struct DRIinfo *dri,int secno, + uint8_t *p,size_t plen) +{ + unsigned long offs = 0; + int sz = 0; + uint16_t rw; + + if (p + plen > dri->end) { + error(41,dri->object->lnkfile->pathname,dri->ff->tname); /* corrupt */ + return 0; + } + + plen >>= 1; /* number of potential relocation words to read */ + while (plen--) { + rw = read16be(p); + p += 2; + + switch (rw & 7) { + case 1: /* relocate relative to data */ + if (!dri_addreloc(gv,dri,secno,1,-1,offs-2*sz,R_ABS,16<>3,offs-2*sz,sz?R_ABS:R_SD,16<>3,offs-2*sz,R_PC,16<hdr->ph_tlen); + unsigned long dlen = read32be(dri->hdr->ph_dlen); + unsigned long poffs,delta; + + if (dri->end - p < 4) { + tos_corrupt: + error(41,dri->object->lnkfile->pathname,dri->ff->tname); /* corrupt */ + return 0; + } + + poffs = read32be(p); + p += 4; + if (poffs==0 && p==dri->end) + return 1; /* no relocations */ + + do { + struct Section *rsec; + unsigned long offs; + int secno; + lword a; + + if (poffs >= tlen) { + if (poffs >= tlen + dlen) + goto tos_corrupt; + secno = 1; + offs = poffs - tlen; + } + else { + secno = 0; + offs = poffs; + } + + a = read_addend(dri,secno,offs,32); + if (a >= (lword)(tlen+dlen)) { + /* reloc to bss */ + a -= tlen+dlen; + rsec = dri_get_section(dri,2); + } + else if (a >= (lword)tlen) { + /* reloc to data */ + a -= tlen; + rsec = dri_get_section(dri,1); + } + else /* reloc to text */ + rsec = dri_get_section(dri,0); + + addreloc(dri->sec[secno], + newreloc(gv,dri->sec[secno],NULL,rsec,0,offs,R_ABS,a), + 0,32,-1); + do { + if ((delta = *p++) == 1) + poffs += 254; + else + poffs += delta; + } while (delta==1 && pend); + } while (delta && p<=dri->end); + + if (p != dri->end) + goto tos_corrupt; + return 1; +} + + +static int dri_make_symbols(struct GlobalVars *gv,struct DRIinfo *dri) +{ + int idx = dri->sozobonx ? 1 : 0; + + while (idx < dri->nsym) { + struct DRIsym *sym = &dri->symtab[idx]; + uint16_t type = read16be(sym->type); + int32_t val = read32be(sym->value); + char *name; + + if (name = dri_symname(dri,idx)) { + struct Section *sec; + uint8_t stype = SYM_RELOC; + uint32_t ssize = 0; /* only common symbols define a size with DRI */ + + idx = dri->symidx; + + if ((type & STYP_EXTERNAL) && val != 0) { + /* Common symbol, with val defining its size */ + stype = SYM_COMMON; + ssize = val; + val = TOS_ALIGNMENT; + sec = common_section(gv,dri->object); + } + else if ((type & (STYP_DEFINED|STYP_EXTERNAL)) == STYP_DEFINED) { + /* we handle all not externally defined symbols here */ + switch (type & + (STYP_TEXT|STYP_DATA|STYP_BSS|STYP_REGISTER|STYP_EQUATED)) { + case STYP_TEXT: + sec = dri_get_section(dri,0); + break; + case STYP_DATA: + sec = dri_get_section(dri,1); + break; + case STYP_BSS: + sec = dri_get_section(dri,2); + break; + case STYP_EQUATED: + stype = SYM_ABS; + sec = abs_section(dri->object); + break; + default: + /* ignore REGISTER symbols or strange combinations */ + free(name); + continue; + } + } + else { + free(name); + continue; + } + + if (type & STYP_GLOBAL) + addsymbol(gv,sec,name,NULL,val,stype,0, + SYMI_NOTYPE,SYMB_GLOBAL,ssize,TRUE); + else + addlocsymbol(gv,sec,name,NULL,val,stype,0,SYMI_NOTYPE,ssize); + } + else + idx++; + } + return 1; +} + + +static int dri_read(struct GlobalVars *gv,struct LinkFile *lf, + uint8_t *p,unsigned long plen) +{ + struct DRIinfo dri; + size_t len; + + dri.ff = fff[lf->format]; + if (read16be(p) != DRIMAGIC) { + error(41,lf->pathname,dri.ff->tname); /* corrupt */ + return 0; + } + dri.object = create_objunit(gv,lf,lf->objname); + dri.hdr = (PH *)p; + dri.end = p + plen; + + p += sizeof(PH); + p = dri_make_sections(&dri,p); + + dri.symtab = (struct DRIsym *)p; + len = read32be(dri.hdr->ph_slen); + dri.nsym = len / sizeof(struct DRIsym); + dri.sozobonx = dri.nsym && !memcmp(dri.symtab->name,XNAME,DRI_NAMELEN) + && check_sozobonx(dri.symtab); + p += len; + + if (lf->type == ID_EXECUTABLE) { + if (read16be(dri.hdr->ph_abs) == 0) { + if (!tos_make_relocs(gv,&dri,p)) + return 0; + } + } + else { /* object file */ + len = read32be(dri.hdr->ph_tlen); + if (!dri_make_relocs(gv,&dri,0,p,len)) /* text relocs */ + return 0; + p += len; + if (!dri_make_relocs(gv,&dri,1,p,read32be(dri.hdr->ph_dlen))) /* data relocs */ + return 0; + } + + if (!dri_make_symbols(gv,&dri)) + return 0; + + add_objunit(gv,dri.object,FALSE); + return 1; } static void readconv(struct GlobalVars *gv,struct LinkFile *lf) { - ierror("readconv(): Can't read Atari TOS"); + if (lf->type == ID_LIBARCH) { + struct ar_info ai; + + if (ar_init(&ai,(char *)lf->data,lf->length,lf->filename)) { + while (ar_extract(&ai)) { + lf->objname = allocstring(ai.name); + if (!dri_read(gv,lf,ai.data,ai.size)) + break; + } + } + else if (read16be(lf->data)==DRI_ARMAGIC || + read16be(lf->data)==DRI_ARMAGIC+1) { + /* convert all DRI library modules */ + uint8_t *p = lf->data + 2; + unsigned long len = lf->length - 2; + struct DRIarheader *darh; + + while (len > sizeof(struct DRIarheader)) { + unsigned long osize; + + darh = (struct DRIarheader *)p; + osize = read32be(darh->a_fsize); + p += sizeof(struct DRIarheader); + lf->objname = allocstring(darh->a_fname); + if (!dri_read(gv,lf,p,osize)) + break; + p += osize; + len -= sizeof(struct DRIarheader) + osize; + } + } + else + ierror("DRI readconv(): archive %s corrupted since last access", + lf->pathname); + } + else { + /* convert single DRI object or TOS executable */ + lf->objname = lf->filename; + dri_read(gv,lf,lf->data,lf->length); + } } @@ -85,7 +680,7 @@ static int targetlink(struct GlobalVars *gv,struct LinkedSection *ls, /* returns 0, if target doesn't care - standard linking rules are used. */ { /* TOS requires that all sections of type CODE or DATA or BSS */ - /* will be combined, because there are only those three available! */ + /* will be merged, because there are only these three available! */ if (ls->type == s->type) return 1; @@ -99,6 +694,22 @@ static int targetlink(struct GlobalVars *gv,struct LinkedSection *ls, /*****************************************************************/ +static int dri_symbol_count(const char *name) +/* determine number of DRI symbol slots to use for this name */ +{ + int len = strlen(name); + int cnt = 1; + + if (len > DRI_NAMELEN) { + if (sozobonx) + cnt += (len - 1) / DRI_NAMELEN; + else if (hisoftdri) + cnt++; + } + return cnt; +} + + static int tos_initwrite(struct GlobalVars *gv, struct LinkedSection **sections) /* find exactly one ST_CODE, ST_DATA and ST_UDATA section, which @@ -109,6 +720,9 @@ static int tos_initwrite(struct GlobalVars *gv, struct Reloc *xref; int i,cnt; + if (gv->dest_object || sozobonx) + hisoftdri = FALSE; /* disable HiSoft extension in object files */ + get_text_data_bss(gv,sections); /* count symbols and unresolved references */ @@ -116,25 +730,23 @@ static int tos_initwrite(struct GlobalVars *gv, if (sections[i]) { for (sym=(struct Symbol *)sections[i]->symbols.first; sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { - if (!discard_symbol(gv,sym)) { - ++cnt; - if (strlen(sym->name) > DRI_NAMELEN) - ++cnt; /* extra symbol for long name */ - } + if (!discard_symbol(gv,sym) && + (sym->type==SYM_ABS || sym->type==SYM_RELOC || + (gv->dest_object && sym->type==SYM_COMMON))) + cnt += dri_symbol_count(sym->name); } - if (gv->dest_object) { + if (gv->dest_object) { /* include externally referenced symbols */ for (xref=(struct Reloc *)sections[i]->xrefs.first; - xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) { - ++cnt; - if (strlen(xref->xrefname) > DRI_NAMELEN) - ++cnt; /* extra symbol for long name */ - } + xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) + cnt += dri_symbol_count(xref->xrefname); } } } text_data_bss_gaps(sections); /* calculate gap size between sections */ + if (cnt && sozobonx) + cnt++; return cnt; } @@ -162,7 +774,9 @@ static void write_dri_sym(FILE *f,const char *name, uint16_t type,uint32_t value) { struct DRIsym stab; - int longname = strlen(name) > DRI_NAMELEN; + int namelen = strlen(name); + int longname = (namelen > DRI_NAMELEN) && hisoftdri; + int szb_extensions = sozobonx ? (namelen-1) / DRI_NAMELEN : 0; strncpy(stab.name,name,DRI_NAMELEN); write16be(stab.type,longname?(type|STYP_LONGNAME):type); @@ -176,14 +790,24 @@ static void write_dri_sym(FILE *f,const char *name, strncpy(rest_of_name,name+DRI_NAMELEN,sizeof(struct DRIsym)); fwritex(f,rest_of_name,sizeof(struct DRIsym)); } + else { + int i = DRI_NAMELEN; + + while (szb_extensions--) { + strncpy(stab.name,name+i,DRI_NAMELEN); + write16be(stab.type,STYP_XFLAGS); + write32be(stab.value,XVALUE); + fwritex(f,&stab,sizeof(struct DRIsym)); + i += DRI_NAMELEN; + } + } } -static void tos_symboltable(struct GlobalVars *gv,FILE *f, - struct LinkedSection **sections) +static int defsymtab(struct GlobalVars *gv,FILE *f, + struct LinkedSection **sections) { struct Symbol *sym; - struct Reloc *xref; int i; for (i=0; i<3; i++) { @@ -197,8 +821,8 @@ static void tos_symboltable(struct GlobalVars *gv,FILE *f, if (sym->type == SYM_ABS) { t = STYP_EQUATED; } - else if (sym->type != SYM_COMMON) { - if (!gv->textbasedsyms) + else if (sym->type == SYM_RELOC) { + if (!textbasedsyms) val -= sections[i]->base; /* symbol value as section offset */ switch (i) { case 0: t = STYP_TEXT; break; @@ -206,42 +830,39 @@ static void tos_symboltable(struct GlobalVars *gv,FILE *f, case 2: t = STYP_BSS; break; } } - else - ierror("tos_symboltable(): Common symbol <%s> not supported", - sym->name); - + else if (sym->type==SYM_COMMON && gv->dest_object) { + t = STYP_EXTERNAL; + val = sym->size; + } + else { + error(33,fff_ataritos.tname,sym->name,sym_bind[sym->bind], + sym_type[sym->type],sym_info[sym->info]); + continue; + } t |= STYP_DEFINED; if (sym->bind > SYMB_LOCAL) t |= STYP_GLOBAL; write_dri_sym(f,sym->name,t,val); - /* FIXME: symbols in DRI objects do not support long names. */ } } + } + } +} - if (gv->dest_object) { - for (xref=(struct Reloc *)sections[i]->xrefs.first; - xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) { - /* This is what Devpac does. Relocations and external - reference types for each word are located after the symbols. - @@@ WARNING! Relocation and reference table is still missing. - Reengineered DRI Format of this table (not yet implemented): - One type-word for each word in a section. - 0x0000 no reloation or reference - 0x0001 data relocation - 0x0002 text relocation - 0x0003 bss relocation - 0x0004 and greater (not 0x0005): external reference - Bit 15-3: symbol table index of reference (starting with 0) - Bit 2: always set for external reference - Bit 1-0: 00 AbsRef, 01 illegal?, 10 PCRelRef, 11 unknown - 0x0005 32-bit prefix. Following word describes a relocation - or reference for the 32-bit longword at this position. */ - - write_dri_sym(f,xref->xrefname,STYP_DEFINED|STYP_EXTERNAL,0); - /* FIXME: external symbols do not support long names and must - only appear once! */ - } + +static void refsymtab(struct GlobalVars *gv,FILE *f, + struct LinkedSection **sections,int idx) +{ + struct Reloc *xref; + int i; + + for (i=0; i<3; i++) { + if (sections[i]) { + for (xref=(struct Reloc *)sections[i]->xrefs.first; + xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) { + write_dri_sym(f,xref->xrefname,STYP_EXTERNAL|STYP_DEFINED,0); + xref->relocsect.xrefinfo = idx++; /* remember symbol table index */ } } } @@ -306,27 +927,117 @@ void tos_writerelocs(struct GlobalVars *gv,FILE *f, } -static unsigned long headersize(struct GlobalVars *gv) +static int secref_type(struct LinkedSection **secs,struct LinkedSection *ls) { - return 0; /* irrelevant */ + static const int rsec_map[] = { 2,1,3 }; /* text, data, bss - reloc type */ + int i; + + for (i=0; i<3; i++) { + if (secs[i] == ls) + return rsec_map[i]; + } + ierror("secref_type: no DRI section: %s",ls->name); + return 0; } -static void writeshared(struct GlobalVars *gv,FILE *f) +static void dri_writerelocs(struct GlobalVars *gv,FILE *f, + struct LinkedSection **sections) { - error(30); /* Target file format doesn't support shared objects */ -} + const char *fn = "dri_writerelocs(): "; + int i; + for (i=0; i<2; i++) { + if (sections[i]) { + struct Reloc *rel,*xref; + unsigned long offs = 0; -static void writeobject(struct GlobalVars *gv,FILE *f) -/* creates a TOS relocatable object file */ -{ - ierror("Atari TOS object file generation has not yet been implemented"); + sort_relocs(§ions[i]->relocs); + rel = (struct Reloc *)sections[i]->relocs.first; + sort_relocs(§ions[i]->xrefs); + xref = (struct Reloc *)sections[i]->xrefs.first; + + while (rel->n.next || xref->n.next) { + struct Reloc *r; + struct RelocInsert *ri; + int t; + int sz = 0; + + if (xref->n.next==NULL || (rel->n.next && rel->offsetoffset)) { + r = rel; + rel = (struct Reloc *)rel->n.next; + } + else { + r = xref; + xref = (struct Reloc *)xref->n.next; + } + + /* determine relocation field width: 0=invalid, 1=16bit, 2=32bit */ + if (ri = r->insert) { + if (ri->next==NULL && ri->bpos==0 && !(ri->bsiz&15)) { + if ((sz = ri->bsiz / 16) <= 2) { + if ((ri->mask & makemask(ri->bsiz)) != makemask(ri->bsiz)) + sz = 0; + } + } + } + if (!sz) { + dri_bad_reloc: + if (ri = r->insert) + error(32,fff_ataritos.tname,reloc_name[r->rtype], + (int)ri->bpos,(int)ri->bsiz,mtaddr(gv,ri->mask), + sections[i]->name,r->offset); + else + ierror("%smissing RelocInsert for rtype %d at %s+%lu", + fn,(int)r->rtype,sections[i]->name,r->offset); + continue; + } + + fwritegap(gv,f,r->offset-offs,0); + offs = r->offset; + + switch (r->rtype) { + case R_ABS: + if (r->xrefname) + t = 4; + else + t = secref_type(sections,r->relocsect.lnk); + break; + case R_PC: + if (r->xrefname) { + t = 6; + break; + } + goto dri_bad_reloc; + case R_SD: + if (r->xrefname) { + t = 4; + break; + } + default: + goto dri_bad_reloc; + } + + if (sz == 2) + fwrite16be(f,5); /* write longword type indicator to MSW */ + if (t==4 || t==6) { + /* external reference requires symbol index in bits 3..15 */ + int idx = r->relocsect.xrefinfo; + + if (idx > 0x1fff) + error(160,fff_ataritos.tname); /* too many symbols */ + t |= idx << 3; + } + fwrite16be(f,t); + offs += sz << 1; + } + fwritegap(gv,f,sections[i]->size+sections[i]->gapsize-offs,0); + } + } } -static void writeexec(struct GlobalVars *gv,FILE *f) -/* creates a TOS executable file (which is relocatable) */ +static void tos_or_dri_output(struct GlobalVars *gv,FILE *f,int exec) { struct LinkedSection *sections[3]; int nsyms = tos_initwrite(gv,sections); @@ -337,25 +1048,63 @@ static void writeexec(struct GlobalVars *gv,FILE *f) sections[2] ? sections[2]->size : 0, (unsigned long)nsyms*sizeof(struct DRIsym),tos_flags); - for (i=0; i<3; i++) - calc_relocs(gv,sections[i]); + if (exec) { + for (i=0; i<3; i++) + calc_relocs(gv,sections[i]); + } if (sections[0]) { fwritex(f,sections[0]->data,sections[0]->filesize); fwritegap(gv,f, - (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize); + (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize, + 0); } if (sections[1]) { fwritex(f,sections[1]->data,sections[1]->filesize); fwritegap(gv,f, - (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize); + (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize, + 0); + } + + if (nsyms) { + if (sozobonx) + write_dri_sym(f,XNAME,STYP_XFLAGS,XVALUE); + if (!exec) + refsymtab(gv,f,sections,sozobonx==TRUE); + defsymtab(gv,f,sections); } - if (nsyms) - tos_symboltable(gv,f,sections); + if (exec) + tos_writerelocs(gv,f,sections); + else + dri_writerelocs(gv,f,sections); +} + - tos_writerelocs(gv,f,sections); +static unsigned long headersize(struct GlobalVars *gv) +{ + return 0; /* irrelevant */ +} + + +static void writeshared(struct GlobalVars *gv,FILE *f) +{ + error(30); /* Target file format doesn't support shared objects */ +} + + +static void writeobject(struct GlobalVars *gv,FILE *f) +/* creates a TOS relocatable object file */ +{ + tos_or_dri_output(gv,f,0); +} + + +static void writeexec(struct GlobalVars *gv,FILE *f) +/* creates a TOS executable file (which is relocatable) */ +{ + tos_or_dri_output(gv,f,1); } diff --git a/t_elf32.c b/t_elf32.c index 172e1b1..44f5ad2 100644 --- a/t_elf32.c +++ b/t_elf32.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32.c V0.17a (30.03.22) +/* $VER: vlink t_elf32.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 + * Copyright (c) 1997-2024 Frank Wille */ @@ -171,7 +171,7 @@ static void elf32_symbols(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, static void elf32_dynrefs(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, struct ObjectUnit *ou,struct Elf32_Shdr *shdr, bool be, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* Find all relocs in a shared object which refer to an undefined symbol. */ { uint8_t *data = (uint8_t *)ehdr + read32(be,shdr->sh_offset); @@ -194,7 +194,7 @@ static void elf32_dynrefs(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, ELF32_R_SYM(read32(be,elfrel->r_info)); uint32_t shndx = (uint32_t)read16(be,sym->st_shndx); struct RelocInsert ri; - uint8_t rtype; + int rtype; if (shndx == SHN_UNDEF || shndx == SHN_COMMON) { memset(&ri,0,sizeof(struct RelocInsert)); @@ -215,7 +215,7 @@ static void elf32_dynrefs(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, static void elf32_reloc(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, struct ObjectUnit *ou,struct Elf32_Shdr *shdr, char *shstrtab,bool be, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* Read ELF32 relocations, which are relative to a defined symbol, into the section's reloc-list. If the symbol is undefined, create an external reference on it, with the supplied relocation type. */ @@ -256,7 +256,7 @@ static void elf32_reloc(struct GlobalVars *gv,struct Elf32_Ehdr *ehdr, struct Reloc *r; struct RelocInsert ri; lword a; - uint8_t rtype; + int rtype; memset(&ri,0,sizeof(struct RelocInsert)); rtype = reloc_elf2vlink(ELF32_R_TYPE(read32(be,elfrel->r_info)),&ri); @@ -406,7 +406,7 @@ static void elf32_stabs(struct GlobalVars *gv,struct LinkFile *lf, void elf32_parse(struct GlobalVars *gv,struct LinkFile *lf, struct Elf32_Ehdr *ehdr, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* parses a complete ELF file and converts into vlink-internal format */ { static const char *fn = "elf32_parse(): "; @@ -871,7 +871,7 @@ static void elf32_writephdrs(struct GlobalVars *gv,FILE *f) gapsize += sizeof(struct Elf32_Phdr); } } - fwritegap(gv,f,gapsize); /* gap at the end, for unused PHDRs */ + fwritegap(gv,f,gapsize,0); /* gap at the end, for unused PHDRs */ } diff --git a/t_elf32arm.c b/t_elf32arm.c index d312f9b..9cfbed4 100644 --- a/t_elf32arm.c +++ b/t_elf32arm.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32arm.c V0.13 (02.11.10) +/* $VER: vlink t_elf32arm.c V0.18 (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2010 Frank Wille + * Copyright (c) 1997-2010,2024 Frank Wille */ @@ -27,6 +27,7 @@ struct FFFuncs fff_elf32armle = { NULL, NULL, NULL, + NULL, elf32_headersize, armle_identify, armle_readconv, @@ -67,7 +68,7 @@ static int armle_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t armle_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int armle_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ @@ -75,20 +76,20 @@ static uint8_t armle_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Reloc conversion table for V.4-ABI - @@@ INCOMPLETE!!! */ static struct ELF2vlink convertV4[] = { R_NONE,0,0,-1, - R_PC,8,24,0x3fffffc, /* PC24, deprecated! Use CALL or JUMP24! */ - R_ABS,0,32,-1, /* ABS32 */ - R_PC,0,32,-1, /* REL32 */ - R_PC,20,12,0x1fff, /* LDR_PC_G0 */ - R_ABS,0,16,-1, /* ABS16 */ - R_ABS,20,12,0xfff, /* ABS12 */ - R_ABS,5,5,0x1f, /* THM_ABS5 */ - R_ABS,0,8,-1, /* ABS8 */ - R_SD,0,32,-1, /* SBREL32 */ - R_PC,5,11,0, /* THM_CALL, needs 2nd ri */ - R_PC,8,8,0x3fc, /* THM_PC8 */ + R_PC|R_S,8,24,~3, /* PC24, deprecated! Use CALL or JUMP24! */ + R_ABS,0,32,-1, /* ABS32 */ + R_PC|R_S,0,32,-1, /* REL32 */ + R_PC|R_S,20,12,-1, /* LDR_PC_G0 */ + R_ABS,0,16,-1, /* ABS16 */ + R_ABS,20,12,-1, /* ABS12 */ + R_ABS,5,5,0x1f, /* THM_ABS5 */ + R_ABS,0,8,-1, /* ABS8 */ + R_SD|R_S,0,32,-1, /* SBREL32 */ + R_PC|R_S,5,11,0, /* THM_CALL, needs 2nd ri */ + R_PC|R_S,8,8,0x3fc, /* THM_PC8 */ R_NONE,0,0,-1, - R_ABS,8,24,0xffffff, /* SWI24, obsolete! */ - R_ABS,8,8,0xff, /* THM_SWI8, obsolete! */ + R_ABS,8,24,-1, /* SWI24, obsolete! */ + R_ABS,8,8,0xff, /* THM_SWI8, obsolete! */ R_NONE,0,0,-1, R_NONE,0,0,-1, R_NONE,0,0,-1, @@ -102,13 +103,13 @@ static uint8_t armle_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) R_NONE,0,0,-1, R_NONE,0,0,-1, R_NONE,0,0,-1, - R_PC,8,24,0x3fffffc, /* CALL, PC24 for uncond. bl/blx only */ - R_PC,8,24,0x3fffffc, /* JUMP24, PC24 for other branches */ + R_PC|R_S,8,24,~3, /* CALL, PC24 for uncond. bl/blx only */ + R_PC|R_S,8,24,~3, /* JUMP24, PC24 for other branches */ R_NONE,0,0,-1, R_NONE,0,0,-1, - R_PC,24,8,0xff, /* ALU_PCREL_7_0, obsolete! */ - R_PC,24,8,0xff00, /* ALU_PCREL_15_8, obsolete! */ - R_PC,24,8,0xff0000, /* ALU_PCREL_23_15, obsolete! */ + R_PC|R_S,24,8,0xff, /* ALU_PCREL_7_0, obsolete! */ + R_PC|R_S,24,8,0xff00, /* ALU_PCREL_15_8, obsolete! */ + R_PC|R_S,24,8,0xff0000, /* ALU_PCREL_23_15, obsolete! */ R_NONE,0,0,-1, R_NONE,0,0,-1, R_NONE,0,0,-1, @@ -200,9 +201,9 @@ static uint8_t armle_reloc_vlink2elf(struct Reloc *r) case 8: return R_ARM_ABS8; } } - else if (size==24 && (pos&31)==8 && mask==0xffffff && !ri2) + else if (size==24 && (pos&31)==8 && mask==-1 && !ri2) return R_ARM_SWI24; /* @@@ obsolete */ - else if (size==12 && (pos&31)==20 && mask==0xfff && !ri2) + else if (size==12 && (pos&31)==20 && mask==-1 && !ri2) return R_ARM_ABS12; else if (size==8 && (pos&15)==8 && mask==0xff && !ri2) return R_ARM_THM_SWI8; /* @@@ obsolete */ @@ -213,9 +214,9 @@ static uint8_t armle_reloc_vlink2elf(struct Reloc *r) case R_PC: if (size==32 && !(pos&7) && mask==-1 && !ri2) return R_ARM_REL32; - else if (size==24 && (pos&31)==8 && mask==0x3fffffc && !ri2) + else if (size==24 && (pos&31)==8 && mask==~3 && !ri2) return R_ARM_PC24; /* @@@ deprecated: use R_ARM_CALL/JUMP24!!! */ - else if (size==12 && (pos&31)==20 && mask==0x1fff && !ri2) + else if (size==12 && (pos&31)==20 && mask==-1 && !ri2) return R_ARM_LDR_PC_G0; else if (size==8 && (pos&31)==24 && mask==0xff && !ri2) return R_ARM_ALU_PCREL_7_0; /* @@@ obsolete */ diff --git a/t_elf32i386.c b/t_elf32i386.c index 44974d8..471b465 100644 --- a/t_elf32i386.c +++ b/t_elf32i386.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32i386.c V0.15a (28.02.15) +/* $VER: vlink t_elf32i386.c V0.18 (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2015 Frank Wille + * Copyright (c) 1997-2015,2024 Frank Wille */ @@ -31,6 +31,7 @@ struct FFFuncs fff_elf32i386 = { NULL, NULL, NULL, + NULL, elf32_headersize, i386_identify, i386_readconv, @@ -73,6 +74,7 @@ struct FFFuncs fff_elf32aros = { NULL, NULL, NULL, + NULL, elf32_headersize, i386_identify, i386_readconv, @@ -118,7 +120,7 @@ static int i386_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t i386_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int i386_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ @@ -126,16 +128,16 @@ static uint8_t i386_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Reloc conversion table for V.4-ABI */ static struct ELF2vlink convertV4[] = { R_NONE,0,0,-1, - R_ABS,0,32,-1, /* R_386_32 */ - R_PC,0,32,-1, /* R_386_PC32 */ - R_GOT,0,32,-1, /* R_386_GOT32 */ - R_PLT,0,32,-1, /* R_386_PLT32 */ - R_COPY,0,32,-1, /* R_386_COPY */ - R_GLOBDAT,0,32,-1, /* R_386_GLOB_DAT */ - R_JMPSLOT,0,0,-1, /* R_386_JMP_SLOT */ - R_LOADREL,0,32,-1, /* R_386_RELATIVE */ - R_GOTOFF,0,32,-1, /* R_386_GOTOFF */ - R_GOTPC,0,32,-1 /* R_386_GOTPC */ + R_ABS,0,32,-1, /* R_386_32 */ + R_PC|R_S,0,32,-1, /* R_386_PC32 */ + R_GOT,0,32,-1, /* R_386_GOT32 */ + R_PLT,0,32,-1, /* R_386_PLT32 */ + R_COPY,0,32,-1, /* R_386_COPY */ + R_GLOBDAT,0,32,-1, /* R_386_GLOB_DAT */ + R_JMPSLOT,0,0,-1, /* R_386_JMP_SLOT */ + R_LOADREL,0,32,-1, /* R_386_RELATIVE */ + R_GOTOFF,0,32,-1, /* R_386_GOTOFF */ + R_GOTPC|R_S,0,32,-1 /* R_386_GOTPC */ }; if (rtype <= R_386_GOTPC) { diff --git a/t_elf32jag.c b/t_elf32jag.c index b17dd6f..3139a05 100644 --- a/t_elf32jag.c +++ b/t_elf32jag.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32jag.c V0.15b (17.08.16) +/* $VER: vlink t_elf32jag.c V0.18 (04.02.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2016 Frank Wille + * Copyright (c) 1997-2016,2024 Frank Wille */ @@ -26,6 +26,7 @@ struct FFFuncs fff_elf32jag = { NULL, NULL, NULL, + NULL, elf32_headersize, jag_identify, jag_readconv, @@ -64,7 +65,7 @@ static int jag_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t jag_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int jag_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ @@ -73,17 +74,17 @@ static uint8_t jag_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) static struct ELF2vlink convert[] = { R_NONE,0,0,-1, - R_ABS,0,32,-1, /* R_JAG_ABS32 */ - R_ABS,0,16,-1, /* R_JAG_ABS16 */ - R_ABS,0,8,-1, /* R_JAG_ABS8 */ - R_PC,0,32,-1, /* R_JAG_REL32 */ - R_PC,0,16,-1, /* R_JAG_REL16 */ - R_PC,0,8,-1, /* R_JAG_REL8 */ - R_ABS,6,5,0x1f, /* R_JAG_ABS5 */ - R_PC,6,5,0x1f, /* R_JAG_REL5 */ - R_PC,6,5,0x3e, /* R_JAG_JR */ - R_ABS,16,16,0xffff0000, /* R_JAG_ABS32SWP */ - R_PC,16,16,0xffff0000 /* R_JAG_REL32SWP */ + R_ABS,0,32,-1, /* R_JAG_ABS32 */ + R_ABS,0,16,-1, /* R_JAG_ABS16 */ + R_ABS,0,8,-1, /* R_JAG_ABS8 */ + R_PC|R_S,0,32,-1, /* R_JAG_REL32 */ + R_PC|R_S,0,16,-1, /* R_JAG_REL16 */ + R_PC|R_S,0,8,-1, /* R_JAG_REL8 */ + R_ABS,6,5,-1, /* R_JAG_ABS5 */ + R_PC|R_S,6,5,-1, /* R_JAG_REL5 */ + R_PC|R_S,6,5,~1, /* R_JAG_JR */ + R_ABS,16,16,~0xffff, /* R_JAG_ABS32SWP */ + R_PC|R_S,16,16,~0xffff /* R_JAG_REL32SWP */ }; if (rtype <= R_JAG_REL32SWP) { @@ -92,7 +93,7 @@ static uint8_t jag_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) ri->mask = convert[rtype].mask; rtype = convert[rtype].rtype; - if (ri->mask == 0xffff0000) { + if (ri->mask == ~0xffff) { /* R_JAG_xxxSWP - add a second RelocInsert */ ri2 = *ri; ri2.bpos = 0; @@ -158,14 +159,14 @@ static uint8_t jag_reloc_vlink2elf(struct Reloc *r) struct RelocInsert *ri2 = ri->next; if (pos==6 && siz==5) { - if (mask == 0x1f) + if (mask == ~0) rt = r->rtype==R_ABS ? R_JAG_ABS5 : R_JAG_REL5; - else if (mask == 0x3e) + else if (mask == ~1) rt = R_JAG_JR; } else if ((pos&15)==0 && siz==32 && ri2!=NULL && ri2->bsiz==32 && - (mask==0xffff0000 || mask==0xffff) && - (ri2->mask==0xffff0000 || ri2->mask==0xffff) && + (mask==~0xffff || mask==0xffff) && + (ri2->mask==~0xffff || ri2->mask==0xffff) && mask!=ri2->mask) { r->offset += mask==0xffff ? (pos >> 3) : (ri2->bpos >> 3); rt = r->rtype==R_ABS ? R_JAG_ABS32SWP : R_JAG_REL32SWP; diff --git a/t_elf32m68k.c b/t_elf32m68k.c index b78de6f..0b28f82 100644 --- a/t_elf32m68k.c +++ b/t_elf32m68k.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32m68k.c V0.13 (02.11.10) +/* $VER: vlink t_elf32m68k.c V0.18 (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2010 Frank Wille + * Copyright (c) 1997-2010,2024 Frank Wille */ @@ -29,6 +29,7 @@ struct FFFuncs fff_elf32m68k = { NULL, NULL, NULL, + NULL, elf32_headersize, m68k_identify, m68k_readconv, @@ -69,35 +70,35 @@ static int m68k_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t m68k_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int m68k_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ { static struct ELF2vlink convertV4[] = { R_NONE,0,0,-1, - R_ABS,0,32,-1, /* R_68K_32 */ - R_ABS,0,16,-1, /* R_68K_16 */ - R_ABS,0,8,-1, /* R_68K_8 */ - R_PC,0,32,-1, /* R_68K_PC32 */ - R_PC,0,16,-1, /* R_68K_PC16 */ - R_PC,0,8,-1, /* R_68K_PC8 */ - R_GOT,0,32,-1, /* R_68K_GOT32 */ - R_GOT,0,16,-1, /* R_68K_GOT16 */ - R_GOT,0,8,-1, /* R_68K_GOT8 */ - R_GOTOFF,0,32,-1, /* R_68K_GOT32O */ - R_GOTOFF,0,16,-1, /* R_68K_GOT16O */ - R_GOTOFF,0,8,-1, /* R_68K_GOT8O */ - R_PLT,0,32,-1, /* R_68K_PLT32 */ - R_PLT,0,16,-1, /* R_68K_PLT16 */ - R_PLT,0,8,-1, /* R_68K_PLT8 */ - R_PLTOFF,0,32,-1, /* R_68K_PLT32O */ - R_PLTOFF,0,16,-1, /* R_68K_PLT16O */ - R_PLTOFF,0,8,-1, /* R_68K_PLT8O */ - R_COPY,0,32,-1, /* R_68K_COPY */ - R_GLOBDAT,0,32,-1, /* R_68K_GLOB_DAT */ - R_JMPSLOT,0,0,-1, /* R_68K_JMP_SLOT */ - R_LOADREL,0,32,-1 /* R_68K_RELATIVE */ + R_ABS,0,32,-1, /* R_68K_32 */ + R_ABS,0,16,-1, /* R_68K_16 */ + R_ABS,0,8,-1, /* R_68K_8 */ + R_PC|R_S,0,32,-1, /* R_68K_PC32 */ + R_PC|R_S,0,16,-1, /* R_68K_PC16 */ + R_PC|R_S,0,8,-1, /* R_68K_PC8 */ + R_GOT,0,32,-1, /* R_68K_GOT32 */ + R_GOT,0,16,-1, /* R_68K_GOT16 */ + R_GOT,0,8,-1, /* R_68K_GOT8 */ + R_GOTOFF,0,32,-1, /* R_68K_GOT32O */ + R_GOTOFF,0,16,-1, /* R_68K_GOT16O */ + R_GOTOFF,0,8,-1, /* R_68K_GOT8O */ + R_PLT,0,32,-1, /* R_68K_PLT32 */ + R_PLT,0,16,-1, /* R_68K_PLT16 */ + R_PLT,0,8,-1, /* R_68K_PLT8 */ + R_PLTOFF,0,32,-1, /* R_68K_PLT32O */ + R_PLTOFF,0,16,-1, /* R_68K_PLT16O */ + R_PLTOFF,0,8,-1, /* R_68K_PLT8O */ + R_COPY,0,32,-1, /* R_68K_COPY */ + R_GLOBDAT,0,32,-1, /* R_68K_GLOB_DAT */ + R_JMPSLOT,0,0,-1, /* R_68K_JMP_SLOT */ + R_LOADREL,0,32,-1 /* R_68K_RELATIVE */ }; if (rtype <= R_68K_RELATIVE) { diff --git a/t_elf32ppcbe.c b/t_elf32ppcbe.c index 0051081..1841b88 100644 --- a/t_elf32ppcbe.c +++ b/t_elf32ppcbe.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf32ppcbe.c V0.15a (28.02.15) +/* $VER: vlink t_elf32ppcbe.c V0.18 (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2015 Frank Wille + * Copyright (c) 1997-2015,2024 Frank Wille */ @@ -10,6 +10,7 @@ #if defined(ELF32_PPC_BE) || defined(ELF32_AMIGA) #define T_ELF32PPCBE_C #include "vlink.h" +#include "cpurelocs.h" #include "elf32.h" #include "rel_elfppc.h" @@ -31,6 +32,7 @@ struct FFFuncs fff_elf32ppcbe = { NULL, NULL, NULL, + NULL, elf32_headersize, ppc32be_identify, ppc32be_readconv, @@ -73,6 +75,7 @@ struct FFFuncs fff_elf32powerup = { NULL, NULL, NULL, + NULL, elf32_headersize, ppc32be_identify, ppc32be_readconv, @@ -102,6 +105,7 @@ struct FFFuncs fff_elf32morphos = { NULL, NULL, NULL, + NULL, elf32_headersize, ppc32be_identify, ppc32be_readconv, @@ -131,6 +135,7 @@ struct FFFuncs fff_elf32amigaos = { NULL, NULL, NULL, + NULL, elf32_headersize, ppc32be_identify, ppc32be_readconv, @@ -202,8 +207,8 @@ static int ppc32be_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t setupRI(uint8_t rtype,struct ELF2vlink *convert, - struct RelocInsert *ri1,struct RelocInsert *ri2) +static int setupRI(uint8_t rtype,struct ELF2vlink *convert, + struct RelocInsert *ri1,struct RelocInsert *ri2) { ri1->bpos = convert[rtype].bpos; ri1->bsiz = convert[rtype].bsiz; @@ -219,7 +224,7 @@ static uint8_t setupRI(uint8_t rtype,struct ELF2vlink *convert, } -static uint8_t ppc32_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int ppc32_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ @@ -227,76 +232,76 @@ static uint8_t ppc32_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Reloc conversion table for V.4-ABI @@@ not complete! */ static struct ELF2vlink convertV4[] = { R_NONE,0,0,-1, - R_ABS,0,32,-1, /* R_PPC_ADDR32 */ - R_ABS,6,24,~3, /* R_PPC_ADDR24 */ - R_ABS,0,16,-1, /* R_PPC_ADDR16 */ - R_ABS,0,16,0xffff, /* R_PPC_ADDR16_LO */ - R_ABS,0,16,0xffff0000, /* R_PPC_ADDR16_HI */ - R_ABS,0,16,0, /* R_PPC_ADDR16_HA */ - R_ABS,16,14,~3, /* R_PPC_ADDR14 */ - R_ABS,16,14,~3, /* R_PPC_ADDR14_BRTAKEN */ - R_ABS,16,14,~3, /* R_PPC_ADDR14_BRNTAKEN */ - R_PC,6,24,~3, /* R_PPC_REL24 */ - R_PC,16,14,~3, /* R_PPC_REL14 */ - R_PC,16,14,~3, /* R_PPC_REL14_BRTAKEN */ - R_PC,16,14,~3, /* R_PPC_REL14_BRNTAKEN */ - R_GOT,0,16,-1, /* R_PPC_GOT16 */ - R_GOT,0,16,0xffff, /* R_PPC_GOT16_LO */ - R_GOT,0,16,0xffff0000, /* R_PPC_GOT16_HI */ - R_GOT,0,16,0, /* R_PPC_GOT16_HA */ - R_PLTPC,6,24,~3, /* R_PPC_PLTREL24 */ - R_COPY,0,32,-1, /* R_PPC_COPY */ - R_GLOBDAT,0,32,-1, /* R_PPC_GLOB_DAT */ - R_JMPSLOT,0,64,-1, /* R_PPC_JMP_SLOT */ - R_LOADREL,0,32,-1, /* R_PPC_RELATIVE */ - R_LOCALPC,6,24,~3, /* R_PPC_LOCAL24PC */ - R_UABS,0,32,-1, /* R_PPC_UADDR32 */ - R_UABS,0,16,-1, /* R_PPC_UADDR16 */ - R_PC,0,32,-1, /* R_PPC_REL32 */ - R_PLT,0,32,-1, /* R_PPC_PLT32 */ - R_PLTPC,0,32,-1, /* R_PPC_PLTREL32 */ - R_PLT,0,16,0xffff, /* R_PPC_PLT16_LO */ - R_PLT,0,16,0xffff0000, /* R_PPC_PLT16_HI */ - R_PLT,0,16,0, /* R_PPC_PLT16_HA */ - R_SD,0,16,-1, /* R_PPC_SDAREL16 */ - R_SECOFF,0,16,-1, /* R_PPC_SECTOFF */ - R_SECOFF,0,16,0xffff, /* R_PPC_SECTOFF_LO */ - R_SECOFF,0,16,0xffff0000, /* R_PPC_SECTOFF_HI */ - R_SECOFF,0,16,0, /* R_PPC_SECTOFF_HA */ - R_ABS,0,30,~3 /* R_PPC_ADDR30 */ + R_ABS,0,32,-1, /* R_PPC_ADDR32 */ + R_ABS|R_S,6,24,~3, /* R_PPC_ADDR24 */ + R_ABS,0,16,-1, /* R_PPC_ADDR16 */ + R_ABS,0,16,0xffff, /* R_PPC_ADDR16_LO */ + R_ABS,0,16,0xffff0000, /* R_PPC_ADDR16_HI */ + R_ABS,0,16,0, /* R_PPC_ADDR16_HA */ + R_ABS|R_S,16,14,~3, /* R_PPC_ADDR14 */ + R_ABS|R_S,16,14,~3, /* R_PPC_ADDR14_BRTAKEN */ + R_ABS|R_S,16,14,~3, /* R_PPC_ADDR14_BRNTAKEN */ + R_PC|R_S,6,24,~3, /* R_PPC_REL24 */ + R_PC|R_S,16,14,~3, /* R_PPC_REL14 */ + R_PC|R_S,16,14,~3, /* R_PPC_REL14_BRTAKEN */ + R_PC|R_S,16,14,~3, /* R_PPC_REL14_BRNTAKEN */ + R_GOT,0,16,-1, /* R_PPC_GOT16 */ + R_GOT,0,16,0xffff, /* R_PPC_GOT16_LO */ + R_GOT,0,16,0xffff0000, /* R_PPC_GOT16_HI */ + R_GOT,0,16,0, /* R_PPC_GOT16_HA */ + R_PLTPC|R_S,6,24,~3, /* R_PPC_PLTREL24 */ + R_COPY,0,32,-1, /* R_PPC_COPY */ + R_GLOBDAT,0,32,-1, /* R_PPC_GLOB_DAT */ + R_JMPSLOT,0,64,-1, /* R_PPC_JMP_SLOT */ + R_LOADREL,0,32,-1, /* R_PPC_RELATIVE */ + R_LOCALPC|R_S,6,24,~3, /* R_PPC_LOCAL24PC */ + R_UABS,0,32,-1, /* R_PPC_UADDR32 */ + R_UABS,0,16,-1, /* R_PPC_UADDR16 */ + R_PC|R_S,0,32,-1, /* R_PPC_REL32 */ + R_PLT,0,32,-1, /* R_PPC_PLT32 */ + R_PLTPC|R_S,0,32,-1, /* R_PPC_PLTREL32 */ + R_PLT,0,16,0xffff, /* R_PPC_PLT16_LO */ + R_PLT,0,16,0xffff0000, /* R_PPC_PLT16_HI */ + R_PLT,0,16,0, /* R_PPC_PLT16_HA */ + R_SD|R_S,0,16,-1, /* R_PPC_SDAREL16 */ + R_SECOFF,0,16,-1, /* R_PPC_SECTOFF */ + R_SECOFF,0,16,0xffff, /* R_PPC_SECTOFF_LO */ + R_SECOFF,0,16,0xffff0000, /* R_PPC_SECTOFF_HI */ + R_SECOFF,0,16,0, /* R_PPC_SECTOFF_HA */ + R_ABS,0,30,~3 /* R_PPC_ADDR30 */ }; /* Reloc conversion table for PPC-EABI @@@ not complete! */ static struct ELF2vlink convertEABI[] = { - R_NONE,0,0,-1, /* R_PPC_EMB_NADDR32 */ - R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16 */ - R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_LO */ - R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_HI */ - R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_HA */ - R_NONE,0,0,-1, /* R_PPC_EMB_SDAI16 */ - R_NONE,0,0,-1, /* R_PPC_EMB_SDA2I16 */ - R_SD2,0,16,-1, /* R_PPC_EMB_SDA2REL */ - R_SD21,16,16,-1, /* R_PPC_EMB_SDA21 */ - R_NONE,0,0,-1, /* R_PPC_EMB_MRKREF */ - R_NONE,0,0,-1, /* R_PPC_EMB_RELSEC16 */ - R_NONE,0,0,-1, /* R_PPC_EMB_RELST_LO */ - R_NONE,0,0,-1, /* R_PPC_EMB_RELST_HI */ - R_NONE,0,0,-1, /* R_PPC_EMB_RELST_HA */ - R_NONE,0,0,-1, /* R_PPC_EMB_BIT_FLD */ - R_NONE,0,0,-1 /* R_PPC_EMB_RELSDA */ + R_NONE,0,0,-1, /* R_PPC_EMB_NADDR32 */ + R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16 */ + R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_LO */ + R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_HI */ + R_NONE,0,0,-1, /* R_PPC_EMB_NADDR16_HA */ + R_NONE,0,0,-1, /* R_PPC_EMB_SDAI16 */ + R_NONE,0,0,-1, /* R_PPC_EMB_SDA2I16 */ + RPPC_SD2|R_S,0,16,-1, /* R_PPC_EMB_SDA2REL */ + RPPC_SD21|R_S,16,16,-1, /* R_PPC_EMB_SDA21 */ + R_NONE,0,0,-1, /* R_PPC_EMB_MRKREF */ + R_NONE,0,0,-1, /* R_PPC_EMB_RELSEC16 */ + R_NONE,0,0,-1, /* R_PPC_EMB_RELST_LO */ + R_NONE,0,0,-1, /* R_PPC_EMB_RELST_HI */ + R_NONE,0,0,-1, /* R_PPC_EMB_RELST_HA */ + R_NONE,0,0,-1, /* R_PPC_EMB_BIT_FLD */ + R_NONE,0,0,-1 /* R_PPC_EMB_RELSDA */ }; /* Reloc conversion table for MorphOS */ static struct ELF2vlink convertMOS[] = { - R_MOSDREL,0,16,-1, /* R_PPC_MORPHOS_DREL */ - R_MOSDREL,0,16,0xffff, /* R_PPC_MORPHOS_DREL_LO */ - R_MOSDREL,0,16,0xffff0000, /* R_PPC_MORPHOS_DREL_HI */ - R_MOSDREL,0,16,0, /* R_PPC_MORPHOS_DREL_HA */ + RPPC_MOSDREL|R_S,0,16,-1, /* R_PPC_MORPHOS_DREL */ + RPPC_MOSDREL|R_S,0,16,0xffff, /* R_PPC_MORPHOS_DREL_LO */ + RPPC_MOSDREL|R_S,0,16,0xffff0000, /* R_PPC_MORPHOS_DREL_HI */ + RPPC_MOSDREL|R_S,0,16,0, /* R_PPC_MORPHOS_DREL_HA */ }; /* Reloc conversion table for AmigaOS/PPC */ static struct ELF2vlink convertAOS[] = { - R_AOSBREL,0,16,-1, /* R_PPC_AMIGAOS_BREL */ - R_AOSBREL,0,16,0xffff, /* R_PPC_AMIGAOS_BREL_LO */ - R_AOSBREL,0,16,0xffff0000, /* R_PPC_AMIGAOS_BREL_HI */ - R_AOSBREL,0,16,0, /* R_PPC_AMIGAOS_BREL_HA */ + RPPC_AOSBREL|R_S,0,16,-1, /* R_PPC_AMIGAOS_BREL */ + RPPC_AOSBREL|R_S,0,16,0xffff, /* R_PPC_AMIGAOS_BREL_LO */ + RPPC_AOSBREL|R_S,0,16,0xffff0000, /* R_PPC_AMIGAOS_BREL_HI */ + RPPC_AOSBREL|R_S,0,16,0, /* R_PPC_AMIGAOS_BREL_HA */ }; static struct RelocInsert ri2; @@ -738,12 +743,12 @@ static uint8_t ppc32_reloc_vlink2elf(struct Reloc *r) /* Embedded and OS-specific relocs */ - case R_SD2: + case RPPC_SD2: if (f==FSTD && size==16) rt = R_PPC_EMB_SDA2REL; break; - case R_SD21: + case RPPC_SD21: if (f==FSTD && size==16) { r->offset -= 2; ri->bpos += 16; @@ -751,7 +756,7 @@ static uint8_t ppc32_reloc_vlink2elf(struct Reloc *r) } break; - case R_MOSDREL: + case RPPC_MOSDREL: switch (f) { case FSTD: if (size == 16) @@ -763,7 +768,7 @@ static uint8_t ppc32_reloc_vlink2elf(struct Reloc *r) } break; - case R_AOSBREL: + case RPPC_AOSBREL: switch (f) { case FSTD: if (size == 16) @@ -942,7 +947,7 @@ static void morphos_writeexec(struct GlobalVars *gv,FILE *f) struct Symbol *sym = (struct Symbol *)ls->symbols.first; struct Symbol *nextsym; unsigned long bss_offset = bss_sec->offset; - lword bss_va = (lword)(ls->base + bss_offset); + lword bss_va = ls->base + bss_offset; sbss = create_lnksect(gv,sbss_name,ST_UDATA, ls->flags|SF_UNINITIALIZED, diff --git a/t_elf64.c b/t_elf64.c index fc493d4..98cbbfa 100644 --- a/t_elf64.c +++ b/t_elf64.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf64.c V0.17a (30.03.22) +/* $VER: vlink t_elf64.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -171,7 +171,7 @@ static void elf64_symbols(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, static void elf64_dynrefs(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, struct ObjectUnit *ou,struct Elf64_Shdr *shdr, bool be, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* Find all relocs in a shared object which refer to an undefined symbol. */ { uint8_t *data = (uint8_t *)ehdr + read64(be,shdr->sh_offset); @@ -194,7 +194,7 @@ static void elf64_dynrefs(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, ELF64_R_SYM(read64(be,elfrel->r_info)); uint32_t shndx = (uint32_t)read16(be,sym->st_shndx); struct RelocInsert ri; - uint8_t rtype; + int rtype; if (shndx == SHN_UNDEF || shndx == SHN_COMMON) { memset(&ri,0,sizeof(struct RelocInsert)); @@ -215,7 +215,7 @@ static void elf64_dynrefs(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, static void elf64_reloc(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, struct ObjectUnit *ou,struct Elf64_Shdr *shdr, char *shstrtab,bool be, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* Read ELF64 relocations, which are relative to a defined symbol, into the section's reloc-list. If the symbol is undefined, create an external reference on it, with the supplied relocation type. */ @@ -256,7 +256,7 @@ static void elf64_reloc(struct GlobalVars *gv,struct Elf64_Ehdr *ehdr, struct Reloc *r; struct RelocInsert ri; lword a; - uint8_t rtype; + int rtype; memset(&ri,0,sizeof(struct RelocInsert)); rtype = reloc_elf2vlink(ELF64_R_TYPE(read64(be,elfrel->r_info)),&ri); @@ -408,7 +408,7 @@ static void elf64_stabs(struct GlobalVars *gv,struct LinkFile *lf, void elf64_parse(struct GlobalVars *gv,struct LinkFile *lf, struct Elf64_Ehdr *ehdr, - uint8_t (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) + int (*reloc_elf2vlink)(uint8_t,struct RelocInsert *)) /* parses a complete ELF file and converts into vlink-internal format */ { static const char *fn = "elf64_parse(): "; @@ -873,7 +873,7 @@ static void elf64_writephdrs(struct GlobalVars *gv,FILE *f) gapsize += sizeof(struct Elf64_Phdr); } } - fwritegap(gv,f,gapsize); /* gap at the end, for unused PHDRs */ + fwritegap(gv,f,gapsize,0); /* gap at the end, for unused PHDRs */ } diff --git a/t_elf64x86.c b/t_elf64x86.c index fafe578..d3cb5ff 100644 --- a/t_elf64x86.c +++ b/t_elf64x86.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_elf64x86.c V0.16d (28.02.20) +/* $VER: vlink t_elf64x86.c V0.18 (26.01.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2020 Frank Wille + * Copyright (c) 1997-2020,2024 Frank Wille */ @@ -28,6 +28,7 @@ struct FFFuncs fff_elf64x86 = { NULL, NULL, NULL, + NULL, elf64_headersize, x86_64_identify, x86_64_readconv, @@ -68,7 +69,7 @@ static int x86_64_identify(struct GlobalVars *gv,char *name,uint8_t *p, } -static uint8_t x86_64_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) +static int x86_64_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Determine vlink internal reloc type from ELF reloc type and fill in reloc-insert description information. All fields of the RelocInsert structure are preset to zero. */ @@ -76,21 +77,21 @@ static uint8_t x86_64_reloc_elf2vlink(uint8_t rtype,struct RelocInsert *ri) /* Reloc conversion table for V.4-ABI */ static struct ELF2vlink convertV4[] = { R_NONE,0,0,-1, - R_ABS,0,64,-1, /* R_X86_64_64 */ - R_PC,0,32,-1, /* R_X86_64_PC32 */ - R_GOT,0,32,-1, /* R_X86_64_GOT32 */ - R_PLT,0,32,-1, /* R_X86_64_PLT32 */ - R_COPY,0,64,-1, /* R_X86_64_COPY */ - R_GLOBDAT,0,64,-1, /* R_X86_64_GLOB_DAT */ - R_JMPSLOT,0,0,-1, /* R_X86_64_JUMP_SLOT */ - R_LOADREL,0,64,-1, /* R_X86_64_RELATIVE */ - R_GOTPC,0,32,-1, /* R_X86_64_GOTPCREL @@@ */ - R_ABS,0,32,-1, /* R_X86_64_32 */ - R_ABS,0,32,-1, /* R_X86_64_32S @@@ signed 32-bit? */ - R_ABS,0,16,-1, /* R_X86_64_16 */ - R_PC,0,16,-1, /* R_X86_64_PC16 */ - R_ABS,0,8,-1, /* R_X86_64_8 */ - R_PC,0,8,-1 /* R_X86_64_PC8 */ + R_ABS,0,64,-1, /* R_X86_64_64 */ + R_PC|R_S,0,32,-1, /* R_X86_64_PC32 */ + R_GOT,0,32,-1, /* R_X86_64_GOT32 */ + R_PLT,0,32,-1, /* R_X86_64_PLT32 */ + R_COPY,0,64,-1, /* R_X86_64_COPY */ + R_GLOBDAT,0,64,-1, /* R_X86_64_GLOB_DAT */ + R_JMPSLOT,0,0,-1, /* R_X86_64_JUMP_SLOT */ + R_LOADREL,0,64,-1, /* R_X86_64_RELATIVE */ + R_GOTPC|R_S,0,32,-1, /* R_X86_64_GOTPCREL @@@ */ + R_ABS,0,32,-1, /* R_X86_64_32 */ + R_ABS|R_S,0,32,-1, /* R_X86_64_32S */ + R_ABS,0,16,-1, /* R_X86_64_16 */ + R_PC|R_S,0,16,-1, /* R_X86_64_PC16 */ + R_ABS,0,8,-1, /* R_X86_64_8 */ + R_PC|R_S,0,8,-1 /* R_X86_64_PC8 */ }; if (rtype <= R_X86_64_PC8) { diff --git a/t_o65.c b/t_o65.c index 41e17d0..e408d5b 100644 --- a/t_o65.c +++ b/t_o65.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_o65.c V0.16i (31.01.22) +/* $VER: vlink t_o65.c V0.18 (09.06.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #include "config.h" @@ -15,6 +15,8 @@ static int options_02(struct GlobalVars *,int,const char **,int *); static int options_816(struct GlobalVars *,int,const char **,int *); +static void printhelp_02(void); +static void printhelp_816(void); static int identify_02(struct GlobalVars *,char *,uint8_t *,unsigned long,bool); static int identify_816(struct GlobalVars *,char *,uint8_t *,unsigned long,bool); static void readconv(struct GlobalVars *,struct LinkFile *); @@ -34,6 +36,7 @@ struct FFFuncs fff_o6502 = { NULL, NULL, options_02, + printhelp_02, headersize, identify_02, readconv, @@ -63,6 +66,7 @@ struct FFFuncs fff_o65816 = { NULL, NULL, options_816, + printhelp_816, headersize, identify_816, readconv, @@ -86,7 +90,6 @@ struct FFFuncs fff_o65816 = { FFF_BASEINCR }; -static struct ar_info ai; /* for scanning library archives */ struct ImportList o65implist; /* list of externally referenced symbol names */ static int o65size; /* words are 2 or 4 bytes */ @@ -195,6 +198,32 @@ static int options_816(struct GlobalVars *gv,int argc,const char **argv,int *i) } +static void printhelp(int cpu816) +{ + printf("-o65-align min.alignment (# of bits to be 0): 0, 1, 2 or 8\n" + "-o65-author store author name in header\n" + "-o65-bsszero request automatic clearing of BSS section\n"); + if (!cpu816) + printf("-o65-cpu 6502, 65c02, 65sc02, 65ce02, nmos6502, 65816\n"); + printf("-o65-fopts gen. info in header: file/linker-name, version, date\n" + "-o65-name overwrite fopts name\n" + "-o65-paged paged alignment and simplified paged relocations\n" + "-o65-stack store required stack size in the header\n"); +} + + +static void printhelp_02(void) +{ + printhelp(0); +} + + +static void printhelp_816(void) +{ + printhelp(1); +} + + /*****************************************************************/ /* Read o65 */ @@ -255,7 +284,7 @@ static void o65_newsec(struct o65info *o65,int secno,uint8_t align) SP_READ|SP_EXEC,SP_READ|SP_WRITE,SP_READ|SP_WRITE,SP_READ|SP_WRITE }; static uint8_t flags[NSECS] = { - SF_ALLOC,SF_ALLOC,SF_ALLOC|SF_UNINITIALIZED,SF_ALLOC|SF_UNINITIALIZED + SF_ALLOC,SF_ALLOC,SF_ALLOC|SF_UNINITIALIZED,SF_ALLOC|SF_UNINITIALIZED|SF_DPAGE }; static const char *names[NSECS] = { TEXTNAME,DATANAME,BSSNAME,ZERONAME @@ -304,6 +333,7 @@ static int o65_getrelocs(struct GlobalVars *gv,struct o65info *o65,int secno) { struct FFFuncs *ff = fff[o65->object->lnkfile->format]; int pagedrel = o65->mode & MO65_PAGED; + int wdc816 = o65->mode & MO65_65816; struct Section *sec = o65->sec[secno]; uint8_t *d = o65->data[secno]; struct Section *rsec; @@ -352,7 +382,7 @@ static int o65_getrelocs(struct GlobalVars *gv,struct o65info *o65,int secno) switch (R65TYPE(b)) { case 0x80: /* 16-bit address */ a = read16le(d+off); - m = -1; + m = wdc816 ? 0xffff : -1; /* ignore bank-byte for 65816 */ sz = 16; break; case 0x40: /* address high-byte */ @@ -519,6 +549,7 @@ static int identify(char *name,uint8_t *p,unsigned long plen, bool lib,int cpu816) /* identify an o65 object file or executable */ { + struct ar_info ai; bool arflag = FALSE; uint16_t mode; @@ -530,6 +561,7 @@ static int identify(char *name,uint8_t *p,unsigned long plen, return ID_IGNORE; } p = ai.data; + plen = ai.size; } if (plen < sizeof(o65magic)+2 || memcmp(p,o65magic,sizeof(o65magic))!=0) @@ -561,6 +593,8 @@ static int identify_816(struct GlobalVars *gv,char *name,uint8_t *p, static void readconv(struct GlobalVars *gv,struct LinkFile *lf) { + struct ar_info ai; + if (lf->type == ID_LIBARCH) { if (ar_init(&ai,(char *)lf->data,lf->length,lf->filename)) { while (ar_extract(&ai)) { @@ -709,7 +743,7 @@ static int o65_simple(struct LinkedSection **secs) if (i < ZSEC) { for (addr=secs[i]->base; ibase != addr) + if (secs[i]->base != addr) return 0; addr += secs[i]->size; } diff --git a/t_os9.c b/t_os9.c index 0622641..26a7b4d 100644 --- a/t_os9.c +++ b/t_os9.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_os9.c V0.16h (16.01.21) +/* $VER: vlink t_os9.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2021 Frank Wille + * Copyright (c) 1997-2021,2024 Frank Wille */ #include "config.h" @@ -14,6 +14,7 @@ static void init(struct GlobalVars *,int); static int options(struct GlobalVars *,int,const char **,int *); +static void printhelp(void); static int identify(struct GlobalVars *,char *,uint8_t *,unsigned long,bool); static void readconv(struct GlobalVars *,struct LinkFile *); static int targetlink(struct GlobalVars *,struct LinkedSection *, @@ -30,6 +31,7 @@ struct FFFuncs fff_os9_6809 = { NULL, init, options, + printhelp, hdrsize_6809, identify, readconv, @@ -131,6 +133,15 @@ static int options(struct GlobalVars *gv,int argc,const char *argv[],int *i) } +static void printhelp(void) +{ + printf("-os9-mem=[K] set size of stack/parameter area (default 1024)\n" + "-os9-name= overwrite the module name in the header\n" + "-os9-ns declare module as non-shareable and non-reentrant\n" + "-os9-rev= set revision in the module header (0-15, def. 0)\n"); +} + + /*****************************************************************/ /* Read OS9 */ @@ -313,7 +324,7 @@ static void writeexec_6809(struct GlobalVars *gv,FILE *f) lword rmask=makemask(16); size_t initdata_size,bss_size,stk_size,file_size; uint16_t entryoffs; - unsigned long lma; + lword lma; uint8_t *buf,crc[3]; mh6809 hdr; @@ -343,7 +354,7 @@ static void writeexec_6809(struct GlobalVars *gv,FILE *f) if (lma==0 && ls->copybasecopybase,sizeof(6809)); /* not enough space */ if (ls->copybase > lma) - fwritegap(gv,f,ls->copybase-lma); + fwritegap(gv,f,ls->copybase-lma,0); lma = ls->copybase + ls->size; checkPIC(ls); @@ -365,7 +376,7 @@ static void writeexec_6809(struct GlobalVars *gv,FILE *f) error(136,ls->name); /* executable section in data segment */ if (ls->copybase > lma) - fwritegap(gv,f,ls->copybase-lma); + fwritegap(gv,f,ls->copybase-lma,0); lma = ls->copybase + ls->size; /* move data-text and data-data relocations into their own list */ @@ -382,7 +393,7 @@ static void writeexec_6809(struct GlobalVars *gv,FILE *f) remnode(&rel->n); writesection(gv,ls->data,rel->offset,rel, rel->relocsect.lnk->base+rel->addend); - rel->offset += (lword)ls->base; /* offset relative to whole segment */ + rel->offset += ls->base; /* offset relative to whole segment */ if (rel->relocsect.lnk->type==ST_CODE) { addtail(&dtrefs,&rel->n); dtrefcnt++; diff --git a/t_rawbin.c b/t_rawbin.c index f6c8b95..6437de8 100644 --- a/t_rawbin.c +++ b/t_rawbin.c @@ -1,19 +1,18 @@ -/* $VER: vlink t_rawbin.c V0.16i (31.01.22) +/* $VER: vlink t_rawbin.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #include "config.h" -#if defined(RAWBIN1) || defined(RAWBIN2) || \ +#if defined(RAWBIN) || defined(IHEX) || defined(SHEX1) || \ defined(SREC19) || defined(SREC28) || defined(SREC37) || \ - defined(IHEX) || defined(SHEX1) || \ defined(AMSDOS) || defined(APPLEBIN) || defined(ATARICOM) || \ defined(BBC) || defined(CBMPRG) || defined(COCOML) || \ - defined(DRAGONBIN) || defined(JAGSRV) || defined(ORICMC) || \ - defined(SINCQL) + defined(DRAGONBIN) || defined(FOENIX) || defined(JAGSRV) || \ + defined(ORICMC) || defined(SINCQL) #define T_RAWBIN_C #include "vlink.h" @@ -35,6 +34,10 @@ enum { HDR_DRAGONBIN, HDR_JAGSRV, HDR_ORICMC, + HDR_PGXC02, + HDR_PGX816, + HDR_PGZ24, + HDR_PGZ32, HDR_QDOS, HDR_XTCC }; @@ -53,12 +56,11 @@ static int rawbin_targetlink(struct GlobalVars *,struct LinkedSection *, struct Section *); static void rawbin_writeobject(struct GlobalVars *,FILE *); static void rawbin_writeshared(struct GlobalVars *,FILE *); -static void rawbin_writeexec(struct GlobalVars *,FILE *,bool,int); -#ifdef RAWBIN1 -static void rawbin_writesingle(struct GlobalVars *,FILE *); -#endif -#ifdef RAWBIN2 -static void rawbin_writemultiple(struct GlobalVars *,FILE *); +static void rawbin_out(struct GlobalVars *,FILE *,int); +#ifdef RAWBIN +static int rawbin_options(struct GlobalVars *,int,const char **,int *); +static void rawbin_printhelp(void); +static void rawbin_write(struct GlobalVars *,FILE *); #endif #ifdef AMSDOS static unsigned long amsdos_headersize(struct GlobalVars *); @@ -75,12 +77,12 @@ static void ataricom_write(struct GlobalVars *,FILE *); #ifdef BBC static void bbc_write(struct GlobalVars *,FILE *); static void bbc_write2(struct GlobalVars *,FILE *); +static FILE *bbcload; #endif #ifdef CBMPRG static unsigned long cbmprg_headersize(struct GlobalVars *); static void cbmprg_write(struct GlobalVars *,FILE *); static void cbmreu_write(struct GlobalVars *,FILE *); -static FILE *bbcload; #endif #ifdef COCOML static unsigned long cocoml_headersize(struct GlobalVars *); @@ -90,6 +92,14 @@ static void cocoml_write(struct GlobalVars *,FILE *); static unsigned long dragonbin_headersize(struct GlobalVars *); static void dragonbin_write(struct GlobalVars *,FILE *); #endif +#ifdef FOENIX +static unsigned long pgx_headersize(struct GlobalVars *); +static void pgxc02_write(struct GlobalVars *,FILE *); +static void pgx816_write(struct GlobalVars *,FILE *); +static unsigned long pgz_headersize(struct GlobalVars *); +static void pgz24_write(struct GlobalVars *,FILE *); +static void pgz32_write(struct GlobalVars *,FILE *); +#endif #ifdef JAGSRV #define TOSHDR_SIZE 28 #define JAGR_SIZE 18 @@ -98,11 +108,13 @@ static void jagsrv_write(struct GlobalVars *,FILE *); #endif #ifdef ORICMC static int oric_options(struct GlobalVars *,int,const char **,int *); +static void oric_printhelp(void); static unsigned long oric_headersize(struct GlobalVars *); static void oric_write(struct GlobalVars *,FILE *); #endif #ifdef SINCQL static int sincql_options(struct GlobalVars *,int,const char **,int *); +static void sincql_printhelp(void); static unsigned long sincql_headersize(struct GlobalVars *); static void sincql_write(struct GlobalVars *,FILE *); #endif @@ -128,7 +140,10 @@ static long hdroffs; static lword relocsize; static struct LinkedSection *hdrsect; -static int autoexec; /* used by oricmc */ +static bool multifile; /* used by rawbin */ +static bool coalesced; /* used by rawbin */ +static lword fillval; /* used by rawbin */ +static bool autoexec; /* used by oricmc */ static lword stacksize; /* used by sinclairql */ static lword udatasize; /* used by sinclairql */ @@ -142,13 +157,14 @@ static const char defaultscript[] = "}\n"; -#ifdef RAWBIN1 -struct FFFuncs fff_rawbin1 = { - "rawbin1", +#ifdef RAWBIN +struct FFFuncs fff_rawbin = { + "rawbin", defaultscript, NULL, NULL, - NULL, + rawbin_options, + rawbin_printhelp, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -160,7 +176,7 @@ struct FFFuncs fff_rawbin1 = { NULL,NULL,NULL, rawbin_writeobject, rawbin_writeshared, - rawbin_writesingle, + rawbin_write, NULL,NULL, 0, 0x8000, @@ -170,39 +186,7 @@ struct FFFuncs fff_rawbin1 = { -1, /* endianness undefined, only write */ 0, /* addr_bits from input */ 0, /* ptr-alignment unknown */ - FFF_SECTOUT|FFF_KEEPRELOCS -}; -#endif - -#ifdef RAWBIN2 -struct FFFuncs fff_rawbin2 = { - "rawbin2", - defaultscript, - NULL, - NULL, - NULL, - rawbin_headersize, - rawbin_identify, - rawbin_readconv, - NULL, - rawbin_targetlink, - NULL, - NULL, - NULL, - NULL,NULL,NULL, - rawbin_writeobject, - rawbin_writeshared, - rawbin_writemultiple, - NULL,NULL, - 0, - 0x8000, - 0, - 0, - RTAB_UNDEF,0, - -1, /* endianness undefined, only write */ - 0, /* addr_bits from input */ - 0, /* ptr-alignment unknown */ - FFF_SECTOUT|FFF_KEEPRELOCS + FFF_SECTOUT|FFF_KEEPRELOCS|FFF_OUTWORDADDR }; #endif @@ -229,6 +213,7 @@ struct FFFuncs fff_amsdos = { NULL, NULL, NULL, + NULL, amsdos_headersize, rawbin_identify, rawbin_readconv, @@ -260,6 +245,7 @@ struct FFFuncs fff_applebin = { NULL, NULL, NULL, + NULL, applebin_headersize, rawbin_identify, rawbin_readconv, @@ -291,6 +277,7 @@ struct FFFuncs fff_ataricom = { NULL, NULL, NULL, + NULL, ataricom_headersize, rawbin_identify, rawbin_readconv, @@ -322,6 +309,7 @@ struct FFFuncs fff_bbc = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -350,6 +338,7 @@ struct FFFuncs fff_bbc2 = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -381,6 +370,7 @@ struct FFFuncs fff_cbmprg = { NULL, NULL, NULL, + NULL, cbmprg_headersize, rawbin_identify, rawbin_readconv, @@ -409,6 +399,7 @@ struct FFFuncs fff_cbmreu = { NULL, NULL, NULL, + NULL, cbmprg_headersize, rawbin_identify, rawbin_readconv, @@ -440,6 +431,7 @@ struct FFFuncs fff_cocoml = { NULL, NULL, NULL, + NULL, cocoml_headersize, rawbin_identify, rawbin_readconv, @@ -471,6 +463,7 @@ struct FFFuncs fff_dragonbin = { NULL, NULL, NULL, + NULL, dragonbin_headersize, rawbin_identify, rawbin_readconv, @@ -495,6 +488,126 @@ struct FFFuncs fff_dragonbin = { }; #endif +#ifdef FOENIX +struct FFFuncs fff_pgxc02 = { + "foenixpgx-c02", + defaultscript, + NULL, + NULL, + NULL, + NULL, + pgx_headersize, + rawbin_identify, + rawbin_readconv, + NULL, + rawbin_targetlink, + NULL, + NULL, + NULL, + NULL,NULL,NULL, + rawbin_writeobject, + rawbin_writeshared, + pgxc02_write, + NULL,NULL, + 0, + 0, + 0, + 0, + RTAB_UNDEF,0, + _LITTLE_ENDIAN_, + 16,0, + FFF_SECTOUT +}; + +struct FFFuncs fff_pgx816 = { + "foenixpgx-816", + defaultscript, + NULL, + NULL, + NULL, + NULL, + pgx_headersize, + rawbin_identify, + rawbin_readconv, + NULL, + rawbin_targetlink, + NULL, + NULL, + NULL, + NULL,NULL,NULL, + rawbin_writeobject, + rawbin_writeshared, + pgx816_write, + NULL,NULL, + 0, + 0, + 0, + 0, + RTAB_UNDEF,0, + _LITTLE_ENDIAN_, + 24,0, + FFF_SECTOUT +}; + +struct FFFuncs fff_pgz24 = { + "foenixpgz-24", + defaultscript, + NULL, + NULL, + NULL, + NULL, + pgz_headersize, + rawbin_identify, + rawbin_readconv, + NULL, + rawbin_targetlink, + NULL, + NULL, + NULL, + NULL,NULL,NULL, + rawbin_writeobject, + rawbin_writeshared, + pgz24_write, + NULL,NULL, + 0, + 0, + 0, + 0, + RTAB_UNDEF,0, + -1,0,0, /* undefined, only write */ + FFF_SECTOUT +}; + +struct FFFuncs fff_pgz32 = { + "foenixpgz-32", + defaultscript, + NULL, + NULL, + NULL, + NULL, + pgz_headersize, + rawbin_identify, + rawbin_readconv, + NULL, + rawbin_targetlink, + NULL, + NULL, + NULL, + NULL,NULL,NULL, + rawbin_writeobject, + rawbin_writeshared, + pgz32_write, + NULL,NULL, + 0, + 0, + 0, + 0, + RTAB_UNDEF,0, + -1,0,0, /* undefined, only write */ + FFF_SECTOUT +}; +#endif + #ifdef JAGSRV static const char jaguarscript[] = "SECTIONS {\n" @@ -521,6 +634,7 @@ struct FFFuncs fff_jagsrv = { NULL, NULL, NULL, + NULL, jagsrv_headersize, rawbin_identify, rawbin_readconv, @@ -552,6 +666,7 @@ struct FFFuncs fff_oricmc = { NULL, NULL, oric_options, + oric_printhelp, oric_headersize, rawbin_identify, rawbin_readconv, @@ -583,6 +698,7 @@ struct FFFuncs fff_sincql = { NULL, NULL, sincql_options, + sincql_printhelp, sincql_headersize, rawbin_identify, rawbin_readconv, @@ -613,6 +729,7 @@ struct FFFuncs fff_srec19 = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -645,6 +762,7 @@ struct FFFuncs fff_srec28 = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -677,6 +795,7 @@ struct FFFuncs fff_srec37 = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -709,6 +828,7 @@ struct FFFuncs fff_ihex = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -730,7 +850,7 @@ struct FFFuncs fff_ihex = { -1, /* endianness undefined, only write */ 0, /* addr_bits from input */ 0, /* ptr-alignment unknown */ - FFF_NOFILE + FFF_NOFILE|FFF_OUTWORDADDR }; #endif @@ -741,6 +861,7 @@ struct FFFuncs fff_shex1 = { NULL, NULL, NULL, + NULL, rawbin_headersize, rawbin_identify, rawbin_readconv, @@ -767,6 +888,31 @@ struct FFFuncs fff_shex1 = { #endif +#ifdef RAWBIN +static int rawbin_options(struct GlobalVars *gv, + int argc,const char **argv,int *i) +{ + if (!strcmp(argv[*i],"-coalesced")) + coalesced = TRUE; + else if (!strcmp(argv[*i],"-multifile")) + multifile = TRUE; + else if (!strncmp(argv[*i],"-fill=",6)) { + long long val; + sscanf(&argv[*i][6],"%lli",&val); + fillval = val; + } + else + return 0; + return 1; +} + +static void rawbin_printhelp(void) +{ + printf("-coalesced output all memory segments without gaps\n" + "-multifile start a new file for a gap between memory segments\n"); +} +#endif + #ifdef ORICMC static int oric_options(struct GlobalVars *gv, int argc,const char **argv,int *i) @@ -777,6 +923,11 @@ static int oric_options(struct GlobalVars *gv, return 0; return 1; } + +static void oric_printhelp(void) +{ + printf("-autox set auto-execute flag in the header\n"); +} #endif #ifdef SINCQL @@ -799,6 +950,13 @@ static int sincql_options(struct GlobalVars *gv, return 0; return 1; } + +static void sincql_printhelp(void) +{ + printf("-qhdr prepend QDOS file header (default)\n" + "-xtcc append XTcc style trailer\n" + "-stack= set stack size, which defaults to 4096\n"); +} #endif @@ -849,10 +1007,28 @@ static unsigned long dragonbin_headersize(struct GlobalVars *gv) } #endif +#ifdef FOENIX +static unsigned long pgx_headersize(struct GlobalVars *gv) +{ + return 8; +} + +static unsigned long pgz_headersize(struct GlobalVars *gv) +{ + return 1; /* followed by 6 or 8 bytes segment header */ +} +#endif + #ifdef ORICMC static unsigned long oric_headersize(struct GlobalVars *gv) { - return 14+strlen(gv->dest_name)+1; + unsigned long len = strlen(gv->dest_name); + + if (len>4 && !stricmp(gv->dest_name+len-4,".tap")) + len -= 4; + if (len > 15) + len = 15; + return 15+len; } #endif @@ -918,7 +1094,7 @@ static FILE *open_ascfile(struct GlobalVars *gv) error(29,gv->dest_name); /* Can't create output file */ return NULL; } - untrim_sections(gv); /* always output trailing 0-bytes in ASCII hex-files */ + untrim_sections(gv,1); /* always output trailing 0-bytes in ASCII hex-files */ return f; } @@ -930,7 +1106,7 @@ static void savehdroffs(struct GlobalVars *gv,FILE *f, hdrsect = ls; hdrfile = f; hdroffs = ftell(f); - fwritegap(gv,f,bytes); + fwritegap(gv,f,bytes,0); } @@ -951,6 +1127,14 @@ static void even_padding(FILE *f) } +static void trace_baseaddr(struct GlobalVars *gv,struct LinkedSection *ls) +{ + if (gv->trace_file) + fprintf(gv->trace_file,"Base address = 0x%08llx.\n", + (unsigned long long)ls->copybase); /* @@@ FIXME! */ +} + + #ifdef AMSDOS static void amsdos_header(FILE *f,uint16_t loadaddr,unsigned size) { @@ -1012,6 +1196,20 @@ static int rawbin_segheader(struct GlobalVars *gv,FILE *f, fwrite16be(f,ls->copybase); return 1; } +#endif +#ifdef FOENIX + if (header == HDR_PGZ24) { /* 24-bit LE load-address and size */ + fwrite16le(f,ls->copybase&0xffff); + fwrite8(f,(ls->copybase>>16)&0xff); + fwrite16le(f,ls->size&0xffff); + fwrite8(f,(ls->size>>16)&0xff); + return 1; + } + if (header == HDR_PGZ32) { /* 32-bit LE load-address and size */ + fwrite32le(f,ls->copybase); + fwrite32le(f,ls->size); + return 1; + } #endif return 0; } @@ -1067,17 +1265,35 @@ static void rawbin_fileheader(struct GlobalVars *gv,FILE *f, fwrite8(f,0xaa); } #endif +#ifdef FOENIX + if (header==HDR_PGXC02 || header==HDR_PGX816) { /* single seg. header */ + static uint8_t hdr[4] = { 'P','G','X' }; + hdr[3] = header==HDR_PGX816 ? 1 : 3; + fwritex(f,hdr,sizeof(hdr)); + fwrite32le(f,ls->copybase); + } + if (header == HDR_PGZ24) { /* 24-bit Foenix multi segment header */ + fwrite8(f,'Z'); + } + if (header == HDR_PGZ32) { /* 32-bit Foenix multi segment header */ + fwrite8(f,'z'); + } +#endif #ifdef ORICMC if (header == HDR_ORICMC) { /* Oric machine code file header */ static const uint8_t hdr[] = { - 0x16,0x16,0x16,0x16,0x24,0,0x80 + 0x16,0x16,0x16,0x16,0x24,0,0,0x80 }; + int i; fwritex(f,hdr,sizeof(hdr)); fwrite8(f,autoexec?0xc7:0); savehdroffs(gv,f,2,ls); /* insert last address later */ fwrite16be(f,ls->copybase); fwrite8(f,0); - fwritex(f,gv->dest_name,strlen(gv->dest_name)+1); + for (i=0; i<15 && gv->dest_name[i] && stricmp(&gv->dest_name[i],".tap"); + i++) + fwrite8(f,toupper((unsigned char)gv->dest_name[i])); + fwrite8(f,0); } #endif #ifdef JAGSRV @@ -1122,6 +1338,18 @@ static void rawbin_trailer(struct GlobalVars *gv,FILE *f, if (header == HDR_DRAGONBIN && rewindtohdr(f)) fwrite16be(f,ls->copybase+ls->size-hdrsect->copybase); #endif +#ifdef FOENIX + if (header == HDR_PGZ24) { + fwrite16le(f,execaddr&0xffff); + fwrite8(f,(execaddr>>16)&0xff); + fwrite16le(f,0); /* no more segments */ + fwrite8(f,0); + } + if (header == HDR_PGZ32) { + fwrite32le(f,execaddr); + fwrite32le(f,0); /* no more segments */ + } +#endif #ifdef ORICMC if (header == HDR_ORICMC && rewindtohdr(f)) fwrite16be(f,ls->copybase+ls->size-1); @@ -1131,6 +1359,11 @@ static void rawbin_trailer(struct GlobalVars *gv,FILE *f, even_padding(f); /* align to even address */ fwrite32be(f,0x58546363); /* XTcc ID */ } + else if (header == HDR_QDOS) { + /* even with QDOS header, the file size must be even, and the + packed reloc table size is not guaranteed to be even */ + even_padding(f); /* align to even address */ + } if (header==HDR_XTCC || (header == HDR_QDOS && rewindtohdr(f))) { fwrite32be(f,(uint32_t)(relocsize+64>udatasize+stacksize? ((relocsize+65)&~1):udatasize+stacksize)); @@ -1145,15 +1378,14 @@ static void rawbin_trailer(struct GlobalVars *gv,FILE *f, } -static void rawbin_writeexec(struct GlobalVars *gv,FILE *f,bool singlefile, - int header) +static void rawbin_out(struct GlobalVars *gv,FILE *f,int header) /* creates executable raw-binary files (with absolute addresses) */ { - const char *fn = "rawbin_writeexec(): "; + const char *fn = "rawbin_out(): "; bool dorelocs = gv->keep_relocs && (fff[gv->dest_format]->flags & FFF_KEEPRELOCS); FILE *firstfile = f; bool firstsec = TRUE; - unsigned long addr,baseaddr; + lword addr,baseaddr; struct LinkedSection *ls,*prevls; struct BinReloc *brp,*brlist=NULL; size_t brcnt = 0; @@ -1171,8 +1403,8 @@ static void rawbin_writeexec(struct GlobalVars *gv,FILE *f,bool singlefile, if (ls->size != 0) { /* make a new file for each output section */ if (gv->trace_file) - fprintf(gv->trace_file,"Base address section %s = 0x%08lx.\n", - ls->name,ls->copybase); + fprintf(gv->trace_file,"Base address section %s = 0x%08llx.\n", + ls->name,(unsigned long long)ls->copybase); /* @@@ FIXME */ if (f != NULL) ierror("%sopen file with output_sections",fn); if (gv->osec_base_name != NULL) { @@ -1194,27 +1426,28 @@ static void rawbin_writeexec(struct GlobalVars *gv,FILE *f,bool singlefile, else if (firstsec) { /* write an optional header before the first section */ baseaddr = ls->copybase; - if (gv->trace_file) - fprintf(gv->trace_file,"Base address = 0x%08lx.\n",ls->copybase); + trace_baseaddr(gv,ls); firstsec = FALSE; rawbin_fileheader(gv,f,ls,header); } else { /* handle gaps between this and the previous section */ if (ls->copybase > addr) { - if (ls->copybase-addr < MAXGAP || singlefile) { - if (!rawbin_segheader(gv,f,ls,header)) - fwritegap(gv,f,ls->copybase-addr); + if (ls->copybase-addr < MAXGAP || !multifile) { + if (!rawbin_segheader(gv,f,ls,header) && !coalesced) + fwritegap(gv,f,ls->copybase-addr,fillval); } else { /* open a new file for this section */ if (f != firstfile) fclose(f); name = alloc(strlen(gv->dest_name)+strlen(ls->name)+2); - if(header == HDR_BBC){ - sprintf(name,"%s%s",gv->dest_name,ls->name); - if(bbcload) - fprintf(bbcload,"*srload %s%s 8000 %s\r",gv->dest_name,ls->name,ls->name+1); - }else +#ifdef BBC + if(header == HDR_BBC){ + sprintf(name,"%s%s",gv->dest_name,ls->name); + if(bbcload) + fprintf(bbcload,"*srload %s%s 8000 %s\r",gv->dest_name,ls->name,ls->name+1); + }else +#endif sprintf(name,"%s.%s",gv->dest_name,ls->name); if (!(f = fopen(name,"wb"))) { error(29,name); @@ -1316,7 +1549,7 @@ static void rawbin_writeexec(struct GlobalVars *gv,FILE *f,bool singlefile, if (diff < 0) { ierror("%snegative reloc offset diff %ld\n",fn,(long)diff); } - else if (diff > limit) { + else if (diff==0 || diff>limit) { fwritetbyte(gv,f,0); /* 0 indicates an address-size difference */ fwritetaddr(gv,f,diff); /* ...in the next word */ relocsize += gv->tbytes_per_taddr; @@ -1354,22 +1587,13 @@ static void rawbin_writeobject(struct GlobalVars *gv,FILE *f) } -#ifdef RAWBIN1 -static void rawbin_writesingle(struct GlobalVars *gv,FILE *f) -/* creates a single raw-binary file, fill gaps between */ -/* sections with zero */ -{ - rawbin_writeexec(gv,f,TRUE,HDR_NONE); -} -#endif - - -#ifdef RAWBIN2 -static void rawbin_writemultiple(struct GlobalVars *gv,FILE *f) -/* creates raw-binary which might get split over several */ -/* files, because of different section base addresses */ +#ifdef RAWBIN +static void rawbin_write(struct GlobalVars *gv,FILE *f) +/* creates a raw-binary file, where memory segments are either written to + multiple files, coalesced to the previous segment or written with a gap + filled by zero-bytes */ { - rawbin_writeexec(gv,f,FALSE,HDR_NONE); + rawbin_out(gv,f,HDR_NONE); } #endif @@ -1379,7 +1603,8 @@ static void amsdos_write(struct GlobalVars *gv,FILE *f) /* creates one or more raw-binary files with an AMSDOS header, suitable */ /* for loading as an executable on Amstrad/Scheider CPC computers */ { - rawbin_writeexec(gv,f,FALSE,HDR_AMSDOS); + multifile = TRUE; + rawbin_out(gv,f,HDR_AMSDOS); } #endif @@ -1389,7 +1614,7 @@ static void applebin_write(struct GlobalVars *gv,FILE *f) /* creates a raw-binary file with an Apple DOS binary header, suitable */ /* for loading as an executable on Apple II computers */ { - rawbin_writeexec(gv,f,TRUE,HDR_APPLEBIN); + rawbin_out(gv,f,HDR_APPLEBIN); } #endif @@ -1399,7 +1624,7 @@ static void ataricom_write(struct GlobalVars *gv,FILE *f) /* creates a raw-binary file with an ATARI COM file header, suitable */ /* for loading as an executable on Atari 8-bit computers */ { - rawbin_writeexec(gv,f,TRUE,HDR_ATARICOM); + rawbin_out(gv,f,HDR_ATARICOM); } #endif @@ -1409,14 +1634,15 @@ static void cbmprg_write(struct GlobalVars *gv,FILE *f) /* creates a raw-binary file with a Commodore header, suitable */ /* for loading as an executable on PET, VIC-20, 64, etc. computers */ { - rawbin_writeexec(gv,f,TRUE,HDR_CBMPRG); + rawbin_out(gv,f,HDR_CBMPRG); } static void cbmreu_write(struct GlobalVars *gv,FILE *f) /* creates a raw-binary file with a Commodore header, followed by */ /* a REU image. */ { - rawbin_writeexec(gv,f,FALSE,HDR_CBMPRG); + multifile = TRUE; + rawbin_out(gv,f,HDR_CBMPRG); } #endif @@ -1426,7 +1652,7 @@ static void cocoml_write(struct GlobalVars *gv,FILE *f) /* creates a raw-binary file with CoCo segment headers and trailer, suitable */ /* for loading as a machine language file on Tandy Color Computer 1,2,3 */ { - rawbin_writeexec(gv,f,TRUE,HDR_COCOML); + rawbin_out(gv,f,HDR_COCOML); } #endif @@ -1436,7 +1662,42 @@ static void dragonbin_write(struct GlobalVars *gv,FILE *f) { /* creates a raw-binary file with a Dragon DOS file header, suitable */ /* for loading as an executable on Dragon 32 and 64 computers */ - rawbin_writeexec(gv,f,TRUE,HDR_DRAGONBIN); + rawbin_out(gv,f,HDR_DRAGONBIN); +} +#endif + + +#ifdef FOENIX +static void pgxc02_write(struct GlobalVars *gv,FILE *f) +{ +/* creates a raw-binary file with a PGX single-segment file header, */ +/* suitable for loading as an executable on the 65C02-based */ +/* C256 Foenix computers */ + rawbin_out(gv,f,HDR_PGXC02); +} + +static void pgx816_write(struct GlobalVars *gv,FILE *f) +{ +/* creates a raw-binary file with a PGX single-segment file header, */ +/* suitable for loading as an executable on the 65816-based */ +/* C256 Foenix computers */ + rawbin_out(gv,f,HDR_PGX816); +} + +static void pgz24_write(struct GlobalVars *gv,FILE *f) +{ +/* creates a raw-binary file with a PGZ file header and multiple segment */ +/* headers, suitable for loading as an executable on a 16- or 24-bit */ +/* Foenix platform */ + rawbin_out(gv,f,HDR_PGZ24); +} + +static void pgz32_write(struct GlobalVars *gv,FILE *f) +{ +/* creates a raw-binary file with a PGZ file header and multiple segment */ +/* headers, suitable for loading as an executable on a 32-bit */ +/* Foenix platform */ + rawbin_out(gv,f,HDR_PGZ32); } #endif @@ -1446,7 +1707,7 @@ static void oric_write(struct GlobalVars *gv,FILE *f) { /* creates a raw-binary file with an Oric machine code file header, */ /* suitable for loading as an executable on Oric-1/Atmos/Telestrat computers */ - rawbin_writeexec(gv,f,TRUE,HDR_ORICMC); + rawbin_out(gv,f,HDR_ORICMC); } #endif @@ -1457,7 +1718,7 @@ static void jagsrv_write(struct GlobalVars *gv,FILE *f) /* for loading and executing on a SkunkBoard equipped Atari Jaguar, or */ /* a VirtualJaguar emulator */ { - rawbin_writeexec(gv,f,TRUE,HDR_JAGSRV); + rawbin_out(gv,f,HDR_JAGSRV); } #endif @@ -1465,15 +1726,17 @@ static void jagsrv_write(struct GlobalVars *gv,FILE *f) static void bbc_write(struct GlobalVars *gv,FILE *f) /* creates a single raw-binary file plus a bbc info file */ { - rawbin_writeexec(gv,f,TRUE,HDR_BBC); + rawbin_out(gv,f,HDR_BBC); } + static void bbc_write2(struct GlobalVars *gv,FILE *f) /* creates a single raw-binary file plus a bbc info file */ { char *name = alloc(strlen(gv->dest_name)+8); sprintf(name,"load%s",gv->dest_name); bbcload = fopen(name,"w"); - rawbin_writeexec(gv,f,FALSE,HDR_BBC); + multifile = TRUE; + rawbin_out(gv,f,HDR_BBC); free(name); } #endif @@ -1498,7 +1761,7 @@ static void sincql_write(struct GlobalVars *gv,FILE *f) stacksize = 4096; /* default */ if ((udatasize + stacksize) & 1) ++udatasize; /* make sure bss-segment has an even number of bytes */ - rawbin_writeexec(gv,f,TRUE,sincqlhdr); + rawbin_out(gv,f,sincqlhdr); } #endif @@ -1540,8 +1803,7 @@ static void srec_write(struct GlobalVars *gv,FILE *f,int addrsize) continue; /* ignore empty sections */ if (firstsec) { - if (gv->trace_file) - fprintf(gv->trace_file,"Base address = 0x%08lx.\n",ls->copybase); + trace_baseaddr(gv,ls); firstsec = FALSE; } @@ -1617,7 +1879,8 @@ static void srec37_write(struct GlobalVars *gv,FILE *f) #ifdef IHEX -static void IHexOut(FILE *f,unsigned long addr,uint8_t *buf,int len) +static void IHexOut(FILE *f,int out_le,unsigned long addr,uint8_t *buf, + int len,size_t opb) { static unsigned long hiaddr; uint8_t chksum; @@ -1630,11 +1893,29 @@ static void IHexOut(FILE *f,unsigned long addr,uint8_t *buf,int len) (unsigned)(-(2+4+(hiaddr>>8)+hiaddr))&0xff); } fprintf(f,":%02X%02X%02X00", - (unsigned)len&0xff,(unsigned)(addr>>8)&0xff,(unsigned)addr&0xff); - chksum = len + (addr>>8) + addr; + (unsigned)(len*opb)&0xff, + (unsigned)(addr>>8)&0xff, + (unsigned)addr&0xff); + chksum = len*opb + (addr>>8) + addr; for (; len>0; len--) { - fprintf(f,"%02X",((unsigned)*buf)&0xff); - chksum += *buf++; + uint8_t b; + int i; + + if (out_le) { + for (i=opb-1; i>=0; i--) { + b = buf[i]; + fprintf(f,"%02X",(unsigned)b); + chksum += b; + } + } + else { + for (i=0; ioctets_per_tbyte; bool firstsec = TRUE; struct LinkedSection *ls; - unsigned long len,addr; + unsigned long addr,len; uint8_t *p; /* open ASCII output file */ @@ -1656,22 +1938,24 @@ static void ihex_write(struct GlobalVars *gv,FILE *f) continue; /* ignore empty sections */ if (firstsec) { - if (gv->trace_file) - fprintf(gv->trace_file,"Base address = 0x%08lx.\n",ls->copybase); + trace_baseaddr(gv,ls); firstsec = FALSE; } /* resolve all relocations and write section contents */ calc_relocs(gv,ls); - for (p=ls->data,addr=ls->copybase,len=ls->filesize; len>0; ) { - int nbytes = (len>MAXIREC) ? MAXIREC : len; - - if ((((unsigned long)addr)&0xffff)+nbytes > 0xffff) - nbytes = 0x10000 - (((unsigned long)addr) & 0xffff); - IHexOut(f,addr,p,nbytes); - p += nbytes; - addr += nbytes; + p = ls->data; + addr = ls->copybase * opb; /* yes, that's probably how it is done */ + len = ls->filesize; + while (len > 0) { + int nbytes = len > MAXIREC/opb ? MAXIREC/opb : len; + + if ((((unsigned long)addr)&0xffff) + nbytes*opb > 0xffff) + nbytes = (0x10000 - (((unsigned long)addr) & 0xffff)) / opb; + IHexOut(f,gv->output_le,addr,p,nbytes,opb); + p += nbytes * opb; + addr += nbytes * opb; len -= nbytes; } } @@ -1716,8 +2000,7 @@ static void shex1_write(struct GlobalVars *gv,FILE *f) continue; /* ignore empty sections */ if (firstsec) { - if (gv->trace_file) - fprintf(gv->trace_file,"Base address = 0x%08lx.\n",ls->copybase); + trace_baseaddr(gv,ls); firstsec = FALSE; } diff --git a/t_rawseg.c b/t_rawseg.c index 01a9437..66770dd 100644 --- a/t_rawseg.c +++ b/t_rawseg.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_rawseg.c V0.16h (10.03.21) +/* $VER: vlink t_rawseg.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2021 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -49,6 +49,7 @@ struct FFFuncs fff_rawseg = { NULL, NULL, NULL, + NULL, rawseg_headersize, rawseg_identify, rawseg_readconv, @@ -131,7 +132,7 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) { const char *fn = "rawseg_writeexec(): "; bool firstsec; - unsigned long addr; + lword addr; FILE *segf; struct Phdr *p; struct LinkedSection *ls,*prevls; @@ -149,8 +150,7 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) /* write segment's sections */ for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - if (ls->copybase>=(unsigned long)p->start && - (ls->copybase+ls->size)<=(unsigned long)p->mem_end && + if (ls->copybase>=p->start && (ls->copybase+ls->size)<=p->mem_end && (ls->flags & SF_ALLOC) && !(ls->ld_flags & LSF_NOLOAD)) { if (gv->keep_relocs) { @@ -191,9 +191,8 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) for (relph=gv->phdrlist; relph; relph=relph->next) { if (relph->type==PT_LOAD && (relph->flags&PHDR_USED) && relph->start!=ADDR_NONE && relph->start_vma!=ADDR_NONE) { - if (relsec->copybase>=(unsigned long)relph->start && - (relsec->copybase+relsec->size)<= - (unsigned long)relph->mem_end) + if (relsec->copybase>=relph->start && + (relsec->copybase+relsec->size)<=relph->mem_end) break; } } @@ -202,7 +201,7 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) lword segoffs,v; struct SegReloc *newsr,*srp; - segoffs = (lword)relsec->copybase - relph->start; + segoffs = relsec->copybase - relph->start; v = writesection(gv,ls->data,r->offset,r,r->addend+segoffs); if (v != 0) { /* Calculated value doesn't fit into relocation type x ... */ @@ -226,8 +225,7 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) newsr = alloc(sizeof(struct SegReloc)); newsr->next = NULL; newsr->seg = relph; - newsr->offset = (ls->copybase - (unsigned long)p->start) + - r->offset; + newsr->offset = (ls->copybase - p->start) + r->offset; if (srp = srlist) { while (srp->next) srp = srp->next; @@ -261,14 +259,14 @@ static void rawseg_writeexec(struct GlobalVars *gv,FILE *f) if (!firstsec) { /* write an alignment gap, when needed */ if (ls->copybase > addr) - fwritegap(gv,segf,ls->copybase-addr); + fwritegap(gv,segf,ls->copybase-addr,0); else if (ls->copybase < addr) error(98,fff[gv->dest_format]->tname,ls->name,prevls->name); } else firstsec = FALSE; - fwritex(segf,ls->data,tbytes(gv,ls->size)); + fwritefullsect(gv,segf,ls); addr = ls->copybase + ls->size; prevls = ls; diff --git a/t_vobj.c b/t_vobj.c index ce101e1..68bf3a7 100644 --- a/t_vobj.c +++ b/t_vobj.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_vobj.c V0.17a (30.03.22) +/* $VER: vlink t_vobj.c V0.18 (03.07.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #include "config.h" @@ -10,16 +10,19 @@ #define T_VOBJ_C #include "vlink.h" #include "vobj.h" - +#include "cpurelocs.h" /* - VOBJ Format (WILL CHANGE!): + Format: .byte 0x56,0x4f,0x42,0x4a .byte flags - 1: BIGENDIAN - 2: LITTLENDIAN - .number bitsperbyte + Bits 0-1: + 1: BIGENDIAN + 2: LITTLENDIAN + Bits 2-7: + VOBJ-Version (0-based) + .number bitsperbyte .number bytespertaddr .string cpu .number nsections [1-based] @@ -31,17 +34,17 @@ nsymbols .number flags .number secindex .number val - .number size + .number size (in target-bytes) nsections .string name .string attr .number flags .number align - .number size + .number size (in target-bytes) .number nrelocs - .number databytes - .byte[databytes] + .number databytes (in target-bytes) + .byte[databytes*(BITSPERBYTE+7)/8] nrelocs [standard|special] standard @@ -60,10 +63,12 @@ special .number:[taddr] .byte 0--127 [0--127] - .byte 128-255 [x-0x80 bytes little-endian] - + .byte 128-191 [x-0x80 bytes little-endian], fill remaining with 0 + .byte 192-255 [x-0xC0 bytes little-endian], fill remaining with 0xff [vobj version 2+] */ +static int vobj_options(struct GlobalVars *,int,const char **,int *); +static void vobj_printhelp(void); static unsigned long vobj_headersize(struct GlobalVars *); static int vobjle_identify(struct GlobalVars *,char *,uint8_t *, unsigned long,bool); @@ -82,7 +87,8 @@ struct FFFuncs fff_vobj_le = { NULL, NULL, NULL, - NULL, + vobj_options, + vobj_printhelp, vobj_headersize, vobjle_identify, vobj_readconv, @@ -103,7 +109,8 @@ struct FFFuncs fff_vobj_le = { RTAB_UNDEF,0, _LITTLE_ENDIAN_, 0, /* defined by VOBJ bytespertaddr*bitsperbyte */ - 0 /* irrelevant - no output format */ + 0, /* ptr alignment is unknown */ + FFF_OUTWORDADDR }; struct FFFuncs fff_vobj_be = { @@ -111,7 +118,8 @@ struct FFFuncs fff_vobj_be = { NULL, NULL, NULL, - NULL, + vobj_options, + vobj_printhelp, vobj_headersize, vobjbe_identify, vobj_readconv, @@ -132,44 +140,93 @@ struct FFFuncs fff_vobj_be = { RTAB_UNDEF,0, _BIG_ENDIAN_, 0, /* defined by VOBJ bytespertaddr*bitsperbyte */ - 0 /* irrelevant - no output format */ + 0, /* ptr alignment is unknown */ + FFF_OUTWORDADDR }; -static struct ar_info ai; /* for scanning library archives */ static uint8_t *p; +static int bitspertaddr; +static const char *vobjcpu; +static int vobjcpuid,vobjver; -/*****************************************************************/ -/* Read VOBJ */ -/*****************************************************************/ +static void vobj_setcpu(const char *id) +{ + static const char *cpus[] = { /* MUST be same order as in cpurelocs.h! */ + "PowerPC" + }; + static const int num_cpus = sizeof(cpus)/sizeof(cpus[0]); + int i; + + if (vobjcpu!=NULL && strcmp(vobjcpu,id)!=0) { + error(156,vobjcpu); /* alternating definitions, keeping the first one */ + return; + } + vobjcpu = id; + vobjcpuid = 0; + for (i=0; i define CPU name, required for spec. relocs with -r\n"); +} + static unsigned long vobj_headersize(struct GlobalVars *gv) { - return 0; /* irrelevant - no write format */ + return 0; /* irrelevant - not used for executable files */ } + +/*****************************************************************/ +/* Read VOBJ */ +/*****************************************************************/ + static taddr read_number(int is_signed) { - int bitcnt; taddr val; - uint8_t n,*q; + uint8_t n; + int i; if ((n = *p++) <= 0x7f) return (taddr)n; - val = 0; - if (n -= 0x80) { - p += n; - q = p; - bitcnt = n << 3; - while (n--) - val = (val<<8) | *(--q); + if (n >= 0xc0) { /* v2 negative numbers */ + n -= 0xc0; + for (i=0,val=~makemask(n*8); n--; i+=8) + val |= (taddr)*p++ << i; + } + else { + for (i=0,n-=0x80,val=0; n--; i+=8) + val |= (taddr)*p++ << i; if (is_signed) - val = sign_extend(val,bitcnt); + val = sign_extend(val,bitspertaddr); /* v1 negative numbers support */ } + return val; } @@ -185,6 +242,7 @@ static void skip_string(void) static int vobj_identify(struct GlobalVars *gv,struct FFFuncs *fff,char *name, uint8_t *dat,unsigned long plen,uint8_t e) { + struct ar_info ai; int id = ID_OBJECT; if (ar_init(&ai,(char *)dat,plen,name)) { @@ -201,7 +259,7 @@ static int vobj_identify(struct GlobalVars *gv,struct FFFuncs *fff,char *name, p = dat; if (plen>8 && p[0]==0x56 && p[1]==0x4f && p[2]==0x42 && - p[3]==0x4a && p[4]==e) { + p[3]==0x4a && (p[4]&3)==e && (p[4]>>2)+1<=VOBJ_MAX_VERSION) { int bpt,bpb; p += 5; @@ -230,6 +288,7 @@ static int vobj_identify(struct GlobalVars *gv,struct FFFuncs *fff,char *name, if (bpt * bpb > (int)gv->bits_per_taddr) gv->bits_per_taddr = bpt * bpb; /* set bits per taddr from this VOBJ */ + vobj_setcpu((const char *)p); return id; } @@ -257,7 +316,8 @@ static void vobj_check_ar_type(struct GlobalVars *gv,struct FFFuncs *ff, p = dat; if (p[0]==0x56 && p[1]==0x4f && p[2]==0x42 && p[3]==0x4a && - p[4]==(ff->endianness ? 1 : 2)) { + (p[4]&3)==(ff->endianness ? 1 : 2) && + (p[4]>>2)+1<=VOBJ_MAX_VERSION) { p += 5; bpb = (int)read_number(0); /* bits per byte */ if ((bpb & 7) != 0) { @@ -277,6 +337,8 @@ static void vobj_check_ar_type(struct GlobalVars *gv,struct FFFuncs *ff, if (bpt * bpb > (int)gv->bits_per_taddr) gv->bits_per_taddr = bpt * bpb; /* set bits per taddr from this VOBJ */ + + vobj_setcpu((const char *)p); } else error(41,name,ff->tname); @@ -313,13 +375,16 @@ static void read_section(struct GlobalVars *gv,struct ObjectUnit *u, skip_string(); /* section name */ for (attr=(char *)p; *attr; attr++) { - switch (tolower((unsigned char)*attr)) { + switch (*attr) { case 'w': prot |= SP_WRITE; break; case 'x': type = ST_CODE; prot |= SP_EXEC; break; case 'c': type = ST_CODE; prot |= SP_EXEC; break; case 'd': type = ST_DATA; break; case 'u': type = ST_UDATA; flags |= SF_UNINITIALIZED; break; - case 'a': flags |= SF_ALLOC; + case 'z': if (!strcmp(vobjcpu,"6502")) flags |= SF_DPAGE; break; + case 'a': flags |= SF_ALLOC; break; + case 'N': if (!strcmp(vobjcpu,"6502")) flags |= SF_SMALLDATA; break; + case 'H': if (!strcmp(vobjcpu,"6502")) flags |= SF_HUGEDATA; break; } } skip_string(); @@ -347,25 +412,45 @@ static void read_section(struct GlobalVars *gv,struct ObjectUnit *u, /* create relocations and unknown symbol references for this section */ for (last_reloc=NULL,last_offs=-1; nrelocs>0; nrelocs--) { struct Reloc *r; + size_t record_len = 0; char *xrefname = NULL; lword offs,mask,addend; uint16_t bpos,bsiz; - uint8_t flags; + unsigned flags; + int rtype; int sym_idx; /* read one relocation entry */ - type = (uint8_t)read_number(0); - offs = read_number(0); - bpos = (uint16_t)read_number(0); - bsiz = (uint16_t)read_number(0); - mask = read_number(1); - addend = read_number(1); - sym_idx = (int)read_number(0) - 1; /* symbol index */ - flags = 0; - - if (type>=R_NONE && type<=LAST_STANDARD_RELOC && - offs>=0 && bsiz<=(sizeof(lword)<<3) && - sym_idx>=0 && sym_idx= FIRST_CPU_RELOC) + record_len = read_number(0); /* 0 for nreloc */ + + if (record_len == 0) { + offs = read_number(0); + bpos = (uint16_t)read_number(0); + bsiz = (uint16_t)read_number(0); + mask = read_number(1); + addend = read_number(1); + sym_idx = (int)read_number(0) - 1; /* symbol index */ + flags = 0; + + if (offs<0 || bsiz>(sizeof(lword)<<3) || sym_idx<0 || sym_idx>=nsyms) + goto bad_reloc; + + if (rtype < FIRST_CPU_RELOC) { + if (rtype & VOBJ_REL_S) + flags |= RELF_S; + else if (rtype & VOBJ_REL_U) + flags |= RELF_U; + rtype = STD_REL_TYPE(rtype); + } + else { + if (vobjcpuid) + rtype = MAKE_RELOC_CPU_ID(vobjcpuid) + rtype - FIRST_CPU_RELOC; + else + goto bad_reloc; + } + if (vsyms[sym_idx].flags & WEAK) { xrefname = vsyms[sym_idx].name; index = 0; @@ -379,35 +464,29 @@ static void read_section(struct GlobalVars *gv,struct ObjectUnit *u, xrefname = vsyms[sym_idx].name; index = 0; } - else { - /* VOBJ relocation not supported */ - error(115,getobjname(u),fff[u->lnkfile->format]->tname, - (int)type,(unsigned long long)offs, - (int)bpos,(int)bsiz,(unsigned long long)mask, - vsyms[sym_idx].name,(int)vsyms[sym_idx].type); - } + else + goto bad_reloc; if (sym_idx==last_sym && offs==last_offs && last_reloc!=NULL) { r = last_reloc; } else { - r = newreloc(gv,s,xrefname,NULL,index,(unsigned long)offs,type,addend); + r = newreloc(gv,s,xrefname,NULL,index,(unsigned long)offs,rtype,addend); r->flags |= flags; last_reloc = r; last_offs = offs; last_sym = sym_idx; } - addreloc(s,r,bpos,bsiz,mask); /* make sure that section reflects the addend for other formats */ writesection(gv,data,offs,r,addend); } - - else if (type != R_NONE) { + else { /* VOBJ relocation not supported */ + bad_reloc: error(115,getobjname(u),fff[u->lnkfile->format]->tname, - (int)type,(unsigned long long)offs, + rtype,(unsigned long long)offs, (int)bpos,(int)bsiz,(unsigned long long)mask, (sym_idx>=0&&sym_idx=0&&sym_idxformat],lf->pathname,data); } p = data + 5; /* skip ID and endianness */ + bitspertaddr = gv->bits_per_taddr; /* skip bits per byte and bytes per address */ read_number(0); @@ -514,6 +594,8 @@ static void vobj_read(struct GlobalVars *gv,struct LinkFile *lf,uint8_t *data) static void vobj_readconv(struct GlobalVars *gv,struct LinkFile *lf) { + struct ar_info ai; + if (lf->type == ID_LIBARCH) { if (ar_init(&ai,(char *)lf->data,lf->length,lf->filename)) { while (ar_extract(&ai)) { @@ -538,8 +620,7 @@ static int vobj_targetlink(struct GlobalVars *gv,struct LinkedSection *ls, /* returns -1, if target doesn't want to combine them, */ /* returns 0, if target doesn't care - standard linking rules are used. */ { - ierror("vobj_targetlink(): Impossible to link vobjects"); - return (0); + return 0; } @@ -549,6 +630,306 @@ static int vobj_targetlink(struct GlobalVars *gv,struct LinkedSection *ls, /*****************************************************************/ +static void fwrite_number(FILE *f,taddr val) +{ + int i,s,u; + taddr tmp; + + if (val>=0 && val<=127) { + fwrite8(f,val); + return; + } + + if (!vobjver) { + s = u = sizeof(taddr); + } + else { + for (i=1,s=u=1,tmp=val; i<=sizeof(taddr); i++) { + if (tmp & 0xff) + s = i; + if ((tmp & 0xff) != 0xff) + u = i; + tmp >>= 8; + } + } + + if (u < s) { + fwrite8(f,0xC0+u); + s = u; + } + else + fwrite8(f,0x80+s); + + for (i=s; i>0; i--) { + fwrite8(f,val&0xff); + val >>= 8; + } +} + + +static void fwrite_string(FILE *f,const char *str) +{ + if (str) + fwritex(f,str,strlen(str)+1); +} + + +static int vobj_initwrite(struct GlobalVars *gv) +{ + struct LinkedSection *ls; + struct Symbol *sym; + struct Reloc *xref; + int cnt = gv->nsecs; /* make room for nsecs section symbols */ + + /* count symbols and unresolved references */ + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + + for (xref=(struct Reloc *)ls->xrefs.first; + xref->n.next!=NULL; xref=(struct Reloc *)xref->n.next) { + sym = findsymbol(gv,NULL,xref->xrefname,0); + if (sym == NULL) { + /* add new undefined symbol to current section */ + sym = newsymbol(xref->xrefname,0,SYM_UNDEF,0,0,SYMB_GLOBAL,0); + addglobsym(gv,sym); + addtail(&ls->symbols,&sym->n); + } + xref->relocsect.symbol = sym; /* "resolve" with undefined symbol */ + } + + for (sym=(struct Symbol *)ls->symbols.first; + sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { + if (!discard_symbol(gv,sym) && sym->info!=SYMI_SECTION) + sym->extra = ++cnt; + else + sym->extra = 0; + } + } + return cnt; +} + + +static void vobj_header(struct GlobalVars *gv,FILE *f) +{ + int endian = fff[gv->dest_format]->endianness==_BIG_ENDIAN_ ? 1 : 2; + + fwrite32be(f,0x564f424a); /* "VOBJ" */ + fwrite_number(f,endian|vobjver); + fwrite_number(f,gv->bits_per_tbyte); + fwrite_number(f,gv->tbytes_per_taddr); + if (vobjcpu == NULL) { + fwrite_string(f,"generic"); + vobjcpuid = 0; + error(154); /* No CPU defined for VOBJ output */ + } + else + fwrite_string(f,vobjcpu); +} + + +static int vobj_symbols(struct GlobalVars *gv,FILE *f) +{ + static const char *fn = "vobj_symbols(): "; + struct LinkedSection *ls; + struct Symbol *sym; + unsigned flags,secid,cnt; + + /* create section symbols first */ + for (cnt=0,ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + if (cnt != ls->index) + ierror("%sSection symbol %s index %u doesn't match section index %u", + fn,ls->name,(unsigned)cnt,(unsigned)ls->index); + fwrite_string(f,ls->name); + fwrite_number(f,LABSYM); + fwrite_number(f,TYPE_SECTION); + fwrite_number(f,++cnt); + fwrite_number(f,0); + fwrite_number(f,0); + } + + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + for (sym=(struct Symbol *)ls->symbols.first; + sym->n.next!=NULL; sym=(struct Symbol *)sym->n.next) { + + if (sym->extra) { + if ((unsigned)sym->extra != ++cnt) + ierror("%sSymbol counter wrong at %s (%u!=%u)", + fn,sym->name,(unsigned)sym->extra,cnt); + + fwrite_string(f,sym->name); + flags = secid = 0; + + switch (sym->type) { + case SYM_COMMON: + flags |= COMMON|TYPE_OBJECT; + case SYM_UNDEF: + fwrite_number(f,IMPORT); + break; + case SYM_ABS: + fwrite_number(f,EXPRESSION); + break; + case SYM_RELOC: + fwrite_number(f,LABSYM); + if (sym->relsect!=NULL && sym->relsect->lnksec!=NULL) + secid = sym->relsect->lnksec->index + 1; + else + ierror("%sNo lnksec for reloc symbol %s",fn,sym->name); + break; + case SYM_INDIR: + error(155,sym->name,sym->indir_name,fff[gv->dest_format]->tname); + break; + default: + ierror("%sIllegal symbol type %d",fn,(int)sym->type); + break; + } + switch (sym->bind) { + case SYMB_GLOBAL: + flags |= EXPORT; + break; + case SYMB_WEAK: + flags |= WEAK; + break; + } + switch (sym->info) { + case SYMI_OBJECT: + flags |= TYPE_OBJECT; + break; + case SYMI_FUNC: + flags |= TYPE_FUNCTION; + break; + case SYMI_SECTION: + flags |= TYPE_SECTION; + break; + case SYMI_FILE: + flags |= TYPE_FILE; + break; + } + + fwrite_number(f,flags); + fwrite_number(f,secid); + fwrite_number(f,sym->value); + fwrite_number(f,sym->size); + } + } + } + return cnt; +} + + +static int write_reloc(struct GlobalVars *gv,FILE *f, + struct LinkedSection *ls,struct Reloc *r) +{ + struct RelocInsert *ri = r->insert; + int rtype = r->rtype; + int cnt = 0; + + if (vobjcpuid && rtype>=MAKE_RELOC_CPU_ID(vobjcpuid)) { + /* cpu-specific reloc type is mapped to FIRST_CPU_RELOC */ + rtype = rtype - MAKE_RELOC_CPU_ID(vobjcpuid) + FIRST_CPU_RELOC; + } + else { + /* standard reloc types may have Signed/Unsigned flags */ + if (f!=NULL && rtype>LAST_STANDARD_RELOC) + error(14,gv->dest_name,fff[gv->dest_format]->tname,rtype, + ls->name,(unsigned)r->offset); /* illegal relocation type */ + + if (r->flags & RELF_S) + rtype |= VOBJ_REL_S; + else if (r->flags & RELF_U) + rtype |= VOBJ_REL_U; + } + + if (1 /* @@@ check if cpu-specific relocation is nreloc */) { + while (ri) { + if (f != NULL) { + fwrite_number(f,rtype); + if (rtype >= FIRST_CPU_RELOC) + fwrite_number(f,0); /* indicate specific reloc is in nreloc format */ + fwrite_number(f,r->offset); + fwrite_number(f,ri->bpos); + fwrite_number(f,ri->bsiz); + fwrite_number(f,ri->mask); + fwrite_number(f,r->addend); + if (r->xrefname == NULL) + fwrite_number(f,r->relocsect.lnk->index+1); /* section symbol */ + else + fwrite_number(f,r->relocsect.symbol->extra); /* xref symbol */ + } + cnt++; + ri = ri->next; + } + } + else { + /* @@@ write cpu-specific relocation */ + } + return cnt; +} + + +static int vobj_relocs(struct GlobalVars *gv,FILE *f,struct LinkedSection *ls) +{ + struct Reloc *r; + int cnt = 0; + + for (r=(struct Reloc *)ls->relocs.first; + r->n.next!=NULL; r=(struct Reloc *)r->n.next) + cnt += write_reloc(gv,f,ls,r); + + for (r=(struct Reloc *)ls->xrefs.first; + r->n.next!=NULL; r=(struct Reloc *)r->n.next) + cnt += write_reloc(gv,f,ls,r); + + return cnt; +} + + +static void vobj_section(struct GlobalVars *gv,FILE *f,struct LinkedSection *ls) +{ + static const char *fn = "vobj_section(): "; + unsigned flags = 0; + char attr[16],*p; + + fwrite_string(f,ls->name); + + p = attr; + if (ls->flags & SF_ALLOC) + *p++ = 'a'; + else + flags |= UNALLOCATED; + + if ((ls->flags&SF_UNINITIALIZED) || ls->type==ST_UDATA) + *p++ = 'u'; + else if (ls->type == ST_DATA) + *p++ = 'd'; + else if (ls->type == ST_CODE) + *p++ = 'c'; + else + ierror("%sUnsupported section type %u",(unsigned)ls->type); + + if (ls->protection & SP_READ) + *p++ = 'r'; + if (ls->protection & SP_WRITE) + *p++ = 'w'; + if (ls->protection & SP_EXEC) + *p++ = 'x'; + + *p = '\0'; + fwrite_string(f,attr); + + fwrite_number(f,flags); + fwrite_number(f,1LL<alignment); + fwrite_number(f,ls->size); + fwrite_number(f,vobj_relocs(gv,NULL,ls)); + + fwrite_number(f,ls->filesize); + fwriterawsect(gv,f,ls); + + vobj_relocs(gv,f,ls); +} + + static void vobj_writeexec(struct GlobalVars *gv,FILE *f) { error(94); /* Target file format doesn't support executable files */ @@ -563,7 +944,20 @@ static void vobj_writeshared(struct GlobalVars *gv,FILE *f) static void vobj_writeobject(struct GlobalVars *gv,FILE *f) { - error(62); /* Target file format doesn't support relocatable objects */ + struct LinkedSection *ls; + int nsyms; + + nsyms = vobj_initwrite(gv); + vobj_header(gv,f); + fwrite_number(f,gv->nsecs); + fwrite_number(f,nsyms); + + if (vobj_symbols(gv,f) != nsyms) + ierror("vobj: didn't write %d symbols as expected",nsyms); + + for (ls=(struct LinkedSection *)gv->lnksec.first; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) + vobj_section(gv,f,ls); } diff --git a/t_xfile.c b/t_xfile.c index 3bc7fd4..b66ba16 100644 --- a/t_xfile.c +++ b/t_xfile.c @@ -1,8 +1,8 @@ -/* $VER: vlink t_xfile.c V0.16c (10.03.19) +/* $VER: vlink t_xfile.c V0.18 (23.12.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2019 Frank Wille + * Copyright (c) 1997-2019,2024 Frank Wille */ #include "config.h" @@ -12,6 +12,8 @@ #include "xfile.h" +static int options(struct GlobalVars *,int,const char **,int *); +static void printhelp(void); static int identify(struct GlobalVars *,char *,uint8_t *,unsigned long,bool); static void readconv(struct GlobalVars *,struct LinkFile *); static int targetlink(struct GlobalVars *,struct LinkedSection *, @@ -27,7 +29,8 @@ struct FFFuncs fff_xfile = { defaultscript, NULL, NULL, - NULL, + options, + printhelp, headersize, identify, readconv, @@ -51,6 +54,25 @@ struct FFFuncs fff_xfile = { FFF_BASEINCR }; +/* options */ +static uint8_t loadmode; + + +static int options(struct GlobalVars *gv,int argc,const char *argv[],int *i) +{ + if (!strcmp(argv[*i],"-x-high")) + loadmode = XLMD_HIGHADDR; + else return 0; + + return 1; +} + + +static void printhelp(void) +{ + printf("-x-high set high-address loading mode\n"); +} + /*****************************************************************/ @@ -110,13 +132,17 @@ static void xfile_initwrite(struct GlobalVars *gv, } -static void xfile_header(FILE *f,unsigned long tsize,unsigned long dsize, +static void xfile_header(FILE *f,unsigned long baseaddr,unsigned long execaddr, + unsigned long tsize,unsigned long dsize, unsigned long bsize) { XFile hdr; memset(&hdr,0,sizeof(XFile)); write16be(hdr.x_id,0x4855); /* "HU" ID for Human68k */ + hdr.x_loadmode = loadmode; + write32be(hdr.x_baseaddr,baseaddr); + write32be(hdr.x_execaddr,execaddr-baseaddr); write32be(hdr.x_textsz,tsize); write32be(hdr.x_datasz,dsize); write32be(hdr.x_heapsz,bsize); @@ -262,23 +288,27 @@ static void writeexec(struct GlobalVars *gv,FILE *f) int i; xfile_initwrite(gv,sections); - xfile_header(f,sections[0] ? sections[0]->size+sections[0]->gapsize : 0, - sections[1] ? sections[1]->size+sections[1]->gapsize : 0, - sections[2] ? sections[2]->size : 0); for (i=0; i<3; i++) calc_relocs(gv,sections[i]); + xfile_header(f,sections[0]->base,entry_address(gv), + sections[0] ? sections[0]->size+sections[0]->gapsize : 0, + sections[1] ? sections[1]->size+sections[1]->gapsize : 0, + sections[2] ? sections[2]->size : 0); + if (sections[0]) { fwritex(f,sections[0]->data,sections[0]->filesize); fwritegap(gv,f, - (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize); + (sections[0]->size-sections[0]->filesize)+sections[0]->gapsize, + 0); } if (sections[1]) { fwritex(f,sections[1]->data,sections[1]->filesize); fwritegap(gv,f, - (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize); + (sections[1]->size-sections[1]->filesize)+sections[1]->gapsize, + 0); } relocsz = xfile_writerelocs(gv,f,sections); diff --git a/targets.c b/targets.c index a89b12a..0b7efb6 100644 --- a/targets.c +++ b/targets.c @@ -1,8 +1,8 @@ -/* $VER: vlink targets.c V0.17a (02.04.22) +/* $VER: vlink targets.c V0.18 (29.11.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ @@ -30,6 +30,9 @@ struct FFFuncs *fff[] = { &fff_o6502, &fff_o65816, #endif +#ifdef APPLE_OMF + &fff_appleomf, +#endif #ifdef ELF32_PPC_BE &fff_elf32ppcbe, #endif @@ -89,11 +92,8 @@ struct FFFuncs *fff[] = { #endif /* the raw binary file formats *must* be the last ones */ -#ifdef RAWBIN1 - &fff_rawbin1, -#endif -#ifdef RAWBIN2 - &fff_rawbin2, +#ifdef RAWBIN + &fff_rawbin, #endif #ifdef AMSDOS &fff_amsdos, @@ -118,6 +118,12 @@ struct FFFuncs *fff[] = { #ifdef DRAGONBIN &fff_dragonbin, #endif +#ifdef FOENIX + &fff_pgxc02, + &fff_pgx816, + &fff_pgz24, + &fff_pgz32, +#endif #ifdef ORICMC &fff_oricmc, #endif @@ -208,16 +214,8 @@ const char noname[] = ""; struct SecRename *secrenames; -size_t tbytes(struct GlobalVars *gv,size_t sz) -/* convert size given in target bytes with gv->bits_per_tbyte to 8-bit bytes */ -/* WARNING: only handles multiple of 8 in bits_per_tbyte! */ -{ - return (sz * gv->bits_per_tbyte) >> 3; -} - - void section_fill(struct GlobalVars *gv,uint8_t *base, - size_t offset,uint16_t fill,size_t n) + size_t offset,uint16_t fill,lword n) { if (n > 0) { uint8_t f[2]; @@ -377,9 +375,10 @@ bool addglobsym(struct GlobalVars *gv,struct Symbol *newsym) if (newsym->bind == SYMB_GLOBAL) { if (newou->lnkfile->type < ID_LIBBASE) { - if (newsym->type==SYM_COMMON && sym->type==SYM_COMMON) { - if ((newsym->size>sym->size && newsym->value>=sym->value) || - (newsym->size>=sym->size && newsym->value>sym->value)) { + if (newsym->type==SYM_COMMON || sym->type==SYM_COMMON) { + if (newsym->type!=SYM_COMMON || (newsym->type==sym->type && + ((newsym->size>sym->size && newsym->value>=sym->value) || + (newsym->size>=sym->size && newsym->value>sym->value)))) { /* replace by common symbol with bigger size or alignment */ newsym->glob_chain = sym->glob_chain; remove_obj_symbol(sym); /* delete old symbol in object unit */ @@ -493,6 +492,23 @@ static void add_objsymbol(struct ObjectUnit *ou,struct Symbol *newsym) } +struct Symbol *newsymbol(const char *name,lword val,uint8_t type, + uint8_t flags,uint8_t info,uint8_t bind, + uint32_t size) +{ + struct Symbol *sym = alloczero(sizeof(struct Symbol)); + + sym->name = name; + sym->value = val; + sym->type = type; + sym->flags = flags; + sym->info = info; + sym->bind = bind; + sym->size = size; + return sym; +} + + struct Symbol *addsymbol(struct GlobalVars *gv,struct Section *s, const char *name,const char *iname,lword val, uint8_t type,uint8_t flags,uint8_t info,uint8_t bind, @@ -534,16 +550,9 @@ struct Symbol *addsymbol(struct GlobalVars *gv,struct Section *s, } /* new symbol */ - sym = alloczero(sizeof(struct Symbol)); - sym->name = name; - sym->indir_name = iname; - sym->value = val; + sym = newsymbol(name,val,type,flags,info,bind,size); sym->relsect = s; - sym->type = type; - sym->flags = flags; - sym->info = info; - sym->bind = bind; - sym->size = size; + sym->indir_name = iname; sym->fmask = fmask; if (type == SYM_COMMON) { @@ -610,16 +619,9 @@ void addlocsymbol(struct GlobalVars *gv,struct Section *s,char *name, while (sym = *chain) chain = &sym->obj_chain; - *chain = sym = alloczero(sizeof(struct Symbol)); - sym->name = name; - sym->indir_name = iname; - sym->value = val; + *chain = sym = newsymbol(name,val,type,flags,info,SYMB_LOCAL,size); sym->relsect = s; - sym->type = type; - sym->flags = flags; - sym->info = info; - sym->bind = SYMB_LOCAL; - sym->size = size; + sym->indir_name = iname; if (check_protection(gv,name)) sym->flags |= SYMF_PROTECTED; } @@ -639,15 +641,8 @@ struct Symbol *addlnksymbol(struct GlobalVars *gv,const char *name,lword val, while (sym = *chain) chain = &sym->obj_chain; - *chain = sym = alloczero(sizeof(struct Symbol)); - sym->name = name; - sym->value = val; - sym->type = type; - sym->flags = flags; - sym->info = info; - sym->bind = bind; - sym->size = size; - return sym; + *chain = newsymbol(name,val,type,flags,info,bind,size); + return *chain; } @@ -777,34 +772,39 @@ static struct SymbolMask *makesymbolmask(struct GlobalVars *gv, } -static void check_global_objsym(struct ObjectUnit *ou,struct Symbol **chain, +static bool check_global_objsym(struct ObjectUnit *ou,struct Symbol **chain, struct Symbol *gsym,struct Symbol *sym) { if (gsym!=NULL && gsym!=sym && gsym->relsect!=NULL && (gsym->relsect->obj->flags & OUF_LINKED)) { - if (sym->type==SYM_COMMON && gsym->type==SYM_COMMON) { - if ((sym->size>gsym->size && sym->value>=gsym->value) || - (sym->size>=gsym->size && sym->value>gsym->value)) { + + if (sym->type==SYM_COMMON || gsym->type==SYM_COMMON) { + if (sym->type!=SYM_COMMON || (sym->type==gsym->type && + ((sym->size>gsym->size && sym->value>=gsym->value) || + (sym->size>=gsym->size && sym->value>gsym->value)))) { /* replace by common symbol with bigger size or alignment */ sym->glob_chain = gsym->glob_chain; remove_obj_symbol(gsym); /* delete old symbol in object unit */ *chain = sym; } + else + return TRUE; /* ignore the object's symbol */ } else { if ((ou->lnkfile->type != ID_SHAREDOBJ) && (gsym->relsect->obj->lnkfile->type != ID_SHAREDOBJ) && - ((gsym->relsect->flags & SF_EHFPPC) - == (sym->relsect->flags & SF_EHFPPC))) { + ((gsym->relsect->obj->flags & OUF_EHFPPC) + == (sym->relsect->obj->flags & OUF_EHFPPC))) { /* Global symbol "x" is already defined in... */ error(19,ou->lnkfile->pathname,sym->name,getobjname(ou), getobjname(gsym->relsect->obj)); } - /* An identical symbol in an EHFPPC and a non-EHFPPC section + /* An identical symbol in an EHFPPC and a non-EHFPPC object unit of a library is tolerated - "amigaehf" will pick the right one. Also redefinitions from shared objects are simply ignored. */ } } + return FALSE; } @@ -820,20 +820,25 @@ void pull_objunit(struct GlobalVars *gv,struct ObjectUnit *ou) This is required when a new unit has been pulled into the linking process to resolve an undefined reference. */ for (i=0; iobjsyms[i]; + struct Symbol **chain = &ou->objsyms[i]; + struct Symbol *sym; - while (sym) { + while (sym = *chain) { if (sym->bind==SYMB_GLOBAL) { - struct Symbol **chain = &gv->symbols[elf_hash(sym->name)%SYMHTABSIZE]; + struct Symbol **gchain = &gv->symbols[elf_hash(sym->name)%SYMHTABSIZE]; struct Symbol *gsym; - while (gsym = *chain) { - if (!strcmp(sym->name,gsym->name)) - check_global_objsym(ou,chain,gsym,sym); - chain = &(*chain)->glob_chain; + while (gsym = *gchain) { + if (!strcmp(sym->name,gsym->name)) { + if (check_global_objsym(ou,gchain,gsym,sym)) { + *chain = sym->obj_chain; /* discard this object symbol */ + break; + } + } + gchain = &(*gchain)->glob_chain; } } - sym = sym->obj_chain; + chain = &sym->obj_chain; } } @@ -895,7 +900,7 @@ struct RelocInsert *initRelocInsert(struct RelocInsert *ri,uint16_t pos, struct Reloc *newreloc(struct GlobalVars *gv,struct Section *sec, const char *xrefname,struct Section *rs,uint32_t id, - unsigned long offset,uint8_t rtype,lword addend) + unsigned long offset,int rtype,lword addend) /* allocate and init new relocation structure */ { struct Reloc *r = alloczero(sizeof(struct Reloc)); @@ -968,7 +973,11 @@ struct Reloc *newreloc(struct GlobalVars *gv,struct Section *sec, r->offset = offset; r->addend = addend; - r->rtype = rtype; + if (rtype & R_S) + r->flags |= RELF_S; + if (rtype & R_U) + r->flags |= RELF_U; + r->rtype = rtype & ~(R_S|R_U); return r; } @@ -1073,47 +1082,45 @@ void fixstabs(struct ObjectUnit *ou) } -struct TargetExt *addtargetext(struct Section *s,uint8_t id,uint8_t subid, - uint16_t flags,uint32_t size) -/* Add a new TargetExt structure of given type to a section. The contents */ -/* of this structure is target-specific. */ +struct SourceLines *newsourcelines(struct Section *s,const char *srcname) +/* Append a new SourceLines record for assinging section offsets to + line numbers of the given source name. */ { - struct TargetExt *te,*newte = alloc(size); + struct SourceLines *sl; + const char *name; - newte->next = NULL; - newte->id = id; - newte->sub_id = subid; - newte->flags = flags; - if (te = s->special) { - while (te->next) - te = te->next; - te->next = newte; + if (sl = s->srclines) { + while (sl->next) + sl = sl->next; + sl->next = alloczero(sizeof(struct SourceLines)); + sl = sl->next; } else - s->special = newte; - return newte; + sl = s->srclines = alloczero(sizeof(struct SourceLines)); + + /* separate file name from path */ + name = base_name(srcname); + if (name != srcname) { + size_t pathlen = name - srcname; + char *pathbuf = alloc(pathlen); + + memcpy(pathbuf,srcname,--pathlen); /* without separator */ + pathbuf[pathlen] = '\0'; + sl->path = pathbuf; + sl->path_sep = srcname[pathlen]; /* remember path-separator here */ + } + sl->name = name; + sl->lang = DW_LANG_DEFAULT; + return sl; } -bool checktargetext(struct LinkedSection *ls,uint8_t id,uint8_t subid) -/* Checks if one of the sections in LinkedSection has a TargetExt */ -/* block with the given id. If subid = 0 it will be ignored. */ +void allocsrclinetab(struct SourceLines *sl,size_t entries) +/* allocate source-line/offset arrays */ { - struct Section *sec = (struct Section *)ls->sections.first; - struct Section *nextsec; - struct TargetExt *te; - - while (nextsec = (struct Section *)sec->n.next) { - if (te = sec->special) { - do { - if (te->id==id && (te->sub_id==subid || subid==0)) - return TRUE; - } - while (te = te->next); - } - sec = nextsec; - } - return FALSE; + sl->entries = entries; + sl->lines = alloc(entries * sizeof(srclinetype)); + sl->offsets = alloc(entries * sizeof(srcoffstype)); } @@ -1122,7 +1129,6 @@ lword readsection(struct GlobalVars *gv,uint8_t rtype, /* Read data from section at 'src' + 'secoffs', using the field-offsets, sizes and masks from the supplied list of RelocInsert structures. */ { - int be = gv->endianness != _LITTLE_ENDIAN_; int maxfldsz = 0; lword data = 0; @@ -1138,7 +1144,7 @@ lword readsection(struct GlobalVars *gv,uint8_t rtype, maxfldsz = n; /* read from bitfield */ - v = readreloc(be,src,ri->bpos,ri->bsiz); + v = readreloc(gv,src,ri->bpos,ri->bsiz); /* mask and denormalize the read value using 'mask' */ n = lshiftcnt(mask); @@ -1163,19 +1169,24 @@ lword writesection(struct GlobalVars *gv,uint8_t *dest,size_t secoffs, Returns 0 on success or the masked and normalized value which failed on the range check. */ { - bool be = gv->endianness != _LITTLE_ENDIAN_; uint8_t t = r->rtype; - bool signedval = t==R_PC||t==R_GOTPC||t==R_GOTOFF||t==R_PLTPC||t==R_PLTOFF|| - t==R_SD||t==R_SD2||t==R_SD21||t==R_MOSDREL; struct RelocInsert *ri; + int sign; if (t == R_NONE) return 0; + if (r->flags & RELF_S) + sign = 1; + else if (r->flags & RELF_U) + sign = 2; + else + sign = 0; + /* Reset all relocation fields to zero. */ dest += tbytes(gv,secoffs); for (ri=r->insert; ri!=NULL; ri=ri->next) - writereloc(be,dest,ri->bpos,ri->bsiz,0); + writereloc(gv,dest,ri->bpos,ri->bsiz,0); /* add value to relocation fields */ for (ri=r->insert; ri!=NULL; ri=ri->next) { @@ -1185,18 +1196,21 @@ lword writesection(struct GlobalVars *gv,uint8_t *dest,size_t secoffs, int bpos = ri->bpos; int bsiz = ri->bsiz; - insval >>= lshiftcnt(mask); /* normalize according mask */ - if (mask>=0 && signedval) + insval >>= lshiftcnt(mask); /* normalize according to mask */ + if (mask < 0) { + if (sign!=2 && bsiz<(int)gv->bits_per_taddr) /* sign extend addresses */ + insval = sign_extend(insval,gv->bits_per_taddr); + if (!checkrange(insval,sign,bsiz)) + return insval; /* range check failed on 'insval' */ + } + else if (sign == 1) insval = sign_extend(insval,bsiz); - if (!checkrange(insval,signedval,bsiz)) - return insval; /* range check failed on 'insval' */ - /* add to value already present in this field */ - oldval = readreloc(be,dest,bpos,bsiz); - if (mask>=0 && signedval) + oldval = readreloc(gv,dest,bpos,bsiz); + if (sign != 2) oldval = sign_extend(oldval,bsiz); - writereloc(be,dest,bpos,bsiz,oldval+insval); + writereloc(gv,dest,bpos,bsiz,oldval+insval); } return 0; @@ -1205,12 +1219,11 @@ lword writesection(struct GlobalVars *gv,uint8_t *dest,size_t secoffs, int writetaddr(struct GlobalVars *gv,void *dst,size_t offs,lword d) { - bool be = gv->endianness == _BIG_ENDIAN_; uint8_t *p = dst; p += tbytes(gv,offs); - writereloc(be,p,0,gv->bits_per_taddr,d); - return (int)gv->bits_per_taddr / 8; + writereloc(gv,p,0,gv->bits_per_taddr,d); + return (int)(gv->bits_per_taddr + 7) / 8; } @@ -2278,8 +2291,8 @@ void text_data_bss_gaps(struct LinkedSection **sections) /* calculate gap size between text-data and data-bss */ { if (sections[0]) { - unsigned long nextsecbase = sections[1] ? sections[1]->base : - (sections[2] ? sections[2]->base : 0); + lword nextsecbase = sections[1] ? sections[1]->base : + (sections[2] ? sections[2]->base : 0); if (nextsecbase) { sections[0]->gapsize = nextsecbase - (sections[0]->base + sections[0]->size); @@ -2341,7 +2354,7 @@ lword entry_address(struct GlobalVars *gv) /* plan c: search for first executable section */ if (ls = find_lnksec(gv,NULL,ST_CODE,SF_ALLOC,SF_ALLOC|SF_UNINITIALIZED, SP_READ|SP_EXEC)) - return (lword)ls->base; + return ls->base; return 0; } @@ -2360,11 +2373,17 @@ struct Section *entry_section(struct GlobalVars *gv) else sym = findsymbol(gv,NULL,"_start",0); - if (sym != NULL) + if (sym == NULL) { + /* no entry: use the first code section from the first real object file */ + struct ObjectUnit *ou = (struct ObjectUnit *)gv->selobjects.first; + + while (ou->n.next!=NULL && (ou->flags&OUF_SCRIPT)) + ou = (struct ObjectUnit *)ou->n.next; + sec = ou->n.next ? find_sect_type(ou,ST_CODE,SP_READ|SP_EXEC) : NULL; + } + else sec = sym->relsect; - else /* no entry: use the first code section from the first object */ - sec = find_sect_type((struct ObjectUnit *)gv->selobjects.first, - ST_CODE,SP_READ|SP_EXEC); + if (sec == NULL) error(132); /* executable code section in 1st object required */ return sec; @@ -2404,18 +2423,18 @@ void trim_sections(struct GlobalVars *gv) if (!gv->dest_object && !gv->keep_trailing_zeros) { for (ls=(struct LinkedSection *)gv->lnksec.first; ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { - for (sec=(struct Section *)ls->sections.first; - sec->n.next!=NULL; sec=(struct Section *)sec->n.next) { - nextsec = (struct Section *)sec->n.next; - if (sec->data!=NULL && !(sec->flags & SF_UNINITIALIZED) && - (nextsec->n.next==NULL || (nextsec->flags & SF_UNINITIALIZED))) { + for (sec=(struct Section *)ls->sections.last; + sec->n.pred!=NULL; sec=(struct Section *)sec->n.pred) { + if (sec->data!=NULL && !(sec->flags & SF_UNINITIALIZED)) { /* This is the last initialized sub-section, so check for trailing zero-bytes, which can be subtracted from filesize. */ unsigned long secinit = sec->size; - uint8_t *p = sec->data + secinit; + uint8_t *p = sec->data + tbytes(gv,secinit) - gv->octets_per_tbyte; - while (secinit>sec->last_reloc && *(--p)==0) + while (secinit>sec->last_reloc && readtbyte(gv,p)==0) { --secinit; + p -= gv->octets_per_tbyte; + } ls->filesize = sec->offset + secinit; break; } @@ -2425,14 +2444,16 @@ void trim_sections(struct GlobalVars *gv) } -void untrim_sections(struct GlobalVars *gv) +void untrim_sections(struct GlobalVars *gv,int bss_too) { struct LinkedSection *ls; /* set filesize=size in all sections */ for (ls=(struct LinkedSection *)gv->lnksec.first; - ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) - ls->filesize = ls->size; + ls->n.next!=NULL; ls=(struct LinkedSection *)ls->n.next) { + if (bss_too || (ls->type!=ST_UDATA && !(ls->flags&SF_UNINITIALIZED))) + ls->filesize = ls->size; + } } diff --git a/tosdefs.h b/tosdefs.h index 9d5b769..184529b 100644 --- a/tosdefs.h +++ b/tosdefs.h @@ -1,11 +1,18 @@ -/* $VER: vlink tosdefs.h V0.13 (02.11.10) +/* $VER: vlink tosdefs.h V0.18 (31.05.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2010 Frank Wille + * Copyright (c) 1997-2010,2024 Frank Wille */ +#define TEXTNAME text_name +#define DATANAME data_name +#define BSSNAME bss_name + +#define TOS_ALIGNMENT 1 /* 16 bits */ + + /* TOS program header */ typedef struct { @@ -19,6 +26,8 @@ typedef struct uint8_t ph_abs[2]; /* has to be 0, otherwise no relocation takes place */ } PH; +#define DRIMAGIC 0x601a /* magic code in ph_branch */ + /* DRI symbol table */ #define DRI_NAMELEN 8 @@ -42,6 +51,39 @@ struct DRIsym #define STYP_LONGNAME 0x0048 #define STYP_TFILE 0x0280 #define STYP_TFARC 0x02c0 +#define STYP_XFLAGS 0x4200 + +#define XVALUE 0x87654321 /* SozobonX symbol value for extended name */ +#define XNAME "SozobonX" /* Name of first symbol for SozobonX */ + + +/* DRI library arheader */ +struct DRIarheader { + char a_fname[14]; + uint8_t a_modtim[4]; + uint8_t a_userid; + uint8_t a_gid; + uint8_t a_fimode[2]; + uint8_t a_fsize[4]; + uint8_t a_reserved[2]; +}; + +#define DRI_ARMAGIC 0xff65 + + +/* DRI Header Info */ +struct DRIinfo { + struct ObjectUnit *object; + struct FFFuncs *ff; + PH *hdr; + uint8_t *end; + struct Section *sec[3]; /* text, data, bss */ + uint8_t *data[2]; /* text and data contents */ + struct DRIsym *symtab; + int nsym; + int sozobonx; /* symtab may have SozobonX name extensions */ + int symidx; /* next symbol index, after dri_symname() */ +}; /* default script */ diff --git a/tosopts.c b/tosopts.c index 4816273..372c59b 100644 --- a/tosopts.c +++ b/tosopts.c @@ -1,8 +1,8 @@ -/* $VER: vlink tosopts.c V0.16i (31.01.22) +/* $VER: vlink tosopts.c V0.18 (31.05.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ #include "config.h" @@ -10,7 +10,10 @@ #define TOSOPTS_C #include "vlink.h" -uint32_t tos_flags; /* flags field in TOS header */ +uint32_t tos_flags; /* flags field in TOS header */ +bool textbasedsyms; /* symbol offsets based on text section */ +bool sozobonx; /* SozobonX extension for unlimited symbol name length */ +bool hisoftdri = TRUE; /* HiSoft symbol name extension */ int tos_options(struct GlobalVars *gv,int argc,const char *argv[],int *i) @@ -37,10 +40,30 @@ int tos_options(struct GlobalVars *gv,int argc,const char *argv[],int *i) else if (!strcmp(argv[*i],"-tos-readable")) tos_flags |= 0x30; else if (!strcmp(argv[*i],"-tos-textbased")) - gv->textbasedsyms = 1; + textbasedsyms = TRUE; + else if (!strcmp(argv[*i],"-tos-stddri")) + hisoftdri = FALSE; + else if (!strcmp(argv[*i],"-tos-sozobonx")) + sozobonx = TRUE; else return 0; return 1; } + +void tos_printhelp(void) +{ + printf("-tos-flags TOS header flags (32 bits)\n" + "-tos-fastload Set the fastload bit in the TOS header\n" + "-tos-fastram Set the fastram bit in the TOS header\n" + "-tos-fastalloc Set the fastalloc bit in the TOS header\n" + "-tos-private Mark memory space as private in the TOS header\n" + "-tos-global Mark memory space as global in the TOS header\n" + "-tos-super Mark memory as supervisor-only in the TOS header\n" + "-tos-readable Mark memory as read-only in the TOS header\n" + "-tos-sozobonx Write Sozobon symbol extension (disables HiSoft)\n" + "-tos-stddri Write standard DRI symbols (disables HiSoft)\n" + "-tos-textbased Write Devpac(MonST)-compatible DRI symbols\n"); +} + #endif diff --git a/version.c b/version.c index e55b3df..05aa058 100644 --- a/version.c +++ b/version.c @@ -1,13 +1,13 @@ -/* $VER: vlink version.c V0.17a (21.03.22) +/* $VER: vlink version.c V0.18 (24.02.24) * * This file is part of vlink, a portable linker for multiple * object formats. - * Copyright (c) 1997-2022 Frank Wille + * Copyright (c) 1997-2024 Frank Wille */ /* version/revision */ -#define VERSION "0.17a" +#define VERSION "0.18" #define VERSION_C #include "vlink.h" @@ -22,16 +22,16 @@ const char *version_str = VERSION; void show_version(void) { - printf(PNAME " V%s (c)1997-2022 by Frank Wille\n" + printf(PNAME " V%s (c)1997-2024 by Frank Wille\n" "build date: " __DATE__ ", " __TIME__ "\n\n",version_str); } -void show_usage(void) +void show_usage(struct GlobalVars *gv,int verbose) { show_version(); - printf("Usage: " PNAME " [-dhkmnqrstvwxMRSXZ] [-B linkmode] [-b targetname] " + printf("Usage: " PNAME " [-dhkmnqrstvwxMSXZ] [-B linkmode] [-b targetname] " "[-baseoff offset] [-C constructor-type] [-Crel] " #if 0 /* not implemented */ "[-D symbol[=value]] " @@ -39,82 +39,90 @@ void show_usage(void) "[-da] [-dc] [-dp] [-EB] [-EL] [-e entrypoint] [-export-dynamic] " "[-f flavour] [-fixunnamed] [-F filename] " "[-gc-all] [-gc-empty] " - "[-hunkattr secname=value] [-interp path] " - "[-L library-search-path] [-l library-specifier] [-minalign value] " + "[-hunkattr secname=value] [-interp path] [-minalign value] " "[-mrel] [-mtype] [-mall] [-multibase] [-nostdlib] " - "[-N old new] [-o filename] [-osec] " + "[-N old new] [-o filename] [-osec] [-Rstd/add/short] " "[-os9-mem/name/rev] [-P symbol] " - "[-rpath path] [-sc] [-sd] [-shared] [-soname name] [-static] " + "[-rpath path] [-sc] [-sd] [-shared] [-soname name] " + "[-static] [-symfile filename] [-symfmt format] [-symctrl=flags] " "[-T filename] [-Ttext addr] [-textbaserel] " "[-tos-flags/fastload/fastram/private/global/super/readable] " - "[-u symbol] [-vicelabels filename]" + "[-u symbol] [-vicelabels filename] " + "[-vobj2] [-vobjcpu name] " "[-V version] [-y symbol] " - "input-files...\n\nOptions:\n" + "input-files...[-L library-search-path] [-l library-specifier]\n\n"); - " object files and libraries to link\n" - "-F read a list of input files from \n" - "-o output file name\n" - "-b output file format\n" - "-l link with specified library (static or dynamic)\n" - "-L add search path for libraries\n" - "-f add a library flavour\n" - "-rpath add search path for dynamic linker\n" - "-e address of program's entry point\n" - "-interp set interpreter path (dynamic linker for ELF)\n" - "-gc-all garbage-collect all unreferenced sections\n" - "-gc-empty garbage-collect empty unreferenced sections\n" - "-y trace symbol accesses by the linker\n" - "-P protect symbol from stripping\n" + if (verbose) { + printf("Options:\n" + " object files and libraries to link\n" + "-F read a list of input files from \n" + "-o output file name\n" + "-b output file format\n" + "-l link with specified library (static or dynamic)\n" + "-L add search path for libraries\n" + "-f add a library flavour\n" + "-rpath add search path for dynamic linker\n" + "-e address of program's entry point\n" + "-interp set interpreter path (dynamic linker for ELF)\n" + "-gc-all garbage-collect all unreferenced sections\n" + "-gc-empty garbage-collect empty unreferenced sections\n" + "-y trace symbol accesses by the linker\n" + "-P protect symbol from stripping\n" #if 0 /* not implemented */ - "-D[=exp] define a symbol\n" + "-D[=exp] define a symbol\n" #endif - "-u mark a symbol as undefined\n" - "-T