/* * CDDL HEADER START * * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may use this file only in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * @(#)ar.cc 1.28 06/12/12 */ #pragma ident "@(#)ar.cc 1.28 06/12/12" /* * Copyright 2017-2020 J. Schilling * * @(#)ar.cc 1.9 20/09/06 2017-2020 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)ar.cc 1.9 20/09/06 2017-2020 J. Schilling"; #endif /* * ar.c * * Deal with the lib.a(member.o) and lib.a((entry-point)) notations * * Look inside archives for notations a(b) and a((b)) * a(b) is file member b in archive a * a((b)) is entry point b in object archive a * * For 6.0, create a make which can understand all archive * formats. This is kind of tricky, and isnt any help. */ /* * Included files */ #include /* alloca() */ #ifdef HAVE_AR_H #include #endif #include #include /* retmem_mb() */ #ifdef HAVE_RANLIB_H #include #else #ifndef __sgi /* * IRIX defines struct ranlib in , so we nust not define it here. */ struct ranlib { union { off_t ran_strx; /* string table index of */ char *ran_name; /* symbol defined by */ } ran_un; off_t ran_off; /* library member at this offset */ }; #endif #endif #include /* isspace */ /* * Defined macros */ #ifndef S5EMUL #undef BITSPERBYTE #define BITSPERBYTE 8 #endif /* * Defines for all the different archive formats. See next comment * block for justification for not using s versions. */ #define AR_5_MAGIC "" /* 5.0 format magic string */ #define AR_5_MAGIC_LENGTH 4 /* 5.0 format string length */ #define AR_PORT_MAGIC "!\n" /* Port. (6.0) magic string */ #define AR_PORT_MAGIC_LENGTH 8 /* Port. (6.0) string length */ #define AR_PORT_END_MAGIC "`\n" /* Port. (6.0) end of header */ #define AR_PORT_WORD 4 /* Port. (6.0) 'word' length */ /* * typedefs & structs */ /* * These are the archive file headers for the formats. Note * that it really doesnt matter if these structures are defined * here. They are correct as of the respective archive format * releases. If the archive format is changed, then since backwards * compatability is the desired behavior, a new structure is added * to the list. */ typedef struct { /* 5.0 ar header format: vax family; 3b family */ char ar_magic[AR_5_MAGIC_LENGTH]; /* AR_5_MAGIC*/ char ar_name[16]; /* Space terminated */ char ar_date[AR_PORT_WORD]; /* sgetl() accessed */ char ar_syms[AR_PORT_WORD]; /* sgetl() accessed */ } Arh_5; typedef struct { /* 5.0 ar symbol format: vax family; 3b family */ char sym_name[8]; /* Space terminated */ char sym_ptr[AR_PORT_WORD]; /* sgetl() accessed */ } Ars_5; typedef struct { /* 5.0 ar member format: vax family; 3b family */ char arf_name[16]; /* Space terminated */ char arf_date[AR_PORT_WORD]; /* sgetl() accessed */ char arf_uid[AR_PORT_WORD]; /* sgetl() accessed */ char arf_gid[AR_PORT_WORD]; /* sgetl() accessed */ char arf_mode[AR_PORT_WORD]; /* sgetl() accessed */ char arf_size[AR_PORT_WORD]; /* sgetl() accessed */ } Arf_5; typedef struct { /* Portable (6.0) ar format: vax family; 3b family */ char ar_name[16]; /* Space terminated */ /* left-adjusted fields; decimal ascii; blank filled */ char ar_date[12]; char ar_uid[6]; char ar_gid[6]; char ar_mode[8]; /* octal ascii */ char ar_size[10]; /* special end-of-header string (AR_PORT_END_MAGIC) */ char ar_fmag[2]; } Ar_port; enum ar_type { AR_5, AR_PORT }; typedef unsigned int ar_port_word; // must be 4-bytes long typedef struct { FILE *fd; /* to distiguish ar format */ enum ar_type type; /* where first ar member header is at */ long first_ar_mem; /* where the symbol lookup starts */ long sym_begin; /* the number of symbols available */ long num_symbols; /* length of symbol directory file */ long sym_size; Arh_5 arh_5; Ars_5 ars_5; Arf_5 arf_5; Ar_port ar_port; } Ar; /* * Static variables */ /* * File table of contents */ extern timestruc_t& read_archive(register Name target); static Boolean open_archive(char *filename, register Ar *arp); static void close_archive(register Ar *arp); static Boolean read_archive_dir(register Ar *arp, Name library, char **long_names_table); static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table); static long sgetl(char *); /* * read_archive(target) * * Read the contents of an ar file. * * Return value: * The time the member was created * * Parameters: * target The member to find time for * * Global variables used: * empty_name The Name "" */ int read_member_header (Ar_port *header, FILE *fd, char* filename); int process_long_names_member (register Ar *arp, char **long_names_table, char *filename); timestruc_t& read_archive(register Name target) { register Property member; wchar_t *slash; String_rec true_member_name; wchar_t buffer[STRING_BUFFER_LENGTH]; register Name true_member = NULL; Ar ar; char *long_names_table = NULL; /* Table of long member names */ member = get_prop(target->prop, member_prop); /* * Check if the member has directory component. * If so, remove the dir and see if we know the date. */ if (member->body.member.member != NULL) { Wstring member_string(member->body.member.member); wchar_t * wcb = member_string.get_string(); if((slash = (wchar_t *) wcsrchr(wcb, (int) slash_char)) != NULL) { INIT_STRING_FROM_STACK(true_member_name, buffer); append_string(member->body.member.library->string_mb, &true_member_name, FIND_LENGTH); append_char((int) parenleft_char, &true_member_name); append_string(slash + 1, &true_member_name, FIND_LENGTH); append_char((int) parenright_char, &true_member_name); true_member = GETNAME(true_member_name.buffer.start, FIND_LENGTH); if (true_member->stat.time != file_no_time) { target->stat.time = true_member->stat.time; return target->stat.time; } } } if (open_archive(member->body.member.library->string_mb, &ar) == failed) { if (errno == ENOENT) { target->stat.stat_errno = ENOENT; close_archive(&ar); if (member->body.member.member == NULL) { member->body.member.member = empty_name; } return target->stat.time = file_doesnt_exist; } else { fatal(gettext("Can't access archive `%s': %s"), member->body.member.library->string_mb, errmsg(errno)); } } if (target->stat.time == file_no_time) { if (read_archive_dir(&ar, member->body.member.library, &long_names_table) == failed){ fatal(gettext("Can't access archive `%s': %s"), member->body.member.library->string_mb, errmsg(errno)); } } if (member->body.member.entry != NULL) { translate_entry(&ar, target, member,&long_names_table); } close_archive(&ar); if (long_names_table) { retmem_mb(long_names_table); } if (true_member != NULL) { target->stat.time = true_member->stat.time; } if (target->stat.time == file_no_time) { target->stat.time = file_doesnt_exist; } return target->stat.time; } /* * open_archive(filename, arp) * * Return value: * Indicates if open failed or not * * Parameters: * filename The name of the archive we need to read * arp Pointer to ar file description block * * Global variables used: */ static Boolean open_archive(char *filename, register Ar *arp) { int fd; char mag_5[AR_5_MAGIC_LENGTH]; char mag_port[AR_PORT_MAGIC_LENGTH]; char buffer[4]; arp->fd = NULL; fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT); if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) { return failed; } (void) fcntl(fileno(arp->fd), F_SETFD, 1); #if !defined(SUN5_0) && !defined(linux) //XXX /* Read enough of the archive to distinguish between the formats */ if (fread(mag_5, AR_5_MAGIC_LENGTH, 1, arp->fd) != 1) { return failed; } if (IS_EQUALN(mag_5, AR_5_MAGIC, AR_5_MAGIC_LENGTH)) { arp->type = AR_5; /* Must read in header to set necessary info */ if (fseek(arp->fd, 0L, 0) != 0 || fread((char *) &arp->arh_5, sizeof (Arh_5), 1, arp->fd) != 1) { return failed; } arp->sym_begin = ftell(arp->fd); arp->num_symbols = sgetl(arp->arh_5.ar_syms); arp->first_ar_mem = arp->sym_begin + sizeof (Ars_5) * arp->num_symbols; arp->sym_size = 0L; return succeeded; } if (fseek(arp->fd, 0L, 0) != 0) { return failed; } #endif if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) { return failed; } if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) { arp->type = AR_PORT; /* * Read in first member header to find out if there is * a symbol definition table. */ int ret = read_member_header(&arp->ar_port, arp->fd, filename); if (ret == failed) { return failed; } else if(ret == -1) { /* There is no member header - empty archive */ arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; arp->first_ar_mem = ftell(arp->fd); return succeeded; } /* * The following values are the default if there is * no symbol directory and long member names. */ arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port); /* * Do we have a symbol table? A symbol table is always * the first member in an archive. In 4.1.x it has the * name __.SYMDEF, in SVr4, it has the name "/ " */ /* #ifdef SUN5_0 MBSTOWCS(wcs_buffer, NOCATGETS("/ ")); if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { #else MBSTOWCS(wcs_buffer, NOCATGETS("__.SYMDEF ")); if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { #endif */ #if defined(SUN5_0) || defined(HP_UX) || defined(linux) if (IS_EQUALN(arp->ar_port.ar_name, NOCATGETS("/ "), 16)) { #else if (IS_EQUALN(arp->ar_port.ar_name, NOCATGETS("__.SYMDEF "), 16)) { #endif if (sscanf(arp->ar_port.ar_size, "%ld", &arp->sym_size) != 1) { return failed; } arp->sym_size += (arp->sym_size & 1); /* round up */ if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) { return failed; } arp->num_symbols = sgetl(buffer); arp->sym_begin = ftell(arp->fd); arp->first_ar_mem = arp->sym_begin + arp->sym_size - sizeof buffer; } return succeeded; } fatal(gettext("`%s' is not an archive"), filename); /* NOTREACHED */ return failed; } /* * close_archive(arp) * * Parameters: * arp Pointer to ar file description block * * Global variables used: */ static void close_archive(register Ar *arp) { if (arp->fd != NULL) { (void) fclose(arp->fd); } } /* * read_archive_dir(arp, library, long_names_table) * * Reads the directory of an archive and enters all * the members into the make symboltable in lib(member) format * with their dates. * * Parameters: * arp Pointer to ar file description block * library Name of lib to enter members for. * Used to form "lib(member)" string. * long_names_table table that contains list of members * with names > 15 characters long * * Global variables used: */ static Boolean #if defined(SUN5_0) || defined(linux) //XXX read_archive_dir(register Ar *arp, Name library, char **long_names_table) #else read_archive_dir(register Ar *arp, Name library, char **) #endif { wchar_t *name_string; wchar_t *member_string; register long len; register wchar_t *p; register char *q; register Name name; Property member; long ptr; long date; #if defined(SUN5_0) || defined(linux) //XXX long offset; /* * If any of the members has a name > 15 chars, * it will be found here. */ if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) { return failed; } #endif name_string = ALLOC_WC((int) (library->hash.length + (int) ar_member_name_len * 2)); (void) mbstowcs(name_string, library->string_mb, (int) library->hash.length); member_string = name_string + library->hash.length; *member_string++ = (int) parenleft_char; if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { goto read_error; } /* Read the directory using the appropriate format */ switch (arp->type) { case AR_5: for (;;) { if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd) != 1) { if (feof(arp->fd)) { return succeeded; } break; } len = sizeof arp->arf_5.arf_name; for (p = member_string, q = arp->arf_5.arf_name; (len > 0) && (*q != (int) nul_char) && !isspace(*q); ) { MBTOWC(p, q); p++; q++; } *p++ = (int) parenright_char; *p = (int) nul_char; name = GETNAME(name_string, FIND_LENGTH); /* * [tolik] Fix for dmake bug 1234018. * If name->stat.time is already set, then it should not * be changed. (D)make propogates time stamp for one * member, and when it calls exists() for another member, * the first one may be changed. */ if(name->stat.time == file_no_time) { name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); name->stat.time.tv_nsec = LONG_MAX; } name->is_member = library->is_member; member = maybe_append_prop(name, member_prop); member->body.member.library = library; *--p = (int) nul_char; if (member->body.member.member == NULL) { member->body.member.member = GETNAME(member_string, FIND_LENGTH); } ptr = sgetl(arp->arf_5.arf_size); ptr += (ptr & 1); if (fseek(arp->fd, ptr, 1) != 0) { goto read_error; } } break; case AR_PORT: for (;;) { if ((fread((char *) &arp->ar_port, sizeof arp->ar_port, 1, arp->fd) != 1) || !IS_EQUALN(arp->ar_port.ar_fmag, AR_PORT_END_MAGIC, sizeof arp->ar_port.ar_fmag)) { if (feof(arp->fd)) { return succeeded; } fatal( gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), library->string_mb, ftell(arp->fd) ); } #if defined(SUN5_0) || defined(linux) //XXX /* If it's a long name, retrieve it from long name table */ if (arp->ar_port.ar_name[0] == '/') { /* * "len" is used for hashing the string. * We're using "ar_member_name_len" instead of * the actual name length since it's the longest * string the "ar" command can handle at this * point. */ len = ar_member_name_len; sscanf(arp->ar_port.ar_name + 1, "%ld", &offset); q = *long_names_table + offset; } else { q = arp->ar_port.ar_name; len = sizeof arp->ar_port.ar_name; } #else q = arp->ar_port.ar_name; len = sizeof arp->ar_port.ar_name; #endif for (p = member_string; (len > 0) && (*q != (int) nul_char) && !isspace(*q) && (*q != (int) slash_char); ) { MBTOWC(p, q); p++; q++; } *p++ = (int) parenright_char; *p = (int) nul_char; name = GETNAME(name_string, FIND_LENGTH); name->is_member = library->is_member; member = maybe_append_prop(name, member_prop); member->body.member.library = library; *--p = (int) nul_char; if (member->body.member.member == NULL) { member->body.member.member = GETNAME(member_string, FIND_LENGTH); } if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) { WCSTOMBS(mbs_buffer, name_string); fatal(gettext("Bad date field for member `%s' in archive `%s'"), mbs_buffer, library->string_mb); } /* * [tolik] Fix for dmake bug 1234018. */ if(name->stat.time == file_no_time) { name->stat.time.tv_sec = date; name->stat.time.tv_nsec = LONG_MAX; } if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) { WCSTOMBS(mbs_buffer, name_string); fatal(gettext("Bad size field for member `%s' in archive `%s'"), mbs_buffer, library->string_mb); } ptr += (ptr & 1); if (fseek(arp->fd, ptr, 1) != 0) { goto read_error; } } break; } /* Only here if fread() [or IS_EQUALN()] failed and not at EOF */ read_error: fatal(gettext("Read error in archive `%s': %s"), library->string_mb, errmsg(errno)); /* NOTREACHED */ return (failed); /* fake return if compiler doesn't grok NOTREACHED */ } /* * process_long_names_member(arp) * * If the archive contains members with names longer * than 15 characters, then it has a special member * with the name "// " that contains a table * of null-terminated long names. This member * is always the first member, after the symbol table * if it exists. * * Parameters: * arp Pointer to ar file description block * * Global variables used: */ int process_long_names_member(register Ar *arp, char **long_names_table, char *filename) { Ar_port *ar_member_header; long table_size; if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { return failed; } if ((ar_member_header = (Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){ perror(gettext("memory allocation failure")); return failed; } int ret = read_member_header(ar_member_header, arp->fd, filename); if (ret == failed) { return failed; } else if(ret == -1) { /* There is no member header - empty archive */ return succeeded; } /* Do we have special member containing long names? */ if (IS_EQUALN(ar_member_header->ar_name, NOCATGETS("// "), 16)){ if (sscanf(ar_member_header->ar_size, "%ld", &table_size) != 1) { return failed; } *long_names_table = (char *) malloc(table_size); /* Read the list of long member names into the table */ if (fread(*long_names_table, table_size, 1, arp->fd) != 1) { return failed; } arp->first_ar_mem = ftell(arp->fd); } return succeeded; } /* * translate_entry(arp, target, member) * * Finds the member for one lib.a((entry)) * * Parameters: * arp Pointer to ar file description block * target Target to find member name for * member Property to fill in with info * * Global variables used: */ static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table) { register int len; register int i; wchar_t *member_string; ar_port_word *offs; int strtablen; char *syms; /* string table */ char *csym; /* string table */ ar_port_word *offend; /* end of offsets table */ long date; register wchar_t *ap; register char *hp; int maxs; long offset; char buffer[4]; if (arp->sym_begin == 0L || arp->num_symbols == 0L) { fatal(gettext("Cannot find symbol `%s' in archive `%s'"), member->body.member.entry->string_mb, member->body.member.library->string_mb); } if (fseek(arp->fd, arp->sym_begin, 0) != 0) { goto read_error; } member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2)); switch (arp->type) { case AR_5: if ((len = member->body.member.entry->hash.length) > 8) { len = 8; } for (i = 0; i < arp->num_symbols; i++) { if (fread((char *) &arp->ars_5, sizeof arp->ars_5, 1, arp->fd) != 1) { goto read_error; } if (IS_EQUALN(arp->ars_5.sym_name, member->body.member.entry->string_mb, len)) { if ((fseek(arp->fd, sgetl(arp->ars_5.sym_ptr), 0) != 0) || (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd) != 1)) { goto read_error; } MBSTOWCS(wcs_buffer, arp->arf_5.arf_name); (void) wcsncpy(member_string, wcs_buffer, wcslen(wcs_buffer)); member_string[sizeof(arp->arf_5.arf_name)] = (int) nul_char; member->body.member.member = GETNAME(member_string, FIND_LENGTH); target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); target->stat.time.tv_nsec = LONG_MAX; return; } } break; case AR_PORT: offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD)); if (fread((char *) offs, AR_PORT_WORD, (int) arp->num_symbols, arp->fd) != arp->num_symbols) { goto read_error; } for(i=0;inum_symbols;i++) { *((int*)buffer)=offs[i]; offs[i]=(ar_port_word)sgetl(buffer); } strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD); syms = (char *) alloca(strtablen); if (fread(syms, sizeof (char), strtablen, arp->fd) != strtablen) { goto read_error; } offend = &offs[arp->num_symbols]; while (offs < offend) { maxs = strlen(member->body.member.entry->string_mb); if(strlen(syms) > maxs) maxs = strlen(syms); if (IS_EQUALN(syms, member->body.member.entry->string_mb, maxs)) { if (fseek(arp->fd, (long) *offs, 0) != 0) { goto read_error; } if ((fread((char *) &arp->ar_port, sizeof arp->ar_port, 1, arp->fd) != 1) || !IS_EQUALN(arp->ar_port.ar_fmag, AR_PORT_END_MAGIC, sizeof arp->ar_port.ar_fmag)) { goto read_error; } if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) { fatal(gettext("Bad date field for member `%s' in archive `%s'"), arp->ar_port.ar_name, target->string_mb); } #if defined(SUN5_0) || defined(linux) //XXX /* If it's a long name, retrieve it from long name table */ if (arp->ar_port.ar_name[0] == '/') { sscanf(arp->ar_port.ar_name + 1, "%ld", &offset); len = ar_member_name_len; hp = *long_names_table + offset; } else { len = sizeof arp->ar_port.ar_name; hp = arp->ar_port.ar_name; } #else hp = arp->ar_port.ar_name; #endif ap = member_string; while (*hp && (*hp != (int) slash_char) && (ap < &member_string[len])) { MBTOWC(ap, hp); ap++; hp++; } *ap = (int) nul_char; member->body.member.member = GETNAME(member_string, FIND_LENGTH); target->stat.time.tv_sec = date; target->stat.time.tv_nsec = LONG_MAX; return; } offs++; while(*syms!='\0') syms++; syms++; } } fatal(gettext("Cannot find symbol `%s' in archive `%s'"), member->body.member.entry->string_mb, member->body.member.library->string_mb); /*NOTREACHED*/ read_error: if (ferror(arp->fd)) { fatal(gettext("Read error in archive `%s': %s"), member->body.member.library->string_mb, errmsg(errno)); } else { fatal(gettext("Read error in archive `%s': Premature EOF"), member->body.member.library->string_mb); } } /* * sgetl(buffer) * * The intent here is to provide a means to make the value of * bytes in an io-buffer correspond to the value of a long * in the memory while doing the io a long at a time. * Files written and read in this way are machine-independent. * * Return value: * Long int read from buffer * Parameters: * buffer buffer we need to read long int from * * Global variables used: */ static long sgetl(register char *buffer) { register long w = 0; register int i = BITSPERBYTE * AR_PORT_WORD; while ((i -= BITSPERBYTE) >= 0) { w |= (long) ((unsigned char) *buffer++) << i; } return w; } /* * read_member_header(header, fd, filename) * * reads the member header for the 4.1.x and SVr4 archives. * * Return value: * fails if read error or member * header is not the right format * Parameters: * header There's one before each archive member * fd file descriptor for the archive file. * * Global variables used: */ int read_member_header(Ar_port *header, FILE *fd, char* filename) { int num = fread((char *) header, sizeof (Ar_port), 1, fd); if (num != 1 && feof(fd)) { /* There is no member header - empty archive */ return -1; } if ((num != 1) || !IS_EQUALN( AR_PORT_END_MAGIC, header->ar_fmag, sizeof (header->ar_fmag) ) ) { fatal( gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), filename, ftell(fd) ); } return succeeded; }