Major cleanup of ElfLinker.

This commit is contained in:
Markus F.X.J. Oberhumer 2006-11-12 10:57:18 +01:00
parent 6e763b0b42
commit a74e239e28
4 changed files with 217 additions and 155 deletions

View File

@ -29,7 +29,7 @@
#include "conf.h" #include "conf.h"
#include "linker.h" #include "linker.h"
static int hex(unsigned char c) static unsigned hex(unsigned char c)
{ {
return (c & 0xf) + (c > '9' ? 9 : 0); return (c & 0xf) + (c > '9' ? 9 : 0);
} }
@ -45,17 +45,20 @@ static bool update_capacity(unsigned size, unsigned *capacity)
return true; return true;
} }
/************************************************************************* /*************************************************************************
// // Section
**************************************************************************/ **************************************************************************/
ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a) : ElfLinker::Section::Section(const char *n, const void *i, unsigned s, unsigned a) :
name(strdup(n)), output(NULL), size(s), offset(0), align(a), next(NULL) name(NULL), output(NULL), size(s), offset(0), p2align(a), next(NULL)
{ {
assert(name); name = strdup(n);
assert(name != NULL);
input = malloc(s + 1); input = malloc(s + 1);
assert(input); assert(input != NULL);
memcpy(input, i, s); memcpy(input, i, s);
((char *)input)[s] = 0;
} }
ElfLinker::Section::~Section() ElfLinker::Section::~Section()
@ -64,10 +67,17 @@ ElfLinker::Section::~Section()
free(input); free(input);
} }
/*************************************************************************
// Symbol
**************************************************************************/
ElfLinker::Symbol::Symbol(const char *n, Section *s, unsigned o) : ElfLinker::Symbol::Symbol(const char *n, Section *s, unsigned o) :
name(strdup(n)), section(s), offset(o) name(NULL), section(s), offset(o)
{ {
assert(name); name = strdup(n);
assert(name != NULL);
assert(section != NULL);
} }
ElfLinker::Symbol::~Symbol() ElfLinker::Symbol::~Symbol()
@ -75,12 +85,77 @@ ElfLinker::Symbol::~Symbol()
free(name); free(name);
} }
/*************************************************************************
// Relocation
**************************************************************************/
ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t, ElfLinker::Relocation::Relocation(const Section *s, unsigned o, const char *t,
const Symbol *v, unsigned a) : const Symbol *v, unsigned a) :
section(s), offset(o), type(t), value(v), add(a) section(s), offset(o), type(t), value(v), add(a)
{} {
assert(section != NULL);
}
/*************************************************************************
// ElfLinker
**************************************************************************/
ElfLinker::ElfLinker() :
input(NULL), output(NULL), head(NULL), tail(NULL),
sections(NULL), symbols(NULL), relocations(NULL),
nsections(0), nsections_capacity(0),
nsymbols(0), nsymbols_capacity(0),
nrelocations(0), nrelocations_capacity(0),
reloc_done(false)
{
}
ElfLinker::~ElfLinker()
{
delete [] input;
delete [] output;
unsigned ic;
for (ic = 0; ic < nsections; ic++)
delete sections[ic];
free(sections);
for (ic = 0; ic < nsymbols; ic++)
delete symbols[ic];
free(symbols);
for (ic = 0; ic < nrelocations; ic++)
delete relocations[ic];
free(relocations);
}
void ElfLinker::init(const void *pdata, int plen)
{
upx_byte *i = new upx_byte[plen + 1];
memcpy(i, pdata, plen);
input = i;
inputlen = plen;
input[plen] = 0;
output = new upx_byte[plen];
outputlen = 0;
int pos = find(input, plen, "Sections:", 9);
assert(pos != -1);
char *psections = (char *) input + pos;
char *psymbols = strstr(psections, "SYMBOL TABLE:");
assert(psymbols != NULL);
char *prelocs = strstr(psymbols, "RELOCATION RECORDS FOR");
assert(prelocs != NULL);
preprocessSections(psections, psymbols);
preprocessSymbols(psymbols, prelocs);
preprocessRelocations(prelocs, (char*) input + inputlen);
addLoader("*UND*");
}
void ElfLinker::preprocessSections(char *start, const char *end) void ElfLinker::preprocessSections(char *start, const char *end)
{ {
nsections = 0; nsections = 0;
@ -114,22 +189,28 @@ void ElfLinker::preprocessSymbols(char *start, const char *end)
{ {
char section[1024]; char section[1024];
char symbol[1024]; char symbol[1024];
unsigned offset; unsigned value, offset;
char *nextl = strchr(start, '\n'); char *nextl = strchr(start, '\n');
assert(nextl != NULL); assert(nextl != NULL);
if (sscanf(start, "%x%*8c %1024s %*x %1023s", if (sscanf(start, "%x g *ABS* %x %1023s",
&value, &offset, symbol) == 3)
{
char *s = strstr(start, symbol);
s[strlen(symbol)] = 0;
addSymbol(s, "*ABS*", value);
assert(offset == 0);
}
else if (sscanf(start, "%x%*8c %1023s %*x %1023s",
&offset, section, symbol) == 3) &offset, section, symbol) == 3)
{ {
char *s = strstr(start, symbol); char *s = strstr(start, symbol);
s[strlen(symbol)] = 0; s[strlen(symbol)] = 0;
if (strcmp(section, "*UND*") == 0) if (strcmp(section, "*UND*") == 0)
offset = 0xdeaddead; offset = 0xdeaddead;
assert(strcmp(section, "*ABS*") != 0);
addSymbol(s, section, offset); addSymbol(s, section, offset);
//printf("symbol %s preprocessed o=%x\n", s, offset);
} }
start = nextl + 1; start = nextl + 1;
@ -185,38 +266,54 @@ void ElfLinker::preprocessRelocations(char *start, const char *end)
} }
} }
ElfLinker::Section *ElfLinker::findSection(const char *name) const ElfLinker::Section *ElfLinker::findSection(const char *name, bool fatal) const
{ {
for (unsigned ic = 0; ic < nsections; ic++) for (unsigned ic = 0; ic < nsections; ic++)
if (strcmp(sections[ic]->name, name) == 0) if (strcmp(sections[ic]->name, name) == 0)
return sections[ic]; return sections[ic];
if (fatal)
printf("unknown section %s\n", name); {
abort(); printf("unknown section %s\n", name);
abort();
}
return NULL; return NULL;
} }
ElfLinker::Symbol *ElfLinker::findSymbol(const char *name) /*const*/ ElfLinker::Symbol *ElfLinker::findSymbol(const char *name, bool fatal) const
{ {
for (unsigned ic = 0; ic < nsymbols; ic++) for (unsigned ic = 0; ic < nsymbols; ic++)
if (strcmp(symbols[ic]->name, name) == 0) if (strcmp(symbols[ic]->name, name) == 0)
return symbols[ic]; return symbols[ic];
if (fatal)
// FIXME: this should be a const method ! {
if ('*' == name[0]) // *ABS* printf("unknown symbol %s\n", name);
return addSymbol(name, name, 0); abort();
}
printf("unknown symbol %s\n", name);
abort();
return NULL; return NULL;
} }
ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section, ElfLinker::Section *ElfLinker::addSection(const char *sname, const void *sdata, int slen, unsigned p2align)
unsigned offset)
{ {
//printf("addSection: %s len=%d align=%d\n", sname, slen, p2align);
if (update_capacity(nsections, &nsections_capacity))
sections = static_cast<Section **>(realloc(sections, nsections_capacity * sizeof(Section *)));
assert(sections);
assert(sname); assert(sname[0]); assert(sname[strlen(sname)-1] != ':');
assert(findSection(sname, false) == NULL);
Section *sec = new Section(sname, sdata, slen, p2align);
sections[nsections++] = sec;
return sec;
}
ElfLinker::Symbol *ElfLinker::addSymbol(const char *name, const char *section,
unsigned offset)
{
//printf("addSymbol: %s %s 0x%x\n", name, section, offset);
if (update_capacity(nsymbols, &nsymbols_capacity)) if (update_capacity(nsymbols, &nsymbols_capacity))
symbols = static_cast<Symbol **>(realloc(symbols, nsymbols_capacity * sizeof(Symbol *))); symbols = static_cast<Symbol **>(realloc(symbols, nsymbols_capacity * sizeof(Symbol *)));
assert(symbols != NULL); assert(symbols != NULL);
assert(name); assert(name[0]); assert(name[strlen(name)-1] != ':');
assert(findSymbol(name, false) == NULL);
Symbol *sym = new Symbol(name, findSection(section), offset); Symbol *sym = new Symbol(name, findSection(section), offset);
symbols[nsymbols++] = sym; symbols[nsymbols++] = sym;
return sym; return sym;
@ -235,59 +332,6 @@ ElfLinker::Relocation *ElfLinker::addRelocation(const char *section, unsigned of
return rel; return rel;
} }
ElfLinker::ElfLinker() :
frozen(false), input(NULL), output(NULL), head(NULL), tail(NULL),
sections(NULL), symbols(NULL), relocations(NULL),
nsections(0), nsections_capacity(0),
nsymbols(0), nsymbols_capacity(0),
nrelocations(0), nrelocations_capacity(0)
{
}
ElfLinker::~ElfLinker()
{
delete [] input;
delete [] output;
unsigned ic;
for (ic = 0; ic < nsections; ic++)
delete sections[ic];
free(sections);
for (ic = 0; ic < nsymbols; ic++)
delete symbols[ic];
free(symbols);
for (ic = 0; ic < nrelocations; ic++)
delete relocations[ic];
free(relocations);
}
void ElfLinker::init(const void *pdata, int plen)
{
upx_byte *i = new upx_byte[plen + 1];
memcpy(i, pdata, plen);
input = i;
inputlen = plen;
output = new upx_byte[plen];
outputlen = 0;
int pos = find(input, plen, "Sections:", 9);
assert(pos != -1);
char *psections = pos + (char *) input;
char *psymbols = strstr(psections, "SYMBOL TABLE:");
assert(psymbols != NULL);
char *prelocs = strstr(psymbols, "RELOCATION RECORDS FOR");
assert(prelocs != NULL);
input[plen] = 0;
preprocessSections(psections, psymbols);
preprocessSymbols(psymbols, prelocs);
preprocessRelocations(prelocs, (char*) input + inputlen);
addLoader("*UND*");
}
void ElfLinker::setLoaderAlignOffset(int phase) void ElfLinker::setLoaderAlignOffset(int phase)
{ {
//assert(phase & 0); //assert(phase & 0);
@ -296,7 +340,6 @@ void ElfLinker::setLoaderAlignOffset(int phase)
int ElfLinker::addLoader(const char *sname) int ElfLinker::addLoader(const char *sname)
{ {
assert(!frozen);
if (sname[0] == 0) if (sname[0] == 0)
return outputlen; return outputlen;
@ -311,27 +354,28 @@ int ElfLinker::addLoader(const char *sname)
break; break;
} }
if (*sect == '+') // alignment if (sect[0] == '+') // alignment
{ {
assert(tail); assert(tail);
if (unsigned l = (hex(sect[2]) - tail->offset - tail->size) unsigned l = (hex(sect[2]) - tail->offset - tail->size) % hex(sect[1]);
% hex(sect[1])) if (l)
{ {
alignCode(l); if (sect[3] == 'D')
alignData(l);
else
alignCode(l);
tail->size += l; tail->size += l;
outputlen += l;
} }
} }
else else
{ {
Section *section = findSection(sect); Section *section = findSection(sect);
if (section->align) { if (section->p2align) {
unsigned const v = ~0u << section->align;
assert(tail); assert(tail);
unsigned const v = ~0u << section->p2align;
if (unsigned const l = ~v & -(tail->offset + tail->size)) { if (unsigned const l = ~v & -(tail->offset + tail->size)) {
alignCode(l); alignCode(l);
tail->size += l; tail->size += l;
outputlen += l;
} }
} }
memcpy(output + outputlen, section->input, section->size); memcpy(output + outputlen, section->input, section->size);
@ -354,38 +398,22 @@ int ElfLinker::addLoader(const char *sname)
return outputlen; return outputlen;
} }
void ElfLinker::addSection(const char *sname, const void *sdata, int slen, int align) int ElfLinker::getSection(const char *sname, int *slen) const
{ {
assert(!frozen); const Section *section = findSection(sname);
if (update_capacity(nsections, &nsections_capacity))
sections = static_cast<Section **>(realloc(sections, nsections_capacity * sizeof(Section *)));
assert(sections);
Section *sec = new Section(sname, sdata, slen, align);
sections[nsections++] = sec;
//return sec;
}
void ElfLinker::freeze()
{
if (frozen)
return;
frozen = true;
}
int ElfLinker::getSection(const char *sname, int *slen)
{
assert(frozen);
Section *section = findSection(sname);
if (slen) if (slen)
*slen = section->size; *slen = section->size;
return section->output - output; return section->output - output;
} }
upx_byte *ElfLinker::getLoader(int *llen) int ElfLinker::getSectionSize(const char *sname) const
{ {
assert(frozen); const Section *section = findSection(sname);
return section->size;
}
upx_byte *ElfLinker::getLoader(int *llen) const
{
if (llen) if (llen)
*llen = outputlen; *llen = outputlen;
return output; return output;
@ -393,31 +421,38 @@ upx_byte *ElfLinker::getLoader(int *llen)
void ElfLinker::relocate() void ElfLinker::relocate()
{ {
assert(frozen); assert(!reloc_done);
reloc_done = true;
for (unsigned ic = 0; ic < nrelocations; ic++) for (unsigned ic = 0; ic < nrelocations; ic++)
{ {
Relocation *rel = relocations[ic]; const Relocation *rel = relocations[ic];
unsigned value;
if (rel->section->output == NULL) if (rel->section->output == NULL)
continue; continue;
if (rel->value->section->output == NULL) if (strcmp(rel->value->section->name, "*ABS*") == 0)
{
value = rel->value->offset;
}
else if (strcmp(rel->value->section->name, "*UND*") == 0 &&
rel->value->offset == 0xdeaddead)
{
printf("undefined symbol '%s' referenced\n", rel->value->name);
abort();
}
else if (rel->value->section->output == NULL)
{ {
printf("can not apply reloc '%s:%x' without section '%s'\n", printf("can not apply reloc '%s:%x' without section '%s'\n",
rel->section->name, rel->offset, rel->section->name, rel->offset,
rel->value->section->name); rel->value->section->name);
abort(); abort();
} }
else
if (strcmp(rel->value->section->name, "*UND*") == 0 &&
rel->value->offset == 0xdeaddead)
{ {
printf("undefined symbol '%s' referenced\n", rel->value->name); value = rel->value->section->offset +
abort(); rel->value->offset + rel->add;
} }
unsigned value = rel->value->section->offset +
rel->value->offset + rel->add;
upx_byte *location = rel->section->output + rel->offset; upx_byte *location = rel->section->output + rel->offset;
relocate1(rel, location, value, rel->type); relocate1(rel, location, value, rel->type);
} }
} }
@ -425,7 +460,12 @@ void ElfLinker::relocate()
void ElfLinker::defineSymbol(const char *name, unsigned value) void ElfLinker::defineSymbol(const char *name, unsigned value)
{ {
Symbol *symbol = findSymbol(name); Symbol *symbol = findSymbol(name);
if (strcmp(symbol->section->name, "*UND*") == 0) // for undefined symbols if (strcmp(symbol->section->name, "*ABS*") == 0)
{
printf("defineSymbol: symbol '%s' is *ABS*\n", name);
abort();
}
else if (strcmp(symbol->section->name, "*UND*") == 0) // for undefined symbols
symbol->offset = value; symbol->offset = value;
else if (strcmp(symbol->section->name, name) == 0) // for sections else if (strcmp(symbol->section->name, name) == 0) // for sections
{ {
@ -438,36 +478,38 @@ void ElfLinker::defineSymbol(const char *name, unsigned value)
} }
else else
{ {
printf("symbol '%s' already defined\n", name); printf("defineSymbol: symbol '%s' already defined\n", name);
abort(); abort();
} }
} }
// debugging support // debugging support
void ElfLinker::dumpSymbols(FILE *fp) const void ElfLinker::dumpSymbols(unsigned flags, FILE *fp) const
{ {
if (fp == NULL) if (fp == NULL)
fp = stdout; fp = stdout;
for (unsigned ic = 0; ic < nsymbols; ic++) for (unsigned ic = 0; ic < nsymbols; ic++)
{ {
Symbol *symbol = symbols[ic]; const Symbol *symbol = symbols[ic];
fprintf(fp, "%-20s 0x%08x | %-20s 0x%08x\n", if ((flags & 1) && symbol->section->output == NULL)
continue;
fprintf(fp, "%-28s 0x%08x | %-28s 0x%08x\n",
symbol->name, symbol->offset, symbol->section->name, symbol->section->offset); symbol->name, symbol->offset, symbol->section->name, symbol->section->offset);
} }
} }
unsigned ElfLinker::getSymbolOffset(const char *name) const unsigned ElfLinker::getSymbolOffset(const char *name) const
{ {
assert(frozen); const Symbol *symbol = findSymbol(name);
Symbol *symbol = const_cast<ElfLinker *>(this)->findSymbol(name);
if (symbol->section->output == NULL) if (symbol->section->output == NULL)
return 0xdeaddead; return 0xdeaddead;
return symbol->section->offset + symbol->offset; return symbol->section->offset + symbol->offset;
} }
void ElfLinker::alignWithByte(unsigned len, upx_byte b) void ElfLinker::alignWithByte(unsigned len, unsigned char b)
{ {
memset(output + outputlen, b, len); memset(output + outputlen, b, len);
outputlen += len;
} }
void ElfLinker::relocate1(const Relocation *rel, upx_byte *, void ElfLinker::relocate1(const Relocation *rel, upx_byte *,
@ -483,6 +525,23 @@ void ElfLinker::relocate1(const Relocation *rel, upx_byte *,
// FIXME: add more displacment overflow checks // FIXME: add more displacment overflow checks
**************************************************************************/ **************************************************************************/
#if 0 // FIXME
static void check8(const Relocation *rel, const upx_byte *location, int v, int d)
{
if (v < -128 || v > 127) {
printf("value out of range (%d) in reloc %s:%x\n",
v, rel->section->name, rel->offset);
abort();
}
if (d < -128 || d > 127) {
printf("displacement target out of range (%d) in reloc %s:%x\n",
v, rel->section->name, rel->offset);
abort();
}
}
#endif
void ElfLinkerAMD64::relocate1(const Relocation *rel, upx_byte *location, void ElfLinkerAMD64::relocate1(const Relocation *rel, upx_byte *location,
unsigned value, const char *type) unsigned value, const char *type)
{ {
@ -580,6 +639,7 @@ void ElfLinkerM68k::alignCode(unsigned len)
assert((outputlen & 1) == 0); assert((outputlen & 1) == 0);
for (unsigned i = 0; i < len; i += 2) for (unsigned i = 0; i < len; i += 2)
set_be16(output + outputlen + i, 0x4e71); // "nop" set_be16(output + outputlen + i, 0x4e71); // "nop"
outputlen += len;
} }
void ElfLinkerM68k::relocate1(const Relocation *rel, upx_byte *location, void ElfLinkerM68k::relocate1(const Relocation *rel, upx_byte *location,
@ -651,12 +711,12 @@ void ElfLinkerX86::relocate1(const Relocation *rel, upx_byte *location,
return super::relocate1(rel, location, value, type); return super::relocate1(rel, location, value, type);
type += 6; type += 6;
int range_check = 0; bool range_check = false;
if (strncmp(type, "PC", 2) == 0) if (strncmp(type, "PC", 2) == 0)
{ {
value -= rel->section->offset + rel->offset; value -= rel->section->offset + rel->offset;
type += 2; type += 2;
range_check = 1; range_check = true;
} }
if (strcmp(type, "8") == 0) if (strcmp(type, "8") == 0)

View File

@ -41,8 +41,6 @@ protected:
struct Symbol; struct Symbol;
struct Relocation; struct Relocation;
bool frozen; // FIXME: can we remove this ?
upx_byte *input; upx_byte *input;
int inputlen; int inputlen;
upx_byte *output; upx_byte *output;
@ -62,11 +60,13 @@ protected:
unsigned nrelocations; unsigned nrelocations;
unsigned nrelocations_capacity; unsigned nrelocations_capacity;
bool reloc_done;
void preprocessSections(char *start, const char *end); void preprocessSections(char *start, const char *end);
void preprocessSymbols(char *start, const char *end); void preprocessSymbols(char *start, const char *end);
void preprocessRelocations(char *start, const char *end); void preprocessRelocations(char *start, const char *end);
Section *findSection(const char *name) const; Section *findSection(const char *name, bool fatal=true) const;
Symbol *findSymbol(const char *name) /*const*/; Symbol *findSymbol(const char *name, bool fatal=true) const;
Symbol *addSymbol(const char *name, const char *section, unsigned offset); Symbol *addSymbol(const char *name, const char *section, unsigned offset);
Relocation *addRelocation(const char *section, unsigned off, const char *type, Relocation *addRelocation(const char *section, unsigned off, const char *type,
@ -79,16 +79,16 @@ public:
virtual void init(const void *pdata, int plen); virtual void init(const void *pdata, int plen);
virtual void setLoaderAlignOffset(int phase); virtual void setLoaderAlignOffset(int phase);
virtual int addLoader(const char *sname); virtual int addLoader(const char *sname);
virtual void addSection(const char *sname, const void *sdata, int slen, int align); virtual Section *addSection(const char *sname, const void *sdata, int slen, unsigned p2align);
virtual void freeze(); virtual int getSection(const char *sname, int *slen=NULL) const;
virtual int getSection(const char *sname, int *slen=NULL); virtual int getSectionSize(const char *sname) const;
virtual upx_byte *getLoader(int *llen=NULL); virtual upx_byte *getLoader(int *llen=NULL) const;
virtual void relocate(); virtual void relocate();
virtual void defineSymbol(const char *name, unsigned value); virtual void defineSymbol(const char *name, unsigned value);
virtual unsigned getSymbolOffset(const char *) const; virtual unsigned getSymbolOffset(const char *) const;
virtual void dumpSymbols(FILE *fp=NULL) const; virtual void dumpSymbols(unsigned flags=0, FILE *fp=NULL) const;
void alignWithByte(unsigned len, upx_byte b); void alignWithByte(unsigned len, unsigned char b);
virtual void alignCode(unsigned len) { alignWithByte(len, 0); } virtual void alignCode(unsigned len) { alignWithByte(len, 0); }
virtual void alignData(unsigned len) { alignWithByte(len, 0); } virtual void alignData(unsigned len) { alignWithByte(len, 0); }
@ -104,7 +104,7 @@ struct ElfLinker::Section : private nocopy
upx_byte *output; upx_byte *output;
unsigned size; unsigned size;
unsigned offset; unsigned offset;
unsigned char align; // log2 unsigned p2align; // log2
Section *next; Section *next;
Section(const char *n, const void *i, unsigned s, unsigned a=0); Section(const char *n, const void *i, unsigned s, unsigned a=0);

View File

@ -932,12 +932,6 @@ unsigned Packer::unoptimizeReloc32(upx_byte **in, upx_byte *image,
// loader util // loader util
**************************************************************************/ **************************************************************************/
void Packer::freezeLoader()
{
linker->freeze();
}
upx_byte *Packer::getLoader() const upx_byte *Packer::getLoader() const
{ {
int size = -1; int size = -1;
@ -1034,6 +1028,12 @@ void Packer::initLoader(const void *pdata, int plen, int small)
} }
void Packer::addLoader(const char *s)
{
if (s && *s)
linker->addLoader(s);
}
void __acc_cdecl_va Packer::addLoader(const char *s, ...) void __acc_cdecl_va Packer::addLoader(const char *s, ...)
{ {
va_list ap; va_list ap;

View File

@ -206,14 +206,16 @@ protected:
virtual int buildLoader(const Filter *ft) = 0; virtual int buildLoader(const Filter *ft) = 0;
virtual Linker* newLinker() const = 0; virtual Linker* newLinker() const = 0;
// loader util for linker // loader util for linker
virtual void freezeLoader(); void freezeLoader() { }
virtual upx_byte *getLoader() const; virtual upx_byte *getLoader() const;
virtual int getLoaderSize() const; virtual int getLoaderSize() const;
virtual void initLoader(const void *pdata, int plen, int small=-1); virtual void initLoader(const void *pdata, int plen, int small=-1);
#if 1 && (ACC_CC_GNUC >= 0x040100) #if 1 && (ACC_CC_GNUC >= 0x040100)
virtual void __acc_cdecl_va addLoader(const char *s, ...) __attribute__((__sentinel__)); virtual void addLoader(const char *);
virtual void __acc_cdecl_va addLoader(const char *, ...) __attribute__((__sentinel__));
#else #else
virtual void __acc_cdecl_va addLoader(const char *s, ...); virtual void addLoader(const char *);
virtual void __acc_cdecl_va addLoader(const char *, ...);
#endif #endif
virtual int getLoaderSection(const char *name, int *slen=NULL) const; virtual int getLoaderSection(const char *name, int *slen=NULL) const;
virtual int getLoaderSectionStart(const char *name, int *slen=NULL) const; virtual int getLoaderSectionStart(const char *name, int *slen=NULL) const;