I want to use an "initcall" system on an ARM CPU in C.
I define these macro :
typedef int (*payload_initcall_t)(payload_state_t *state);
extern payload_initcall_t __payload_initcall_start, __payload_initcall_end;
#define __payload_initcall(fn) \
static payload_initcall_t __payload_initcall_##fn __payload_init_call = fn
#define __payload_init_call __attribute__ ((unused,__section__ (".payload_init_ptrs")))
#define payload_init(x) __payload_initcall(x);
#define __init __attribute__ ((__section__ (".code_segment")))
An example of module :
static int __init inspec_init(payload_state_t *state) {
payload_log(LOG_INFO, "I AM THE PAYLOAD 1 !!");
return 0;
}
payload_init(inspec_init)
The function which calls the modules :
payload_initcall_t *call_p;
call_p = &__payload_initcall_start;
do {
payload_log(LOG_DEBUG, "payload init fct call: %p", (void *)call_p);
(*call_p)(state);
++call_p;
} while (call_p < &__payload_initcall_end);
In the linker file (just after .text section :
__payload_initcall_start = .;
payload_init_ptrs : { *(.payload_init_ptrs) }
__payload_initcall_end = .;
code_segment : { *(.code_segment) }
In my x86_64 computer it works.
I cross compile this code with a buildroot toolchain (GCC 7.4.0). The compilation terminates with no error but when I run the binary on the ARM target it segfault at the "(*call_p)(state)" line.
When I run objdump on the x86_64 binary I find "code_segment" and "payload_init_ptrs" but in the ARM binary I can't find these sections.
Is my GCC outdated ?
Thanks in advance.
Related
I use a USB Device wit a single Button and LED inside.
I tested software to change color of the device,
API for the hardware is only implemented in c:
https://github.com/katie-snow/Ultimarc-linux
To run it at golang, the c-code was embedded in go-code (above import "C")
The c-library "libultimarc" of the USB-Button was provided to the sw as static c lib: libultimarc.a
Also the lib for USB is static: libusb-1.0.a
Other libs are dynamic. (see ldd below)
I use Linux and x86_64 computers. Build-host is ubuntu, test Host is debian.
The program compiles and works nice, the color of the USB Device is changing as intended.
But only at the build system.
If I try to run the executable from the compile-host at other x86_64 linux machines (test-host)
it is doing nothing, just waiting without any response.
Golang code with embedded c-code goC-LedButton.go :
package main
/*
// Everything in comments above the import "C" is C code and will be compiled with GCC.
#cgo LDFLAGS: "/home/user/BUTTON/Ultimarc-linux/src/libs/.libs/libultimarc.a" "/home/user/BUTTON/libusb-1.0.26/libusb/.libs/libusb-1.0.a" "-ludev" "-ljson-c"
// path of json.h
#cgo CFLAGS: "-I/usr/local/include/json-c/"
#include "Ultimarc-linux/src/libs/ulboard.h"
#include "Ultimarc-linux/src/libs/common.h"
#include "Ultimarc-linux/src/libs/usbbutton.h"
#include "Ultimarc-linux/src/libs/ipacseries.h"
#include <json.h>
int changeColor(char *str) {
ulboard myboard;
myboard.type = ulboard_type_usbbutton;
myboard.version = ulboard_version_null;
struct json_object *parsed_json_color;
_Bool ret;
parsed_json_color = json_tokener_parse(str);
ret = updateBoardUSBButtonColor(parsed_json_color, &myboard);
return ret;
}
int toRed(void) {
_Bool ret;
char *str = "{ \"red\" : 255, \"green\" : 0, \"blue\" : 0 }";
ret = changeColor(str);
return ret;
}
int toGreen(void) {
_Bool ret;
char *str = "{ \"red\" : 0, \"green\" : 255, \"blue\" : 0 }";
ret = changeColor(str);
return ret;
}
int toBlue(void) {
_Bool ret;
char *str = "{ \"red\" : 0, \"green\" : 0, \"blue\" : 255 }";
ret = changeColor(str);
return ret;
}
*/
import "C"
import "fmt"
func main() {
c := C.toRed()
fmt.Println("Changing Color to Red ", c )
c = C.toGreen()
fmt.Println("Changing Color to Green ", c )
c = C.toBlue()
fmt.Println("Changing Color to Blue ", c )
}
Used libraries of binary:
ldd goC-LedButton
linux-vdso.so.1 (0x00007ffe9fafa000)
libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007ff9cbd49000)
libjson-c.so.3 => /lib/x86_64-linux-gnu/libjson-c.so.3 (0x00007ff9cbd3b000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007ff9cbd18000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff9cbb26000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff9cbd9c000)
The lib /lib/x86_64-linux-gnu/libjson-c.so.3 is on both machines installed.
The problem is that the sw is not running on other machines, and I don't understand why.
The behavior is, that at the test-host it is not responding at all after start, but can be stopped with [str]+[c].
I will be very thankful for hints about how to get the sw also running on the test-host.
It's most probable a lib problem?
All the best, Alex
I tried to write a simple D Program and use it to access a simple C library but there is unknown error.
My c Code, Box.c
#include "Box.h"
int SayHello( int _int)
{
_int ++;
return _int;
}
My c header file, Box.h
#ifndef BOX_H_INCLUDED
#define BOX_H_INCLUDED
/* export interfaces */
#ifdef __cplusplus
extern "C" {
#endif
int SayHello( int _int);
#ifdef __cplusplus
}
#endif
#endif // BOX_H_INCLUDED
I compile it
gcc -c Box.c Box.h
resulting files
Box.o
Box.h.gch
I place them to my D Program's project directory
My D Code
module main;
import std.stdio;
import std.conv;
import std.c.stdio;
import clib;
int main(string[] args)
{
// test external c library
auto s = to!string( SayHello(3) ) ;
writefln( "my int is "~ s );
readln();
return 0;
}
My D interface file ( clib ), trying to link to my C library
module clib;
import std.c.stdio;
extern (C) int SayHello( int _int);
The error I get when I compile it using codeblocks
Compiling: hello.d
Linking console executable: bin/Debug/tutorial03-access-c-library4
obj/Debug/hello.o: In function `_Dmain':
/home/hamilton/Tutorial/tutorial03-access-c-library4/hello.d:11: **undefined reference to `SayHello'**
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings
Error is "undefined reference to `SayHello'"
There is no error I get when I compile it using command in console
$ dmd Box.o hello.d clib.di
it will be very painful if I cannot use codeblocks as I need the debugging functionality.
Thanks
Update:
Compiler setting in codeblocks as followed
Linker for dynamic libs: gcc -m32 -lrt
Linker for static libs: ar
Debugger: gdb
You can change the build options in CodeBlocks from Project -> Build Options, Compiler settings -> Other options. The simplest thing to do would be to just add Box.o to Other options.
On Linux with GCC if I define
__attribute__((constructor)) static void myfunc(void) {}
, then the address of myfunc will be appended to __init_array_start in the .ctors section. But how can I append a function pointer to __preinit_array_start?
Is __preinit_array_start relevant in a statically linked binary?
As there's no __attribute__((preconstructor)), you can just mush the code into the relevant section using some section attributes e.g.
#include <stdio.h>
int v;
int w;
int x;
__attribute__((constructor)) static void
foo(void)
{
printf("Foo %d %d %d\n", v, w, x);
}
static void
bar(void)
{
v = 3;
}
static void
bar1(void)
{
w = 2;
}
static void
bar2(void)
{
x = 1;
}
__attribute__((section(".preinit_array"))) static void (*y[])(void) = { &bar, &bar1, &bar2 };
int
main(int argc, char **argv)
{
printf("Hello World\n");
}
File dumped into foo.c, compiled using: gcc -o foo foo.c, and then run yields an output of:
Foo 3 2 1
Hello World
File compiled using gcc -static -o foo foo.c, and then run yields the same output, so it does appear to work with statically linked binaries.
It will not work with .so files, though; the linker complains with:
/usr/bin/ld: /tmp/ccI0lMgd.o: .preinit_array section is not allowed in DSO
/usr/bin/ld: failed to set dynamic section sizes: Nonrepresentable section on output
I'd be inclined to avoid it, as code run in that section precedes all other initialization routines. If you're trying to perform some 'this is supposed to run first' initialization, then it's really not a good idea - you're just fighting a race condition which should be solved by some other mechanism.
I am working with the Stellaris Launchpad, GCC arm embedded and I am trying to use malloc. According to this it should not be a problem at all, but it is a problem at compile time: you need an implementation of _sbrk to make it run. So based on this thread I created the following _sbrk implementation:
#include "stdio.h"
extern int _HEAP_START;
extern int _HEAP_END;
extern void *_sbrk(int incr)
{
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL) {
heap = (unsigned char *)&_HEAP_START;
}
prev_heap = heap;
if ((heap + incr) >= (unsigned char *)&_HEAP_END) {
return 0;
}
heap += incr;
return (void *)prev_heap;
}
I also have the following linkerscript to define a stack, a heap and all the rest (also based on the same thread)
MEMORY
{
FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 256K
SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}
SECTIONS
{
.text :
{
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
_etext = .;
} > FLASH
.data : AT (ADDR(.text) + SIZEOF(.text))
{
_data = .;
*(vtable)
*(.data*)
_edata = .;
} > SRAM
.bss :
{
_bss = .;
*(.bss*)
*(COMMON)
_ebss = .;
. = ALIGN (8);
_end = .;
} > SRAM
}
/* end of allocated ram _end */
PROVIDE( _HEAP_START = _end );
/* end of the heap -> align 8 byte */
PROVIDE ( _HEAP_END = ALIGN(ORIGIN(SRAM) + LENGTH(SRAM) - 8 ,8) );
the last pieces of information: Compiler params:
-g -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp -ffunction-sections -fdata-sections -MD -std=c99 -Wall -pedantic -DPART_LM4F120H5QR -c -Iinc/ -Ilib/inc -I/usr/arm-none-eabi/include -DTARGET_IS_BLIZZARD_RA1
And the Linker params:
-Tlib/linker_script.ld --entry ResetISR --gc-sections -L lib/ -L /usr/arm-none-eabi/lib -o final.elf obj/main.c.o obj/_sbrk.c.o obj/startup_gcc.c.o obj/switch.S.o -ldriver-cm4f -lc -lm
The main file consists of one malloc call:
#include "stdio.h"
#include "stdlib.h"
int main(){
int *i = malloc(sizeof(int));
}
And it crashes on the malloc call. Breakpoints in the _sbrk do not get hit, so the program crashes before it even reaches _sbrk. What is going on? Why does malloc not, as promised, work on its own? And why does it crash before it reaches its core algorithm (_sbrk)?
Ah, I found the mistake: I used the wrong libraries. For this version of the stellaris launchpad and GCC arm embedded 4.8 you need to use the Thumb libraries, which (on my pc) has the following directories:
/usr/arm-none-eabi/lib/thumb (for libM and libC (and libC++ etc))
/usr/lib/gcc/arm-none-eabi/4.8.3/thumb/ (for libgcc)
I'm struggling to convert a C-program linked with ld, of the gnu tool-chain
to make it compile as a visual-studio (2005) project.
The program puts .data-symbols in different segments and during an
initialization phase it copies data between segments. Pointers to the
start and end of the segments are defined in the ld linker script.
I understand how to locate the variables into different, user-defined
segments, but i havent been able to figure out how to define linker constants
such as _start_of_my_segment or if there is something similar to a linker
script in Visual Studio.
My goal is to be able to compile the program with, prefferably
no modifications to the source-code that refers to the linker-defined
symbols, but with my own custom layout of the data in the Visual Studio
project.
Below is some example C-code that illustrates what i'd like
to do and a (stripped-down, possibly syntax-incorrect) version
of the make-script used when linking with gcc/ld.
Any hints would be greatly appreciated!
#pragma data_seg( "MY_DATA_FOO" )
#pragma data_seg( "MY_DATA_BAR" )
#pragma comment(linker, "/section:MY_DATA_BAR,R")
__declspec(allocate("MY_DATA_FOO")) int foo1;
__declspec(allocate("MY_DATA_FOO")) int foo2;
__declspec(allocate("MY_DATA_BAR")) int bar1 = 1;
__declspec(allocate("MY_DATA_BAR")) int bar2 = 2;
#pragma data_seg( )
void test() {
foo1 = bar1;
foo2 = bar2;
// i would rather do this as
//extern unsigned int __start_of_MY_DATA_FOO;
//extern unsigned int __start_of_MY_DATA_BAR;
//extern unsigned int __size_of_MY_DATA_BAR;
//memcpy(__start_of_MY_DATA_FOO, _start_of_MY_DATA_BAR, _size_of_MY_DATA_BAR);
}
Pseudo link-script (what would be the equivalent for Visual Studio
MEMORY
{
foo: org=0x1000, len=0x100
bar: org=0x2000, len=0x100
}
SECTIONS
{
GROUP:
{
MY_DATA_FOO : {}
__start_of_MY_DATA_FOO = ADDR(MY_DATA_FOO);
__end_of_MY_DATA_FOO = .;
__size_of_MY_DATA_FOO = SIZEOF(MY_DATA_FOO);
} > foo
GROUP:
{
MY_DATA_BAR : {}
__start_of_MY_DATA_BAR = ADDR(MY_DATA_BAR);
__end_of_MY_DATA_BAR = .;
__size_of_MY_DATA_BAR = SIZEOF(MY_DATA_BAR);
} > bar
}
padding can be removed by segments merging
for example
#pragma data_seg(".foo_begin")
#pragma data_seg(".foo_data")
#pragma data_seg(".foo_end")
#pragma comment( linker, "/merge:.foo_begin=.foo" )
#pragma comment( linker, "/merge:.foo_data=.foo" )
#pragma comment( linker, "/merge:.foo_end=.foo" )
__declspec(allocate(".foo_begin")) int foo_begin_marker;
__declspec(allocate(".foo_end")) int foo_end_marker;
__declspec(allocate(".foo_data")) int foo_data;
Create additional segments (they are placed in memory alphabetically):
#pragma data_seg("MY_DATA_FOO__a")
#pragma data_seg("MY_DATA_FOO__z")
#pragma data_seg("MY_DATA_FOO__m")
__declspec(allocate("MY_DATA_FOO__a")) int fooFirst;
__declspec(allocate("MY_DATA_FOO__z")) int fooLast;
__declspec(allocate("MY_DATA_FOO__m")) int foo1;
__declspec(allocate("MY_DATA_FOO__m")) int foo2;
Then copy everything between &fooFirst and &fooLast.