Bug in gcc and/or binutils (fwd)

Peter S. Mazinger ps.m at gmx.net
Fri May 12 22:07:40 UTC 2006


Hello!

I'll forward this to the list as well, maybe those having the problem w/ 
hidden internal optarg/optind can as well contribute, find out why this 
happens

Reedited for uclibc-ml taste (removed "gentoo dependency")

Peter

---------- Forwarded message ----------
Date: Fri, 12 May 2006 23:21:13 +0200 (CEST)
From: Peter S. Mazinger <mps at lnx.bridge.intra>
To: vapier at gentoo.org
Cc: solar at gentoo.org
Subject: Bug in gcc and/or binutils

Hello!

I have gotten a bug report when I have hidden internal optind/optarg 
(either w/ attribute_hidden or using libc_hidden_*) some time ago. I have 
driven today about 6 hours being alone and had time to rethink the cases 
when it failed/worked.
I couldn't reproduce the bug because I used exclusively PIE executables.
Qemu is the only app on my box being non-pie.
The only solution to get qemu running on a hardened box is to build it 
completely ET_EXEC (almost all of the files have to be compiled w/o -fPIE) 
and then the failure came up w/ dyngen.c.

The attached files can reproduce this, do:
1. gcc -fPIC -shared -o libgetopt.so getopt.c (getopt.c is an adapted mjn3 
getopt-susv3.c to use hidden and weak_alias)
2. gcc -pie -fPIE -o hidden hidden.c -L. -lgetopt
3. gcc -o hidden hidden.c -L. -lgetopt (assuming that the toolchain does 
not produce -pie per default)
4. gcc -fPIE -o hidden.o -c hidden.c; gcc -o hidden hidden.c

run ./hidden -o out.o any_existant_file (for ex. hidden.c)
my failure: getopt_hidden: could not open '(null)'

only the non-pie (ET_EXEC) hidden executable fails if built from an object 
not compiled w/ -fPIE|-fPIC

If I hide only optind, then it fails some other way (it seems it can't 
find the argument after -o)

I have no idea how to minimize this test case, but it affects probably 
most of global data hidings in uClibc (only constants are currently 
hidden in svn)

It does not matter if I use strong_alias or weak_alias for optind/optarg

4. tells me that it is rather a gcc issue, not linker

Peter

-- 
Peter S. Mazinger <ps dot m at gmx dot net>           ID: 0xA5F059F2
Key fingerprint = 92A4 31E1 56BC 3D5A 2D08  BB6E C389 975E A5F0 59F2
-------------- next part --------------
#include <stdio.h>

#define attribute_hidden __attribute__((visibility ("hidden")))

#define weak_alias(name, aliasname) _weak_alias (name, aliasname)
#define _weak_alias(name, aliasname) \
  extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));

#define strong_alias(name, aliasname) _strong_alias(name, aliasname)
#define _strong_alias(name, aliasname) \
  extern __typeof (name) aliasname __attribute__ ((alias (#name)));

static const char missing[] = "%s: option requires an argument -- %c\n";
static const char illegal[] = "%s: illegal option -- %c\n";

int __opterr attribute_hidden = 1;
weak_alias(__opterr,opterr)
int __optind attribute_hidden = 1;
weak_alias(__optind,optind_test)
int __optopt attribute_hidden = 0;
weak_alias(__optind,optind)
char *__optarg attribute_hidden = NULL;
weak_alias(__optarg,optarg_test)

int getopt_test(int argc, char * const argv[], const char *optstring)
{
	static const char *o;		/* multi opt position */
	register const char *p;
	register const char *s;
	int retval = -1;

	__optopt = 0;
	__optarg = NULL;

	if (!o) {				/* Not in a multi-option arg. */
		if ((__optind >= argc)	/* No more args? */
			|| ((p = argv[__optind]) == NULL) /* Missing? */
			|| (*p != '-')		/* Not an option? */
			|| (!*++p)			/* "-" case? */
			) {
			goto DONE;
		}
		if ((*p == '-') && (p[1] == 0)) { /* "--" case. */
/* 			++optind; */
/* 			goto DONE; */
			goto NEXTOPT;		/* Less code generated... */
		}
		o = p;
	}

	retval = (unsigned char) *o; /* Avoid problems for char val of -1. */

	if ((*o == ':') || !(s = strchr(optstring, *o))) { /* Illegal option? */
		s = illegal;
		retval = '?';
		goto BAD;
	}
	
	if (s[1] == ':') {			/* Option takes an arg? */
		if (o[1]) {					/* No space between option and arg? */
			__optarg = (char *)(o + 1);
			goto NEXTOPT;
		}

		if (__optind + 1 < argc) {	/* Space between option and arg? */
			__optarg = argv[++__optind];
		} else {				/* Out of args! */
			s = missing;
			retval = ':';
		BAD:
			__optopt = *o;
			if (*optstring != ':') {
				retval = '?';
				if (__opterr) {
					fprintf(stderr, s, argv[0], *o);
				}
			}
		}
	}

	if (!*++o) {
	NEXTOPT:
		o = NULL;
		++__optind;
	}
 DONE:
	return retval;
}
-------------- next part --------------
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

extern char *optarg_test;
extern int optind_test;
extern int getopt_test (int ___argc, char *const *___argv, const char *__shortopts);

enum {
    OUT_GEN_OP,
    OUT_CODE,
    OUT_INDEX_OP,
};

/* load an elf object file */
static int load_object(const char *filename)
{
    return 0;
}

static int gen_file(FILE *outfile, int out_type)
{
    return 0;
}

static void usage(void)
{
    printf("usage: getopt_hidden [-o outfile] [-c] objfile\n"
           "Generate a dynamic code generator from an object file\n"
           "-c     output enum of operations\n"
           "-g     output gen_op_xx() functions\n"
           );
    exit(1);
}

static void __attribute__((noreturn)) __attribute__((format (printf, 1, 2))) error(const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    fprintf(stderr, "getopt_hidden: ");
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, "\n");
    va_end(ap);
    exit(1);
}

int main(int argc, char **argv)
{
    int c, out_type;
    const char *filename, *outfilename;
    FILE *outfile;

    outfilename = "out.c";
    out_type = OUT_CODE;
    for(;;) {
        c = getopt_test(argc, argv, "ho:cg");
        if (c == -1)
            break;
        switch(c) {
        case 'h':
            usage();
            break;
        case 'o':
            outfilename = optarg_test;
            break;
        case 'c':
            out_type = OUT_INDEX_OP;
            break;
        case 'g':
            out_type = OUT_GEN_OP;
            break;
        }
    }
    if (optind_test >= argc)
        usage();
    filename = argv[optind_test];
    outfile = fopen(outfilename, "w");
    if (!outfile)
        error("could not open '%s'", outfilename);

    load_object(filename);
    gen_file(outfile, out_type);
    fclose(outfile);
    return 0;
}


More information about the uClibc mailing list