add_val(where,~0L,s+a-(ul)got);
break;
case R_MIPS_26:
- add_val(where,MASK(26),(s+a)>>2);
+ if (((s+a)>>28)!=(((ul)where)>>28)) {
+ gote=got+sym->st_size-1;
+ massert(!write_26_stub(s+a,got,gote));
+ store_val(where,MASK(26),((ul)gote)>>2);
+ } else
+ add_val(where,MASK(26),(s+a)>>2);
break;
case R_MIPS_32:
add_val(where,~0L,s+a);
case R_MIPS_CALL16:
gote=got+sym->st_size-1;
store_val(where,MASK(16),((void *)gote-(void *)got));
- *gote=s;
+ if (s>=ggot && s<ggote) {
+ massert(!write_stub(s,got,gote));
+ } else
+ *gote=s;
break;
case R_MIPS_HI16:
if (sym->st_other) s=gpd=(ul)got-(sym->st_other==2 ? 0 : (ul)where);
-static ul gpd; static Rel *hr;
+#include <page.h>
+
+static ul gpd,ggot,ggote,can_gp; static Rel *hr;
+
+typedef struct {
+ ul addr_hi,addr_lo,jr,nop;
+} mips_26_tramp;
+
+static int
+write_26_stub(ul s,ul *got,ul *gote) {
+
+ static mips_26_tramp t1={(0xf<<26)|(0x0<<21)|(0x19<<16), /*lui t9*/
+ (0xe<<26)|(0x19<<21)|(0x19<<16), /*ori t9,t9 */
+ 0x03200008, /*jr t9*/
+ 0x00200825}; /*mv at,at */;
+ mips_26_tramp *t=(void *)gote;
+
+ *t=t1;
+ t->addr_hi|=s>>16;
+ t->addr_lo|=s&0xffff;
+
+ return 0;
+
+}
+
+typedef struct {
+ ul entry,addr_hi,addr_lo,lw,jr,lwcan;
+} call_16_tramp;
+
+static int
+write_stub(ul s,ul *got,ul *gote) {
+
+ static call_16_tramp t1={0,
+ (0xf<<26)|(0x0<<21)|(0x19<<16), /*lui t9*/
+ (0xe<<26)|(0x19<<21)|(0x19<<16), /*ori t9,t9 */
+ (0x23<<26)|(0x19<<21)|(0x19<<16), /*lw t9,(0)t9*/
+ 0x03200008, /*jr t9*/
+ /*stub addresses need veneer setting gp to canonical*/
+ (0x23<<26)|(0x1c<<21)|(0x1c<<16)};/*lw gp,(0)gp*/
+ call_16_tramp *t=(void *)gote++;
+
+ *t=t1;
+ *got=can_gp;
+
+ t->entry=(ul)gote;
+ t->addr_hi|=s>>16;
+ t->addr_lo|=s&0xffff;
+
+ return 0;
+
+}
static int
find_special_params(void *v,Shdr *sec1,Shdr *sece,const char *sn,
const char *st1,Sym *ds1,Sym *dse,Sym *sym,Sym *syme) {
+ Shdr *sec;
+ ul *q,gotsym=0,locgotno=0,stub,stube;
+ void *p,*pe;
+
+ massert(sec=get_section(".dynamic",sec1,sece,sn));
+ for (p=(void *)sec->sh_addr,pe=p+sec->sh_size;p<pe;p+=sec->sh_entsize) {
+ q=p;
+ if (q[0]==DT_MIPS_GOTSYM)
+ gotsym=q[1];
+ if (q[0]==DT_MIPS_LOCAL_GOTNO)
+ locgotno=q[1];
+ if (q[0]==DT_PLTGOT)
+ can_gp=q[1]+0x7ff0;
+
+ }
+ massert(gotsym && locgotno && can_gp);
+
+ massert(sec=get_section(".MIPS.stubs",sec1,sece,sn));
+ stub=sec->sh_addr;
+ stube=sec->sh_addr+sec->sh_size;
+
+ massert(sec=get_section(".got",sec1,sece,sn));
+ ggot=sec->sh_addr+locgotno*sec->sh_entsize;
+ ggote=sec->sh_addr+sec->sh_size;
+
+ for (ds1+=gotsym,sym=ds1;sym<dse;sym++)
+ if (!sym->st_value || (sym->st_value>=stub && sym->st_value<stube))
+ sym->st_value=ggot+(sym-ds1)*sec->sh_entsize;
+
return 0;
}
Rel *r;
Sym *sym;
- Shdr *sec;
+ Shdr *sec,*ssec;
void *v,*ve;
ul q;
+ struct node *a;
for (q=0,sym=sym1;sym<syme;sym++) {
const char *s=st1+sym->st_name;
for (sym=sym1;sym<syme;sym++)
sym->st_size=0;
- for (*gs=0,sec=sec1;sec<sece;sec++)
- if (sec->sh_type==SHT_REL)
+ for (*gs=1,sec=sec1;sec<sece;sec++)/*can_gp in got[0]*/
+ if (sec->sh_type==SHT_REL)/*no addend*/
for (v=v1+sec->sh_offset,ve=v+sec->sh_size,r=v;v<ve;v+=sec->sh_entsize,r=v)
- if (ELF_R_TYPE(r->r_info)==R_MIPS_CALL16||
- ELF_R_TYPE(r->r_info)==R_MIPS_GOT16) {
+ if (!(sym=sym1+ELF_R_SYM(r->r_info))->st_size)
- sym=sym1+ELF_R_SYM(r->r_info);
+ switch(ELF_R_TYPE(r->r_info)) {
- if (!sym->st_size)
- sym->st_size=++*gs;
+ case R_MIPS_26:
+ if (((ul)(pagetochar(page(heap_end))+r->r_offset))>>28) {
+ sym->st_size=++*gs;
+ (*gs)+=sizeof(mips_26_tramp)/sizeof(ul)-1;
+ }
+ break;
+ case R_MIPS_CALL16:
+ sym->st_size=++*gs;
+ if (((ssec=sec1+sym->st_shndx)>=sece || !ALLOC_SEC(ssec)) &&
+ (a=find_sym_ptable(st1+sym->st_name)) &&
+ a->address>=ggot && a->address<ggote)
+ (*gs)+=sizeof(call_16_tramp)/sizeof(ul)-1;
+ break;
+ case R_MIPS_GOT16:
+ sym->st_size=++*gs;
+ break;
+ }
- }
-
return 0;
}