Hi All,

I've been attempting to build uClibc on Ubuntu 12 (x64) so I can get
some familiar tools on it. I've been having some issues, and they are
expected.

Attached is a small program that reads unistd.h (from
<linux>/asm/unistd.h) and writes it to sysnum.h (in <uClibc>/bits) for
use by uClibc's build process. It's offered to others who might also
find it useful.

The program attempts to fill the gap of incomplete parsing by
gen_bits_syscall_h.sh. In my particular case, a number of syscalls
were missing, including __NR_open, __NR_dup, __NR_fstat, and
__NR_pipe.

The trick to using the program (uclibc-syscall) is to run `make`, wait
for the errors due to missing __NR_* defines, and then run
`uclibc-syscall.exe`. You wait for the errors from the build process
because `make` creates a number of directories and symlinks, so its
easiest to allow make to do the work (and create the bits/ directory).

I hard coded the values for UNISTD_IN and SYSNUM_OUT. Please adjust to
suite your taste. It would probably be best to read these from argv[1]
and argv[2], but that would mean a lot more defensive programming due
to the possible taint of argv[]. The defensive programming would
include complete validation and compiler/linker hardening
(https://www.owasp.org/index.php/C-Based_Toolchain_Hardening).

Jeff
// Written and placed in public domain by Jeffrey Walton, noloa...@gmail.com
//

// uclibc-syscall attempts to address gaps in uClibc's gen_bits_syscall_h.sh script.
// The uClibc script generates a file named sysnums.h, and includes the call gate
// defines for open(), close(), dup(), etc. The gaps being addressed are missing
// defines (for example, __NR_open, __NR_close, or __NR_dup).
//

// uclibc-syscall is complete but very *unintelligent*. It does not attempt to sort
// out items such as:
//   #define __NR_open __NR3264_open
// The program simply rejects the define when based on another define. Fortunately,
// the strategy been successful so far.
//

// Change UNISTD_IN and SYSNUM_OUT to suite your taste (defined below).
//

// Debug builds:
//   g++ -O0 -g3 -ggdb -DDEBUG=1 -Wall -Wextra -Wconversion uclibc-syscall.cpp -o uclibc-syscall.exe
// Release builds:
//   g++ -O2 -g -DNDEBUG=1 -Wall -Wextra -Wconversion uclibc-syscall.cpp -o uclibc-syscall.exe
//

#include <iostream>
using std::cout;
using std::cerr;
using std::endl;

#include <string>
using std::string;

#include <vector>
using std::vector;

#include <fstream>
using std::ifstream;
using std::ofstream;

#include <sstream>
using std::istringstream;

#include <algorithm>
using std::stable_sort;

#include <stdexcept>
using std::exception;
using std::runtime_error;

#include <cstdlib>
#include <cstring>
#include <cassert>

#define UNUSED(x) ((void)(x))

//////////////////////////////////////////////////////////

struct Sysnum
{
	Sysnum(const string& syscall, const string& number)
	  : m_syscall(syscall), m_number(number) {}

	string m_syscall;
	string m_number;
};

bool operator<(const Sysnum &lhs, const Sysnum &rhs)
{
	assert(!lhs.m_syscall.empty());
	assert(!rhs.m_syscall.empty());

	if(lhs.m_syscall.empty())
		throw runtime_error("operator<: lhs is not valid");

	if(rhs.m_syscall.empty())
		throw runtime_error("operator<: rhs is not valid");

	if(lhs.m_syscall == rhs.m_syscall)
	{
		// Don't throw - treat a missing value as 0 (WTF???)
		assert(!lhs.m_number.empty());
		assert(!rhs.m_number.empty());

		istringstream lss(lhs.m_number), rss(rhs.m_number);
		int l=0, r=0;

		if(!lhs.m_number.empty())
			lss >> l;

		if(!rhs.m_number.empty())
			rss >> r;
		
		return l < r;
	}
	
    	return strcmp(lhs.m_syscall.c_str(), rhs.m_syscall.c_str()) < 0;
}

//////////////////////////////////////////////////////////

typedef vector<Sysnum> SysnumVector;

void ProcessFiles(const string& infile, const string& outfile);

void SortSysnums(SysnumVector& sysnums);

void WritePrologue(ofstream& outfile);
void WriteSyscalls(ofstream& outfile, SysnumVector& sysnums);
void WriteEpilogue(ofstream& outfile);

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

// Ubuntu: sudo apt-get install kernel-headers-$(uname -r)
string UNISTD_IN = "/usr/src/linux-headers-3.2.0-38/include/asm/unistd.h";
// Fedora: su-; yum install hernel-headers
// string UNISTD_IN = "/usr/include/linux/asm/unistd.h";

// string SYSNUM_OUT = "<uClibc>/include/bits/sysnum.h";
string SYSNUM_OUT = "./sysnum.h";

//////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////

int main(int argc, char* argv[])
{
	UNUSED(argc), UNUSED(argv);

	try
	{
		ProcessFiles(UNISTD_IN, SYSNUM_OUT);
	}
	catch(const exception& e)
	{
		cerr << e.what() << endl;
		exit(EXIT_FAILURE);
	}

	return EXIT_SUCCESS;
}

void ProcessFiles(const string& infile, const string& outfile)
{
	assert(!infile.empty());
	if(infile.empty())
		throw runtime_error("filename (in) is not valid");

	assert(!outfile.empty());
	if(outfile.empty())
		throw runtime_error("filename (out) is not valid");

	ifstream instrm(infile.c_str());
	assert(instrm.good());
	if(!instrm.good())
		throw runtime_error("Failed to open " + infile);

	ofstream outstrm(outfile.c_str(), ofstream::out | ofstream::trunc);
	assert(outstrm.good());
	if(!outstrm.good())
		throw runtime_error("Failed to open " + outfile);

	SysnumVector sysnums;

	do
	{
		string line;
		getline(instrm, line);

		assert(!instrm.bad());
		if(instrm.bad())
			throw runtime_error("Failed to process " + infile);

		// First smoke test. Throw out anything other than a define.
		string::size_type pos = line.find("#define");
		if(pos == string::npos) continue;
		line = line.substr(pos+8, -1);

		// Throw out anything anything not a syscall value.
		pos = line.find("__NR_");
		if(pos == string::npos) continue;

		// Tokenize on whitespace.
		pos = line.find_first_of(" \t\n\v\f\r", 0);
		if(pos == string::npos) continue;

		// This is __NR_*. For example, __NR_open.
		string syscall(line.substr(0, pos));

		// Attempt to locate its define value
		pos = line.find_first_not_of(" \t\n\v\f\r", pos);
		if(pos == string::npos) continue;

		// This is what its defined to. It could be a
		// number, or it could be another define.
		string value(line.substr(pos, -1));

		// This tests if its a number. x86_64 has a number
		// of defines based on other defines for x86/x64.
		// For example #define __NR_open __NR3264_open.
		istringstream iss(value); int t=0;
		iss >> t;

		if(iss.fail()) continue;

		// Paydirt. At this point we have a good tuple.
		// For example, {__NR_open,40}.
		Sysnum sysnum(syscall, value);
		sysnums.push_back(sysnum);

	} while(!instrm.eof());

	SortSysnums(sysnums);

	WritePrologue(outstrm);

	WriteSyscalls(outstrm, sysnums);

	WriteEpilogue(outstrm);
}

void SortSysnums(SysnumVector& sysnums)
{
	assert(!sysnums.empty());
	if(sysnums.empty())
		throw runtime_error("No system call numbers");

	stable_sort(sysnums.begin(), sysnums.end());
}

void WritePrologue(ofstream& outfile)
{
	outfile << "/* WARNING!!! AUTO-GENERATED FILE!!! DO NOT EDIT!!! */" << endl;
	outfile << endl;
	outfile << "#ifndef _BITS_SYSNUM_H" << endl;
	outfile << "#define _BITS_SYSNUM_H" << endl;
	outfile << endl;
	outfile << "#ifndef _SYSCALL_H" << endl;
	outfile << "# error \"Never use <bits/sysnum.h> directly; include <sys/syscall.h> instead.\"" << endl;
	outfile << "#endif" << endl;
}

void WriteSyscalls(ofstream& outfile, SysnumVector& sysnums)
{
	for(size_t i=0; i < sysnums.size(); i++)
	{
		const Sysnum& sysnum = sysnums[i];
		assert(!sysnum.m_syscall.empty());
		assert(!sysnum.m_number.empty());

		outfile << endl;

		outfile << "#undef " << sysnum.m_syscall << endl;
		outfile << "#define " << sysnum.m_syscall << " ";
		outfile << sysnum.m_number << endl;

		string syscall(sysnum.m_syscall);
		syscall.replace(0, 4, "SYS");

		outfile << "#define " << syscall << " ";
		outfile << sysnum.m_syscall << endl;		
	}
}

void WriteEpilogue(ofstream& outfile)
{
	outfile << endl;
	outfile << "#endif" << endl;
	outfile << endl;
}

_______________________________________________
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc

Reply via email to