This page was produced by an automated import process, and may have formatting errors; feel free to fix.

Clean Design and Portable Implementation

In addition to getting the syntax right, there’s the little question of semantics. Some things are done in certain ways in GDB because long experience has shown that the more obvious ways caused various kinds of trouble.

You can’t assume the byte order of anything that comes from a target (including values, object files, and instructions). Such things must be byte-swapped using SWAP_TARGET_AND_HOST in GDB, or one of the swap routines defined in bfd.h, such as bfd_get_32.

You can’t assume that you know what interface is being used to talk to the target system. All references to the target must go through the current target_ops vector.

You can’t assume that the host and target machines are the same machine (except in the “native” support modules). In particular, you can’t assume that the target machine’s header files will be available on the host machine. Target code must bring along its own header files – written from scratch or explicitly donated by their owner, to avoid copyright problems.

Insertion of new #ifdef’s will be frowned upon. It’s much better to write the code portably than to conditionalize it for various systems.

New #ifdef’s which test for specific compilers or manufacturers or operating systems are unacceptable. All #ifdef’s should test for features. The information about which configurations contain which features should be segregated into the configuration files. Experience has proven far too often that a feature unique to one particular system often creeps into other systems; and that a conditional based on some predefined macro for your current system will become worthless over time, as new versions of your system come out that behave differently with regard to this feature.

Adding code that handles specific architectures, operating systems, target interfaces, or hosts, is not acceptable in generic code.

One particularly notorious area where system dependencies tend to creep in is handling of file names. The mainline GDB code assumes Posix semantics of file names: absolute file names begin with a forward slash /, slashes are used to separate leading directories, case-sensitive file names. These assumptions are not necessarily true on non-Posix systems such as MS-Windows. To avoid system-dependent code where you need to take apart or construct a file name, use the following portable macros:

This preprocessing symbol is defined to a non-zero value on hosts whose filesystems belong to the MS-DOS/MS-Windows family. Use this symbol to write conditional code which should only be compiled for such hosts.

Evaluates to a non-zero value if c is a directory separator character. On Unix and GNU/Linux systems, only a slash / is such a character, but on Windows, both / and \ will pass.

Evaluates to a non-zero value if file is an absolute file name. For Unix and GNU/Linux hosts, a name which begins with a slash / is absolute. On DOS and Windows, d:/foo and x:\bar are also absolute file names.

Calls a function which compares file names f1 and f2 as appropriate for the underlying host filesystem. For Posix systems, this simply calls strcmp; on case-insensitive filesystems it will call strcasecmp instead.

Evaluates to a character which separates directories in PATH-style lists, typically held in environment variables. This character is ‘:’ on Unix, ‘;’ on DOS and Windows.

This evaluates to a constant string you should use to produce an absolute filename from leading directories and the file’s basename. SLASH_STRING is "/" on most systems, but might be "\\" for some Windows-based ports.

In addition to using these macros, be sure to use portable library functions whenever possible. For example, to extract a directory or a basename part from a file name, use the dirname and basename library functions (available in libiberty for platforms which don’t provide them), instead of searching for a slash with strrchr.

Another way to generalize GDB along a particular interface is with an attribute struct. For example, GDB has been generalized to handle multiple kinds of remote interfaces—not by #ifdefs everywhere, but by defining the target_ops structure and having a current target (as well as a stack of targets below it, for memory references). Whenever something needs to be done that depends on which remote interface we are using, a flag in the current target_ops structure is tested (e.g., target_has_stack), or a function is called through a pointer in the current target_ops structure. In this way, when a new remote interface is added, only one module needs to be touched—the one that actually implements the new remote interface. Other examples of attribute-structs are BFD access to multiple kinds of object file formats, or GDB’s access to multiple source languages.

Please avoid duplicating code. For example, in GDB 3.x all the code interfacing between ptrace and the rest of GDB was duplicated in *-dep.c, and so changing something was very painful. In GDB 4.x, these have all been consolidated into infptrace.c. infptrace.c can deal with variations between systems the same way any system-independent file would (hooks, #if defined, etc.), and machines which are radically different don’t need to use infptrace.c at all.

All debugging code must be controllable using the ‘set debug ''module''’ command. Do not use printf to print trace messages. Use fprintf_unfiltered(gdb_stdlog, .... Do not use #ifdef DEBUG.

None: Internals Clean-Design-and-Portable-Implementation (last edited 2013-08-20 23:40:27 by StanShebs)

All content (C) 2008 Free Software Foundation. For terms of use, redistribution, and modification, please see the WikiLicense page.