Ada library initialisation/elaboration and GPR directives : can't find elaboration symbol - libraries

I am trying to produce an Ada library for iOS.
However, it is necessary to perform the Ada elaboration manually.
I know that the compiler can produce an init symbol, that can be later imported and used. However, with the following GPR definition, it is not produced (the nm command does not list it). The naming is supposed to be <libname>init with <libname> corresponding to the value defined in the GPR directive Library_Name
The GPR is defined in the following fashion (this one is windows/style -see DLL references-, but the problems also applies when producing for iOS on a Mac):
project adalib is
for Languages use ("Ada");
for Source_Dirs use (project'Project_Dir & "./src");
for Library_Kind use "static"; --"static" on iOS will produce a .a file
for Library_Name use project'Name; -- will produce "libadalib.a"
for Library_Dir use project'Project_Dir & "./lib";
for Library_Src_Dir use project'Project_Dir & "./includes";
-- define your favorite compiler, builder, binder, linker options
end adalib;
I'm missing it : how to produce that symbol ?

I found the solution.
My GPR was missing this simple directive:
for Library_Interface use ("mypackage"); -- put whatever packages you want to expose, without .adb/.ads since we're talking about packages
With the above directive, I can find the adalibinit symbol via nm command.
When I import it in my ada code, I can also use it, see :
package body mypackage is
procedure Init_My_Lib
is
-- I want to call elaboration;
pragma import (C, ada_elaboration, "adalibinit");
begin
ada_elaboration;
-- further code
end Init_My_Lib;
-- rest of package
So, the full GPR should be:
project adalib is
for Languages use ("Ada");
for Source_Dirs use (project'Project_Dir & "./src");
for Library_Kind use "static"; -- will produce a .a file
for Library_Name use project'Name; -- will produce "libadalib.a"
for Library_Interface use ("mypackage"); -- <=== THIS IS HERE
for Library_Dir use project'Project_Dir & "./lib";
for Library_Src_Dir use project'Project_Dir & "./includes";
-- define your favorite compiler, builder, binder, linker options
end adalib;

Related

Parse C source code and extract variables and methods from it

I want to parse c source code for extracting variables and functions from it. Is there any library available for this purpose?
I tried to achieve it through query language available in tree-sitter parser generator but when I run the program it says undefined reference to the query functions used in the file even I have included the header file (api.h) containing query functions. I tried to resolve these errors but couldn't so looking for some other library which can serve the purpose.
It's better to do it with Clang (e.g. cindex Python API).
For example, to find all function definitions in a translation unit:
#!/usr/bin/env python3
import sys
import clang.cindex
def find_functions(node):
if node.kind == clang.cindex.CursorKind.FUNCTION_DECL:
nm = node.spelling
print(f"Function {nm}")
else:
for c in node.get_children():
find_functions(c)
index = clang.cindex.Index.create()
tu = index.parse(sys.argv[1])
print('Translation unit:', tu.spelling)
find_functions(tu.cursor)

How to integrate C/C++ analysis tooling in Bazel?

I have a code analysis tool that I'd like to run for each cc_library (and cc_binary, silently implied for rest of the question). The tool has a CLI interfaces taking:
A tool project file
Compiler specifics, such as type sizes, built-ins, macros etc.
Files to analyze
File path, includes, defines
Rules to (not) apply
Files to add to the project
Options for synchronizing files with build data
JSON compilation database
Parse build log
Analyze and generate analysis report
I've been looking at how to integrate this in Bazel so that the files to analyze AND the associated includes and defines are updated automatically, and that any analysis result is properly cached. Generating JSON compilation database (using third party lib) or parsing build log both requires separate runs and updating the source tree. For this question I consider that a workaround I'm trying to remove.
What I've tried so far is using aspects, adding an analysis aspect to any library. The general idea is having a base project file holding library invariant configuration, appended with the cc_library files to analysis, and finally an analysis is triggered generating the report. But I'm having trouble to execute, and I'm not sure it's even possible.
This is my aspect implementation so far, trying to iterate through cc_library attributes and target compilation context:
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if f.path.endswith(".c"):
print("file: ")
print(f.path)
print("includes: ")
print(target[CcInfo].compilation_context.includes)
print("quote_includes: ")
print(target[CcInfo].compilation_context.quote_includes)
print("system_includes: ")
print(target[CcInfo].compilation_context.system_includes)
print("define: " + define)
print(ctx.rule.attr.defines)
print("local_defines: ")
print(ctx.rule.attr.local_defines)
print("") # empty line to separate file prints
return []
What I cannot figure out is how to get ALL includes and defines used when compiling the library:
From libraries depended upon, recursively
copts, defines, includes
From the toolchain
features, cxx_builtin_include_directories
Questions:
How do I get the missing flags, continuing on presented technique?
Can I somehow retrieve the compile action command string?
Appended to analysis project using the build log API
Some other solution entirely?
Perhaps there is something one can do with cc_toolchain instead of aspects...
Aspects are the right tool to do that. The information you're looking for is contained in the providers, fragments, and toolchains of the cc_* rules the aspect has access to. Specifically, CcInfo has the target-specific pieces, the cpp fragment has the pieces configured from the command-line flag, and CcToolchainInfo has the parts from the toolchain.
CcInfo in target tells you if the current target has that provider, and target[CcInfo] accesses it.
The rules_cc my_c_compile example is where I usually look for pulling out a complete compiler command based on a CcInfo. Something like this should work from the aspect:
load("#rules_cc//cc:action_names.bzl", "C_COMPILE_ACTION_NAME")
load("#rules_cc//cc:toolchain_utils.bzl", "find_cpp_toolchain")
[in the impl]:
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
c_compiler_path = cc_common.get_tool_for_action(
feature_configuration = feature_configuration,
action_name = C_COMPILE_ACTION_NAME,
)
[in the loop]
c_compile_variables = cc_common.create_compile_variables(
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
user_compile_flags = ctx.fragments.cpp.copts + ctx.fragments.cpp.conlyopts,
source_file = src.path,
)
command_line = cc_common.get_memory_inefficient_command_line(
feature_configuration = feature_configuration,
action_name = C_COMPILE_ACTION_NAME,
variables = c_compile_variables,
)
env = cc_common.get_environment_variables(
feature_configuration = feature_configuration,
action_name = C_COMPILE_ACTION_NAME,
variables = c_compile_variables,
)
That example only handles C files (not C++), you'll have to change the action names and which parts of the fragment it uses appropriately.
You have to add toolchains = ["#bazel_tools//tools/cpp:toolchain_type"] and fragments = ["cpp"] to the aspect invocation to use those. Also see the note in find_cc_toolchain.bzl about the _cc_toolchain attr if you're using legacy toolchain resolution.
The information coming from the rules and the toolchain is already structured. Depending on what your analysis tool wants, it might make more sense to extract it directly instead of generating a full command line. Most of the provider, fragment, and toolchain is well-documented if you want to look at those directly.
You might pass required_providers = [CcInfo] to aspect to limit propagation to rules which include it, depending on how you want to manage propagation of your aspect.
The Integrating with C++ Rules documentation page also has some more info.

Import Pcaml grammar to extend OCaml's printer using camlp5

I want to create a printer extension for OCaml using camlp5. My code would look like the example of this tutorial but instead of creating my own extension of the grammar, I would like to use OCaml's grammar to parse a program.
For that, I would like to use the Pcaml module to parse the given string with OCaml's grammar. Unfortunately, each time I try to use it, I get the:
Required module 'Pcaml' is unavailable
This is the part of my code where I load and open modules, as well as part of the code that uses Pcaml:
#load "pa_extprint.cmo";;
#load "q_MLast.cmo";;
#load "pa_o.cmo";;
open Pcaml;;
open Pprintf;;
let pa_ocaml = Grammar.Entry.create Pcaml.gram "pcaml_gram";;
I tried multiple command to run the program, like for example:
ocamlc -pp camlp5o -I +camlp5 gramlib.cma <my_file>.ml
What do I need to be able to use Pcaml and Pcaml.gram?
I recommend to use ocamlfind to build and link your programs. The only reason for newcomer against it, is that thing could become buggy when you use Windows without WSL. The compilation command without error is below
ocamlfind c -syntax camlp5o -package camlp5 -linkpkg a.ml
#load "pa_extprint.cmo";;
#load "q_MLast.cmo";;
#load "pa_o.cmo";;
open Pcaml;;
open Pprintf;;
let pa_ocaml : int Grammar.Entry.e = Grammar.Entry.create Pcaml.gram "pcaml_gram";;
FYI, your #load commands can and should be replaced by specifying right ocamlfind's packages.

Calling Library in Lua

I have created a Wireshark dissector in Lua for an application over TCP. I am attempting to use zlib compression and base64 decryption. How do I actually create or call an existing c library in Lua?
The documentation I have seen just says that you can get the libraries and use either the require() call or the luaopen_ call, but not how to actually make the program find and recognize the actual library. All of this is being done in Windows.
You can't load any existing C library, which was not created for Lua, with plain Lua. It's not trivial at least.
*.so/*.dll must follow some specific standard, which is bluntly mentioned in programming in Lua#26.2 and lua-users wiki, code sample. Also similar question answered here.
There are two ways You could solve Your problem:
Writing Your own Lua zlib library wrapper, following those standards.
Taking some already finished solution:
zlib#luapower
lua-zlib
ffi
Bigger list #lua-users wiki
The same applies to base64 encoding/decoding. Only difference, there are already plain-Lua libraries for that. Code samples and couple of links #lua-users wiki.
NOTE: Lua module package managers like LuaRocks or
LuaDist MIGHT save You plenty of time.
Also, simply loading a Lua module usually consists of one line:
local zlib = require("zlib")
The module would be searched in places defined in Your Lua interpreter's luaconf.h file.
For 5.1 it's:
#if defined(_WIN32)
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
*/
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT \
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \
LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else
How do I actually create or call an existing c library in Lua?
An arbitrary library, not written for use by Lua? You generally can't.
A Lua consumable "module" must be linked against the Lua API -- the same version as the host interpreter, such as Lua5.1.dll in the root of the Wireshark directory -- and expose a C-callable function matching the lua_CFunction signature. Lua can load the library and call that function, and it's up to that function to actually expose functionality to Lua using the Lua API.
Your zlib and/or base64 libraries know nothing about Lua. If you had a Lua interpreter with a built-in FFI, or you found a FFI Lua module you could load, you could probably get this to work, but it's really more trouble than it's worth. Writing a Lua module is actually super easy, and you can tailor the interface to be more idiomatic for Lua.
I don't have zlib or a base64 C library handy, so for example's sake lets say we wanted to let our Lua script use the MessageBox function from the user32.dll library in Windows.
#include <windows.h>
#include "lauxlib.h"
static int luaMessageBox (lua_State* L) {
const char* message = luaL_checkstring(L,1);
MessageBox(NULL, message, "", MB_OK);
return 0;
}
int __declspec(dllexport) __cdecl luaopen_messagebox (lua_State* L) {
lua_register(L, "msgbox", luaMessageBox);
return 0;
}
To build this, we need to link against user32.dll (contains MessageBox) and lua5.1.dll (contains the Lua API). You can get Lua5.1.lib from the Wireshark source. Here's using Microsoft's compiler to produce messagebox.dll:
cl /LD /Ilua-5.1.4/src messagebox.c user32.lib lua5.1.lib
Now your Lua scripts can write:
require "messagebox"
msgbox("Hello, World!")
Your only option is to use a library library like alien. See my answer Disabling Desktop Composition using Lua Scripting for other FFI libraries.

Export C functions for LabView with c++builder

I have a DLL that I have ported from VC2008 to C++ Builder XE2. The DLL is used in LabVIEW's TestStand.
TestStand, when importing the VC2008 DLL, can see the function names and their arguments. When using the C++ Builder DLL, all its sees are the function names and not the arguments. All exports are C functions and use extern "C" declspec( dllexport ).
Is there a way to get the exports correct?
I have read that adding a TLB file will do the job, if this is true, how do I create a TLB that exports only C functions?
TestStand can read a .c/.cpp file and derive parameters from that file. You still load the DLL and select the function you want to call. You then 'verify' the parameters and select the .c/.cpp file in the dialog. TestStand will find the function with the same name and insert the parameters itself.
The function must be very specific, I had to create a dummy .c file that contained the prototypes as TestStand could not handle the #defines for dllexport and dllimport. It likes a very specific format. For the function:
TESTAPI bool StartTest( long inNumber ) {}
where TESTAPIis either extern "C" __declspec( dllexport ) or extern "C" __declspec( dllimport I had to write the line below in my dummy file:
bool __declspec( dllexport ) StartTest( long inNumber ) {}
That does it.
DLL function parameters cannot be determined from exports alone, unless they are being decorated by the calling convention (which is unusual to do in a DLL). If a TLB (aka a Type Library) solves the problem, then the VC2008 DLL is likely an In-Process ActiveX/COM object rather than a flat C DLL. If so, then in C+Builder you can use the IDE wizards on the "File | New" menu to create an "ActiveX Library" project, then a "COM Object" to add to the library. Then you will have a TLB that you can define your object with, and the IDE will generate stub code that you can fill in with your object's implementation.
If that is not what LabViews is expecting, then I suggest you contact them and ask. If all it needs is a TLB with flat C functions (which is very unusual, because TLB's are object-oriented), then you can omit the "COM Object" portion and just create an "ActiveX Library" project to get a bare-bones TLB, then add your definitions to it as needed, an then add your exports to the project.
From the reference here:
Avoid using the extern "C" syntax to export symbols. The extern "C" syntax prevents the C/C++ DLL Adapter from obtaining type information for function and method parameters."
A little late to the game, but your problem may be that C++ Builder is decorating the exported function with a leading underscore. The TLIB command line utility should help prove this (assuming tlib still ships with C++Builder)
TLIB mydll.lib, mydll.lst
Look at the resulting lst file and see if it contains StartTest or _StartTest. LabView is probably expecting to find a function without the underscore.
You can add a DEF file to your C++Builder project that will suppress the leading underscore. Try this:
Use the __cdecl calling convention instead of __stdcall.
Export plain "C" functions. No C++ classes or member functions.
Make sure you have an extern "C" {} around your function prototypes.
Create a DEF file that aliases the exported functions to a Microsoft
compatible name. Alias the names so they don't contain a leading
underscore. The DEF file will look like this:
EXPORTS
; MSVC name = C++Builder name
StartTest = _StartTest
Foo = _Foo
Bar = _Bar
5- Add the DEF file to your BCB DLL project and rebuild it.
Check out these ancient articles for more details:
http://bcbjournal.org/articles/vol4/0012/Using_Visual_C_DLLs_with_CBuilder.htm
The reverse article (creating C++Builder DLLs that get called from VC++ created applications) is buried in this archive:
http://www.frasersoft.net/program/bcbdev.zip : /articles/bcbdll.htm. It describes the DEF file trick in more detail, plus some other options.
Note that my answer is based on the way thing were in 1998 or so. They may have changed since then. If they have, then the C++Builder command line tools impdef, tlib, tdump, plus the Microsoft equivalents of those tools, should be able to show you exactly what is in your DLL vs the MSVC one.
H^2
I suggest to use ActiveX object: you can create an automation object in C++Builder and in Labview / TestStand you can import this object. If you use automation, in Lavbiew you will have the correct parameter definition. Make sure you are using a set of compatible type variables with Labview / TestStand.
For example, this fragment of code is the implementation of an array passed from Labview to C++:
STDMETHODIMP TCanLibraryImpl::DataDownload(VARIANT Data, long* RV)
{
_precondition_cmodule();
*RV = 0;
TSafeArrayLong1 mySafeArray(Data.parray);
int dLen =mySafeArray.BoundsLength[0];
...
}
In Labview you will pass to this function an array of I64

Resources