'Passing String from C to Ada via C Interpreter (VxWorks)

I'm trying to pass a string from C to Ada by using the C interpreter in a telnet window to a VxWorks box.

Interface.h

#pragma once

#ifdef _cplusplus
extern "C"
{
#endif

extern void Ada_SetNewAddress(char*);

extern "C" void SetNewAddrBroker(char* ipAddress);

#ifdef __cplusplus
}
#endif

Interface.cpp

#include "Interface.h"
#include <stdio>

extern "C" void SetNewAddrBroker(char* ipAddress)
{
    printf("We passed the value -> %s", ipAddress);
    Ada_SetNewAddress(ipAddress);
    printf("Ada was called!\n");
}

Streamer.ads

with Interfaces.C;
with Interfaces.C.Strings;

package Streamer is
    procedure Initialize;
    procedure SetNewAddress(str : Interfaces.C.Strings.chars_ptr);
    pragma Export (C, SetNewAddress, "Ada_SetNewAddress");
end Streamer;

Streamer.adb

package body Streamer is
    Socket : Socket_Type;
    DefaultAddr : String := "127.0.0.1";
    Address : Sock_Addr_Type := (Family_Inet, Inet_Addr(DefaultAddr), 1024);
    Buffer : Stream_Access;

    procedure Initialize is
    begin
        Create_Socket(Socket, Family_Inet, Socket_Datagram);
        Buffer := Stream(Socket, Address);
    end;

    procedure SetNewAddress(str : Interfaces.C.Strings.chars_ptr)
        cstar : String := Interfaces.C.Strings.Value(str);
    begin
        Address := (Family_Inet, Inet_Addr(cstar), 1024);
        Buffer := Stream(socket, Address);
    end;
end Streamer;

When I call the C function SetNewAddrBroker("192.168.1.1") I get a 'data access' error, this is via telnet to the VxWorks machine that this code exists on, the Ada program is the main task, so I know it's not the missing "adainit() and adafinal()" calls. I can't figure out why it's throwing a random data access error. I can use putty or teraterm for the telnet client if that matters, both throw the same error.

THE ERROR OUTPUT

    We passed -> 192.168.1.1
    data access
    Exception current instruction address: 0x002e3ab0
    ............
    trcStack aborted: error in top frame
    Shell task 'tShellRem1' restarted...

Examining, the instruction that threw the error

    0x2e3ab0 stw r30,8(r9)

I do not know assembly but I imagine this is trying to store the string in a place that is too small?

I need to set the IP of the broker for the client at runtime, the Ada is the client, and the broker is just on my LAN. I want to be able to telnet to the Ada client and just update the ip address, but the only interface exposed to me is the C interpreter for the VxWorks box, so I'm stuck with interfacing with this.

VxWorks Version 6.3



Solution 1:[1]

Here is a suggestion on changing the code in https://codeshare.io/DZBnMO such that it still calls Ada from C (instead of signalling an Ada task), but gets the string from the C structure without using Secondary Stack, because all string variables now have a statically known length:

In package Streamer, change the declaration of GetNewAddress to be:

subtype String_15 is String (1 .. 15);
type String_15_Ptr is access all String_15;

function GetNewAddress return String_15_Ptr;
pragma Import ( .. as before .. );

In package body Streamer, change SetNewAddress as follows:

procedure SetNewAddress
is -- (this "is" was missing from codeshare, btw)
   csize : constant Natural := Natural (GetNewAddressLen);
   cstrptr : constant String_15_Ptr := GetNewAddress;
   adaString : String_15;
begin
   for I in 1 .. csize loop
      adaString(I) := cstrptr(I);
   end loop;
   Address := (Family_Inet, Inet_Addr(adaString(1 .. csize)), 1024);
   Buffer := Stream (Socket, Address);
end;

Note that I haven't compiled this code, but I hope it shows the method to avoid Secondary Stack: every global and local variable must have a static size, and so must all function return-value types, but it is possible to pass slices of dynamic size as parameters (because GNAT uses pass-by-reference there).

The suggested Ada code is maybe not fully portable, because it assumes a certain correspondence between C types like "char *" and Ada types like "String_15_Ptr", but on normal architectures I expect it to work.

By the way, your C code has a potential buffer-overflow bug: SetNewAddrBroker should use strncpy(), not strcpy().

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Niklas Holsti