mirror of
https://github.com/golang/go
synced 2024-11-23 04:50:06 -07:00
liblink, cmd/ld: reenable nosplit checking and test
The new code is adapted from the Go 1.2 nosplit code, but it does not have the bug reported in issue 7623: g% go run nosplit.go g% go1.2 run nosplit.go BUG rejected incorrectly: main 0 call f; f 120 linker output: # _/tmp/go-test-nosplit021064539 main.main: nosplit stack overflow 120 guaranteed after split check in main.main 112 on entry to main.f -8 after main.f uses 120 g% Fixes #6931. Fixes #7623. LGTM=iant R=golang-codereviews, iant, ality CC=golang-codereviews, r https://golang.org/cl/88190043
This commit is contained in:
parent
877e0a135f
commit
5e8c922625
@ -124,6 +124,8 @@ struct LSym
|
|||||||
short type;
|
short type;
|
||||||
short version;
|
short version;
|
||||||
uchar dupok;
|
uchar dupok;
|
||||||
|
uchar external;
|
||||||
|
uchar nosplit;
|
||||||
uchar reachable;
|
uchar reachable;
|
||||||
uchar cgoexport;
|
uchar cgoexport;
|
||||||
uchar special;
|
uchar special;
|
||||||
@ -229,7 +231,9 @@ enum
|
|||||||
{
|
{
|
||||||
R_ADDR = 1,
|
R_ADDR = 1,
|
||||||
R_SIZE,
|
R_SIZE,
|
||||||
R_CALL,
|
R_CALL, // relocation for direct PC-relative call
|
||||||
|
R_CALLARM, // relocation for ARM direct call
|
||||||
|
R_CALLIND, // marker for indirect call (no actual relocating necessary)
|
||||||
R_CONST,
|
R_CONST,
|
||||||
R_PCREL,
|
R_PCREL,
|
||||||
R_TLS,
|
R_TLS,
|
||||||
@ -313,19 +317,20 @@ struct Pcln
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Pcdata iterator.
|
// Pcdata iterator.
|
||||||
// for(pciterinit(&it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) }
|
// for(pciterinit(ctxt, &it, &pcd); !it.done; pciternext(&it)) { it.value holds in [it.pc, it.nextpc) }
|
||||||
struct Pciter
|
struct Pciter
|
||||||
{
|
{
|
||||||
Pcdata d;
|
Pcdata d;
|
||||||
uchar *p;
|
uchar *p;
|
||||||
uint32 pc;
|
uint32 pc;
|
||||||
uint32 nextpc;
|
uint32 nextpc;
|
||||||
|
uint32 pcscale;
|
||||||
int32 value;
|
int32 value;
|
||||||
int start;
|
int start;
|
||||||
int done;
|
int done;
|
||||||
};
|
};
|
||||||
|
|
||||||
void pciterinit(Pciter*, Pcdata*);
|
void pciterinit(Link*, Pciter*, Pcdata*);
|
||||||
void pciternext(Pciter*);
|
void pciternext(Pciter*);
|
||||||
|
|
||||||
// symbol version, incremented each time a file is loaded.
|
// symbol version, incremented each time a file is loaded.
|
||||||
|
@ -102,7 +102,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
|
|
||||||
// Handle relocations found in ELF object files.
|
// Handle relocations found in ELF object files.
|
||||||
case 256 + R_ARM_PLT32:
|
case 256 + R_ARM_PLT32:
|
||||||
r->type = R_CALL;
|
r->type = R_CALLARM;
|
||||||
if(targ->type == SDYNIMPORT) {
|
if(targ->type == SDYNIMPORT) {
|
||||||
addpltsym(ctxt, targ);
|
addpltsym(ctxt, targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
@ -148,7 +148,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case 256 + R_ARM_CALL:
|
case 256 + R_ARM_CALL:
|
||||||
r->type = R_CALL;
|
r->type = R_CALLARM;
|
||||||
if(targ->type == SDYNIMPORT) {
|
if(targ->type == SDYNIMPORT) {
|
||||||
addpltsym(ctxt, targ);
|
addpltsym(ctxt, targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
@ -178,7 +178,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
|
|
||||||
case 256 + R_ARM_PC24:
|
case 256 + R_ARM_PC24:
|
||||||
case 256 + R_ARM_JUMP24:
|
case 256 + R_ARM_JUMP24:
|
||||||
r->type = R_CALL;
|
r->type = R_CALLARM;
|
||||||
if(targ->type == SDYNIMPORT) {
|
if(targ->type == SDYNIMPORT) {
|
||||||
addpltsym(ctxt, targ);
|
addpltsym(ctxt, targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
@ -192,7 +192,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
switch(r->type) {
|
switch(r->type) {
|
||||||
case R_PCREL:
|
case R_CALLARM:
|
||||||
addpltsym(ctxt, targ);
|
addpltsym(ctxt, targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
r->add = targ->plt;
|
r->add = targ->plt;
|
||||||
@ -243,7 +243,7 @@ elfreloc1(Reloc *r, vlong sectoff)
|
|||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case R_CALL:
|
case R_CALLARM:
|
||||||
if(r->siz == 4) {
|
if(r->siz == 4) {
|
||||||
if((r->add & 0xff000000) == 0xeb000000) // BL
|
if((r->add & 0xff000000) == 0xeb000000) // BL
|
||||||
LPUT(R_ARM_CALL | elfsym<<8);
|
LPUT(R_ARM_CALL | elfsym<<8);
|
||||||
@ -310,7 +310,7 @@ archreloc(Reloc *r, LSym *s, vlong *val)
|
|||||||
|
|
||||||
if(linkmode == LinkExternal) {
|
if(linkmode == LinkExternal) {
|
||||||
switch(r->type) {
|
switch(r->type) {
|
||||||
case R_CALL:
|
case R_CALLARM:
|
||||||
r->done = 0;
|
r->done = 0;
|
||||||
|
|
||||||
// set up addend for eventual relocation via outer symbol.
|
// set up addend for eventual relocation via outer symbol.
|
||||||
@ -357,7 +357,7 @@ archreloc(Reloc *r, LSym *s, vlong *val)
|
|||||||
*val = 0xe5bcf000U +
|
*val = 0xe5bcf000U +
|
||||||
(0xfff & (uint32)(symaddr(r->sym) - (symaddr(linklookup(ctxt, ".plt", 0)) + r->off) + r->add + 8));
|
(0xfff & (uint32)(symaddr(r->sym) - (symaddr(linklookup(ctxt, ".plt", 0)) + r->off) + r->add + 8));
|
||||||
return 0;
|
return 0;
|
||||||
case R_CALL: // bl XXXXXX or b YYYYYY
|
case R_CALLARM: // bl XXXXXX or b YYYYYY
|
||||||
*val = braddoff((0xff000000U & (uint32)r->add),
|
*val = braddoff((0xff000000U & (uint32)r->add),
|
||||||
(0xffffff & (uint32)
|
(0xffffff & (uint32)
|
||||||
((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4)));
|
((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4)));
|
||||||
|
@ -200,6 +200,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
switch(r->type) {
|
switch(r->type) {
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
addpltsym(targ);
|
addpltsym(targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
@ -288,6 +289,7 @@ elfreloc1(Reloc *r, vlong sectoff)
|
|||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
if(r->siz == 4) {
|
if(r->siz == 4) {
|
||||||
if(r->xsym->type == SDYNIMPORT)
|
if(r->xsym->type == SDYNIMPORT)
|
||||||
@ -342,6 +344,7 @@ machoreloc1(Reloc *r, vlong sectoff)
|
|||||||
case R_ADDR:
|
case R_ADDR:
|
||||||
v |= MACHO_X86_64_RELOC_UNSIGNED<<28;
|
v |= MACHO_X86_64_RELOC_UNSIGNED<<28;
|
||||||
break;
|
break;
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
v |= 1<<24; // pc-relative bit
|
v |= 1<<24; // pc-relative bit
|
||||||
v |= MACHO_X86_64_RELOC_BRANCH<<28;
|
v |= MACHO_X86_64_RELOC_BRANCH<<28;
|
||||||
|
@ -189,6 +189,7 @@ adddynrel(LSym *s, Reloc *r)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
switch(r->type) {
|
switch(r->type) {
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
addpltsym(ctxt, targ);
|
addpltsym(ctxt, targ);
|
||||||
r->sym = linklookup(ctxt, ".plt", 0);
|
r->sym = linklookup(ctxt, ".plt", 0);
|
||||||
@ -256,6 +257,7 @@ elfreloc1(Reloc *r, vlong sectoff)
|
|||||||
return -1;
|
return -1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
if(r->siz == 4)
|
if(r->siz == 4)
|
||||||
LPUT(R_386_PC32 | elfsym<<8);
|
LPUT(R_386_PC32 | elfsym<<8);
|
||||||
@ -303,6 +305,7 @@ machoreloc1(Reloc *r, vlong sectoff)
|
|||||||
case R_ADDR:
|
case R_ADDR:
|
||||||
v |= MACHO_GENERIC_RELOC_VANILLA<<28;
|
v |= MACHO_GENERIC_RELOC_VANILLA<<28;
|
||||||
break;
|
break;
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
v |= 1<<24; // pc-relative bit
|
v |= 1<<24; // pc-relative bit
|
||||||
v |= MACHO_GENERIC_RELOC_VANILLA<<28;
|
v |= MACHO_GENERIC_RELOC_VANILLA<<28;
|
||||||
|
@ -132,13 +132,11 @@ relocsym(LSym *s)
|
|||||||
{
|
{
|
||||||
Reloc *r;
|
Reloc *r;
|
||||||
LSym *rs;
|
LSym *rs;
|
||||||
Prog p;
|
|
||||||
int32 i, off, siz, fl;
|
int32 i, off, siz, fl;
|
||||||
vlong o;
|
vlong o;
|
||||||
uchar *cast;
|
uchar *cast;
|
||||||
|
|
||||||
ctxt->cursym = s;
|
ctxt->cursym = s;
|
||||||
memset(&p, 0, sizeof p);
|
|
||||||
for(r=s->r; r<s->r+s->nr; r++) {
|
for(r=s->r; r<s->r+s->nr; r++) {
|
||||||
r->done = 1;
|
r->done = 1;
|
||||||
off = r->off;
|
off = r->off;
|
||||||
@ -153,6 +151,8 @@ relocsym(LSym *s)
|
|||||||
}
|
}
|
||||||
if(r->type >= 256)
|
if(r->type >= 256)
|
||||||
continue;
|
continue;
|
||||||
|
if(r->siz == 0) // informational relocation - no work to do
|
||||||
|
continue;
|
||||||
|
|
||||||
// Solaris needs the ability to reference dynimport symbols.
|
// Solaris needs the ability to reference dynimport symbols.
|
||||||
if(HEADTYPE != Hsolaris && r->sym != S && r->sym->type == SDYNIMPORT)
|
if(HEADTYPE != Hsolaris && r->sym != S && r->sym->type == SDYNIMPORT)
|
||||||
@ -244,6 +244,7 @@ relocsym(LSym *s)
|
|||||||
}
|
}
|
||||||
o = symaddr(r->sym) + r->add;
|
o = symaddr(r->sym) + r->add;
|
||||||
break;
|
break;
|
||||||
|
case R_CALL:
|
||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
|
// r->sym can be null when CALL $(constant) is transformed from absolute PC to relative PC call.
|
||||||
if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != ctxt->cursym->sect) {
|
if(linkmode == LinkExternal && r->sym && r->sym->type != SCONST && r->sym->sect != ctxt->cursym->sect) {
|
||||||
@ -299,7 +300,7 @@ relocsym(LSym *s)
|
|||||||
s->p[off] = (int8)o;
|
s->p[off] = (int8)o;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if(r->type == R_PCREL) {
|
if(r->type == R_PCREL || r->type == R_CALL) {
|
||||||
if(o != (int32)o)
|
if(o != (int32)o)
|
||||||
diag("pc-relative relocation address is too big: %#llx", o);
|
diag("pc-relative relocation address is too big: %#llx", o);
|
||||||
} else {
|
} else {
|
||||||
@ -568,6 +569,9 @@ datblk(int32 addr, int32 size)
|
|||||||
case R_PCREL:
|
case R_PCREL:
|
||||||
typ = "pcrel";
|
typ = "pcrel";
|
||||||
break;
|
break;
|
||||||
|
case R_CALL:
|
||||||
|
typ = "call";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Bprint(&bso, "\treloc %.8ux/%d %s %s+%#llx [%#llx]\n",
|
Bprint(&bso, "\treloc %.8ux/%d %s %s+%#llx [%#llx]\n",
|
||||||
(uint)(sym->value+r->off), r->siz, typ, rsname, (vlong)r->add, (vlong)(r->sym->value+r->add));
|
(uint)(sym->value+r->off), r->siz, typ, rsname, (vlong)r->add, (vlong)(r->sym->value+r->add));
|
||||||
|
@ -1604,8 +1604,8 @@ writelines(void)
|
|||||||
|
|
||||||
finddebugruntimepath(s);
|
finddebugruntimepath(s);
|
||||||
|
|
||||||
pciterinit(&pcfile, &s->pcln->pcfile);
|
pciterinit(ctxt, &pcfile, &s->pcln->pcfile);
|
||||||
pciterinit(&pcline, &s->pcln->pcline);
|
pciterinit(ctxt, &pcline, &s->pcln->pcline);
|
||||||
epc = pc;
|
epc = pc;
|
||||||
while(!pcfile.done && !pcline.done) {
|
while(!pcfile.done && !pcline.done) {
|
||||||
if(epc - s->value >= pcfile.nextpc) {
|
if(epc - s->value >= pcfile.nextpc) {
|
||||||
@ -1761,7 +1761,7 @@ writeframes(void)
|
|||||||
addrput(0); // initial location
|
addrput(0); // initial location
|
||||||
addrput(0); // address range
|
addrput(0); // address range
|
||||||
|
|
||||||
for(pciterinit(&pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp))
|
for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp))
|
||||||
putpccfadelta(pcsp.nextpc - pcsp.pc, PtrSize + pcsp.value);
|
putpccfadelta(pcsp.nextpc - pcsp.pc, PtrSize + pcsp.value);
|
||||||
|
|
||||||
fdesize = cpos() - fdeo - 4; // exclude the length field.
|
fdesize = cpos() - fdeo - 4; // exclude the length field.
|
||||||
|
@ -601,24 +601,9 @@ ldelf(Biobuf *f, char *pkg, int64 len, char *pn)
|
|||||||
s->size = sym.size;
|
s->size = sym.size;
|
||||||
s->outer = sect->sym;
|
s->outer = sect->sym;
|
||||||
if(sect->sym->type == STEXT) {
|
if(sect->sym->type == STEXT) {
|
||||||
Prog *p;
|
if(s->external && !s->dupok)
|
||||||
|
|
||||||
if(s->text != P) {
|
|
||||||
if(!s->dupok)
|
|
||||||
diag("%s: duplicate definition of %s", pn, s->name);
|
diag("%s: duplicate definition of %s", pn, s->name);
|
||||||
} else {
|
s->external = 1;
|
||||||
// build a TEXT instruction with a unique pc
|
|
||||||
// just to make the rest of the linker happy.
|
|
||||||
p = ctxt->arch->prg();
|
|
||||||
p->as = ATEXT;
|
|
||||||
p->from.type = D_EXTERN;
|
|
||||||
p->from.sym = s;
|
|
||||||
ctxt->arch->settextflag(p, 7);
|
|
||||||
p->to.type = D_CONST;
|
|
||||||
p->link = nil;
|
|
||||||
p->pc = ctxt->pc++;
|
|
||||||
s->text = p;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -641,22 +641,9 @@ ldmacho(Biobuf *f, char *pkg, int64 len, char *pn)
|
|||||||
if(!(s->cgoexport & CgoExportDynamic))
|
if(!(s->cgoexport & CgoExportDynamic))
|
||||||
s->dynimplib = nil; // satisfy dynimport
|
s->dynimplib = nil; // satisfy dynimport
|
||||||
if(outer->type == STEXT) {
|
if(outer->type == STEXT) {
|
||||||
Prog *p;
|
if(s->external && !s->dupok)
|
||||||
|
diag("%s: duplicate definition of %s", pn, s->name);
|
||||||
if(s->text != P)
|
s->external = 1;
|
||||||
diag("%s sym#%d: duplicate definition of %s", pn, i, s->name);
|
|
||||||
// build a TEXT instruction with a unique pc
|
|
||||||
// just to make the rest of the linker happy.
|
|
||||||
// TODO: this is too 6l-specific ?
|
|
||||||
p = ctxt->arch->prg();
|
|
||||||
p->as = ATEXT;
|
|
||||||
p->from.type = D_EXTERN;
|
|
||||||
p->from.sym = s;
|
|
||||||
ctxt->arch->settextflag(p, 7);
|
|
||||||
p->to.type = D_CONST;
|
|
||||||
p->link = nil;
|
|
||||||
p->pc = ctxt->pc++;
|
|
||||||
s->text = p;
|
|
||||||
}
|
}
|
||||||
sym->sym = s;
|
sym->sym = s;
|
||||||
}
|
}
|
||||||
|
@ -366,21 +366,9 @@ ldpe(Biobuf *f, char *pkg, int64 len, char *pn)
|
|||||||
s->size = 4;
|
s->size = 4;
|
||||||
s->outer = sect->sym;
|
s->outer = sect->sym;
|
||||||
if(sect->sym->type == STEXT) {
|
if(sect->sym->type == STEXT) {
|
||||||
Prog *p;
|
if(s->external && !s->dupok)
|
||||||
|
|
||||||
if(s->text != P)
|
|
||||||
diag("%s: duplicate definition of %s", pn, s->name);
|
diag("%s: duplicate definition of %s", pn, s->name);
|
||||||
// build a TEXT instruction with a unique pc
|
s->external = 1;
|
||||||
// just to make the rest of the linker happy.
|
|
||||||
p = ctxt->arch->prg();
|
|
||||||
p->as = ATEXT;
|
|
||||||
p->from.type = D_EXTERN;
|
|
||||||
p->from.sym = s;
|
|
||||||
ctxt->arch->settextflag(p, 7);
|
|
||||||
p->to.type = D_CONST;
|
|
||||||
p->link = nil;
|
|
||||||
p->pc = ctxt->pc++;
|
|
||||||
s->text = p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
129
src/cmd/ld/lib.c
129
src/cmd/ld/lib.c
@ -1021,50 +1021,59 @@ dostkcheck(void)
|
|||||||
morestack = linklookup(ctxt, "runtime.morestack", 0);
|
morestack = linklookup(ctxt, "runtime.morestack", 0);
|
||||||
newstack = linklookup(ctxt, "runtime.newstack", 0);
|
newstack = linklookup(ctxt, "runtime.newstack", 0);
|
||||||
|
|
||||||
// TODO
|
// Every splitting function ensures that there are at least StackLimit
|
||||||
// First the nosplits on their own.
|
// bytes available below SP when the splitting prologue finishes.
|
||||||
for(s = ctxt->textp; s != nil; s = s->next) {
|
// If the splitting function calls F, then F begins execution with
|
||||||
if(s->text == nil || s->text->link == nil || (ctxt->arch->textflag(s->text) & NOSPLIT) == 0)
|
// at least StackLimit - callsize() bytes available.
|
||||||
continue;
|
// Check that every function behaves correctly with this amount
|
||||||
ctxt->cursym = s;
|
// of stack, following direct calls in order to piece together chains
|
||||||
ch.up = nil;
|
// of non-splitting functions.
|
||||||
ch.sym = s;
|
ch.up = nil;
|
||||||
ch.limit = StackLimit - callsize();
|
ch.limit = StackLimit - callsize();
|
||||||
stkcheck(&ch, 0);
|
|
||||||
s->stkcheck = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check calling contexts.
|
// Check every function, but do the nosplit functions in a first pass,
|
||||||
// Some nosplits get called a little further down,
|
// to make the printed failure chains as short as possible.
|
||||||
// like newproc and deferproc. We could hard-code
|
|
||||||
// that knowledge but it's more robust to look at
|
|
||||||
// the actual call sites.
|
|
||||||
for(s = ctxt->textp; s != nil; s = s->next) {
|
for(s = ctxt->textp; s != nil; s = s->next) {
|
||||||
if(s->text == nil || s->text->link == nil || (ctxt->arch->textflag(s->text) & NOSPLIT) != 0)
|
// runtime.racesymbolizethunk is called from gcc-compiled C
|
||||||
|
// code running on the operating system thread stack.
|
||||||
|
// It uses more than the usual amount of stack but that's okay.
|
||||||
|
if(strcmp(s->name, "runtime.racesymbolizethunk") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if(s->nosplit) {
|
||||||
ctxt->cursym = s;
|
ctxt->cursym = s;
|
||||||
ch.up = nil;
|
|
||||||
ch.sym = s;
|
ch.sym = s;
|
||||||
ch.limit = StackLimit - callsize();
|
|
||||||
stkcheck(&ch, 0);
|
stkcheck(&ch, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for(s = ctxt->textp; s != nil; s = s->next) {
|
||||||
|
if(!s->nosplit) {
|
||||||
|
ctxt->cursym = s;
|
||||||
|
ch.sym = s;
|
||||||
|
stkcheck(&ch, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
stkcheck(Chain *up, int depth)
|
stkcheck(Chain *up, int depth)
|
||||||
{
|
{
|
||||||
Chain ch, ch1;
|
Chain ch, ch1;
|
||||||
Prog *p;
|
|
||||||
LSym *s;
|
LSym *s;
|
||||||
int limit, prolog;
|
int limit;
|
||||||
|
Reloc *r, *endr;
|
||||||
|
Pciter pcsp;
|
||||||
|
|
||||||
limit = up->limit;
|
limit = up->limit;
|
||||||
s = up->sym;
|
s = up->sym;
|
||||||
p = s->text;
|
|
||||||
|
|
||||||
// Small optimization: don't repeat work at top.
|
// Don't duplicate work: only need to consider each
|
||||||
if(s->stkcheck && limit == StackLimit-callsize())
|
// function at top of safe zone once.
|
||||||
|
if(limit == StackLimit-callsize()) {
|
||||||
|
if(s->stkcheck)
|
||||||
return 0;
|
return 0;
|
||||||
|
s->stkcheck = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if(depth > 100) {
|
if(depth > 100) {
|
||||||
diag("nosplit stack check too deep");
|
diag("nosplit stack check too deep");
|
||||||
@ -1072,7 +1081,7 @@ stkcheck(Chain *up, int depth)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(p == nil || p->link == nil) {
|
if(s->external || s->pcln == nil) {
|
||||||
// external function.
|
// external function.
|
||||||
// should never be called directly.
|
// should never be called directly.
|
||||||
// only diagnose the direct caller.
|
// only diagnose the direct caller.
|
||||||
@ -1092,50 +1101,56 @@ stkcheck(Chain *up, int depth)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ch.up = up;
|
ch.up = up;
|
||||||
prolog = (ctxt->arch->textflag(s->text) & NOSPLIT) == 0;
|
|
||||||
for(p = s->text; p != P; p = p->link) {
|
// Walk through sp adjustments in function, consuming relocs.
|
||||||
limit -= p->spadj;
|
r = s->r;
|
||||||
if(prolog && p->spadj != 0) {
|
endr = r + s->nr;
|
||||||
// The first stack adjustment in a function with a
|
for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp)) {
|
||||||
// split-checking prologue marks the end of the
|
// pcsp.value is in effect for [pcsp.pc, pcsp.nextpc).
|
||||||
// prologue. Assuming the split check is correct,
|
|
||||||
// after the adjustment there should still be at least
|
// Check stack size in effect for this span.
|
||||||
// StackLimit bytes available below the stack pointer.
|
if(limit - pcsp.value < 0) {
|
||||||
// If this is not the top call in the chain, no need
|
stkbroke(up, limit - pcsp.value);
|
||||||
// to duplicate effort, so just stop.
|
|
||||||
if(depth > 0)
|
|
||||||
return 0;
|
|
||||||
prolog = 0;
|
|
||||||
limit = StackLimit;
|
|
||||||
}
|
|
||||||
if(limit < 0) {
|
|
||||||
stkbroke(up, limit);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if(ctxt->arch->iscall(p)) {
|
|
||||||
limit -= callsize();
|
// Process calls in this span.
|
||||||
ch.limit = limit;
|
for(; r < endr && r->off < pcsp.nextpc; r++) {
|
||||||
if(p->to.type == D_BRANCH) {
|
switch(r->type) {
|
||||||
|
case R_CALL:
|
||||||
|
case R_CALLARM:
|
||||||
// Direct call.
|
// Direct call.
|
||||||
ch.sym = p->to.sym;
|
ch.limit = limit - pcsp.value - callsize();
|
||||||
|
ch.sym = r->sym;
|
||||||
if(stkcheck(&ch, depth+1) < 0)
|
if(stkcheck(&ch, depth+1) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
|
||||||
// Indirect call. Assume it is a splitting function,
|
// If this is a call to morestack, we've just raised our limit back
|
||||||
|
// to StackLimit beyond the frame size.
|
||||||
|
if(strncmp(r->sym->name, "runtime.morestack", 17) == 0) {
|
||||||
|
limit = StackLimit + s->locals;
|
||||||
|
if(thechar == '5')
|
||||||
|
limit += 4; // saved LR
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case R_CALLIND:
|
||||||
|
// Indirect call. Assume it is a call to a splitting function,
|
||||||
// so we have to make sure it can call morestack.
|
// so we have to make sure it can call morestack.
|
||||||
limit -= callsize();
|
// Arrange the data structures to report both calls, so that
|
||||||
|
// if there is an error, stkprint shows all the steps involved.
|
||||||
|
ch.limit = limit - pcsp.value - callsize();
|
||||||
ch.sym = nil;
|
ch.sym = nil;
|
||||||
ch1.limit = limit;
|
ch1.limit = ch.limit - callsize(); // for morestack in called prologue
|
||||||
ch1.up = &ch;
|
ch1.up = &ch;
|
||||||
ch1.sym = morestack;
|
ch1.sym = morestack;
|
||||||
if(stkcheck(&ch1, depth+2) < 0)
|
if(stkcheck(&ch1, depth+2) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
limit += callsize();
|
break;
|
||||||
}
|
}
|
||||||
limit += callsize();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1158,7 +1173,7 @@ stkprint(Chain *ch, int limit)
|
|||||||
|
|
||||||
if(ch->up == nil) {
|
if(ch->up == nil) {
|
||||||
// top of chain. ch->sym != nil.
|
// top of chain. ch->sym != nil.
|
||||||
if(ctxt->arch->textflag(ch->sym->text) & NOSPLIT)
|
if(ch->sym->nosplit)
|
||||||
print("\t%d\tassumed on entry to %s\n", ch->limit, name);
|
print("\t%d\tassumed on entry to %s\n", ch->limit, name);
|
||||||
else
|
else
|
||||||
print("\t%d\tguaranteed after split check in %s\n", ch->limit, name);
|
print("\t%d\tguaranteed after split check in %s\n", ch->limit, name);
|
||||||
|
@ -55,7 +55,7 @@ ftabaddstring(LSym *ftab, char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
renumberfiles(LSym **files, int nfiles, Pcdata *d)
|
renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
LSym *f;
|
LSym *f;
|
||||||
@ -78,7 +78,7 @@ renumberfiles(LSym **files, int nfiles, Pcdata *d)
|
|||||||
newval = -1;
|
newval = -1;
|
||||||
memset(&out, 0, sizeof out);
|
memset(&out, 0, sizeof out);
|
||||||
|
|
||||||
for(pciterinit(&it, d); !it.done; pciternext(&it)) {
|
for(pciterinit(ctxt, &it, d); !it.done; pciternext(&it)) {
|
||||||
// value delta
|
// value delta
|
||||||
oldval = it.value;
|
oldval = it.value;
|
||||||
if(oldval == -1)
|
if(oldval == -1)
|
||||||
@ -94,7 +94,7 @@ renumberfiles(LSym **files, int nfiles, Pcdata *d)
|
|||||||
addvarint(&out, v);
|
addvarint(&out, v);
|
||||||
|
|
||||||
// pc delta
|
// pc delta
|
||||||
addvarint(&out, it.nextpc - it.pc);
|
addvarint(&out, (it.nextpc - it.pc) / it.pcscale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// terminating value delta
|
// terminating value delta
|
||||||
@ -179,10 +179,10 @@ pclntab(void)
|
|||||||
off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize);
|
off = setuint32(ctxt, ftab, off, ctxt->cursym->locals + frameptrsize);
|
||||||
|
|
||||||
if(pcln != &zpcln) {
|
if(pcln != &zpcln) {
|
||||||
renumberfiles(pcln->file, pcln->nfile, &pcln->pcfile);
|
renumberfiles(ctxt, pcln->file, pcln->nfile, &pcln->pcfile);
|
||||||
if(0) {
|
if(0) {
|
||||||
// Sanity check the new numbering
|
// Sanity check the new numbering
|
||||||
for(pciterinit(&it, &pcln->pcfile); !it.done; pciternext(&it)) {
|
for(pciterinit(ctxt, &it, &pcln->pcfile); !it.done; pciternext(&it)) {
|
||||||
if(it.value < 1 || it.value > ctxt->nhistfile) {
|
if(it.value < 1 || it.value > ctxt->nhistfile) {
|
||||||
diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile);
|
diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, ctxt->nhistfile);
|
||||||
errorexit();
|
errorexit();
|
||||||
|
@ -75,9 +75,13 @@ func (p *Prog) loadPackage(pkg *Package) {
|
|||||||
|
|
||||||
// TODO(rsc): Define full enumeration for relocation types.
|
// TODO(rsc): Define full enumeration for relocation types.
|
||||||
const (
|
const (
|
||||||
R_ADDR = 1
|
R_ADDR = 1
|
||||||
R_SIZE = 2
|
R_SIZE = 2
|
||||||
R_PCREL = 5
|
R_CALL = 3
|
||||||
|
R_CALLARM = 4
|
||||||
|
R_CALLIND = 5
|
||||||
|
R_CONST = 6
|
||||||
|
R_PCREL = 7
|
||||||
)
|
)
|
||||||
|
|
||||||
// relocateSym applies relocations to sym's data.
|
// relocateSym applies relocations to sym's data.
|
||||||
@ -93,9 +97,9 @@ func (p *Prog) relocateSym(sym *Sym, data []byte) {
|
|||||||
switch r.Type {
|
switch r.Type {
|
||||||
default:
|
default:
|
||||||
p.errorf("%v: unknown relocation type %d", sym, r.Type)
|
p.errorf("%v: unknown relocation type %d", sym, r.Type)
|
||||||
case R_ADDR:
|
case R_ADDR, R_CALLIND:
|
||||||
// ok
|
// ok
|
||||||
case R_PCREL:
|
case R_PCREL, R_CALL:
|
||||||
val -= sym.Addr + Addr(r.Offset+r.Size)
|
val -= sym.Addr + Addr(r.Offset+r.Size)
|
||||||
}
|
}
|
||||||
frag := data[r.Offset : r.Offset+r.Size]
|
frag := data[r.Offset : r.Offset+r.Size]
|
||||||
|
BIN
src/cmd/link/testdata/autosection.6
vendored
BIN
src/cmd/link/testdata/autosection.6
vendored
Binary file not shown.
BIN
src/cmd/link/testdata/autoweak.6
vendored
BIN
src/cmd/link/testdata/autoweak.6
vendored
Binary file not shown.
BIN
src/cmd/link/testdata/dead.6
vendored
BIN
src/cmd/link/testdata/dead.6
vendored
Binary file not shown.
BIN
src/cmd/link/testdata/hello.6
vendored
BIN
src/cmd/link/testdata/hello.6
vendored
Binary file not shown.
BIN
src/cmd/link/testdata/layout.6
vendored
BIN
src/cmd/link/testdata/layout.6
vendored
Binary file not shown.
BIN
src/cmd/link/testdata/pclntab.6
vendored
BIN
src/cmd/link/testdata/pclntab.6
vendored
Binary file not shown.
@ -1302,7 +1302,7 @@ if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->na
|
|||||||
rel->siz = 4;
|
rel->siz = 4;
|
||||||
rel->sym = p->to.sym;
|
rel->sym = p->to.sym;
|
||||||
rel->add = o1 | ((v >> 2) & 0xffffff);
|
rel->add = o1 | ((v >> 2) & 0xffffff);
|
||||||
rel->type = R_CALL;
|
rel->type = R_CALLARM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(p->pcond != nil)
|
if(p->pcond != nil)
|
||||||
@ -1324,6 +1324,10 @@ if(0 /*debug['G']*/) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->na
|
|||||||
ctxt->diag("%P: doesn't support BL offset(REG) where offset != 0", p);
|
ctxt->diag("%P: doesn't support BL offset(REG) where offset != 0", p);
|
||||||
o1 = oprrr(ctxt, ABL, p->scond);
|
o1 = oprrr(ctxt, ABL, p->scond);
|
||||||
o1 |= p->to.reg;
|
o1 |= p->to.reg;
|
||||||
|
rel = addrel(ctxt->cursym);
|
||||||
|
rel->off = ctxt->pc;
|
||||||
|
rel->siz = 0;
|
||||||
|
rel->type = R_CALLIND;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 8: /* sll $c,[R],R -> mov (R<<$c),R */
|
case 8: /* sll $c,[R],R -> mov (R<<$c),R */
|
||||||
|
@ -124,6 +124,7 @@ enum
|
|||||||
Z_rp,
|
Z_rp,
|
||||||
Zbr,
|
Zbr,
|
||||||
Zcall,
|
Zcall,
|
||||||
|
Zcallindreg,
|
||||||
Zib_,
|
Zib_,
|
||||||
Zib_rp,
|
Zib_rp,
|
||||||
Zibo_m,
|
Zibo_m,
|
||||||
@ -503,8 +504,8 @@ static uchar yloop[] =
|
|||||||
};
|
};
|
||||||
static uchar ycall[] =
|
static uchar ycall[] =
|
||||||
{
|
{
|
||||||
Ynone, Yml, Zo_m64, 0,
|
Ynone, Yml, Zcallindreg, 0,
|
||||||
Yrx, Yrx, Zo_m64, 2,
|
Yrx, Yrx, Zcallindreg, 2,
|
||||||
Ynone, Ybr, Zcall, 1,
|
Ynone, Ybr, Zcall, 1,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
@ -2903,6 +2904,7 @@ found:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Zo_m64:
|
case Zo_m64:
|
||||||
|
case_Zo_m64:
|
||||||
*ctxt->andptr++ = op;
|
*ctxt->andptr++ = op;
|
||||||
asmandsz(ctxt, &p->to, o->op[z+1], 0, 1);
|
asmandsz(ctxt, &p->to, o->op[z+1], 0, 1);
|
||||||
break;
|
break;
|
||||||
@ -3074,11 +3076,18 @@ found:
|
|||||||
r->off = p->pc + ctxt->andptr - ctxt->and;
|
r->off = p->pc + ctxt->andptr - ctxt->and;
|
||||||
r->sym = p->to.sym;
|
r->sym = p->to.sym;
|
||||||
r->add = p->to.offset;
|
r->add = p->to.offset;
|
||||||
r->type = R_PCREL;
|
r->type = R_CALL;
|
||||||
r->siz = 4;
|
r->siz = 4;
|
||||||
put4(ctxt, 0);
|
put4(ctxt, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Zcallindreg:
|
||||||
|
r = addrel(ctxt->cursym);
|
||||||
|
r->off = p->pc;
|
||||||
|
r->type = R_CALLIND;
|
||||||
|
r->siz = 0;
|
||||||
|
goto case_Zo_m64;
|
||||||
|
|
||||||
case Zbr:
|
case Zbr:
|
||||||
case Zjmp:
|
case Zjmp:
|
||||||
case Zloop:
|
case Zloop:
|
||||||
@ -3548,7 +3557,7 @@ asmins(Link *ctxt, Prog *p)
|
|||||||
break;
|
break;
|
||||||
if(ctxt->rexflag)
|
if(ctxt->rexflag)
|
||||||
r->off++;
|
r->off++;
|
||||||
if(r->type == R_PCREL)
|
if(r->type == R_PCREL || r->type == R_CALL)
|
||||||
r->add -= p->pc + n - (r->off + r->siz);
|
r->add -= p->pc + n - (r->off + r->siz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ enum
|
|||||||
Zcall,
|
Zcall,
|
||||||
Zcallcon,
|
Zcallcon,
|
||||||
Zcallind,
|
Zcallind,
|
||||||
|
Zcallindreg,
|
||||||
Zib_,
|
Zib_,
|
||||||
Zib_rp,
|
Zib_rp,
|
||||||
Zibo_m,
|
Zibo_m,
|
||||||
@ -414,8 +415,8 @@ static uchar yloop[] =
|
|||||||
};
|
};
|
||||||
static uchar ycall[] =
|
static uchar ycall[] =
|
||||||
{
|
{
|
||||||
Ynone, Yml, Zo_m, 0,
|
Ynone, Yml, Zcallindreg, 0,
|
||||||
Yrx, Yrx, Zo_m, 2,
|
Yrx, Yrx, Zcallindreg, 2,
|
||||||
Ynone, Ycol, Zcallind, 2,
|
Ynone, Ycol, Zcallind, 2,
|
||||||
Ynone, Ybr, Zcall, 0,
|
Ynone, Ybr, Zcall, 0,
|
||||||
Ynone, Yi32, Zcallcon, 1,
|
Ynone, Yi32, Zcallcon, 1,
|
||||||
@ -2289,6 +2290,7 @@ found:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Zo_m:
|
case Zo_m:
|
||||||
|
case_Zo_m:
|
||||||
*ctxt->andptr++ = op;
|
*ctxt->andptr++ = op;
|
||||||
asmand(ctxt, &p->to, o->op[z+1]);
|
asmand(ctxt, &p->to, o->op[z+1]);
|
||||||
break;
|
break;
|
||||||
@ -2406,7 +2408,7 @@ found:
|
|||||||
*ctxt->andptr++ = op;
|
*ctxt->andptr++ = op;
|
||||||
r = addrel(ctxt->cursym);
|
r = addrel(ctxt->cursym);
|
||||||
r->off = p->pc + ctxt->andptr - ctxt->and;
|
r->off = p->pc + ctxt->andptr - ctxt->and;
|
||||||
r->type = R_PCREL;
|
r->type = R_CALL;
|
||||||
r->siz = 4;
|
r->siz = 4;
|
||||||
r->sym = p->to.sym;
|
r->sym = p->to.sym;
|
||||||
r->add = p->to.offset;
|
r->add = p->to.offset;
|
||||||
@ -2509,6 +2511,13 @@ found:
|
|||||||
put4(ctxt, 0);
|
put4(ctxt, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Zcallindreg:
|
||||||
|
r = addrel(ctxt->cursym);
|
||||||
|
r->off = p->pc;
|
||||||
|
r->type = R_CALLIND;
|
||||||
|
r->siz = 0;
|
||||||
|
goto case_Zo_m;
|
||||||
|
|
||||||
case Zbyte:
|
case Zbyte:
|
||||||
v = vaddr(ctxt, &p->from, &rel);
|
v = vaddr(ctxt, &p->from, &rel);
|
||||||
if(rel.siz != 0) {
|
if(rel.siz != 0) {
|
||||||
|
@ -417,16 +417,16 @@ addstacksplit(Link *ctxt, LSym *cursym)
|
|||||||
cursym->locals = textstksiz;
|
cursym->locals = textstksiz;
|
||||||
|
|
||||||
if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) {
|
if(autoffset < StackSmall && !(p->from.scale & NOSPLIT)) {
|
||||||
for(q = p; q != nil; q = q->link)
|
for(q = p; q != nil; q = q->link) {
|
||||||
if(q->as == ACALL)
|
if(q->as == ACALL)
|
||||||
goto noleaf;
|
goto noleaf;
|
||||||
|
if((q->as == ADUFFCOPY || q->as == ADUFFZERO) && autoffset >= StackSmall - 8)
|
||||||
|
goto noleaf;
|
||||||
|
}
|
||||||
p->from.scale |= NOSPLIT;
|
p->from.scale |= NOSPLIT;
|
||||||
noleaf:;
|
noleaf:;
|
||||||
}
|
}
|
||||||
|
|
||||||
if((p->from.scale & NOSPLIT) && autoffset >= StackLimit)
|
|
||||||
ctxt->diag("nosplit func likely to overflow stack");
|
|
||||||
|
|
||||||
q = nil;
|
q = nil;
|
||||||
if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
|
if(!(p->from.scale & NOSPLIT) || (p->from.scale & WRAPPER)) {
|
||||||
p = appendp(ctxt, p);
|
p = appendp(ctxt, p);
|
||||||
|
@ -43,12 +43,13 @@
|
|||||||
// - gotype [symbol reference]
|
// - gotype [symbol reference]
|
||||||
// - p [data block]
|
// - p [data block]
|
||||||
// - nr [int]
|
// - nr [int]
|
||||||
// - r [nr relocations]
|
// - r [nr relocations, sorted by off]
|
||||||
//
|
//
|
||||||
// If type == STEXT, there are a few more fields:
|
// If type == STEXT, there are a few more fields:
|
||||||
//
|
//
|
||||||
// - args [int]
|
// - args [int]
|
||||||
// - locals [int]
|
// - locals [int]
|
||||||
|
// - nosplit [int]
|
||||||
// - leaf [int]
|
// - leaf [int]
|
||||||
// - nlocal [int]
|
// - nlocal [int]
|
||||||
// - local [nlocal automatics]
|
// - local [nlocal automatics]
|
||||||
@ -226,6 +227,8 @@ writeobj(Link *ctxt, Biobuf *b)
|
|||||||
flag = p->from.scale;
|
flag = p->from.scale;
|
||||||
if(flag & DUPOK)
|
if(flag & DUPOK)
|
||||||
s->dupok = 1;
|
s->dupok = 1;
|
||||||
|
if(flag & NOSPLIT)
|
||||||
|
s->nosplit = 1;
|
||||||
s->next = nil;
|
s->next = nil;
|
||||||
s->type = STEXT;
|
s->type = STEXT;
|
||||||
s->text = p;
|
s->text = p;
|
||||||
@ -294,6 +297,8 @@ writesym(Link *ctxt, Biobuf *b, LSym *s)
|
|||||||
Bprint(ctxt->bso, "t=%d ", s->type);
|
Bprint(ctxt->bso, "t=%d ", s->type);
|
||||||
if(s->dupok)
|
if(s->dupok)
|
||||||
Bprint(ctxt->bso, "dupok ");
|
Bprint(ctxt->bso, "dupok ");
|
||||||
|
if(s->nosplit)
|
||||||
|
Bprint(ctxt->bso, "nosplit ");
|
||||||
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
|
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
|
||||||
if(s->type == STEXT) {
|
if(s->type == STEXT) {
|
||||||
Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals);
|
Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals);
|
||||||
@ -353,6 +358,7 @@ writesym(Link *ctxt, Biobuf *b, LSym *s)
|
|||||||
if(s->type == STEXT) {
|
if(s->type == STEXT) {
|
||||||
wrint(b, s->args);
|
wrint(b, s->args);
|
||||||
wrint(b, s->locals);
|
wrint(b, s->locals);
|
||||||
|
wrint(b, s->nosplit);
|
||||||
wrint(b, s->leaf);
|
wrint(b, s->leaf);
|
||||||
n = 0;
|
n = 0;
|
||||||
for(a = s->autom; a != nil; a = a->link)
|
for(a = s->autom; a != nil; a = a->link)
|
||||||
@ -574,6 +580,7 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
|
|||||||
if(s->type == STEXT) {
|
if(s->type == STEXT) {
|
||||||
s->args = rdint(f);
|
s->args = rdint(f);
|
||||||
s->locals = rdint(f);
|
s->locals = rdint(f);
|
||||||
|
s->nosplit = rdint(f);
|
||||||
s->leaf = rdint(f);
|
s->leaf = rdint(f);
|
||||||
n = rdint(f);
|
n = rdint(f);
|
||||||
for(i=0; i<n; i++) {
|
for(i=0; i<n; i++) {
|
||||||
@ -630,6 +637,8 @@ readsym(Link *ctxt, Biobuf *f, char *pkg, char *pn)
|
|||||||
Bprint(ctxt->bso, "t=%d ", s->type);
|
Bprint(ctxt->bso, "t=%d ", s->type);
|
||||||
if(s->dupok)
|
if(s->dupok)
|
||||||
Bprint(ctxt->bso, "dupok ");
|
Bprint(ctxt->bso, "dupok ");
|
||||||
|
if(s->nosplit)
|
||||||
|
Bprint(ctxt->bso, "nosplit ");
|
||||||
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
|
Bprint(ctxt->bso, "size=%lld value=%lld", (vlong)s->size, (vlong)s->value);
|
||||||
if(s->type == STEXT)
|
if(s->type == STEXT)
|
||||||
Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals);
|
Bprint(ctxt->bso, " args=%#llux locals=%#llux", (uvlong)s->args, (uvlong)s->locals);
|
||||||
|
@ -347,11 +347,11 @@ pciternext(Pciter *it)
|
|||||||
|
|
||||||
// pc delta
|
// pc delta
|
||||||
v = getvarint(&it->p);
|
v = getvarint(&it->p);
|
||||||
it->nextpc = it->pc + v;
|
it->nextpc = it->pc + v*it->pcscale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
pciterinit(Pciter *it, Pcdata *d)
|
pciterinit(Link *ctxt, Pciter *it, Pcdata *d)
|
||||||
{
|
{
|
||||||
it->d = *d;
|
it->d = *d;
|
||||||
it->p = it->d.p;
|
it->p = it->d.p;
|
||||||
@ -360,5 +360,6 @@ pciterinit(Pciter *it, Pcdata *d)
|
|||||||
it->value = -1;
|
it->value = -1;
|
||||||
it->start = 1;
|
it->start = 1;
|
||||||
it->done = 0;
|
it->done = 0;
|
||||||
|
it->pcscale = ctxt->arch->minlc;
|
||||||
pciternext(it);
|
pciternext(it);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +191,7 @@ type Func struct {
|
|||||||
Args int // size in bytes of argument frame: inputs and outputs
|
Args int // size in bytes of argument frame: inputs and outputs
|
||||||
Frame int // size in bytes of local variable frame
|
Frame int // size in bytes of local variable frame
|
||||||
Leaf bool // function omits save of link register (ARM)
|
Leaf bool // function omits save of link register (ARM)
|
||||||
|
NoSplit bool // function omits stack split prologue
|
||||||
Var []Var // detail about local variables
|
Var []Var // detail about local variables
|
||||||
PCSP Data // PC → SP offset map
|
PCSP Data // PC → SP offset map
|
||||||
PCFile Data // PC → file number map (index into File)
|
PCFile Data // PC → file number map (index into File)
|
||||||
@ -623,6 +624,7 @@ func (r *objReader) parseObject(prefix []byte) error {
|
|||||||
f.Args = r.readInt()
|
f.Args = r.readInt()
|
||||||
f.Frame = r.readInt()
|
f.Frame = r.readInt()
|
||||||
f.Leaf = r.readInt() != 0
|
f.Leaf = r.readInt() != 0
|
||||||
|
f.NoSplit = r.readInt() != 0
|
||||||
f.Var = make([]Var, r.readInt())
|
f.Var = make([]Var, r.readInt())
|
||||||
for i := range f.Var {
|
for i := range f.Var {
|
||||||
v := &f.Var[i]
|
v := &f.Var[i]
|
||||||
|
313
test/nosplit.go
Normal file
313
test/nosplit.go
Normal file
@ -0,0 +1,313 @@
|
|||||||
|
// run
|
||||||
|
|
||||||
|
// Copyright 2014 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var tests = `
|
||||||
|
# These are test cases for the linker analysis that detects chains of
|
||||||
|
# nosplit functions that would cause a stack overflow.
|
||||||
|
#
|
||||||
|
# Lines beginning with # are comments.
|
||||||
|
#
|
||||||
|
# Each test case describes a sequence of functions, one per line.
|
||||||
|
# Each function definition is the function name, then the frame size,
|
||||||
|
# then optionally the keyword 'nosplit', then the body of the function.
|
||||||
|
# The body is assembly code, with some shorthands.
|
||||||
|
# The shorthand 'call x' stands for CALL x(SB).
|
||||||
|
# The shorthand 'callind' stands for 'CALL R0', where R0 is a register.
|
||||||
|
# Each test case must define a function named main, and it must be first.
|
||||||
|
# That is, a line beginning "main " indicates the start of a new test case.
|
||||||
|
# Within a stanza, ; can be used instead of \n to separate lines.
|
||||||
|
#
|
||||||
|
# After the function definition, the test case ends with an optional
|
||||||
|
# REJECT line, specifying the architectures on which the case should
|
||||||
|
# be rejected. "REJECT" without any architectures means reject on all architectures.
|
||||||
|
# The linker should accept the test case on systems not explicitly rejected.
|
||||||
|
#
|
||||||
|
# 64-bit systems do not attempt to execute test cases with frame sizes
|
||||||
|
# that are only 32-bit aligned.
|
||||||
|
|
||||||
|
# Ordinary function should work
|
||||||
|
main 0
|
||||||
|
|
||||||
|
# Large frame marked nosplit is always wrong.
|
||||||
|
main 10000 nosplit
|
||||||
|
REJECT
|
||||||
|
|
||||||
|
# Calling a large frame is okay.
|
||||||
|
main 0 call big
|
||||||
|
big 10000
|
||||||
|
|
||||||
|
# But not if the frame is nosplit.
|
||||||
|
main 0 call big
|
||||||
|
big 10000 nosplit
|
||||||
|
REJECT
|
||||||
|
|
||||||
|
# Recursion is okay.
|
||||||
|
main 0 call main
|
||||||
|
|
||||||
|
# Recursive nosplit runs out of space.
|
||||||
|
main 0 nosplit call main
|
||||||
|
REJECT
|
||||||
|
|
||||||
|
# Chains of ordinary functions okay.
|
||||||
|
main 0 call f1
|
||||||
|
f1 80 call f2
|
||||||
|
f2 80
|
||||||
|
|
||||||
|
# Chains of nosplit must fit in the stack limit, 128 bytes.
|
||||||
|
main 0 call f1
|
||||||
|
f1 80 nosplit call f2
|
||||||
|
f2 80 nosplit
|
||||||
|
REJECT
|
||||||
|
|
||||||
|
# Larger chains.
|
||||||
|
main 0 call f1
|
||||||
|
f1 16 call f2
|
||||||
|
f2 16 call f3
|
||||||
|
f3 16 call f4
|
||||||
|
f4 16 call f5
|
||||||
|
f5 16 call f6
|
||||||
|
f6 16 call f7
|
||||||
|
f7 16 call f8
|
||||||
|
f8 16 call end
|
||||||
|
end 1000
|
||||||
|
|
||||||
|
main 0 call f1
|
||||||
|
f1 16 nosplit call f2
|
||||||
|
f2 16 nosplit call f3
|
||||||
|
f3 16 nosplit call f4
|
||||||
|
f4 16 nosplit call f5
|
||||||
|
f5 16 nosplit call f6
|
||||||
|
f6 16 nosplit call f7
|
||||||
|
f7 16 nosplit call f8
|
||||||
|
f8 16 nosplit call end
|
||||||
|
end 1000
|
||||||
|
REJECT
|
||||||
|
|
||||||
|
# Test cases near the 128-byte limit.
|
||||||
|
|
||||||
|
# Ordinary stack split frame is always okay.
|
||||||
|
main 112
|
||||||
|
main 116
|
||||||
|
main 120
|
||||||
|
main 124
|
||||||
|
main 128
|
||||||
|
main 132
|
||||||
|
main 136
|
||||||
|
|
||||||
|
# A nosplit leaf can use the whole 128-CallSize bytes available on entry.
|
||||||
|
main 112 nosplit
|
||||||
|
main 116 nosplit
|
||||||
|
main 120 nosplit
|
||||||
|
main 124 nosplit
|
||||||
|
main 128 nosplit; REJECT
|
||||||
|
main 132 nosplit; REJECT
|
||||||
|
main 136 nosplit; REJECT
|
||||||
|
|
||||||
|
# Calling a nosplit function from a nosplit function requires
|
||||||
|
# having room for the saved caller PC and the called frame.
|
||||||
|
# Because ARM doesn't save LR in the leaf, it gets an extra 4 bytes.
|
||||||
|
main 112 nosplit call f; f 0 nosplit
|
||||||
|
main 116 nosplit call f; f 0 nosplit; REJECT amd64
|
||||||
|
main 120 nosplit call f; f 0 nosplit; REJECT amd64
|
||||||
|
main 124 nosplit call f; f 0 nosplit; REJECT amd64 386
|
||||||
|
main 128 nosplit call f; f 0 nosplit; REJECT
|
||||||
|
main 132 nosplit call f; f 0 nosplit; REJECT
|
||||||
|
main 136 nosplit call f; f 0 nosplit; REJECT
|
||||||
|
|
||||||
|
# Calling a splitting function from a nosplit function requires
|
||||||
|
# having room for the saved caller PC of the call but also the
|
||||||
|
# saved caller PC for the call to morestack. Again the ARM works
|
||||||
|
# in less space.
|
||||||
|
main 104 nosplit call f; f 0 call f
|
||||||
|
main 108 nosplit call f; f 0 call f
|
||||||
|
main 112 nosplit call f; f 0 call f; REJECT amd64
|
||||||
|
main 116 nosplit call f; f 0 call f; REJECT amd64
|
||||||
|
main 120 nosplit call f; f 0 call f; REJECT amd64 386
|
||||||
|
main 124 nosplit call f; f 0 call f; REJECT amd64 386
|
||||||
|
main 128 nosplit call f; f 0 call f; REJECT
|
||||||
|
main 132 nosplit call f; f 0 call f; REJECT
|
||||||
|
main 136 nosplit call f; f 0 call f; REJECT
|
||||||
|
|
||||||
|
# Indirect calls are assumed to be splitting functions.
|
||||||
|
main 104 nosplit callind
|
||||||
|
main 108 nosplit callind
|
||||||
|
main 112 nosplit callind; REJECT amd64
|
||||||
|
main 116 nosplit callind; REJECT amd64
|
||||||
|
main 120 nosplit callind; REJECT amd64 386
|
||||||
|
main 124 nosplit callind; REJECT amd64 386
|
||||||
|
main 128 nosplit callind; REJECT
|
||||||
|
main 132 nosplit callind; REJECT
|
||||||
|
main 136 nosplit callind; REJECT
|
||||||
|
|
||||||
|
# Issue 7623
|
||||||
|
main 0 call f; f 112
|
||||||
|
main 0 call f; f 116
|
||||||
|
main 0 call f; f 120
|
||||||
|
main 0 call f; f 124
|
||||||
|
main 0 call f; f 128
|
||||||
|
main 0 call f; f 132
|
||||||
|
main 0 call f; f 136
|
||||||
|
`
|
||||||
|
|
||||||
|
var (
|
||||||
|
commentRE = regexp.MustCompile(`(?m)^#.*`)
|
||||||
|
rejectRE = regexp.MustCompile(`(?s)\A(.+?)((\n|; *)REJECT(.*))?\z`)
|
||||||
|
lineRE = regexp.MustCompile(`(\w+) (\d+)( nosplit)?(.*)`)
|
||||||
|
callRE = regexp.MustCompile(`\bcall (\w+)\b`)
|
||||||
|
callindRE = regexp.MustCompile(`\bcallind\b`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
goarch := os.Getenv("GOARCH")
|
||||||
|
if goarch == "" {
|
||||||
|
goarch = runtime.GOARCH
|
||||||
|
}
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "go-test-nosplit")
|
||||||
|
if err != nil {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("creating temp dir: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
ioutil.WriteFile(filepath.Join(dir, "main.go"), []byte("package main\nfunc main()\n"), 0666)
|
||||||
|
|
||||||
|
tests = strings.Replace(tests, "\t", " ", -1)
|
||||||
|
tests = commentRE.ReplaceAllString(tests, "")
|
||||||
|
|
||||||
|
nok := 0
|
||||||
|
nfail := 0
|
||||||
|
TestCases:
|
||||||
|
for len(tests) > 0 {
|
||||||
|
var stanza string
|
||||||
|
i := strings.Index(tests, "\nmain ")
|
||||||
|
if i < 0 {
|
||||||
|
stanza, tests = tests, ""
|
||||||
|
} else {
|
||||||
|
stanza, tests = tests[:i], tests[i+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
m := rejectRE.FindStringSubmatch(stanza)
|
||||||
|
if m == nil {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("invalid stanza:\n\t%s\n", indent(stanza))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lines := strings.TrimSpace(m[1])
|
||||||
|
reject := false
|
||||||
|
if m[2] != "" {
|
||||||
|
if strings.TrimSpace(m[4]) == "" {
|
||||||
|
reject = true
|
||||||
|
} else {
|
||||||
|
for _, rej := range strings.Fields(m[4]) {
|
||||||
|
if rej == goarch {
|
||||||
|
reject = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lines == "" && !reject {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if goarch == "arm" {
|
||||||
|
fmt.Fprintf(&buf, "#define CALL BL\n#define REGISTER (R0)\n")
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(&buf, "#define REGISTER AX\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(lines, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, subline := range strings.Split(line, ";") {
|
||||||
|
subline = strings.TrimSpace(subline)
|
||||||
|
if subline == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
m := lineRE.FindStringSubmatch(subline)
|
||||||
|
if m == nil {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("invalid function line: %s\n", subline)
|
||||||
|
continue TestCases
|
||||||
|
}
|
||||||
|
name := m[1]
|
||||||
|
size, _ := strconv.Atoi(m[2])
|
||||||
|
if goarch == "amd64" && size%8 == 4 {
|
||||||
|
continue TestCases
|
||||||
|
}
|
||||||
|
nosplit := m[3]
|
||||||
|
body := m[4]
|
||||||
|
|
||||||
|
if nosplit != "" {
|
||||||
|
nosplit = ",7"
|
||||||
|
} else {
|
||||||
|
nosplit = ",0"
|
||||||
|
}
|
||||||
|
body = callRE.ReplaceAllString(body, "CALL ·$1(SB);")
|
||||||
|
body = callindRE.ReplaceAllString(body, "CALL REGISTER;")
|
||||||
|
|
||||||
|
fmt.Fprintf(&buf, "TEXT ·%s(SB)%s,$%d-0\n\t%s\n\tRET\n\n", name, nosplit, size, body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ioutil.WriteFile(filepath.Join(dir, "asm.s"), buf.Bytes(), 0666)
|
||||||
|
|
||||||
|
|
||||||
|
cmd := exec.Command("go", "build")
|
||||||
|
cmd.Dir = dir
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
nok++
|
||||||
|
if reject {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("accepted incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nfail++
|
||||||
|
if !reject {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("rejected incorrectly:\n\t%s\n", indent(strings.TrimSpace(stanza)))
|
||||||
|
fmt.Printf("\n\tlinker output:\n\t%s\n", indent(string(output)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bugged && (nok == 0 || nfail == 0) {
|
||||||
|
bug()
|
||||||
|
fmt.Printf("not enough test cases run\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func indent(s string) string {
|
||||||
|
return strings.Replace(s, "\n", "\n\t", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bugged = false
|
||||||
|
|
||||||
|
func bug() {
|
||||||
|
if !bugged {
|
||||||
|
bugged = true
|
||||||
|
fmt.Printf("BUG\n")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user