box64/wrapperhelper
2024-09-30 17:41:47 +02:00
..
include-override [WRAPPERHELPER] Added box32 and line number support in the wrapperhelper (#1890) 2024-09-30 12:31:45 +02:00
src [WRAPPERHELPER] Added box32 and line number support in the wrapperhelper (#1890) 2024-09-30 12:31:45 +02:00
example-libc.h [WRAPPERHELPER] Added predefined mark-simple and conversions (#1845) 2024-09-20 14:32:26 +02:00
Makefile [BOX32][WRAPPER] Added 32bits wrapping for SDL2_image 2024-09-30 17:41:47 +02:00
README.md [WRAPPERHELPER] Added box32 and line number support in the wrapperhelper (#1890) 2024-09-30 12:31:45 +02:00

Wrapper helper

This folder is semi-independent from the parent project (box64). This sub-project aims to (partially) automating the generation of the private headers in src/wrapped. This is, however, still a work-in-progress and in alpha.

As such, this sub-project is mainly aimed at people who know how to read code and are familiar with the wrapped libraries part of box64.

Licensing

This program is under the MIT license. However, some system header files under the LGPL license (copied from a GNU libc Arch Linux installation) have been adapted into the include-fixed folder; these files are not copied into the output and simply serve as data. As such, I believe this falls under fair use, and does not lead to the output of this program (used in the parent box64 project) being under the (L)GPL license.

Compiling

You need a C compiler and GNU Make. No library is required.

Go to this folder, then run the make command. This will produce a binary called bin/wrapperhelper.

This project has been compiled and tested with GCC 14.2.1 20240805 on an x86_64 machine, with no warning emitted.

You may also use the make clean and make distclean commands to remove output files (clean) and directories (distclean).

Usage

To use the wrapper helper, run the following command in the folder containing this README.md:

bin/wrapperhelper -I/path/to/system/include "path_to_support_file" "path_to_private.h" "path_to_private.h"

You may add as many -I options as needed. The folders include-override/<arch> and include-override/common are always prioritized, as if they appeared first in the command line.

You may also use the -32 and -64 switches to generate box32 or box64 files respectively. Alterately, you can use the --emu arch and --target arch options to select more precisely the emlated and executing platforms, though only x86, x86_64 and aarch64 are supported for now. By default, everything is as if -64 was supplied.

The first file is a C file containing every declaration required. The second file is the "requests" input. The third file is the output file, which may be a different file.

The support file may contain pragma declarations of the form

#pragma wrappers type_letters c TYPE
#pragma wrappers type_letters_strict c TYPE

where TYPE is a type-name. The second form marks the exact type TYPE as being a complex type though with a conversion as c (which may be multiple characters), while the first marks the type TYPE, regardless of type qualifers (_Atomic, const, restrict, volatile). Meaning:

  • if a parameter has type TYPE, the character output will be c;
  • if a parameter has a pointer to TYPE, or a structure containing TYPE, the output will be a GOM function.

Declarations of the form

#pragma wrappers mark_simple TAG

will mark the structure or union with tag TAG, or the structure or union aliased to TAG by a typedef if no such structure exist, as simple. This means that a pointer to such a structure will have a character output of p. This is not the same as making the pointer to the structure a complex type with conversion as p as e.g. pointers to pointers will behave differently.

System headers included (directly or indirectly) by the support file are overriden by the files in include-fixed.

The first three lines of the input are ignored.

A "request" is a structure containing an object name and, eventually, a default value (GO, GO2 with type vFiV to function xxx, DATA...) and/or a "solved" value (which is similar, but deduced from the support file).

Valid requests (in the reference file) are:

{GO/GOM/GOW/GOWM} ( name , type )
{GOD/GO2/GOWD/GOW2} ( name , type , name )
// {GO/GOM/GOW/GOWM} ( name ,
// {GO/GOM/GOW/GOWM} ( name , type )
// {GOD/GO2/GOWD/GOW2} ( name ,
// {GOD/GO2/GOWD/GOW2} ( name , type , name )
DATA[V/B/M] ( name , int )
// DATA[V/B/M] ( name ,
// DATA[V/B/M] ( name , int )

(where {A/B} means A or B and [A/B] means A, B or nothing). All other comments are ignored.

If you want to explore the output of the different stages of the helper, you can use the following forms:

bin/wrapperhelper --prepare "path_to_support_file" # (1)
bin/wrapperhelper --preproc "path_to_support_file" # (2)
bin/wrapperhelper --proc "path_to_support_file"    # (3)
bin/wrapperhelper "path_to_support_file"           # (3) as well
  1. This form outputs the list of preprocessor tokens (the "post-prepare" phase).
  2. This form outputs the list of processor tokens (the "post-preprocessor" phase).
  3. This form outputs the list of constants, type definitions, structure definitions, and declarations (the "post-processor" phase).

Example

To remake the wrappedlibc_private.h file, use the following command:

bin/wrapperhelper example-libc.h ../src/wrapped/wrappedlibc_private.h ../src/wrapped/wrappedlibc_private.h

This will emit a few marnings and (non-fatal) errors, then write the result directly in wrappedlibc_private.h.

Maintaining

All of the source code is included in the src folder.

The main function is in main.c.

The first phase of compilation (steps 1-3 and a part of step 5 of the translation phases) is implemented in prepare.c.

The second phase of compilation (steps 4 and 6) is implemented in preproc.c.

The third phase of compilation (step 7) is implemented in parse.c, though no actual parsing of function definitions takes place.

The reading and writing of the _private.h files is implemented in generator.c.

Known issues

Only native structures are read. This means that the current version of wrapperhelper does not detect an issue when a structure has different members or alignments in two different architectures.

No checking of signatures under #ifdefs is made.

Phase 5 is partially implemented, but could be greatly improved.

The following features are missing from the generator:

  • Structures with at least two elements as a parameter
  • Large structure as a return type (more than 16 bytes on 64bits, or 8 bytes on 32bits)
  • Atomic types

The following features are missing from the preprocessor:

  • General token concatenation (though the concatenation of two PTOK_IDENT works without issue)
  • Stringify
  • Skipped unexpected token warnings

The following features are missing from the parser:

  • _Alignas(type-name) and _Alignas(constant-expression)
  • (type-name){initializer-list}
  • Old style function declarations
  • Function definitions are ignored, not parsed
  • Attributes are ignored everywhere (with a #define __attribute__(_))