Skip to content

Commit

Permalink
Fix generation of function addresses in #emit code
Browse files Browse the repository at this point in the history
This fixes a bug where the compiler generated incorrect addresses for
functions in #emit code if they were defined after the point of use.

For example, you couldn't reliably get the address of a function with
"#emit CONST.pri XXX" unless you make sure that XXX is defined before
the function in which you have you are referencing it.

Now the resolution of the function's address is deferred until assembling
phase (as with CALL), and the assembler is guaranteed to see all symbols
with their final addresses. To distinguish between functions and numeric
operands I added a '.' (period) in front of function names for both #emit
and normal calls.

See also 5) here: http://forum.sa-mp.com/showthread.php?t=355877

--------- test code --------

#include <a_samp> // DO NOT REMOVE THIS

main() {
	#emit const.pri foo
}

stock foo() {
	printf("Hello, World!");
}

----- end of test code -----
  • Loading branch information
Zeex committed Jan 3, 2014
1 parent 0fa621f commit eba8474
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 16 deletions.
21 changes: 12 additions & 9 deletions source/compiler/sc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -1220,16 +1220,19 @@ static int command(void)
if (sym==NULL || sym->ident!=iFUNCTN && sym->ident!=iREFFUNC && (sym->usage & uDEFINE)==0) {
error(17,str); /* undefined symbol */
} else {
if (strcmp(name, "call")==0) {
assert((sym->ident & iFUNCTN)!=0 || (sym->ident & iREFFUNC)!=0);
stgwrite(sym->name);
if (sym->ident==iFUNCTN || sym->ident==iREFFUNC) {
if ((sym->usage & uNATIVE)!=0) {
/* reserve a SYSREQ id if called for the first time */
sym->addr=ntv_funcid++;
outval(sym->addr,FALSE);
} else {
/* normal function, write its name instead of the address
* so that the address will be resolved at assemble time
*/
stgwrite(".");
stgwrite(sym->name);
} /* if */
} else {
if ((sym->ident & iFUNCTN)!=0 && (sym->usage & uNATIVE)!=0) {
if (sc_status==statWRITE && (sym->usage & uREAD)==0 && sym->addr>=0) {
/* reserve a SYSREQ id if called for the first time */
sym->addr=ntv_funcid++;
} /* if */
}
outval(sym->addr,FALSE);
} /* if */
/* mark symbol as "used", unknown whether for read or write */
Expand Down
1 change: 1 addition & 0 deletions source/compiler/sc4.c
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ SC_FUNC void ffcall(symbol *sym,const char *label,int numargs)
stgwrite("l.");
stgwrite(label);
} else {
stgwrite(".");
stgwrite(sym->name);
} /* if */
if (sc_asmfile
Expand Down
34 changes: 27 additions & 7 deletions source/compiler/sc6.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,32 @@ static ucell hex2long(const char *s,char **n)

static ucell getparam(const char *s,char **n)
{
int i;
ucell result=0;
for ( ;; ) {
result+=hex2long(s,(char**)&s);
if (*s!='+')
break;
s++;
} /* for */
char name[sNAMEMAX+1];
symbol *sym;

if (*s=='.') {
/* this is a function, find it in the global symbol table */
for (i=0; !isspace(*(++s)); i++) {
assert(*s!='\0');
assert(i<sNAMEMAX);
name[i]=*s;
} /* for */
name[i]='\0';
sym=findglb(name,sGLOBAL);
assert(sym!=NULL);
assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC);
assert(sym->vclass==sGLOBAL);
result=sym->addr;
} else {
for ( ;; ) {
result+=hex2long(s,(char**)&s);
if (*s!='+')
break;
s++;
} /* for */
} /* if */
if (n!=NULL)
*n=(char*)s;
return result;
Expand Down Expand Up @@ -385,7 +404,8 @@ static cell do_call(FILE *fbin,char *params,cell opcode)
/* look up the function address; note that the correct file number must
* already have been set (in order for static globals to be found).
*/
sym=findglb(name,sGLOBAL);
assert(name[0]=='.');
sym=findglb(name+1,sGLOBAL);
assert(sym!=NULL);
assert(sym->ident==iFUNCTN || sym->ident==iREFFUNC);
assert(sym->vclass==sGLOBAL);
Expand Down

0 comments on commit eba8474

Please sign in to comment.