0

I've got some software compiled to run on an embedded NRF24 target, using a gcc-arm-none-eabi toolchain from here (a custom one that provides gdb with support for python3) . I'm trying essentially, malloc an array from the GDB debugger console at runtime, then fill it with elements that I provide.

I have a pointer defined in a .c file like: static float32_t *array;. I want to then call a cmd like: call (void*) malloc(num_of_elements*sizeof(float32_t)) from inside the GDB console to allocate an array at runtime, and then fill it with elements with something like maybe: call (void*) memcpy(array, {var1, var2... var n}, n)

My issue is the GDB debugger cannot find the malloc stdlib function. If I do something like:

break malloc 
Function "malloc" not defined.
Make breakpoint pending on future shared library load? (y or [n]) [answered N; input not from terminal]

It can't find this function, although it is fine with finding <string.h> fns, like memcpy for example and I can't quite work out why this is.

I have a feeling it could be something to do with linking, the program is built with a Makefile, the flags towards the end may be of interest:

LIB_FILES += \
  $(SDK_ROOT)/components/toolchain/cmsis/dsp/GCC/libarm_cortexM4lf_math.a \

# Optimization flags
OPT = -O0 -g3
# Uncomment the line below to enable link time optimization
#OPT += -flto

# C flags common to all targets
CFLAGS += $(OPT)
CFLAGS += -DBOARD_PCA10056
CFLAGS += -DARM_MATH_CM4
CFLAGS += -DBSP_DEFINES_ONLY
CFLAGS += -DCONFIG_GPIO_AS_PINRESET
CFLAGS += -DFLOAT_ABI_HARD
CFLAGS += -DNRF52840_XXAA
CFLAGS += -mcpu=cortex-m4
CFLAGS += -mthumb -mabi=aapcs
CFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# keep every function in a separate section, this allows linker to discard unused ones
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin -fshort-enums

CFLAGS += -DDEV8_PINOUT
CFLAGS += -DNUM_FLASH_BLOCKS=128
CFLAGS += -DDEBUG
CFLAGS += -DNRF_LOG_ENABLED=1
CFLAGS += -DNRF_LOG_BACKEND_UART_ENABLED=1

# C++ flags common to all targets
CXXFLAGS += $(OPT)
# Assembler flags common to all targets
ASMFLAGS += $(OPT)
ASMFLAGS += -mcpu=cortex-m4
ASMFLAGS += -mthumb -mabi=aapcs
ASMFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
ASMFLAGS += -DBOARD_PCA10056
ASMFLAGS += -DBSP_DEFINES_ONLY
ASMFLAGS += -DCONFIG_GPIO_AS_PINRESET
ASMFLAGS += -DFLOAT_ABI_HARD
ASMFLAGS += -DNRF52840_XXAA
ASMFLAGS += -DARM_MATH_CM4

# Linker flags
LDFLAGS += $(OPT)
LDFLAGS += -mthumb -mabi=aapcs -L$(SDK_ROOT)/modules/nrfx/mdk -T$(LINKER_SCRIPT)
LDFLAGS += -mcpu=cortex-m4
LDFLAGS += -mfloat-abi=hard -mfpu=fpv4-sp-d16
# let linker dump unused sections
LDFLAGS += -Wl,--gc-sections
# use newlib in nano version
LDFLAGS += --specs=nano.specs
LDFLAGS += -Wl,--print-memory-usage

nrf52840_xxaa: CFLAGS += -D__HEAP_SIZE=8192
nrf52840_xxaa: CFLAGS += -D__STACK_SIZE=8192
nrf52840_xxaa: ASMFLAGS += -D__HEAP_SIZE=8192
nrf52840_xxaa: ASMFLAGS += -D__STACK_SIZE=8192

# Add standard libraries at the very end of the linker input, after all objects
# that may need symbols provided by these libraries.
LIB_FILES += -lc -lnosys -lm

To debug I'm using GDB with a Jlink server type setup.

savvn001
  • 3
  • 3

2 Answers2

0

It can't find this function

The function may not be linked into your binary.

  • Does your binary call malloc elsewhere?
  • Do you link against libc.so or libc.a?
  • Does nm a.out | grep ' malloc' find it?

it is fine with finding <string.h> fns, like memcpy

If your binary calls memcpy and you link against libc.a, then memcpy implementation will be linked in. Using the same nm command from above will show that the memcpy symbol is present and malloc is not.

If you want to call malloc at runtime, you need to make sure it's linked in. One way to achieve this is to add:

const char *argv0;
int main(int argc, char *argv[])
{
  argv0 = strdup(argv[0]);  // This should guarantee that malloc is linked in.
  // rest of the program
}
Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Ah yes, adding your code snippet seemed to do the job, thanks for that. Actually this also made me realise a rookie error on my end. Malloc is called in my code, but in a function that wasn't actually accessible through normal program flow. The compiler being clever of course decides not to link it in. Whoops! – savvn001 Dec 16 '21 at 18:54
  • Is there a specific reason for invoking `strdup()` to force `malloc()` to link rather than calling `malloc()` directly? I would suggest perhaps `free( malloc( 0 ) ) ;` having fewer side effects and no memory leak. – Clifford Dec 23 '21 at 14:50
  • @Clifford No particular reason. You could just use `argv0 = malloc(1);` Using `free(malloc(0))` doesn't work -- GCC knows what `malloc` and `free` do and may optimize this out. – Employed Russian Dec 23 '21 at 18:13
  • Fair point re malloc(0). – Clifford Dec 24 '21 at 11:51
0

Your code needs to explicitly reference the symbol to force it to link, and you need to prevent link optimisation from removing unused references. Rather then doing a dummy call with potential unwanted side effects, you can simply refer to the symbol via a function pointer instantiation thus:

 void* (*volatile force_malloc_link)(size_t) = &malloc ;

Or more simply since you will not actually invoke the function through the pointer:

volatile void* force_link_malloc = &malloc ;

You could make it generic with a macro for any symbol you wish to link:

#define FORCE_LINK( sym ) volatile void* force_link_ ## sym = &sym 

You can also force the entire library to be linked (if you have the space) - How to force gcc to link an unused static library. One of the answers there explains how you can also unpack the static library and link individual object files.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Note that if `force_link_malloc` is not actually use, GCC may well optimize it out, so this solution is not better. – Employed Russian Dec 22 '21 at 03:22
  • @EmployedRussian - that is what the `volatile` is for. Moreover it is very seldom you would be running optimised code in GDB in any case. Not better - I never made any such claim - just an alternative. – Clifford Dec 22 '21 at 20:52
  • The volatile does not guarantee what you claim it does. And debugging optimized code with GDB is quite common where I come from. – Employed Russian Dec 22 '21 at 22:21
  • @EmployedRussian : It does not have to guarantee it - if it does it, it does it. If empirically it works it does not matter what the standard says _could_ happen - because it didn't happen. The requirement is to make malloc available in GDB - if malloc is available in GDB _in this instance_; job done - with no side effects. The possibility of unwanted side effects is a more serious concern I suggest. When the declaration has global scope the compiler cannot optimise it away (though conceivably the linker could). – Clifford Dec 23 '21 at 13:05