MFHead @ r275232

This commit is contained in:
Enji Cooper 2014-11-29 05:28:40 +00:00
commit 840e70929b
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/building-blocks/; revision=275233
758 changed files with 29000 additions and 3829 deletions

View File

@ -48,7 +48,6 @@ cd(4) ken Pre-commit review requested.
pass(4) ken Pre-commit review requested.
ch(4) ken Pre-commit review requested.
em(4) jfv Pre-commit review requested.
tdfx(4) cokane Just keep me informed of changes, try not to break it.
sendmail gshapiro Pre-commit review requested.
etc/mail gshapiro Pre-commit review requested.
Keep in sync with -STABLE.
@ -118,7 +117,6 @@ lib/libc/stdtime edwin Heads-up appreciated, since parts of this code
is maintained by a third party source.
sbin/routed bms Pre-commit review; notify vendor at rhyolite.com
isci(4) jimharris Pre-commit review requested.
3dfx cokane Pre-commit review preferred.
cmx daniel@roe.ch Pre-commit review preferred.
filemon obrien Pre-commit review preferred.
sysdoc trhodes Pre-commit review preferred.

View File

@ -38,6 +38,12 @@
# xargs -n1 | sort | uniq -d;
# done
# 20141126: convert sbin/mdconfig/tests to ATF format tests
OLD_FILES+=usr/tests/sbin/mdconfig/legacy_test
OLD_FILES+=usr/tests/sbin/mdconfig/mdconfig.test
OLD_FILES+=usr/tests/sbin/mdconfig/run.pl
# 20141126: remove xform_ipip decapsulation fallback
OLD_FILES+=usr/include/netipsec/ipip_var.h
# 20141109: faith/faithd removal
OLD_FILES+=etc/rc.d/faith
OLD_FILES+=usr/share/man/man4/faith.4.gz
@ -134,10 +140,12 @@ OLD_FILES+=usr/include/readline/chardefs.h
OLD_FILES+=usr/include/readline/history.h
OLD_FILES+=usr/include/readline/keymaps.h
OLD_FILES+=usr/include/readline/readline.h
OLD_FILES+=usr/include/readline/tilde.h
OLD_FILES+=usr/include/readline/rlconf.h
OLD_FILES+=usr/include/readline/rlstdc.h
OLD_FILES+=usr/include/readline/rltypedefs.h
OLD_FILES+=usr/include/readline/rltypedefs.h
OLD_DIRS+=usr/include/readline
OLD_FILES+=usr/share/info/readline.info.gz
OLD_FILES+=usr/share/man/man3/readline.3.gz
# 20140625: csup removal

View File

@ -40,8 +40,7 @@ MLINKS= csh.1 tcsh.1
# utilities of the same name are handled with the associated manpage,
# builtin.1 in share/man/man1/.
DPADD= ${LIBTERMCAPW} ${LIBCRYPT}
LDADD= -ltermcapw -lcrypt
LIBADD= termcapw crypt
LINKS= ${BINDIR}/csh ${BINDIR}/tcsh

View File

@ -9,7 +9,6 @@ SRCS= df.c vfslist.c
CFLAGS+= -I${MOUNT}
DPADD= ${LIBUTIL} ${LIBXO}
LDADD= -lutil -lxo
LIBADD= xo util
.include <bsd.prog.mk>

View File

@ -9,8 +9,7 @@ MLINKS= ed.1 red.1
.if ${MK_OPENSSL} != "no" && ${MK_ED_CRYPTO} != "no"
CFLAGS+=-DDES
DPADD= ${LIBCRYPTO}
LDADD= -lcrypto
LIBADD= crypto
.endif
.include <bsd.prog.mk>

View File

@ -5,14 +5,12 @@
PROG= ls
SRCS= cmp.c ls.c print.c util.c
DPADD= ${LIBUTIL}
LDADD= -lutil
LIBADD= util
.if !defined(RELEASE_CRUNCH) && \
${MK_LS_COLORS} != no
CFLAGS+= -DCOLORLS
DPADD+= ${LIBTERMCAPW}
LDADD+= -ltermcapw
LIBADD+= termcapw
.endif
.include <bsd.prog.mk>

View File

@ -5,8 +5,7 @@
PROG= pkill
DPADD= ${LIBKVM}
LDADD= -lkvm
LIBADD= kvm
LINKS= ${BINDIR}/pkill ${BINDIR}/pgrep
MLINKS= pkill.1 pgrep.1

View File

@ -11,7 +11,6 @@ SRCS= fmt.c keyword.c nlist.c print.c ps.c
# on large systems.
#
CFLAGS+=-DLAZY_PS
DPADD= ${LIBM} ${LIBKVM} ${LIBJAIL}
LDADD= -lm -lkvm -ljail
LIBADD= m kvm jail
.include <bsd.prog.mk>

View File

@ -14,11 +14,7 @@ MAN= rmail.8
WARNS?= 2
CFLAGS+=-I${SENDMAIL_DIR}/include -I.
LIBSMDIR= ${.OBJDIR}/../../lib/libsm
LIBSM= ${LIBSMDIR}/libsm.a
DPADD= ${LIBSM}
LDADD= ${LIBSM}
LIBADD= sm
SRCS+= sm_os.h
CLEANFILES+=sm_os.h

View File

@ -18,8 +18,7 @@ SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS}
# utilities of the same name are handled with the associated manpage,
# builtin.1 in share/man/man1/.
DPADD= ${LIBEDIT} ${LIBTERMCAPW}
LDADD= -ledit -ltermcapw
LIBADD= edit
CFLAGS+=-DSHELL -I. -I${.CURDIR}
# for debug:

View File

@ -1211,13 +1211,13 @@ dt_module_update(dtrace_hdl_t *dtp, struct kld_file_stat *k_stat)
#if defined(__FreeBSD__)
if (sh.sh_size == 0)
continue;
if (is_elf_obj && (sh.sh_type == SHT_PROGBITS ||
sh.sh_type == SHT_NOBITS)) {
if (sh.sh_type == SHT_PROGBITS || sh.sh_type == SHT_NOBITS) {
alignmask = sh.sh_addralign - 1;
mapbase += alignmask;
mapbase &= ~alignmask;
sh.sh_addr = mapbase;
dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr;
if (is_elf_obj)
dmp->dm_sec_offsets[elf_ndxscn(sp)] = sh.sh_addr;
mapbase += sh.sh_size;
}
#endif

View File

@ -545,8 +545,9 @@ dt_probe_define(dt_provider_t *pvp, dt_probe_t *prp,
for (pip = prp->pr_inst; pip != NULL; pip = pip->pi_next) {
if (strcmp(pip->pi_fname, fname) == 0 &&
((rname == NULL && pip->pi_rname[0] == '\0') ||
(rname != NULL && strcmp(pip->pi_rname, rname)) == 0))
((rname == NULL && pip->pi_rname == NULL) ||
(rname != NULL && pip->pi_rname != NULL &&
strcmp(pip->pi_rname, rname) == 0)))
break;
}

View File

@ -0,0 +1,15 @@
# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $
TOP= ..
PROG= addr2line
SRCS= addr2line.c
WARNS?= 6
DPADD= ${LIBELF} ${LIBELFTC} ${LIBDWARF}
LDADD= -lelftc -ldwarf -lelf
MAN1= addr2line.1
.include "${TOP}/mk/elftoolchain.prog.mk"

View File

@ -0,0 +1,159 @@
.\" Copyright (c) 2009,2010 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: addr2line.1 2066 2011-10-26 15:40:28Z jkoshy $
.\"
.Dd July 25, 2010
.Os
.Dt ADDR2LINE 1
.Sh NAME
.Nm addr2line
.Nd translate program addresses to source file names and line numbers
.Sh SYNOPSIS
.Nm
.Op Fl b Ar target | Fl -target Ns = Ns Ar target
.Op Fl e Ar pathname | Fl -exe Ns = Ns Ar pathname
.Op Fl f | Fl -functions
.Op Fl j Ar sectionname | Fl -section Ns = Ns Ar sectionname
.Op Fl s | Fl -basename
.Op Fl C | Fl -demangle
.Op Fl H | Fl -help
.Op Fl V | Fl -version
.Op Ar hexaddress Ns ...
.Sh DESCRIPTION
The
.Nm
utility translates program addresses specified by the command line
arguments
.Ar hexaddress
to their corresponding source file names and line numbers.
If no arguments are given to
.Nm ,
it will read these addresses from standard input.
.Pp
Program addresses specified by arguments
.Ar hexaddress
are encoded using the conventions accepted by
.Xr strtoull 3 .
.Pp
By default,
.Nm
will use the executable
.Dq Pa a.out .
The
.Fl e
option may be used to specified a different ELF object.
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width indent
.It Fl b Ar target | Fl -target Ns = Ns Ar target
This option is recognized by
.Nm
but is ignored.
It is supported for compatibility with GNU binutils.
.It Fl e Ar pathname | Fl -exe Ns = Ns Ar pathname
Use the ELF object specified by argument
.Ar pathname
to translate addresses.
If this option is not specified,
.Nm
will use the file
.Dq Pa a.out .
.It Fl f | Fl -functions
Display function names in addition to file and line number information.
.It Fl j Ar sectionname | Fl -section Ns = Ns Ar sectionname
The values specified by arguments
.Ar hexaddress
are to be treated as offsets into the section named
.Ar sectionname .
.It Fl s | -basename
Display only the base name for each file name.
.It Fl C | Fl -demangle
Demangle C++ names.
.It Fl H | Fl -help
Print a help message.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Sh OUTPUT FORMAT
If the
.Fl f
option was not specified,
.Nm
will print the file name and line number for each address specified
on a separate line.
.Pp
If the
.Fl f
option was specified,
.Nm
will print a line containing the name of the function corresponding
to program address
.Ar hexaddress ,
followed by a line with the file name and line number.
.Pp
The
.Nm
utility prints the file name and line number using the format
.Dq FILENAME:LINENUMBER .
.Pp
If a file or function name could not be determined,
.Nm
will print a question mark in their place.
If the line number could not be determined,
.Nm
will print a zero in its place.
.Sh EXAMPLES
To map address 080483c4 in the default executable
.Pa a.out
to a source file name and line number use:
.D1 "% addr2line 080483c4"
.Pp
To map address 080483c4 in executable
.Pa helloworld ,
use:
.D1 "% addr2line -e helloworld 080483c4"
.Pp
To have
.Nm
act as a filter reading addresses from its standard input use:
.D1 "% addr2line"
.Pp
To print the function name corresponding to an address in addition to
its source file and line number use:
.D1 "% addr2line -f 080483c4"
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr nm 1 ,
.Xr elfdump 1 ,
.Xr elfcopy 1 ,
.Xr strtoull 3
.Sh AUTHORS
The
.Nm
utility was written by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

View File

@ -0,0 +1,410 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <dwarf.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <getopt.h>
#include <libdwarf.h>
#include <libelftc.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: addr2line.c 2185 2011-11-19 16:07:16Z jkoshy $");
static struct option longopts[] = {
{"target" , required_argument, NULL, 'b'},
{"demangle", no_argument, NULL, 'C'},
{"exe", required_argument, NULL, 'e'},
{"functions", no_argument, NULL, 'f'},
{"section", required_argument, NULL, 'j'},
{"basename", no_argument, NULL, 's'},
{"help", no_argument, NULL, 'H'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
static int demangle, func, base;
static char unknown[] = { '?', '?', '\0' };
static Dwarf_Addr section_base;
#define USAGE_MESSAGE "\
Usage: %s [options] hexaddress...\n\
Map program addresses to source file names and line numbers.\n\n\
Options:\n\
-b TGT | --target=TGT (Accepted but ignored).\n\
-e EXE | --exec=EXE Use program \"EXE\" to translate addresses.\n\
-f | --functions Display function names.\n\
-j NAME | --section=NAME Values are offsets into section \"NAME\".\n\
-s | --basename Only show the base name for each file name.\n\
-C | --demangle Demangle C++ names.\n\
-H | --help Print a help message.\n\
-V | --version Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
version(void)
{
fprintf(stderr, "%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static void
search_func(Dwarf_Debug dbg, Dwarf_Die die, Dwarf_Addr addr,
const char **rlt_func)
{
Dwarf_Die ret_die, spec_die;
Dwarf_Error de;
Dwarf_Half tag;
Dwarf_Unsigned lopc, hipc;
Dwarf_Off ref;
Dwarf_Attribute sub_at, spec_at;
char *func0;
int ret;
if (*rlt_func != NULL)
return;
if (dwarf_tag(die, &tag, &de)) {
warnx("dwarf_tag: %s", dwarf_errmsg(de));
goto cont_search;
}
if (tag == DW_TAG_subprogram) {
if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) ||
dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de))
goto cont_search;
if (addr < lopc || addr >= hipc)
goto cont_search;
/* Found it! */
*rlt_func = unknown;
ret = dwarf_attr(die, DW_AT_name, &sub_at, &de);
if (ret == DW_DLV_ERROR)
return;
if (ret == DW_DLV_OK) {
if (dwarf_formstring(sub_at, &func0, &de))
*rlt_func = unknown;
else
*rlt_func = func0;
return;
}
/*
* If DW_AT_name is not present, but DW_AT_specification is
* present, then probably the actual name is in the DIE
* referenced by DW_AT_specification.
*/
if (dwarf_attr(die, DW_AT_specification, &spec_at, &de))
return;
if (dwarf_global_formref(spec_at, &ref, &de))
return;
if (dwarf_offdie(dbg, ref, &spec_die, &de))
return;
if (dwarf_attrval_string(spec_die, DW_AT_name, rlt_func, &de))
*rlt_func = unknown;
return;
}
cont_search:
/* Search children. */
ret = dwarf_child(die, &ret_die, &de);
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_child: %s", dwarf_errmsg(de));
else if (ret == DW_DLV_OK)
search_func(dbg, ret_die, addr, rlt_func);
/* Search sibling. */
ret = dwarf_siblingof(dbg, die, &ret_die, &de);
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_siblingof: %s", dwarf_errmsg(de));
else if (ret == DW_DLV_OK)
search_func(dbg, ret_die, addr, rlt_func);
}
static void
translate(Dwarf_Debug dbg, const char* addrstr)
{
Dwarf_Die die;
Dwarf_Line *lbuf;
Dwarf_Error de;
Dwarf_Half tag;
Dwarf_Unsigned lopc, hipc, addr, lineno, plineno;
Dwarf_Signed lcount;
Dwarf_Addr lineaddr, plineaddr;
const char *funcname;
char *file, *file0, *pfile;
char demangled[1024];
int i, ret;
addr = strtoull(addrstr, NULL, 16);
addr += section_base;
lineno = 0;
file = unknown;
while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de)) == DW_DLV_OK) {
die = NULL;
while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) {
if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) {
warnx("dwarf_tag failed: %s",
dwarf_errmsg(de));
goto out;
}
/* XXX: What about DW_TAG_partial_unit? */
if (tag == DW_TAG_compile_unit)
break;
}
if (die == NULL) {
warnx("could not find DW_TAG_compile_unit die");
goto out;
}
if (!dwarf_attrval_unsigned(die, DW_AT_low_pc, &lopc, &de) &&
!dwarf_attrval_unsigned(die, DW_AT_high_pc, &hipc, &de)) {
/*
* Check if the address falls into the PC range of
* this CU.
*/
if (addr < lopc || addr >= hipc)
continue;
}
if (dwarf_srclines(die, &lbuf, &lcount, &de) != DW_DLV_OK) {
warnx("dwarf_srclines: %s", dwarf_errmsg(de));
goto out;
}
plineaddr = ~0ULL;
plineno = 0;
pfile = unknown;
for (i = 0; i < lcount; i++) {
if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) {
warnx("dwarf_lineaddr: %s",
dwarf_errmsg(de));
goto out;
}
if (dwarf_lineno(lbuf[i], &lineno, &de)) {
warnx("dwarf_lineno: %s",
dwarf_errmsg(de));
goto out;
}
if (dwarf_linesrc(lbuf[i], &file0, &de)) {
warnx("dwarf_linesrc: %s",
dwarf_errmsg(de));
} else
file = file0;
if (addr == lineaddr)
goto out;
else if (addr < lineaddr && addr > plineaddr) {
lineno = plineno;
file = pfile;
goto out;
}
plineaddr = lineaddr;
plineno = lineno;
pfile = file;
}
}
out:
funcname = NULL;
if (ret == DW_DLV_OK && func)
search_func(dbg, die, addr, &funcname);
if (func) {
if (funcname == NULL)
funcname = unknown;
if (demangle &&
!elftc_demangle(funcname, demangled, sizeof(demangled), 0))
printf("%s\n", demangled);
else
printf("%s\n", funcname);
}
(void) printf("%s:%ju\n", base ? basename(file) : file, lineno);
/*
* Reset internal CU pointer, so we will start from the first CU
* next round.
*/
while (ret != DW_DLV_NO_ENTRY) {
if (ret == DW_DLV_ERROR)
errx(EXIT_FAILURE, "dwarf_next_cu_header: %s",
dwarf_errmsg(de));
ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
&de);
}
}
static void
find_section_base(const char *exe, Elf *e, const char *section)
{
Dwarf_Addr off;
Elf_Scn *scn;
GElf_Ehdr eh;
GElf_Shdr sh;
size_t shstrndx;
int elferr;
const char *name;
if (gelf_getehdr(e, &eh) != &eh) {
warnx("gelf_getehdr failed: %s", elf_errmsg(-1));
return;
}
if (!elf_getshstrndx(e, &shstrndx)) {
warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
return;
}
(void) elf_errno();
off = 0;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(e, shstrndx, sh.sh_name)) == NULL)
goto next;
if (!strcmp(section, name)) {
if (eh.e_type == ET_EXEC || eh.e_type == ET_DYN) {
/*
* For executables, section base is the virtual
* address of the specified section.
*/
section_base = sh.sh_addr;
} else if (eh.e_type == ET_REL) {
/*
* For relocatables, section base is the
* relative offset of the specified section
* to the start of the first section.
*/
section_base = off;
} else
warnx("unknown e_type %u", eh.e_type);
return;
}
next:
off += sh.sh_size;
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
errx(EXIT_FAILURE, "%s: cannot find section %s", exe, section);
}
int
main(int argc, char **argv)
{
Elf *e;
Dwarf_Debug dbg;
Dwarf_Error de;
const char *exe, *section;
char line[1024];
int fd, i, opt;
exe = NULL;
section = NULL;
while ((opt = getopt_long(argc, argv, "b:Ce:fj:sHV", longopts, NULL)) !=
-1) {
switch (opt) {
case 'b':
/* ignored */
break;
case 'C':
demangle = 1;
break;
case 'e':
exe = optarg;
break;
case 'f':
func = 1;
break;
case 'j':
section = optarg;
break;
case 's':
base = 1;
break;
case 'H':
usage();
case 'V':
version();
default:
usage();
}
}
argv += optind;
argc -= optind;
if (exe == NULL)
exe = "a.out";
if ((fd = open(exe, O_RDONLY)) < 0)
err(EXIT_FAILURE, "%s", exe);
if (dwarf_init(fd, DW_DLC_READ, NULL, NULL, &dbg, &de))
errx(EXIT_FAILURE, "dwarf_init: %s", dwarf_errmsg(de));
if (dwarf_get_elf(dbg, &e, &de) != DW_DLV_OK)
errx(EXIT_FAILURE, "dwarf_get_elf: %s", dwarf_errmsg(de));
if (section)
find_section_base(exe, e, section);
else
section_base = 0;
if (argc > 0)
for (i = 0; i < argc; i++)
translate(dbg, argv[i]);
else
while (fgets(line, sizeof(line), stdin) != NULL)
translate(dbg, line);
dwarf_finish(dbg, &de);
(void) elf_end(e);
exit(0);
}

View File

@ -0,0 +1,15 @@
# $Id: Makefile 2066 2011-10-26 15:40:28Z jkoshy $
TOP= ..
PROG= c++filt
SRCS= cxxfilt.c
WARNS?= 6
DPADD= ${LIBELFTC} ${LIBELF}
LDADD= -lelftc -lelf
MAN1= c++filt.1
.include "${TOP}/mk/elftoolchain.prog.mk"

View File

@ -0,0 +1,109 @@
.\" Copyright (c) 2009-2011 Joseph Koshy <jkoshy@users.sourceforge.net>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: c++filt.1 2175 2011-11-16 05:51:49Z jkoshy $
.\"
.Dd August 24, 2011
.Os
.Dt C++FILT 1
.Sh NAME
.Nm c++filt
.Nd decode C++ symbols
.Sh SYNOPSIS
.Nm
.Op Fl -help
.Op Fl _ | Fl -strip-underscores
.Op Fl n | Fl -no-strip-underscores
.Op Fl p | Fl -no-params
.Op Fl s Ar scheme | Fl -format Ns = Ns Ar scheme
.Op Fl V | Fl -version
.Op Ar encoded-names ...
.Sh DESCRIPTION
The
.Nm
utility translates encoded C++ symbol names to human-readable form.
.Pp
The
.Nm
utility has two operating modes.
.Bl -bullet
.It
If arguments
.Ar encoded-names
are not specified, then
.Nm
will act as a filter, reading from standard input
and writing to standard output.
.It
If arguments
.Ar encoded-names
are specified, then
.Nm
will decode each such argument in turn, writing its decoded form
to standard output.
.El
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width indent
.It Fl -help
Print a help message and exit.
.It Fl _ | Fl -strip-underscores
Remove a leading underscore from symbol names prior to decoding them.
.It Fl n | Fl -no-strip-underscores
Do not remove leading underscores from names.
.It Fl p | Fl -no-params
This option is recognized but ignored.
.It Fl s Ar scheme | Fl -format Ns = Ns Ar scheme
Select the encoding scheme to use.
Argument
.Ar scheme
can be one of the following:
.Bl -tag -width "gnu-v5"
.It Ar arm
Use the encoding scheme specified by the C++ Annotated Reference Manual.
.It Ar auto
Guess the encoding scheme from the input.
.It Ar gnu
Use the encoding scheme used by the GNU C++ compiler.
.It Ar gnu-v3
Use the encoding scheme used by the GNU C++ compiler, version 3.
.El
.It Fl V | Fl -version
Print a version identifier for
.Nm
and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr nm 1 ,
.Xr strip 1 ,
.Xr elftc_demangle 3
.Sh AUTHORS
The
.Nm
utility was written by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

View File

@ -0,0 +1,224 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <ctype.h>
#include <err.h>
#include <getopt.h>
#include <libelftc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: cxxfilt.c 2185 2011-11-19 16:07:16Z jkoshy $");
#define STRBUFSZ 8192
static int stripus = 0;
static int noparam = 0;
static int format = 0;
enum options
{
OPTION_HELP,
OPTION_VERSION
};
static struct option longopts[] =
{
{"format", required_argument, NULL, 's'},
{"help", no_argument, NULL, OPTION_HELP},
{"no-params", no_argument, NULL, 'p'},
{"no-strip-underscores", no_argument, NULL, 'n'},
{"strip-underscores", no_argument, NULL, '_'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0}
};
static struct {
const char *fname;
int fvalue;
} flist[] = {
{"auto", 0},
{"arm", ELFTC_DEM_ARM},
{"gnu", ELFTC_DEM_GNU2},
{"gnu-v3", ELFTC_DEM_GNU3}
};
#define USAGE_MESSAGE "\
Usage: %s [options] [encoded-names...]\n\
Translate C++ symbol names to human-readable form.\n\n\
Options:\n\
-_ | --strip-underscores Remove leading underscores prior to decoding.\n\
-n | --no-strip-underscores Do not remove leading underscores.\n\
-p | --no-params (Accepted but ignored).\n\
-s SCHEME | --format=SCHEME Select the encoding scheme to use.\n\
Valid schemes are: 'arm', 'auto', 'gnu' and\n\
'gnu-v3'.\n\
--help Print a help message.\n\
--version Print a version identifier and exit.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(1);
}
static void
version(void)
{
fprintf(stderr, "%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(0);
}
static int
find_format(const char *fstr)
{
int i;
for (i = 0; (size_t) i < sizeof(flist) / sizeof(flist[0]); i++) {
if (!strcmp(fstr, flist[i].fname))
return (flist[i].fvalue);
}
return (-1);
}
static char *
demangle(char *name, int strict, int *pos)
{
static char dem[STRBUFSZ];
char nb[STRBUFSZ];
int p, t;
if (stripus && *name == '_') {
strncpy(nb, name + 1, sizeof(nb) - 1);
t = 1;
} else {
strncpy(nb, name, sizeof(nb) - 1);
t = 0;
}
nb[sizeof(nb) - 1] = '\0';
p = strlen(nb);
if (p <= 0)
return NULL;
while (elftc_demangle(nb, dem, sizeof(dem), format) < 0) {
if (!strict && p > 1) {
nb[--p] = '\0';
continue;
} else
return (NULL);
}
if (pos != NULL)
*pos = t ? p + 1 : p;
return (dem);
}
int
main(int argc, char **argv)
{
char *dem, buf[STRBUFSZ];
int c, i, p, s, opt;
while ((opt = getopt_long(argc, argv, "_nps:V", longopts, NULL)) !=
-1) {
switch (opt) {
case '_':
stripus = 1;
break;
case 'n':
stripus = 0;
break;
case 'p':
noparam = 1;
break;
case 's':
if ((format = find_format(optarg)) < 0)
errx(EXIT_FAILURE, "unsupported format: %s",
optarg);
break;
case 'V':
version();
/* NOT REACHED */
case OPTION_HELP:
default:
usage();
/* NOT REACHED */
}
}
argv += optind;
argc -= optind;
if (*argv != NULL) {
for (i = 0; i < argc; i++) {
if ((dem = demangle(argv[i], 1, NULL)) == NULL)
fprintf(stderr, "Failed: %s\n", argv[i]);
else
printf("%s\n", dem);
}
} else {
p = 0;
for (;;) {
c = fgetc(stdin);
if (c == EOF || !isprint(c) || strchr(" \t\n", c)) {
if (p > 0) {
buf[p] = '\0';
if ((dem = demangle(buf, 0, &s)) ==
NULL)
printf("%s", buf);
else {
printf("%s", dem);
for (i = s; i < p; i++)
putchar(buf[i]);
}
p = 0;
}
if (c == EOF)
break;
if (isprint(c) || strchr(" \t\n", c))
putchar(c);
} else {
if ((size_t) p >= sizeof(buf) - 1)
warnx("buffer overflowed");
else
buf[p++] = c;
}
}
}
exit(0);
}

View File

@ -0,0 +1,41 @@
# $Id: Makefile 2290 2011-12-04 07:20:46Z jkoshy $
TOP= ..
PROG= elfcopy
SRCS= archive.c ascii.c binary.c main.c sections.c segments.c symbols.c
WARNS?= 5
DPADD= ${LIBELF} ${LIBELFTC}
LDADD= -lelf -lelftc
.if !defined(LIBELF_AR)
LDADD+= -larchive
.endif
MAN= elfcopy.1 mcs.1 strip.1
NO_SHARED?= yes
LINKS= ${BINDIR}/elfcopy ${BINDIR}/strip \
${BINDIR}/elfcopy ${BINDIR}/mcs
EXTRA_TARGETS= strip mcs
CLEANFILES+= ${EXTRA_TARGETS}
# Create in-place symbolic links to "elfcopy" at build time.
all: ${EXTRA_TARGETS}
${EXTRA_TARGETS}: ${PROG}
ln -s ${PROG} ${.TARGET}
.include "${TOP}/mk/elftoolchain.prog.mk"
.if ${OS_HOST} == "OpenBSD"
CFLAGS+= -I/usr/local/include
LDFLAGS+= -L/usr/local/lib
.endif

View File

@ -0,0 +1,528 @@
/*-
* Copyright (c) 2007-2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef LIBELF_AR
#include <archive.h>
#include <archive_entry.h>
#endif /* ! LIBELF_AR */
#include "elfcopy.h"
ELFTC_VCSID("$Id: archive.c 2370 2011-12-29 12:48:12Z jkoshy $");
#define _ARMAG_LEN 8 /* length of ar magic string */
#define _ARHDR_LEN 60 /* length of ar header */
#define _INIT_AS_CAP 128 /* initial archive string table size */
#define _INIT_SYMOFF_CAP (256*(sizeof(uint32_t))) /* initial so table size */
#define _INIT_SYMNAME_CAP 1024 /* initial sn table size */
#define _MAXNAMELEN_SVR4 15 /* max member name length in svr4 variant */
#ifndef LIBELF_AR
static void ac_read_objs(struct elfcopy *ecp, int ifd);
static void ac_write_cleanup(struct elfcopy *ecp);
static void ac_write_data(struct archive *a, const void *buf, size_t s);
static void ac_write_objs(struct elfcopy *ecp, int ofd);
#endif /* ! LIBELF_AR */
static void add_to_ar_str_table(struct elfcopy *elfcopy, const char *name);
static void add_to_ar_sym_table(struct elfcopy *ecp, const char *name);
static void extract_arsym(struct elfcopy *ecp);
static void process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj);
static void sync_ar(struct elfcopy *ecp);
static void
process_ar_obj(struct elfcopy *ecp, struct ar_obj *obj)
{
struct stat sb;
char *tempfile;
int fd;
/* Output to a temporary file. */
create_tempfile(&tempfile, &fd);
if ((ecp->eout = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
elf_flagelf(ecp->eout, ELF_C_SET, ELF_F_LAYOUT);
create_elf(ecp);
elf_end(ecp->ein);
elf_end(ecp->eout);
free(obj->buf);
obj->buf = NULL;
/* Extract archive symbols. */
if (lseek(fd, 0, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed for '%s'", tempfile);
if ((ecp->eout = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
extract_arsym(ecp);
elf_end(ecp->eout);
if (fstat(fd, &sb) == -1)
err(EXIT_FAILURE, "fstat %s failed", tempfile);
if (lseek(fd, 0, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek %s failed", tempfile);
obj->size = sb.st_size;
if ((obj->maddr = malloc(obj->size)) == NULL)
err(EXIT_FAILURE, "memory allocation failed for '%s'",
tempfile);
if ((size_t) read(fd, obj->maddr, obj->size) != obj->size)
err(EXIT_FAILURE, "read failed for '%s'", tempfile);
if (unlink(tempfile))
err(EXIT_FAILURE, "unlink %s failed", tempfile);
free(tempfile);
close(fd);
if (strlen(obj->name) > _MAXNAMELEN_SVR4)
add_to_ar_str_table(ecp, obj->name);
ecp->rela_off += _ARHDR_LEN + obj->size + obj->size % 2;
STAILQ_INSERT_TAIL(&ecp->v_arobj, obj, objs);
}
/*
* Append to the archive string table buffer.
*/
static void
add_to_ar_str_table(struct elfcopy *ecp, const char *name)
{
if (ecp->as == NULL) {
ecp->as_cap = _INIT_AS_CAP;
ecp->as_sz = 0;
if ((ecp->as = malloc(ecp->as_cap)) == NULL)
err(EXIT_FAILURE, "malloc failed");
}
/*
* The space required for holding one member name in as table includes:
* strlen(name) + (1 for '/') + (1 for '\n') + (possibly 1 for padding).
*/
while (ecp->as_sz + strlen(name) + 3 > ecp->as_cap) {
ecp->as_cap *= 2;
ecp->as = realloc(ecp->as, ecp->as_cap);
if (ecp->as == NULL)
err(EXIT_FAILURE, "realloc failed");
}
strncpy(&ecp->as[ecp->as_sz], name, strlen(name));
ecp->as_sz += strlen(name);
ecp->as[ecp->as_sz++] = '/';
ecp->as[ecp->as_sz++] = '\n';
}
/*
* Append to the archive symbol table buffer.
*/
static void
add_to_ar_sym_table(struct elfcopy *ecp, const char *name)
{
if (ecp->s_so == NULL) {
if ((ecp->s_so = malloc(_INIT_SYMOFF_CAP)) == NULL)
err(EXIT_FAILURE, "malloc failed");
ecp->s_so_cap = _INIT_SYMOFF_CAP;
ecp->s_cnt = 0;
}
if (ecp->s_sn == NULL) {
if ((ecp->s_sn = malloc(_INIT_SYMNAME_CAP)) == NULL)
err(EXIT_FAILURE, "malloc failed");
ecp->s_sn_cap = _INIT_SYMNAME_CAP;
ecp->s_sn_sz = 0;
}
if (ecp->s_cnt * sizeof(uint32_t) >= ecp->s_so_cap) {
ecp->s_so_cap *= 2;
ecp->s_so = realloc(ecp->s_so, ecp->s_so_cap);
if (ecp->s_so == NULL)
err(EXIT_FAILURE, "realloc failed");
}
ecp->s_so[ecp->s_cnt] = ecp->rela_off;
ecp->s_cnt++;
/*
* The space required for holding one symbol name in sn table includes:
* strlen(name) + (1 for '\n') + (possibly 1 for padding).
*/
while (ecp->s_sn_sz + strlen(name) + 2 > ecp->s_sn_cap) {
ecp->s_sn_cap *= 2;
ecp->s_sn = realloc(ecp->s_sn, ecp->s_sn_cap);
if (ecp->s_sn == NULL)
err(EXIT_FAILURE, "realloc failed");
}
strncpy(&ecp->s_sn[ecp->s_sn_sz], name, strlen(name));
ecp->s_sn_sz += strlen(name);
ecp->s_sn[ecp->s_sn_sz++] = '\0';
}
static void
sync_ar(struct elfcopy *ecp)
{
size_t s_sz; /* size of archive symbol table. */
size_t pm_sz; /* size of pseudo members */
int i;
/*
* Pad the symbol name string table. It is treated specially because
* symbol name table should be padded by a '\0', not the common '\n'
* for other members. The size of sn table includes the pad bit.
*/
if (ecp->s_cnt != 0 && ecp->s_sn_sz % 2 != 0)
ecp->s_sn[ecp->s_sn_sz++] = '\0';
/*
* Archive string table is padded by a "\n" as the normal members.
* The difference is that the size of archive string table counts
* in the pad bit, while normal members' size fileds do not.
*/
if (ecp->as != NULL && ecp->as_sz % 2 != 0)
ecp->as[ecp->as_sz++] = '\n';
/*
* If there is a symbol table, calculate the size of pseudo members,
* convert previously stored relative offsets to absolute ones, and
* then make them Big Endian.
*
* absolute_offset = htobe32(relative_offset + size_of_pseudo_members)
*/
if (ecp->s_cnt != 0) {
s_sz = (ecp->s_cnt + 1) * sizeof(uint32_t) + ecp->s_sn_sz;
pm_sz = _ARMAG_LEN + (_ARHDR_LEN + s_sz);
if (ecp->as != NULL)
pm_sz += _ARHDR_LEN + ecp->as_sz;
for (i = 0; (size_t)i < ecp->s_cnt; i++)
*(ecp->s_so + i) = htobe32(*(ecp->s_so + i) +
pm_sz);
}
}
/*
* Extract global symbols from archive members.
*/
static void
extract_arsym(struct elfcopy *ecp)
{
Elf_Scn *scn;
GElf_Shdr shdr;
GElf_Sym sym;
Elf_Data *data;
char *name;
size_t n, shstrndx;
int elferr, tabndx, len, i;
if (elf_kind(ecp->eout) != ELF_K_ELF) {
warnx("internal: cannot extract symbols from non-elf object");
return;
}
if (elf_getshstrndx(ecp->eout, &shstrndx) == 0) {
warnx("elf_getshstrndx failed: %s", elf_errmsg(-1));
return;
}
tabndx = -1;
scn = NULL;
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if ((name = elf_strptr(ecp->eout, shstrndx, shdr.sh_name)) ==
NULL) {
warnx("elf_strptr failed: %s", elf_errmsg(-1));
continue;
}
if (strcmp(name, ".strtab") == 0) {
tabndx = elf_ndxscn(scn);
break;
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
/* Ignore members without symbol table. */
if (tabndx == -1)
return;
scn = NULL;
while ((scn = elf_nextscn(ecp->eout, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("elf_getshdr failed: %s", elf_errmsg(-1));
continue;
}
if (shdr.sh_type != SHT_SYMTAB)
continue;
data = NULL;
n = 0;
while (n < shdr.sh_size &&
(data = elf_getdata(scn, data)) != NULL) {
len = data->d_size / shdr.sh_entsize;
for (i = 0; i < len; i++) {
if (gelf_getsym(data, i, &sym) != &sym) {
warnx("gelf_getsym failed: %s",
elf_errmsg(-1));
continue;
}
/* keep only global or weak symbols */
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL &&
GELF_ST_BIND(sym.st_info) != STB_WEAK)
continue;
/* keep only defined symbols */
if (sym.st_shndx == SHN_UNDEF)
continue;
if ((name = elf_strptr(ecp->eout, tabndx,
sym.st_name)) == NULL) {
warnx("elf_strptr failed: %s",
elf_errmsg(-1));
continue;
}
add_to_ar_sym_table(ecp, name);
}
}
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
}
#ifndef LIBELF_AR
/*
* Convenient wrapper for general libarchive error handling.
*/
#define AC(CALL) do { \
if ((CALL)) \
errx(EXIT_FAILURE, "%s", archive_error_string(a)); \
} while (0)
/* Earlier versions of libarchive had some functions that returned 'void'. */
#if ARCHIVE_VERSION_NUMBER >= 2000000
#define ACV(CALL) AC(CALL)
#else
#define ACV(CALL) do { \
(CALL); \
} while (0)
#endif
int
ac_detect_ar(int ifd)
{
struct archive *a;
struct archive_entry *entry;
int r;
r = -1;
if ((a = archive_read_new()) == NULL)
return (0);
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
if (archive_read_open_fd(a, ifd, 10240) == ARCHIVE_OK)
r = archive_read_next_header(a, &entry);
archive_read_close(a);
archive_read_finish(a);
return (r == ARCHIVE_OK);
}
void
ac_create_ar(struct elfcopy *ecp, int ifd, int ofd)
{
ac_read_objs(ecp, ifd);
sync_ar(ecp);
ac_write_objs(ecp, ofd);
ac_write_cleanup(ecp);
}
static void
ac_read_objs(struct elfcopy *ecp, int ifd)
{
struct archive *a;
struct archive_entry *entry;
struct ar_obj *obj;
const char *name;
char *buff;
size_t size;
int r;
ecp->rela_off = 0;
if (lseek(ifd, 0, SEEK_SET) == -1)
err(EXIT_FAILURE, "lseek failed");
if ((a = archive_read_new()) == NULL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
archive_read_support_compression_none(a);
archive_read_support_format_ar(a);
AC(archive_read_open_fd(a, ifd, 10240));
for(;;) {
r = archive_read_next_header(a, &entry);
if (r == ARCHIVE_FATAL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
if (r == ARCHIVE_EOF)
break;
if (r == ARCHIVE_WARN || r == ARCHIVE_RETRY)
warnx("%s", archive_error_string(a));
if (r == ARCHIVE_RETRY)
continue;
name = archive_entry_pathname(entry);
/* skip pseudo members. */
if (strcmp(name, "/") == 0 || strcmp(name, "//") == 0)
continue;
size = archive_entry_size(entry);
if (size > 0) {
if ((buff = malloc(size)) == NULL)
err(EXIT_FAILURE, "malloc failed");
if (archive_read_data(a, buff, size) != (ssize_t)size) {
warnx("%s", archive_error_string(a));
free(buff);
continue;
}
if ((obj = malloc(sizeof(*obj))) == NULL)
err(EXIT_FAILURE, "malloc failed");
if ((obj->name = strdup(name)) == NULL)
err(EXIT_FAILURE, "strdup failed");
obj->buf = buff;
obj->uid = archive_entry_uid(entry);
obj->gid = archive_entry_gid(entry);
obj->md = archive_entry_mode(entry);
obj->mtime = archive_entry_mtime(entry);
if ((ecp->ein = elf_memory(buff, size)) == NULL)
errx(EXIT_FAILURE, "elf_memory() failed: %s",
elf_errmsg(-1));
if (elf_kind(ecp->ein) != ELF_K_ELF)
errx(EXIT_FAILURE,
"file format not recognized");
process_ar_obj(ecp, obj);
}
}
AC(archive_read_close(a));
ACV(archive_read_finish(a));
}
static void
ac_write_objs(struct elfcopy *ecp, int ofd)
{
struct archive *a;
struct archive_entry *entry;
struct ar_obj *obj;
int nr;
if ((a = archive_write_new()) == NULL)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
archive_write_set_format_ar_svr4(a);
archive_write_set_compression_none(a);
AC(archive_write_open_fd(a, ofd));
/* Write the archive symbol table, even if it's empty. */
entry = archive_entry_new();
archive_entry_copy_pathname(entry, "/");
archive_entry_set_mtime(entry, time(NULL), 0);
archive_entry_set_size(entry, (ecp->s_cnt + 1) * sizeof(uint32_t) +
ecp->s_sn_sz);
AC(archive_write_header(a, entry));
nr = htobe32(ecp->s_cnt);
ac_write_data(a, &nr, sizeof(uint32_t));
ac_write_data(a, ecp->s_so, sizeof(uint32_t) * ecp->s_cnt);
ac_write_data(a, ecp->s_sn, ecp->s_sn_sz);
archive_entry_free(entry);
/* Write the archive string table, if exist. */
if (ecp->as != NULL) {
entry = archive_entry_new();
archive_entry_copy_pathname(entry, "//");
archive_entry_set_size(entry, ecp->as_sz);
AC(archive_write_header(a, entry));
ac_write_data(a, ecp->as, ecp->as_sz);
archive_entry_free(entry);
}
/* Write normal members. */
STAILQ_FOREACH(obj, &ecp->v_arobj, objs) {
entry = archive_entry_new();
archive_entry_copy_pathname(entry, obj->name);
archive_entry_set_uid(entry, obj->uid);
archive_entry_set_gid(entry, obj->gid);
archive_entry_set_mode(entry, obj->md);
archive_entry_set_size(entry, obj->size);
archive_entry_set_mtime(entry, obj->mtime, 0);
archive_entry_set_filetype(entry, AE_IFREG);
AC(archive_write_header(a, entry));
ac_write_data(a, obj->maddr, obj->size);
archive_entry_free(entry);
}
AC(archive_write_close(a));
ACV(archive_write_finish(a));
}
static void
ac_write_cleanup(struct elfcopy *ecp)
{
struct ar_obj *obj, *obj_temp;
STAILQ_FOREACH_SAFE(obj, &ecp->v_arobj, objs, obj_temp) {
STAILQ_REMOVE(&ecp->v_arobj, obj, ar_obj, objs);
if (obj->maddr != NULL)
free(obj->maddr);
free(obj->name);
free(obj);
}
free(ecp->as);
free(ecp->s_so);
free(ecp->s_sn);
ecp->as = NULL;
ecp->s_so = NULL;
ecp->s_sn = NULL;
}
/*
* Wrapper for archive_write_data().
*/
static void
ac_write_data(struct archive *a, const void *buf, size_t s)
{
if (archive_write_data(a, buf, s) != (ssize_t)s)
errx(EXIT_FAILURE, "%s", archive_error_string(a));
}
#endif /* ! LIBELF_AR */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,286 @@
/*-
* Copyright (c) 2010,2011 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <err.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "elfcopy.h"
ELFTC_VCSID("$Id: binary.c 2358 2011-12-19 18:22:32Z kaiwang27 $");
/*
* Convert ELF object to `binary'. Sections with SHF_ALLOC flag set
* are copied to the result binary. The relative offsets for each section
* are retained, so the result binary file might contain "holes".
*/
void
create_binary(int ifd, int ofd)
{
Elf *e;
Elf_Scn *scn;
Elf_Data *d;
GElf_Shdr sh;
off_t base, off;
int elferr;
if ((e = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
errx(EXIT_FAILURE, "elf_begin() failed: %s",
elf_errmsg(-1));
base = 0;
if (lseek(ofd, base, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed");
/*
* Find base offset in the first iteration.
*/
base = -1;
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
(void) elf_errno();
continue;
}
if ((sh.sh_flags & SHF_ALLOC) == 0 ||
sh.sh_type == SHT_NOBITS ||
sh.sh_size == 0)
continue;
if (base == -1 || (off_t) sh.sh_offset < base)
base = sh.sh_offset;
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
if (base == -1)
return;
/*
* Write out sections in the second iteration.
*/
scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) {
if (gelf_getshdr(scn, &sh) == NULL) {
warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
(void) elf_errno();
continue;
}
if ((sh.sh_flags & SHF_ALLOC) == 0 ||
sh.sh_type == SHT_NOBITS ||
sh.sh_size == 0)
continue;
(void) elf_errno();
if ((d = elf_getdata(scn, NULL)) == NULL) {
elferr = elf_errno();
if (elferr != 0)
warnx("elf_getdata failed: %s", elf_errmsg(-1));
continue;
}
if (d->d_buf == NULL || d->d_size == 0)
continue;
/* lseek to section offset relative to `base'. */
off = sh.sh_offset - base;
if (lseek(ofd, off, SEEK_SET) < 0)
err(EXIT_FAILURE, "lseek failed");
/* Write out section contents. */
if (write(ofd, d->d_buf, d->d_size) != (ssize_t) d->d_size)
err(EXIT_FAILURE, "write failed");
}
elferr = elf_errno();
if (elferr != 0)
warnx("elf_nextscn failed: %s", elf_errmsg(elferr));
}
#define _SYMBOL_NAMSZ 1024
/*
* Convert `binary' to ELF object. The input `binary' is converted to
* a relocatable (.o) file, a few symbols will also be created to make
* it easier to access the binary data in other compilation units.
*/
void
create_elf_from_binary(struct elfcopy *ecp, int ifd, const char *ifn)
{
char name[_SYMBOL_NAMSZ];
struct section *sec, *sec_temp, *shtab;
struct stat sb;
GElf_Ehdr oeh;
GElf_Shdr sh;
void *content;
uint64_t off, data_start, data_end, data_size;
/* Reset internal section list. */
if (!TAILQ_EMPTY(&ecp->v_sec))
TAILQ_FOREACH_SAFE(sec, &ecp->v_sec, sec_list, sec_temp) {
TAILQ_REMOVE(&ecp->v_sec, sec, sec_list);
free(sec);
}
if (fstat(ifd, &sb) == -1)
err(EXIT_FAILURE, "fstat failed");
/* Read the input binary file to a internal buffer. */
if ((content = malloc(sb.st_size)) == NULL)
err(EXIT_FAILURE, "malloc failed");
if (read(ifd, content, sb.st_size) != sb.st_size)
err(EXIT_FAILURE, "read failed");
/*
* TODO: copy the input binary to output binary verbatim if -O is not
* specified.
*/
/* Create EHDR for output .o file. */
if (gelf_newehdr(ecp->eout, ecp->oec) == NULL)
errx(EXIT_FAILURE, "gelf_newehdr failed: %s",
elf_errmsg(-1));
if (gelf_getehdr(ecp->eout, &oeh) == NULL)
errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
elf_errmsg(-1));
/* Initialise e_ident fields. */
oeh.e_ident[EI_CLASS] = ecp->oec;
oeh.e_ident[EI_DATA] = ecp->oed;
/*
* TODO: Set OSABI according to the OS platform where elfcopy(1)
* was build. (probably)
*/
oeh.e_ident[EI_OSABI] = ELFOSABI_NONE;
oeh.e_machine = ecp->oem;
oeh.e_type = ET_REL;
oeh.e_entry = 0;
ecp->flags |= RELOCATABLE;
/* Create .shstrtab section */
init_shstrtab(ecp);
ecp->shstrtab->off = 0;
/*
* Create `.data' section which contains the binary data. The
* section is inserted immediately after EHDR.
*/
off = gelf_fsize(ecp->eout, ELF_T_EHDR, 1, EV_CURRENT);
if (off == 0)
errx(EXIT_FAILURE, "gelf_fsize() failed: %s", elf_errmsg(-1));
(void) create_external_section(ecp, ".data", NULL, content, sb.st_size,
off, SHT_PROGBITS, ELF_T_BYTE, SHF_ALLOC | SHF_WRITE, 1, 0, 1);
/* Insert .shstrtab after .data section. */
if ((ecp->shstrtab->os = elf_newscn(ecp->eout)) == NULL)
errx(EXIT_FAILURE, "elf_newscn failed: %s",
elf_errmsg(-1));
insert_to_sec_list(ecp, ecp->shstrtab, 1);
/* Insert section header table here. */
shtab = insert_shtab(ecp, 1);
/* Count in .symtab and .strtab section headers. */
shtab->sz += gelf_fsize(ecp->eout, ELF_T_SHDR, 2, EV_CURRENT);
#define _GEN_SYMNAME(S) do { \
snprintf(name, sizeof(name), "%s%s%s", "_binary_", ifn, S); \
} while (0)
/*
* Create symbol table.
*/
create_external_symtab(ecp);
data_start = 0;
data_end = data_start + sb.st_size;
data_size = sb.st_size;
_GEN_SYMNAME("_start");
add_to_symtab(ecp, name, data_start, 0, 1,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
_GEN_SYMNAME("_end");
add_to_symtab(ecp, name, data_end, 0, 1,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
_GEN_SYMNAME("_size");
add_to_symtab(ecp, name, data_size, 0, SHN_ABS,
ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, 1);
finalize_external_symtab(ecp);
create_symtab_data(ecp);
#undef _GEN_SYMNAME
/*
* Write the underlying ehdr. Note that it should be called
* before elf_setshstrndx() since it will overwrite e->e_shstrndx.
*/
if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
elf_errmsg(-1));
/* Generate section name string table (.shstrtab). */
ecp->flags |= SYMTAB_EXIST;
set_shstrtab(ecp);
/* Update sh_name pointer for each section header entry. */
update_shdr(ecp, 0);
/* Properly set sh_link field of .symtab section. */
if (gelf_getshdr(ecp->symtab->os, &sh) == NULL)
errx(EXIT_FAILURE, "692 gelf_getshdr() failed: %s",
elf_errmsg(-1));
sh.sh_link = elf_ndxscn(ecp->strtab->os);
if (!gelf_update_shdr(ecp->symtab->os, &sh))
errx(EXIT_FAILURE, "gelf_update_shdr() failed: %s",
elf_errmsg(-1));
/* Renew oeh to get the updated e_shstrndx. */
if (gelf_getehdr(ecp->eout, &oeh) == NULL)
errx(EXIT_FAILURE, "gelf_getehdr() failed: %s",
elf_errmsg(-1));
/* Resync section offsets. */
resync_sections(ecp);
/* Store SHDR offset in EHDR. */
oeh.e_shoff = shtab->off;
/* Update ehdr since we modified e_shoff. */
if (gelf_update_ehdr(ecp->eout, &oeh) == 0)
errx(EXIT_FAILURE, "gelf_update_ehdr() failed: %s",
elf_errmsg(-1));
/* Write out the output elf object. */
if (elf_update(ecp->eout, ELF_C_WRITE) < 0)
errx(EXIT_FAILURE, "elf_update() failed: %s",
elf_errmsg(-1));
/* Release allocated resource. */
free(content);
free_elf(ecp);
}

View File

@ -0,0 +1,323 @@
.\" Copyright (c) 2008-2009,2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elfcopy.1 2373 2011-12-30 07:13:44Z jkoshy $
.\"
.Dd October 03, 2011
.Os
.Dt ELFCOPY 1
.Sh NAME
.Nm elfcopy
.Nd copy and translate object files
.Sh SYNOPSIS
.Nm
.Op Fl I Ar objformat | Fl s Ar objformat | Fl -input-target= Ns Ar objformat
.Op Fl K Ar symbolname | Fl -keep-symbol= Ns Ar symbolname
.Op Fl L Ar symbolname | Fl -localize-symbol= Ns Ar symbolname
.Op Fl N Ar symbolname | Fl -strip-symbol= Ns Ar symbolname
.Op Fl O Ar objformat | Fl -output-target= Ns Ar objformat
.Op Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
.Op Fl S | Fl -strip-all
.Op Fl V | Fl -version
.Op Fl W Ar symbolname | Fl -weaken-symbol= Ns Ar symbolname
.Op Fl X | Fl -discard-locals
.Op Fl d | Fl g | Fl -strip-debug
.Op Fl h | Fl -help
.Op Fl j Ar sectionname | Fl -only-section= Ns Ar sectionname
.Op Fl p | Fl -preserve-dates
.Op Fl w | Fl -wildcard
.Op Fl x | Fl -discard-all
.Op Fl -add-section Ar sectionname Ns = Ns Ar filename
.Oo
.Fl -adjust-section-vma Ar section Ns {+|-|=} Ns Ar val |
.Fl -change-section-address Ar section Ns {+|-|=} Ns Ar val
.Oc
.Oo
.Fl -adjust-start Ns = Ns Ar increment |
.Fl -change-start Ns = Ns Ar increment
.Oc
.Oo
.Fl -adjust-vma Ns = Ns Ar increment |
.Fl -change-addresses Ns = Ns Ar increment
.Oc
.Op Fl -adjust-warnings | Fl -change-warnings
.Op Fl -change-section-lma Ar section Ns {+|-|=} Ns Ar val
.Op Fl -change-section-vma Ar section Ns {+|-|=} Ns Ar val
.Op Fl -gap-fill Ns = Ns Ar val
.Op Fl -no-adjust-warnings | Fl -no-change-warnings
.Op Fl -only-keep-debug
.Op Fl -pad-to Ns = Ns Ar address
.Op Fl -prefix-alloc-sections Ns = Ns Ar string
.Op Fl -prefix-sections Ns = Ns Ar string
.Op Fl -prefix-symbols Ns = Ns Ar string
.Op Fl -rename-section Ar oldname Ns = Ns Ar newname Ns Op Ar ,flags
.Op Fl -set-section-flags Ar sectionname Ns = Ns Ar flags
.Op Fl -set-start Ns = Ns Ar address
.Op Fl -srec-forceS3
.Op Fl -srec-len Ns = Ns Ar val
.Op Fl -strip-unneeded
.Ar infile
.Op Ar outfile
.Sh DESCRIPTION
The
.Nm
utility copies the content of the ELF object named by argument
.Ar infile
to that named by argument
.Ar outfile ,
transforming it according to the command line options specified.
If argument
.Ar outfile
is not specified,
.Nm
will create a temporary file and will subsequently rename it as
.Ar infile .
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl I Ar objformat | Fl s Ar objformat | Fl -input-target= Ns Ar objformat
Specify that the input file named by the argument
.Ar infile
is in the object format specified by the argument
.Ar objformat .
.It Fl K Ar symbolname | Fl -keep-symbol= Ns Ar symbolname
Copy the symbol named by argument
.Ar symbolname
to the output.
.It Fl L Ar symbolname | Fl -localize-symbol= Ns Ar symbolname
Make the symbol named by argument
.Ar symbolname
local to the output file.
.It Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbolname
Do not copy the symbol named by argument
.Ar symbolname
to the output.
.It Fl O Ar objformat | Fl -output-target= Ns Ar objformat
Write the output file using the object format specified in argument
.Ar objformat .
.It Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
Remove any section with name
.Ar sectionname
from the output file.
.It Fl S | Fl -strip-all
Do not copy symbol and relocation information to the target file.
.It Fl V | Fl -version
Print a version identifier and exit.
.It Fl W Ar symbolname | Fl -weaken-symbol= Ns Ar symbolname
Mark the symbol named by argument
.Ar symbolname
as weak in the output.
.It Fl X | Fl -discard-locals
Do not copy compiler generated local symbols to the output.
.It Fl d | Fl g | Fl -strip-debug
Do not copy debugging information to the target file.
.It Fl h | Fl -help
Display a help message and exit.
.It Fl j Ar sectionname | Fl -only-section= Ns Ar sectionname
Copy only the section named by argument
.Ar sectionname
to the output.
.It Fl p | Fl -preserve-dates
Set the access and modification times of the output file to the
same as those of the input.
.It Fl w | Fl -wildcard
Use shell-style patterns to name symbols.
The following meta-characters are recognized in patterns:
.Bl -tag -width "...." -compact
.It Li !
If this is the first character of the pattern, invert the sense of the
pattern match.
.It Li *
Matches any string of characters in a symbol name.
.It Li ?
Matches zero or one character in a symbol name.
.It Li [
Mark the start of a character class.
.It Li \e
Remove the special meaning of the next character in the pattern.
.It Li ]
Mark the end of a character class.
.El
.It Fl x | Fl -discard-all
Do not copy non-global symbols to the output.
.It Fl -add-section Ar sectionname Ns = Ns Ar filename
Add a new section to the output file with name
.Ar sectionname .
The contents of the section are taken from the file named by
argument
.Ar filename .
The size of the section will be the number of bytes in file
.Ar filename .
.It Xo
.Fl -adjust-section-vma Ar section Ns {+|-|=} Ns Ar val |
.Fl -change-section-address Ar section Ns {+|-|=} Ns Ar val
.Xc
Depending on the operator specified, increase, decrease or set both
the virtual memory address and the load memory address of the section
named by the argument
.Ar section .
The argument
.Ar val
specifies the desired increment, decrement or new value for the
address.
.It Xo
.Fl -adjust-start Ns = Ns Ar increment |
.Fl -change-start Ns = Ns Ar increment
.Xc
Increase the entry point address of the output ELF object by the value
specified in the argument
.Ar increment .
.It Xo
.Fl -adjust-vma Ns = Ns Ar increment |
.Fl -change-addresses Ns = Ns Ar increment
.Xc
Increase the virtual memory address and the load memory address of all
sections by the value specified by the argument
.Ar increment .
.It Fl -adjust-warnings | Fl -change-warnings
Issue a warning if the section specified by the options
.Fl -change-section-address ,
.Fl -change-section-lma
or
.Fl -change-section-vma
does not exist in the input object.
This is the default.
.It Fl -change-section-lma Ar section Ns {+|-|=} Ns Ar val
Change or set the load memory address of the section named by the
argument
.Ar section .
Depending on the operator specified, the value in argument
.Ar val
will be used as an increment, a decrement or as the new value
of the load memory address.
.It Fl -change-section-vma Ar section Ns {+|-|=} Ns Ar val
Change or set the virtual memory address of the section named by the
argument
.Ar section .
Depending on the operator specified, the value in argument
.Ar val
will be used as an increment, a decrement or as the new value
of the virtual memory address.
.It Fl -gap-fill Ns = Ns Ar val
Fill the gaps between sections with the byte value specified by
the argument
.Ar val .
.It Fl -no-adjust-warnings | Fl -no-change-warnings
Do not issue a warning if the section specified by the options
.Fl -change-section-address ,
.Fl -change-section-lma
or
.Fl -change-section-vma
is missing in the input object.
.It Fl -only-keep-debug
Copy only debugging information to the output file.
.It Fl -pad-to Ns = Ns Ar address
Pad the load memory address of the output object to the value
specified by the argument
.Ar address
by increasing the size of the section with the highest load memory
address.
.It Fl -prefix-alloc-sections Ns = Ns Ar string
Prefix the section names of all the allocated sections with
.Ar string .
.It Fl -prefix-sections Ns = Ns Ar string
Prefix the section names of all the sections with
.Ar string .
.It Fl -prefix-symbols Ns = Ns Ar string
Prefix the symbol names of all the symbols with
.Ar string .
.It Fl -rename-section Ar oldname Ns = Ns Ar newname Ns Op Ar ,flags
Rename the section named by argument
.Ar oldname
to
.Ar newname ,
optionally changing the sections flags to that specified by argument
.Ar flags .
Allowed values for the argument
.Ar flags
are as for option
.Fl -set-section-flags
below.
.It Fl -set-section-flags Ar sectionname Ns = Ns Ar flags
Set the flags for the section named by argument
.Ar sectionname
to those specified by argument
.Ar flags .
Argument
.Ar flags
is a comma separated list of the following flag names:
.Bl -tag -width "readonly" -compact
.It alloc
The section occupies space in the output file.
.It code
The section contains machine instructions.
.It contents
This flag is accepted but is ignored.
.It data
The section contains writeable data.
.It debug
The section holds debugging information.
.It load
The section is loadable.
.It noload
The section should not be loaded into memory.
.It readonly
The section is not writable.
.It rom
The section contains ROM'able contents.
.It share
This flag is accepted but is ignored.
.El
.It Fl -set-start Ns = Ns Ar address
Set the start address of the output ELF object to the value specified
by the argument
.Ar address .
.It Fl -srec-forceS3
Only generate S-records of type
.Dq S3 .
This option is only meaningful when the output target is set to
.Dq srec .
.It Fl -srec-len Ns = Ns Ar val
Set the maximum length of an S-record line to
.Ar val .
This option is only meaningful when the output target is set to
.Dq srec .
.It Fl -strip-unneeded
Do not copy symbols that are not needed for relocation processing.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr ld 1 ,
.Xr mcs 1 ,
.Xr strip 1 ,
.Xr elf 3 ,
.Xr ar 5 ,
.Xr elf 5
.Sh HISTORY
.Nm
has been implemented by
.An "Kai Wang" Aq kaiwang27@users.sourceforge.net .

View File

@ -0,0 +1,313 @@
/*-
* Copyright (c) 2007-2013 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id: elfcopy.h 2970 2013-12-01 15:22:12Z kaiwang27 $
*/
#include <sys/queue.h>
#include <gelf.h>
#include <libelftc.h>
#include "_elftc.h"
/*
* User specified symbol operation (strip, keep, localize, globalize,
* weaken, rename, etc).
*/
struct symop {
const char *name;
const char *newname;
#define SYMOP_KEEP 0x0001U
#define SYMOP_STRIP 0x0002U
#define SYMOP_GLOBALIZE 0x0004U
#define SYMOP_LOCALIZE 0x0008U
#define SYMOP_KEEPG 0x0010U
#define SYMOP_WEAKEN 0x0020U
#define SYMOP_REDEF 0x0040U
unsigned int op;
STAILQ_ENTRY(symop) symop_list;
};
/* File containing symbol list. */
struct symfile {
dev_t dev;
ino_t ino;
size_t size;
char *data;
unsigned int op;
STAILQ_ENTRY(symfile) symfile_list;
};
/* Sections to copy/remove/rename/... */
struct sec_action {
const char *name;
const char *addopt;
const char *newname;
const char *string;
uint64_t lma;
uint64_t vma;
int64_t lma_adjust;
int64_t vma_adjust;
#define SF_ALLOC 0x0001U
#define SF_LOAD 0x0002U
#define SF_NOLOAD 0x0004U
#define SF_READONLY 0x0008U
#define SF_DEBUG 0x0010U
#define SF_CODE 0x0020U
#define SF_DATA 0x0040U
#define SF_ROM 0x0080U
#define SF_SHARED 0X0100U
#define SF_CONTENTS 0x0200U
int flags;
int add;
int append;
int compress;
int copy;
int print;
int remove;
int rename;
int setflags;
int setlma;
int setvma;
STAILQ_ENTRY(sec_action) sac_list;
};
/* Sections to add from file. */
struct sec_add {
char *name;
char *content;
size_t size;
STAILQ_ENTRY(sec_add) sadd_list;
};
struct segment;
/* Internal data structure for sections. */
struct section {
struct segment *seg; /* containing segment */
const char *name; /* section name */
char *newname; /* new section name */
Elf_Scn *is; /* input scn */
Elf_Scn *os; /* output scn */
void *buf; /* section content */
uint8_t *pad; /* section padding */
uint64_t off; /* section offset */
uint64_t sz; /* section size */
uint64_t cap; /* section capacity */
uint64_t align; /* section alignment */
uint64_t type; /* section type */
uint64_t vma; /* section virtual addr */
uint64_t lma; /* section load addr */
uint64_t pad_sz;/* section padding size */
int loadable; /* whether loadable */
int pseudo;
int nocopy;
TAILQ_ENTRY(section) sec_list; /* next section */
};
/* Internal data structure for segments. */
struct segment {
uint64_t addr; /* load addr */
uint64_t off; /* file offset */
uint64_t fsz; /* file size */
uint64_t msz; /* memory size */
uint64_t type; /* segment type */
int remove; /* whether remove */
int nsec; /* number of sections contained */
struct section **v_sec; /* list of sections contained */
STAILQ_ENTRY(segment) seg_list; /* next segment */
};
/*
* In-memory representation of ar(1) archive member(object).
*/
struct ar_obj {
char *name; /* member name */
char *buf; /* member content */
void *maddr; /* mmap start address */
uid_t uid; /* user id */
gid_t gid; /* group id */
mode_t md; /* octal file permissions */
size_t size; /* member size */
time_t mtime; /* modification time */
STAILQ_ENTRY(ar_obj) objs;
};
/*
* Structure encapsulates the "global" data for "elfcopy" program.
*/
struct elfcopy {
const char *progname; /* program name */
int iec; /* elfclass of input object */
Elftc_Bfd_Target_Flavor itf; /* flavour of input object */
Elftc_Bfd_Target_Flavor otf; /* flavour of output object */
const char *otgt; /* output target name */
int oec; /* elfclass of output object */
unsigned char oed; /* endianess of output object */
int oem; /* EM_XXX of output object */
int abi; /* OSABI of output object */
Elf *ein; /* ELF descriptor of input object */
Elf *eout; /* ELF descriptor of output object */
int iphnum; /* num. of input object phdr entries */
int ophnum; /* num. of output object phdr entries */
int nos; /* num. of output object sections */
enum {
STRIP_NONE = 0,
STRIP_ALL,
STRIP_DEBUG,
STRIP_NONDEBUG,
STRIP_UNNEEDED
} strip;
#define EXECUTABLE 0x00000001U
#define DYNAMIC 0x00000002U
#define RELOCATABLE 0x00000004U
#define SYMTAB_EXIST 0x00000010U
#define SYMTAB_INTACT 0x00000020U
#define KEEP_GLOBAL 0x00000040U
#define DISCARD_LOCAL 0x00000080U
#define WEAKEN_ALL 0x00000100U
#define PRESERVE_DATE 0x00001000U
#define SREC_FORCE_S3 0x00002000U
#define SREC_FORCE_LEN 0x00004000U
#define SET_START 0x00008000U
#define GAP_FILL 0x00010000U
#define WILDCARD 0x00020000U
#define NO_CHANGE_WARN 0x00040000U
#define SEC_ADD 0x00080000U
#define SEC_APPEND 0x00100000U
#define SEC_COMPRESS 0x00200000U
#define SEC_PRINT 0x00400000U
#define SEC_REMOVE 0x00800000U
#define SEC_COPY 0x01000000U
#define DISCARD_LLABEL 0x02000000U
int flags; /* elfcopy run control flags. */
int64_t change_addr; /* Section address adjustment. */
int64_t change_start; /* Entry point adjustment. */
uint64_t set_start; /* Entry point value. */
unsigned long srec_len; /* S-Record length. */
uint64_t pad_to; /* load address padding. */
uint8_t fill; /* gap fill value. */
char *prefix_sec; /* section prefix. */
char *prefix_alloc; /* alloc section prefix. */
char *prefix_sym; /* symbol prefix. */
char *debuglink; /* GNU debuglink file. */
struct section *symtab; /* .symtab section. */
struct section *strtab; /* .strtab section. */
struct section *shstrtab; /* .shstrtab section. */
uint64_t *secndx; /* section index map. */
uint64_t *symndx; /* symbol index map. */
unsigned char *v_rel; /* symbols needed by relocation. */
unsigned char *v_secsym; /* sections with section symbol. */
STAILQ_HEAD(, segment) v_seg; /* list of segments. */
STAILQ_HEAD(, sec_action) v_sac;/* list of section operations. */
STAILQ_HEAD(, sec_add) v_sadd; /* list of sections to add. */
STAILQ_HEAD(, symop) v_symop; /* list of symbols operations. */
STAILQ_HEAD(, symfile) v_symfile; /* list of symlist files. */
TAILQ_HEAD(, section) v_sec; /* list of sections. */
/*
* Fields for the ar(1) archive.
*/
char *as; /* buffer for archive string table. */
size_t as_sz; /* current size of as table. */
size_t as_cap; /* capacity of as table buffer. */
uint32_t s_cnt; /* current number of symbols. */
uint32_t *s_so; /* symbol offset table. */
size_t s_so_cap; /* capacity of so table buffer. */
char *s_sn; /* symbol name table */
size_t s_sn_cap; /* capacity of sn table buffer. */
size_t s_sn_sz; /* current size of sn table. */
off_t rela_off; /* offset relative to pseudo members. */
STAILQ_HEAD(, ar_obj) v_arobj; /* archive object(member) list. */
};
void add_section(struct elfcopy *_ecp, const char *_optarg);
void add_to_shstrtab(struct elfcopy *_ecp, const char *_name);
void add_to_symop_list(struct elfcopy *_ecp, const char *_name,
const char *_newname, unsigned int _op);
void add_to_symtab(struct elfcopy *_ecp, const char *_name,
uint64_t _st_value, uint64_t _st_size, uint16_t _st_shndx,
unsigned char _st_info, unsigned char _st_other, int _ndx_known);
int add_to_inseg_list(struct elfcopy *_ecp, struct section *_sec);
void adjust_addr(struct elfcopy *_ecp);
void copy_content(struct elfcopy *_ecp);
void copy_data(struct section *_s);
void copy_phdr(struct elfcopy *_ecp);
void copy_shdr(struct elfcopy *_ecp, struct section *_s, const char *_name,
int _copy, int _sec_flags);
void create_binary(int _ifd, int _ofd);
void create_elf(struct elfcopy *_ecp);
void create_elf_from_binary(struct elfcopy *_ecp, int _ifd, const char *ifn);
void create_elf_from_ihex(struct elfcopy *_ecp, int _ifd);
void create_elf_from_srec(struct elfcopy *_ecp, int _ifd);
struct section *create_external_section(struct elfcopy *_ecp, const char *_name,
char *_newname, void *_buf, uint64_t _size, uint64_t _off, uint64_t _stype,
Elf_Type _dtype, uint64_t flags, uint64_t _align, uint64_t _vma,
int _loadable);
void create_external_symtab(struct elfcopy *_ecp);
void create_ihex(int _ifd, int _ofd);
void create_scn(struct elfcopy *_ecp);
void create_srec(struct elfcopy *_ecp, int _ifd, int _ofd, const char *_ofn);
void create_symtab(struct elfcopy *_ecp);
void create_symtab_data(struct elfcopy *_ecp);
void create_tempfile(char **_fn, int *_fd);
void finalize_external_symtab(struct elfcopy *_ecp);
void free_elf(struct elfcopy *_ecp);
void free_sec_act(struct elfcopy *_ecp);
void free_sec_add(struct elfcopy *_ecp);
void free_symtab(struct elfcopy *_ecp);
void init_shstrtab(struct elfcopy *_ecp);
void insert_to_sec_list(struct elfcopy *_ecp, struct section *_sec,
int _tail);
struct section *insert_shtab(struct elfcopy *_ecp, int tail);
int is_remove_reloc_sec(struct elfcopy *_ecp, uint32_t _sh_info);
int is_remove_section(struct elfcopy *_ecp, const char *_name);
struct sec_action *lookup_sec_act(struct elfcopy *_ecp,
const char *_name, int _add);
struct symop *lookup_symop_list(struct elfcopy *_ecp, const char *_name,
unsigned int _op);
void resync_sections(struct elfcopy *_ecp);
void set_shstrtab(struct elfcopy *_ecp);
void setup_phdr(struct elfcopy *_ecp);
void update_shdr(struct elfcopy *_ecp, int _update_link);
#ifndef LIBELF_AR
int ac_detect_ar(int _ifd);
void ac_create_ar(struct elfcopy *_ecp, int _ifd, int _ofd);
#endif /* ! LIBELF_AR */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY JOSEPH KOSHY ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL JOSEPH KOSHY BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: mcs.1 2247 2011-11-29 08:41:34Z jkoshy $
.\"
.Dd November 29, 2011
.Os
.Dt MCS 1
.Sh NAME
.Nm mcs
.Nd manipulate the comment section of an ELF object
.Sh SYNOPSIS
.Nm
.Op Fl a Ar string
.Op Fl c
.Op Fl n Ar name
.Op Fl p
.Ar
.Nm
.Fl d
.Op Fl n Ar name
.Ar
.Nm
.Fl h | Fl -help
.Nm
.Fl V | Fl -version
.Sh DESCRIPTION
The
.Nm
utility is used to manipulate comment sections in an ELF object.
If a command-line argument
.Ar file
names an
.Xr ar 1
archive, then
.Nm
will operate on the ELF objects contained in the archive.
.Pp
By default
.Nm
operates on the ELF section named
.Dq .comment .
This may be changed using the
.Fl n
option.
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width ".Fl a Ar string"
.It Fl a Ar string
Append the text in
.Ar string
to the comment section.
This option may be specified multiple times.
.It Fl c
Compress the comment section by removing duplicate entries.
.It Fl d
Delete the comment section from the ELF object.
.It Fl h | Fl -help
Display a usage message and exit.
.It Fl n Ar name
Operate on the section named
.Ar name .
.It Fl p
Print the contents of the comment section.
This step is taken after actions specified by the
.Fl a
and
.Fl c
options (if any) are completed.
.It Fl V | Fl -version
Print a version identifier and exit.
.El
.Sh COMPATIBILITY
The behavior of the
.Nm
utility differs from its SVR4 counterpart in the following ways:
.Bl -bullet -compact
.It
If the
.Fl d
option is specified, it causes any
.Fl a ,
.Fl c
and
.Fl p
options present to be ignored.
.It
The order of options
.Fl a ,
.Fl c ,
.Fl d ,
and
.Fl p
on the command line is not significant.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr elfcopy 1 ,
.Xr ld 1 ,
.Xr nm 1 ,
.Xr strip 1

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,493 @@
/*-
* Copyright (c) 2007-2010,2012 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/queue.h>
#include <err.h>
#include <gelf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "elfcopy.h"
ELFTC_VCSID("$Id: segments.c 2542 2012-08-12 16:14:15Z kaiwang27 $");
static void insert_to_inseg_list(struct segment *seg, struct section *sec);
/*
* elfcopy's segment handling is relatively simpler and less powerful than
* libbfd. Program headers are modified or copied from input to output objects,
* but never re-generated. As a result, if the input object has incorrect
* program headers, the output object's program headers will remain incorrect
* or become even worse.
*/
/*
* Check whether a section is "loadable". If so, add it to the
* corresponding segment list(s) and return 1.
*/
int
add_to_inseg_list(struct elfcopy *ecp, struct section *s)
{
struct segment *seg;
int loadable;
if (ecp->ophnum == 0)
return (0);
/*
* Segment is a different view of an ELF object. One segment can
* contain one or more sections, and one section can be included
* in one or more segments, or not included in any segment at all.
* We call those sections which can be found in one or more segments
* "loadable" sections, and call the rest "unloadable" sections.
* We keep track of "loadable" sections in their containing
* segment(s)' v_sec queue. These information are later used to
* recalculate the extents of segments, when sections are removed,
* for example.
*/
loadable = 0;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (s->off < seg->off)
continue;
if (s->off + s->sz > seg->off + seg->fsz &&
s->type != SHT_NOBITS)
continue;
if (s->off + s->sz > seg->off + seg->msz)
continue;
insert_to_inseg_list(seg, s);
if (seg->type == PT_LOAD)
s->seg = seg;
s->lma = seg->addr + (s->off - seg->off);
loadable = 1;
}
return (loadable);
}
void
adjust_addr(struct elfcopy *ecp)
{
struct section *s, *s0;
struct segment *seg;
struct sec_action *sac;
uint64_t dl, lma, old_vma, start, end;
int found, i;
/*
* Apply VMA and global LMA changes in the first iteration.
*/
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
/* Only adjust loadable section's address. */
if (!s->loadable || s->seg == NULL)
continue;
/* Apply global LMA adjustment. */
if (ecp->change_addr != 0)
s->lma += ecp->change_addr;
if (!s->pseudo) {
old_vma = s->vma;
/* Apply global VMA adjustment. */
if (ecp->change_addr != 0)
s->vma += ecp->change_addr;
/* Apply section VMA adjustment. */
sac = lookup_sec_act(ecp, s->name, 0);
if (sac == NULL)
continue;
if (sac->setvma)
s->vma = sac->vma;
if (sac->vma_adjust != 0)
s->vma += sac->vma_adjust;
}
}
/*
* Apply sections LMA change in the second iteration.
*/
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
/* Only adjust loadable section's LMA. */
if (!s->loadable || s->seg == NULL)
continue;
/*
* Check if there is a LMA change request for this
* section.
*/
sac = lookup_sec_act(ecp, s->name, 0);
if (sac == NULL)
continue;
if (!sac->setlma && sac->lma_adjust == 0)
continue;
lma = s->lma;
if (sac->setlma)
lma = sac->lma;
if (sac->lma_adjust != 0)
lma += sac->lma_adjust;
if (lma == s->lma)
continue;
/*
* Check if the LMA change is viable.
*
* 1. Check if the new LMA is properly aligned accroding to
* section alignment.
*
* 2. Compute the new extent of segment that contains this
* section, make sure it doesn't overlap with other
* segments.
*/
#ifdef DEBUG
printf("LMA for section %s: %#jx\n", s->name, lma);
#endif
if (lma % s->align != 0)
errx(EXIT_FAILURE, "The load address %#jx for "
"section %s is not aligned to %ju",
(uintmax_t) lma, s->name, s->align);
if (lma < s->lma) {
/* Move section to lower address. */
if (lma < s->lma - s->seg->addr)
errx(EXIT_FAILURE, "Not enough space to move "
"section %s load address to %#jx", s->name,
(uintmax_t) lma);
start = lma - (s->lma - s->seg->addr);
if (s == s->seg->v_sec[s->seg->nsec - 1])
end = start + s->seg->msz;
else
end = s->seg->addr + s->seg->msz;
} else {
/* Move section to upper address. */
if (s == s->seg->v_sec[0])
start = lma;
else
start = s->seg->addr;
end = lma + (s->seg->addr + s->seg->msz - s->lma);
if (end < start)
errx(EXIT_FAILURE, "Not enough space to move "
"section %s load address to %#jx", s->name,
(uintmax_t) lma);
}
#ifdef DEBUG
printf("new extent for segment containing %s: (%#jx,%#jx)\n",
s->name, start, end);
#endif
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg == s->seg || seg->type != PT_LOAD)
continue;
if (start > seg->addr + seg->msz)
continue;
if (end < seg->addr)
continue;
errx(EXIT_FAILURE, "The extent of segment containing "
"section %s overlaps with segment(%#jx,%#jx)",
s->name, seg->addr, seg->addr + seg->msz);
}
/*
* Update section LMA and file offset.
*/
if (lma < s->lma) {
/*
* To move a section to lower load address, we decrease
* the load addresses of the section and all the
* sections that are before it, and we increase the
* file offsets of all the sections that are after it.
*/
dl = s->lma - lma;
for (i = 0; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->lma -= dl;
#ifdef DEBUG
printf("section %s LMA set to %#jx\n",
s0->name, (uintmax_t) s0->lma);
#endif
if (s0 == s)
break;
}
for (i = i + 1; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->off += dl;
#ifdef DEBUG
printf("section %s offset set to %#jx\n",
s0->name, (uintmax_t) s0->off);
#endif
}
} else {
/*
* To move a section to upper load address, we increase
* the load addresses of the section and all the
* sections that are after it, and we increase the
* their file offsets too unless the section in question
* is the first in its containing segment.
*/
dl = lma - s->lma;
for (i = 0; i < s->seg->nsec; i++)
if (s->seg->v_sec[i] == s)
break;
if (i >= s->seg->nsec)
errx(EXIT_FAILURE, "Internal: section `%s' not"
" found in its containing segement",
s->name);
for (; i < s->seg->nsec; i++) {
s0 = s->seg->v_sec[i];
s0->lma += dl;
#ifdef DEBUG
printf("section %s LMA set to %#jx\n",
s0->name, (uintmax_t) s0->lma);
#endif
if (s != s->seg->v_sec[0]) {
s0->off += dl;
#ifdef DEBUG
printf("section %s offset set to %#jx\n",
s0->name, (uintmax_t) s0->off);
#endif
}
}
}
}
/*
* Apply load address padding.
*/
if (ecp->pad_to != 0) {
/*
* Find the section with highest load address.
*/
s = NULL;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg->type != PT_LOAD)
continue;
for (i = seg->nsec - 1; i >= 0; i--)
if (seg->v_sec[i]->type != SHT_NOBITS)
break;
if (i < 0)
continue;
if (s == NULL)
s = seg->v_sec[i];
else {
s0 = seg->v_sec[i];
if (s0->lma > s->lma)
s = s0;
}
}
if (s == NULL)
goto issue_warn;
/* No need to pad if the pad_to address is lower. */
if (ecp->pad_to <= s->lma + s->sz)
goto issue_warn;
s->pad_sz = ecp->pad_to - (s->lma + s->sz);
#ifdef DEBUG
printf("pad section %s load to address %#jx by %#jx\n", s->name,
(uintmax_t) ecp->pad_to, (uintmax_t) s->pad_sz);
#endif
}
issue_warn:
/*
* Issue a warning if there are VMA/LMA adjust requests for
* some nonexistent sections.
*/
if ((ecp->flags & NO_CHANGE_WARN) == 0) {
STAILQ_FOREACH(sac, &ecp->v_sac, sac_list) {
if (!sac->setvma && !sac->setlma &&
!sac->vma_adjust && !sac->lma_adjust)
continue;
found = 0;
TAILQ_FOREACH(s, &ecp->v_sec, sec_list) {
if (s->pseudo || s->name == NULL)
continue;
if (!strcmp(s->name, sac->name)) {
found = 1;
break;
}
}
if (!found)
warnx("cannot find section `%s'", sac->name);
}
}
}
static void
insert_to_inseg_list(struct segment *seg, struct section *sec)
{
struct section *s;
int i;
seg->nsec++;
seg->v_sec = realloc(seg->v_sec, seg->nsec * sizeof(*seg->v_sec));
if (seg->v_sec == NULL)
err(EXIT_FAILURE, "realloc failed");
/*
* Sort the section in order of offset.
*/
for (i = seg->nsec - 1; i > 0; i--) {
s = seg->v_sec[i - 1];
if (sec->off >= s->off) {
seg->v_sec[i] = sec;
break;
} else
seg->v_sec[i] = s;
}
if (i == 0)
seg->v_sec[0] = sec;
}
void
setup_phdr(struct elfcopy *ecp)
{
struct segment *seg;
GElf_Phdr iphdr;
size_t iphnum;
int i;
if (elf_getphnum(ecp->ein, &iphnum) == 0)
errx(EXIT_FAILURE, "elf_getphnum failed: %s",
elf_errmsg(-1));
ecp->ophnum = ecp->iphnum = iphnum;
if (iphnum == 0)
return;
/* If --only-keep-debug is specified, discard all program headers. */
if (ecp->strip == STRIP_NONDEBUG) {
ecp->ophnum = 0;
return;
}
for (i = 0; (size_t)i < iphnum; i++) {
if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
if ((seg = calloc(1, sizeof(*seg))) == NULL)
err(EXIT_FAILURE, "calloc failed");
seg->addr = iphdr.p_vaddr;
seg->off = iphdr.p_offset;
seg->fsz = iphdr.p_filesz;
seg->msz = iphdr.p_memsz;
seg->type = iphdr.p_type;
STAILQ_INSERT_TAIL(&ecp->v_seg, seg, seg_list);
}
}
void
copy_phdr(struct elfcopy *ecp)
{
struct segment *seg;
struct section *s;
GElf_Phdr iphdr, ophdr;
int i;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (seg->type == PT_PHDR) {
if (!TAILQ_EMPTY(&ecp->v_sec)) {
s = TAILQ_FIRST(&ecp->v_sec);
if (s->pseudo)
seg->addr = s->lma +
gelf_fsize(ecp->eout, ELF_T_EHDR,
1, EV_CURRENT);
}
seg->fsz = seg->msz = gelf_fsize(ecp->eout, ELF_T_PHDR,
ecp->ophnum, EV_CURRENT);
continue;
}
seg->fsz = seg->msz = 0;
for (i = 0; i < seg->nsec; i++) {
s = seg->v_sec[i];
seg->msz = s->off + s->sz - seg->off;
if (s->type != SHT_NOBITS)
seg->fsz = seg->msz;
}
}
/*
* Allocate space for program headers, note that libelf keep
* track of the number in internal variable, and a call to
* elf_update is needed to update e_phnum of ehdr.
*/
if (gelf_newphdr(ecp->eout, ecp->ophnum) == NULL)
errx(EXIT_FAILURE, "gelf_newphdr() failed: %s",
elf_errmsg(-1));
/*
* This elf_update() call is to update the e_phnum field in
* ehdr. It's necessary because later we will call gelf_getphdr(),
* which does sanity check by comparing ndx argument with e_phnum.
*/
if (elf_update(ecp->eout, ELF_C_NULL) < 0)
errx(EXIT_FAILURE, "elf_update() failed: %s", elf_errmsg(-1));
/*
* iphnum == ophnum, since we don't remove program headers even if
* they no longer contain sections.
*/
i = 0;
STAILQ_FOREACH(seg, &ecp->v_seg, seg_list) {
if (i >= ecp->iphnum)
break;
if (gelf_getphdr(ecp->ein, i, &iphdr) != &iphdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
if (gelf_getphdr(ecp->eout, i, &ophdr) != &ophdr)
errx(EXIT_FAILURE, "gelf_getphdr failed: %s",
elf_errmsg(-1));
ophdr.p_type = iphdr.p_type;
ophdr.p_vaddr = seg->addr;
ophdr.p_paddr = seg->addr;
ophdr.p_flags = iphdr.p_flags;
ophdr.p_align = iphdr.p_align;
ophdr.p_offset = seg->off;
ophdr.p_filesz = seg->fsz;
ophdr.p_memsz = seg->msz;
if (!gelf_update_phdr(ecp->eout, i, &ophdr))
err(EXIT_FAILURE, "gelf_update_phdr failed :%s",
elf_errmsg(-1));
i++;
}
}

View File

@ -0,0 +1,132 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY JOSEPH KOSHY ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL JOSEPH KOSHY BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: strip.1 2069 2011-10-26 15:53:48Z jkoshy $
.\"
.Dd September 17, 2011
.Os
.Dt STRIP 1
.Sh NAME
.Nm strip
.Nd discard information from ELF objects
.Sh SYNOPSIS
.Nm
.Op Fl d | Fl g | Fl S | Fl -strip-debug
.Op Fl h | Fl -help
.Op Fl -only-keep-debug
.Op Fl o Ar outputfile | Fl -output-file= Ns Ar outputfile
.Op Fl p | Fl -preserve-dates
.Op Fl s | Fl -strip-all
.Op Fl -strip-unneeded
.Op Fl w | Fl -wildcard
.Op Fl x | Fl -discard-all
.Op Fl I Ar format | Fl -input-target= Ns Ar format
.Op Fl K Ar symbol | Fl -keep-symbol= Ns Ar symbol
.Op Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbol
.Op Fl O Ar format | Fl -output-target= Ns Ar format
.Op Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
.Op Fl V | Fl -version
.Op Fl X | Fl -discard-locals
.Ar
.Sh DESCRIPTION
The
.Nm
utility is used to discard information from ELF objects.
.Pp
The
.Nm
utility supports the following options:
.Bl -tag -width indent
.It Fl d | Fl g | Fl S | Fl -strip-debug
Remove debugging symbols only.
.It Fl h | Fl -help
Print a help message and exit.
.It Fl -only-keep-debug
Remove all content except that which would be used for debugging.
.It Fl o Ar outputfile | Fl -output-file= Ns Ar outputfile
Write the stripped object to file
.Ar outputfile .
The default behaviour is to modify objects in place.
.It Fl p | Fl -preserve-dates
Preserve the object's access and modification times.
.It Fl s | Fl -strip-all
Remove all symbols.
.It Fl -strip-unneeded
Remove all symbols not needed for further relocation processing.
.It Fl w | Fl -wildcard
Use shell-style patterns to name symbols.
The following meta-characters are recognized in patterns:
.Bl -tag -width "...." -compact
.It Li !
If this is the first character of the pattern, invert the sense of the
pattern match.
.It Li *
Matches any string of characters in a symbol name.
.It Li ?
Matches zero or one character in a symbol name.
.It Li [
Mark the start of a character class.
.It Li \e
Remove the special meaning of the next character in the pattern.
.It Li ]
Mark the end of a character class.
.El
.It Fl x | Fl -discard-all
Discard all non-global symbols.
.It Fl I Ar format | Fl -input-target= Ns Ar format
These options are accepted, but are ignored.
.It Fl K Ar symbol | Fl -keep-symbol= Ns Ar symbol
Keep the symbol
.Ar symbol
even if it would otherwise be stripped.
This option may be specified multiple times.
.It Fl N Ar symbol | Fl -strip-symbol= Ns Ar symbol
Remove the symbol
.Ar symbol
even if it would otherwise have been kept.
This option may be specified multiple times.
.It Fl O Ar format | Fl -output-target= Ns Ar format
Set the output file format to
.Ar format .
For the full list of supported formats, please see the documentation
for function
.Xr elftc_bfd_find_target 3 .
.It Fl R Ar sectionname | Fl -remove-section= Ns Ar sectionname
Remove the section named by the argument
.Ar sectionname .
This option may be specified multiple times.
.It Fl V | Fl -version
Print a version identifier and exit.
.It Fl X | Fl -discard-locals
Remove compiler-generated local symbols.
.El
.Sh DIAGNOSTICS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr elfcopy 1 ,
.Xr ld 1 ,
.Xr mcs 1 ,
.Xr elf 3 ,
.Xr elftc_bfd_find_target 3 ,
.Xr fnmatch 3

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
# $Id: Makefile 2859 2013-01-05 09:21:54Z jkoshy $
TOP= ${.CURDIR}/..
LIB= elftc
SRCS= elftc_bfdtarget.c \
elftc_copyfile.c \
elftc_demangle.c \
elftc_set_timestamps.c \
elftc_string_table.c \
elftc_version.c \
libelftc_bfdtarget.c \
libelftc_dem_arm.c \
libelftc_dem_gnu2.c \
libelftc_dem_gnu3.c \
libelftc_hash.c \
libelftc_vstr.c
INCS= libelftc.h
INCSDIR= /usr/include
RELEASE= HEAD # Change this on release branches.
SHLIB_MAJOR= 1
WARNS?= 6
CLEANFILES+= elftc_version.c
LDADD+= -lelf
MAN= elftc.3 \
elftc_bfd_find_target.3 \
elftc_copyfile.3 \
elftc_demangle.3 \
elftc_set_timestamps.3 \
elftc_string_table_create.3 \
elftc_version.3
MLINKS= elftc_bfd_find_target.3 elftc_bfd_target_byteorder.3 \
elftc_bfd_find_target.3 elftc_bfd_target_class.3 \
elftc_bfd_find_target.3 elftc_bfd_target_flavor.3 \
elftc_string_table_create.3 elftc_string_table_from_section.3 \
elftc_string_table_create.3 elftc_string_table_destroy.3 \
elftc_string_table_create.3 elftc_string_table_image.3 \
elftc_string_table_create.3 elftc_string_table_insert.3 \
elftc_string_table_create.3 elftc_string_table_lookup.3 \
elftc_symbol_table_create.3 elftc_symbol_table_create_nested.3 \
elftc_symbol_table_create.3 elftc_symbol_table_delete_name.3 \
elftc_symbol_table_create.3 elftc_symbol_table_delete_entry.3 \
elftc_symbol_table_create.3 elftc_symbol_table_destroy.3 \
elftc_symbol_table_create.3 elftc_symbol_table_from_section.3 \
elftc_symbol_table_create.3 elftc_symbol_table_insert.3 \
elftc_symbol_table_create.3 elftc_symbol_table_iterate.3 \
elftc_symbol_table_create.3 elftc_symbol_table_lookup.3 \
elftc_symbol_table_create.3 elftc_symbol_table_to_image.3
.if !make(clean) && !make(clobber)
.BEGIN: .SILENT
${.CURDIR}/make-toolchain-version -t ${TOP} -r ${RELEASE} \
-h ${OS_HOST}
.endif
.include "${TOP}/mk/elftoolchain.lib.mk"

View File

@ -0,0 +1,18 @@
/*
* $Id: Version.map 2574 2012-09-11 15:11:59Z jkoshy $
*/
R1.0 {
global:
elftc_bfd_find_target;
elftc_bfd_target_byteorder;
elftc_bfd_target_class;
elftc_bfd_target_flavor;
elftc_bfd_target_machine;
elftc_copyfile;
elftc_demangle;
elftc_set_timestamps;
elftc_version;
local:
*;
};

View File

@ -0,0 +1,89 @@
/*-
* Copyright (c) 2009 Kai Wang
* Copyright (c) 2007,2008 Hyogeol Lee <hyogeollee@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: _libelftc.h 2856 2013-01-04 16:00:26Z jkoshy $
*/
#ifndef __LIBELFTC_H_
#define __LIBELFTC_H_
#include <stdbool.h>
#include "_elftc.h"
struct _Elftc_Bfd_Target {
const char *bt_name; /* target name. */
unsigned int bt_type; /* target type. */
unsigned int bt_byteorder; /* elf target byteorder. */
unsigned int bt_elfclass; /* elf target class (32/64bit). */
unsigned int bt_machine; /* elf target arch. */
unsigned int bt_osabi; /* elf target abi. */
};
extern struct _Elftc_Bfd_Target _libelftc_targets[];
/** @brief Dynamic vector data for string. */
struct vector_str {
/** Current size */
size_t size;
/** Total capacity */
size_t capacity;
/** String array */
char **container;
};
#define BUFFER_GROWFACTOR 1.618
#define ELFTC_FAILURE 0
#define ELFTC_ISDIGIT(C) (isdigit((C) & 0xFF))
#define ELFTC_SUCCESS 1
#define VECTOR_DEF_CAPACITY 8
__BEGIN_DECLS
char *cpp_demangle_ARM(const char *_org);
char *cpp_demangle_gnu2(const char *_org);
char *cpp_demangle_gnu3(const char *_org);
bool is_cpp_mangled_ARM(const char *_org);
bool is_cpp_mangled_gnu2(const char *_org);
bool is_cpp_mangled_gnu3(const char *_org);
unsigned int libelftc_hash_string(const char *);
void vector_str_dest(struct vector_str *_vec);
int vector_str_find(const struct vector_str *_vs, const char *_str,
size_t _len);
char *vector_str_get_flat(const struct vector_str *_vs, size_t *_len);
bool vector_str_init(struct vector_str *_vs);
bool vector_str_pop(struct vector_str *_vs);
bool vector_str_push(struct vector_str *_vs, const char *_str,
size_t _len);
bool vector_str_push_vector_head(struct vector_str *_dst,
struct vector_str *_org);
char *vector_str_substr(const struct vector_str *_vs, size_t _begin,
size_t _end, size_t *_rlen);
__END_DECLS
#endif /* __LIBELFTC_H */

View File

@ -0,0 +1,83 @@
.\" Copyright (c) 2012 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc.3 2818 2012-12-24 12:32:48Z jkoshy $
.\"
.Dd December 24, 2012
.Os
.Dt ELFTC 3
.Sh NAME
.Nm elftc
.Nd support routines used in the Elftoolchain project
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Sh DESCRIPTION
The
.Lb libelftc
provides support routines used for developing the utilities in the
Elftoolchain source tree.
.Pp
This manual page serves as an overview of the functionality in this
library.
Additional reference information may be found in the individual
manual pages for the functions listed below.
.Ss Functional Grouping
.Bl -tag -width indent
.It "Binary Object Handling"
.Bl -tag -compact
.It Fn elftc_bfd_find_target
Locate a binary object descriptor.
.It Fn elftc_bfd_target_class
Query the ELF class for a binary object descriptor.
.It Fn elftc_bfd_target_byteorder
Query the byte order for a binary object descriptor.
.It Fn elftc_bfd_target_flavor
Query the object format for a binary object descriptor.
.It Fn elftc_bfd_target_machine
Query the target machine for a binary object descriptor.
.El
.It "C++ support"
.Bl -tag -compact
.It Fn elftc_demangle
Decodes a symbol name encoded according to the encoding rules for the
C++ language.
.El
.It "Programming conveniences"
.Bl -tag -compact
.It Fn elftc_copyfile
Copies the contents of a file to another.
.It Fn elftc_set_timestamp
Portably set the time stamps on a file.
.El
.It "Project Configuration"
.Bl -tag -compact
.It Fn elftc_version
Returns a project-wide identifier string that encodes the source
revision of the source tree.
.El
.El
.Sh SEE ALSO
.Xr dwarf 3 ,
.Xr elf 3

View File

@ -0,0 +1,189 @@
.\" Copyright (c) 2010-2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_bfd_find_target.3 2251 2011-11-30 16:50:06Z jkoshy $
.\"
.Dd November 30, 2011
.Os
.Dt ELFTC_BFD_FIND_TARGET
.Sh NAME
.Nm elftc_bfd_find_target ,
.Nm elftc_bfd_target_byteorder ,
.Nm elftc_bfd_target_class ,
.Nm elftc_bfd_target_flavor ,
.Nm elftc_bfd_target_machine
.Nd binary object descriptor handling
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Vt struct Elftc_Bfd_Target;
.Ft "Elftc_Bfd_Target *"
.Fn elftc_bfd_find_target "const char *target_name"
.Ft "unsigned int"
.Fn elftc_bfd_target_class "Elftc_Bfd_Target *target"
.Ft "unsigned int"
.Fn elftc_bfd_target_byteorder "Elftc_Bfd_Target *target"
.Ft Elftc_Bfd_Target_Flavor
.Fn elftc_bfd_target_flavor "Elftc_Bfd_Target *target"
.Ft "unsigned int"
.Fn elftc_bfd_target_machine "Elftc_Bfd_Target *target"
.Sh DESCRIPTION
Function
.Fn elftc_bfd_find_target
locates a binary object descriptor corresponding to the descriptor
name in argument
.Ar "target_name" .
Binary object descriptors encapsulate properties of an object format
such as its file representation, ELF class, and byte endianness.
.Pp
Known descriptor names and their properties include:
.Bl -column -offset "XXXX" ".Li elf32-x86-64-freebsd" "Object format" "Byte Order" "ELF Class"
.It Em Name Ta Em "Object Format" Ta Em "Byte Order" Ta Em "ELF Class"
.It Li binary Ta Binary Ta - Ta -
.It Li elf32-avr Ta ELF Ta LSB Ta 32
.It Li elf32-big Ta ELF Ta MSB Ta 32
.It Li elf32-bigarm Ta ELF Ta MSB Ta 32
.It Li elf32-bigmips Ta ELF Ta MSB Ta 32
.It Li elf32-i386 Ta ELF Ta LSB Ta 32
.It Li elf32-i386-freebsd Ta ELF Ta LSB Ta 32
.It Li elf32-ia64-big Ta ELF Ta MSB Ta 32
.It Li elf32-little Ta ELF Ta LSB Ta 32
.It Li elf32-littlearm Ta ELF Ta LSB Ta 32
.It Li elf32-littlemips Ta ELF Ta LSB Ta 32
.It Li elf32-powerpc Ta ELF Ta MSB Ta 32
.It Li elf32-powerpcle Ta ELF Ta LSB Ta 32
.It Li elf32-sh Ta ELF Ta MSB Ta 32
.It Li elf32-shl Ta ELF Ta LSB Ta 32
.It Li elf32-sh-nbsd Ta ELF Ta MSB Ta 32
.It Li elf32-shl-nbsd Ta ELF Ta LSB Ta 32
.It Li elf32-shbig-linux Ta ELF Ta MSB Ta 32
.It Li elf32-shl-linux Ta ELF Ta LSB Ta 32
.It Li elf32-sparc Ta ELF Ta MSB Ta 32
.It Li elf64-alpha Ta ELF Ta LSB Ta 64
.It Li elf64-alpha-freebsd Ta ELF Ta LSB Ta 64
.It Li elf64-big Ta ELF Ta MSB Ta 64
.It Li elf64-bigmips Ta ELF Ta MSB Ta 64
.It Li elf64-ia64-big Ta ELF Ta MSB Ta 64
.It Li elf64-ia64-little Ta ELF Ta LSB Ta 64
.It Li elf64-little Ta ELF Ta LSB Ta 64
.It Li elf64-littlemips Ta ELF Ta LSB Ta 64
.It Li elf64-powerpc Ta ELF Ta MSB Ta 64
.It Li elf64-powerpcle Ta ELF Ta LSB Ta 64
.It Li elf64-sh64 Ta ELF Ta MSB Ta 64
.It Li elf64-sh64l Ta ELF Ta LSB Ta 64
.It Li elf64-sh64-nbsd Ta ELF Ta MSB Ta 64
.It Li elf64-sh64l-nbsd Ta ELF Ta LSB Ta 64
.It Li elf64-sh64big-linux Ta ELF Ta MSB Ta 64
.It Li elf64-sh64-linux Ta ELF Ta LSB Ta 64
.It Li elf64-sparc Ta ELF Ta MSB Ta 64
.It Li elf64-sparc-freebsd Ta ELF Ta MSB Ta 64
.It Li elf64-x86-64 Ta ELF Ta LSB Ta 64
.It Li elf64-x86-64-freebsd Ta ELF Ta LSB Ta 64
.It Li ihex Ta IHEX Ta - Ta -
.It Li srec Ta SREC Ta - Ta -
.It Li symbolsrec Ta SREC Ta - Ta -
.El
.Pp
Function
.Fn elftc_bfd_target_byteorder
returns the ELF byte order associated with target descriptor
.Ar target .
.Pp
Function
.Fn elftc_bfd_target_class
returns the ELF class associated with target descriptor
.Ar target .
.Pp
Function
.Fn elftc_bfd_target_flavor
returns the object format associated with target descriptor
.Ar target .
The known object formats are:
.Bl -tag -offset "XXXX" -width ".Dv ETF_BINARY" -compact
.It Dv ETF_ELF
An ELF object.
.It Dv ETF_BINARY
Raw binary.
.It Dv ETF_IHEX
An object encoded in
.Tn Intel
hex format.
.It Dv ETF_NONE
An unknown object format.
.It Dv ETF_SREC
An object encoded as S-records.
.El
.Sh RETURN VALUES
Function
.Fn elftc_bfd_find_target
returns a valid pointer to an opaque binary target descriptor if
successful, or NULL in case of an error.
.Pp
Function
.Fn elftc_bfd_target_byteorder
returns the ELF byte order associated with the target descriptor; one of
.Dv ELFDATA2MSB
or
.Dv ELFDATA2LSB .
.Pp
Function
.Fn elftc_bfd_target_class
returns the ELF class associated with the target descriptor; one of
.Dv ELFCLASS32
or
.Dv ELFCLASS64 .
.Pp
Function
.Fn elftc_bfd_target_machine
returns the ELF architecture associated with the target descriptor.
.Pp
Function
.Fn elftc_bfd_target_flavor
returns one of
.Dv ETF_BINARY ,
.Dv ETF_ELF ,
.Dv ETF_IHEX
or
.Dv ETF_SREC
if successful or
.Dv ETF_NONE
in case of error.
.Sh EXAMPLES
To return descriptor information associated with target name
.Dq elf64-big
use:
.Bd -literal -offset indent
struct Elftc_Bfd_Target *t;
if ((t = elftc_bfd_find_target("elf64-big")) == NULL)
errx(EXIT_FAILURE, "Cannot find target descriptor");
printf("Class: %s\\n", elftc_bfd_target_class(t) == ELFCLASS32 ?
"ELFCLASS32" : "ELFCLASS64");
printf("Byteorder: %s\\n",
elftc_bfd_target_byteorder(t) == ELFDATA2LSB ? "LSB" : "MSB");
printf("Flavor: %d\\n", elftc_bfd_target_flavor(t));
.Ed
.Sh SEE ALSO
.Xr elf 3

View File

@ -0,0 +1,75 @@
/*-
* Copyright (c) 2008,2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <string.h>
#include <libelftc.h>
#include "_libelftc.h"
ELFTC_VCSID("$Id: elftc_bfdtarget.c 2251 2011-11-30 16:50:06Z jkoshy $");
Elftc_Bfd_Target *
elftc_bfd_find_target(const char *tgt_name)
{
Elftc_Bfd_Target *tgt;
for (tgt = _libelftc_targets; tgt->bt_name; tgt++)
if (!strcmp(tgt_name, tgt->bt_name))
return (tgt);
return (NULL); /* not found */
}
Elftc_Bfd_Target_Flavor
elftc_bfd_target_flavor(Elftc_Bfd_Target *tgt)
{
return (tgt->bt_type);
}
unsigned int
elftc_bfd_target_byteorder(Elftc_Bfd_Target *tgt)
{
return (tgt->bt_byteorder);
}
unsigned int
elftc_bfd_target_class(Elftc_Bfd_Target *tgt)
{
return (tgt->bt_elfclass);
}
unsigned int
elftc_bfd_target_machine(Elftc_Bfd_Target *tgt)
{
return (tgt->bt_machine);
}

View File

@ -0,0 +1,73 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_copyfile.3 2315 2011-12-11 09:28:55Z jkoshy $
.\"
.Dd December 11, 2011
.Os
.Dt ELFTC_COPYFILE 3
.Sh NAME
.Nm elftc_copyfile
.Nd convenience function to copy data
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Ft in
.Fn elftc_copyfile "int ifd" "int ofd"
.Sh DESCRIPTION
The function
.Fn elftc_copyfile
copies the contents of the file referenced by argument
.Ar ifd
to the file referenced by argument
.Ar ofd .
.Pp
The argument
.Ar ifd
should contain a file descriptor opened for reading, with its file
offset at the beginning of the file.
.Pp
The argument
.Ar ofd
should contain a file descriptor opened for writing.
.Sh RETURN VALUE
.Rv -std
.Sh ERRORS
The function
.Fn elftc_copyfile
may fail with any of the errors returned by
.Xr fstat 2 ,
.Xr malloc 3 ,
.Xr mmap 2 ,
.Xr munmap 2 ,
.Xr read 2
or
.Xr write 2 .
.Sh SEE ALSO
.Xr fstat 2 ,
.Xr malloc 3 ,
.Xr mmap 2 ,
.Xr munmap 2 ,
.Xr read 2 ,
.Xr write 2

View File

@ -0,0 +1,107 @@
/*-
* Copyright (c) 2011, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include "libelftc.h"
#include "_libelftc.h"
#if ELFTC_HAVE_MMAP
#include <sys/mman.h>
#endif
ELFTC_VCSID("$Id: elftc_copyfile.c 2318 2011-12-11 10:54:27Z jkoshy $");
/*
* Copy the contents referenced by 'ifd' to 'ofd'. Returns 0 on
* success and -1 on error.
*/
int
elftc_copyfile(int ifd, int ofd)
{
int buf_mmapped;
struct stat sb;
char *b, *buf;
ssize_t nw;
size_t n;
/* Determine the input file's size. */
if (fstat(ifd, &sb) < 0)
return (-1);
/* Skip files without content. */
if (sb.st_size == 0)
return (0);
buf = NULL;
buf_mmapped = 0;
#if ELFTC_HAVE_MMAP
/*
* Prefer mmap() if it is available.
*/
buf = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, ifd, (off_t) 0);
if (buf != MAP_FAILED)
buf_mmapped = 1;
else
buf = NULL;
#endif
/*
* If mmap() is not available, or if the mmap() operation
* failed, allocate a buffer, and read in input data.
*/
if (buf == NULL) {
if ((buf = malloc(sb.st_size)) == NULL)
return (-1);
if (read(ifd, buf, sb.st_size) != sb.st_size)
return (-1);
}
/*
* Write data to the output file descriptor.
*/
for (n = sb.st_size, b = buf; n > 0; n -= nw, b += nw)
if ((nw = write(ofd, b, n)) <= 0)
break;
/* Release the input buffer. */
#if ELFTC_HAVE_MMAP
if (buf_mmapped && munmap(buf, sb.st_size) < 0)
return (-1);
#endif
if (!buf_mmapped)
free(buf);
return (n > 0 ? -1 : 0);
}

View File

@ -0,0 +1,116 @@
.\" Copyright (c) 2009,2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_demangle.3 2065 2011-10-26 15:24:47Z jkoshy $
.\"
.Dd August 24, 2011
.Os
.Dt ELFTC_DEMANGLE 3
.Sh NAME
.Nm elftc_demangle
.Nd demangle a C++ name
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Ft int
.Fo elftc_demangle
.Fa "const char *encodedname"
.Fa "char *buffer"
.Fa "size_t bufsize"
.Fa "unsigned int flags"
.Fc
.Sh DESCRIPTION
Function
.Fn elftc_demangle
decodes a symbol name encoded according to the type encoding rules
for the C++ language and returns a string denoting an equivalent
C++ prototype.
.Pp
Argument
.Ar encodedname
specifies the encoded symbol name.
Argument
.Ar buffer
denotes a programmer-specified area to place the prototype string in.
Argument
.Ar bufsize
specifies the size of the programmer-specified area.
Argument
.Ar flags
specifies the encoding style in use for argument
.Ar encodedname .
Supported encoding styles are:
.Bl -tag -width ".Dv ELFTC_DEM_GNU3"
.It Dv ELFTC_DEM_ARM
The encoding style used by compilers adhering to the conventions of the
C++ Annotated Reference Manual.
.It Dv ELFTC_DEM_GNU2
The encoding style by GNU C++ version 2.
.It Dv ELFTC_DEM_GNU3
The encoding style by GNU C++ version 3 and later.
.El
.Pp
Argument
.Ar flags
may be zero, in which case the function will attempt to guess the
encoding scheme from the contents of
.Ar encodedname .
.Sh RETURN VALUE
Function
.Fn elftc_demangle
returns 0 on success.
In case of an error it returns -1 and sets the
.Va errno
variable.
.Sh EXAMPLES
To decode a name that uses an unknown encoding style use:
.Bd -literal -offset indent
char buffer[1024];
const char *funcname;
funcname = ...; /* points to string to be demangled */
if (elftc_demangle(funcname, buffer, sizeof(buffer), 0) == 0)
printf("Demangled name: %\\n", buffer);
else
perror("Cannot demangle %s", funcname);
.Ed
.Sh ERRORS
Function
.Fn elftc_demangle
may fail with the following errors:
.Bl -tag -width ".Bq Er ENAMETOOLONG"
.It Bq Er EINVAL
Argument
.Ar encodedname
was not a valid encoded name.
.It Bq Er ENAMETOOLONG
The output buffer specified by arguments
.Ar buffer
and
.Ar bufsize
was too small to hold the decoded function prototype.
.El
.Sh SEE ALSO
.Xr elf 3 ,
.Xr elf_strptr 3

View File

@ -0,0 +1,110 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <assert.h>
#include <errno.h>
#include <libelf.h>
#include <libelftc.h>
#include <stdlib.h>
#include <string.h>
#include "_libelftc.h"
ELFTC_VCSID("$Id: elftc_demangle.c 2065 2011-10-26 15:24:47Z jkoshy $");
static int
is_mangled(const char *s, int style)
{
switch (style) {
case ELFTC_DEM_ARM: return (is_cpp_mangled_ARM(s) ? style : 0);
case ELFTC_DEM_GNU2: return (is_cpp_mangled_gnu2(s) ? style : 0);
case ELFTC_DEM_GNU3: return (is_cpp_mangled_gnu3(s) ? style : 0);
}
/* No style or invalid style spcified, try to guess. */
if (is_cpp_mangled_gnu3(s))
return (ELFTC_DEM_GNU3);
if (is_cpp_mangled_gnu2(s))
return (ELFTC_DEM_GNU2);
if (is_cpp_mangled_ARM(s))
return (ELFTC_DEM_ARM);
/* Cannot be demangled. */
return (0);
}
static char *
demangle(const char *s, int style, int rc)
{
(void) rc; /* XXX */
switch (style) {
case ELFTC_DEM_ARM: return (cpp_demangle_ARM(s));
case ELFTC_DEM_GNU2: return (cpp_demangle_gnu2(s));
case ELFTC_DEM_GNU3: return (cpp_demangle_gnu3(s));
default:
assert(0);
return (NULL);
}
}
int
elftc_demangle(const char *mangledname, char *buffer, size_t bufsize,
unsigned int flags)
{
int style, rc;
char *rlt;
style = flags & 0xFFFF;
rc = flags >> 16;
if (mangledname == NULL ||
((style = is_mangled(mangledname, style)) == 0)) {
errno = EINVAL;
return (-1);
}
if ((rlt = demangle(mangledname, style, rc)) == NULL) {
errno = EINVAL;
return (-1);
}
if (buffer == NULL || bufsize < strlen(rlt) + 1) {
free(rlt);
errno = ENAMETOOLONG;
return (-1);
}
strncpy(buffer, rlt, bufsize);
buffer[bufsize - 1] = '\0';
free(rlt);
return (0);
}

View File

@ -0,0 +1,84 @@
.\" Copyright (c) 2011 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id$
.\"
.Dd December 15, 2011
.Os
.Dt ELFTC_SET_TIMESTAMPS 3
.Sh NAME
.Nm elftc_set_timestamps
.Nd set file timestamps
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Ft int
.Fn elftc_set_timestamps "const char *filename" "struct stat *sb"
.Sh DESCRIPTION
The
.Fn elftc_set_timestamps
function is used to set the access and modified time stamps on a file
based on the contents of a
.Vt "struct stat"
descriptor.
.Pp
Argument
.Ar filename
names an existing file in the file system.
.Pp
Argument
.Ar sb
points to structure of type
.Vt "struct stat"
populated by a prior call to
.Xr fstat 2
or
.Xr stat 2 .
.Sh IMPLEMENTATION NOTES
This function will invoke the high-resolution
.Xr utimes 2
system call if the underlying operating system supports it.
On operating systems lacking support for
.Xr utimes 2 ,
the function will use lower resolution
.Xr utime 2
system call.
.Sh EXAMPLES
To set the access and modified times for a new file to those of an
existing file, use:
.Bd -literal -offset indent
struct stat sb;
const char *existing_filename, *new_filename;
if (stat(existing_filename, &sb) < 0)
err(EXIT_FAILURE, "stat failed");
if (elftc_set_timestamps(new_filename, &sb) < 0)
err(EXIT_FAILURE, "timestamps could not be set");
.Ed
.Sh SEE ALSO
.Xr fstat 2 ,
.Xr stat 2 ,
.Xr utime 2 ,
.Xr utimes 2 .

View File

@ -0,0 +1,85 @@
/*-
* Copyright (c) 2011 Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include "libelftc.h"
#include "_libelftc.h"
ELFTC_VCSID("$Id$");
/*
* Determine the field name for the timestamp fields inside a 'struct
* stat'.
*/
#if defined(__FreeBSD__) || defined(__NetBSD__)
#define ATIME st_atimespec
#define MTIME st_mtimespec
#define LIBELFTC_HAVE_UTIMES 1
#endif
#if defined(__DragonFly__) || defined(__linux__) || defined(__OpenBSD__)
#define ATIME st_atim
#define MTIME st_mtim
#define LIBELFTC_HAVE_UTIMES 1
#endif
#if LIBELFTC_HAVE_UTIMES
#include <sys/time.h>
#else
#include <utime.h>
#endif
int
elftc_set_timestamps(const char *fn, struct stat *sb)
{
#if LIBELFTC_HAVE_UTIMES
/*
* The BSD utimes() system call offers timestamps
* 1-microsecond granularity.
*/
struct timeval tv[2];
tv[0].tv_sec = sb->ATIME.tv_sec;
tv[0].tv_usec = sb->ATIME.tv_nsec / 1000;
tv[1].tv_sec = sb->MTIME.tv_sec;
tv[1].tv_usec = sb->MTIME.tv_nsec / 1000;
return (utimes(fn, tv));
#else
/*
* On OSes without utimes(), fall back to the POSIX utime()
* call, which offers 1-second granularity.
*/
struct utimbuf utb;
utb.actime = sb->st_atime;
utb.modtime = sb->st_mtime;
return (utime(fn, &utb));
#endif
}

View File

@ -0,0 +1,392 @@
/*-
* Copyright (c) 2013, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/param.h>
#include <sys/queue.h>
#include <assert.h>
#include <errno.h>
#include <gelf.h>
#include <stdlib.h>
#include <string.h>
#include "libelftc.h"
#include "_libelftc.h"
ELFTC_VCSID("$Id: elftc_string_table.c 2869 2013-01-06 13:29:18Z jkoshy $");
#define ELFTC_STRING_TABLE_DEFAULT_SIZE (4*1024)
#define ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE 16
#define ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH 8
#define ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT (4*1024)
struct _Elftc_String_Table_Entry {
int ste_idx;
SLIST_ENTRY(_Elftc_String_Table_Entry) ste_next;
};
#define ELFTC_STRING_TABLE_COMPACTION_FLAG 0x1
#define ELFTC_STRING_TABLE_LENGTH(st) ((st)->st_len >> 1)
#define ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st) do { \
(st)->st_len &= ~ELFTC_STRING_TABLE_COMPACTION_FLAG; \
} while (0)
#define ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st) do { \
(st)->st_len |= ELFTC_STRING_TABLE_COMPACTION_FLAG; \
} while (0)
#define ELFTC_STRING_TABLE_UPDATE_LENGTH(st, len) do { \
(st)->st_len = \
((st)->st_len & \
ELFTC_STRING_TABLE_COMPACTION_FLAG) | \
((len) << 1); \
} while (0)
struct _Elftc_String_Table {
unsigned int st_len; /* length and flags */
int st_nbuckets;
int st_string_pool_size;
char *st_string_pool;
SLIST_HEAD(_Elftc_String_Table_Bucket,
_Elftc_String_Table_Entry) st_buckets[];
};
static struct _Elftc_String_Table_Entry *
elftc_string_table_find_hash_entry(Elftc_String_Table *st, const char *string,
int *rhashindex)
{
struct _Elftc_String_Table_Entry *ste;
int hashindex;
char *s;
hashindex = libelftc_hash_string(string) % st->st_nbuckets;
if (rhashindex)
*rhashindex = hashindex;
SLIST_FOREACH(ste, &st->st_buckets[hashindex], ste_next) {
s = st->st_string_pool + abs(ste->ste_idx);
assert(s > st->st_string_pool &&
s < st->st_string_pool + st->st_string_pool_size);
if (strcmp(s, string) == 0)
return (ste);
}
return (NULL);
}
static int
elftc_string_table_add_to_pool(Elftc_String_Table *st, const char *string)
{
char *newpool;
int len, newsize, stlen;
len = strlen(string) + 1; /* length, including the trailing NUL */
stlen = ELFTC_STRING_TABLE_LENGTH(st);
/* Resize the pool, if needed. */
if (stlen + len >= st->st_string_pool_size) {
newsize = roundup(st->st_string_pool_size +
ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT,
ELFTC_STRING_TABLE_POOL_SIZE_INCREMENT);
if ((newpool = realloc(st->st_string_pool, newsize)) ==
NULL)
return (0);
st->st_string_pool = newpool;
st->st_string_pool_size = newsize;
}
strcpy(st->st_string_pool + stlen, string);
ELFTC_STRING_TABLE_UPDATE_LENGTH(st, stlen + len);
return (stlen);
}
Elftc_String_Table *
elftc_string_table_create(int sizehint)
{
int n, nbuckets, tablesize;
struct _Elftc_String_Table *st;
if (sizehint < ELFTC_STRING_TABLE_DEFAULT_SIZE)
sizehint = ELFTC_STRING_TABLE_DEFAULT_SIZE;
nbuckets = sizehint / (ELFTC_STRING_TABLE_EXPECTED_CHAIN_LENGTH *
ELFTC_STRING_TABLE_EXPECTED_STRING_SIZE);
tablesize = sizeof(struct _Elftc_String_Table) +
nbuckets * sizeof(struct _Elftc_String_Table_Bucket);
if ((st = malloc(tablesize)) == NULL)
return (NULL);
if ((st->st_string_pool = malloc(sizehint)) == NULL) {
free(st);
return (NULL);
}
for (n = 0; n < nbuckets; n++)
SLIST_INIT(&st->st_buckets[n]);
st->st_len = 0;
st->st_nbuckets = nbuckets;
st->st_string_pool_size = sizehint;
*st->st_string_pool = '\0';
ELFTC_STRING_TABLE_UPDATE_LENGTH(st, 1);
return (st);
}
void
elftc_string_table_destroy(Elftc_String_Table *st)
{
int n;
struct _Elftc_String_Table_Entry *s, *t;
for (n = 0; n < st->st_nbuckets; n++)
SLIST_FOREACH_SAFE(s, &st->st_buckets[n], ste_next, t)
free(s);
free(st->st_string_pool);
free(st);
return;
}
Elftc_String_Table *
elftc_string_table_from_section(Elf_Scn *scn, int sizehint)
{
int len;
Elf_Data *d;
GElf_Shdr sh;
const char *s, *end;
Elftc_String_Table *st;
/* Verify the type of the section passed in. */
if (gelf_getshdr(scn, &sh) == NULL ||
sh.sh_type != SHT_STRTAB) {
errno = EINVAL;
return (NULL);
}
if ((d = elf_getdata(scn, NULL)) == NULL ||
d->d_size == 0) {
errno = EINVAL;
return (NULL);
}
if ((st = elftc_string_table_create(sizehint)) == NULL)
return (NULL);
s = d->d_buf;
/*
* Verify that the first byte of the data buffer is '\0'.
*/
if (*s != '\0') {
errno = EINVAL;
goto fail;
}
end = s + d->d_size;
/*
* Skip the first '\0' and insert the strings in the buffer,
* in order.
*/
for (s += 1; s < end; s += len) {
if (elftc_string_table_insert(st, s) == 0)
goto fail;
len = strlen(s) + 1; /* Include space for the trailing NUL. */
}
return (st);
fail:
if (st)
(void) elftc_string_table_destroy(st);
return (NULL);
}
const char *
elftc_string_table_image(Elftc_String_Table *st, size_t *size)
{
char *r, *s, *end;
struct _Elftc_String_Table_Entry *ste;
struct _Elftc_String_Table_Bucket *head;
int copied, hashindex, offset, length, newsize;
/*
* For the common case of a string table has not seen
* a string deletion, we can just export the current
* pool.
*/
if ((st->st_len & ELFTC_STRING_TABLE_COMPACTION_FLAG) == 0) {
if (size)
*size = ELFTC_STRING_TABLE_LENGTH(st);
return (st->st_string_pool);
}
/*
* Otherwise, compact the string table in-place.
*/
assert(*st->st_string_pool == '\0');
newsize = 1;
end = st->st_string_pool + ELFTC_STRING_TABLE_LENGTH(st);
for (r = s = st->st_string_pool + 1;
s < end;
s += length, r += copied) {
copied = 0;
length = strlen(s) + 1;
ste = elftc_string_table_find_hash_entry(st, s,
&hashindex);
head = &st->st_buckets[hashindex];
assert(ste != NULL);
/* Ignore deleted strings. */
if (ste->ste_idx < 0) {
SLIST_REMOVE(head, ste, _Elftc_String_Table_Entry,
ste_next);
free(ste);
continue;
}
/* Move 'live' strings up. */
offset = newsize;
newsize += length;
copied = length;
if (r == s) /* Nothing removed yet. */
continue;
memmove(r, s, copied);
/* Update the index for this entry. */
ste->ste_idx = offset;
}
ELFTC_STRING_TABLE_CLEAR_COMPACTION_FLAG(st);
ELFTC_STRING_TABLE_UPDATE_LENGTH(st, newsize);
if (size)
*size = newsize;
return (st->st_string_pool);
}
size_t
elftc_string_table_insert(Elftc_String_Table *st, const char *string)
{
int hashindex, idx;
struct _Elftc_String_Table_Entry *ste;
hashindex = 0;
ste = elftc_string_table_find_hash_entry(st, string, &hashindex);
assert(hashindex >= 0 && hashindex < st->st_nbuckets);
if (ste == NULL) {
if ((ste = malloc(sizeof(*ste))) == NULL)
return (0);
if ((ste->ste_idx = elftc_string_table_add_to_pool(st,
string)) == 0) {
free(ste);
return (0);
}
SLIST_INSERT_HEAD(&st->st_buckets[hashindex], ste, ste_next);
}
idx = ste->ste_idx;
if (idx < 0) /* Undelete. */
ste->ste_idx = idx = (- idx);
return (idx);
}
size_t
elftc_string_table_lookup(Elftc_String_Table *st, const char *string)
{
int hashindex, idx;
struct _Elftc_String_Table_Entry *ste;
ste = elftc_string_table_find_hash_entry(st, string, &hashindex);
assert(hashindex >= 0 && hashindex < st->st_nbuckets);
if (ste == NULL || (idx = ste->ste_idx) < 0)
return (0);
return (idx);
}
int
elftc_string_table_remove(Elftc_String_Table *st, const char *string)
{
int idx;
struct _Elftc_String_Table_Entry *ste;
ste = elftc_string_table_find_hash_entry(st, string, NULL);
if (ste == NULL || (idx = ste->ste_idx) < 0)
return (ELFTC_FAILURE);
assert(idx > 0 && idx < (int) ELFTC_STRING_TABLE_LENGTH(st));
ste->ste_idx = (- idx);
ELFTC_STRING_TABLE_SET_COMPACTION_FLAG(st);
return (ELFTC_SUCCESS);
}
const char *
elftc_string_table_to_string(Elftc_String_Table *st, size_t offset)
{
const char *s;
s = st->st_string_pool + offset;
/*
* Check for:
* - An offset value within pool bounds.
* - A non-NUL byte at the specified offset.
* - The end of the prior string at offset - 1.
*/
if (offset == 0 || offset >= ELFTC_STRING_TABLE_LENGTH(st) ||
*s == '\0' || *(s - 1) != '\0') {
errno = EINVAL;
return (NULL);
}
return (s);
}

View File

@ -0,0 +1,227 @@
.\" Copyright (c) 2012-2013 Joseph Koshy.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_string_table_create.3 2866 2013-01-06 03:20:14Z jkoshy $
.\"
.Dd January 5, 2013
.Os
.Dt ELFTC_STRING_TABLE_CREATE 3
.Sh NAME
.Nm elftc_string_table_create ,
.Nm elftc_string_table_destroy ,
.Nm elftc_string_table_from_section ,
.Nm elftc_string_table_image ,
.Nm elftc_string_table_insert ,
.Nm elftc_string_table_lookup ,
.Nm elftc_string_table_remove ,
.Nm elftc_string_table_to_string
.Nd convenience routines for handling ELF string tables
.Sh SYNOPSIS
.In libelftc.h
.Ft "Elftc_String_Table *"
.Fn elftc_string_table_create "int sizehint"
.Ft int
.Fn elftc_string_table_destroy "Elftc_String_Table *table"
.Ft "Elftc_String_Table *"
.Fn elftc_string_table_from_section "Elf_Scn *scn" "int sizehint"
.Ft "const char *"
.Fo elftc_string_table_image
.Fa "Elftc_String_Table *table"
.Fa "size_t *size"
.Fc
.Ft size_t
.Fo elftc_string_table_insert
.Fa "Elftc_String_Table *table"
.Fa "const char *string"
.Fc
.Ft size_t
.Fo elftc_string_table_lookup
.Fa "Elftc_String_Table *table"
.Fa "const char *string"
.Fc
.Ft int
.Fo elftc_string_table_remove
.Fa "Elftc_String_Table *table"
.Fa "const char *string"
.Fc
.Ft "const char *"
.Fo elftc_string_table_to_string
.Fa "Elftc_String_Table *table"
.Fa "size_t offset"
.Fc
.Sh DESCRIPTION
This manual page documents convenience routines for handling ELF
string tables.
.Pp
Function
.Fn elftc_string_table_create
creates a new, empty string table.
The argument
.Ar sizehint
provides a hint about the expected number of bytes of string data in
the table.
If the argument
.Ar sizehint
is zero, an implementation-defined default will be used instead.
.Pp
Function
.Fn elftc_string_table_destroy
destroys the previously allocated string table specified by
argument
.Ar table ,
and frees the internal resources allocated for it.
.Pp
Function
.Fn elftc_string_table_from_section
creates a new string table and initializes it based on the
contents of the section specified by argument
.Ar scn .
This section must be of type
.Dv SHT_STRTAB .
The argument
.Ar sizehint
provides a hint about expected number of bytes of string data in the
table.
If the value of
.Ar sizehint
is zero, an implementation-default will be used instead.
.Pp
Function
.Fn elftc_string_table_image
returns a pointer to the ELF representation of the contents of the
string table specified by argument
.Ar table .
If argument
.Ar size
is not NULL, the size of the ELF representation of the string table is
stored in the location pointed to by argument
.Ar size .
The function
.Fn elftc_string_table_image
will compact the string table if the table contains deleted strings.
The string offsets returned by prior calls to
.Fn elftc_string_table_insert
and
.Fn elftc_string_table_lookup
should be treated as invalid after a call to this function.
.Pp
Function
.Fn elftc_string_table_insert
inserts the NUL-terminated string pointed to by argument
.Ar string
into the string table specified by argument
.Ar table ,
and returns an offset value usable in ELF data structures.
Multiple insertions of the same content will return the same offset.
The offset returned will remain valid until the next call to
.Fn elftc_string_table_image .
.Pp
Function
.Fn elftc_string_table_lookup
looks up the string referenced by argument
.Ar string
in the string table specified by argument
.Ar table ,
and if found, returns the offset associated with the string.
The returned offset will be valid till the next call to function
.Fn elftc_string_table_image .
.Pp
Function
.Fn elftc_string_table_remove
removes the string pointed by argument
.Ar string
from the string table referenced by argument
.Ar table ,
if it is present in the string table.
.Pp
Function
.Fn elftc_string_table_to_string
returns a pointer to the NUL-terminated string residing at argument
.Ar offset
in the string table specified by argument
.Ar table .
The value of argument
.Ar offset
should be one returned by a prior call to
.Fn elftc_string_table_insert
or
.Fn elftc_string_table_lookup .
The returned pointer will remain valid until the next call to
.Fn elftc_string_table_insert
or
.Fn elftc_string_table_image .
.Ss Memory Management
The
.Lb libelftc
library manages its own memory allocations.
The application should not free the pointers returned by the string
table functions.
.El
.Sh IMPLEMENTATION NOTES
The current implementation is optimized for the case where strings are
added to a string table, but rarely removed from it.
.Pp
The functions
.Fn elftc_string_table_insert ,
.Fn elftc_string_table_lookup ,
.Fn elftc_string_table_remove
and
.Fn elftc_string_table_to_string
have O(1) asymptotic behavior.
The function
.Fn elftc_string_table_image
can have O(size) asymptotic behavior, where
.Ar size
denotes the size of the string table.
.Sh RETURN VALUES
Functions
.Fn elftc_string_table_create
and
.Fn elftc_string_table_from_section
return a valid pointer to an opaque structure of type
.Vt Elftc_String_Table
on success, or NULL in case of an error.
.Pp
The function
.Fn elftc_string_table_image
returns a pointer to an in-memory representation of an ELF string
table on success, or NULL in case of an error.
.Pp
Functions
.Fn elftc_string_table_insert
and
.Fn elftc_string_table_lookup
return a non-zero offset on success, or zero in case of an error.
.Pp
Function
.Fn elftc_string_table_remove
returns a positive value on success, or zero in case of an error.
.Pp
Function
.Fn elftc_string_table_to_string
returns a valid pointer on success, or NULL in case of an error.
.Sh SEE ALSO
.Xr dwarf 3 ,
.Xr elf 3 ,
.Xr elftc 3

View File

@ -0,0 +1,529 @@
.\" Copyright (c) 2012 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_symbol_table_create.3 2825 2012-12-29 14:25:33Z jkoshy $
.\"
.Dd December 29, 2012
.Os
.Dt ELFTC_SYMBOL_TABLE_CREATE 3
.Sh NAME
.Nm elftc_elf_symbol_table_from_section ,
.Nm elftc_symbol_table_count ,
.Nm elftc_symbol_table_create ,
.Nm elftc_symbol_table_create_nested ,
.Nm elftc_symbol_table_delete_name ,
.Nm elftc_symbol_table_delete_entry ,
.Nm elftc_symbol_table_destroy ,
.Nm elftc_symbol_table_insert ,
.Nm elftc_symbol_table_iterate ,
.Nm elftc_symbol_table_lookup ,
.Nm elftc_symbol_table_lookup_value ,
.Nm elftc_symbol_table_replace ,
.Nm elftc_symbol_table_sort ,
.Nm elftc_symbol_table_step
.Nd symbol table management routines
.Sh SYNOPSIS
.In libelftc.h
.Bd -literal
typedef struct _Elftc_Symbol_Table Elftc_Symbol_Table;
typedef struct _Elftc_Symbol {
... library private fields ...
const char *sym_name;
uintptr_t sym_value;
} Elftc_Symbol;
.Ed
.Ft size_t
.Fn elftc_symbol_table_count "Elftc_Symbol_Table *table"
.Ft "Elftc_Symbol_Table *"
.Fo elftc_symbol_table_create
.Fa "size_t entrysize"
.Fa "int sizehint"
.Fc
.Ft "Elftc_Symbol_Table *"
.Fo elftc_symbol_table_create_nested
.Fa "Elftc_Symbol_Table *table"
.Fa "int sizehint"
.Fc
.Ft int
.Fo elftc_symbol_table_delete_name
.Fa "Elftc_Symbol_Table *table"
.Fa "const char *name"
.Fc
.Ft int
.Fo elftc_symbol_table_delete_entry
.Fa "Elftc_Symbol_Table *table"
.Fa "Elftc_Symbol *entry"
.Fc
.Ft int
.Fn elftc_symbol_table_destroy "Elftc_Symbol_Table *table"
.Ft "Elftc_Symbol *entry"
.Fo elftc_symbol_table_insert
.Fa "Elftc_Symbol_Table *table"
.Fa "const char *symbolname"
.Fa "int *status"
.Fc
.Ft int
.Fo elftc_symbol_table_iterate
.Fa "Elftc_Symbol_Table *table"
.Fa "int (*iterfn)(Elftc_Symbol *entry, void *cookie)"
.Fa "void *cookie"
.Fc
.Ft "Elftc_Symbol *"
.Fo elftc_symbol_table_lookup
.Fa "Elftc_Symbol_Table *table"
.Fa "const char *symbolname"
.Fc
.Ft "Elftc_Elf_Symbol *"
.Fo elftc_symbol_table_lookup_value
.Fa "Elftc_Symbol_Table *table"
.Fa "uintptr_t value"
.Fa "int searchflags"
.Fc
.Ft int
.Fo elftc_symbol_table_replace
.Fa "Elftc_Symbol_Table *table"
.Fa "Elftc_Symbol *sym1"
.Fa "Elftc_Symbol *sym2"
.Fc
.Ft int
.Fo elftc_symbol_table_sort
.Fa "Elftc_Symbol_Table *table"
.Fa "int (*cmpfn)(Elftc_Symbol *s1, Elftc_Symbol *s2)"
.Fc
.Ft "Elftc_Symbol *"
.Fo elftc_symbol_table_step
.Fa "Elftc_Symbol_Table *table"
.Fa "Elftc_Symbol *cursym"
.Fa "int direction"
.Fc
.Bd -literal
typedef struct _Elftc_Elf_Symbol {
... library private fields ...
const char *sym_name;
Gelf_Sym sym_elf;
} Elftc_Elf_Symbol;
.Ed
.Ft "Elftc_Symbol_Table *"
.Fo elftc_elf_symbol_table_from_section
.Fa "Elf_Scn *symscn"
.Fa "Elf_Scn *strscn"
.Fc
.Sh DESCRIPTION
This manual page documents convenience routines for handling symbol
tables.
Two flavors of symbol tables are supported:
.Bl -bullet -compact
.It
.Dq Regular
symbol tables supporting insertion, deletion and lookup of entries by
name or by value, sorting of entries, and stepping through entries in
the table's current traversal order.
.It
.Dq ELF-centric
symbol tables support additional operations for conversions to and
from the symbol table format understood by
.Lb libelf .
.El
The default traversal order for a symbol table is the order in which
entries were inserted into it.
This traversal order may be changed using function
.Fn elftc_symbol_table_sort .
.Ss Operations on Regular Symbol Tables
Regular symbol tables use symbols that are subtypes of
.Vt Elftc_Symbol ,
as described in the section
.Sx "Structure of a Symbol Table Entry"
below.
.Pp
Function
.Fn elftc_symbol_table_count
returns the number of entries currently in the symbol table.
.Pp
Function
.Fn elftc_symbol_table_create
creates a new, empty symbol table.
The argument
.Ar entrysize
specifies the size of each symbol table entry, as described
in the section
.Sx "Structure of a Symbol Table Entry"
below.
The argument
.Ar sizehint
specifies the expected number of symbol table entries.
If
.Ar sizehint
is zero, an implementation-defined default will be used.
.Pp
Function
.Fn elftc_symbol_table_create_nested
creates a symbol table whose search scope nests inside that of a
parent symbol table.
The argument
.Ar parent
specifies the parent symbol table to nest under.
The argument
.Ar sizehint
specifies the expected number of symbol table entries.
If
.Ar sizehint
is zero, an implementation-defined default will be used instead.
.Pp
The function
.Fn elftc_symbol_table_delete_name
removes the symbol entry named by the argument
.Ar name
from the symbol table specified by argument
.Ar table ,
according to the rules described in section
.Sx "Symbol Search Rules" .
.Pp
The function
.Fn elftc_symbol_table_delete_entry
removes the symbol table entry specified by argument
.Ar entry
from the symbol table specified by argument
.Ar table .
.Pp
Function
.Fn elftc_symbol_table_destroy
is used to destroy a symbol table and free up its internal
resources.
.Pp
The function
.Fn elftc_symbol_table_insert
inserts a symbol entry for the name specified by argument
.Ar symbolname
into the symbol table specified by argument
.Ar table ,
returning a pointer to a symbol table entry.
The argument
.Ar status
should point to a location that will be updated with one of
the following values:
.Bl -tag -width indent -compact -offset indent
.It Dv ELFTC_INSERT_ERROR
An error occured during insertion of the symbol.
.It Dv ELFTC_INSERT_EXISTING
The name in argument
.Ar symbolname
was already in the symbol table, and a pointer to the existing
symbol table entry is being returned.
.It Dv ELFTC_INSERT_NEW
A new symbol table entry was allocated for the symbol name
in
.Ar symbolname .
The application will need to initialize the application-specific
fields of the symbol table entry.
.El
Insertion obeys the scoping rules described in section
.Sx "Symbol Search Rules" .
.Pp
The function
.Fn elftc_symbol_table_iterate
iterates over the symbol table specifed by argument
.Ar table ,
applying the function pointed to by argument
.Ar iterfn
to each symbol table entry.
The return value from the function
.Ar iterfn
controls progress of the iteration:
.Bl -tag -width indent -compact -offset indent
.It Dv ELFTC_ITERATE_ABORT
Terminates the iteration.
.It Dv ELFTC_ITERATE_CONTINUE
Iteration will continue on to the next element in the symbol table.
.El
Argument
.Ar cookie
will be passed to each invocation of
.Ar iterfn ,
and may be used to track persistent state.
The ordering of symbol table entries presented to function
.Ar iterfn
is not defined.
The behavior of the iteration is undefined if
.Ar iterfn
adds or deletes symbol entries from a symbol table that currently
being iterated through.
.Pp
Function
.Fn elftc_symbol_table_lookup
returns the symbol entry corresponding to the name of the symbol
in argument
.Ar symbolname .
.Pp
Function
.Fn elftc_symbol_table_lookup_value
returns the symbol entry that has a
.Va sym_value
field that is closest to the value specified in argument
.Ar value .
The argument
.Ar table
should point to a symbol table, that has been sorted
by a prior call to
.Fn elftc_symbol_table_sort .
The argument
.Ar searchflags
can be a combination of the following flags:
.Bl -tag -width indent -compact -offset indent
.It Dv ELFTC_SEARCH_FORWARD
Find the symbol entry with the next higher value in its
.Va sym_value
field.
.It Dv ELFTC_SEARCH_BACKWARD
Find the symbol entry with next lower value in its
.Va sym_value
field.
.El
If both
.Dv ELFTC_SEARCH_FORWARD
and
.Dv ELFTC_SEARCH_BACKWARD
are specified, then this function will return the symbol that is
closest to the argument
.Ar value .
.Pp
Function
.Fn elftc_symbol_table_replace
moves the symbol table entry pointed to by argument
.Ar sym2
into the traversal position for the entry pointed to by
.Ar sym1 ,
and implicitly deletes the entry pointed to by argument
.Ar sym1 .
Argument
.Ar table
should point to a valid symbol table.
.Pp
Function
.Fn elftc_symbol_table_sort
is used to define an ordering of symbol entries in a symbol
table.
This ordering will be associated with the symbol table till the next
call to function
.Fn elftc_symbol_table_insert ,
.Fn elftc_symbol_table_delete_name
or
.Fn elftc_symbol_table_delete_entry .
The argument
.Ar cmpfn
should point to a function that compares two symbol entries pointed
to by
.Ar s1
and
.Ar s2
and returns -1, 0, or 1, depending whether
.Ar s1
is less, equal to, or greater than
.Ar s2
respectively.
.Pp
Function
.Fn elftc_symbol_table_step
is used to step to the next symbol in a sorted symbol table.
Argument
.Ar table
should point to a symbol table.
The argument
.Ar cursym
specifies the current symbol.
The argument
.Ar direction
specifies the direction to step:
.Bl -tag -width indent -compact -offset ident
.It Dv ELFTC_STEP_NEXT
Return the symbol which follows the argument
.Ar cursym
in the current traversal order.
If argument
.Ar cursym
is NULL, return the first symbol in the current
traversal order.
.It Dv ELFTC_STEP_PREVIOUS
Return the symbol which precedes the argument
.Ar cursym
in the current traversal order.
If argument
.Ar cursym
is NULL, return the last symbol in the current
traversal order.
.El
.Ss Operations on ELF-centric symbol tables
ELF-centric symbol tables use symbols that are subtypes of
.Vt Elftc_Elf_Symbol ,
as described in the section
.Sx "Structure of a Symbol Table Entry"
below.
.Pp
In addition to the operations on regular symbol tables listed above,
these symbol tables may be used with the following additional
functions.
.Pp
The function
.Fn elftc_elf_symbol_table_from_section
builds a symbol table from the contents of an ELF section.
The argument
.Ar symscn
should reference an ELF section of type
.Dv SHT_SYMTAB
or
.Dv SHT_DYNSYM .
The argument
.Ar strscn
should reference an ELF section of type
.Dv SHT_STRTAB
containing the string table associated wit section
.Ar symscn .
.Ss Structure of a Symbol Table Entry
The symbol tables managed by
.Lb libelftc
are collections of symbol table entries.
Each entry should be a subtype of one of the
.Vt Elftc_Symbol
or
.Vt Elftc_Elf_Symbol
types.
In other words, each entry should have an
.Vt Elftc_Symbol
or
.Vt Elftc_Elf_Symbol
structure as its first member, before any application specific
fields.
For example:
.Bd -literal -offset indent
struct _MySymbol {
Elftc_Symbol sym_base;
... other application-specific fields ...
};
.Ed
.Pp
The size of the combined entry is indicated to the library
at the time of creating a new symbol table.
Applications may then cast the returned pointers from these
routines to the appropriate type:
.Bd -literal -offset indent
struct _MySymbol *mysym;
mysym = (struct _MySymbol *) elftc_symbol_table_lookup(table,
name);
.Ed
.Pp
The
.Vt Elftc_Symbol
type has two public fields:
.Bl -tag -width ".Va sym_value" -compact -offset indent
.It Va sym_name
Points to a NUL-terminated string containing the symbol's name.
The application should not change the value of this field.
.It Va sym_value
The value associated with this symbol.
This field is entirely under the application's control.
.El
.Pp
The
.Vt Elftc_Elf_Symbol
type has two public fields:
.Bl -tag -width ".Va sym_value" -compact -offset indent
.It Va sym_name
Points to a NUL-terminated string containing the symbol's name.
The application should not change the value of this field.
.It Va sym_elf
A structure of type
.Vt Gelf_Sym
containing ELF symbol information.
This field is entirely under the application's control.
.El
.Ss Symbol Search Rules
During lookups, symbols are looked up first in the symbol table passed in
to the
.Fn elftc_symbol_table_lookup
function.
If the specified symbol is not found, and if the symbol table has a
parent, then the search continues recursively up the chain of parent
symbol tables till either a matching symbol is found or till there are
no more parent symbol tables to search in.
.Pp
Insertions and deletion only work on the specified symbol table and
do not recurse into parent symbol tables.
.Ss Memory Management
The
.Lb libelftc
manages its memory allocations.
Applications should not free the pointers returned by the
API documented in this manual page.
.Sh RETURN VALUES
Function
.Fn elftc_symbol_table_count
returns a count of the number of symbol table entries as an unsigned
value.
.Pp
Functions
.Fn elftc_symbol_table_create ,
.Fn elftc_symbol_table_create_nested
and
.Fn elftc_symbol_table_from_section
return a pointer to an opaque structure of type
.Vt Elftc_Symbol_Table
on success, or return NULL in case of an error.
.Pp
Functions
.Fn elftc_symbol_table_delete_name ,
.Fn elftc_symbol_table_delete_name
.Fn elftc_symbol_table_destroy ,
.Fn elftc_symbol_table_replace
and
.Fn elftc_symbol_table_sort
return a non-zero value on success, or return zero in case of an error.
.Pp
Functions
.Fn elftc_symbol_table_insert ,
.Fn elftc_symbol_table_lookup
and
.Fn elftc_symbol_table_lookup_value
return a pointer to a structure that is a subtype of
.Vt Elftc_Symbol
on success, or return NULL in case of an error.
.Pp
The function
.Fn elftc_symbol_table_step
return a pointer to a structure that is a subtype of
.Vt Elftc_Symbol
on success.
The function returns NULL if there are no more elements in the
specified traversal direction.
.Pp
The function
.Fn elftc_symbol_table_iterate
returns
.Dv ELFTC_ITERATE_SUCCESS
if the symbol table was successfully traversed, or
.Dv ELFTC_ITERATE_ABORT
in case the iteration function aborted the traversal.
.Sh SEE ALSO
.Xr dwarf 3 ,
.Xr elf 3 ,
.Xr elftc 3

View File

@ -0,0 +1,79 @@
.\" Copyright (c) 2011,2012 Joseph Koshy. All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" This software is provided by Joseph Koshy ``as is'' and
.\" any express or implied warranties, including, but not limited to, the
.\" implied warranties of merchantability and fitness for a particular purpose
.\" are disclaimed. in no event shall Joseph Koshy be liable
.\" for any direct, indirect, incidental, special, exemplary, or consequential
.\" damages (including, but not limited to, procurement of substitute goods
.\" or services; loss of use, data, or profits; or business interruption)
.\" however caused and on any theory of liability, whether in contract, strict
.\" liability, or tort (including negligence or otherwise) arising in any way
.\" out of the use of this software, even if advised of the possibility of
.\" such damage.
.\"
.\" $Id: elftc_version.3 2828 2012-12-30 04:41:27Z jkoshy $
.\"
.Dd December 30, 2012
.Os
.Dt ELFTC_VERSION 3
.Sh NAME
.Nm elftc_version
.Nd return a project-wide version identifier string
.Sh LIBRARY
.Lb libelftc
.Sh SYNOPSIS
.In libelftc.h
.Ft const char *
.Fn elftc_version void
.Sh DESCRIPTION
Function
.Fn elftc_version
returns a project-wide identifier string that encodes the source
revision of the project source tree.
.Pp
The returned identifier has four space-separated fields:
.Bl -tag -width ".Em Project Branch"
.It Em "Project-Name"
This is always
.Dq elftoolchain .
.It Em "Project-Branch"
The branch name for the project source tree.
.It Em "Build-OS"
The operating system that the tool chain was compiled for.
.It Em "Version-Number"
A tree-wide version number extracted from the version control
system in use.
.El
.Sh RETURN VALUE
Function
.Fn elftc_program_version
returns a pointer to an internal character buffer.
.Sh EXAMPLES
To retrieve and print the current toolchain version identifier, use:
.Bd -literal -offset indent
#include <sys/types.h>
#include <libelftc.h>
(void) printf("%s\en", elftc_version());
.Ed
.Pp
On the HEAD branch of the project's sources, when checked out using
Subversion and compiled on a NetBSD host, this would print:
.D1 Dq elftoolchain HEAD NetBSD svn: Ns Em REVINFO
where
.Em REVINFO
would be the current revision information for the project source tree.
.Sh ERRORS
Function
.Fn elftc_program_version
always succeeds.

View File

@ -0,0 +1,89 @@
/*-
* Copyright (c) 2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD: users/kaiwang27/elftc/libelftc.h 392 2009-05-31 19:17:46Z kaiwang27 $
* $Id: libelftc.h 2863 2013-01-06 03:18:32Z jkoshy $
*/
#ifndef _LIBELFTC_H_
#define _LIBELFTC_H_
#include <sys/stat.h>
#include <libelf.h>
typedef struct _Elftc_Bfd_Target Elftc_Bfd_Target;
typedef struct _Elftc_String_Table Elftc_String_Table;
/* Target types. */
typedef enum {
ETF_NONE,
ETF_ELF,
ETF_BINARY,
ETF_SREC,
ETF_IHEX
} Elftc_Bfd_Target_Flavor;
/*
* Demangler flags.
*/
/* Name mangling style. */
#define ELFTC_DEM_UNKNOWN 0x00000000U /* Not specified. */
#define ELFTC_DEM_ARM 0x00000001U /* C++ Ann. Ref. Manual. */
#define ELFTC_DEM_GNU2 0x00000002U /* GNU version 2. */
#define ELFTC_DEM_GNU3 0x00000004U /* GNU version 3. */
/* Demangling behaviour control. */
#define ELFTC_DEM_NOPARAM 0x00010000U
__BEGIN_DECLS
Elftc_Bfd_Target *elftc_bfd_find_target(const char *_tgt_name);
Elftc_Bfd_Target_Flavor elftc_bfd_target_flavor(Elftc_Bfd_Target *_tgt);
unsigned int elftc_bfd_target_byteorder(Elftc_Bfd_Target *_tgt);
unsigned int elftc_bfd_target_class(Elftc_Bfd_Target *_tgt);
unsigned int elftc_bfd_target_machine(Elftc_Bfd_Target *_tgt);
int elftc_copyfile(int _srcfd, int _dstfd);
int elftc_demangle(const char *_mangledname, char *_buffer,
size_t _bufsize, unsigned int _flags);
int elftc_set_timestamps(const char *_filename, struct stat *_sb);
Elftc_String_Table *elftc_string_table_create(int _hint);
void elftc_string_table_destroy(Elftc_String_Table *_table);
Elftc_String_Table *elftc_string_table_from_section(Elf_Scn *_scn,
int _hint);
const char *elftc_string_table_image(Elftc_String_Table *_table,
size_t *_sz);
size_t elftc_string_table_insert(Elftc_String_Table *_table,
const char *_string);
size_t elftc_string_table_lookup(Elftc_String_Table *_table,
const char *_string);
int elftc_string_table_remove(Elftc_String_Table *_table,
const char *_string);
const char *elftc_string_table_to_string(Elftc_String_Table *_table,
size_t offset);
const char *elftc_version(void);
__END_DECLS
#endif /* _LIBELFTC_H_ */

View File

@ -0,0 +1,383 @@
/*-
* Copyright (c) 2008,2009 Kai Wang
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <libelf.h>
#include <libelftc.h>
#include "_libelftc.h"
ELFTC_VCSID("$Id: libelftc_bfdtarget.c 2251 2011-11-30 16:50:06Z jkoshy $");
struct _Elftc_Bfd_Target _libelftc_targets[] = {
{
.bt_name = "binary",
.bt_type = ETF_BINARY,
},
{
.bt_name = "elf32-avr",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_AVR,
},
{
.bt_name = "elf32-big",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
},
{
.bt_name = "elf32-bigarm",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_ARM,
},
{
.bt_name = "elf32-bigmips",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_MIPS,
},
{
.bt_name = "elf32-i386",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_386,
},
{
.bt_name = "elf32-i386-freebsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_386,
.bt_osabi = ELFOSABI_FREEBSD,
},
{
.bt_name = "elf32-ia64-big",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_IA_64,
},
{
.bt_name = "elf32-little",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
},
{
.bt_name = "elf32-littlearm",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_ARM,
},
{
.bt_name = "elf32-littlemips",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_MIPS,
},
{
.bt_name = "elf32-powerpc",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_PPC,
},
{
.bt_name = "elf32-powerpcle",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_PPC,
},
{
.bt_name = "elf32-sh",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
},
{
.bt_name = "elf32-shl",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
},
{
.bt_name = "elf32-sh-nbsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_NETBSD,
},
{
.bt_name = "elf32-shl-nbsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_NETBSD,
},
{
.bt_name = "elf32-shbig-linux",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_LINUX,
},
{
.bt_name = "elf32-sh-linux",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_LINUX,
},
{
.bt_name = "elf32-sparc",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS32,
.bt_machine = EM_SPARC,
},
{
.bt_name = "elf64-alpha",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_ALPHA,
},
{
.bt_name = "elf64-alpha-freebsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_ALPHA,
.bt_osabi = ELFOSABI_FREEBSD
},
{
.bt_name = "elf64-big",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
},
{
.bt_name = "elf64-bigmips",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_MIPS,
},
{
.bt_name = "elf64-ia64-big",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_IA_64,
},
{
.bt_name = "elf64-ia64-little",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_IA_64,
},
{
.bt_name = "elf64-little",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
},
{
.bt_name = "elf64-littlemips",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_MIPS,
},
{
.bt_name = "elf64-powerpc",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_PPC64,
},
{
.bt_name = "elf64-powerpcle",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_PPC64,
},
{
.bt_name = "elf64-sh64",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
},
{
.bt_name = "elf64-sh64l",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
},
{
.bt_name = "elf64-sh64-nbsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_NETBSD,
},
{
.bt_name = "elf64-sh64l-nbsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_NETBSD,
},
{
.bt_name = "elf64-sh64big-linux",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_LINUX,
},
{
.bt_name = "elf64-sh64-linux",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SH,
.bt_osabi = ELFOSABI_LINUX,
},
{
.bt_name = "elf64-sparc",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SPARCV9,
},
{
.bt_name = "elf64-sparc-freebsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2MSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_SPARCV9,
.bt_osabi = ELFOSABI_FREEBSD
},
{
.bt_name = "elf64-x86-64",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_X86_64,
},
{
.bt_name = "elf64-x86-64-freebsd",
.bt_type = ETF_ELF,
.bt_byteorder = ELFDATA2LSB,
.bt_elfclass = ELFCLASS64,
.bt_machine = EM_X86_64,
.bt_osabi = ELFOSABI_FREEBSD
},
{
.bt_name = "ihex",
.bt_type = ETF_IHEX,
},
{
.bt_name = "srec",
.bt_type = ETF_SREC,
},
{
.bt_name = "symbolsrec",
.bt_type = ETF_SREC,
},
{
.bt_name = NULL,
.bt_type = ETF_NONE,
},
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
/*-
* Copyright (c) 2013, Joseph Koshy
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* An implementation of the Fowler-Noll-Vo hash function.
*
* References:
* - http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function
* - http://www.isthe.com/chongo/tech/comp/fnv/
*/
#include <sys/types.h>
#include <limits.h>
#include "_libelftc.h"
ELFTC_VCSID("$Id: libelftc_hash.c 2870 2013-01-07 10:38:43Z jkoshy $");
/*
* Use the size of an 'int' to determine the magic numbers used by the
* hash function.
*/
#if INT_MAX == 2147483647UL
#define FNV_PRIME 16777619UL
#define FNV_OFFSET 2166136261UL
#elif INT_MAX == 18446744073709551615ULL
#define FNV_PRIME 1099511628211ULL
#define FNV_OFFSET 14695981039346656037ULL
#else
#error sizeof(int) is unknown.
#endif
unsigned int
libelftc_hash_string(const char *s)
{
char c;
unsigned int hash;
for (hash = FNV_OFFSET; (c = *s) != '\0'; s++) {
hash ^= c;
hash *= FNV_PRIME;
}
return (hash);
}

View File

@ -0,0 +1,318 @@
/*-
* Copyright (c) 2008 Hyogeol Lee <hyogeollee@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer
* in this position and unchanged.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/types.h>
#include <assert.h>
#include <libelftc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "_libelftc.h"
ELFTC_VCSID("$Id: libelftc_vstr.c 2065 2011-10-26 15:24:47Z jkoshy $");
/**
* @file vector_str.c
* @brief Dynamic vector data for string implementation.
*
* Resemble to std::vector<std::string> in C++.
*/
static size_t get_strlen_sum(const struct vector_str *v);
static bool vector_str_grow(struct vector_str *v);
static size_t
get_strlen_sum(const struct vector_str *v)
{
size_t i, len = 0;
if (v == NULL)
return (0);
assert(v->size > 0);
for (i = 0; i < v->size; ++i)
len += strlen(v->container[i]);
return (len);
}
/**
* @brief Deallocate resource in vector_str.
*/
void
vector_str_dest(struct vector_str *v)
{
size_t i;
if (v == NULL)
return;
for (i = 0; i < v->size; ++i)
free(v->container[i]);
free(v->container);
}
/**
* @brief Find string in vector_str.
* @param v Destination vector.
* @param o String to find.
* @param l Length of the string.
* @return -1 at failed, 0 at not found, 1 at found.
*/
int
vector_str_find(const struct vector_str *v, const char *o, size_t l)
{
size_t i;
if (v == NULL || o == NULL)
return (-1);
for (i = 0; i < v->size; ++i)
if (strncmp(v->container[i], o, l) == 0)
return (1);
return (0);
}
/**
* @brief Get new allocated flat string from vector.
*
* If l is not NULL, return length of the string.
* @param v Destination vector.
* @param l Length of the string.
* @return NULL at failed or NUL terminated new allocated string.
*/
char *
vector_str_get_flat(const struct vector_str *v, size_t *l)
{
ssize_t elem_pos, elem_size, rtn_size;
size_t i;
char *rtn;
if (v == NULL || v->size == 0)
return (NULL);
if ((rtn_size = get_strlen_sum(v)) == 0)
return (NULL);
if ((rtn = malloc(sizeof(char) * (rtn_size + 1))) == NULL)
return (NULL);
elem_pos = 0;
for (i = 0; i < v->size; ++i) {
elem_size = strlen(v->container[i]);
memcpy(rtn + elem_pos, v->container[i], elem_size);
elem_pos += elem_size;
}
rtn[rtn_size] = '\0';
if (l != NULL)
*l = rtn_size;
return (rtn);
}
static bool
vector_str_grow(struct vector_str *v)
{
size_t i, tmp_cap;
char **tmp_ctn;
if (v == NULL)
return (false);
assert(v->capacity > 0);
tmp_cap = v->capacity * BUFFER_GROWFACTOR;
assert(tmp_cap > v->capacity);
if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL)
return (false);
for (i = 0; i < v->size; ++i)
tmp_ctn[i] = v->container[i];
free(v->container);
v->container = tmp_ctn;
v->capacity = tmp_cap;
return (true);
}
/**
* @brief Initialize vector_str.
* @return false at failed, true at success.
*/
bool
vector_str_init(struct vector_str *v)
{
if (v == NULL)
return (false);
v->size = 0;
v->capacity = VECTOR_DEF_CAPACITY;
assert(v->capacity > 0);
if ((v->container = malloc(sizeof(char *) * v->capacity)) == NULL)
return (false);
assert(v->container != NULL);
return (true);
}
/**
* @brief Remove last element in vector_str.
* @return false at failed, true at success.
*/
bool
vector_str_pop(struct vector_str *v)
{
if (v == NULL)
return (false);
if (v->size == 0)
return (true);
--v->size;
free(v->container[v->size]);
v->container[v->size] = NULL;
return (true);
}
/**
* @brief Push back string to vector.
* @return false at failed, true at success.
*/
bool
vector_str_push(struct vector_str *v, const char *str, size_t len)
{
if (v == NULL || str == NULL)
return (false);
if (v->size == v->capacity && vector_str_grow(v) == false)
return (false);
if ((v->container[v->size] = malloc(sizeof(char) * (len + 1))) == NULL)
return (false);
snprintf(v->container[v->size], len + 1, "%s", str);
++v->size;
return (true);
}
/**
* @brief Push front org vector to det vector.
* @return false at failed, true at success.
*/
bool
vector_str_push_vector_head(struct vector_str *dst, struct vector_str *org)
{
size_t i, j, tmp_cap;
char **tmp_ctn;
if (dst == NULL || org == NULL)
return (false);
tmp_cap = (dst->size + org->size) * BUFFER_GROWFACTOR;
if ((tmp_ctn = malloc(sizeof(char *) * tmp_cap)) == NULL)
return (false);
for (i = 0; i < org->size; ++i)
if ((tmp_ctn[i] = strdup(org->container[i])) == NULL) {
for (j = 0; j < i; ++j)
free(tmp_ctn[j]);
free(tmp_ctn);
return (false);
}
for (i = 0; i < dst->size; ++i)
tmp_ctn[i + org->size] = dst->container[i];
free(dst->container);
dst->container = tmp_ctn;
dst->capacity = tmp_cap;
dst->size += org->size;
return (true);
}
/**
* @brief Get new allocated flat string from vector between begin and end.
*
* If r_len is not NULL, string length will be returned.
* @return NULL at failed or NUL terminated new allocated string.
*/
char *
vector_str_substr(const struct vector_str *v, size_t begin, size_t end,
size_t *r_len)
{
size_t cur, i, len;
char *rtn;
if (v == NULL || begin > end)
return (NULL);
len = 0;
for (i = begin; i < end + 1; ++i)
len += strlen(v->container[i]);
if ((rtn = malloc(sizeof(char) * (len + 1))) == NULL)
return (NULL);
if (r_len != NULL)
*r_len = len;
cur = 0;
for (i = begin; i < end + 1; ++i) {
len = strlen(v->container[i]);
memcpy(rtn + cur, v->container[i], len);
cur += len;
}
rtn[cur] = '\0';
return (rtn);
}

View File

@ -0,0 +1,104 @@
#!/bin/sh
#
# This script generates a project-wide version identifier for use by
# the `elftc_version()' API.
#
# $Id: make-toolchain-version 2583 2012-09-14 09:49:25Z jkoshy $
#
# Defaults.
#
buildhost=`uname -s`
elftcname="elftoolchain"
options="e:h:o:r:t:"
top=""
version="HEAD"
versionfile="elftc_version.c"
progname=`basename ${0}`
usage()
{
exec >&2
# Print a message, if supplied.
if [ -n "${*}" ]; then echo "##${@}"; fi
echo "Usage: ${progname} [options]"
echo " Generate a toolchain-wide version number"
echo " -e PROJECTNAME Set the project name [default: ${elftcname}]."
echo " -h HOSTOS Set the build OS [default: ${buildhost}]."
echo " -o OUTPUT Set the output file [default: ${versionfile}]."
echo " -r VERSION Set the version string [default: ${version}]."
echo " -t TOPDIR Set the top-of-tree directory [required]."
exit 1
}
#
# Parse options.
#
while getopts ${options} option
do
case ${option} in
'e') elftcname="${OPTARG}" ;;
'h') buildhost="${OPTARG}" ;;
'o') versionfile="${OPTARG}" ;;
'r') version="${OPTARG}" ;;
't') top="${OPTARG}" ;;
'?') usage ;;
esac
done
[ -n "${top}" ] || usage
# Try to determine the in-tree revision number.
#
# This script attempts to handle the case where our sources have been
# incorporated into an operating system's base sources.
#
# - If SVN is detected, we use the `svninfo' tool to determine the
# in-tree revision number.
# - If CVS is detected, we use the string `unknown'.
# - Otherwise, we use `git --describe'.
curdir=`pwd`
cd ${top} || usage "ERROR: Cannot change directory to \"${top}\"."
if [ -d .svn ]; then # FreeBSD and SF.Net sources.
versionstring=" svn:"$(svnversion)
elif [ -d CVS ]; then # NetBSD.
versionstring=" cvs:unknown"
else # DragonFlyBSD.
versionstring=" git:"$(git describe --all --dirty --long 2> /dev/null)
# Cannot determine an in-tree version number.
if [ $? -ne 0 ]; then
versionstring=""
fi
fi
cd ${curdir} || usage "Cannot change back to ${curdir}."
#
# Only replace the source file if its content has changed.
#
tmpfile=`mktemp ${TMPDIR:-/tmp}/MV.XXXXXXX`
trap "rm -f ${tmpfile};" 0 1 2 3 15
cat > ${tmpfile} <<EOF
/* WARNING: Generated by "${progname}". */
#include <sys/types.h>
#include <libelftc.h>
const char *
elftc_version(void)
{
return "${elftcname} ${version} ${buildhost}${versionstring}";
}
EOF
if ! cmp -s ${tmpfile} ${versionfile}; then
echo "@ ${progname}: building \"${versionfile}\"."
cp ${tmpfile} ${versionfile} || exit ${?}
fi

View File

@ -0,0 +1,7 @@
#
# Building for a FreeBSD target.
#
# $Id: os.freebsd.mk 189 2008-07-20 10:38:08Z jkoshy $
# Symbol versioning support [FreeBSD 7.X and later]
VERSION_MAP= ${.CURDIR}/Version.map

View File

@ -0,0 +1,3 @@
# $Id: os.Linux.mk 994 2010-06-13 10:39:19Z jkoshy $
CFLAGS+= -Wall

View File

@ -0,0 +1,13 @@
# $Id: Makefile 2076 2011-10-27 03:50:33Z jkoshy $
TOP= ..
PROG= nm
SRCS= nm.c
WARNS?= 6
LDADD= -ldwarf -lelftc -lelf
.include "${TOP}/mk/elftoolchain.prog.mk"

View File

@ -0,0 +1,334 @@
.\" Copyright (c) 2007 Hyogeol Lee <hyogeollee@gmail.com>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer
.\" in this position and unchanged.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $Id: nm.1 2377 2012-01-03 07:10:59Z jkoshy $
.\"
.Dd January 3, 2012
.Os
.Dt NM 1
.Sh NAME
.Nm nm
.Nd display symbolic information in object files
.Sh SYNOPSIS
.Nm
.Op Fl -debug-syms
.Op Fl -defined-only
.Op Fl -demangle Ns Op = Ns style
.Op Fl -dynamic
.Op Fl -help
.Op Fl -line-numbers
.Op Fl -no-demangle
.Op Fl -no-sort
.Op Fl -numeric-sort
.Op Fl -print-armap
.Op Fl -print-file-name
.Op Fl -print-size
.Op Fl -radix= Ns Ar format
.Op Fl -reverse-sort
.Op Fl -size-sort
.Op Fl -undefined-only
.Op Fl -version
.Op Fl A
.Op Fl B
.Op Fl C Op Ar style
.Op Fl D
.Op Fl P
.Op Fl V
.Op Fl a
.Op Fl e
.Op Fl g
.Op Fl h
.Op Fl l
.Op Fl n
.Op Fl o
.Op Fl p
.Op Fl r
.Op Fl S
.Op Fl s
.Op Fl t Ar format
.Op Fl u
.Op Fl x
.Ar
.Sh DESCRIPTION
The
.Nm
utility displays symbolic information in the object files,
executables, and object library files named by its arguments.
Lack of symbolic information in an otherwise valid input
file, is not considered to be an error.
If no files are specified on the command line,
.Nm
will attempt to read
.Pa a.out .
.Pp
The
.Nm
utility recognizes the following options:
.Bl -tag -width ".Fl d Ar argument"
.It Fl -debug-syms
Display all symbols, including debugger-only symbols.
.It Fl -defined-only
Display only defined symbols.
.It Fl -demangle Ns Op = Ns Ar style
Decode (demangle) low-level symbol names into human-readable names.
Supported values for argument
.Ar style
are
.Sq auto ,
.Sq gnu-v2 ,
.Sq gnu-v3
and
.Sq arm.
If argument
.Ar style
is not specified, it is taken to be
.Sq auto .
.It Fl -dynamic
Only display dynamic symbols.
This option is only meaningful for shared libraries.
.It Fl -help
Display a help message and exit.
.It Fl -format Ns = Ns Ar format
Display output in the format specified by argument
.Ar format .
Supported values for the format argument are
.Sq bsd ,
.Sq sysv ,
and
.Sq posix .
The default output format is
.Sq bsd .
.It Fl -line-numbers
Display the filename and line number associated a symbol using
any debugging information present in the input file.
For defined symbols, look up the line number associated with
the address of the symbol.
For undefined symbols, look up the line number associated with
a relocation entry that refers to the symbol.
If line number information can be determined, it is displayed after
other symbol information.
.It Fl -no-demangle
Do not demangle symbol names (default).
.It Fl -no-sort
Do not sort symbols.
.It Fl -numeric-sort
Sort symbols numerically by address instead of alphabetically by name.
.It Fl -print-armap
For
.Xr ar 1
archives, include the index of the archive's members.
.It Fl -print-file-name
Write the full pathname or library name of an object on each line,
before the rest of the information for a symbol.
If this option is not specified,
.Nm
will only identify an input file once, before its symbols are
listed.
.It Fl -print-size
Print the size of each symbol instead of its value.
.It Fl -radix Ns = Ns Ar radix
Print numeric values using the specified radix.
Supported values for argument
.Ar radix
are
.Sq d
for decimal,
.Sq o
for octal, and
.Sq x
for hexadecimal.
.It Fl -reverse-sort
Reverse the order of the sort.
.It Fl -size-sort
Sort symbols by size instead of alphabetically by name.
.It Fl -undefined-only
Display only undefined symbols.
.It Fl -version
Display the version identifier for
.Nm
and exit.
.It Fl A
Equivalent to specifying option
.Fl -print-file-name .
.It Fl B
Equivalent to specifying option
.Fl -format= Ns Ar bsd .
.It Fl C Op Ar style
Equivalent to specifying option
.Fl -demangle Ns Op = Ns Ar style .
.It Fl D
Equivalent to specifying option
.Fl -dynamic .
.It Fl F Ar format
Equivalent to specifying option
.Fl -format Ns = Ns Ar format .
.It Fl P
Equivalent to specifying option
.Fl -format Ns = Ns Ar posix .
.It Fl S
Equivalent to specifying option
.Fl -print-size .
.It Fl V
Equivalent to specifying option
.Fl -version .
.It Fl a
Equivalent to specifying option
.Fl -debug-syms .
.It Fl e
Only display information for global and static symbols.
.It Fl f
Produce full output (default).
.It Fl g
Only display information about global (external) symbols.
.It Fl h
Equivalent to specifying option
.Fl -help .
.It Fl l
Equivalent to specifying option
.Fl -line-numbers .
.It Fl n
Equivalent to specifying option
.Fl -numeric-sort .
.It Fl o
If POSIX output was specified using the
.Fl F Ar posix
or
.Fl P
options, this option is equivalent to specifying
.Fl -radix Ns = Ns Sq Ar o .
If POSIX output was not specified, this option
acts as a synonym for the
.Fl -print-file-name
option.
.It Fl p
Equivalent to specifying option
.Fl -no-sort .
.It Fl v
Equivalent to option
.Fl n .
.It Fl r
Equivalent to specifying option
.Fl -reverse-sort
.It Fl s
Equivalent to specifying option
.Fl -print-armap .
.It Fl t Ar radix
Equivalent to specifying option
.Fl -radix= Ns Ar radix .
.It Fl u
Equivalent to specifying option
.Fl -undefined-only .
.It Fl x
Write numeric values in hexadecimal (equivalent to -t x).
.El
.Sh OUTPUT FORMAT
.Pp
The
.Nm
utility can present its information in a number of formats, numeric
radices and sort orders.
By default
.Nm
uses BSD style output, a hexadecimal radix, without output sorted
alphabetically by name and without demangling of names.
.Pp
For each symbol listed,
.Nm
presents the following information:
.Bl -bullet -compact
.It
The library or object name, if options
.Fl A
or
.Fl -print-file-name
were specified.
.It
The symbol name.
.It
The type of the symbol denoted by a single character as below:
.Bl -tag -compact -width indent
.It A
A global, absolute symbol.
.It B
A global
.Dq bss
(uninitialized data) symbol.
.It C
A
.Dq common
symbol, representing uninitialized data.
.It D
A global symbol naming initialized data.
.It N
A debugger symbol.
.It R
A read-only data symbol.
.It T
A global text symbol.
.It U
An undefined symbol.
.It V
A weak object.
.It W
A weak reference.
.It a
A local absolute symbol.
.It b
A local
.Dq bss
(uninitialized data) symbol.
.It d
A local data symbol.
.It t
A local text symbol.
.It v
A weak object that is undefined.
.It w
A weak symbol that is undefined.
.It ?
None of the above.
.El
.It
The value of the symbol.
.It
The size of the symbol if applicable.
.It
Line number information, if available and if options
.Fl l
or
.Fl -line-numbers
were specified.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr ar 1 ,
.Xr objdump 1 ,
.Xr ranlib 1 ,
.Xr elf 3
.Sh AUTHORS
The
.Nm
utility and this manual page were written by
.An Hyogeol Lee Aq hyogeollee@gmail.com .

2096
contrib/elftoolchain/nm/nm.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
# $Id: Makefile 2043 2011-10-23 14:49:16Z jkoshy $
TOP= ..
PROG= size
WARNS?= 6
LDADD= -lelftc -lelf
DPADD= ${LIBELFTC} ${LIBELF}
.include "${TOP}/mk/elftoolchain.prog.mk"

View File

@ -0,0 +1,257 @@
.\" Copyright (c) 2007 S.Sam Arun Raj
.\" Copyright (c) 2008,2011 Joseph Koshy
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: size.1 2043 2011-10-23 14:49:16Z jkoshy $
.\"
.Dd August 25, 2011
.Dt SIZE 1
.Os
.Sh NAME
.Nm size
.Nd "display section sizes and total size of ELF objects"
.Sh SYNOPSIS
.Nm
.Op Fl -format= Ns Ar format
.Op Fl -help
.Op Fl -radix= Ns Ar radix
.Op Fl -totals
.Op Fl -version
.Op Fl ABVdhotx
.Op Ar
.Sh DESCRIPTION
The
.Nm
utility
lists the sizes of ELF sections, and optionally the total size, for
each input
.Ar file
specified on the command line.
The
.Nm
utility can operate on ELF objects, on
.Xr ar 1
archives containing ELF objects, and on core dumps.
If no file name is specified on the command-line,
.Pa a.out
is assumed.
.Pp
The
.Nm
utility recognized the following options:
.Bl -tag -width indent
.It Fl -format= Ns Ar format
Display output using the format specified by argument
.Ar format .
Supported values for this argument are:
.Sq berkeley
and
.Sq sysv .
The default output format is
.Sq berkeley .
See
.Sx Display Formats
below for more information.
.It Fl -help
Display a help message and exit.
.It Fl -radix= Ns Ar radix
Display numeric values using the radix specified by argument
.Ar radix .
Supported values for
.Ar radix
are 8, 10 and 16.
The default radix is 10.
.It Fl -totals
Shows cumulative totals of section sizes from all objects.
This option is ignored for System V style output.
.It Fl -version
Display a version identifier and exit.
.It Fl A
Equivalent to specifying option
.Fl -format= Ns Ar sysv .
.It Fl B
Equivalent to specifying option
.Fl -format= Ns Ar berkeley .
.It Fl V
Equivalent to specifying option
.Fl -version .
.It Fl d
Equivalent to specifying option
.Fl -radix= Ns Ar 10 .
.It Fl h
Equivalent to specifying option
.Fl -help .
.It Fl o
Equivalent to specifying option
.Fl -radix= Ns Ar 8 .
.It Fl t
Equivalent to specifying option
.Fl -totals .
.It Fl x
Equivalent to specifying option
.Fl -radix= Ns Ar 16 .
.El
.Sh DISPLAY FORMATS
.Ss Berkeley Style Output
If
.Sq berkeley
style output is in effect, an initial header line naming fields will
be output, followed by one line of output for each ELF object specified
on the command line or found in an archive.
.Pp
Each line will contain the following whitespace separated fields
in order:
.Bl -enum -compact
.It
The size of the text segment in the object.
.It
The size of the data segment in the object.
.It
The size of the
.Sq bss
segment in the object.
.It
The total size of the object in either decimal or octal.
Decimal output is used if the specified output radix for numeric values
is 10 or 16.
Octal output is used if the radix being used for numeric values
is 8.
.It
The total size of the object in hexadecimal.
.It
The file name of the object.
.El
.Pp
If option
.Fl -totals
was specified, an additional line in the same format as above will be
output at the end containing the sum of the respective fields.
The file name field for the line will contain the string
.Sq (TOTALS) .
.Ss System V Style Output
If System V style output is selected,
.Nm
will output the following information for each object:
.Bl -enum -compact
.It
The name of the object followed by a colon.
.It
A header line containing the names of fields of subsequent lines.
.It
One line per section present in the object.
Each line has three fields:
.Bl -enum -compact
.It
The name of the section.
.It
Its size, in the selected radix for numeric values.
.It
The address associated with the section, in the selected numeric radix.
.El
.It
A line whose section name field contains the string
.Sq Total
and whose size field contains the sum of all reported section sizes.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
To display the section sizes for
.Pa /bin/ls
use:
.Bd -literal
$ size /bin/ls
text data bss dec hex filename
20975 540 392 21907 5593 /bin/ls
.Ed
.Pp
To display sizes and total for
.Pa /bin/ls
and
.Pa /bin/dd
in hexadecimal, use:
.Bd -literal
$ size -tx /bin/ls /bin/dd
text data bss dec hex filename
0x51ef 0x21c 0x188 21907 5593 /bin/ls
0x3df5 0x170 0x200 16741 4165 /bin/dd
0x8fe4 0x38c 0x388 38648 96f8 (TOTALS)
.Ed
.Pp
To display section sizes for
.Pa /bin/ls
in System V format use:
.Bd -literal
$ size -A /bin/ls
/bin/ls :
section size addr
\&.interp 21 4194704
\&.note.ABI-tag 24 4194728
\&.hash 624 4194752
\&.dynsym 2088 4195376
\&.dynstr 810 4197464
\&.rela.dyn 120 4198280
\&.rela.plt 1656 4198400
\&.init 19 4200056
\&.plt 1120 4200076
\&.text 15224 4201200
\&.fini 14 4216424
\&.rodata 1472 4216448
\&.data 80 5267456
\&.eh_frame 1624 5267536
\&.dynamic 384 5269160
\&.ctors 16 5269544
\&.dtors 16 5269560
\&.jcr 8 5269576
\&.got 576 5269584
\&.bss 528 5270176
\&.comment 686 0
Total 27110
.Ed
.Sh SEE ALSO
.Xr ar 1 ,
.Xr nm 1 ,
.Xr objdump 1 ,
.Xr readelf 1 ,
.Xr strings 1 ,
.Xr elf 3 ,
.Xr gelf 3
.Rs
.%A "AT&T Unix Systems Labs"
.%T "System V Application Binary Interface"
.%O http://www.sco.com/developers/gabi/
.Re
.Sh HISTORY
The
.Nm
utility first appeared in
.At v6 .
.Sh AUTHORS
.An -nosplit
The
.Nm
utility was re-written by
.An S. Sam Arun Raj Aq samarunraj@gmail.com
This manual page was written by
.An S. Sam Arun Raj Aq samarunraj@gmail.com

View File

@ -0,0 +1,914 @@
/*-
* Copyright (c) 2007 S.Sam Arun Raj
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <assert.h>
#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <getopt.h>
#include <libelftc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: size.c 2350 2011-12-19 10:20:06Z jkoshy $");
#define BUF_SIZE 1024
#define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
#define SIZE_VERSION_STRING "size 1.0"
enum return_code {
RETURN_OK,
RETURN_NOINPUT,
RETURN_DATAERR,
RETURN_USAGE
};
enum output_style {
STYLE_BERKELEY,
STYLE_SYSV
};
enum radix_style {
RADIX_OCTAL,
RADIX_DECIMAL,
RADIX_HEX
};
static uint64_t bss_size, data_size, text_size, total_size;
static uint64_t bss_size_total, data_size_total, text_size_total;
static int show_totals;
static int size_option;
static enum radix_style radix = RADIX_DECIMAL;
static enum output_style style = STYLE_BERKELEY;
static const char *default_args[2] = { "a.out", NULL };
static struct {
int row;
int col;
int *width;
char ***tbl;
} *tb;
enum {
OPT_FORMAT,
OPT_RADIX
};
static struct option size_longopts[] = {
{ "format", required_argument, &size_option, OPT_FORMAT },
{ "help", no_argument, NULL, 'h' },
{ "radix", required_argument, &size_option, OPT_RADIX },
{ "totals", no_argument, NULL, 't' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
};
static void berkeley_calc(GElf_Shdr *);
static void berkeley_footer(const char *, const char *, const char *);
static void berkeley_header(void);
static void berkeley_totals(void);
static int handle_core(char const *, Elf *elf, GElf_Ehdr *);
static void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
static int handle_elf(char const *);
static void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
const char *);
static void show_version(void);
static void sysv_header(const char *, Elf_Arhdr *);
static void sysv_footer(void);
static void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
static void usage(void);
static void tbl_new(int);
static void tbl_print(const char *, int);
static void tbl_print_num(uint64_t, enum radix_style, int);
static void tbl_append(void);
static void tbl_flush(void);
/*
* size utility using elf(3) and gelf(3) API to list section sizes and
* total in elf files. Supports only elf files (core dumps in elf
* included) that can be opened by libelf, other formats are not supported.
*/
int
main(int argc, char **argv)
{
int ch, r, rc;
const char **files, *fn;
rc = RETURN_OK;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization failed: %s",
elf_errmsg(-1));
while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
NULL)) != -1)
switch((char)ch) {
case 'A':
style = STYLE_SYSV;
break;
case 'B':
style = STYLE_BERKELEY;
break;
case 'V':
show_version();
break;
case 'd':
radix = RADIX_DECIMAL;
break;
case 'o':
radix = RADIX_OCTAL;
break;
case 't':
show_totals = 1;
break;
case 'x':
radix = RADIX_HEX;
break;
case 0:
switch (size_option) {
case OPT_FORMAT:
if (*optarg == 's' || *optarg == 'S')
style = STYLE_SYSV;
else if (*optarg == 'b' || *optarg == 'B')
style = STYLE_BERKELEY;
else {
warnx("unrecognized format \"%s\".",
optarg);
usage();
}
break;
case OPT_RADIX:
r = strtol(optarg, NULL, 10);
if (r == 8)
radix = RADIX_OCTAL;
else if (r == 10)
radix = RADIX_DECIMAL;
else if (r == 16)
radix = RADIX_HEX;
else {
warnx("unsupported radix \"%s\".",
optarg);
usage();
}
break;
default:
err(EXIT_FAILURE, "Error in option handling.");
/*NOTREACHED*/
}
break;
case 'h':
case '?':
default:
usage();
/* NOTREACHED */
}
argc -= optind;
argv += optind;
files = (argc == 0) ? default_args : (void *) argv;
while ((fn = *files) != NULL) {
rc = handle_elf(fn);
if (rc != RETURN_OK)
warnx(rc == RETURN_NOINPUT ?
"'%s': No such file" :
"%s: File format not recognized", fn);
files++;
}
if (style == STYLE_BERKELEY) {
if (show_totals)
berkeley_totals();
tbl_flush();
}
return (rc);
}
static Elf_Data *
xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
Elf_Type type, size_t size)
{
Elf_Data src, dst;
src.d_buf = _src;
src.d_type = type;
src.d_version = elfhdr->e_version;
src.d_size = size;
dst.d_buf = _dst;
dst.d_version = elfhdr->e_version;
dst.d_size = size;
return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]));
}
#define NOTE_OFFSET_32(nhdr, namesz, offset) \
((char *)nhdr + sizeof(Elf32_Nhdr) + \
ELF_ALIGN((int32_t)namesz, 4) + offset)
#define NOTE_OFFSET_64(nhdr, namesz, offset) \
((char *)nhdr + sizeof(Elf32_Nhdr) + \
ELF_ALIGN((int32_t)namesz, 8) + offset)
#define PID32(nhdr, namesz, offset) \
(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \
namesz, offset)));
#define PID64(nhdr, namesz, offset) \
(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \
namesz, offset)));
#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \
if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \
offset += ELF_ALIGN((int32_t)descsz, 4) + \
sizeof(Elf32_Nhdr) + \
ELF_ALIGN((int32_t)namesz, 4); \
} else { \
offset += ELF_ALIGN((int32_t)descsz, 8) + \
sizeof(Elf32_Nhdr) + \
ELF_ALIGN((int32_t)namesz, 8); \
} \
} while (0)
/*
* Parse individual note entries inside a PT_NOTE segment.
*/
static void
handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
char **cmd_line)
{
size_t max_size;
uint64_t raw_size;
GElf_Off offset;
static pid_t pid;
uintptr_t ver;
Elf32_Nhdr *nhdr, nhdr_l;
static int reg_pseudo = 0, reg2_pseudo = 0, regxfp_pseudo = 0;
char buf[BUF_SIZE], *data, *name;
if (elf == NULL || elfhdr == NULL || phdr == NULL)
return;
data = elf_rawfile(elf, &max_size);
offset = phdr->p_offset;
while (data != NULL && offset < phdr->p_offset + phdr->p_filesz) {
nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
ELF_T_WORD, sizeof(Elf32_Word)) ||
!xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
ELF_T_WORD, sizeof(Elf32_Word)) ||
!xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
ELF_T_WORD, sizeof(Elf32_Word)))
break;
name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
switch (nhdr_l.n_type) {
case NT_PRSTATUS: {
raw_size = 0;
if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
nhdr_l.n_namesz == 0x8 &&
!strcmp(name,"FreeBSD")) {
if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
raw_size = (uint64_t)*((uint32_t *)
(uintptr_t)(name +
ELF_ALIGN((int32_t)
nhdr_l.n_namesz, 4) + 8));
ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
nhdr_l.n_namesz,0);
if (*((int *)ver) == 1)
pid = PID32(nhdr,
nhdr_l.n_namesz, 24);
} else {
raw_size = *((uint64_t *)(uintptr_t)
(name + ELF_ALIGN((int32_t)
nhdr_l.n_namesz, 8) + 16));
ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
nhdr_l.n_namesz,0);
if (*((int *)ver) == 1)
pid = PID64(nhdr,
nhdr_l.n_namesz, 40);
}
xlatetom(elf, elfhdr, &raw_size, &raw_size,
ELF_T_WORD, sizeof(uint64_t));
xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD,
sizeof(pid_t));
}
if (raw_size != 0 && style == STYLE_SYSV) {
(void) snprintf(buf, BUF_SIZE, "%s/%d",
".reg", pid);
tbl_append();
tbl_print(buf, 0);
tbl_print_num(raw_size, radix, 1);
tbl_print_num(0, radix, 2);
if (!reg_pseudo) {
tbl_append();
tbl_print(".reg", 0);
tbl_print_num(raw_size, radix, 1);
tbl_print_num(0, radix, 2);
reg_pseudo = 1;
text_size_total += raw_size;
}
text_size_total += raw_size;
}
}
break;
case NT_FPREGSET: /* same as NT_PRFPREG */
if (style == STYLE_SYSV) {
(void) snprintf(buf, BUF_SIZE,
"%s/%d", ".reg2", pid);
tbl_append();
tbl_print(buf, 0);
tbl_print_num(nhdr_l.n_descsz, radix, 1);
tbl_print_num(0, radix, 2);
if (!reg2_pseudo) {
tbl_append();
tbl_print(".reg2", 0);
tbl_print_num(nhdr_l.n_descsz, radix,
1);
tbl_print_num(0, radix, 2);
reg2_pseudo = 1;
text_size_total += nhdr_l.n_descsz;
}
text_size_total += nhdr_l.n_descsz;
}
break;
case NT_AUXV:
if (style == STYLE_SYSV) {
tbl_append();
tbl_print(".auxv", 0);
tbl_print_num(nhdr_l.n_descsz, radix, 1);
tbl_print_num(0, radix, 2);
text_size_total += nhdr_l.n_descsz;
}
break;
case NT_PRXFPREG:
if (style == STYLE_SYSV) {
(void) snprintf(buf, BUF_SIZE, "%s/%d",
".reg-xfp", pid);
tbl_append();
tbl_print(buf, 0);
tbl_print_num(nhdr_l.n_descsz, radix, 1);
tbl_print_num(0, radix, 2);
if (!regxfp_pseudo) {
tbl_append();
tbl_print(".reg-xfp", 0);
tbl_print_num(nhdr_l.n_descsz, radix,
1);
tbl_print_num(0, radix, 2);
regxfp_pseudo = 1;
text_size_total += nhdr_l.n_descsz;
}
text_size_total += nhdr_l.n_descsz;
}
break;
case NT_PSINFO:
case NT_PRPSINFO: {
/* FreeBSD 64-bit */
if (nhdr_l.n_descsz == 0x78 &&
!strcmp(name,"FreeBSD")) {
*cmd_line = strdup(NOTE_OFFSET_64(nhdr,
nhdr_l.n_namesz, 33));
/* FreeBSD 32-bit */
} else if (nhdr_l.n_descsz == 0x6c &&
!strcmp(name,"FreeBSD")) {
*cmd_line = strdup(NOTE_OFFSET_32(nhdr,
nhdr_l.n_namesz, 25));
}
/* Strip any trailing spaces */
if (*cmd_line != NULL) {
char *s;
s = *cmd_line + strlen(*cmd_line);
while (s > *cmd_line) {
if (*(s-1) != 0x20) break;
s--;
}
*s = 0;
}
break;
}
case NT_PSTATUS:
case NT_LWPSTATUS:
default:
break;
}
NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
}
}
/*
* Handles program headers except for PT_NOTE, when sysv output stlye is
* choosen, prints out the segment name and length. For berkely output
* style only PT_LOAD segments are handled, and text,
* data, bss size is calculated for them.
*/
static void
handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
uint32_t idx, const char *name)
{
uint64_t addr, size;
int split;
char buf[BUF_SIZE];
if (elf == NULL || elfhdr == NULL || phdr == NULL)
return;
size = addr = 0;
split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) &&
(phdr->p_memsz > phdr->p_filesz);
if (style == STYLE_SYSV) {
(void) snprintf(buf, BUF_SIZE,
"%s%d%s", name, idx, (split ? "a" : ""));
tbl_append();
tbl_print(buf, 0);
tbl_print_num(phdr->p_filesz, radix, 1);
tbl_print_num(phdr->p_vaddr, radix, 2);
text_size_total += phdr->p_filesz;
if (split) {
size = phdr->p_memsz - phdr->p_filesz;
addr = phdr->p_vaddr + phdr->p_filesz;
(void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
idx, "b");
text_size_total += phdr->p_memsz - phdr->p_filesz;
tbl_append();
tbl_print(buf, 0);
tbl_print_num(size, radix, 1);
tbl_print_num(addr, radix, 2);
}
} else {
if (phdr->p_type != PT_LOAD)
return;
if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
data_size += phdr->p_filesz;
if (split)
data_size += phdr->p_memsz - phdr->p_filesz;
} else {
text_size += phdr->p_filesz;
if (split)
text_size += phdr->p_memsz - phdr->p_filesz;
}
}
}
/*
* Given a core dump file, this function maps program headers to segments.
*/
static int
handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
{
GElf_Phdr phdr;
uint32_t i;
char *core_cmdline;
const char *seg_name;
if (name == NULL || elf == NULL || elfhdr == NULL)
return (RETURN_DATAERR);
if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
return (RETURN_DATAERR);
seg_name = core_cmdline = NULL;
if (style == STYLE_SYSV)
sysv_header(name, NULL);
else
berkeley_header();
for (i = 0; i < elfhdr->e_phnum; i++) {
if (gelf_getphdr(elf, i, &phdr) != NULL) {
if (phdr.p_type == PT_NOTE) {
handle_phdr(elf, elfhdr, &phdr, i, "note");
handle_core_note(elf, elfhdr, &phdr,
&core_cmdline);
} else {
switch(phdr.p_type) {
case PT_NULL:
seg_name = "null";
break;
case PT_LOAD:
seg_name = "load";
break;
case PT_DYNAMIC:
seg_name = "dynamic";
break;
case PT_INTERP:
seg_name = "interp";
break;
case PT_SHLIB:
seg_name = "shlib";
break;
case PT_PHDR:
seg_name = "phdr";
break;
case PT_GNU_EH_FRAME:
seg_name = "eh_frame_hdr";
break;
case PT_GNU_STACK:
seg_name = "stack";
break;
default:
seg_name = "segment";
}
handle_phdr(elf, elfhdr, &phdr, i, seg_name);
}
}
}
if (style == STYLE_BERKELEY) {
if (core_cmdline != NULL) {
berkeley_footer(core_cmdline, name,
"core file invoked as");
} else {
berkeley_footer(core_cmdline, name, "core file");
}
} else {
sysv_footer();
if (core_cmdline != NULL) {
(void) printf(" (core file invoked as %s)\n\n",
core_cmdline);
} else {
(void) printf(" (core file)\n\n");
}
}
free(core_cmdline);
return (RETURN_OK);
}
/*
* Given an elf object,ar(1) filename, and based on the output style
* and radix format the various sections and their length will be printed
* or the size of the text, data, bss sections will be printed out.
*/
static int
handle_elf(char const *name)
{
GElf_Ehdr elfhdr;
GElf_Shdr shdr;
Elf *elf, *elf1;
Elf_Arhdr *arhdr;
Elf_Scn *scn;
Elf_Cmd elf_cmd;
int exit_code, fd;
if (name == NULL)
return (RETURN_NOINPUT);
if ((fd = open(name, O_RDONLY, 0)) < 0)
return (RETURN_NOINPUT);
elf_cmd = ELF_C_READ;
elf1 = elf_begin(fd, elf_cmd, NULL);
while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
arhdr = elf_getarhdr(elf);
if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
(void) elf_end(elf);
(void) elf_end(elf1);
(void) close(fd);
return (RETURN_DATAERR);
}
if (elf_kind(elf) != ELF_K_ELF ||
(gelf_getehdr(elf, &elfhdr) == NULL)) {
elf_cmd = elf_next(elf);
(void) elf_end(elf);
warnx("%s: File format not recognized",
arhdr->ar_name);
continue;
}
/* Core dumps are handled seperately */
if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
exit_code = handle_core(name, elf, &elfhdr);
(void) elf_end(elf);
(void) elf_end(elf1);
(void) close(fd);
return (exit_code);
} else {
scn = NULL;
if (style == STYLE_BERKELEY) {
berkeley_header();
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != NULL)
berkeley_calc(&shdr);
}
} else {
sysv_header(name, arhdr);
scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) != NULL)
sysv_calc(elf, &elfhdr, &shdr);
}
}
if (style == STYLE_BERKELEY) {
if (arhdr != NULL) {
berkeley_footer(name, arhdr->ar_name,
"ex");
} else {
berkeley_footer(name, NULL, "ex");
}
} else {
sysv_footer();
}
}
elf_cmd = elf_next(elf);
(void) elf_end(elf);
}
(void) elf_end(elf1);
(void) close(fd);
return (RETURN_OK);
}
/*
* Sysv formatting helper functions.
*/
static void
sysv_header(const char *name, Elf_Arhdr *arhdr)
{
text_size_total = 0;
if (arhdr != NULL)
(void) printf("%s (ex %s):\n", arhdr->ar_name, name);
else
(void) printf("%s :\n", name);
tbl_new(3);
tbl_append();
tbl_print("section", 0);
tbl_print("size", 1);
tbl_print("addr", 2);
}
static void
sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
{
char *section_name;
section_name = elf_strptr(elf, elfhdr->e_shstrndx,
(size_t) shdr->sh_name);
if ((shdr->sh_type == SHT_SYMTAB ||
shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
return;
tbl_append();
tbl_print(section_name, 0);
tbl_print_num(shdr->sh_size, radix, 1);
tbl_print_num(shdr->sh_addr, radix, 2);
text_size_total += shdr->sh_size;
}
static void
sysv_footer(void)
{
tbl_append();
tbl_print("Total", 0);
tbl_print_num(text_size_total, radix, 1);
tbl_flush();
putchar('\n');
}
/*
* berkeley style output formatting helper functions.
*/
static void
berkeley_header(void)
{
static int printed;
text_size = data_size = bss_size = 0;
if (!printed) {
tbl_new(6);
tbl_append();
tbl_print("text", 0);
tbl_print("data", 1);
tbl_print("bss", 2);
if (radix == RADIX_OCTAL)
tbl_print("oct", 3);
else
tbl_print("dec", 3);
tbl_print("hex", 4);
tbl_print("filename", 5);
printed = 1;
}
}
static void
berkeley_calc(GElf_Shdr *shdr)
{
if (shdr != NULL) {
if (!(shdr->sh_flags & SHF_ALLOC))
return;
if ((shdr->sh_flags & SHF_ALLOC) &&
((shdr->sh_flags & SHF_EXECINSTR) ||
!(shdr->sh_flags & SHF_WRITE)))
text_size += shdr->sh_size;
else if ((shdr->sh_flags & SHF_ALLOC) &&
(shdr->sh_flags & SHF_WRITE) &&
(shdr->sh_type != SHT_NOBITS))
data_size += shdr->sh_size;
else
bss_size += shdr->sh_size;
}
}
static void
berkeley_totals(void)
{
long unsigned int grand_total;
grand_total = text_size_total + data_size_total + bss_size_total;
tbl_append();
tbl_print_num(text_size_total, radix, 0);
tbl_print_num(data_size_total, radix, 1);
tbl_print_num(bss_size_total, radix, 2);
if (radix == RADIX_OCTAL)
tbl_print_num(grand_total, RADIX_OCTAL, 3);
else
tbl_print_num(grand_total, RADIX_DECIMAL, 3);
tbl_print_num(grand_total, RADIX_HEX, 4);
}
static void
berkeley_footer(const char *name, const char *ar_name, const char *msg)
{
char buf[BUF_SIZE];
total_size = text_size + data_size + bss_size;
if (show_totals) {
text_size_total += text_size;
bss_size_total += bss_size;
data_size_total += data_size;
}
tbl_append();
tbl_print_num(text_size, radix, 0);
tbl_print_num(data_size, radix, 1);
tbl_print_num(bss_size, radix, 2);
if (radix == RADIX_OCTAL)
tbl_print_num(total_size, RADIX_OCTAL, 3);
else
tbl_print_num(total_size, RADIX_DECIMAL, 3);
tbl_print_num(total_size, RADIX_HEX, 4);
if (ar_name != NULL && name != NULL)
(void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
name);
else if (ar_name != NULL && name == NULL)
(void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
else
(void) snprintf(buf, BUF_SIZE, "%s", name);
tbl_print(buf, 5);
}
static void
tbl_new(int col)
{
assert(tb == NULL);
assert(col > 0);
if ((tb = calloc(1, sizeof(*tb))) == NULL)
err(EXIT_FAILURE, "calloc");
if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
err(EXIT_FAILURE, "calloc");
if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
err(EXIT_FAILURE, "calloc");
tb->col = col;
tb->row = 0;
}
static void
tbl_print(const char *s, int col)
{
int len;
assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
err(EXIT_FAILURE, "strdup");
len = strlen(s);
if (len > tb->width[col])
tb->width[col] = len;
}
static void
tbl_print_num(uint64_t num, enum radix_style rad, int col)
{
char buf[BUF_SIZE];
(void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
tbl_print(buf, col);
}
static void
tbl_append(void)
{
int i;
assert(tb != NULL && tb->col > 0);
tb->row++;
for (i = 0; i < tb->col; i++) {
tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
if (tb->tbl[i] == NULL)
err(EXIT_FAILURE, "realloc");
tb->tbl[i][tb->row - 1] = NULL;
}
}
static void
tbl_flush(void)
{
const char *str;
int i, j;
if (tb == NULL)
return;
assert(tb->col > 0);
for (i = 0; i < tb->row; i++) {
if (style == STYLE_BERKELEY)
printf(" ");
for (j = 0; j < tb->col; j++) {
str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
if (style == STYLE_SYSV && j == 0)
printf("%-*s", tb->width[j], str);
else if (style == STYLE_BERKELEY && j == tb->col - 1)
printf("%s", str);
else
printf("%*s", tb->width[j], str);
if (j == tb->col -1)
putchar('\n');
else
printf(" ");
}
}
for (i = 0; i < tb->col; i++) {
for (j = 0; j < tb->row; j++) {
if (tb->tbl[i][j])
free(tb->tbl[i][j]);
}
free(tb->tbl[i]);
}
free(tb->tbl);
free(tb->width);
free(tb);
tb = NULL;
}
#define USAGE_MESSAGE "\
Usage: %s [options] file ...\n\
Display sizes of ELF sections.\n\n\
Options:\n\
--format=format Display output in specified format. Supported\n\
values are `berkeley' and `sysv'.\n\
--help Display this help message and exit.\n\
--radix=radix Display numeric values in the specified radix.\n\
Supported values are: 8, 10 and 16.\n\
--totals Show cumulative totals of section sizes.\n\
--version Display a version identifier and exit.\n\
-A Equivalent to `--format=sysv'.\n\
-B Equivalent to `--format=berkeley'.\n\
-V Equivalent to `--version'.\n\
-d Equivalent to `--radix=10'.\n\
-h Same as option --help.\n\
-o Equivalent to `--radix=8'.\n\
-t Equivalent to option --totals.\n\
-x Equivalent to `--radix=16'.\n"
static void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(EXIT_FAILURE);
}
static void
show_version(void)
{
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(EXIT_SUCCESS);
}

View File

@ -0,0 +1,11 @@
# $Id: Makefile 2044 2011-10-23 14:52:59Z jkoshy $
TOP= ..
PROG= strings
WARNS?= 6
DPADD= ${LIBELFTC} ${LIBELF}
LDADD= -lelftc -lelf
.include "${TOP}/mk/elftoolchain.prog.mk"

View File

@ -0,0 +1,162 @@
.\" Copyright (c) 2007 S.Sam Arun Raj
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $Id: strings.1 2352 2011-12-19 11:21:10Z jkoshy $
.\"
.Dd December 19, 2011
.Dt STRINGS 1
.Os
.Sh NAME
.Nm strings
.Nd "print the strings of printable characters in files"
.Sh SYNOPSIS
.Nm
.Op Fl a | Fl -all
.Op Fl e Ar encoding | Fl -encoding= Ns Ar encoding
.Op Fl f | Fl -print-file-name
.Op Fl h | Fl -help
.Op Fl n Ar number | Fl -bytes= Ns Ar number | Fl Ar number
.Op Fl o
.Op Fl t Ar radix | Fl -radix= Ns Ar radix
.Op Fl v | Fl -version
.Op Ar
.Sh DESCRIPTION
For each
.Ar file
specified, the
.Nm
utility prints contiguous sequences of printable
characters that are at least
.Va n
characters long and are followed by an unprintable character.
The default value of
.Va n
is 4.
By default, the
.Nm
utility only scans the initialized and loaded sections of ELF objects;
for other file types, the entire file is scanned.
The
.Nm
utility is mainly used for determining the contents of non-text files.
.Pp
If no file name is specified as an argument, standard input is read.
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl a | Fl -all
For ELF objects, scan the entire file for printable strings.
.It Fl e Ar encoding | Fl -encoding= Ns Ar encoding
Select the character encoding to be used while searching for strings.
Valid values for argument
.Ar encoding
are:
.Bl -tag -width indent -compact
.It Ar s
for single 7-bit-byte characters (ASCII, ISO 8859).
.It Ar S
for single 8-bit-byte characters.
.It Ar l
for 16-bit little-endian.
.It Ar b
for 16-bit big-endian.
.It Ar L
for 32-bit little-endian.
.It Ar B
for 32-bit big-endian.
.El
The default is to assume that characters are encoded using a single
7-bit byte.
.It Fl f | Fl -print-file-name
Print the name of the file before each string.
.It Fl h | Fl -help
Print a usage summary and exit.
.It Xo
.Fl n Ar number |
.Fl -bytes= Ns Ar number |
.Fl Ar number
.Xc
Print the contiguous character sequence of at least
.Ar number
characters long, instead of the default of 4 characters.
.It Fl o
Equivalent to specifying
.Fl t Ar o .
.It Fl t Ar radix | Fl -radix= Ns Ar radix
Print the offset from the start of the file before each string
using the specified radix.
Valid values for argument
.Ar radix
are:
.Bl -tag -width indent -compact
.It Ar d
for decimal
.It Ar o
for octal
.It Ar x
for hexadecimal
.El
.It Fl v | Fl -version
Display a version identifier and exit.
.El
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES
To display strings in
.Pa /bin/ls
use:
.Dl "$ strings /bin/ls"
.Pp
To display strings in all sections of
.Pa /bin/ln
use:
.Dl "$ strings -a /bin/ln"
.Pp
To display strings in all sections of
.Pa /bin/cat
prefixed with the filename and the offset within the file use:
.Dl "$ strings -a -f -t x /bin/cat"
.Sh SEE ALSO
.Xr ar 1 ,
.Xr nm 1 ,
.Xr objdump 1 ,
.Xr ranlib ,
.Xr readelf 1 ,
.Xr size 1
.Sh HISTORY
The first FreeBSD
.Nm
utility appeared in
.Fx v3.
It was later discontinued in
.Fx v5 ,
when i386-only a.out format was dropped in favor of ELF.
.Sh AUTHORS
.An -nosplit
The
.Nm
utility was re-written by
.An S.Sam Arun Raj Aq samarunraj@gmail.com .
This manual page was written by
.An S.Sam Arun Raj Aq samarunraj@gmail.com .

View File

@ -0,0 +1,454 @@
/*-
* Copyright (c) 2007 S.Sam Arun Raj
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libelf.h>
#include <libelftc.h>
#include <gelf.h>
#include "_elftc.h"
ELFTC_VCSID("$Id: strings.c 2351 2011-12-19 11:20:37Z jkoshy $");
enum return_code {
RETURN_OK,
RETURN_NOINPUT,
RETURN_SOFTWARE
};
enum radix_style {
RADIX_DECIMAL,
RADIX_HEX,
RADIX_OCTAL
};
enum encoding_style {
ENCODING_7BIT,
ENCODING_8BIT,
ENCODING_16BIT_BIG,
ENCODING_16BIT_LITTLE,
ENCODING_32BIT_BIG,
ENCODING_32BIT_LITTLE
};
#define PRINTABLE(c) \
((c) >= 0 && (c) <= 255 && \
((c) == '\t' || isprint((c)) || \
(encoding == ENCODING_8BIT && (c) > 127)))
int encoding_size, entire_file, min_len, show_filename, show_loc;
enum encoding_style encoding;
enum radix_style radix;
static struct option strings_longopts[] = {
{ "all", no_argument, NULL, 'a'},
{ "bytes", required_argument, NULL, 'n'},
{ "encoding", required_argument, NULL, 'e'},
{ "help", no_argument, NULL, 'h'},
{ "print-file-name", no_argument, NULL, 'f'},
{ "radix", required_argument, NULL, 't'},
{ "version", no_argument, NULL, 'v'},
{ NULL, 0, NULL, 0 }
};
long getcharacter(void);
int handle_file(const char *);
int handle_elf(const char *, int);
int handle_binary(const char *, int);
int find_strings(const char *, off_t, off_t);
void show_version(void);
void usage(void);
/*
* strings(1) extracts text(contiguous printable characters)
* from elf and binary files.
*/
int
main(int argc, char **argv)
{
int ch, rc;
rc = RETURN_OK;
min_len = 0;
encoding_size = 1;
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization failed: %s",
elf_errmsg(-1));
while ((ch = getopt_long(argc, argv, "1234567890ae:fhn:ot:Vv",
strings_longopts, NULL)) != -1)
switch((char)ch) {
case 'a':
entire_file = 1;
break;
case 'e':
if (*optarg == 's') {
encoding = ENCODING_7BIT;
} else if (*optarg == 'S') {
encoding = ENCODING_8BIT;
} else if (*optarg == 'b') {
encoding = ENCODING_16BIT_BIG;
encoding_size = 2;
} else if (*optarg == 'B') {
encoding = ENCODING_32BIT_BIG;
encoding_size = 4;
} else if (*optarg == 'l') {
encoding = ENCODING_16BIT_LITTLE;
encoding_size = 2;
} else if (*optarg == 'L') {
encoding = ENCODING_32BIT_LITTLE;
encoding_size = 4;
} else
usage();
/* NOTREACHED */
break;
case 'f':
show_filename = 1;
break;
case 'n':
min_len = (int)strtoimax(optarg, (char**)NULL, 10);
break;
case 'o':
show_loc = 1;
radix = RADIX_OCTAL;
break;
case 't':
show_loc = 1;
if (*optarg == 'd')
radix = RADIX_DECIMAL;
else if (*optarg == 'o')
radix = RADIX_OCTAL;
else if (*optarg == 'x')
radix = RADIX_HEX;
else
usage();
/* NOTREACHED */
break;
case 'v':
case 'V':
show_version();
/* NOTREACHED */
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
min_len *= 10;
min_len += ch - '0';
break;
case 'h':
case '?':
default:
usage();
/* NOTREACHED */
}
argc -= optind;
argv += optind;
if (!min_len)
min_len = 4;
if (!*argv)
rc = handle_file("{standard input}");
else while (*argv) {
rc = handle_file(*argv);
argv++;
}
return (rc);
}
int
handle_file(const char *name)
{
int fd, rt;
if (name == NULL)
return (RETURN_NOINPUT);
if (strcmp("{standard input}", name) != 0) {
if (freopen(name, "rb", stdin) == NULL) {
warnx("'%s': %s", name, strerror(errno));
return (RETURN_NOINPUT);
}
} else {
return (find_strings(name, (off_t)0, (off_t)0));
}
fd = fileno(stdin);
if (fd < 0)
return (RETURN_NOINPUT);
rt = handle_elf(name, fd);
return (rt);
}
/*
* Files not understood by handle_elf, will be passed off here and will
* treated as a binary file. This would include text file, core dumps ...
*/
int
handle_binary(const char *name, int fd)
{
struct stat buf;
memset(&buf, 0, sizeof(struct stat));
(void) lseek(fd, (off_t)0, SEEK_SET);
if (!fstat(fd, &buf))
return (find_strings(name, (off_t)0, buf.st_size));
return (RETURN_SOFTWARE);
}
/*
* Will analyse a file to see if it ELF, other files including ar(1),
* core dumps are passed off and treated as flat binary files. Unlike
* GNU size in FreeBSD this routine will not treat ELF object from
* different archs as flat binary files(has to overridden using -a).
*/
int
handle_elf(const char *name, int fd)
{
GElf_Ehdr elfhdr;
GElf_Shdr shdr;
Elf *elf;
Elf_Scn *scn;
int rc;
rc = RETURN_OK;
/* If entire file is choosen, treat it as a binary file */
if (entire_file)
return (handle_binary(name, fd));
(void) lseek(fd, (off_t)0, SEEK_SET);
elf = elf_begin(fd, ELF_C_READ, NULL);
if (elf_kind(elf) != ELF_K_ELF) {
(void) elf_end(elf);
return (handle_binary(name, fd));
}
if (gelf_getehdr(elf, &elfhdr) == NULL) {
(void) elf_end(elf);
warnx("%s: ELF file could not be processed", name);
return (RETURN_SOFTWARE);
}
if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
(void) elf_end(elf);
return (handle_binary(name, fd));
} else {
scn = NULL;
while ((scn = elf_nextscn(elf, scn)) != NULL) {
if (gelf_getshdr(scn, &shdr) == NULL)
continue;
if (shdr.sh_type != SHT_NOBITS &&
(shdr.sh_flags & SHF_ALLOC) != 0) {
rc = find_strings(name, shdr.sh_offset,
shdr.sh_size);
}
}
}
(void) elf_end(elf);
return (rc);
}
/*
* Retrieves a character from input stream based on the encoding
* type requested.
*/
long
getcharacter(void)
{
long rt;
int i;
char buf[4], c;
rt = EOF;
for(i = 0; i < encoding_size; i++) {
c = getc(stdin);
if (feof(stdin))
return (EOF);
buf[i] = c;
}
switch(encoding) {
case ENCODING_7BIT:
case ENCODING_8BIT:
rt = buf[0];
break;
case ENCODING_16BIT_BIG:
rt = (buf[0] << 8) | buf[1];
break;
case ENCODING_16BIT_LITTLE:
rt = buf[0] | (buf[1] << 8);
break;
case ENCODING_32BIT_BIG:
rt = ((long) buf[0] << 24) | ((long) buf[1] << 16) |
((long) buf[2] << 8) | buf[3];
break;
case ENCODING_32BIT_LITTLE:
rt = buf[0] | ((long) buf[1] << 8) | ((long) buf[2] << 16) |
((long) buf[3] << 24);
break;
}
return (rt);
}
/*
* Input stream stdin is read until the end of file is reached or until
* the section size is reached in case of ELF files. Contiguous
* characters of >= min_size(default 4) will be displayed.
*/
int
find_strings(const char *name, off_t offset, off_t size)
{
off_t cur_off, start_off;
char *obuf;
long c;
int i;
if ((obuf = (char*)calloc(1, min_len + 1)) == NULL) {
(void) fprintf(stderr, "Unable to allocate memory: %s\n",
strerror(errno));
return (RETURN_SOFTWARE);
}
(void) fseeko(stdin, offset, SEEK_SET);
cur_off = offset;
start_off = 0;
while(1) {
if ((offset + size) && (cur_off >= offset + size))
break;
start_off = cur_off;
memset(obuf, 0, min_len+1);
for(i = 0; i < min_len; i++) {
c = getcharacter();
if (c == EOF && feof(stdin))
goto _exit1;
if (PRINTABLE(c)) {
obuf[i] = c;
obuf[i+1] = 0;
cur_off += encoding_size;
} else {
if (encoding == ENCODING_8BIT &&
(uint8_t)c > 127) {
obuf[i] = c;
obuf[i+1] = 0;
cur_off += encoding_size;
continue;
}
cur_off += encoding_size;
break;
}
}
if (i >= min_len && ((cur_off <= offset + size) ||
!(offset + size))) {
if (show_filename)
printf ("%s: ", name);
if (show_loc) {
switch(radix) {
case RADIX_DECIMAL:
(void) printf("%7ju ",
(uintmax_t)start_off);
break;
case RADIX_HEX:
(void) printf("%7jx ",
(uintmax_t)start_off);
break;
case RADIX_OCTAL:
(void) printf("%7jo ",
(uintmax_t)start_off);
break;
}
}
printf("%s", obuf);
while(1) {
if ((offset + size) &&
(cur_off >= offset + size))
break;
c = getcharacter();
cur_off += encoding_size;
if (encoding == ENCODING_8BIT &&
(uint8_t)c > 127) {
putchar(c);
continue;
}
if (!PRINTABLE(c) || c == EOF)
break;
putchar(c);
}
putchar('\n');
}
}
_exit1:
free(obuf);
return (RETURN_OK);
}
#define USAGE_MESSAGE "\
Usage: %s [options] [file...]\n\
Print contiguous sequences of printable characters.\n\n\
Options:\n\
-a | --all Scan the entire file for strings.\n\
-e ENC | --encoding=ENC Select the character encoding to use.\n\
-f | --print-file-name Print the file name before each string.\n\
-h | --help Print a help message and exit.\n\
-n N | --bytes=N | -N Print sequences with 'N' or more characters.\n\
-o Print offsets in octal.\n\
-t R | --radix=R Print offsets using the radix named by 'R'.\n\
-v | --version Print a version identifier and exit.\n"
void
usage(void)
{
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
exit(EXIT_FAILURE);
}
void
show_version(void)
{
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
exit(EXIT_SUCCESS);
}

23
contrib/libucl/COPYING Normal file
View File

@ -0,0 +1,23 @@
Copyright (c) 2013-2014, Vsevolod Stakhov <vsevolod@highsecure.ru>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -4,3 +4,19 @@
- Streamline emitter has been added, so it is now possible to output partial `ucl` objects
- Emitter now is more flexible due to emitter_context structure
### 0.5.1
- Fixed number of bugs and memory leaks
### 0.5.2
- Allow userdata objects to be emitted and destructed
- Use userdata objects to store lua function references
### Libucl 0.6
- Reworked macro interface
### Libucl 0.6.1
- Various utilities fixes

View File

@ -4,4 +4,8 @@ EXTRA_DIST = uthash README.md
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libucl.pc
SUBDIRS = src tests utils doc
if LUA_SUB
LUA_SUBDIR = lua
endif
SUBDIRS = src tests utils doc $(LUA_SUBDIR)

View File

@ -33,6 +33,7 @@ OBJECTS = $(OBJDIR)/ucl_hash.o \
$(OBJDIR)/ucl_util.o \
$(OBJDIR)/ucl_parser.o \
$(OBJDIR)/ucl_emitter.o \
$(OBJDIR)/ucl_emitter_utils.o \
$(OBJDIR)/ucl_schema.o \
$(OBJDIR)/xxhash.o
@ -51,6 +52,8 @@ $(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
$(OBJDIR)/ucl_emitter_utils.o: $(SRCDIR)/ucl_emitter_utils.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_emitter_utils.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter_utils.c
$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
$(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
@ -61,7 +64,7 @@ $(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS)
clean:
$(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate
$(RMDIR) $(OBJDIR)
# Utils
chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
@ -75,7 +78,7 @@ test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(
run-test: test
TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
$(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)

View File

@ -223,15 +223,57 @@ UCL supports external macros both multiline and single line ones:
....
};
```
There are two internal macros provided by UCL:
* `include` - read a file `/path/to/file` or an url `http://example.com/file` and include it to the current place of
UCL configuration;
* `try\_include` - try to read a file or url and include it but do not create a fatal error if a file or url is not accessible;
* `includes` - read a file or an url like the previous macro, but fetch and check the signature file (which is obtained
by `.sig` suffix appending).
Moreover, each macro can accept an optional list of arguments in braces. These
arguments themselves are the UCL object that is parsed and passed to a macro as
options:
Public keys which are used for the last command are specified by the concrete UCL user.
```nginx
.macro(param=value) "something";
.macro(param={key=value}) "something";
.macro(.include "params.conf") "something";
.macro(#this is multiline macro
param = [value1, value2]) "something";
.macro(key="()") "something";
```
UCL also provide a convenient `include` macro to load content from another files
to the current UCL object. This macro accepts either path to file:
```nginx
.include "/full/path.conf"
.include "./relative/path.conf"
.include "${CURDIR}/path.conf"
```
or URL (if ucl is built with url support provided by either `libcurl` or `libfetch`):
.include "http://example.com/file.conf"
`.include` macro supports a set of options:
* `try` (default: **false**) - if this option is `true` than UCL treats errors on loading of
this file as non-fatal. For example, such a file can be absent but it won't stop the parsing
of the top-level document.
* `sign` (default: **false**) - if this option is `true` UCL loads and checks the signature for
a file from path named `<FILEPATH>.sig`. Trusted public keys should be provided for UCL API after
parser is created but before any configurations are parsed.
* `glob` (default: **false**) - if this option is `true` UCL treats the filename as GLOB pattern and load
all files that matches the specified pattern (normally the format of patterns is defined in `glob` manual page
for your operating system). This option is meaningless for URL includes.
* `url` (default: **true**) - allow URL includes.
* `priority` (default: 0) - specify priority for the include (see below).
Priorities are used by UCL parser to manage the policy of objects rewriting during including other files
as following:
* If we have two objects with the same priority then we form an implicit array
* If a new object has bigger priority then we overwrite an old one
* If a new object has lower priority then we ignore it
By default, the priority of top-level object is set to zero (lowest priority). Currently,
you can define up to 16 priorities (from 0 to 15). Includes with bigger priorities will
rewrite keys from the objects with lower priorities as specified by the policy.
### Variables support
@ -317,7 +359,7 @@ ucl: emitted compact json in 0.0991 seconds
ucl: emitted yaml in 0.1354 seconds
```
You can do your own benchmarks by running `make test` in libucl top directory.
You can do your own benchmarks by running `make check` in libucl top directory.
## Conclusion

View File

@ -1,8 +1,8 @@
PROJECT(libucl C)
SET(LIBUCL_VERSION_MAJOR 0)
SET(LIBUCL_VERSION_MINOR 2)
SET(LIBUCL_VERSION_PATCH 9)
SET(LIBUCL_VERSION_MINOR 5)
SET(LIBUCL_VERSION_PATCH 0)
SET(LIBUCL_VERSION "${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}")
@ -86,6 +86,8 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../uthash")
SET(UCLSRC ../src/ucl_util.c
../src/ucl_parser.c
../src/ucl_emitter.c
../src/ucl_emitter_streamline.c
../src/ucl_emitter_utils.c
../src/ucl_hash.c
../src/ucl_schema.c
../src/xxhash.c)
@ -98,6 +100,18 @@ ENDIF (BUILD_SHARED_LIBS)
ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC})
SET_TARGET_PROPERTIES(ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
IF(WITH_LUA)
SET(UCL_LUA_SRC ../lua/lua_ucl.c)
ADD_LIBRARY(lua-ucl ${LIB_TYPE} ${UCL_LUA_SRC})
IF(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl "${LUAJIT_LIBRARY}")
ELSE(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl "${LUA_LIBRARY}")
ENDIF(ENABLE_LUAJIT MATCHES "ON")
TARGET_LINK_LIBRARIES(lua-ucl ucl)
SET_TARGET_PROPERTIES(lua-ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
ENDIF(WITH_LUA)
IF(HAVE_FETCH_H)
TARGET_LINK_LIBRARIES(ucl fetch)
ELSE(HAVE_FETCH_H)

View File

@ -1,12 +1,13 @@
m4_define([maj_ver], [0])
m4_define([med_ver], [5])
m4_define([min_ver], [0])
m4_define([so_version], [2:0:0])
m4_define([med_ver], [6])
m4_define([min_ver], [1])
m4_define([so_version], [3:0:1])
m4_define([ucl_version], [maj_ver.med_ver.min_ver])
AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
AC_CONFIG_SRCDIR([configure.ac])
AM_INIT_AUTOMAKE([1.11 foreign silent-rules -Wall -Wportability no-dist-gzip dist-xz])
AM_INIT_AUTOMAKE([1.11 foreign -Wall -Wportability no-dist-gzip dist-xz])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
UCL_VERSION=ucl_version
SO_VERSION=so_version
@ -57,6 +58,9 @@ AC_ARG_ENABLE([regex], AS_HELP_STRING([--enable-regex],
AC_ARG_ENABLE([signatures], AS_HELP_STRING([--enable-signatures],
[Enable signatures check (requires openssl) @<:@default=no@:>@]), [],
[enable_signatures=no])
AC_ARG_ENABLE([lua], AS_HELP_STRING([--enable-lua],
[Enable lua API build (requires lua libraries and headers) @<:@default=no@:>@]), [],
[enable_lua=no])
AC_ARG_ENABLE([utils],
AS_HELP_STRING([--enable-utils], [Build and install utils @<:@default=no@:>@]),
[case "${enableval}" in
@ -99,6 +103,21 @@ AS_IF([test "x$enable_regex" = "xyes"], [
])
AC_SUBST(LIBREGEX_LIB)
AS_IF([test "x$enable_lua" = "xyes"], [
AX_PROG_LUA([5.1], [], [
AX_LUA_HEADERS([
AX_LUA_LIBS([
AC_DEFINE(HAVE_LUA, 1, [Define to 1 for lua support.])
with_lua="yes"
], [AC_MSG_ERROR([unable to find the lua libraries])
])
], [AC_MSG_ERROR([unable to find the lua header files])
])
], [AC_MSG_ERROR([unable to find the lua interpreter])])
], [with_lua="no"])
AM_CONDITIONAL([LUA_SUB], [test "$with_lua" = "yes"])
AS_IF([test "x$enable_urls" = "xyes"], [
AC_CHECK_HEADER([fetch.h], [
AC_DEFINE(HAVE_FETCH_H, 1, [Define to 1 if you have the <fetch.h> header file.])
@ -155,9 +174,11 @@ AC_LINK_IFELSE([
AC_CONFIG_FILES(Makefile \
src/Makefile \
lua/Makefile
tests/Makefile \
utils/Makefile \
doc/Makefile \
lua/libucl.rockspec \
libucl.pc)
AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
AC_OUTPUT

View File

@ -0,0 +1,194 @@
## Module `ucl`
This lua module allows to parse objects from strings and to store data into
ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
Example:
~~~lua
local ucl = require("ucl")
local parser = ucl.parser()
local res,err = parser:parse_string('{key=value}')
if not res then
print('parser error: ' .. err)
else
local obj = parser:get_object()
local got = ucl.to_format(obj, 'json')
endif
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
~~~
###Brief content:
**Functions**:
> [`ucl_object_push_lua(L, obj, allow_array)`](#function-ucl_object_push_lual-obj-allow_array)
> [`ucl.to_format(var, format)`](#function-uclto_formatvar-format)
**Methods**:
> [`parser:parse_file(name)`](#method-parserparse_filename)
> [`parser:parse_string(input)`](#method-parserparse_stringinput)
> [`parser:get_object()`](#method-parserget_object)
## Functions
The module `ucl` defines the following functions.
### Function `ucl_object_push_lua(L, obj, allow_array)`
This is a `C` function to push `UCL` object as lua variable. This function
converts `obj` to lua representation using the following conversions:
- *scalar* values are directly presented by lua objects
- *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
this can be used to pass functions from lua to c and vice-versa
- *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
- *objects* are converted to lua tables with string indicies
**Parameters:**
- `L {lua_State}`: lua state pointer
- `obj {ucl_object_t}`: object to push
- `allow_array {bool}`: expand implicit arrays (should be true for all but partial arrays)
**Returns:**
- `{int}`: `1` if an object is pushed to lua
Back to [module description](#module-ucl).
### Function `ucl.to_format(var, format)`
Converts lua variable `var` to the specified `format`. Formats supported are:
- `json` - fine printed json
- `json-compact` - compacted json
- `config` - fine printed configuration
- `ucl` - same as `config`
- `yaml` - embedded yaml
If `var` contains function, they are called during output formatting and if
they return string value, then this value is used for ouptut.
**Parameters:**
- `var {variant}`: any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
- `format {string}`: any available format
**Returns:**
- `{string}`: string representation of `var` in the specific `format`.
Example:
~~~lua
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
~~~
Back to [module description](#module-ucl).
## Methods
The module `ucl` defines the following methods.
### Method `parser:parse_file(name)`
Parse UCL object from file.
**Parameters:**
- `name {string}`: filename to parse
**Returns:**
- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
Example:
~~~lua
local parser = ucl.parser()
local res,err = parser:parse_file('/some/file.conf')
if not res then
print('parser error: ' .. err)
else
-- Do something with object
end
~~~
Back to [module description](#module-ucl).
### Method `parser:parse_string(input)`
Parse UCL object from file.
**Parameters:**
- `input {string}`: string to parse
**Returns:**
- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
Back to [module description](#module-ucl).
### Method `parser:get_object()`
Get top object from parser and export it to lua representation.
**Parameters:**
nothing
**Returns:**
- `{variant or nil}`: ucl object as lua native variable
Back to [module description](#module-ucl).
Back to [top](#).

View File

@ -0,0 +1,69 @@
/* Copyright (c) 2014, Vsevolod Stakhov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LUA_UCL_H_
#define LUA_UCL_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include "ucl.h"
/**
* Closure structure for lua function storing inside UCL
*/
struct ucl_lua_funcdata {
lua_State *L;
int idx;
char *ret;
};
/**
* Initialize lua UCL API
*/
UCL_EXTERN int luaopen_ucl (lua_State *L);
/**
* Import UCL object from lua state
* @param L lua state
* @param idx index of object at the lua stack to convert to UCL
* @return new UCL object or NULL, the caller should unref object after using
*/
UCL_EXTERN ucl_object_t* ucl_object_lua_import (lua_State *L, int idx);
/**
* Push an object to lua
* @param L lua state
* @param obj object to push
* @param allow_array traverse over implicit arrays
*/
UCL_EXTERN int ucl_object_push_lua (lua_State *L,
const ucl_object_t *obj, bool allow_array);
UCL_EXTERN struct ucl_lua_funcdata* ucl_object_toclosure (
const ucl_object_t *obj);
#endif /* LUA_UCL_H_ */

View File

@ -147,7 +147,8 @@ typedef enum ucl_emitter {
typedef enum ucl_parser_flags {
UCL_PARSER_KEY_LOWERCASE = 0x1, /**< Convert all keys to lower case */
UCL_PARSER_ZEROCOPY = 0x2, /**< Parse input in zero-copy mode if possible */
UCL_PARSER_NO_TIME = 0x4 /**< Do not parse time and treat time values as strings */
UCL_PARSER_NO_TIME = 0x4, /**< Do not parse time and treat time values as strings */
UCL_PARSER_NO_IMPLICIT_ARRAYS = 0x8 /** Create explicit arrays instead of implicit ones */
} ucl_parser_flags_t;
/**
@ -171,9 +172,12 @@ typedef enum ucl_string_flags {
* Basic flags for an object
*/
typedef enum ucl_object_flags {
UCL_OBJECT_ALLOCATED_KEY = 1, /**< An object has key allocated internally */
UCL_OBJECT_ALLOCATED_VALUE = 2, /**< An object has a string value allocated internally */
UCL_OBJECT_NEED_KEY_ESCAPE = 4 /**< The key of an object need to be escaped on output */
UCL_OBJECT_ALLOCATED_KEY = 0x1, /**< An object has key allocated internally */
UCL_OBJECT_ALLOCATED_VALUE = 0x2, /**< An object has a string value allocated internally */
UCL_OBJECT_NEED_KEY_ESCAPE = 0x4, /**< The key of an object need to be escaped on output */
UCL_OBJECT_EPHEMERAL = 0x8, /**< Temporary object that does not need to be freed really */
UCL_OBJECT_MULTILINE = 0x10, /**< String should be displayed as multiline string */
UCL_OBJECT_MULTIVALUE = 0x20 /**< Object is a key with multiple values */
} ucl_object_flags_t;
/**
@ -195,14 +199,21 @@ typedef struct ucl_object_s {
const char *key; /**< Key of an object */
struct ucl_object_s *next; /**< Array handle */
struct ucl_object_s *prev; /**< Array handle */
unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */
unsigned keylen; /**< Lenght of a key */
unsigned len; /**< Size of an object */
enum ucl_type type; /**< Real type */
uint16_t ref; /**< Reference count */
uint32_t keylen; /**< Lenght of a key */
uint32_t len; /**< Size of an object */
uint32_t ref; /**< Reference count */
uint16_t flags; /**< Object flags */
uint16_t type; /**< Real type */
unsigned char* trash_stack[2]; /**< Pointer to allocated chunks */
} ucl_object_t;
/**
* Destructor type for userdata objects
* @param ud user specified data pointer
*/
typedef void (*ucl_userdata_dtor)(void *ud);
typedef const char* (*ucl_userdata_emitter)(void *ud);
/** @} */
/**
@ -238,6 +249,31 @@ UCL_EXTERN ucl_object_t* ucl_object_new (void) UCL_WARN_UNUSED_RESULT;
*/
UCL_EXTERN ucl_object_t* ucl_object_typed_new (ucl_type_t type) UCL_WARN_UNUSED_RESULT;
/**
* Create new object with type and priority specified
* @param type type of a new object
* @param priority priority of an object
* @return new object
*/
UCL_EXTERN ucl_object_t* ucl_object_new_full (ucl_type_t type, unsigned priority)
UCL_WARN_UNUSED_RESULT;
/**
* Create new object with userdata dtor
* @param dtor destructor function
* @return new object
*/
UCL_EXTERN ucl_object_t* ucl_object_new_userdata (ucl_userdata_dtor dtor,
ucl_userdata_emitter emitter) UCL_WARN_UNUSED_RESULT;
/**
* Perform deep copy of an object copying everything
* @param other object to copy
* @return new object with refcount equal to 1
*/
UCL_EXTERN ucl_object_t * ucl_object_copy (const ucl_object_t *other)
UCL_WARN_UNUSED_RESULT;
/**
* Return the type of an object
* @return the object type
@ -293,7 +329,7 @@ UCL_EXTERN ucl_object_t* ucl_object_frombool (bool bv) UCL_WARN_UNUSED_RESULT;
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key'
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -306,7 +342,7 @@ UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
/**
* Replace a object 'elt' to the hash 'top' and associate it with key 'key', old object will be unrefed,
* if no object has been found this function works like ucl_object_insert_key()
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -316,6 +352,15 @@ UCL_EXTERN bool ucl_object_insert_key (ucl_object_t *top, ucl_object_t *elt,
UCL_EXTERN bool ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
const char *key, size_t keylen, bool copy_key);
/**
* Merge the keys from one object to another object. Overwrite on conflict
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must be of type UCL_OBJECT)
* @param copy copy rather than reference the elements
* @return true if all keys have been merged
*/
UCL_EXTERN bool ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy);
/**
* Delete a object associated with key 'key', old object will be unrefered,
* @param top object
@ -335,8 +380,9 @@ UCL_EXTERN bool ucl_object_delete_key (ucl_object_t *top,
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* Removes `key` from `top` object, returning the object that was removed. This
* object is not released, caller must unref the returned object when it is no
* longer needed.
* @param top object
* @param key key to remove
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -346,8 +392,9 @@ UCL_EXTERN ucl_object_t* ucl_object_pop_keyl (ucl_object_t *top, const char *key
size_t keylen) UCL_WARN_UNUSED_RESULT;
/**
* Delete key from `top` object returning the object deleted. This object is not
* released
* Removes `key` from `top` object returning the object that was removed. This
* object is not released, caller must unref the returned object when it is no
* longer needed.
* @param top object
* @param key key to remove
* @return removed object or NULL if object has not been found
@ -356,9 +403,9 @@ UCL_EXTERN ucl_object_t* ucl_object_pop_key (ucl_object_t *top, const char *key)
UCL_WARN_UNUSED_RESULT;
/**
* Insert a object 'elt' to the hash 'top' and associate it with key 'key', if the specified key exist,
* try to merge its content
* @param top destination object (will be created automatically if top is NULL)
* Insert a object 'elt' to the hash 'top' and associate it with key 'key', if
* the specified key exist, try to merge its content
* @param top destination object (must be of type UCL_OBJECT)
* @param elt element to insert (must NOT be NULL)
* @param key key to associate with this object (either const or preallocated)
* @param keylen length of the key (or 0 for NULL terminated keys)
@ -369,8 +416,8 @@ UCL_EXTERN bool ucl_object_insert_key_merged (ucl_object_t *top, ucl_object_t *e
const char *key, size_t keylen, bool copy_key);
/**
* Append an element to the front of array object
* @param top destination object (will be created automatically if top is NULL)
* Append an element to the end of array object
* @param top destination object (must NOT be NULL)
* @param elt element to append (must NOT be NULL)
* @return true if value has been inserted
*/
@ -379,7 +426,7 @@ UCL_EXTERN bool ucl_array_append (ucl_object_t *top,
/**
* Append an element to the start of array object
* @param top destination object (will be created automatically if top is NULL)
* @param top destination object (must NOT be NULL)
* @param elt element to append (must NOT be NULL)
* @return true if value has been inserted
*/
@ -387,8 +434,19 @@ UCL_EXTERN bool ucl_array_prepend (ucl_object_t *top,
ucl_object_t *elt);
/**
* Removes an element `elt` from the array `top`. Caller must unref the returned object when it is not
* needed.
* Merge all elements of second array into the first array
* @param top destination array (must be of type UCL_ARRAY)
* @param elt array to copy elements from (must be of type UCL_ARRAY)
* @param copy copy elements instead of referencing them
* @return true if arrays were merged
*/
UCL_EXTERN bool ucl_array_merge (ucl_object_t *top, ucl_object_t *elt,
bool copy);
/**
* Removes an element `elt` from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @param elt element to remove
* @return removed element or NULL if `top` is NULL or not an array
@ -411,35 +469,50 @@ UCL_EXTERN const ucl_object_t* ucl_array_head (const ucl_object_t *top);
UCL_EXTERN const ucl_object_t* ucl_array_tail (const ucl_object_t *top);
/**
* Removes the last element from the array `top`. Caller must unref the returned object when it is not
* needed.
* Removes the last element from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
UCL_EXTERN ucl_object_t* ucl_array_pop_last (ucl_object_t *top);
/**
* Return object identified by an index of the array `top`
* @param obj object to get a key from (must be of type UCL_ARRAY)
* @param index index to return
* @return object at the specified index or NULL if index is not found
*/
UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
unsigned int index);
/**
* Removes the first element from the array `top`. Caller must unref the returned object when it is not
* needed.
* Removes the first element from the array `top`, returning the object that was
* removed. This object is not released, caller must unref the returned object
* when it is no longer needed.
* @param top array ucl object
* @return removed element or NULL if `top` is NULL or not an array
*/
UCL_EXTERN ucl_object_t* ucl_array_pop_first (ucl_object_t *top);
/**
* Return object identified by index of the array `top`
* @param top object to get a key from (must be of type UCL_ARRAY)
* @param index array index to return
* @return object at the specified index or NULL if index is not found
*/
UCL_EXTERN const ucl_object_t* ucl_array_find_index (const ucl_object_t *top,
unsigned int index);
/**
* Replace an element in an array with a different element, returning the object
* that was replaced. This object is not released, caller must unref the
* returned object when it is no longer needed.
* @param top destination object (must be of type UCL_ARRAY)
* @param elt element to append (must NOT be NULL)
* @param index array index in destination to overwrite with elt
* @return object that was replaced or NULL if index is not found
*/
ucl_object_t *
ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
unsigned int index);
/**
* Append a element to another element forming an implicit array
* @param head head to append (may be NULL)
* @param elt new element
* @return true if element has been inserted
* @return the new implicit array
*/
UCL_EXTERN ucl_object_t * ucl_elt_append (ucl_object_t *head,
ucl_object_t *elt);
@ -533,7 +606,7 @@ UCL_EXTERN const char* ucl_object_tolstring (const ucl_object_t *obj, size_t *tl
* Return object identified by a key in the specified object
* @param obj object to get a key from (must be of type UCL_OBJECT)
* @param key key to search
* @return object matched the specified key or NULL if key is not found
* @return object matching the specified key or NULL if key was not found
*/
UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
const char *key);
@ -543,7 +616,7 @@ UCL_EXTERN const ucl_object_t* ucl_object_find_key (const ucl_object_t *obj,
* @param obj object to get a key from (must be of type UCL_OBJECT)
* @param key key to search
* @param klen length of a key
* @return object matched the specified key or NULL if key is not found
* @return object matching the specified key or NULL if key was not found
*/
UCL_EXTERN const ucl_object_t* ucl_object_find_keyl (const ucl_object_t *obj,
const char *key, size_t klen);
@ -575,6 +648,7 @@ UCL_EXTERN const char* ucl_object_keyl (const ucl_object_t *obj, size_t *len);
/**
* Increase reference count for an object
* @param obj object to ref
* @return the referenced object
*/
UCL_EXTERN ucl_object_t* ucl_object_ref (const ucl_object_t *obj);
@ -611,6 +685,21 @@ UCL_EXTERN int ucl_object_compare (const ucl_object_t *o1,
UCL_EXTERN void ucl_object_array_sort (ucl_object_t *ar,
int (*cmp)(const ucl_object_t *o1, const ucl_object_t *o2));
/**
* Get the priority for specific UCL object
* @param obj any ucl object
* @return priority of an object
*/
UCL_EXTERN unsigned int ucl_object_get_priority (const ucl_object_t *obj);
/**
* Set explicit priority of an object.
* @param obj any ucl object
* @param priority new priroity value (only 4 least significant bits are considred)
*/
UCL_EXTERN void ucl_object_set_priority (ucl_object_t *obj,
unsigned int priority);
/**
* Opaque iterator object
*/
@ -640,11 +729,14 @@ UCL_EXTERN const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
* Macro handler for a parser
* @param data the content of macro
* @param len the length of content
* @param arguments arguments object
* @param ud opaque user data
* @param err error pointer
* @return true if macro has been parsed
*/
typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len, void* ud);
typedef bool (*ucl_macro_handler) (const unsigned char *data, size_t len,
const ucl_object_t *arguments,
void* ud);
/* Opaque parser */
struct ucl_parser;
@ -702,12 +794,23 @@ UCL_EXTERN void ucl_parser_set_variables_handler (struct ucl_parser *parser,
* @param parser parser structure
* @param data the pointer to the beginning of a chunk
* @param len the length of a chunk
* @param err if *err is NULL it is set to parser error
* @return true if chunk has been added and false in case of error
*/
UCL_EXTERN bool ucl_parser_add_chunk (struct ucl_parser *parser,
const unsigned char *data, size_t len);
/**
* Load new chunk to a parser with the specified priority
* @param parser parser structure
* @param data the pointer to the beginning of a chunk
* @param len the length of a chunk
* @param priority the desired priority of a chunk (only 4 least significant bits
* are considered for this parameter)
* @return true if chunk has been added and false in case of error
*/
UCL_EXTERN bool ucl_parser_add_chunk_priority (struct ucl_parser *parser,
const unsigned char *data, size_t len, unsigned priority);
/**
* Load ucl object from a string
* @param parser parser structure
@ -835,7 +938,7 @@ struct ucl_emitter_context {
/** A set of output operations */
const struct ucl_emitter_operations *ops;
/** Current amount of indent tabs */
unsigned int ident;
unsigned int indent;
/** Top level object */
const ucl_object_t *top;
/** The rest of context */

View File

@ -7,5 +7,5 @@ Name: LibUCL
Description: Universal configuration library
Version: @UCL_VERSION@
Libs: -L${libdir} -lucl
Libs.private: @LIBS_EXTRA@
Libs.private: @LIBS_EXTRA@ @LUA_LIB@
Cflags: -I${includedir}/

View File

@ -0,0 +1,26 @@
ucl_common_cflags= -I$(top_srcdir)/src \
-I$(top_srcdir)/include \
-I$(top_srcdir)/uthash \
-Wall -W -Wno-unused-parameter -Wno-pointer-sign
luaexec_LTLIBRARIES= ucl.la
ucl_la_SOURCES= lua_ucl.c
ucl_la_CFLAGS= $(ucl_common_cflags) \
@LUA_INCLUDE@
ucl_la_LDFLAGS = -module -export-dynamic -avoid-version
ucl_la_LIBADD= $(top_srcdir)/src/libucl.la \
@LIBFETCH_LIBS@ \
@LIBCRYPTO_LIB@ \
@LIBREGEX_LIB@ \
@CURL_LIBS@ \
@LUA_LIB@
include_HEADERS= $(top_srcdir)/include/lua_ucl.h
ROCKSPEC = $(PACKAGE)-$(VERSION)-1.rockspec
EXTRA_DIST = $(PACKAGE).rockspec.in \
test.lua
DISTCLEANFILES = $(PACKAGE).rockspec
$(ROCKSPEC): $(PACKAGE).rockspec dist
sed -e 's/@MD5@/'`$(MD5SUM) $(distdir).tar.gz | \
cut -d " " -f 1`'/g' < $(PACKAGE).rockspec > $@

View File

@ -0,0 +1,26 @@
package="@PACKAGE@"
version="@VERSION@-1"
source = {
url = "https://github.com/downloads/vstakhov/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz",
md5 = "@MD5@",
dir = "@PACKAGE@-@VERSION@"
}
description = {
summary = "UCL - json like configuration language",
detailed = [[
UCL is heavily infused by nginx configuration as the example
of a convenient configuration system.
However, UCL is fully compatible with JSON format and is able
to parse json files.
]],
homepage = "http://github.com/vstakhov/@PACKAGE@/",
license = ""
}
dependencies = {
"lua >= 5.1"
}
build = {
type = "command",
build_command = "LUA=$(LUA) CPPFLAGS=-I$(LUA_INCDIR) ./configure --prefix=$(PREFIX) --libdir=$(LIBDIR) --datadir=$(LUADIR) && make clean && make",
install_command = "make install"
}

View File

@ -0,0 +1,820 @@
/* Copyright (c) 2014, Vsevolod Stakhov
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
* @file lua ucl bindings
*/
#include "ucl.h"
#include "ucl_internal.h"
#include "lua_ucl.h"
#include <strings.h>
/***
* @module ucl
* This lua module allows to parse objects from strings and to store data into
* ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
* @example
local ucl = require("ucl")
local parser = ucl.parser()
local res,err = parser:parse_string('{key=value}')
if not res then
print('parser error: ' .. err)
else
local obj = parser:get_object()
local got = ucl.to_format(obj, 'json')
endif
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
}
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
*/
#define PARSER_META "ucl.parser.meta"
#define EMITTER_META "ucl.emitter.meta"
#define NULL_META "null.emitter.meta"
static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
static void *ucl_null;
/**
* Push a single element of an object to lua
* @param L
* @param key
* @param obj
*/
static void
ucl_object_lua_push_element (lua_State *L, const char *key,
const ucl_object_t *obj)
{
lua_pushstring (L, key);
ucl_object_push_lua (L, obj, true);
lua_settable (L, -3);
}
static void
lua_ucl_userdata_dtor (void *ud)
{
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
if (fd->ret != NULL) {
free (fd->ret);
}
free (fd);
}
static const char *
lua_ucl_userdata_emitter (void *ud)
{
struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
const char *out = "";
lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
lua_pcall (fd->L, 0, 1, 0);
out = lua_tostring (fd->L, -1);
if (out != NULL) {
/* We need to store temporary string in a more appropriate place */
if (fd->ret) {
free (fd->ret);
}
fd->ret = strdup (out);
}
lua_settop (fd->L, 0);
return fd->ret;
}
/**
* Push a single object to lua
* @param L
* @param obj
* @return
*/
static int
ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
bool allow_array)
{
const ucl_object_t *cur;
ucl_object_iter_t it = NULL;
int nelt = 0;
if (allow_array && obj->next != NULL) {
/* Actually we need to push this as an array */
return ucl_object_lua_push_array (L, obj);
}
/* Optimize allocation by preallocation of table */
while (ucl_iterate_object (obj, &it, true) != NULL) {
nelt ++;
}
lua_createtable (L, 0, nelt);
it = NULL;
while ((cur = ucl_iterate_object (obj, &it, true)) != NULL) {
ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
}
return 1;
}
/**
* Push an array to lua as table indexed by integers
* @param L
* @param obj
* @return
*/
static int
ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
{
const ucl_object_t *cur;
int i = 1, nelt = 0;
/* Optimize allocation by preallocation of table */
LL_FOREACH (obj, cur) {
nelt ++;
}
lua_createtable (L, nelt, 0);
LL_FOREACH (obj, cur) {
ucl_object_push_lua (L, cur, false);
lua_rawseti (L, -2, i);
i ++;
}
return 1;
}
/**
* Push a simple object to lua depending on its actual type
*/
static int
ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
bool allow_array)
{
struct ucl_lua_funcdata *fd;
if (allow_array && obj->next != NULL) {
/* Actually we need to push this as an array */
return ucl_object_lua_push_array (L, obj);
}
switch (obj->type) {
case UCL_BOOLEAN:
lua_pushboolean (L, ucl_obj_toboolean (obj));
break;
case UCL_STRING:
lua_pushstring (L, ucl_obj_tostring (obj));
break;
case UCL_INT:
#if LUA_VERSION_NUM >= 501
lua_pushinteger (L, ucl_obj_toint (obj));
#else
lua_pushnumber (L, ucl_obj_toint (obj));
#endif
break;
case UCL_FLOAT:
case UCL_TIME:
lua_pushnumber (L, ucl_obj_todouble (obj));
break;
case UCL_NULL:
lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
break;
case UCL_USERDATA:
fd = (struct ucl_lua_funcdata *)obj->value.ud;
lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
break;
default:
lua_pushnil (L);
break;
}
return 1;
}
/***
* @function ucl_object_push_lua(L, obj, allow_array)
* This is a `C` function to push `UCL` object as lua variable. This function
* converts `obj` to lua representation using the following conversions:
*
* - *scalar* values are directly presented by lua objects
* - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
* this can be used to pass functions from lua to c and vice-versa
* - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
* - *objects* are converted to lua tables with string indicies
* @param {lua_State} L lua state pointer
* @param {ucl_object_t} obj object to push
* @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
* @return {int} `1` if an object is pushed to lua
*/
int
ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
{
switch (obj->type) {
case UCL_OBJECT:
return ucl_object_lua_push_object (L, obj, allow_array);
case UCL_ARRAY:
return ucl_object_lua_push_array (L, obj->value.av);
default:
return ucl_object_lua_push_scalar (L, obj, allow_array);
}
}
/**
* Parse lua table into object top
* @param L
* @param top
* @param idx
*/
static ucl_object_t *
ucl_object_lua_fromtable (lua_State *L, int idx)
{
ucl_object_t *obj, *top = NULL;
size_t keylen;
const char *k;
bool is_array = true;
int max = INT_MIN;
if (idx < 0) {
/* For negative indicies we want to invert them */
idx = lua_gettop (L) + idx + 1;
}
/* Check for array */
lua_pushnil (L);
while (lua_next (L, idx) != 0) {
if (lua_type (L, -2) == LUA_TNUMBER) {
double num = lua_tonumber (L, -2);
if (num == (int)num) {
if (num > max) {
max = num;
}
}
else {
/* Keys are not integer */
lua_pop (L, 2);
is_array = false;
break;
}
}
else {
/* Keys are not numeric */
lua_pop (L, 2);
is_array = false;
break;
}
lua_pop (L, 1);
}
/* Table iterate */
if (is_array) {
int i;
top = ucl_object_typed_new (UCL_ARRAY);
for (i = 1; i <= max; i ++) {
lua_pushinteger (L, i);
lua_gettable (L, idx);
obj = ucl_object_lua_fromelt (L, lua_gettop (L));
if (obj != NULL) {
ucl_array_append (top, obj);
}
}
}
else {
lua_pushnil (L);
top = ucl_object_typed_new (UCL_OBJECT);
while (lua_next (L, idx) != 0) {
/* copy key to avoid modifications */
k = lua_tolstring (L, -2, &keylen);
obj = ucl_object_lua_fromelt (L, lua_gettop (L));
if (obj != NULL) {
ucl_object_insert_key (top, obj, k, keylen, true);
}
lua_pop (L, 1);
}
}
return top;
}
/**
* Get a single element from lua to object obj
* @param L
* @param obj
* @param idx
*/
static ucl_object_t *
ucl_object_lua_fromelt (lua_State *L, int idx)
{
int type;
double num;
ucl_object_t *obj = NULL;
struct ucl_lua_funcdata *fd;
type = lua_type (L, idx);
switch (type) {
case LUA_TSTRING:
obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
break;
case LUA_TNUMBER:
num = lua_tonumber (L, idx);
if (num == (int64_t)num) {
obj = ucl_object_fromint (num);
}
else {
obj = ucl_object_fromdouble (num);
}
break;
case LUA_TBOOLEAN:
obj = ucl_object_frombool (lua_toboolean (L, idx));
break;
case LUA_TUSERDATA:
if (lua_topointer (L, idx) == ucl_null) {
obj = ucl_object_typed_new (UCL_NULL);
}
break;
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
if (luaL_getmetafield (L, idx, "__gen_ucl")) {
if (lua_isfunction (L, -1)) {
lua_settop (L, 3); /* gen, obj, func */
lua_insert (L, 1); /* func, gen, obj */
lua_insert (L, 2); /* func, obj, gen */
lua_call(L, 2, 1);
obj = ucl_object_lua_fromelt (L, 1);
}
lua_pop (L, 2);
}
else {
if (type == LUA_TTABLE) {
obj = ucl_object_lua_fromtable (L, idx);
}
else if (type == LUA_TFUNCTION) {
fd = malloc (sizeof (*fd));
if (fd != NULL) {
lua_pushvalue (L, idx);
fd->L = L;
fd->ret = NULL;
fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
lua_ucl_userdata_emitter);
obj->type = UCL_USERDATA;
obj->value.ud = (void *)fd;
}
}
}
break;
}
return obj;
}
/**
* @function ucl_object_lua_import(L, idx)
* Extracts ucl object from lua variable at `idx` position,
* @see ucl_object_push_lua for conversion definitions
* @param {lua_state} L lua state machine pointer
* @param {int} idx index where the source variable is placed
* @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
* this object thus needs to be unref'ed after usage.
*/
ucl_object_t *
ucl_object_lua_import (lua_State *L, int idx)
{
ucl_object_t *obj;
int t;
t = lua_type (L, idx);
switch (t) {
case LUA_TTABLE:
obj = ucl_object_lua_fromtable (L, idx);
break;
default:
obj = ucl_object_lua_fromelt (L, idx);
break;
}
return obj;
}
static int
lua_ucl_parser_init (lua_State *L)
{
struct ucl_parser *parser, **pparser;
int flags = 0;
if (lua_gettop (L) >= 1) {
flags = lua_tonumber (L, 1);
}
parser = ucl_parser_new (flags);
if (parser == NULL) {
lua_pushnil (L);
}
pparser = lua_newuserdata (L, sizeof (parser));
*pparser = parser;
luaL_getmetatable (L, PARSER_META);
lua_setmetatable (L, -2);
return 1;
}
static struct ucl_parser *
lua_ucl_parser_get (lua_State *L, int index)
{
return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
}
/***
* @method parser:parse_file(name)
* Parse UCL object from file.
* @param {string} name filename to parse
* @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
@example
local parser = ucl.parser()
local res,err = parser:parse_file('/some/file.conf')
if not res then
print('parser error: ' .. err)
else
-- Do something with object
end
*/
static int
lua_ucl_parser_parse_file (lua_State *L)
{
struct ucl_parser *parser;
const char *file;
int ret = 2;
parser = lua_ucl_parser_get (L, 1);
file = luaL_checkstring (L, 2);
if (parser != NULL && file != NULL) {
if (ucl_parser_add_file (parser, file)) {
lua_pushboolean (L, true);
ret = 1;
}
else {
lua_pushboolean (L, false);
lua_pushstring (L, ucl_parser_get_error (parser));
}
}
else {
lua_pushboolean (L, false);
lua_pushstring (L, "invalid arguments");
}
return ret;
}
/***
* @method parser:parse_string(input)
* Parse UCL object from file.
* @param {string} input string to parse
* @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
*/
static int
lua_ucl_parser_parse_string (lua_State *L)
{
struct ucl_parser *parser;
const char *string;
size_t llen;
int ret = 2;
parser = lua_ucl_parser_get (L, 1);
string = luaL_checklstring (L, 2, &llen);
if (parser != NULL && string != NULL) {
if (ucl_parser_add_chunk (parser, (const unsigned char *)string, llen)) {
lua_pushboolean (L, true);
ret = 1;
}
else {
lua_pushboolean (L, false);
lua_pushstring (L, ucl_parser_get_error (parser));
}
}
else {
lua_pushboolean (L, false);
lua_pushstring (L, "invalid arguments");
}
return ret;
}
/***
* @method parser:get_object()
* Get top object from parser and export it to lua representation.
* @return {variant or nil} ucl object as lua native variable
*/
static int
lua_ucl_parser_get_object (lua_State *L)
{
struct ucl_parser *parser;
ucl_object_t *obj;
int ret = 1;
parser = lua_ucl_parser_get (L, 1);
obj = ucl_parser_get_object (parser);
if (obj != NULL) {
ret = ucl_object_push_lua (L, obj, false);
/* no need to keep reference */
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return ret;
}
static int
lua_ucl_parser_gc (lua_State *L)
{
struct ucl_parser *parser;
parser = lua_ucl_parser_get (L, 1);
ucl_parser_free (parser);
return 0;
}
static void
lua_ucl_parser_mt (lua_State *L)
{
luaL_newmetatable (L, PARSER_META);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pushcfunction (L, lua_ucl_parser_gc);
lua_setfield (L, -2, "__gc");
lua_pushcfunction (L, lua_ucl_parser_parse_file);
lua_setfield (L, -2, "parse_file");
lua_pushcfunction (L, lua_ucl_parser_parse_string);
lua_setfield (L, -2, "parse_string");
lua_pushcfunction (L, lua_ucl_parser_get_object);
lua_setfield (L, -2, "get_object");
lua_pop (L, 1);
}
static int
lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
{
unsigned char *result;
result = ucl_object_emit (obj, type);
if (result != NULL) {
lua_pushstring (L, (const char *)result);
free (result);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_to_json (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
if (lua_gettop (L) > 1) {
if (lua_toboolean (L, 2)) {
format = UCL_EMIT_JSON_COMPACT;
}
}
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_to_config (lua_State *L)
{
ucl_object_t *obj;
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
/***
* @function ucl.to_format(var, format)
* Converts lua variable `var` to the specified `format`. Formats supported are:
*
* - `json` - fine printed json
* - `json-compact` - compacted json
* - `config` - fine printed configuration
* - `ucl` - same as `config`
* - `yaml` - embedded yaml
*
* If `var` contains function, they are called during output formatting and if
* they return string value, then this value is used for ouptut.
* @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
* @param {string} format any available format
* @return {string} string representation of `var` in the specific `format`.
* @example
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end
}
print(ucl.to_format(table, 'ucl'))
-- Output:
--[[
num = 100500;
str = "value";
null = null;
func = "huh";
--]]
*/
static int
lua_ucl_to_format (lua_State *L)
{
ucl_object_t *obj;
int format = UCL_EMIT_JSON;
if (lua_gettop (L) > 1) {
if (lua_type (L, 2) == LUA_TNUMBER) {
format = lua_tonumber (L, 2);
if (format < 0 || format >= UCL_EMIT_YAML) {
lua_pushnil (L);
return 1;
}
}
else if (lua_type (L, 2) == LUA_TSTRING) {
const char *strtype = lua_tostring (L, 2);
if (strcasecmp (strtype, "json") == 0) {
format = UCL_EMIT_JSON;
}
else if (strcasecmp (strtype, "json-compact") == 0) {
format = UCL_EMIT_JSON_COMPACT;
}
else if (strcasecmp (strtype, "yaml") == 0) {
format = UCL_EMIT_YAML;
}
else if (strcasecmp (strtype, "config") == 0 ||
strcasecmp (strtype, "ucl") == 0) {
format = UCL_EMIT_CONFIG;
}
}
}
obj = ucl_object_lua_import (L, 1);
if (obj != NULL) {
lua_ucl_to_string (L, obj, format);
ucl_object_unref (obj);
}
else {
lua_pushnil (L);
}
return 1;
}
static int
lua_ucl_null_tostring (lua_State* L)
{
lua_pushstring (L, "null");
return 1;
}
static void
lua_ucl_null_mt (lua_State *L)
{
luaL_newmetatable (L, NULL_META);
lua_pushcfunction (L, lua_ucl_null_tostring);
lua_setfield (L, -2, "__tostring");
lua_pop (L, 1);
}
int
luaopen_ucl (lua_State *L)
{
lua_ucl_parser_mt (L);
lua_ucl_null_mt (L);
/* Create the refs weak table: */
lua_createtable (L, 0, 2);
lua_pushliteral (L, "v"); /* tbl, "v" */
lua_setfield (L, -2, "__mode");
lua_pushvalue (L, -1); /* tbl, tbl */
lua_setmetatable (L, -2); /* tbl */
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
lua_newtable (L);
lua_pushcfunction (L, lua_ucl_parser_init);
lua_setfield (L, -2, "parser");
lua_pushcfunction (L, lua_ucl_to_json);
lua_setfield (L, -2, "to_json");
lua_pushcfunction (L, lua_ucl_to_config);
lua_setfield (L, -2, "to_config");
lua_pushcfunction (L, lua_ucl_to_format);
lua_setfield (L, -2, "to_format");
ucl_null = lua_newuserdata (L, 0);
luaL_getmetatable (L, NULL_META);
lua_setmetatable (L, -2);
lua_pushvalue (L, -1);
lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
lua_setfield (L, -2, "null");
return 1;
}
struct ucl_lua_funcdata*
ucl_object_toclosure (const ucl_object_t *obj)
{
if (obj == NULL || obj->type != UCL_USERDATA) {
return NULL;
}
return (struct ucl_lua_funcdata*)obj->value.ud;
}

View File

@ -0,0 +1,48 @@
local ucl = require("ucl")
function test_simple()
local expect =
'['..
'"float",1.5,'..
'"integer",5,'..
'"true",true,'..
'"false",false,'..
'"null",null,'..
'"string","hello",'..
'"array",[1,2],'..
'"object",{"key":"value"}'..
']'
-- Input to to_value matches the output of to_string:
local parser = ucl.parser()
local res,err = parser:parse_string(expect)
if not res then
print('parser error: ' .. err)
return 1
end
local obj = parser:get_object()
local got = ucl.to_json(obj, true)
if expect == got then
return 0
else
print(expect .. " == " .. tostring(got))
return 1
end
end
test_simple()
local table = {
str = 'value',
num = 100500,
null = ucl.null,
func = function ()
return 'huh'
end,
badfunc = function()
print("I'm bad")
end
}
print(ucl.to_format(table, 'ucl'))

4
contrib/libucl/m4/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

606
contrib/libucl/m4/ax_lua.m4 Normal file
View File

@ -0,0 +1,606 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_lua.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
#
# DESCRIPTION
#
# Detect a Lua interpreter, optionally specifying a minimum and maximum
# version number. Set up important Lua paths, such as the directories in
# which to install scripts and modules (shared libraries).
#
# Also detect Lua headers and libraries. The Lua version contained in the
# header is checked to match the Lua interpreter version exactly. When
# searching for Lua libraries, the version number is used as a suffix.
# This is done with the goal of supporting multiple Lua installs (5.1 and
# 5.2 side-by-side).
#
# A note on compatibility with previous versions: This file has been
# mostly rewritten for serial 18. Most developers should be able to use
# these macros without needing to modify configure.ac. Care has been taken
# to preserve each macro's behavior, but there are some differences:
#
# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
# AX_PROG_LUA with no arguments.
#
# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
# unnecessary, so it is deprecated and does not expand to anything.
#
# 3) The configure flag --with-lua-suffix no longer exists; the user
# should instead specify the LUA precious variable on the command line.
# See the AX_PROG_LUA description for details.
#
# Please read the macro descriptions below for more information.
#
# This file was inspired by Andrew Dalke's and James Henstridge's
# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
# (serial 17). Basically, this file is a mash-up of those two files. I
# like to think it combines the best of the two!
#
# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
# paths. Adds precious variable LUA, which may contain the path of the Lua
# interpreter. If LUA is blank, the user's path is searched for an
# suitable interpreter.
#
# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION will be accepted. If
# TOO-BIG- VERSION is also supplied, then only Lua interpreters with a
# version number greater or equal to MINIMUM-VERSION and less than
# TOO-BIG-VERSION will be accepted.
#
# The Lua version number, LUA_VERSION, is found from the interpreter, and
# substituted. LUA_PLATFORM is also found, but not currently supported (no
# standard representation).
#
# Finally, the macro finds four paths:
#
# luadir Directory to install Lua scripts.
# pkgluadir $luadir/$PACKAGE
# luaexecdir Directory to install Lua modules.
# pkgluaexecdir $luaexecdir/$PACKAGE
#
# These paths a found based on $prefix, $exec_prefix, Lua's package.path,
# and package.cpath. The first path of package.path beginning with $prefix
# is selected as luadir. The first path of package.cpath beginning with
# $exec_prefix is used as luaexecdir. This should work on all reasonable
# Lua installations. If a path cannot be determined, a default path is
# used. Of course, the user can override these later when invoking make.
#
# luadir Default: $prefix/share/lua/$LUA_VERSION
# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
#
# These directories can be used by Automake as install destinations. The
# variable name minus 'dir' needs to be used as a prefix to the
# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
#
# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
# FOUND is blank, then it will default to printing an error. To prevent
# the default behavior, give ':' as an action.
#
# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_INCLUDE, which
# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
# LUA_INCLUDE is blank, then this macro will attempt to find suitable
# flags.
#
# LUA_INCLUDE can be used by Automake to compile Lua modules or
# executables with embedded interpreters. The *_CPPFLAGS variables should
# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
#
# This macro searches for the header lua.h (and others). The search is
# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
# If the search is unsuccessful, then some common directories are tried.
# If the headers are then found, then LUA_INCLUDE is set accordingly.
#
# The paths automatically searched are:
#
# * /usr/include/luaX.Y
# * /usr/include/lua/X.Y
# * /usr/include/luaXY
# * /usr/local/include/luaX.Y
# * /usr/local/include/lua-X.Y
# * /usr/local/include/lua/X.Y
# * /usr/local/include/luaXY
#
# (Where X.Y is the Lua version number, e.g. 5.1.)
#
# The Lua version number found in the headers is always checked to match
# the Lua interpreter's version number. Lua headers with mismatched
# version numbers are not accepted.
#
# If headers are found, then ACTION-IF-FOUND is performed, otherwise
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
# it will default to printing an error. To prevent the default behavior,
# set the action to ':'.
#
# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
# expanded before this macro. Adds precious variable LUA_LIB, which may
# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
# then this macro will attempt to find suitable flags.
#
# LUA_LIB can be used by Automake to link Lua modules or executables with
# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
#
# This macro searches for the Lua library. More technically, it searches
# for a library containing the function lua_load. The search is performed
# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
#
# If the search determines that some linker flags are missing, then those
# flags will be added to LUA_LIB.
#
# If libraries are found, then ACTION-IF-FOUND is performed, otherwise
# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
# it will default to printing an error. To prevent the default behavior,
# set the action to ':'.
#
# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
# Autoconf Archive.
#
# If a readline compatible library is found, then ACTION-IF-FOUND is
# performed, otherwise ACTION-IF-NOT-FOUND is performed.
#
# LICENSE
#
# Copyright (c) 2014 Reuben Thomas <rrt@sc3d.org>
# Copyright (c) 2013 Tim Perkins <tprk77@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 23
dnl =========================================================================
dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_PROG_LUA],
[
dnl Make LUA a precious variable.
AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
dnl Find a Lua interpreter.
m4_define_default([_AX_LUA_INTERPRETER_LIST],
[lua lua5.2 lua52 lua5.1 lua51 lua50])
m4_if([$1], [],
[ dnl No version check is needed. Find any Lua interpreter.
AS_IF([test "x$LUA" = 'x'],
[AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
ax_display_LUA='lua'
dnl At least check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
],
[ dnl A version check is needed.
AS_IF([test "x$LUA" != 'x'],
[ dnl Check if this is a Lua interpreter.
AC_MSG_CHECKING([if $LUA is a Lua interpreter])
_AX_LUA_CHK_IS_INTRP([$LUA],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([not a Lua interpreter])
])
dnl Check the version.
m4_if([$2], [],
[_ax_check_text="whether $LUA version >= $1"],
[_ax_check_text="whether $LUA version >= $1, < $2"])
AC_MSG_CHECKING([$_ax_check_text])
_AX_LUA_CHK_VER([$LUA], [$1], [$2],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([version is out of range for specified LUA])])
ax_display_LUA=$LUA
],
[ dnl Try each interpreter until we find one that satisfies VERSION.
m4_if([$2], [],
[_ax_check_text="for a Lua interpreter with version >= $1"],
[_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
AC_CACHE_CHECK([$_ax_check_text],
[ax_cv_pathless_LUA],
[ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
test "x$ax_cv_pathless_LUA" = 'xnone' && break
_AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
_AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
done
])
dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
[LUA=':'],
[AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
ax_display_LUA=$ax_cv_pathless_LUA
])
])
AS_IF([test "x$LUA" = 'x:'],
[ dnl Run any user-specified action, or abort.
m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
],
[ dnl Query Lua for its version number.
AC_CACHE_CHECK([for $ax_display_LUA version], [ax_cv_lua_version],
[ ax_cv_lua_version=`$LUA -e 'print(_VERSION:match "(%d+%.%d+)")'` ])
AS_IF([test "x$ax_cv_lua_version" = 'x'],
[AC_MSG_ERROR([invalid Lua version number])])
AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | sed 's|\.||'`])
dnl The following check is not supported:
dnl At times (like when building shared libraries) you may want to know
dnl which OS platform Lua thinks this is.
AC_CACHE_CHECK([for $ax_display_LUA platform], [ax_cv_lua_platform],
[ax_cv_lua_platform=`$LUA -e "print('unknown')"`])
AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
dnl Use the values of $prefix and $exec_prefix for the corresponding
dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
dnl variables so they can be overridden if need be. However, the general
dnl consensus is that you shouldn't need this ability.
AC_SUBST([LUA_PREFIX], ['${prefix}'])
AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
dnl Lua provides no way to query the script directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $prefix, then we can store scripts there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA script directory],
[ax_cv_lua_luadir],
[ AS_IF([test "x$prefix" = 'xNONE'],
[ax_lua_prefix=$ac_default_prefix],
[ax_lua_prefix=$prefix])
dnl Initialize to the default path.
ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_prefix" | sed 's|.|.|g'`
ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
sed "s,^$_ax_strip_prefix,$LUA_PREFIX,"`
])
])
AC_SUBST([luadir], [$ax_cv_lua_luadir])
AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
dnl Lua provides no way to query the module directory, and instead
dnl provides LUA_PATH. However, we should be able to make a safe educated
dnl guess. If the built-in search path contains a directory which is
dnl prefixed by $exec_prefix, then we can store modules there. The first
dnl matching path will be used.
AC_CACHE_CHECK([for $ax_display_LUA module directory],
[ax_cv_lua_luaexecdir],
[ AS_IF([test "x$exec_prefix" = 'xNONE'],
[ax_lua_exec_prefix=$ax_lua_prefix],
[ax_lua_exec_prefix=$exec_prefix])
dnl Initialize to the default path.
ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
dnl Try to find a path with the prefix.
_AX_LUA_FND_PRFX_PTH([$LUA],
[$ax_lua_exec_prefix], [package.cpathd])
AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
[ dnl Fix the prefix.
_ax_strip_prefix=`echo "$ax_lua_exec_prefix" | sed 's|.|.|g'`
ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
sed "s,^$_ax_strip_prefix,$LUA_EXEC_PREFIX,"`
])
])
AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
dnl Run any user specified action.
$3
])
])
dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
AC_DEFUN([AX_WITH_LUA],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA]])
AX_PROG_LUA
])
dnl =========================================================================
dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
[
dnl Just print _VERSION because all Lua interpreters have this global.
AS_IF([$1 -e "print('Hello ' .. _VERSION .. '!')" &>/dev/null],
[$2], [$3])
])
dnl =========================================================================
dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
dnl =========================================================================
AC_DEFUN([_AX_LUA_CHK_VER],
[
AS_IF([$1 2>/dev/null -e '
function norm (v) i,j=v:match "(%d+)%.(%d+)" return 100 * i + j end
v=norm (_VERSION)
os.exit ((v >= norm ("$2") and ("$3" == "" or v < norm ("$3"))) and 0 or 1)'],
[$4], [$5])
])
dnl =========================================================================
dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, LUA-PATH-VARIABLE)
dnl =========================================================================
AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
[
dnl Invokes the Lua interpreter PROG to print the path variable
dnl LUA-PATH-VARIABLE, usually package.path or package.cpath. Paths are
dnl then matched against PREFIX. The first path to begin with PREFIX is set
dnl to ax_lua_prefixed_path.
ax_lua_prefixed_path=''
_ax_package_paths=`$1 -e 'print($3)' 2>/dev/null | sed 's|;|\n|g'`
dnl Try the paths in order, looking for the prefix.
for _ax_package_path in $_ax_package_paths; do
dnl Copy the path, up to the use of a Lua wildcard.
_ax_path_parts=`echo "$_ax_package_path" | sed 's|/|\n|g'`
_ax_reassembled=''
for _ax_path_part in $_ax_path_parts; do
echo "$_ax_path_part" | grep '\?' >/dev/null && break
_ax_reassembled="$_ax_reassembled/$_ax_path_part"
done
dnl Check the path against the prefix.
_ax_package_path=$_ax_reassembled
if echo "$_ax_package_path" | grep "^$2" >/dev/null; then
dnl Found it.
ax_lua_prefixed_path=$_ax_package_path
break
fi
done
])
dnl =========================================================================
dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_HEADERS],
[
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
])
dnl Make LUA_INCLUDE a precious variable.
AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
dnl Some default directories to search.
LUA_SHORT_VERSION=`echo "$LUA_VERSION" | sed 's|\.||'`
m4_define_default([_AX_LUA_INCLUDE_LIST],
[ /usr/include/lua$LUA_VERSION \
/usr/include/lua/$LUA_VERSION \
/usr/include/lua$LUA_SHORT_VERSION \
/usr/local/include/lua$LUA_VERSION \
/usr/local/include/lua-$LUA_VERSION \
/usr/local/include/lua/$LUA_VERSION \
/usr/local/include/lua$LUA_SHORT_VERSION \
])
dnl Try to find the headers.
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
CPPFLAGS=$_ax_lua_saved_cppflags
dnl Try some other directories if LUA_INCLUDE was not set.
AS_IF([test "x$LUA_INCLUDE" = 'x' &&
test "x$ac_cv_header_lua_h" != 'xyes'],
[ dnl Try some common include paths.
for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
test ! -d "$_ax_include_path" && continue
AC_MSG_CHECKING([for Lua headers in])
AC_MSG_RESULT([$_ax_include_path])
AS_UNSET([ac_cv_header_lua_h])
AS_UNSET([ac_cv_header_lualib_h])
AS_UNSET([ac_cv_header_lauxlib_h])
AS_UNSET([ac_cv_header_luaconf_h])
_ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
CPPFLAGS=$_ax_lua_saved_cppflags
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
[ LUA_INCLUDE="-I$_ax_include_path"
break
])
done
])
AS_IF([test "x$ac_cv_header_lua_h" = 'xyes' && test "x$cross_compiling" != 'xyes'],
[ dnl Make a program to print LUA_VERSION defined in the header.
dnl TODO This probably shouldn't be a runtime test.
AC_CACHE_CHECK([for Lua header version],
[ax_cv_lua_header_version],
[ _ax_lua_saved_cppflags=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
AC_RUN_IFELSE(
[ AC_LANG_SOURCE([[
#include <lua.h>
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char ** argv)
{
if(argc > 1) printf("%s", LUA_VERSION);
exit(EXIT_SUCCESS);
}
]])
],
[ ax_cv_lua_header_version=`./conftest$EXEEXT p | \
sed "s|^Lua \(.*\)|\1|" | \
grep -o "^@<:@0-9@:>@\+\\.@<:@0-9@:>@\+"`
],
[ax_cv_lua_header_version='unknown'])
CPPFLAGS=$_ax_lua_saved_cppflags
])
dnl Compare this to the previously found LUA_VERSION.
AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
[ AC_MSG_RESULT([yes])
ax_header_version_match='yes'
],
[ AC_MSG_RESULT([no])
ax_header_version_match='no'
])
],
[
ax_header_version_match='yes'
])
dnl Was LUA_INCLUDE specified?
AS_IF([test "x$ax_header_version_match" != 'xyes' &&
test "x$LUA_INCLUDE" != 'x'],
[AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
dnl Test the final result and run user code.
AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
])
dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
AC_DEFUN([AX_LUA_HEADERS_VERSION],
[
AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS]])
])
dnl =========================================================================
dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_LIBS],
[
dnl TODO Should this macro also check various -L flags?
dnl Check for LUA_VERSION.
AC_MSG_CHECKING([if LUA_VERSION is defined])
AS_IF([test "x$LUA_VERSION" != 'x'],
[AC_MSG_RESULT([yes])],
[ AC_MSG_RESULT([no])
AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
])
dnl Make LUA_LIB a precious variable.
AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
AS_IF([test "x$LUA_LIB" != 'x'],
[ dnl Check that LUA_LIBS works.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load], [],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'])
LIBS=$_ax_lua_saved_libs
dnl Check the result.
AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
[AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
],
[ dnl First search for extra libs.
_ax_lua_extra_libs=''
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([exp], [m])
AC_SEARCH_LIBS([dlopen], [dl])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
test "x$ac_cv_search_exp" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
test "x$ac_cv_search_dlopen" != 'xnone required'],
[_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
dnl Try to find the Lua libs.
_ax_lua_saved_libs=$LIBS
LIBS="$LIBS $LUA_LIB"
AC_SEARCH_LIBS([lua_load],
[ lua$LUA_VERSION \
lua$LUA_SHORT_VERSION \
lua-$LUA_VERSION \
lua-$LUA_SHORT_VERSION \
lua],
[_ax_found_lua_libs='yes'],
[_ax_found_lua_libs='no'],
[$_ax_lua_extra_libs])
LIBS=$_ax_lua_saved_libs
AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
test "x$ac_cv_search_lua_load" != 'xnone required'],
[LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
])
dnl Test the result and run user code.
AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
[m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
])
dnl =========================================================================
dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
dnl =========================================================================
AC_DEFUN([AX_LUA_READLINE],
[
AX_LIB_READLINE
AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
test "x$ac_cv_header_readline_history_h" != 'x'],
[ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
$1
],
[$2])
])

View File

@ -130,6 +130,19 @@ ucl_emitter_print_key (bool print_key, struct ucl_emitter_context *ctx,
func->ucl_emitter_append_character (' ', 1, func->ud);
}
}
else if (ctx->id == UCL_EMIT_YAML) {
if (obj->keylen > 0 && (obj->flags & UCL_OBJECT_NEED_KEY_ESCAPE)) {
ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
}
else if (obj->keylen > 0) {
func->ucl_emitter_append_len (obj->key, obj->keylen, func->ud);
}
else {
func->ucl_emitter_append_len ("null", 4, func->ud);
}
func->ucl_emitter_append_len (": ", 2, func->ud);
}
else {
if (obj->keylen > 0) {
ucl_elt_string_write_json (obj->key, obj->keylen, ctx);
@ -182,7 +195,7 @@ ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
const struct ucl_emitter_functions *func = ctx->func;
if (UCL_EMIT_IDENT_TOP_OBJ(ctx, obj)) {
ctx->ident --;
ctx->indent --;
if (compact) {
func->ucl_emitter_append_character ('}', 1, func->ud);
}
@ -191,7 +204,7 @@ ucl_emitter_common_end_object (struct ucl_emitter_context *ctx,
/* newline is already added for this format */
func->ucl_emitter_append_character ('\n', 1, func->ud);
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
func->ucl_emitter_append_character ('}', 1, func->ud);
}
}
@ -210,7 +223,7 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
{
const struct ucl_emitter_functions *func = ctx->func;
ctx->ident --;
ctx->indent --;
if (compact) {
func->ucl_emitter_append_character (']', 1, func->ud);
}
@ -219,7 +232,7 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
/* newline is already added for this format */
func->ucl_emitter_append_character ('\n', 1, func->ud);
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
func->ucl_emitter_append_character (']', 1, func->ud);
}
@ -249,7 +262,7 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
func->ucl_emitter_append_len ("[\n", 2, func->ud);
}
ctx->ident ++;
ctx->indent ++;
if (obj->type == UCL_ARRAY) {
/* explicit array */
@ -294,7 +307,7 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
else {
func->ucl_emitter_append_len ("{\n", 2, func->ud);
}
ctx->ident ++;
ctx->indent ++;
}
while ((cur = ucl_hash_iterate (obj->value.ov, &it))) {
@ -315,7 +328,7 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
func->ucl_emitter_append_len (",\n", 2, func->ud);
}
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
ucl_emitter_common_start_array (ctx, cur, true, compact);
ucl_emitter_common_end_array (ctx, cur, compact);
}
@ -342,17 +355,23 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
{
const struct ucl_emitter_functions *func = ctx->func;
bool flag;
struct ucl_object_userdata *ud;
const char *ud_out = "";
if (ctx->id != UCL_EMIT_CONFIG && !first) {
if (compact) {
func->ucl_emitter_append_character (',', 1, func->ud);
}
else {
func->ucl_emitter_append_len (",\n", 2, func->ud);
if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
func->ucl_emitter_append_len ("\n", 1, func->ud);
} else {
func->ucl_emitter_append_len (",\n", 2, func->ud);
}
}
}
ucl_add_tabs (func, ctx->ident, compact);
ucl_add_tabs (func, ctx->indent, compact);
switch (obj->type) {
case UCL_INT:
@ -379,7 +398,12 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
break;
case UCL_STRING:
ucl_emitter_print_key (print_key, ctx, obj, compact);
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
if (ctx->id == UCL_EMIT_CONFIG && ucl_maybe_long_string (obj)) {
ucl_elt_string_write_multiline (obj->value.sv, obj->len, ctx);
}
else {
ucl_elt_string_write_json (obj->value.sv, obj->len, ctx);
}
ucl_emitter_finish_object (ctx, obj, compact, !print_key);
break;
case UCL_NULL:
@ -396,6 +420,16 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_common_end_array (ctx, obj, compact);
break;
case UCL_USERDATA:
ud = (struct ucl_object_userdata *)obj;
ucl_emitter_print_key (print_key, ctx, obj, compact);
if (ud->emitter) {
ud_out = ud->emitter (obj->value.ud);
if (ud_out == NULL) {
ud_out = "null";
}
}
ucl_elt_string_write_json (ud_out, strlen (ud_out), ctx);
ucl_emitter_finish_object (ctx, obj, compact, !print_key);
break;
}
}
@ -425,10 +459,10 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_common_end_array (ctx, obj, (compact)); \
}
UCL_EMIT_TYPE_IMPL(json, false);
UCL_EMIT_TYPE_IMPL(json_compact, true);
UCL_EMIT_TYPE_IMPL(config, false);
UCL_EMIT_TYPE_IMPL(yaml, false);
UCL_EMIT_TYPE_IMPL(json, false)
UCL_EMIT_TYPE_IMPL(json_compact, true)
UCL_EMIT_TYPE_IMPL(config, false)
UCL_EMIT_TYPE_IMPL(yaml, false)
unsigned char *
ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type)
@ -461,7 +495,7 @@ ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
if (ctx != NULL) {
memcpy (&my_ctx, ctx, sizeof (my_ctx));
my_ctx.func = emitter;
my_ctx.ident = 0;
my_ctx.indent = 0;
my_ctx.top = obj;
my_ctx.ops->ucl_emitter_write_elt (&my_ctx, obj, true, false);

View File

@ -108,9 +108,8 @@ ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx,
st->is_array = false;
sctx->ops->ucl_emitter_start_object (ctx, obj, print_key);
}
LL_PREPEND (sctx->containers, st);
}
LL_PREPEND (sctx->containers, st);
}
void

View File

@ -93,9 +93,7 @@ ucl_elt_string_write_json (const char *str, size_t size,
size_t len = 0;
const struct ucl_emitter_functions *func = ctx->func;
if (ctx->id != UCL_EMIT_YAML) {
func->ucl_emitter_append_character ('"', 1, func->ud);
}
func->ucl_emitter_append_character ('"', 1, func->ud);
while (size) {
if (ucl_test_character (*p, UCL_CHARACTER_JSON_UNSAFE)) {
@ -137,9 +135,18 @@ ucl_elt_string_write_json (const char *str, size_t size,
if (len > 0) {
func->ucl_emitter_append_len (c, len, func->ud);
}
if (ctx->id != UCL_EMIT_YAML) {
func->ucl_emitter_append_character ('"', 1, func->ud);
}
func->ucl_emitter_append_character ('"', 1, func->ud);
}
void
ucl_elt_string_write_multiline (const char *str, size_t size,
struct ucl_emitter_context *ctx)
{
const struct ucl_emitter_functions *func = ctx->func;
func->ucl_emitter_append_len ("<<EOD\n", sizeof ("<<EOD\n") - 1, func->ud);
func->ucl_emitter_append_len (str, size, func->ud);
func->ucl_emitter_append_len ("\nEOD", sizeof ("\nEOD") - 1, func->ud);
}
/*
@ -154,7 +161,7 @@ ucl_utstring_append_character (unsigned char c, size_t len, void *ud)
utstring_append_c (buf, c);
}
else {
utstring_reserve (buf, len);
utstring_reserve (buf, len + 1);
memset (&buf->d[buf->i], c, len);
buf->i += len;
buf->d[buf->i] = '\0';
@ -267,19 +274,23 @@ ucl_fd_append_character (unsigned char c, size_t len, void *ud)
unsigned char *buf;
if (len == 1) {
write (fd, &c, 1);
return write (fd, &c, 1);
}
else {
buf = malloc (len);
if (buf == NULL) {
/* Fallback */
while (len --) {
write (fd, &c, 1);
if (write (fd, &c, 1) == -1) {
return -1;
}
}
}
else {
memset (buf, c, len);
write (fd, buf, len);
if (write (fd, buf, len) == -1) {
return -1;
}
free (buf);
}
}
@ -292,9 +303,7 @@ ucl_fd_append_len (const unsigned char *str, size_t len, void *ud)
{
int fd = *(int *)ud;
write (fd, str, len);
return 0;
return write (fd, str, len);
}
static int
@ -304,9 +313,7 @@ ucl_fd_append_int (int64_t val, void *ud)
char intbuf[64];
snprintf (intbuf, sizeof (intbuf), "%jd", (intmax_t)val);
write (fd, intbuf, strlen (intbuf));
return 0;
return write (fd, intbuf, strlen (intbuf));
}
static int
@ -327,9 +334,7 @@ ucl_fd_append_double (double val, void *ud)
snprintf (nbuf, sizeof (nbuf), "%lf", val);
}
write (fd, nbuf, strlen (nbuf));
return 0;
return write (fd, nbuf, strlen (nbuf));
}
struct ucl_emitter_functions*
@ -464,3 +469,18 @@ ucl_object_emit_single_json (const ucl_object_t *obj)
return res;
}
#define LONG_STRING_LIMIT 80
bool
ucl_maybe_long_string (const ucl_object_t *obj)
{
if (obj->len > LONG_STRING_LIMIT || (obj->flags & UCL_OBJECT_MULTILINE)) {
/* String is long enough, so search for newline characters in it */
if (memchr (obj->value.sv, '\n', obj->len) != NULL) {
return true;
}
}
return false;
}

View File

@ -66,6 +66,20 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
HASH_ADD_KEYPTR (hh, hashlin->buckets, key, keylen, node);
}
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
const ucl_object_t *new)
{
ucl_hash_node_t *node;
HASH_FIND (hh, hashlin->buckets, old->key, old->keylen, node);
if (node != NULL) {
/* Direct replacement */
node->data = new;
node->hh.key = new->key;
node->hh.keylen = new->keylen;
}
}
const void*
ucl_hash_iterate (ucl_hash_t *hashlin, ucl_hash_iter_t *iter)
{
@ -122,5 +136,6 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
if (found) {
HASH_DELETE (hh, hashlin->buckets, found);
UCL_FREE (sizeof (ucl_hash_node_t), found);
}
}

View File

@ -65,6 +65,12 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func *func);
void ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj, const char *key,
unsigned keylen);
/**
* Replace element in the hash
*/
void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
const ucl_object_t *new);
/**
* Delete an element from the the hashtable.
*/

View File

@ -163,6 +163,7 @@ struct ucl_chunk {
size_t remain;
unsigned int line;
unsigned int column;
unsigned priority;
struct ucl_chunk *next;
};
@ -182,7 +183,7 @@ struct ucl_variable {
char *value;
size_t var_len;
size_t value_len;
struct ucl_variable *next;
struct ucl_variable *prev, *next;
};
struct ucl_parser {
@ -192,6 +193,7 @@ struct ucl_parser {
int flags;
ucl_object_t *top_obj;
ucl_object_t *cur_obj;
char *cur_file;
struct ucl_macro *macroes;
struct ucl_stack *stack;
struct ucl_chunk *chunks;
@ -202,6 +204,12 @@ struct ucl_parser {
UT_string *err;
};
struct ucl_object_userdata {
ucl_object_t obj;
ucl_userdata_dtor dtor;
ucl_userdata_emitter emitter;
};
/**
* Unescape json string inplace
* @param str
@ -216,9 +224,11 @@ size_t ucl_unescape_json_string (char *str, size_t len);
* @param err error ptr
* @return
*/
bool ucl_include_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_try_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
/**
* Handle includes macro
@ -228,7 +238,8 @@ bool ucl_try_include_handler (const unsigned char *data, size_t len, void* ud);
* @param err error ptr
* @return
*/
bool ucl_includes_handler (const unsigned char *data, size_t len, void* ud);
bool ucl_includes_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud);
size_t ucl_strlcpy (char *dst, const char *src, size_t siz);
size_t ucl_strlcpy_unsafe (char *dst, const char *src, size_t siz);
@ -264,7 +275,7 @@ ucl_create_err (UT_string **err, const char *fmt, ...)
static inline bool
ucl_maybe_parse_boolean (ucl_object_t *obj, const unsigned char *start, size_t len)
{
const unsigned char *p = start;
const char *p = (const char *)start;
bool ret = false, val = false;
if (len == 5) {
@ -351,13 +362,22 @@ const struct ucl_emitter_context *
ucl_emit_get_standard_context (enum ucl_emitter emit_type);
/**
* Serialise string
* Serialize string as JSON string
* @param str string to emit
* @param buf target buffer
*/
void ucl_elt_string_write_json (const char *str, size_t size,
struct ucl_emitter_context *ctx);
/**
* Write multiline string using `EOD` as string terminator
* @param str
* @param size
* @param ctx
*/
void ucl_elt_string_write_multiline (const char *str, size_t size,
struct ucl_emitter_context *ctx);
/**
* Emit a single object to string
* @param obj
@ -365,4 +385,12 @@ void ucl_elt_string_write_json (const char *str, size_t size,
*/
unsigned char * ucl_object_emit_single_json (const ucl_object_t *obj);
/**
* Check whether a specified string is long and should be likely printed in
* multiline mode
* @param obj
* @return
*/
bool ucl_maybe_long_string (const ucl_object_t *obj);
#endif /* UCL_INTERNAL_H_ */

View File

@ -26,8 +26,8 @@
#include "ucl_chartable.h"
/**
* @file rcl_parser.c
* The implementation of rcl parser
* @file ucl_parser.c
* The implementation of ucl parser
*/
struct ucl_parser_saved_state {
@ -56,20 +56,33 @@ struct ucl_parser_saved_state {
} while (0)
static inline void
ucl_set_err (struct ucl_chunk *chunk, int code, const char *str, UT_string **err)
ucl_set_err (struct ucl_parser *parser, int code, const char *str, UT_string **err)
{
if (chunk->pos < chunk->end) {
if (isgraph (*chunk->pos)) {
ucl_create_err (err, "error on line %d at column %d: '%s', character: '%c'",
chunk->line, chunk->column, str, *chunk->pos);
}
else {
ucl_create_err (err, "error on line %d at column %d: '%s', character: '0x%02x'",
chunk->line, chunk->column, str, (int)*chunk->pos);
}
const char *fmt_string, *filename;
struct ucl_chunk *chunk = parser->chunks;
if (parser->cur_file) {
filename = parser->cur_file;
}
else {
ucl_create_err (err, "error at the end of chunk: %s", str);
filename = "<unknown>";
}
if (chunk->pos < chunk->end) {
if (isgraph (*chunk->pos)) {
fmt_string = "error while parsing %s: "
"line: %d, column: %d - '%s', character: '%c'";
}
else {
fmt_string = "error while parsing %s: "
"line: %d, column: %d - '%s', character: '0x%02x'";
}
ucl_create_err (err, fmt_string,
filename, chunk->line, chunk->column,
str, *chunk->pos);
}
else {
ucl_create_err (err, "error while parsing %s: at the end of chunk: %s",
filename, str);
}
}
@ -84,11 +97,12 @@ ucl_skip_comments (struct ucl_parser *parser)
struct ucl_chunk *chunk = parser->chunks;
const unsigned char *p;
int comments_nested = 0;
bool quoted = false;
p = chunk->pos;
start:
if (*p == '#') {
if (chunk->remain > 0 && *p == '#') {
if (parser->state != UCL_STATE_SCOMMENT &&
parser->state != UCL_STATE_MCOMMENT) {
while (p < chunk->end) {
@ -100,34 +114,41 @@ ucl_skip_comments (struct ucl_parser *parser)
}
}
}
else if (*p == '/' && chunk->remain >= 2) {
else if (chunk->remain >= 2 && *p == '/') {
if (p[1] == '*') {
ucl_chunk_skipc (chunk, p);
comments_nested ++;
ucl_chunk_skipc (chunk, p);
while (p < chunk->end) {
if (*p == '*') {
ucl_chunk_skipc (chunk, p);
if (*p == '/') {
comments_nested --;
if (comments_nested == 0) {
ucl_chunk_skipc (chunk, p);
goto start;
}
}
ucl_chunk_skipc (chunk, p);
if (*p == '"' && *(p - 1) != '\\') {
quoted = !quoted;
}
else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
comments_nested ++;
ucl_chunk_skipc (chunk, p);
ucl_chunk_skipc (chunk, p);
continue;
if (!quoted) {
if (*p == '*') {
ucl_chunk_skipc (chunk, p);
if (*p == '/') {
comments_nested --;
if (comments_nested == 0) {
ucl_chunk_skipc (chunk, p);
goto start;
}
}
ucl_chunk_skipc (chunk, p);
}
else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
comments_nested ++;
ucl_chunk_skipc (chunk, p);
ucl_chunk_skipc (chunk, p);
continue;
}
}
ucl_chunk_skipc (chunk, p);
}
if (comments_nested != 0) {
ucl_set_err (chunk, UCL_ENESTED, "unfinished multiline comment", &parser->err);
ucl_set_err (parser, UCL_ENESTED,
"unfinished multiline comment", &parser->err);
return false;
}
}
@ -492,7 +513,8 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
/* Copy string */
*dst = UCL_ALLOC (in_len + 1);
if (*dst == NULL) {
ucl_set_err (parser->chunks, 0, "cannot allocate memory for a string", &parser->err);
ucl_set_err (parser, 0, "cannot allocate memory for a string",
&parser->err);
return false;
}
if (need_lowercase) {
@ -514,6 +536,10 @@ ucl_copy_or_store_ptr (struct ucl_parser *parser,
*dst = tmp;
ret = tret;
}
else {
/* Free unexpanded value */
UCL_FREE (in_len + 1, tmp);
}
}
*dst_const = *dst;
}
@ -539,7 +565,7 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
if (!is_array) {
if (obj == NULL) {
obj = ucl_object_typed_new (UCL_OBJECT);
obj = ucl_object_new_full (UCL_OBJECT, parser->chunks->priority);
}
else {
obj->type = UCL_OBJECT;
@ -549,7 +575,7 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
}
else {
if (obj == NULL) {
obj = ucl_object_typed_new (UCL_ARRAY);
obj = ucl_object_new_full (UCL_ARRAY, parser->chunks->priority);
}
else {
obj->type = UCL_ARRAY;
@ -559,7 +585,9 @@ ucl_add_parser_stack (ucl_object_t *obj, struct ucl_parser *parser, bool is_arra
st = UCL_ALLOC (sizeof (struct ucl_stack));
if (st == NULL) {
ucl_set_err (parser->chunks, 0, "cannot allocate memory for an object", &parser->err);
ucl_set_err (parser, 0, "cannot allocate memory for an object",
&parser->err);
ucl_object_unref (obj);
return NULL;
}
st->obj = obj;
@ -676,8 +704,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
}
/* Now check endptr */
if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0' ||
ucl_test_character (*endptr, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
p = endptr;
goto set_obj;
}
@ -788,8 +815,21 @@ ucl_maybe_parse_number (ucl_object_t *obj,
goto set_obj;
}
break;
case '\t':
case ' ':
while (p < end && ucl_test_character(*p, UCL_CHARACTER_WHITESPACE)) {
p++;
}
if (ucl_lex_is_atom_end(*p))
goto set_obj;
break;
}
}
else if (endptr == end) {
/* Just a number at the end of chunk */
p = endptr;
goto set_obj;
}
*pos = c;
return EINVAL;
@ -835,7 +875,7 @@ ucl_lex_number (struct ucl_parser *parser,
return true;
}
else if (ret == ERANGE) {
ucl_set_err (chunk, ERANGE, "numeric value out of range", &parser->err);
ucl_set_err (parser, ERANGE, "numeric value out of range", &parser->err);
}
return false;
@ -860,10 +900,12 @@ ucl_lex_json_string (struct ucl_parser *parser,
if (c < 0x1F) {
/* Unmasked control character */
if (c == '\n') {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected newline", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected newline",
&parser->err);
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected control character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected control character",
&parser->err);
}
return false;
}
@ -871,7 +913,8 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
c = *p;
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
&parser->err);
return false;
}
else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
@ -879,13 +922,15 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
for (i = 0; i < 4 && p < chunk->end; i ++) {
if (!isxdigit (*p)) {
ucl_set_err (chunk, UCL_ESYNTAX, "invalid utf escape", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "invalid utf escape",
&parser->err);
return false;
}
ucl_chunk_skipc (chunk, p);
}
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished escape character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
&parser->err);
return false;
}
}
@ -910,10 +955,42 @@ ucl_lex_json_string (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
ucl_set_err (chunk, UCL_ESYNTAX, "no quote at the end of json string", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "no quote at the end of json string",
&parser->err);
return false;
}
static void
ucl_parser_append_elt (struct ucl_parser *parser, ucl_hash_t *cont,
ucl_object_t *top,
ucl_object_t *elt)
{
ucl_object_t *nobj;
if ((parser->flags & UCL_PARSER_NO_IMPLICIT_ARRAYS) == 0) {
/* Implicit array */
top->flags |= UCL_OBJECT_MULTIVALUE;
DL_APPEND (top, elt);
}
else {
if ((top->flags & UCL_OBJECT_MULTIVALUE) != 0) {
/* Just add to the explicit array */
DL_APPEND (top->value.av, elt);
}
else {
/* Convert to an array */
ucl_hash_delete (cont, top);
nobj = ucl_object_typed_new (UCL_ARRAY);
nobj->key = top->key;
nobj->keylen = top->keylen;
nobj->flags |= UCL_OBJECT_MULTIVALUE;
DL_APPEND (nobj->value.av, top);
DL_APPEND (nobj->value.av, elt);
ucl_hash_insert (cont, nobj, nobj->key, nobj->keylen);
}
}
}
/**
* Parse a key in an object
* @param parser
@ -981,7 +1058,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
else {
/* Invalid identifier */
ucl_set_err (chunk, UCL_ESYNTAX, "key must begin with a letter", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "key must begin with a letter",
&parser->err);
return false;
}
}
@ -997,7 +1075,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
break;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "invalid character in a key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "invalid character in a key",
&parser->err);
return false;
}
}
@ -1015,7 +1094,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
if (p >= chunk->end && got_content) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
return false;
}
else if (!got_content) {
@ -1033,7 +1112,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
got_eq = true;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected '=' character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected '=' character",
&parser->err);
return false;
}
}
@ -1043,7 +1123,8 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
got_semicolon = true;
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected ':' character", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unexpected ':' character",
&parser->err);
return false;
}
}
@ -1061,7 +1142,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
if (p >= chunk->end && got_content) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished key", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
return false;
}
@ -1096,7 +1177,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
}
/* Create a new object */
nobj = ucl_object_new ();
nobj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
keylen = ucl_copy_or_store_ptr (parser, c, &nobj->trash_stack[UCL_TRASH_KEY],
&key, end - c, need_unescape, parser->flags & UCL_PARSER_KEY_LOWERCASE, false);
if (keylen == -1) {
@ -1104,7 +1185,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
return false;
}
else if (keylen == 0) {
ucl_set_err (chunk, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "empty keys are not allowed", &parser->err);
ucl_object_unref (nobj);
return false;
}
@ -1120,7 +1201,27 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk, bool *next_ke
parser->stack->obj->len ++;
}
else {
DL_APPEND (tobj, nobj);
/*
* The logic here is the following:
*
* - if we have two objects with the same priority, then we form an
* implicit or explicit array
* - if a new object has bigger priority, then we overwrite an old one
* - if a new object has lower priority, then we ignore it
*/
unsigned priold = ucl_object_get_priority (tobj),
prinew = ucl_object_get_priority (nobj);
if (priold == prinew) {
ucl_parser_append_elt (parser, container, tobj, nobj);
}
else if (priold > prinew) {
ucl_object_unref (nobj);
return true;
}
else {
ucl_hash_replace (container, tobj, nobj);
ucl_object_unref (tobj);
}
}
if (ucl_escape) {
@ -1197,11 +1298,6 @@ ucl_parse_string_value (struct ucl_parser *parser,
ucl_chunk_skipc (chunk, p);
}
if (p >= chunk->end) {
ucl_set_err (chunk, UCL_ESYNTAX, "unfinished value", &parser->err);
return false;
}
return true;
}
@ -1219,7 +1315,7 @@ ucl_parse_multiline_string (struct ucl_parser *parser,
int term_len, unsigned char const **beg,
bool *var_expand)
{
const unsigned char *p, *c;
const unsigned char *p, *c, *tend;
bool newline = false;
int len = 0;
@ -1232,7 +1328,13 @@ ucl_parse_multiline_string (struct ucl_parser *parser,
if (chunk->end - p < term_len) {
return 0;
}
else if (memcmp (p, term, term_len) == 0 && (p[term_len] == '\n' || p[term_len] == '\r')) {
else if (memcmp (p, term, term_len) == 0) {
tend = p + term_len;
if (*tend != '\n' && *tend != ';' && *tend != ',') {
/* Incomplete terminator */
ucl_chunk_skipc (chunk, p);
continue;
}
len = p - c;
chunk->remain -= term_len;
chunk->pos = p + term_len;
@ -1263,7 +1365,7 @@ ucl_get_value_object (struct ucl_parser *parser)
if (parser->stack->obj->type == UCL_ARRAY) {
/* Object must be allocated */
obj = ucl_object_new ();
obj = ucl_object_new_full (UCL_NULL, parser->chunks->priority);
t = parser->stack->obj->value.av;
DL_APPEND (t, obj);
parser->cur_obj = obj;
@ -1378,7 +1480,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
chunk->line ++;
if ((str_len = ucl_parse_multiline_string (parser, chunk, c,
p - c, &c, &var_expand)) == 0) {
ucl_set_err (chunk, UCL_ESYNTAX, "unterminated multiline value", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"unterminated multiline value", &parser->err);
return false;
}
obj->type = UCL_STRING;
@ -1423,7 +1526,8 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
str_len = chunk->pos - c - stripped_spaces;
if (str_len <= 0) {
ucl_set_err (chunk, 0, "string value must not be empty", &parser->err);
ucl_set_err (parser, 0, "string value must not be empty",
&parser->err);
return false;
}
else if (str_len == 4 && memcmp (c, "null", 4) == 0) {
@ -1482,7 +1586,9 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
else if (ucl_test_character (*p, UCL_CHARACTER_VALUE_END)) {
if (*p == '}' || *p == ']') {
if (parser->stack == NULL) {
ucl_set_err (chunk, UCL_ESYNTAX, "end of array or object detected without corresponding start", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"end of array or object detected without corresponding start",
&parser->err);
return false;
}
if ((*p == '}' && parser->stack->obj->type == UCL_OBJECT) ||
@ -1503,7 +1609,9 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
}
}
else {
ucl_set_err (chunk, UCL_ESYNTAX, "unexpected terminating symbol detected", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX,
"unexpected terminating symbol detected",
&parser->err);
return false;
}
@ -1525,7 +1633,8 @@ ucl_parse_after_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
else {
/* Anything else */
if (!got_sep) {
ucl_set_err (chunk, UCL_ESYNTAX, "delimiter is missing", &parser->err);
ucl_set_err (parser, UCL_ESYNTAX, "delimiter is missing",
&parser->err);
return false;
}
return true;
@ -1612,6 +1721,120 @@ ucl_parse_macro_value (struct ucl_parser *parser,
return true;
}
/**
* Parse macro arguments as UCL object
* @param parser parser structure
* @param chunk the current data chunk
* @return
*/
static ucl_object_t *
ucl_parse_macro_arguments (struct ucl_parser *parser,
struct ucl_chunk *chunk)
{
ucl_object_t *res = NULL;
struct ucl_parser *params_parser;
int obraces = 1, ebraces = 0, state = 0;
const unsigned char *p, *c;
size_t args_len = 0;
struct ucl_parser_saved_state saved;
saved.column = chunk->column;
saved.line = chunk->line;
saved.pos = chunk->pos;
saved.remain = chunk->remain;
p = chunk->pos;
if (*p != '(' || chunk->remain < 2) {
return NULL;
}
/* Set begin and start */
ucl_chunk_skipc (chunk, p);
c = p;
while ((p) < (chunk)->end) {
switch (state) {
case 0:
/* Parse symbols and check for '(', ')' and '"' */
if (*p == '(') {
obraces ++;
}
else if (*p == ')') {
ebraces ++;
}
else if (*p == '"') {
state = 1;
}
/* Check pairing */
if (obraces == ebraces) {
state = 99;
}
else {
args_len ++;
}
/* Check overflow */
if (chunk->remain == 0) {
goto restore_chunk;
}
ucl_chunk_skipc (chunk, p);
break;
case 1:
/* We have quote character, so skip all but quotes */
if (*p == '"' && *(p - 1) != '\\') {
state = 0;
}
if (chunk->remain == 0) {
goto restore_chunk;
}
ucl_chunk_skipc (chunk, p);
break;
case 99:
/*
* We have read the full body of arguments, so we need to parse and set
* object from that
*/
params_parser = ucl_parser_new (parser->flags);
if (!ucl_parser_add_chunk (params_parser, c, args_len)) {
ucl_set_err (parser, UCL_ESYNTAX, "macro arguments parsing error",
&parser->err);
}
else {
res = ucl_parser_get_object (params_parser);
}
ucl_parser_free (params_parser);
return res;
break;
}
}
return res;
restore_chunk:
chunk->column = saved.column;
chunk->line = saved.line;
chunk->pos = saved.pos;
chunk->remain = saved.remain;
return NULL;
}
#define SKIP_SPACES_COMMENTS(parser, chunk, p) do { \
while ((p) < (chunk)->end) { \
if (!ucl_test_character (*(p), UCL_CHARACTER_WHITESPACE_UNSAFE)) { \
if ((chunk)->remain >= 2 && ucl_lex_is_comment ((p)[0], (p)[1])) { \
if (!ucl_skip_comments (parser)) { \
return false; \
} \
p = (chunk)->pos; \
} \
break; \
} \
ucl_chunk_skipc (chunk, p); \
} \
} while(0)
/**
* Handle the main states of rcl parser
* @param parser parser structure
@ -1622,13 +1845,13 @@ ucl_parse_macro_value (struct ucl_parser *parser,
static bool
ucl_state_machine (struct ucl_parser *parser)
{
ucl_object_t *obj;
ucl_object_t *obj, *macro_args;
struct ucl_chunk *chunk = parser->chunks;
const unsigned char *p, *c = NULL, *macro_start = NULL;
unsigned char *macro_escaped;
size_t macro_len = 0;
struct ucl_macro *macro = NULL;
bool next_key = false, end_of_object = false;
bool next_key = false, end_of_object = false, ret;
if (parser->top_obj == NULL) {
if (*chunk->pos == '[') {
@ -1654,7 +1877,6 @@ ucl_state_machine (struct ucl_parser *parser)
* if we got [ or { correspondingly or can just treat new data as
* a key of newly created object
*/
obj = parser->cur_obj;
if (!ucl_skip_comments (parser)) {
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
@ -1691,7 +1913,7 @@ ucl_state_machine (struct ucl_parser *parser)
}
if (parser->stack == NULL) {
/* No objects are on stack, but we want to parse a key */
ucl_set_err (chunk, UCL_ESYNTAX, "top object is finished but the parser "
ucl_set_err (parser, UCL_ESYNTAX, "top object is finished but the parser "
"expects a key", &parser->err);
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
@ -1757,7 +1979,8 @@ ucl_state_machine (struct ucl_parser *parser)
p = chunk->pos;
break;
case UCL_STATE_MACRO_NAME:
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE) &&
*p != '(') {
ucl_chunk_skipc (chunk, p);
}
else if (p - c > 0) {
@ -1772,48 +1995,51 @@ ucl_state_machine (struct ucl_parser *parser)
return false;
}
/* Now we need to skip all spaces */
while (p < chunk->end) {
if (!ucl_test_character (*p, UCL_CHARACTER_WHITESPACE_UNSAFE)) {
if (chunk->remain >= 2 && ucl_lex_is_comment (p[0], p[1])) {
/* Skip comment */
if (!ucl_skip_comments (parser)) {
return false;
}
p = chunk->pos;
}
break;
}
ucl_chunk_skipc (chunk, p);
}
SKIP_SPACES_COMMENTS(parser, chunk, p);
parser->state = UCL_STATE_MACRO;
}
break;
case UCL_STATE_MACRO:
if (*chunk->pos == '(') {
macro_args = ucl_parse_macro_arguments (parser, chunk);
p = chunk->pos;
if (macro_args) {
SKIP_SPACES_COMMENTS(parser, chunk, p);
}
}
else {
macro_args = NULL;
}
if (!ucl_parse_macro_value (parser, chunk, macro,
&macro_start, &macro_len)) {
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
return false;
}
macro_len = ucl_expand_variable (parser, &macro_escaped, macro_start, macro_len);
macro_len = ucl_expand_variable (parser, &macro_escaped,
macro_start, macro_len);
parser->state = parser->prev_state;
if (macro_escaped == NULL) {
if (!macro->handler (macro_start, macro_len, macro->ud)) {
return false;
}
ret = macro->handler (macro_start, macro_len, macro_args,
macro->ud);
}
else {
if (!macro->handler (macro_escaped, macro_len, macro->ud)) {
UCL_FREE (macro_len + 1, macro_escaped);
return false;
}
ret = macro->handler (macro_escaped, macro_len, macro_args,
macro->ud);
UCL_FREE (macro_len + 1, macro_escaped);
}
p = chunk->pos;
if (macro_args) {
ucl_object_unref (macro_args);
}
if (!ret) {
return false;
}
break;
default:
/* TODO: add all states */
ucl_set_err (chunk, UCL_EINTERNAL, "internal error: parser is in an unknown state", &parser->err);
ucl_set_err (parser, UCL_EINTERNAL,
"internal error: parser is in an unknown state", &parser->err);
parser->state = UCL_STATE_ERROR;
return false;
}
@ -1888,7 +2114,7 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
if (new != NULL) {
/* Remove variable */
LL_DELETE (parser->variables, new);
DL_DELETE (parser->variables, new);
free (new->var);
free (new->value);
UCL_FREE (sizeof (struct ucl_variable), new);
@ -1910,7 +2136,7 @@ ucl_parser_register_variable (struct ucl_parser *parser, const char *var,
new->value = strdup (value);
new->value_len = strlen (value);
LL_PREPEND (parser->variables, new);
DL_APPEND (parser->variables, new);
}
else {
free (new->value);
@ -1929,15 +2155,19 @@ ucl_parser_set_variables_handler (struct ucl_parser *parser,
}
bool
ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
size_t len)
ucl_parser_add_chunk_priority (struct ucl_parser *parser, const unsigned char *data,
size_t len, unsigned priority)
{
struct ucl_chunk *chunk;
if (data == NULL || len == 0) {
if (data == NULL) {
ucl_create_err (&parser->err, "invalid chunk added");
return false;
}
if (len == 0) {
parser->top_obj = ucl_object_new_full (UCL_OBJECT, priority);
return true;
}
if (parser->state != UCL_STATE_ERROR) {
chunk = UCL_ALLOC (sizeof (struct ucl_chunk));
if (chunk == NULL) {
@ -1950,6 +2180,7 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
chunk->end = chunk->begin + len;
chunk->line = 1;
chunk->column = 0;
chunk->priority = priority;
LL_PREPEND (parser->chunks, chunk);
parser->recursion ++;
if (parser->recursion > UCL_MAX_RECURSION) {
@ -1965,6 +2196,13 @@ ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
return false;
}
bool
ucl_parser_add_chunk (struct ucl_parser *parser, const unsigned char *data,
size_t len)
{
return ucl_parser_add_chunk_priority (parser, data, len, 0);
}
bool
ucl_parser_add_string (struct ucl_parser *parser, const char *data,
size_t len)

View File

@ -25,6 +25,8 @@
#include "ucl_internal.h"
#include "ucl_chartable.h"
#include <glob.h>
#ifdef HAVE_LIBGEN_H
#include <libgen.h> /* For dirname */
#endif
@ -129,11 +131,6 @@ static char* ucl_realpath(const char *path, char *resolved_path) {
#define ucl_realpath realpath
#endif
/**
* @file rcl_util.c
* Utilities for rcl parsing
*/
typedef void (*ucl_object_dtor) (ucl_object_t *obj);
static void ucl_object_free_internal (ucl_object_t *obj, bool allow_rec,
ucl_object_dtor dtor);
@ -148,7 +145,19 @@ ucl_object_dtor_free (ucl_object_t *obj)
if (obj->trash_stack[UCL_TRASH_VALUE] != NULL) {
UCL_FREE (obj->len, obj->trash_stack[UCL_TRASH_VALUE]);
}
UCL_FREE (sizeof (ucl_object_t), obj);
/* Do not free ephemeral objects */
if ((obj->flags & UCL_OBJECT_EPHEMERAL) == 0) {
if (obj->type != UCL_USERDATA) {
UCL_FREE (sizeof (ucl_object_t), obj);
}
else {
struct ucl_object_userdata *ud = (struct ucl_object_userdata *)obj;
if (ud->dtor) {
ud->dtor (obj->value.ud);
}
UCL_FREE (sizeof (*ud), obj);
}
}
}
/*
@ -423,7 +432,11 @@ ucl_parser_free (struct ucl_parser *parser)
}
if (parser->err != NULL) {
utstring_free(parser->err);
utstring_free (parser->err);
}
if (parser->cur_file) {
free (parser->cur_file);
}
UCL_FREE (sizeof (struct ucl_parser), parser);
@ -701,7 +714,8 @@ ucl_sig_check (const unsigned char *data, size_t datalen,
*/
static bool
ucl_include_url (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist)
struct ucl_parser *parser, bool check_signature, bool must_exist,
unsigned priority)
{
bool res;
@ -744,7 +758,7 @@ ucl_include_url (const unsigned char *data, size_t len,
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
res = ucl_parser_add_chunk (parser, buf, buflen);
res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
if (res == true) {
/* Remove chunk from the stack */
chunk = parser->chunks;
@ -761,23 +775,30 @@ ucl_include_url (const unsigned char *data, size_t len,
}
/**
* Include a file to configuration
* Include a single file to the parser
* @param data
* @param len
* @param parser
* @param err
* @param check_signature
* @param must_exist
* @param allow_glob
* @param priority
* @return
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist)
ucl_include_file_single (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist,
unsigned priority)
{
bool res;
struct ucl_chunk *chunk;
unsigned char *buf = NULL;
char *old_curfile;
size_t buflen;
char filebuf[PATH_MAX], realbuf[PATH_MAX];
int prev_state;
struct ucl_variable *cur_var, *tmp_var, *old_curdir = NULL,
*old_filename = NULL;
snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
if (ucl_realpath (filebuf, realbuf) == NULL) {
@ -790,6 +811,13 @@ ucl_include_file (const unsigned char *data, size_t len,
return false;
}
if (parser->cur_file && strcmp (realbuf, parser->cur_file) == 0) {
/* We are likely including the file itself */
ucl_create_err (&parser->err, "trying to include the file %s from itself",
realbuf);
return false;
}
if (!ucl_fetch_file (realbuf, &buf, &buflen, &parser->err, must_exist)) {
return (!must_exist || false);
}
@ -818,19 +846,66 @@ ucl_include_file (const unsigned char *data, size_t len,
#endif
}
old_curfile = parser->cur_file;
parser->cur_file = strdup (realbuf);
/* Store old file vars */
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
if (strcmp (cur_var->var, "CURDIR") == 0) {
old_curdir = cur_var;
DL_DELETE (parser->variables, cur_var);
}
else if (strcmp (cur_var->var, "FILENAME") == 0) {
old_filename = cur_var;
DL_DELETE (parser->variables, cur_var);
}
}
ucl_parser_set_filevars (parser, realbuf, false);
prev_state = parser->state;
parser->state = UCL_STATE_INIT;
res = ucl_parser_add_chunk (parser, buf, buflen);
if (res == true) {
/* Remove chunk from the stack */
chunk = parser->chunks;
if (chunk != NULL) {
parser->chunks = chunk->next;
UCL_FREE (sizeof (struct ucl_chunk), chunk);
res = ucl_parser_add_chunk_priority (parser, buf, buflen, priority);
if (!res && !must_exist) {
/* Free error */
utstring_free (parser->err);
parser->err = NULL;
parser->state = UCL_STATE_AFTER_VALUE;
}
/* Remove chunk from the stack */
chunk = parser->chunks;
if (chunk != NULL) {
parser->chunks = chunk->next;
UCL_FREE (sizeof (struct ucl_chunk), chunk);
parser->recursion --;
}
/* Restore old file vars */
parser->cur_file = old_curfile;
DL_FOREACH_SAFE (parser->variables, cur_var, tmp_var) {
if (strcmp (cur_var->var, "CURDIR") == 0 && old_curdir) {
DL_DELETE (parser->variables, cur_var);
free (cur_var->var);
free (cur_var->value);
UCL_FREE (sizeof (struct ucl_variable), cur_var);
}
else if (strcmp (cur_var->var, "FILENAME") == 0 && old_filename) {
DL_DELETE (parser->variables, cur_var);
free (cur_var->var);
free (cur_var->value);
UCL_FREE (sizeof (struct ucl_variable), cur_var);
}
}
if (old_filename) {
DL_APPEND (parser->variables, old_filename);
}
if (old_curdir) {
DL_APPEND (parser->variables, old_curdir);
}
if (old_curfile) {
free (old_curfile);
}
parser->state = prev_state;
@ -842,6 +917,138 @@ ucl_include_file (const unsigned char *data, size_t len,
return res;
}
/**
* Include a file to configuration
* @param data
* @param len
* @param parser
* @param err
* @return
*/
static bool
ucl_include_file (const unsigned char *data, size_t len,
struct ucl_parser *parser, bool check_signature, bool must_exist,
bool allow_glob, unsigned priority)
{
const unsigned char *p = data, *end = data + len;
bool need_glob = false;
int cnt = 0;
glob_t globbuf;
char glob_pattern[PATH_MAX];
size_t i;
if (!allow_glob) {
return ucl_include_file_single (data, len, parser, check_signature,
must_exist, priority);
}
else {
/* Check for special symbols in a filename */
while (p != end) {
if (*p == '*' || *p == '?') {
need_glob = true;
break;
}
p ++;
}
if (need_glob) {
memset (&globbuf, 0, sizeof (globbuf));
ucl_strlcpy (glob_pattern, (const char *)data, sizeof (glob_pattern));
if (glob (glob_pattern, 0, NULL, &globbuf) != 0) {
return (!must_exist || false);
}
for (i = 0; i < globbuf.gl_pathc; i ++) {
if (!ucl_include_file_single ((unsigned char *)globbuf.gl_pathv[i],
strlen (globbuf.gl_pathv[i]), parser, check_signature,
must_exist, priority)) {
globfree (&globbuf);
return false;
}
cnt ++;
}
globfree (&globbuf);
if (cnt == 0 && must_exist) {
ucl_create_err (&parser->err, "cannot match any files for pattern %s",
glob_pattern);
return false;
}
}
else {
return ucl_include_file_single (data, len, parser, check_signature,
must_exist, priority);
}
}
return true;
}
/**
* Common function to handle .*include* macros
* @param data
* @param len
* @param args
* @param parser
* @param default_try
* @param default_sign
* @return
*/
static bool
ucl_include_common (const unsigned char *data, size_t len,
const ucl_object_t *args, struct ucl_parser *parser,
bool default_try,
bool default_sign)
{
bool try_load, allow_glob, allow_url, need_sign;
unsigned priority;
const ucl_object_t *param;
ucl_object_iter_t it = NULL;
/* Default values */
try_load = default_try;
allow_glob = false;
allow_url = true;
need_sign = default_sign;
priority = 0;
/* Process arguments */
if (args != NULL && args->type == UCL_OBJECT) {
while ((param = ucl_iterate_object (args, &it, true)) != NULL) {
if (param->type == UCL_BOOLEAN) {
if (strcmp (param->key, "try") == 0) {
try_load = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "sign") == 0) {
need_sign = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "glob") == 0) {
allow_glob = ucl_object_toboolean (param);
}
else if (strcmp (param->key, "url") == 0) {
allow_url = ucl_object_toboolean (param);
}
}
else if (param->type == UCL_INT) {
if (strcmp (param->key, "priority") == 0) {
priority = ucl_object_toint (param);
}
}
}
}
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, need_sign, !try_load,
allow_glob, priority);
}
else if (allow_url) {
/* Globbing is not used for URL's */
return ucl_include_url (data, len, parser, need_sign, !try_load,
priority);
}
return false;
}
/**
* Handle include macro
* @param data include data
@ -851,16 +1058,12 @@ ucl_include_file (const unsigned char *data, size_t len,
* @return
*/
UCL_EXTERN bool
ucl_include_handler (const unsigned char *data, size_t len, void* ud)
ucl_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, false, true);
}
return ucl_include_url (data, len, parser, false, true);
return ucl_include_common (data, len, args, parser, false, false);
}
/**
@ -872,30 +1075,22 @@ ucl_include_handler (const unsigned char *data, size_t len, void* ud)
* @return
*/
UCL_EXTERN bool
ucl_includes_handler (const unsigned char *data, size_t len, void* ud)
ucl_includes_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, true, true);
}
return ucl_include_url (data, len, parser, true, true);
return ucl_include_common (data, len, args, parser, false, true);
}
UCL_EXTERN bool
ucl_try_include_handler (const unsigned char *data, size_t len, void* ud)
ucl_try_include_handler (const unsigned char *data, size_t len,
const ucl_object_t *args, void* ud)
{
struct ucl_parser *parser = ud;
if (*data == '/' || *data == '.') {
/* Try to load a file */
return ucl_include_file (data, len, parser, false, false);
}
return ucl_include_url (data, len, parser, false, false);
return ucl_include_common (data, len, args, parser, true, false);
}
UCL_EXTERN bool
@ -947,6 +1142,10 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
return false;
}
if (parser->cur_file) {
free (parser->cur_file);
}
parser->cur_file = strdup (realbuf);
ucl_parser_set_filevars (parser, realbuf, false);
ret = ucl_parser_add_chunk (parser, buf, len);
@ -957,6 +1156,39 @@ ucl_parser_add_file (struct ucl_parser *parser, const char *filename)
return ret;
}
UCL_EXTERN bool
ucl_parser_add_fd (struct ucl_parser *parser, int fd)
{
unsigned char *buf;
size_t len;
bool ret;
struct stat st;
if (fstat (fd, &st) == -1) {
ucl_create_err (&parser->err, "cannot stat fd %d: %s",
fd, strerror (errno));
return false;
}
if ((buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
ucl_create_err (&parser->err, "cannot mmap fd %d: %s",
fd, strerror (errno));
return false;
}
if (parser->cur_file) {
free (parser->cur_file);
}
parser->cur_file = NULL;
len = st.st_size;
ret = ucl_parser_add_chunk (parser, buf, len);
if (len > 0) {
ucl_munmap (buf, len);
}
return ret;
}
size_t
ucl_strlcpy (char *dst, const char *src, size_t siz)
{
@ -1176,6 +1408,15 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
}
/* workaround for some use cases */
if (elt->trash_stack[UCL_TRASH_KEY] != NULL &&
key != (const char *)elt->trash_stack[UCL_TRASH_KEY]) {
/* Remove copied key */
free (elt->trash_stack[UCL_TRASH_KEY]);
elt->trash_stack[UCL_TRASH_KEY] = NULL;
elt->flags &= ~UCL_OBJECT_ALLOCATED_KEY;
}
elt->key = key;
elt->keylen = keylen;
@ -1185,9 +1426,8 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
found = __DECONST (ucl_object_t *, ucl_hash_search_obj (top->value.ov, elt));
if (!found) {
if (found == NULL) {
top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
DL_APPEND (found, elt);
top->len ++;
if (replace) {
ret = false;
@ -1195,11 +1435,8 @@ ucl_object_insert_key_common (ucl_object_t *top, ucl_object_t *elt,
}
else {
if (replace) {
ucl_hash_delete (top->value.ov, found);
ucl_hash_replace (top->value.ov, found, elt);
ucl_object_unref (found);
top->value.ov = ucl_hash_insert_object (top->value.ov, elt);
found = NULL;
DL_APPEND (found, elt);
}
else if (merge) {
if (found->type != UCL_OBJECT && elt->type == UCL_OBJECT) {
@ -1310,6 +1547,40 @@ ucl_object_replace_key (ucl_object_t *top, ucl_object_t *elt,
return ucl_object_insert_key_common (top, elt, key, keylen, copy_key, false, true);
}
bool
ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
{
ucl_object_t *cur = NULL, *cp = NULL, *found = NULL;
ucl_object_iter_t iter = NULL;
if (top == NULL || top->type != UCL_OBJECT || elt == NULL || elt->type != UCL_OBJECT) {
return false;
}
/* Mix two hashes */
while ((cur = (ucl_object_t*)ucl_hash_iterate (elt->value.ov, &iter))) {
if (copy) {
cp = ucl_object_copy (cur);
}
else {
cp = ucl_object_ref (cur);
}
found = __DECONST(ucl_object_t *, ucl_hash_search (top->value.ov, cp->key, cp->keylen));
if (found == NULL) {
/* The key does not exist */
top->value.ov = ucl_hash_insert_object (top->value.ov, cp);
top->len ++;
}
else {
/* The key already exists, replace it */
ucl_hash_replace (top->value.ov, found, cp);
ucl_object_unref (found);
}
}
return true;
}
const ucl_object_t *
ucl_object_find_keyl (const ucl_object_t *obj, const char *key, size_t klen)
{
@ -1372,9 +1643,6 @@ ucl_iterate_object (const ucl_object_t *obj, ucl_object_iter_t *iter, bool expan
elt = *iter;
if (elt == NULL) {
elt = obj;
if (elt == NULL) {
return NULL;
}
}
else if (elt == obj) {
return NULL;
@ -1442,29 +1710,59 @@ ucl_lookup_path (const ucl_object_t *top, const char *path_in) {
ucl_object_t *
ucl_object_new (void)
{
ucl_object_t *new;
new = malloc (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = UCL_NULL;
}
return new;
return ucl_object_typed_new (UCL_NULL);
}
ucl_object_t *
ucl_object_typed_new (ucl_type_t type)
{
return ucl_object_new_full (type, 0);
}
ucl_object_t *
ucl_object_new_full (ucl_type_t type, unsigned priority)
{
ucl_object_t *new;
new = malloc (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = (type <= UCL_NULL ? type : UCL_NULL);
if (type != UCL_USERDATA) {
new = UCL_ALLOC (sizeof (ucl_object_t));
if (new != NULL) {
memset (new, 0, sizeof (ucl_object_t));
new->ref = 1;
new->type = (type <= UCL_NULL ? type : UCL_NULL);
new->next = NULL;
new->prev = new;
ucl_object_set_priority (new, priority);
}
}
else {
new = ucl_object_new_userdata (NULL, NULL);
ucl_object_set_priority (new, priority);
}
return new;
}
ucl_object_t*
ucl_object_new_userdata (ucl_userdata_dtor dtor, ucl_userdata_emitter emitter)
{
struct ucl_object_userdata *new;
size_t nsize = sizeof (*new);
new = UCL_ALLOC (nsize);
if (new != NULL) {
memset (new, 0, nsize);
new->obj.ref = 1;
new->obj.type = UCL_USERDATA;
new->obj.next = NULL;
new->obj.prev = (ucl_object_t *)new;
new->dtor = dtor;
new->emitter = emitter;
}
return (ucl_object_t *)new;
}
ucl_type_t
ucl_object_type (const ucl_object_t *obj)
{
@ -1576,6 +1874,30 @@ ucl_array_prepend (ucl_object_t *top, ucl_object_t *elt)
return true;
}
bool
ucl_array_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
{
ucl_object_t *cur, *tmp, *cp;
if (elt == NULL || top == NULL || top->type != UCL_ARRAY || elt->type != UCL_ARRAY) {
return false;
}
DL_FOREACH_SAFE (elt->value.av, cur, tmp) {
if (copy) {
cp = ucl_object_copy (cur);
}
else {
cp = ucl_object_ref (cur);
}
if (cp != NULL) {
ucl_array_append (top, cp);
}
}
return true;
}
ucl_object_t *
ucl_array_delete (ucl_object_t *top, ucl_object_t *elt)
{
@ -1660,6 +1982,28 @@ ucl_array_find_index (const ucl_object_t *top, unsigned int index)
return NULL;
}
ucl_object_t *
ucl_array_replace_index (ucl_object_t *top, ucl_object_t *elt,
unsigned int index)
{
ucl_object_t *cur, *tmp;
if (top == NULL || top->type != UCL_ARRAY || elt == NULL ||
top->len == 0 || (index + 1) > top->len) {
return NULL;
}
DL_FOREACH_SAFE (top->value.av, cur, tmp) {
if (index == 0) {
DL_REPLACE_ELEM (top->value.av, cur, elt);
return cur;
}
--index;
}
return NULL;
}
ucl_object_t *
ucl_elt_append (ucl_object_t *head, ucl_object_t *elt)
{
@ -1849,16 +2193,99 @@ ucl_object_ref (const ucl_object_t *obj)
ucl_object_t *res = NULL;
if (obj != NULL) {
res = __DECONST (ucl_object_t *, obj);
if (obj->flags & UCL_OBJECT_EPHEMERAL) {
/*
* Use deep copy for ephemeral objects, note that its refcount
* is NOT increased, since ephemeral objects does not need refcount
* at all
*/
res = ucl_object_copy (obj);
}
else {
res = __DECONST (ucl_object_t *, obj);
#ifdef HAVE_ATOMIC_BUILTINS
(void)__sync_add_and_fetch (&res->ref, 1);
(void)__sync_add_and_fetch (&res->ref, 1);
#else
res->ref ++;
res->ref ++;
#endif
}
}
return res;
}
static ucl_object_t *
ucl_object_copy_internal (const ucl_object_t *other, bool allow_array)
{
ucl_object_t *new;
ucl_object_iter_t it = NULL;
const ucl_object_t *cur;
new = malloc (sizeof (*new));
if (new != NULL) {
memcpy (new, other, sizeof (*new));
if (other->flags & UCL_OBJECT_EPHEMERAL) {
/* Copied object is always non ephemeral */
new->flags &= ~UCL_OBJECT_EPHEMERAL;
}
new->ref = 1;
/* Unlink from others */
new->next = NULL;
new->prev = new;
/* deep copy of values stored */
if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
new->trash_stack[UCL_TRASH_KEY] =
strdup (other->trash_stack[UCL_TRASH_KEY]);
if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
new->key = new->trash_stack[UCL_TRASH_KEY];
}
}
if (other->trash_stack[UCL_TRASH_VALUE] != NULL) {
new->trash_stack[UCL_TRASH_VALUE] =
strdup (other->trash_stack[UCL_TRASH_VALUE]);
if (new->type == UCL_STRING) {
new->value.sv = new->trash_stack[UCL_TRASH_VALUE];
}
}
if (other->type == UCL_ARRAY || other->type == UCL_OBJECT) {
/* reset old value */
memset (&new->value, 0, sizeof (new->value));
while ((cur = ucl_iterate_object (other, &it, true)) != NULL) {
if (other->type == UCL_ARRAY) {
ucl_array_append (new, ucl_object_copy_internal (cur, false));
}
else {
ucl_object_t *cp = ucl_object_copy_internal (cur, true);
if (cp != NULL) {
ucl_object_insert_key (new, cp, cp->key, cp->keylen,
false);
}
}
}
}
else if (allow_array && other->next != NULL) {
LL_FOREACH (other->next, cur) {
ucl_object_t *cp = ucl_object_copy_internal (cur, false);
if (cp != NULL) {
DL_APPEND (new, cp);
}
}
}
}
return new;
}
ucl_object_t *
ucl_object_copy (const ucl_object_t *other)
{
return ucl_object_copy_internal (other, true);
}
void
ucl_object_unref (ucl_object_t *obj)
{
@ -1956,3 +2383,25 @@ ucl_object_array_sort (ucl_object_t *ar,
DL_SORT (ar->value.av, cmp);
}
#define PRIOBITS 4
unsigned int
ucl_object_get_priority (const ucl_object_t *obj)
{
if (obj == NULL) {
return 0;
}
return (obj->flags >> ((sizeof (obj->flags) * NBBY) - PRIOBITS));
}
void
ucl_object_set_priority (ucl_object_t *obj,
unsigned int priority)
{
if (obj != NULL) {
priority &= (0x1 << PRIOBITS) - 1;
obj->flags |= priority << ((sizeof (obj->flags) * NBBY) - PRIOBITS);
}
}

View File

@ -1,4 +1,4 @@
EXTRA_DIST = $(TESTS) basic schema generate.res rcl_test.json.xz
EXTRA_DIST = $(TESTS) basic schema generate.res streamline.res rcl_test.json.xz
TESTS = basic.test \
generate.test \

View File

@ -0,0 +1,2 @@
key1: 12 ,
key2: 12 value

View File

@ -0,0 +1,3 @@
key1 = 12;
key2 = "12 value";

View File

@ -0,0 +1,9 @@
key = value_orig;
# test glob
.include(glob=true) "${CURDIR}/include_dir/test*.conf"
.include(priority=1) "${CURDIR}/include_dir/pri1.conf"
.include(priority=2) "${CURDIR}/include_dir/pri2.conf"
.include(try=true) "${CURDIR}/include_dir/invalid.conf"

View File

@ -0,0 +1,8 @@
key = "value_orig";
key = "value1";
key = "value2";
key = "value3";
key_pri = "priority2";
key_trace1 = "pri1";
key_trace2 = "pri2";

View File

@ -10,7 +10,14 @@ licenses [
"BSD",
]
flatsize = 60523;
desc = "pkgconf is a program which helps to configure compiler and linker flags for\ndevelopment frameworks. It is similar to pkg-config, but was written from\nscratch in Summer of 2011 to replace pkg-config, which now needs itself to build\nitself.\n\nWWW: https://github.com/pkgconf/pkgconf";
desc = <<EOD
pkgconf is a program which helps to configure compiler and linker flags for
development frameworks. It is similar to pkg-config, but was written from
scratch in Summer of 2011 to replace pkg-config, which now needs itself to build
itself.
WWW: https://github.com/pkgconf/pkgconf
EOD;
categories [
"devel",
]
@ -31,6 +38,17 @@ scripts {
pre-deinstall = "cd /usr/local\nn";
post-deinstall = "cd /usr/local\nn";
}
multiline-key = "test\ntest\ntest\\n\n/* comment like */\n# Some invalid endings\n EOD\nEOD \nEOF\n# Valid ending + empty string\n";
multiline-key = <<EOD
test
test
test\n
/* comment like */
# Some invalid endings
EOD
EOD
EOF
# Valid ending + empty string
EOD;
normal-key = "<<EODnot";

View File

@ -0,0 +1,25 @@
# This test is intended to check various comments in ucl
obj {
key = value
key = "/* value"
/*
key = value
*/
# Nested comments
key = nested
/*
adasdasdads
/* asdasdasd */asjdasjldaskd
/* asdsadasd */
/* /* /* /* /* */ */ */ */ */
# some
*/
key = quotes # quoted
# Quotes
/*
key = "/* value"
key = "*/value"
*/
}

View File

@ -0,0 +1,7 @@
obj {
key = "value";
key = "/* value";
key = "nested";
key = "quotes";
}

View File

@ -0,0 +1 @@
@@@@ BAD UCL ~~~~

View File

@ -0,0 +1,2 @@
key_pri = priority1;
key_trace1 = pri1;

Some files were not shown because too many files have changed in this diff Show More